尧图精选

从零开始AI工程:数据、RAG、Agent与系统化落地的完整实践

🕒 发布时间:2026/10/1 4:13:12 📁 来源:尧图网络
从零开始搞AI工程这事很多人以为是“调API、套模板、跑通一个demo”就完事了。真上手做才发现搭一个能稳定产出价值的AI系统数据、模型、提示词、部署、观测、迭代一环扣一环每一项都藏着不少坑。这个项目叫“ai-engineering-from-scratch”核心就是带着你把这些环节从头到尾捋一遍不是看别人封装好的教程而是自己亲手把地基打起来。它能解决什么实际问题说白了就是治“demo能跑但不敢上线”的病。你可能会调一个聊天机器人、会写Prompt但一涉及到私有知识库问答、Agent多步任务、给业务方交付一个可靠接口就不知道从哪下手了。这篇文章就是围绕“从零”这两个字把AI工程的完整链路拆开讲清楚适合刚入门的开发者、想转型的工程人也适合已经在做AI项目但总觉得差点体系感的同学。1. 从零构建AI工程的整体设计思路1.1 先想清楚你做的到底是模型还是系统做AI工程最容易犯的一个错就是把“模型”当成“产品”。模型只是一个推理引擎它接收输入、吐输出但真正的工程系统是围绕这个引擎搭起来的一整套流水线。我见过太多人花了大量精力调Prompt、选模型结果忽略了数据质量和上线后的观测最后项目死在“偶尔好用、经常瞎编”的尴尬状态里。“ai-engineering-from-scratch”这个项目名里的“engineering”强调的是工程化思维。工程化意味着你做的东西要可复现、可度量、可持续迭代。比如你今天用某个Prompt模板跑通了效果明天换了批数据还能不能用上线之后用户问了一个训练集里没有覆盖的问题系统会不会崩这些才是工程要解决的问题。模型可以黑盒但系统必须白盒。从零开始的第一原则先画架构图再写代码。哪怕你手上只有一台笔记本、只想做个个人知识库问答也要把“数据怎么进、怎么切向量怎么存检索怎么排生成怎么控”这几层画出来。画图不是为了交文档而是逼自己把每个环节的输入输出想清楚。这个习惯我从一开始做到现在省下的回头路远比画图花的时间多。1.2 三条主线并进数据线、模型线、工程线我拆项目的时候习惯把整个AI工程分成三条并行推进的线任何一个环节掉队整体都会卡住。第一条是数据线。包括数据的采集、清洗、格式化、切分、标注、质量评估。很多人忽略数据觉得开源模型能力那么强喂点文档就能用。但实际做下来数据质量直接决定了效果天花板。模型能力再强喂进去的是脏数据出来的一定是脏结果这不是玄学是工程规律。第二条是模型线。这包括模型的选型、Prompt设计、微调如果要的话、离线评测。选型不是越大的模型越好要看任务复杂度、响应延迟要求、单位成本。Prompt设计也不是写一段“你是助手”就完了要针对业务场景做结构化模板还要考虑模型对指令格式的敏感度。第三条是工程线。包括API封装、并发控制、缓存、降级、监控、日志、版本管理。一个AI系统上线后工程线决定了它能不能活下来。模型推理偶尔出点错可以靠重试兜底但接口大面积超时、内存溢出、向量库挂了问题就完全超出AI范畴了这是传统后端工程的基本功。三条线不是先后顺序关系而是并行迭代的关系。拿到一个需求我会先把数据线拉起来摸一遍数据用少量样本跑通模型线的基线效果同时工程线搭一个最简单的HTTP接口做前后端联调。三条线都通了一遍再回头精细化打磨每一项。这个流程比“先把离线脚本都调好再上线”的古典做法快得多也更贴近真实业务对交付节奏的要求。1.3 为什么强调“从零”而不是“从Demo”开始GitHub上AI项目一抓一大把很多都能直接跑起来那为什么还要从零搭关键原因在于拿来即用的Demo是基于别人的假设设计好的你换成自己的数据、自己的场景改动成本往往比重写还高。举个例子很多开源问答项目默认用固定的向量化脚本和Prompt模板你换一类文档进来切分逻辑可能要变Prompt可能要重写甚至连数据库Schema都得调。这时候你面对的不是“加一个功能”而是“逆向理解别人的设计再打补丁”。从零搭虽然前期慢但你清楚每一个参数为什么这么设、每一条数据为什么这么流动后续维护和扩展的自由度是完全不一样的。不过我也要说句实话“从零”不等于“什么都自己造”。向量数据库用现成的Chroma、FAISS模型调用用现成的SDK这些不用重复造轮子。从零的核心是“自己掌握系统设计和各环节的衔接方式”不是从零写一个Transformer。2. 核心环节拆解与关键技术选型2.1 数据准备AI工程的隐形大头数据准备占据整个AI工程60%以上的工作量这句话一点都不夸张。我见过一个项目前期聊需求、选模型、设计架构花了两周结果一到数据阶段又干了一个半月而且真正焦头烂额的全是那些当时没预料到的问题。首先是数据清洗。文档类数据最典型的问题有PDF表格提取后行列错乱、代码块被正文分隔、标题层级丢失、统一编码乱掉。清洗的目标不是让数据“好看”而是让后续切分和向量化拿到的文本块是语义完整、结构清晰的。我常用的套路是先做一次全量扫描统计异常字符率、空行密度、标题数量用数据报告判断清洗需要做到什么程度而不是一上来就写一堆替换正则。然后是切分Chunking。这块是最容易被低估的环节。切分策略直接决定检索命中率。固定长度切分最简单比如每500字切一块加50字重叠但这种切法会把一个完整的技术方案从中间砍断。我的经验是优先按文档结构切利用Markdown标题、章节号作为天然边界结构不够明显时再退回到语义切分。切分粒度要和后续模型能接收的上下文长度匹配同时要保证一个chunk内部能独立回答一个问题。还有标注和质量评估。做问答类项目时我习惯挑几十个典型问题人工标注出“标准答案对应哪些chunk”这批数据量不用大但作用很大。第一它能用来调检索参数比如TopK、相似度阈值第二它能用来做回归测试防止后面改动流程导致老问题答错。2.2 模型选型先想清楚任务类型再谈模型参数模型选型是个很现实的问题。同样的任务闭源API的效果大概率优于本地小模型但涉及数据安全时又得考虑私有化部署。我一般先分清楚任务属于哪一类单轮问答、多轮对话、多步骤Agent、还是结构化抽取。任务类型决定模型的能力规模而不是越强越好。以典型的文本问答为例如果只是根据给定上下文回答不需要太多额外推理那么一个7B-14B量级的开源模型用得好性价比可能是最高的。但如果是多步骤Agent比如“先查库存再生成采购单最后发通知”这类任务需要较强的工具调用和规划能力我会倾向于选择能力更强的模型哪怕是付费API因为Agent一旦规划错了后续步骤全错省下的模型成本根本覆盖不了错误带来的麻烦。还要补充一个容易被忽略的点模型在Prompt格式上的差异性。同一个指令发给两个不同公司的模型结果的表现力可能差很远。选型阶段要顺手做一个“指令遵循度”测试拿10-20条业务代表性的输入统一模板去测根据结果再做决定。不要只看跑分榜跑分接近的两个模型实际业务效果可能差到离谱。2.3 Prompt Engineering不是写话术而是写接口规范Prompt Engineering是AI工程里绕不开的环节。但很多人的做法还停留在“润色提示词”比如把“帮我总结”改成“你是一个专业的助手请帮我用简洁的语言总结”。这种主观性的措辞优化对效果提升其实非常有限。真正的Prompt Engineering是把这个任务的结构、边界、输出格式、判别标准都定义清楚。我的实操习惯是把Prompt当成代码来写。固定部分用模板变量动态部分用占位符关键输出约束用格式说明加示例Few-shot。比如做意图识别我不写“请分析用户的意图”而是给出明确的分类枚举、每个类的解释、两个典型示例、以及输出JSON格式的字段定义。这样的Prompt模型表现的稳定性会明显提升。还有一个技巧是“给模型失败通道”。很多Prompt设计只写了“成功路径”比如“请从文本中抽取公司名称”却没定义“如果不存在怎么办、如果有多条怎么办”。实际工程中失败通道比成功路径更重要。我会在Prompt里明确要求找不全时输出空数组而不是硬凑不确定时标注置信度低而不是乱猜。这样下游处理起来干净得多排查问题时也不用靠猜。2.4 微调还是RAG两条路线的判断逻辑“微调和RAG到底选哪个”这是我被问到最多的问题之一也是项目初期最容易纠结的决策点。我的判断逻辑很直接看“知识更新频率”和“输出格式要求”。如果知识是静态的公司制度、历史文档、产品手册而且希望模型回答时能引用原文、标明出处那就无脑选RAG。RAG的本质是“外挂知识库”它把检索结果拼进上下文让模型基于给定材料回答。优点是知识更新只需替换向量库不需要重新训练模型回答还能溯到源。如果任务有固定的输入输出结构比如从合同里抽取关键信息且格式要求极其严格、几乎没有变化那微调更合适。微调能让模型在特定格式上形成非常稳定的“肌肉记忆”比单纯靠Prompt约束更可靠。但微调的坑也很多数据要人工标注几百上千条、训练要调参、部署要独占显存、后续知识更新还得重新训。这些成本要把账算清楚。实际生产里这两条路不是二选一可以组合。比如先用RAG解决知识广度问题再对最终输出的格式做一层轻量级微调或者用Prompt约束输出格式。我现在的做法是能用RAG就不微调除非格式要求真的苛刻到Prompt搞不定。这个判断帮我避免了很多无效投入。3. 一个完整实操案例从0到1搭建技术文档智能问答助手3.1 需求定义与技术方案为了把前面讲的东西落到实处我拆一个真实做过的项目——技术文档智能问答助手。背景很简单团队内部的系统有几百篇Markdown文档分布在多个仓库里新人入职问问题全靠手动翻文档效率极低。需求就一句话“输入一句自然语言提问返回准确的答案并给出答案对应的文档位置。”技术方案我用了经典的RAG结构没有做微调。原因很简单文档每周都更新RAG可以随时重建索引不用跟着文档改模型回答要求可追溯RAG天然支持引用原文。整体链路是文档解析 - 结构切分 - 向量化 - 存入向量库 - 用户提问 - 检索TopK相关片段 - 拼Prompt - 大模型生成 - 返回答案与引用。模型选型上向量化模型用了开源的bge-base-zh-v1.5文本生成用了国内主流的API模型。为什么向量化不用闭源模型因为向量化调用量大、对单条质量不敏感、用开源模型部署在自己环境里成本低而且便于后续批量重算全部文档。3.2 文档解析与切分决定问答效果的第一步这一步做扎实了后面能省很多事。原本文档是分散的Markdown文件很多人会直接读取全文扔进向量库结果检索出来的经常是整篇内容不相关的长文本。我先把文档按仓库分组再清洗掉重复的版本说明和旧的发布记录这部分规则写了大概两个多小时全是针对具体格式的脏数据处理的。切分策略我选了“结构化优先回退到长度切分”。具体实现是用Markdown的标题层级#、##、###作为主边界把文档拆成若干大块某些没有清晰标题的长段落再用固定长度切分兜底长度设置为300到500字之间重叠50字。为什么要重叠因为切分边界附近的语义容易断裂重叠一部分可以让检索时边界上下文更完整。切分完成后我给每条chunk额外保留了“来源文档路径”、还有“所在章节路径”这两个元数据字段。这个设计在后面生成引用链接时发挥了大作用。# 伪代码示意结构化切分逻辑 for file in docs/*.md: content clean(file) # 清洗脏数据 chunks split_by_heading(content) # 按标题切分 for chunk in chunks: if len(chunk) 500: chunk split_by_length(chunk, 300, 50) # 长度兜底 save_to_db(chunk, meta{path: file, section: heading})3.3 向量化与检索参数怎么定效果差在哪切分完成后把所有chunk传给向量模型得到768维的向量写入向量库。数据库我选了Chroma因为它支持持久化、API简洁、不需要额外起服务个人项目和中小团队用起来极其顺手。索引结构用的默认参数数据量只有几千条时调底层索引参数收益不大没必要纠结。检索参数这里有几个变量TopK数量、相似度阈值、以及是否需要按文档做去重。我拿小批量人工标注的测试问答做基线跑了几个组合最终定的TopK5。这个值能保证上下文信息够用又不会把Prompt撑得太大毕竟上下文太长既增加模型接口耗时也可能让模型被无关片段干扰。相似度阈值我设了0.4。低于这个值的检索结果干脆不用直接返回“暂时未在知识库中找到相关内容”。这里提一句阈值设置不当是最容易引发“答非所问”的坑。阈值设得太低无关内容混进来模型只能硬编设得太高合法内容被过滤召回率骤降。这个值不同领域差异很大一定要基于你自己数据的分布去调。# 检索环节的关键参数示例 from chromadb.utils import embedding_functions # 向量化 embedder embedding_functions.LocalEmbeddingFunction( model_namebge-base-zh-v1.5 ) # 查询示例用户在提问后先向量化再检索 query_embedding embedder.embed_query(user_question) results collection.query( query_embeddings[query_embedding], n_results5, where{source_path: None}, # 可按需要过滤来源 )3.4 Prompt模板与工作流搭建把“会回答”变成“答得准”检索到相关chunk之后生成环节的Prompt设计直接决定了用户拿到的答案长什么样。我设计的模板结构是这个样子开头定义角色与任务边界中间是“检索到的相关资料”然后是用户问题最后是输出约束。你是一个面向技术团队的知识库助手。 请严格基于“参考资料”中的内容回答用户问题。 如果参考资料中无法找到明确答案请直接回答“知识库中暂未找到相关说明”不要推测。 回答时尽量引用原文的关键信息并在回答末尾列出参考文档路径。 参考资料 --- 开始 --- {retrieved_chunks_with_meta} --- 结束 --- 用户问题{user_question} 输出要求简洁、准确、只输出回答内容与参考文档路径。这里的关键设计有两个。第一明确告诉模型“不要推测”让RAG的边界内收避免模型把检索到的内容跟自己的预训练知识混在一起。第二要求输出“参考文档路径”这一步靠的是切分阶段保留的元数据直接把路径放进Prompt里模型只需要照抄不需要自己猜。实测下来这个模板让答案的准确率和可追溯性都明显提升。工作流方面我把整个过程封装成了一个状态机。接收用户输入之后依次执行意图判断简单规则判断是否为闲聊、向量检索、片段拼接、模型调用、结果格式化。为什么要做“状态机”这一步因为当链路越来越长如果只是一段顺序代码后面加缓存、加日志、加失败重试都会非常痛苦。用状态机把每一步的输出和状态记录下来排查问题的时候能直接看到卡在哪个环节。3.5 模型调用与结果校验不能拿到的都用模型调用层的第一个经验是必须做超时控制和重试。大模型接口延迟波动很大高峰期一次调用可能要等30秒以上但用户不会等。我设置了10秒为接口超时上限超过后返回“服务繁忙请稍后重试”。重试策略上采用指数退避第一次重试等1秒第二次等2秒最多重试3次。这样能扛住偶发的接口抖动又不会因为无脑重试把下游资源耗尽。结果校验是我认为“工程化”和“写脚本”的分水岭。模型返回结果后我做三层校验第一层检查返回值是否包含“知识库中暂未找到相关说明”这类兜底句如果是就跳过引用步骤第二层检查回答中包含的参考文档路径是否真的存在于知识库防止模型自己编造了一个路径第三层用规则扫描回答中是否包含明显的乱码符号。校验不通过的结果我不会返回给用户而是直接记录到日志里打上“校验失败”标签后续人工分析。这层校验看起来不起眼但对线上系统的可靠性提升极大。模型偶尔会在输出里“加戏”比如自己编一个“请参阅内部Wiki”这种没有来源的句子如果不校验用户拿着这句话去搜会发现根本不存在对系统信任度的影响是致命的。3.6 部署与观测接口上线只是开始部署部分我用了FastAPI封装容器化后用最简单的单机部署。选FastAPI没别的就是生态成熟、支持异步、自动生成OpenAPI文档前后端联调省心。服务内部把向量库的读连接设为常驻模型API的HTTP连接池复用避免每次请求都重新握手。相比部署本身我更想强调的是观测。上线第一天我就加了三类指标调用量、耗时分布、检索命中率。调用量是最基础的容量指标看趋势能知道什么时候该扩容耗时分布要按链路拆检索耗时、模型调用耗时、总耗时分开看这样用户反馈“变慢了”的时候能快速定位是检索层还是生成层的问题检索命中率是最关键的质量指标——用户问的问题有多少是检索环节能捞到相关内容的浪涌这个值直接反映知识库覆盖率和检索参数是否合理。日志方面我记录了每一次完整请求的输入输出包括检索到的chunk ID、模型生成结果、校验结果。线上排查时最怕“用户说回答不对但你不知道当时模型看到了什么”。有了完整日志把当时的上下文捞出来问题就能复现复现就能修复修复就能验证。这一套观测习惯是从零开始搭AI系统时最值得提前投入的部分。4. 常见问题与排查技巧实录4.1 检索不到内容或检索结果明显相关度不足这是RAG项目最容易被用户感知到的故障。排查顺序固定先查数据切分是否合理再查向量化模型是否匹配语言接着查TopK和相似度阈值。切分不合理的典型现象是某块chunk特别长且混杂多个主题用户问题明明包含其中的关键词但整体向量相似度被其它无关内容拉低。这类问题看日志里的chunk ID就能发现锁定后调整切分逻辑即可。向量化模型和语言不匹配也很常见比如数据是中文技术文档却默认用了英文优化的向量模型排序效果自然差。还有种情况是阈值设太高检索结果全被过滤掉了。排查办法是我前面说的人工标注测试集把检索结果逐一打印出来看不要凭感觉猜。4.2 模型生成的答案像是“抄”了不相关内容明明检索到的chunk是对的但生成回答却跟问题对不上这多半是Prompt模板里的资料拼接顺序出了问题。模型对Prompt中资料的关注度很大程度上取决于位置。检索结果里最靠前的chunk如果是噪声模型可能被带偏。我处理过一个案例用户提问“怎么重启服务”检索到的第三个chunk里恰好提到“重启”二字但讲的完全不是一回事模型却把这段当主角答案彻底跑偏。解决思路是做重排。检索回来TopK条chunk后不直接把列表塞进Prompt而是用一个轻量级的重排序模型或规则把与用户问题关键词语义重合度更高的chunk提前。工程上如果不想引入额外的模型推理开销可以先做关键词重叠度打分把明显不相关的排到末尾。这个小改动成本很低对答案准确率的提升却立竿见影。4.3 模型接口偶发超时如何保证整体可用性大模型接口的超时几乎是必然事件直接对用户报错会显得系统特别不稳定。我的经验是三层防护缓存、降级、兜底。缓存是最容易被忽视的一层优化。实际场景中大量用户会问非常相近的问题或者同一个问题被反复问。我把问题和检索结果拼成一个缓存key命中缓存后直接返回历史答案既省了模型调用的钱又大幅降低了响应耗时。降级策略是把“检索生成”的正链路切掉一半变成“只检索并返回相关文档片段”让用户先看到能用的内容。最后再兜底真正全部失败时返回友好提示并记录日志第二天上报分析。这三层防护不是上线后才想到的而是在设计阶段就要规划好的。AI系统不像传统接口那样行为可控每多一层防护线上可用性就稳一分。4.4 多AI协作和Agent化带来的复杂问题当项目从单轮问答向Agent方向扩展时会遇到一类新问题模型不仅要回答还要规划步骤、选择工具、处理中间结果。这个时候单一Prompt已经管不住全部行为了。我的处理方式是把Agent的每个步骤拆成独立的“子Agent”每个子Agent只管一件事比如“意图识别Agent”“工具选择Agent”“结果校验Agent”父流程负责编排和传递数据。这种多Agent协作模式比让一个模型一口气跑完全部步骤要稳定得多。原因不难理解单次生成越长越容易在中途“走神”拆成多段后每一段的任务都足够具体即使某一步出错也能单独重试不会推倒重来。工程实现上就是状态机再加一层工具注册表每个Agent执行前先声明自己需要什么输入、能产出什么输出。这套设计我已经在多个项目里复用了排错体验比单一长链路好不少。4.5 实际问题排查速查表我把几个典型问题的排查路径整理成了表格方便直接对着查。症状优先排查项关键操作检索结果不相关切分粒度、向量模型语言匹配打印chunk内容检查语义完整性答案不引用原文Prompt约束缺失、元数据未传入在Prompt中强制输出来源路径答案频繁自夸/跑题检索到的上下文包含噪声加入重排逻辑或降低TopK接口耗时高模型调用无缓存、并发不足加缓存层、检查连接池配置偶发无结果相似度阈值过高下调阈值并观察命中率指标Agent多步任务出错单步长链路无拆分拆分子Agent并独立记录日志排查问题的核心心法是“先看日志再看配置最后看模型本身”。大多数问题的根源不在模型而在数据流和控制流的设计缺陷。把日志做全、做细很多问题一眼就能定位。5. 这套思路的后续扩展方向从零搭完一个基础问答助手后项目还有很多可以深度扩展的方向。一个是接入更多数据源。目前只处理了Markdown文档实际业务里PDF表格、网页、音视频转写都可能成为知识来源。每增加一种数据源都要重新梳理解析和清洗流程“一个统一的文档解析层”会变成刚需。另一个方向是把“单轮问答”升级成“可执行任务的AI Agent”。比如用户问的不再是“怎么申请服务器”而是“帮我申请一台服务器”这时候就需要Agent具备调用后端接口的能力并能管理多步任务的中间状态。我在前面提到的“多Agent协作”模式就是为这个铺路的。还有一块值得投入的是自动评测体系的建设。人工评估效果是现在的通用做法但项目变大之后人工评估根本跟不上迭代速度。沉淀一批自动化测试用例把答案的命中率、召回率、格式合规率做成流水线里的检查项每次改动完自动跑一遍靠数据来决定合不合并代码。这会让AI项目的迭代节奏从“玄学”变成“工程”。基于之前实操的经验我最想提醒你的一点是AI工程不是把模型接上就完事它真正的复杂度全在模型之外的流程设计、数据管理、观测体系和防错机制上。从零开始搭一套能用的系统比直接套用任何现成模板都更能帮你理解每个环节的逻辑。后面提到Agent化和多AI协作它们的根基也是这套从零建立的基本功。最后分享一个小习惯每次修完一个AI系统的问题不要急着删掉报错记录把当时的日志快照存起来标上问题描述和修复方案。过三个月回看这些沉淀下来的案例就是你最好的排错参考手册。AI技术迭代很快但这些工程经验不会过时。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →