☰
AI工程从零实战:亲手构建Transformer、RAG与Agent全链路
2026/9/30 4:10:06 网站建设 项目流程

做AI工程的人,如果从来没有从零写过一次Transformer训练,没有亲手调过Embedding、搭过检索链路,那他做的东西本质上是在玩盲盒。遇到问题只能靠猜,效果不好只能换提示词,模型升级换代就得跟着跑,完全没有主动权。“ai-engineering-from-scratch”这个项目路线,要解决的就是这个问题——把AI工程从黑盒变成白盒,从调用工具变成真正理解工具、改进工具。以我的经验,这条路最适合两类人:一类是刚入行、想系统建立AI工程知识体系的开发者,另一类是已经调了很久API、但遇到复杂问题总觉得心里没底的应用工程师。它不会帮你造出GPT-5,但能让你把一条完整的AI系统链路——数据、模型、检索、Agent、评估、部署——每个环节都讲清楚来龙去脉,出了问题也知道从哪里下手查。

1. 为什么“从零开始”才是AI工程的正路

1.1 调包侠和工程师的区别,往往就隔着一个“重写”

我一直有个观点:能跑通别人搭好的框架,和能自己从头搭一遍,是完全不同的两种能力。前者是“会用”,后者是“会造”。在传统软件工程里,大家可能觉得“会用框架”就够了,但在AI工程这个领域,事情变化太快,你很难指望一个第三方框架永远替你兜底。今天你用的向量库方案,明天可能被新的检索范式替代;今天你调的闭源模型,明天可能因为成本、数据合规、效果上限等各种原因要换成开源模型。这时候,如果你只懂调包,一切就得推倒重来,而如果你理解底层逻辑,换的只是实现细节。

from-scratch路线的价值就在这里:它会强迫你去重写那些看起来“不必要”的东西。比如为了做一个RAG系统,你要自己写文本切分、自己接一个开源的Embedding模型、自己实现向量索引的召回逻辑、自己做重排序,甚至自己写一段大模型推理代码。这个过程会很痛苦,但做完之后,你对“检索为什么不准”“上下文为什么超限”“回答为什么乱编”这些问题的理解会完全不同。因为你见过每一层是怎么运作的,排查问题就不是从现象猜测根因,而是顺着链路直接定位到具体环节。

我遇到很多从API开发转过来的朋友,一开始觉得“为什么要自己训练模型?直接用现成的不行吗?”我的回答是:直接用现成模型当然行,但那不叫AI工程,那叫API运维。AI工程的核心是你要能控制效果、控制成本、控制质量,这三件事没有任何一个现成模型能替你全部完成。

1.2 这个路线解决的核心痛点:黑盒恐慌

我观察到一个很有意思的现象:那些只调API的开发者,往往特别害怕模型升级。模型一升级,之前的提示词可能不灵了,输出格式变了,效果波动了,他们只能被动跟着调。而那些做过from-scratch训练的开发者,面对模型升级反而比较淡定。为什么?因为他们是知道模型内部发生了什么的人。他们知道输出不稳定大概率是采样参数问题,格式乱了就去结构化输出解析那里排查,效果变差就去评估集上做回归测试。这种“不慌”的底气,正是这个路线带给你的最核心的收益。

另外一个痛点也特别实际:面试和岗位竞争。现在AI方向的人才密度很高,光说“我用过LangChain”已经不算优势了,因为人人都用过。但如果你能说出“我自己实现过一个简化的Attention机制”“我给模型做过LoRA微调并且处理过灾难性遗忘”“我从零搭过一条包含重排序的RAG链路”,这背后的信息量是完全不同的。前者说明你消费过工具,后者说明你能创造工具。

2. 看清全貌:AI工程要啃的五大硬骨头

2.1 从数据到权重的模型构建

很多人以为AI工程就是“训练一个模型”,其实模型训练只是中间段。完整的模型工程是:数据采集 → 数据清洗 → 分词器训练 → 预训练 → SFT指令微调 → 对齐(RLHF/DPO)→ 评估 → 上线。任何一个环节做得粗糙,都会在最终效果上暴雷。

在实际从零项目里,最容易被低估的是分词器(Tokenization)和数据处理。我见过有人辛辛苦苦用别人的词表训练一个中文模型,结果发现分词器对中文的支持极差,一个字拆成好几个token,训练效率和生成效果都崩了。分词器必须和语料一起做,针对你的领域数据统计字节对频率,这一步不能省。数据清洗更是如此,很多公开数据集里混着重复文本、HTML标签、莫名其妙的语言乱码,不清干净,模型训练到一半就出现严重过拟合,生成的句子都是车轱辘话。

预训练阶段的核心是掌握训练节奏。你的loss曲线应该呈现一种平滑下降的趋势,如果出现阶梯状跳水或突然的尖峰,通常意味着学习率设置、batch size和数据顺序有问题。到了SFT阶段,问题又变成“教什么、怎么教”。我通常把指令数据视作一个质量远高于预训练语料的小数据集,它不需要太大,但必须格式干净、覆盖目标场景、避免重复。在这个阶段,我建议严格限制每条样本的长度,超出上下文窗口的数据直接截断,不然训练时长和效果都会收到明显负面影响。

2.2 RAG链路与向量工程

RAG是当前工程落地最多的方向,但它绝不是“文档切一下,向量存起来,检索问答”这么简单。一条完整的RAG链路包括:文档解析 → 清洗 → 分块 → Embedding模型选型 → 向量索引构建 → 召回 → 重排序 → 上下文拼装 → 生成。我见过很多效果差的RAG,问题根本不在模型,而在链条前端的解析和分块环节。

举一个常见的例子:一个PDF文档里有表格,你用普通文本解析器把表格拆成一行一行的碎片,然后喂给分割器,最后检索出来的都是残缺的表格碎片,模型再聪明也拼不出正确答案。正确的做法是要么用支持复杂版式的解析工具保留表格结构,要么干脆把表格整体当做一个块来处理。分块策略同样大有讲究:固定长度分块最省事,但会把语义完整的段落拦腰截断;语义分块效果好,但耗时高。工程上比较稳的组合是“先按文档结构粗分,再结合长度限制微调”,既照顾了语义边界,又控制了块的大小。

Embedding模型的选型和检索效果的关联度,经常被低估。有些人图省事直接用默认的小模型,结果在专业领域召回率惨不忍睹。我的经验是,至少准备两个候选Embedding模型,用一套人工标注的评测集跑一遍召回率对比,再决定用哪个。这一步多做几小时,能避免后续上线几个月被用户骂“检索不准”。

2.3 Agent与工具调用

Agent方向是过去一年演进最快、也最容易让人迷失的部分。从纯提示词驱动的ReAct、到结构化Function Calling、再到复杂多智能体协作,每一步都在往前走。但从from-scratch的角度看,Agent的核心其实不复杂,就三件事:规划、工具调用、记忆管理。

规划对应的是“模型如何决定下一步做什么”。最简单的实现是在prompt里给出一组工具的描述和JSON Schema,让模型输出一个结构化的“思考+行动”片段,然后由代码解析并执行。这就是ReAct的基本形态。工具调用对应“如何把自然语言意图映射到具体函数参数”。这里最关键的细节是函数描述要写得足够清晰:不要写“搜索知识库”,而要写“根据用户问题,在『产品文档库』中用关键词或语义检索找相关信息,输入参数为query字符串”,模型看到这种描述才能正确传参。记忆管理则是最大的隐性工程问题,上下文有限,必须做摘要、裁剪、优先级淘汰,这些逻辑全部需要手工实现。

我自己的体会是,Agent出问题90%出在工具调用格式不匹配上:模型输出了你想让它输出的JSON,但包含了一个多余字段,或者某个字段类型不对。所以,做Agent工程的第一课不是学什么高深框架,而是把“结构化输出的校验与纠错”模块写得非常健壮,要有模式修正、类型强转、重新请求这样的兜底逻辑。

2.4 评估、可观测性与数据闭环

这条可能是整个from-scratch路线里最劝退新手、但最应该被重视的部分。很多人对模型的认知只停留在“跑一次、看结果”,完全没有把质量量化、把过程记录下来的意识。但真正的AI工程,比训练本身花更多时间的一定是评估。

评估要解决的第一个问题是“什么叫好”。落地场景里,不能只看模型给的0到1的满意度评分,要看具体指标。答案是结构化抽取类任务,就看字段命中率和格式合法率;开放问答类任务,就用LLM-as-Judge让一个更强的模型打分,同时人工抽样复核;但检索是单独的指标,看召回率、命中位置、重排收益。把这些指标汇总成一张离线评测表,每次改动模型或prompt就重新跑一遍,才能防止“改好一个问题、弄坏三个问题”。

可观测性是另一个很多人忽略的点。上线之后,你的系统每一个请求都会消耗算力,产生迟延和成本。如果不对每次请求做链路日志记录,一旦效果变差,你根本不知道是检索环节没召回到正确文档,还是模型回答环节跑偏了,还是prompt模板拼错了。我建议在RAG链路的每个环节都埋点:检索耗费多少毫秒、命中了哪些chunk、重排后取了哪些chunk、最终喂给大模型的上下文有多长、首token延迟是多少。这些日志和指标,是后期优化最重要的依据。

