ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

AI应用工程化实战:从零手工搭建RAG全链路,破解生产环境四大坑

AI应用工程化实战:从零手工搭建RAG全链路,破解生产环境四大坑 做AI应用开发超过半年我发现自己回答最多的问题不是“模型选哪个”而是“为什么我的东西一上生产就废”。这个问题问的人多了我才意识到大家缺的并不是Prompt技巧而是一整套从数据到评估的工程能力。我落地过一个叫 ai-engineering-from-scratch 的项目核心思路只有一个不靠任何封装的AI框架从数据清洗、索引构建、检索召回、提示词设计到评估闭环全链路亲手搭一遍。这篇文章就是那个项目的完整复盘包括我为什么坚持“从零开始”每一层怎么选型、怎么写、怎么排错以及踩过的那些在文档里根本找不到的坑。适合两类人看一是刚入门AI应用、想系统建立知识体系的人二是已经在用API接功能、但被检索质量、幻觉、成本问题反复折磨的工程师。1. 整体设计思路真正的“从零”不是从张量开始1.1 先划清“从零”的边界项目刚开始我遇到最大的困惑就是“从零”到底指什么。是不是得像深度学习教科书那样手写一个Transformer、手动实现反向传播才算从零我试过花了两周时间在PyTorch里复现Attention机制跑出来的效果一塌糊涂对业务毫无帮助。后来我意识到那是模型训练工程师的从零不是AI应用工程师的从零。绝大多数团队真正缺的不是再造一个模型而是把现成大模型稳定接进业务流程的工程能力。所以我给这个项目定的边界很明确不借助LangChain这类端到端框架从数据准备、向量化索引、检索召回、生成链路、评估闭环五层亲手搭一条完整的AI应用流水线。每一层都知道它是怎么工作的出了问题知道去哪一层排查而不是对着一个黑盒束手无策。1.2 五层能力模型与项目路线图我这张路线图后来也分享给了团队基本就是AI应用工程的能力分层层级核心职责关键问题数据层采集、清洗、切块、增强数据质量直接决定效果上限索引层向量化、存储、元数据管理索引结构决定检索效率检索层召回、重排、过滤找得到相关材料是一切的根基生成层上下文组装、提示词、输出约束让模型只基于证据说话评估层指标、评测集、线上追踪没有度量就没有迭代这五层是有依赖顺序的。我见过不少团队一上来就花大力气调Prompt结果检索回来的东西根本不对Prompt调得再花哨也白搭。所以项目路线也按这个顺序推进先把数据和检索做扎实再碰生成最后才建评估。每一步都跑通并写测试才进入下一层。1.3 为什么“亲手搭一遍”反而更快有人会觉得市面上现成框架一大把自己造轮子不是傻吗我的体会恰恰相反。直接用框架你确实能在一小时里搭出一个Demo但一旦出了问题框架的封装层会像俄罗斯套娃一样把错误包在里面。LangChain报一个“Retriever returned no documents”你根本不知道是embedding模型挂了、向量库连不上、还是切块切出了空文档。亲手搭一遍每个环节你都捏过一遍相当于给系统画了一张“内部地图”。后面再切换到LangChain或者更重的生产框架你一眼就能看出它在每层做了什么、做得好不好。这个“先裸写再上框架”的顺序我后面在实操里还会细说。它前期看着慢实则在帮你规避后期最大的成本排查问题的成本。2. 技术选型动手之前先把这四个决策想清楚2.1 模型层本地小模型打底云端大模型做生产模型选型是第一个绕不开的决策。我把选项分成两条路本地方案和API方案。本地方案用Ollama、llama.cpp或者HuggingFace Transformers跑开源模型你说什么它都能跑但效果受硬件限制API方案效果强、迭代快但每次调用都花钱而且调试时很难看到模型内部行为。我的建议是学习和调试阶段用本地小模型生产环境按预算和效果选API或私有化部署。为什么本地小模型虽然聪明程度一般但它能在你机器上稳定复现方便你一条条测试检索链路。比如我用Qwen2.5-7B-Instruct做开发和排查确认检索和提示词都没问题后再生产环境切换成效果更强的商用模型。这样既省钱又能把“模型能力”和“工程链路”这两个变量分开来排错。2.2 数据与向量化切块参数才是检索质量的胜负手项目里我花了最多时间的不是模型而是数据。原始文档要先清洗去重、去噪、统一格式。接着是切块chunking这步直接决定检索质量。切块太大一个块里塞了太多无关信息向量表达被稀释召回精度暴跌切块太小语义不完整召回又容易漏。我实测下来通用技术文档用512个token一块、块与块之间重叠80个token比较稳。重叠的原因是防止关键句子恰好被切在边界上导致语义断裂。这个参数不是拍脑袋定的我是用一组标注好的问答对迭代测试不同切片长度下的召回率发现500到600这个区间最稳。没有标准答案但每换一种文档类型都应该重新做一次这个测试。2.3 向量数据库从Chroma到生产级方案的路径向量库的选择也不用过度纠结。学习阶段我强烈推荐Chroma或者FAISS。Chroma直接pip安装就能跑自带持久化几百兆内存就能启动适合把链路先跑通FAISS是Meta出的轻量库索引机制透明简练适合理解向量索引的底层原理。等数据量到了百万级、需要高并发和权限过滤时再迁移到Qdrant、Milvus或者Weaviate这类真正的分布式向量数据库。这里插一句向量数据库不是越快越好。生产项目里权限过滤、混合检索、多租户隔离往往比裸查询性能更重要。选型之前先列一下你的过滤条件有哪些比如“只检索某个部门的文档”“排除已过期的文档”这些在轻量方案里实现起来会非常痛苦尽早用带Metadata Filter的方案才能少走弯路。2.4 编排框架晚一点再上LangChainLa链这个工具我的评价是很好但别急着用。我在项目的前六周完全没碰任何编排框架用原生Python写数据加载、向量化、检索、拼上下文、调模型。等每一步的逻辑都理解透了才对照着看LangChain是怎么组织的。这个切换过程非常丝滑因为我完全看得懂它每一步在做什么。框架的抽象本身没有错错的是在没理解基础链路之前就依赖框架。框架帮你省掉的是样板代码帮你省不掉的是理解和排查。先裸写后框架的顺序也让我在选框架的时候有底气不盲从最新热门的那个而是看它的组件边界是否清晰、日志是否透明、是否便于我插入自己的评估逻辑。3. 实操全流程从零搭建一个文档问答助手3.1 第一步圈定场景控制数据范围实操部分我拿一个非常典型的场景来走全流程给团队做一套“运维故障手册问答助手”。为什么不选一个宏大的“企业知识库”因为第一版项目最重要的目标是跑通闭环而不是覆盖所有功能。范围越小越容易验证每一层的对错。我收集的数据来源有三个历史故障工单、运维手册Wiki、常见问题排查文档。文档格式有Markdown、PDF和HTML共两百多份。第一步不是急着处理而是先人工把所有文档浏览一遍删掉过时内容补上缺失的版本号。这一步虽然费眼睛却是整条流水线的地基——脏数据进索引后面所有环节都会被污染。3.2 第二步清洗、切块与向量化索引清洗的时候有几个细节值得说。一是把所有格式统一转成纯文本PDF必须经过解析抽文本表格用分隔符转成CSV风格二是重复内容按标题和正文哈希去重避免同一份知识在检索里重复出现三是给每个文档块打上Metadata比如来源文档名、章节路径、更新时间这些后面做权限过滤和引用溯源都要用。切块和向量化的代码很简短我用的Embedding模型是BAAI/bge-large-zh-v1.5中文场景里性价比很高。核心逻辑大概长这样from chromadb import Documents, EmbeddingFunction, Embeddings from sentence_transformers import SentenceTransformer model SentenceTransformer(BAAI/bge-large-zh-v1.5) class MyEmbeddingFunction(EmbeddingFunction): def __call__(self, input: Documents) - Embeddings: return model.encode(input, normalize_embeddingsTrue).tolist() # 切块按 token 数切带 overlap def chunk_text(text: str, chunk_size: int 512, overlap: int 80) - list[str]: tokens text.split() # 生产环境请用正式 tokenizer chunks [] start 0 while start len(tokens): end start chunk_size chunks.append( .join(tokens[start:end])) start chunk_size - overlap return chunks构建索引之前先做一个小验证把10个带标准答案的问题和对应的正确文档块分别算相似度确认Embedding模型语义理解符合预期。这一步能提前暴露Embedding模型选错的问题比如用了通用英文模型来处理中文文档检索效果会非常糟糕。3.3 第三步检索召回与重排细节索引建好了检索看起来就是一句collection.query()但里面的参数值得细抠。首先是top_k我测试下来4到8个块比较合适。太少会漏信息太多会把噪声塞进上下文让模型不知道该信哪段。其次是相似度阈值。我用的是余弦相似度Chroma里默认的distance函数是L2需要显式改成余弦。阈值太高会问什么都召不回太低又会召回一堆无关内容我根据自己的评测集把阈值定在0.65到0.75之间低于这个的直接不返回宁缺毋滥。还有一个容易忽略的问题是检索排序。向量相似度只保证“语义相近”不保证“答案顺序正确”。我当时遇到一个典型case问题问“数据库连接超时怎么排查”召回的三个块里最相关的那段被排在了第二位模型照着顺序读下来反而被前一段干扰。后来加了重排环节用一个轻量rerank模型把第一轮召回的候选重新排序回答质量立刻上了一个台阶。如果不想引入额外模型至少要在Prompt里让模型“先看哪段再做判断”也能缓解排序带来的影响。3.4 第四步生成链路与提示词模板生成层要解决的三个问题怎么把检索结果装进上下文、怎么约束模型只基于证据回答、怎么保证输出格式可控。先说上下文组装。模型有上下文窗口限制不能把五个块一股脑塞进去。我先对各块按相似度排序留前5个再把每个块压缩成“摘要关键证据句”的形式保证总长度不超过窗口的一半为模型输出留足空间。这一步有个我踩过的坑不要按原始文档顺序拼接上下文正常段落里离提问越远的内容越容易被模型忽略所以必须把最相关的内容放最后、紧挨着提问语句实测效果更好。提示词模板没有必要写得很玄关键是给模型立规则。我的模板核心部分如下你是一名运维知识库助手。请严格根据给定的参考文档回答问题。 规则 1. 如果参考文档中有答案请用你自己的话清晰作答 2. 如果参考文档中没有答案只说“我无法根据现有资料回答”不要编造 3. 回答结尾必须标注引用来源编号格式为【来源1】【来源2】。 参考文档 【来源1】{chunk_1} 【来源2】{chunk_2} 问题{question}生成参数也不要忽略。事实问答场景temperature我设在0.2太高模型会“自由发挥”max_tokens限制在500以内如果希望输出JSON结构化可以在Prompt里指定Schema并要求模型只输出JSON实测大部分模型都能很好配合。3.5 第五步评估闭环与上线迭代很多人做到上一步就宣布大功告成实际上漏了最重要的评估环节。我建了一套离线评估集从真实工单里挑出80条问题每条标注了标准答案和对应的文档来源。每次改动切块参数、Embedding模型或Prompt就跑一遍这80条用三个指标衡量检索召回率、答案忠实度、答案相关性。其中“忠实度”这个指标最值得做。做法是拿生成的答案去和参考资料对比看答案里的关键事实是否都能在参考文档里找到出处找不到的就是一次幻觉。这个指标在RAG场景下比任何人工打分都更有说服力。线上再埋点记录用户反馈比如“点赞/点踩”定期抽样看坏case。这套闭环跑起来之后我才真正理解了什么叫“迭代”。以前调Prompt是凭感觉现在是改完一个参数用数字说话。整个项目的后半段我的精力基本都花在补数据、调检索而不是调模型这是评估体系带来的最大改变。4. 踩坑实录生产环境最常见的四类问题排查4.1 检索召回差八成问题出在数据不是模型召回差是最容易迷茫的问题。表现为用户问A系统答非所问。我排错的第一步永远不是看模型而是把召回的原始文档块打出来肉眼检查。检查下来发现大部分问题集中在三个方面切块破坏了语义、文档本身质量差、Embedding模型领域不匹配。举个具体case。我的故障手册里有张表列了各种错误码和对应解决办法。切块的时候表格被拦腰截断错误码在上一块、解决办法在下一块检索自然召不回完整信息。解决办法是切块前把表格转成带描述的自然语言文本再参与切块。还有一次是排查工单里的“内存”和“内存条”语义相近但场景差异大通用Embedding分不清最后是通过在文档里补充上下文描述搞定的。记住模型解决不了数据本身缺的东西。4.2 幻觉拦不住别把Prompt当万能补丁幻觉问题人人都会提但多数人只会在Prompt里加一句“不要编造”然后发现没用。根本原因在于如果检索没能给模型提供证据Prompt再狠也拦不住模型凭训练记忆发挥。我的处理分两步。第一步是硬约束在Prompt里明确规则“不能使用参考文档之外的知识”同时把产生回答所用的证据块编号嵌入答案来源没法指向任何参考文档的回答直接判定为不合格。第二步是软兜底如果检索结果的最高相似度低于阈值直接告诉用户“这个问题不在知识库范围内”而不是让模型硬答。产品层面做一次“拒绝回答”比答错一次更值得信任。4.3 上下文一长就乱留意“Lost in the Middle”效应有段时间助手回答开始不稳定相关问题变多时答案经常忽略掉中间部分的资料。一开始我以为是模型能力问题后来查资料才发现这本身是长上下文模型的通病对开头的指令和结尾的输入记忆更强对中间的细节容易遗忘研究里称为Lost in the Middle。所以组装上下文时我把答案最关键、相似度最高的证据块放到整个上下文最后紧贴问题次重要的放开头那些补充性内容才放中间。这个方法我看过论文验证也在自己的项目里对比过回答准确率有明显提升。生产环境不需要和模型的天性做对抗顺着它的注意力分布来组织内容比强行堆更多资料有效得多。4.4 评估流于形式你的评测集可能在自欺欺人最后一个坑出现在我自认为已经做得很好的时候。评测指标全绿上线后真实用户却反馈不少问题。后来查出来我的评测集混入了大量来自原始训练文档的句子模型在这些问题上表现好是因为它在训练时“见过”类似的表述而不是系统真的检索对了。用不干净的评测集做评估等于每次拿答案去估分分数当然漂亮。修正办法是用人工从真实用户问题中积累评测样本并且每季度做一次样本更新防止数据分布漂移。另一个心得是评测不要只看正确率要把检索的召回来源打印出来人工抽看“召回内容是否真的对应了问题”。这个动作能帮你精准发现评测集在哪个环节造假及时纠正方向。回头再看这段从零搭建AI工程链路的经历我最深的体会是这个领域真正稀缺的不是模型能力而是把每个环节的基础做扎实的耐心。现在我看到一个新框架或者新模型第一反应不是追着上而是先想它接进我的五层链路里替代的是哪一层会不会让排查变得更透明。最后再分享一个小建议从零开始的第一版项目规模一定要小领域一定要窄但环节一个都不能省。小场景可以让你快速看清每层的问题全环节则能帮你建立完整的系统观和排查手感。这些才是面对AI应用生产环境变化时不会慌的真正底气。
返回列表