
最近总有朋友问我想系统入门 AI 工程到底该不该从框架和现成库开始说实话我自己最早就是这么学的先装了 PyTorch、HuggingFace跑通了几个 Demo觉得自己已经“入门了”。可真到要独立做一个项目时数据一换就崩、参数一调就乱、效果一差就不知道是模型问题还是数据问题。那段时间最大的挫败感不是“不会用工具”而是“脑子里没有完整的工程地图”。后来我把整个项目刻意归零不依赖任何现成的 pipeline从数据组织、训练循环、推理服务到评估反馈一层一层自己搭才真正理解了所谓 ai engineering from scratch。这个“from scratch”并不是让你重新造轮子而是让你亲手把每个环节的依赖关系摸清楚把“黑盒”拆成“白盒”。这篇内容就是我从那次归零重构里沉淀下来的完整思路适合想真正落地 AI 应用的工程师、准备深入研究大模型原理的研究者以及那些不想停留在“调包侠”阶段的人。你会看到一套可执行的工程骨架、小规模的 reasoning model 实践路径以及我在真实部署过程中踩过的最隐蔽的坑。1. 先纠正一个误区从零开始不是重新发明轮子而是重建心智模型我发现很多学习者的路径是反的先学 FastAPI、先学 LangChain、先学 Transformers遇到问题就搜索“如何用 X 实现 Y”结果项目做完一问“为什么这个 tokenizer 要加 special token”答不上来。这不是你的错而是大部分教程把“使用”和“理解”混为一谈了。真正的“从零开始”应该解决三件事第一让你知道每个组件为什么存在第二让你明白组件之间的数据流向第三让你在系统出现故障时不至于只能重新跑一遍。1.1 框架给你的是一条捷径但捷径会遮蔽依赖关系用现成的库常常只需要三行代码就能加载一个预训练模型from transformers import AutoTokenizer, AutoModelForCausalLM tokenizer AutoTokenizer.from_pretrained(gpt2) model AutoModelForCausalLM.from_pretrained(gpt2)这三行代码背后至少隐藏了六个环节词表切分、特殊 token 管理、嵌入矩阵初始化、位置编码、多层自注意力计算、语言模型头的输出映射。如果这些环节没有在你的头脑里建立基本认知当你想换成自己的 tokenizer、想改模型层数、想给注意力加一个 mask 时就会寸步难行。所以我在重构项目时给自己定的规矩是每次引入一个现成库之前先想一想“如果不用它我要自己怎么写”。哪怕不真的从零手写只是写出伪代码也有效。1.2 从零重建心智模型的三个层次操作系统层理解数据如何从原始文本变成 tensor如何构造 attention mask如何做 padding 和 truncation。这一层决定你能不能处理真实场景里格式脏乱的数据。算法层理解 transformer 的前向传播、反向传播的梯度流向、学习率策略的意义。这一层决定你能不能稳定地训练一个模型。系统层理解 GPU 显存管理、推理服务并发、请求批处理、监控告警。这一层决定你能不能把模型真正交到用户手里。有一个我常用的类比用框架做 AI 项目就像在毛坯房里做精装修但如果你不知道承重墙在哪里、水管怎么走一旦遇到问题就得砸墙重来。从零开始搭过一遍毛坯管线的人后来做精装修的速度反而更快。1.3 三种学习者三种路径建议如果你只是想快速验证一个想法可以直接用现成库但至少要过一遍“最小重建清单”自己写一个二分类模型的训练循环不用 Trainer。如果你想进入大模型应用研发我建议从 prompt 和一个极小的模型开始逐步构建评估、反馈、迭代的闭环。如果你想研究模型原理不要急着上大规模先把一个 1 亿参数不到的模型从零跑到收敛再去看大模型的论文会顺畅很多。从零开始的意义不是否定轮子的价值而是让你成为那个能修轮子的人。2. 工程地图数据闭环、模型生命周期、推理服务、评估反馈在我重构项目的过程中最受益的一步是绘制了一张“AI 工程地图”。这张地图把整个 AI 工程拆成四层我再也不会因为局部问题而全局慌乱。2.1 四层架构与它们的连接关系数据层包括采集、清洗、标注、增强、版本管理。它的产出物是“可被信任的数据集”。模型层包括选择预训练基座、微调、从零训练、checkpoint 管理。它的产出物是“可被复现的模型实例”。推理层包括接口封装、服务化、批处理、性能优化。它的产出物是“稳定低延迟的服务”。评估层包括离线指标、在线评测、用户反馈收集、回归测试。它的产出物是“可量化的质量度量系统”。它们不是一个单向流水线而是一个闭环。数据决定模型上限模型影响推理策略推理产生用户反馈反馈反哺数据与模型迭代。我见过很多团队把四个环节拆给了四个小组结果模型效果好但服务超时或者评估指标高但用户根本不满意就是因为缺少闭环视角。2.2 数据层缺失的版本管理比模型性能更致命很多从零开始做 AI 工程的人把 80% 时间花在调模型上但真正影响效果的往往是数据。我建议从一开始就建立三个习惯给数据集加版本号哪怕只是用 Git 管理数据生成脚本并把数据文件哈希记录在日志里。数据采样前先做 EDA统计长度分布、标签分布、脏数据率而不是直接开训。划分数据时保持时间一致性比如按时间顺序划分避免未来数据泄露到训练集。一个具体做法是使用带校验的 JSONL 格式存储训练样本每一行包含id、input、output、metadata。metadata 里记录来源、采样时间、标注版本。这样即使模型跑偏了也能追查是哪一批数据引入的问题。2.3 模型层从基座到微调之间的决策树模型层最常见的问题不是“怎么训练”而是“该不该训练”。我给自己整理了一个决策顺序现有开源模型能否在 zero-shot 或 few-shot 下解决 80% 的问题能就别训练。不能但通过 prompt 调整能接近目标先不要微调先优化 prompt 和数据组织。prompt 已经榨干仍不够好做领域微调或指令微调。需要极其特殊的推理能力且微调无效才考虑从头训练且优先从较小参数量开始。这个顺序能帮你节省大量时间和 GPU 成本。尤其在资源有限的情况下从零训练并不是浪漫主义而是非常具体的工程决策。2.4 推理层不只是把模型包成 HTTP 接口我重构后的推理层做了几件不复杂但很关键的事动态批处理并发请求到达后在窗口时间内聚合为同一 batch提高 GPU 利用率。超时分级简单请求给短超时复杂请求给长超时并通过队列做优先级调度。优雅降级模型服务过载时返回缓存结果或降级文案而不是直接报错。这些看起来是通用后端工程但结合模型推理的计算特征显存占用、时长不确定后变得非常考验细节。比如 batch 内序列长度差异过大会造成大量 padding 浪费所以服务端要按长度分桶组 batch。2.5 评估层把评估当成一套持续集成测试我见过太多项目只在最后跑一次测试集得出一个准确率就宣布完成。但 AI 系统最怕回归——今天效果不错的功能可能因为某次数据更新而悄悄变差。因此我把评估做成两个层次离线回归测试每次更新模型或 prompt 后在固定测试集上跑一遍指标包括准确率、召回、token 级别的鲁棒性检查。在线质量监控对线上请求抽样用模型裁判LLM-as-judge或规则检查评估回答质量并建立人工复核通道。离线指标解决“这次改动有没有变好”在线监控解决“用户实际感受有没有变差”。两者缺一不可。我后来很多次发现问题都是在线监控先报警然后再回查离线测试集——因为测试集没有覆盖长尾场景。3. 从零搭建工程骨架目录、配置、数据版本与实验追踪等地图成型我做的第一件事不是写模型代码而是搭一套哪怕换一个人也能接着跑的工程骨架。这比想象中重要得多AI 项目迭代速度快如果没有统一骨架两周后你自己都看不懂自己的代码。3.1 一个经过实战检验的目录结构ai-engineering-from-scratch/ ├── configs/ # 所有实验配置YAML 或 JSON │ ├── base.yaml │ ├── train_small.yaml │ └── serve.yaml ├── data/ # 数据目录通常不入库 │ ├── raw/ # 原始数据 │ ├── processed/ # 清洗后数据 │ └── versions/ # 带版本号的数据集快照 ├── src/ │ ├── data/ # 数据加载、清洗、增强 │ ├── models/ # 模型定义、训练器 │ ├── inference/ # 推理服务、批处理 │ ├── evaluation/ # 评估指标、评测脚本 │ └── common/ # 通用工具 ├── experiments/ # 每个实验的产物 │ ├── exp_001/ │ │ ├── config.yaml │ │ ├── metrics.json │ │ └── checkpoints/ ├── scripts/ # 从数据准备到部署的脚本 └── pyproject.toml 或 requirements.txt这套结构的核心思路是配置和代码分离、数据和模型分离、实验产物可以追溯。你不需要一开始就上一个重型的机器学习平台一个 Git 仓库加一个本地文件规范就够了。3.2 配置管理的核心不仅是方便而是让实验可复现我用 YAML 文件管理所有超参数同时在代码里通过配置类强制校验字段。比如from dataclasses import dataclass dataclass class TrainConfig: model_name: str gpt2 seq_len: int 512 batch_size: int 8 lr: float 5e-5 epochs: int 3然后在运行时把配置内容、Git commit、数据版本一起写入experiments/exp_XXX/config.yaml和metrics.json。以下是我的记录模板{ experiment_id: exp_001, git_commit: a1b2c3d, data_version: 2025-06-01-v3, config: {}, metrics: {}, notes: 第一次从零训练小模型 }你可能觉得这很啰嗦但某个深夜当你发现两天前最好的结果不知道用的哪份数据时就会庆幸自己做了这些记录。3.3 数据版本与实验追踪最简单的落地方案先别急着上复杂的云端平台。对一个从零开始的项目我用了一套几乎零成本但足够可靠的方案数据版本用sha256计算数据文件哈希记录在data/versions/manifest.json里。实验追踪用 Git 管理代码用 JSON 文件记录每次实验的配置与指标。长期保存每周把重要 checkpoint 上传到对象存储并在本地保留最近 3 个版本。这套方案足够支撑几十次到上百次实验。如果实验数量再多再引入 MLflow 或 WB 也来得及而且你已经对数据流有了清晰认知工具迁移会很轻松。4. prompt engineering 与 harness engineering 的边界可组合的提示系统当模型选型结束最容易被高估的一环就是 prompt。很多人以为只要在系统提示里堆叠规则模型就会乖乖听话。但真实工程里的 prompt只负责“表达意图”真正稳定的系统需要 harness 来组织一切。4.1 从两个概念的区别厘清设计边界Prompt engineering面向模型的文本输入设计包括指令、示例、格式要求、上下文组织。它解决的是“模型在单次推理中如何更好理解任务”。Harness engineering面向系统的外部框架设计包括工具注册、调用循环、记忆管理、错误恢复、安全过滤器。它解决的是“模型如何在多轮、多工具、多请求的复杂环境中稳定工作”。我见过一个团队花了大量时间调 prompt想让大模型稳定调用外部 API。但问题根本不在 prompt而是他们的 harness 缺少工具调用的反馈回路模型生成了一个 API 调用结果 API 超时了harness 直接把这个错误结果传给了模型然后就乱了。4.2 最小 harness 的六大模块如果你要从零实现一个 AI Agent 或复杂推理系统我建议先实现一个最小 harness包含以下模块上下文组装器把系统提示、历史记录、工具说明、当前输入拼装成模型可理解的上下文。工具注册表定义每个工具的name、description、input_schema供模型选择。解析器把模型输出解析为结构化指令比如 JSON 格式的tool_call。执行器调用真实工具并处理超时、异常。结果回填器把工具结果按固定格式追加到上下文形成“调用-观察-思考-行动”的循环。策略控制器控制最大轮数、终止条件、安全兜底。以下是一个极简伪代码流程while not done and step max_steps: prompt context_assembler(system_prompt, memory, tool_schemas) response model.generate(prompt) action parser.parse(response) if action.type finish: done True else: result executor.execute(action) context_assembler.append_observation(result)这个循环听起来简单但它把所有坑都暴露出来了模型输出不合法 JSON 怎么办工具结果太长导致上下文爆炸怎么办连续调用同一个工具太多次怎么办这些都需要在 harness 里不断加防御性设计。4.3 上下文管理的工程级技巧大模型上下文窗口是很大但实际可用容量会被质量和成本压缩。我常用的三个技巧Token 预算分层给系统提示、历史、工具说明、当前输入分别设上限超出后优先压缩或裁剪历史。关键信息重排重要信息放在开头和结尾primacy 和 recency 效应中间放次要内容。动态摘要当历史超过阈值时调用一个小模型把旧历史压缩为摘要保留关键实体和决策。这里有一个现实工程经验不要为了保留每一句对话而让上下文无限增长。根据我的实测许多模型在长上下文上的表现不比“摘要近期原文”好多少但 token 成本却高得多。5. 小模型起步从零构建一个可运行的推理模型的路径现在来到大家最感兴趣的部分。标题里有“from scratch”而当前社区里很火的短语是 “build a reasoning model from scratch”。如果你有耐心和有限的资源完全可以从一个几亿参数的小模型开始走完预训练、指令微调、强化学习对齐的完整流程。接下来我分享的是一条低成本可执行的路径。5.1 为什么先用小模型而不是直接对标大模型大语言模型的成功依赖数据规模、算力和分布式训练的工程能力。个人或小团队想从零复现 GPT-4 级别的模型不现实。但“小模型”的价值在于它让你在几小时或几天内看到完整的训练-评估-迭代闭环。我建议初始参数量控制在 1 亿以下用大众显卡也能跑。比如一个 4 层、隐藏维度 512、6 头注意力的小 GPT参数量大约 6 千万足够做简单的数学推理任务。这样的小模型不能跟 70B 模型比知识量但它可以验证“模型结构、数据组织、训练策略、推理方法”是否走得通。这就像学飞行先上螺旋桨小飞机而不是直接开喷气式客机。5.2 数据合成先解决模型“看到什么”的问题推理模型需要的是带思考过程的训练数据。一个常见做法是“从粗到精合成推理轨迹”准备基础问题集比如小学数学应用题、逻辑谜题。用规则或较强的大模型生成逐步推理过程但你需要检查每一步。把所有数据洗牌、去重、过滤格式错误存储为统一 JSONL。样例如下{ question: 一个盒子有 12 个苹果拿走 3 个又放进去 5 个现在有多少个, reasoning: 先拿走 3 个12-39。再放进去 5 个9514。, answer: 14, source: synthetic_v1 }注意合成数据质量比数量更重要。十个带完整正确推理步骤的样本好过一千个只有答案没有推理过程的样本。在后续训练里模型会从“模仿推理格式”开始逐步学会“推理的因果结构”。5.3 从零定义一个极简 Transformer 骨架如果你真的想“from scratch”你可以不直接使用nn.Transformer而是手写一个极其简小的模块化实现。核心只需要三件套Token 嵌入与位置编码因果自注意力前馈网络下面是核心骨架的简化代码片段用于演示思路不是完整实现import torch import torch.nn as nn class TinyAttention(nn.Module): def __init__(self, d_model, n_heads, causalTrue): super().__init__() self.qkv nn.Linear(d_model, 3 * d_model) self.out nn.Linear(d_model, d_model) self.n_heads n_heads self.causal causal def forward(self, x): B, T, C x.shape qkv self.qkv(x).reshape(B, T, 2, self.n_heads, C // self.n_heads) q, k, v qkv[:, :, 0], qkv[:, :, 1], qkv[:, :, 2] att (q k.transpose(-2, -1)) / (C // self.n_heads) ** 0.5 if self.causal: mask torch.triu(torch.ones(T, T), diagonal1).bool().to(x.device) att att.masked_fill(mask, float(-inf)) att att.softmax(-1) out att v out out.transpose(1, 2).reshape(B, T, C) return self.out(out)这段代码重点是展示注意力 mask 和维度变换逻辑。真的实现时还要考虑 KV Cache、量化等。但从这里起步你会非常自然理解为什么大模型里需要 mask为什么 attention score 要除以根号 d。5.4 训练一个“能说步骤”的小模型准备工作完成后训练分为两个阶段阶段一语言建模预训练。用普通文本训练模型学习基础语言规律损失函数是交叉熵。这个阶段可以让模型先学会“生成连贯文本”。阶段二推理指令微调。在合成推理数据上继续训练输入是question输出是reasoning和answer。这一阶段的关键是避免灾难性遗忘可以混合一部分通用文本数据。对于 6 千万参数的小模型我用单张消费级显卡也能在几小时内跑完阶段一视数据规模。阶段二更快几百条到几千条样本就能看到明显的“行为改变”。5.5 推理贪心还是采样训练完成后推理阶段也有一些小但重要的选择。贪心解码适合需要稳定答案的任务但对多步骤问题容易陷入重复带温度的采样能增加多样性但可能产出错误步骤。我建议在推理任务上先用带一定温度的采样生成多条候选再用一个简单的验证器比如计算最终答案是否正确做原则性筛选。这种“生成多个候选 验证器筛选”的思路实际上是很多 reasoning model 的雏形。它的核心理念是让模型探索更多路径然后让系统选择可信路径而不是让模型一次性输出完美答案。6. 从可跑通到可部署性能、成本与监控的实战权衡模型在实验环境跑通只是起点。真正把系统交给用户使用会遇到比训练更琐碎的问题。我在此分享一些可复制的部署策略。6.1 性能优化决策按场景选择武器部署一个 AI 服务不是把所有请求都塞给最大的模型。我的配置思路是“分级路由”场景模型方案延迟目标成本实时对话小模型或量化模型1s 内低复杂推理大模型 多步骤 harness5s-30s高异步任务离线批处理不要求实时分钟级可调度一个常用技巧是先用小模型过滤简单问题只有遇到低置信度或复杂关键词时才把请求升级到大模型。这样能节省相当可观的成本。6.2 模型优化量化、蒸馏与缓存量化将模型权重从 FP16 降到 INT8 或 INT4显存占用大幅下降速度提升但可能在边缘 case 上损失质量。建议先在评测集上验证退化程度。蒸馏用大模型的输出作为小模型的训练目标让小模型学习“大模型的判断边界”。如果你的场景响应延迟敏感这比直接调用大模型更经济。缓存对重复性高的 Prompt 和结果做语义缓存。比如很多用户的问题只有少量变体可以用 embedding 相似度检索命中缓存就不调模型。6.3 可观测性不要等用户投诉才发现问题我把 AI 系统的监控分成三个等级基础设施级GPU 利用率、显存、延迟、请求 QPS。模型质量级回答长度、拒绝率、缓存命中率、平均置信度。业务效果级用户点击、停留时长、工单解决率。很多时候基础设施一切正常但模型质量悄悄崩了。所以我强烈建议在推理服务里加入“影子评估”随机抽取 1% 的线上请求记录 prompt 和 response第二天用评测模型做一次质量打分并按天汇总趋势。如果你的评分曲线突然下降往往说明有数据分布漂移或 prompt 被误改。6.4 一次真实的线上问题复盘有一次我们的模型回答质量突然下降。看起来像模型退化但检查 GPU 利用率正常。后来通过影子评估发现评分从 4.2 降到了 3.6再追踪 prompt 记录原来是某个上游系统把用户输入的编码从 UTF-8 变成了带 BOM 的格式导致第一段文本多了一个不可见字符。模型其实没坏坏的是数据链路。这个经历让我非常坚定AI 工程里90% 的“模型变笨了”其实是数据链路变化而不是模型本身出了问题。没有完善的日志与追踪你只能盲猜。7. 避坑指南三个我在从零实践里踩过的隐蔽陷阱最后这一部分我不讲宏大架构只想分享几个让很多人头大的真实教训。这些坑都有一个共同点看起来很平常但破坏力很大。7.1 评估集污染你的指标在说谎我刚开始做实验时习惯把随机划分的数据作为评估集。后来发现训练样本和评估样本有大量重复语义比如同一个问题只是换了数字模型记住了模式评估分数虚高。但这个“高分模型”一上线就暴露问题。现在我的做法是用时间或来源划分数据保证评估集的分布与线上尽量一致。每次新增数据时先计算与已有评估集的相似度去重后再进入训练集。保留一个“金标准集”永远不参与训练只用于最终评估。7.2 上下文窗口的假象看得见不等于用得好很多模型支持 128K 上下文但实际效果取决于注意力可以触及多远。我做过对比实验把关键信息放在中间位置模型经常忽略放在首尾准确率显著提升。还有一个现象是上下文越长模型在后续生成时越容易重复或走神。因此我在工程设计里从不假设“模型能使用全部上下文”。关键信息必须前置或者通过强制格式比如 XML、JSON让结构更清晰。必要时把长文档拆分成多个子任务分别处理后汇总结果。7.3 实验管理混乱昨天最好的结果为什么复现不了这个问题几乎人人都遇到过。早期我为了快速迭代直接改同一个脚本不断覆盖原来的配置。结果有一天发现之前的指标很好看但代码已经改得面目全非完全不知道当时的参数组合。这个坑不是靠聪明能解决的就是靠规范。我现在的强制流程是每次实验前从main分支创建新分支或实验目录。实验结束立即填写实验记录表包含配置、数据版本、指标、结论。每个新 idea 都是新实验不修改历史实验产物。虽然听起来像“写文档”但它真的能让你避免大量返工。尤其是部署上线前你要回滚到“之前的最好版本”一套完整的记录系统就是你的保命绳。这里我还想加一个隐藏的教训不要急着一个文件里同时解决数据和模型逻辑。AI 项目代码的演进速度极快模块化设计可以让你在新增一个数据来源或换一个模型时只动对应的模块而不是把整个项目推倒重来。从零开始不等于永远从小开始而是让你拥有随时“重组”的能力。如果你也正在做 ai engineering from scratch我最后的小建议是把目标拆成两个递进的小闭环。第一个闭环是“数据-训练-评估”哪怕用最小的模型也要走完整第二个闭环是“请求-推理-监控”哪怕没有复杂业务也要把线上链路打通。这两条闭环会成为你后续所有 AI 工程能力提升的骨架。