2.5 推理优化与部署

模型训练完、链路调通了,下一步是上线。但线上环境跟本地实验完全是两个世界。本地实验你只需要让代码跑通,线上你必须关注延迟、吞吐、并发、成本和稳定性。

推理优化方面,最常用的是量化和批处理。一个FP16的7B模型在单张A10上跑,显存勉强够用,但并发一高就OOM;转成INT8或者INT4量化之后,显存占用能降到原来的三分之一到一半,吞吐大幅提升,但精度会有轻微损失,需要实测评估。批处理则是把多个并发请求拼成一个batch走一次前向计算,能显著提升吞吐,但也会增加单个请求的等待时间,需要权衡。

部署方案上,我个人的建议是能不用复杂的分布式系统就不用,先把单机推理性能压到极致。一个大模型服务节点加一个开源向量库,足以支撑中小规模的业务。等真正遇到瓶颈,再往分布式部署迁移也不迟。频繁更换架构,反而会引入很多和模型能力无关的稳定性问题。

3. 亲手复现一遍:从零训练一个真正能说话的小模型

3.1 搭建最小训练管线需要的七件事

如果你刚接触from-scratch,我建议把目标定低一点:不要一上来就盯着中文千亿大模型,先去把一个不超过1亿参数的小模型训练到能输出连贯句子,这个目标听起来不大,但足以让你理解训练全流程。要做的事只有七件:

  1. 选数据集:一个比较干净、又是领域相近的文本语料,比如英文小说、中文百科的子集都行。第一遍训练尽量用小数据,控制在几十MB到几百MB。“tiny shakespeare”之类的经典数据集是很好的起点,规模小、文本结构丰富。

  2. 做分词器:直接在语料上训练一个BPE分词器,词表大小设为5000到10000。这一步能让你直观感受到词表大小对训练速度和模型参数量影响巨大。

  3. 写数据加载器:把原始文本切成固定长度(比如256或512个token)的训练样本,设置好batch size和随机打乱策略。

  4. 实现模型结构:自己写一个简化版Transformer,包括Embedding层、若干层DecoderBlock(每层有多头自注意力+前馈网络)、LayerNorm和最后的输出投影。如果嫌麻烦,可以在PyTorch里一步步组装,但每一步都要明白它为什么存在。

  5. 定义损失函数:交叉熵损失就行,关键是理解它是如何衡量“模型预测下一个token的分布 vs 真实下一个token”之间的差距的。

  6. 写训练循环:前向传播 → 计算损失 → 反向传播 → 更新参数。加上学习率调度、梯度裁剪和定期保存checkpoint。

  7. 写生成脚本:训练完成后写一个推理采样函数,用temperature和top-p控制随机性,看看模型是否生成出语法基本正确的句子。

这套流程完整跑一遍,我对你的建议是别贪多,重点是让训练loss持续下降,并且最终能生成自然语言片段。至于效果多惊艳,那是后面的事,先练就“控制训练过程”的本事。

3.2 调参记录:我在一张消费级显卡上跑通的经验

我自己的实操参数可以给你做个参考。数据量大概200MB左右的英文小说语料,词表8000,模型大概用了6层Decoder、8个注意力头、隐藏维度512,总参数量不到4000万。序列长度256,batch size我调到了16条,等效一次前向处理的token数大概是4096。优化器用AdamW,初始学习率3e-4,先做了500步线性warmup,之后用余弦退火衰减到1e-5。梯度裁剪设为1.0,避免loss出现尖峰炸掉。

显存方面,单卡12GB完全够用。batch size如果更大,可以用梯度累积模拟,但第一次跑建议不要搞复杂,直接落到一个能装满显存但不会OOM的数值就好。训练到loss降到2附近,生成效果已经能看出来明显的语言结构,部分句子语法正确但逻辑跳跃。这个过程大概要三到五个小时,取决于显卡算力。

训练过程中有几点特别容易翻车:第一,学习率设置过高,会出现loss突然飙升到NaN的“爆炸”;第二,没有做梯度裁剪,长数据集训练时会出现偶发的大梯度尖峰,把已经训好的参数冲坏;第三,随机种子没固定,导致实验之间无法横向对比。这几点我都踩过,写在这里算是替你们提前探了雷。

3.3 别把微调当成炼丹:SFT阶段的三类典型坑

预训练完成只代表模型学会了“说人话”,不代表它“听指令”。SFT阶段用指令数据让模型学会问答,但这里坑非常多。第一类坑是数据污染和重复。指令数据如果不做去重,同一个问题反复出现,模型会把那几句标准答案背下来,遇到其他问法就哑火。我建议去做精确匹配和近似去重,把重复样本控制到最低比例,给其他问题更多学习机会。

第二类坑是过拟合到指令模板。如果所有指令都长成一个模板样子,模型的泛化能力会很差。训练集里要让问题表达尽量多样,甚至人工改写一些同义问法。我见过一个模型,你把问题正常写它回答得很好,但问题里多了口语词或网络词,回答质量立刻掉一截,就是因为训练数据太“干净”、太单一了。

第三类坑是灾难性遗忘。用指令数据微调之后,模型原先从预训练语料学到的通用语言能力可能会被冲掉。缓解的手段有几个:微调时混入一些小比例预训练语料进行联合训练;第二个是学习率不要太大,微调阶段一般用预训练的十分之一甚至更低;第三个是做带正则的LoRA,只更新部分参数,能显著降低遗忘率。这些对策组合使用,能帮你保住“底子”的同时学会“听话”。

4. 用from-scratch思路搭一条生产级RAG

4.1 为什么我放弃了“关键词搜索+一步到位”的懒人方案

很多人搭RAG的第一版喜欢图省事:文档全扔进去,调一个现成的检索库,然后拼到prompt里让大模型回答。这种方案Demo能跑,但效果极不稳定,因为检索的召回结果鱼龙混杂,真正有用的信息可能排在第三页,被大模型忽略。真正生产级的RAG要做的是“从源头控制上下文质量”,而不是寄希望于大模型从垃圾里淘金。

我的建议是,至少分四级来做:第一级是用Embedding做语义召回,第二级是关键词/BM25召回做互补,第三级是用重排序模型(Cross-Encoder)对两路结果做融合精排,第四级才是把选出来的优质上下文交给大模型。前三级的目标只有一个:把最相关的信息稳定地放进有限的上下文窗口里。

4.2 分块和嵌入的工程选型:没有银弹,只有权衡

先说分块。表格直接给你参考,方便你对照业务选策略:

策略适用场景优势风险
固定长度chunk(如512 token)格式统一、内容偏文本的文档实现简单、速度最快切断语义边界,检索结果完整性差
按段落/章节分块结构化文档、Markdown/HTML语义完整,检索精准度高长段落可能超出嵌入模型长度限制
语义分块(按句号/主题切)混合格式、问答型文档质量最高处理耗时,需要额外逻辑

执行上,我建议先用按段落/章节粗分,然后对超长段落用长度限制二次切分。分块重叠设成20到50个token,可以避免边界信息丢失。分块之后要做清洗,去掉页眉页脚、无意义空行、乱码字符,这些噪音对Embedding的语义表征干扰很大。

Embedding模型的选型,我用过几代开源模型之后,总结出的原则是:优先选专门针对中文或你的业务语言进行过优化、并且支持较长上下文(比如512或1024以上 token)的模型;其次看维度,维度越高表达能力越强,但存储和检索成本也更高。上线前一定用一组人工标注查询去测试召回命中率,我见过有些号称很强的通用Embedding模型,在垂直领域的效果远不如一个小而专的模型,所以别只看榜单。

4.3 召回、精排、上下文压缩:完整配置带你避坑

具体的链路配置,我按生产环境参数举例说明。假设知识库有10万条文档分块,Embedding维度768,向量库我用开源的HNSW索引,M值设为16,ef_construction设为200,这种配置下召回质量和查询延迟平衡得不错。

查询来时,我同时跑两路召回:一路用Embedding做语义检索,取top20;一路用BM25做关键词检索,取top10。两个结果合并去重后,送进一个Cross-Encoder重排序模型。这个模型会把每一对(查询,候选文档)打一个相关度分数,按分数降序取top5,作为最终上下文。重排序的意义在于,它弥补了向量检索对精确关键词不敏感的缺点,把最相关的几篇文档顶到最前面。实测下来,加了一路重排之后,回答的准确率能提高10到20个百分点,这在RAG链路里是非常大的收益了。

最后一步是上下文拼装。我的建议是做一个“prompt组装器”,而不是直接把文档瞎拼进去。组装器要做的事包括:截断过长的文档摘要、给每条文档加元信息(来源文件名、章节路径)、删掉明显冗余的重复片段,以及在上下文太长时做优先级裁剪。这一层做得是否细致,会直观反映在回答稳定性和可追溯性上。

5. 实战中经常翻车的11个问题速查

5.1 模型不收敛的四种死因与排查方向

模型训练不收敛,90%以上逃不出四个原因。第一,学习率设置不当,过高导致震荡甚至发散,过低导致长时间停滞;排查方法是观察loss曲线上是不是出现锯齿状波动,如果是,就把学习率调低两到三倍。第二,数据问题,比如标签错乱、数据分布方差过大;排查方法是抽样看数据,确认输入输出对齐。第三,参数初始化或模型结构错误,特别是LayerNorm位置、残差连接这些细节;排查方法是先跑一个极小的batch,看能不能实现过拟合,如果连一个batch都过拟合不了,说明模型实现有问题。第四,loss计算或掩码错误,导致某些位置被错误计算;排查方法是写一个极小的人工样本,手动推导出期望loss,再和代码输出比对。

5.2 检索不准、回答乱编的排查路线

如果你的RAG系统效果差,第一步先区分问题出在检索还是生成,方法很简单:把每次实际检索到的chunk打印出来看,如果这些chunk本身就不相关,那么问题在检索侧;如果chunk相关但模型仍然答错,那问题在生成侧或prompt组装侧。

检索侧继续排查,先看Embedding模型是不是和领域匹配,再看分块策略是否合理,最后看召回数量是否足够。我遇到过最典型的问题是:文档里信息很全,但被切到了不同的chunk里,两者都不完整,导致检索出来每个都有一点相关但都不够。生成侧则重点排查提示词是否说得足够具体,比如要让模型“只能依据给定片段回答,如果片段不足以回答则明确说不知道”,这句话听起来简单,但能显著减少幻觉输出。最后,别忘了加一个可观测层,把每次请求的检索chunk和模型输出都打点记下来,不然排查效率会非常低。

5.3 部署性能与成本失控的隐蔽项

很多人评估部署成本只看模型显存大小,这是一个大误区。实际成本的大头往往是并发吞吐、KV Cache、长上下文的算力消耗。一个支持4K上下文和32K上下文的模型,推理成本可能是数倍差异;每多一个并发请求,延迟和显存都会有非线性增长。

部署层面最常见的坑有四个:一是几十个并发上来之后,GPU显存被KV Cache打爆;二是没有做请求排队和限流,导致压力增大时服务整体抖动甚至雪崩;三是量化版本没有经过评测集验证,上线才发现效果严重下滑;四是日志和监控缺失,等到用户反馈大量异常才后知后觉。我的建议是上线前先压测,用脚本模拟线上流量并发请求,观察延迟、吞吐和错误率,把瓶颈找出来再调优。

6. 学习节奏与最后的经验之谈

6.1 一份可执行的三个月实操路线

我对想要系统走完这条路线的人提一个时间规划,你可以根据自己的基础压缩或拉长:

  • 第1个月:专攻模型搭建与训练。目标就是跑通上面第3章的小模型训练,并且能够熟练调整关键超参数,理解loss曲线的含义。
  • 第2个月:主攻RAG与评估体系。搭一条至少包含召回、重排、生成三段的RAG链路,同时建立一套离线评测集,学会用指标来衡量改动效果。
  • 第3个月:综合Agent、部署与优化。把之前的小模型或开源模型部署起来,做一个带工具调用的Agent演示,然后做一次量化、性能压测和成本估算。

这个节奏的好处是每个月都有看得见的成果,不会陷入“学了很久但什么都没做出来”的焦虑感。如果你本身有工程基础,时间可以压缩,但我不建议跳步骤。尤其不要跳过“从零训练模型”那一步,它是理解后面一切工程的基石。

6.2 写在最后:几点掏心窝子的经验

这条路我完整走过一遍,最大的收获不是“我会训练模型了”,而是我建立了一套判断问题的直觉。现在项目里模型效果不行,我不会先怀疑模型不行,而是会先检查数据、检查评估集、检查检索链路。90%的工程问题,其实都出在“给你看的那一层”之外。

另一个经验是:不要试图一次做完所有事。我见过太多人一开始就想做一个完美支持长上下文、多Agent协作、超大规模知识库的系统,结果项目半年都没上线。真正务实的做法是先把一条最小闭环跑通,哪怕效果糙一点,再基于评估数据逐步优化。工程不是炫技,是让系统在真实环境下稳定产出价值。

最后再分享一个小技巧:务必养成记录实验的习惯。每一次调参、每一个prompt改动、每一版检索链路更新,都记录下评估指标的变化。这个习惯前期看起来很费时间,但当你积累了几十条实验记录之后,你会发现你对整个系统的理解会变得异常清晰,别人问你效果为什么提升,你能直接说出是哪个环节的哪个改动起了作用。这种掌控感,就是从from-scratch一路走下来最值得的东西。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询