ARTICLE DETAIL

资讯详情

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

从零构建AI工程体系:RAG知识库问答系统全链路实践

从零构建AI工程体系:RAG知识库问答系统全链路实践 1. 为什么说从零开始才是AI工程师的第一课这几年我面试了不少人简历上都写着熟悉AI工程化会大模型应用开发能跑通RAG流程。但真到实际项目里问题往往出在同一个地方大家太习惯直接拿现成的框架和平台往上摞遇到问题两眼一抹黑。尤其是这类题目——从零构建一套完整的AI工程体系——很多人不是不会做而是根本不知道应该从哪里下手。ai-engineering-from-scratch这个命题核心不是让你复刻一个AI产品而是让你把一条完整的AI工程链路亲手搭一遍数据从哪来、怎么清洗、怎么切分、怎么变成向量、模型怎么接、检索怎么调、答案怎么生成、系统怎么部署、指标怎么度量、线上出问题怎么排查。每一步都自己动手而不是import一下完事。说白了从零开始做AI工程最终练就的是三样东西判断力知道每个环节为什么这么设计遇到问题能定位到具体层而不是盲目调参。控制力不需要依赖某个平台黑盒所有模块都可以按需替换、升级、降级。成本力明白哪些环节必须上重型工具哪些用简朴方案就能解决钱花在刀刃上。这篇文章会以一个非常典型的场景为载体——从零搭建企业内部知识库问答系统RAG——把AI工程从无到有的完整过程拆开揉碎讲一遍。里面所有步骤、参数、代码思路都是我实际动手验证过、替换过的方案不是纸上谈兵。需要说明的是文章里出现的模型、框架、参数我更想传递的是这个位置应该考虑什么、为什么这样选而不是让你把我用过的参数原封不动抄走。每个团队的数据、算力、业务形态不一样方案一定要跟着场景走。适合看这篇文章的人刚入门想系统理解AI工程链路的同学、已经在做应用开发但对底层细节模糊的同行、以及需要在团队里从0拉起一条AI业务线的技术负责人。2. 从零搭建前的整体设计与选型思路2.1 先别碰代码把需求拆成工程模块我见过太多人拿到需求第一件事就是先装个LangChain跑起来。这个顺序是错的。从零做AI工程的正确打开方式是先把业务问题拆解成你可以单独设计、单独验证的工程模块。以企业内部知识库问答为例即使只做一个MVP也需要下面这些模块模块职责核心问题数据接入从不同数据源网页、文档、数据库拉到原始内容格式怎么统一数据清洗去重、去噪、保留语义完整度哪些内容该删文档切分把长文本切成适合向量化的片段chunk多大合适向量化把文本变成embedding向量选什么模型、怎么批量处理向量存储与检索存向量、按相似度召回用什么库、TopK怎么定重排对召回结果二次排序Rerank模型怎么用生成把检索结果交给大模型组织答案上下文怎么写、幻觉怎么控服务封装把链路封装成API异步还是同步、流式怎么处理评估回归验证每次改动是变好还是变坏标准数据从哪来这一步做完你就把做一个问答系统这个模糊需求变成了一个一个可以平行开工的工程任务。这个拆解是AI工程和AI算法最大的区别算法关注单点精度工程关注整条链路的稳定和可控。2.2 技术选型为什么走轻量起步、按需升级路线技术选型是最容易翻车的地方。我记得有一次一个小伙伴上来就上了Kubernetes集群结果总共就一个服务两个模型光维护集群占掉一半精力。从零起步做AI工程我的原则非常明确能用SQLite就不上PostgreSQL能用单机就不上分布式能用小模型就不上大模型一切升级都等瓶颈出现了再说。具体到本篇案例的选型服务框架用FastAPI异步支持好文档自动生成和Python生态无缝衔接调试成本低。向量存储先用SQLite的向量扩展sqlite-vec数据量在百万级以内完全够用。等向量超过百万、需要分布式召回时再平滑迁移到Milvus或者pgvector。Embedding模型选通用型中文/多语言开源模型向量维度控制在1024以内兼顾效果和存储成本。生成模型优先考虑可本地私有化部署的开源模型。内部知识库涉及公司资料数据监管要求决定了数据不能出内网这一点从第一版就要想清楚。编排层尽量少用重型Agent框架第一版用脚本函数就能搞定链路清楚问题也好定位。这套组合的最大好处是依赖少、能跑通、每行代码都看得懂。等你真正跑到瓶颈自然知道该往哪个方向升级那时候再换工具就是水到渠成的事。2.3 从零构建相比直接使用成熟平台的价值差异有一些人问我市面上明明有那么多现成的RAG平台、AI应用开发框架甚至拖拽式的工作流工具为什么还要自己从零搭一遍这个问题我在带团队时经常正面回答过。平台解决的是提效问题不是能力问题。你用平台把系统跑起来了但平台的封装让你看不到内部的失败模式。当检索召回质量差、答案出现幻觉、链路响应慢的时候你对着平台配置界面是无从下手的。可控性决定了成本天花板。成熟平台往往按调用量、按token计费业务量上来之后每一次检索和生成都是边际成本。自己搭的体系模型可以量化部署、检索可以用缓存削减、生成可以控制上下文长度每一分优化都是成本优势。团队能力的沉淀路径不同。自己从零搭一遍团队里至少会有两三个人对全链路每个环节都心里有数。这种人才是AI项目里真正稀缺的。当然不是所有场景都适合从零自研。如果你们是几十人的非技术驱动团队业务验证时间窗口非常短那用成熟平台快速出Demo完全合理。但如果你要做的是一个长期演进、要求数据可控、需要持续深度优化的AI系统千万别跳过从零搭建这一步哪怕只是为了获得那份透彻的理解。3. 核心细节解析数据、向量化与检索是RAG的命根子3.1 数据清洗与切分这一步决定了检索质量的上限做RAG的人经常有个误区模型选得够大Prompt写得好效果就一定好。实际上经验会告诉你**检索不到正确的信息模型再强也答不对。**而检索效果的上限在你落盘向量库的那一刻就已经定了。企业内部知识库的数据形态极其混乱有PDF、有Word、有HTML页面、有Excel表格、甚至有些内容藏在图片里。第一版的清洗流程我建议至少做这几件事统一正文抽取PDF用解析工具抽文本层Word用python-docxHTML用BeautifulSoup去标签表格导出成Markdown格式保留结构。去重去噪文档里常年的页眉页脚、重复声明、无效空行统一用正则表达式清理掉。这里注意不要用一条正则打天下要针对不同来源写不同的清理规则。超链接与引用处理正文里经常有详见XXX部门制度文件这类指引语切分后和具体内容分开会造成检索到了但答案不完整。我建议把链接目标指向的信息在预处理阶段就近拼接进去。切分策略我实测下来最稳定的配置是chunk_size设为512个字符左右中文场景按字符数算overlap设64个字符。这个数值不是玄学它意味着每个片段大概覆盖一页文档核心内容同时重叠区能保证句子不被拦腰截断。切分还有一条铁律永远不要让一个chunk跨过文档的语义边界。比如说一个完整的表格、一段代码块、一个章节标题下的全部内容如果被硬切到两个chunk里语义就碎了。我的做法是先按Markdown标题切出语义块再在语义块内部按大小二次切分。代码实现上优先选择按结构递归切分的方式而不是纯按长度硬切。这里补充一个容易踩坑的地方用中文模型做embedding时标点符号和特殊字符的清洗顺序会影响切分质量。我的经验是先抽正文、再统一换行符、再做空行压缩最后按结构语义切分。顺序乱了比如先按长度切然后再清洗很容易留下大量半截句子。3.2 Embedding模型的选择与批量向量化实操Embedding模型是整个检索链路的编码器它的输出质量直接决定了向量召回的天花板。选择时我建议看三个指标它在目标语言中文场景就是中文的语义相似度评测表现、向量维度大小直接影响存储和计算成本、以及商业化授权是否允许私域部署。我实测下来通用场景优先选中文和英文都能覆盖的多语言模型。这类模型的向量分布对中英混合的文档比较友好企业内部文档经常中英文夹杂单一语种的模型在这种场景下表现会差不少。另一个容易被忽略的点是查询query和文档document是否需要不同的编码方式。有些模型专门区分了query和passage的编码前缀拼接字段时别搞反否则检索效果会明显打折。实际操作上我习惯把向量化写成独立的批处理任务而不是在线实时逐条调用。核心原因是embedding模型推理在CPU上也能跑但批量处理时GPU的吞吐优势非常明显。举个例子一个一万条文档的知识库切成三万个chunk批处理向量化通常半小时内能完成但如果写成在线一条条调用你的应用服务会被拖到没法用。批量向量的代码思路大概是这样的# 伪代码核心是控制batch_size和处理速度 def vectorize_chunks(chunks: list[str], model, batch_size64): all_embeddings [] for i in range(0, len(chunks), batch_size): batch chunks[i:i batch_size] embeddings model.encode(batch, normalize_embeddingsTrue) all_embeddings.extend(embeddings) return all_embeddings这里有两个细节值得强调一是normalize_embeddingsTrue务必打开这样后续可以用余弦相似度计算而且提前归一化之后内积等价于余弦相似度召回性能会好很多。二是批处理大小不是越大越好batch_size过大可能出现显存溢出或单批处理时间过长建议按自己机器的显存实测调整。3.3 向量检索的TopK设置与Rerank的必要性向量检索的原理并不复杂把用户query向量化之后在向量库里按相似度取前TopK个chunk。但工程上一个常见问题是TopK设置多少合适我第一版习惯直接设20理由很实际20个chunk大概率能覆盖正确答案所在的位置同时不会给生成模型塞过多的无关上下文。但这带来一个奇怪的现象检索结果前几名经常是看起来相关但不精准的噪声。原因在于纯向量检索是语义层面的模糊匹配它擅长发现话题相关不擅长精确判断能否回答问题。这个差距就需要Rerank模型来弥补。Rerank的思路和embedding检索有本质区别embedding是把文本压缩成向量再算相似度这个压缩过程必然损失细节而Rerank是直接把query和每个候选chunk拼在一起用交叉编码器逐个打分精度高一个量级。代价是推理成本高所以Rerank的输入一定是经过一轮粗召回之后的候选集比如Top20重排后只保留Top3到Top5。我把这个链路总结成一句话粗召回用向量保证召回率精排序用Rerank保证准确率。这个漏斗结构是RAG工程效率的核心。工程上还有一个优化技巧Rerank排序之后不要直接把得分当作可信度因为Rerank模型的分数分布不是一个标准化的概率值。我在实践中会额外做一个规则校验比如判断query中的关键词是否真的出现在候选chunk里这种轻量规则能滤掉不少幻觉来源。4. 生成侧的工程实现上下文组装、幻觉控制与流式响应4.1 Prompt模板与上下文组装策略检索到的chunk如何送进大模型这是RAG工程化和学院派做法拉开距离的地方。很多人直接在System Prompt后面拼接一堆检索内容效果波动很大。我总结了一套三层结构化拼接的写法角色与任务层明确告诉模型你是知识库助手只能依据给定资料回答资料里没有的信息要明确说未找到相关信息不要编造。资料与引用层把重排后的chunk按顺序编号要求模型在回答时标注信息来源编号如[1][2]这样既方便验证也能抑制幻觉。约束与输出格式层明确回答长度、是否允许分点、遇到多问题如何拆解。三层拼接的顺序和措辞会直接影响模型的服从性。这里有一个亲测有效的细节资料层的起始标记要和用户问题之间做明显区隔比如用类似context标签包裹模型经过指令微调后对这种结构化输入更敏感。无脑用自然语言描述以下是参考资料效果会差一些。上下文长度的问题是另一个重灾区。大模型的输入有窗口限制你的chunk总长一旦超过窗口比例轻则报错重则被静默截断导致答案残缺。我的习惯做法是估算max_tokens时留出30%的冗余保证模型有足够的输出空间一旦发现上下文总长可能超标优先裁掉排名靠后的chunk而不是压缩每个chunk的内容。4.2 幻觉问题的工程化控制手段做RAG最头疼的问题就是幻觉。模型一本正经地编造知识库根本没有的内容而且编得很有说服力。这里需要从工程侧而不是模型侧做预防第一道防线是提示词约束。但不是简单地说不要乱编而是给模型一个明确的放弃路径检索内容不足以回答时直接输出未在知识库中找到相关内容。这个路径越明确模型编造的概率越低。第二道防线是引用绑定。要求模型必须引用资料编号并且在解析阶段做一致性检查——如果模型在回答里引用了[3]但[3]的内容和回答观点不匹配那这个问题就要被打回重做。这道检查用规则代码就能实现不需要上模型判断。第三道防线是内容校验器。用一个轻量模型或者同一模型做二次判断给定问题和回答判断回答的事实是否都在给定的资料集内。这个环节类似陪审团复核实测能把幻觉率再压下去不少尤其适合问答类场景。这道防线不是所有场景都要上。如果只是内部辅助问答人工会核对关键信息那前两道就够如果是面向客户的自动回答三道都建议保留。4.3 服务封装同步、异步与流式响应怎么取舍RAG链路全部跑通之后工程上要解决的最后一个关键点是怎么把链路稳定地暴露给外部调用方。我做过几个版本的迭代最大的体会是不要把大模型HTTP调用包在一个普通同步函数里不管不问。同步短请求适用于内部工具、低并发场景代码最简单。缺点是模型推理慢的时候服务线程会被占住并发一上来就雪崩。异步任务队列适用于耗时长的批量任务。把请求丢进队列立刻返回任务ID前端轮询结果。我用Celery加Redis跑过稳定可靠。流式输出适用于聊天体验要求的场景。大模型本身支持流式token输出接口层如果用WebSocket或SSE转发用户可以边看边等心理体感快很多。我的建议很直接第一版老老实实用同步接口把功能跑通第二步再考虑流式输出优化体验异步任务队列看场景不是所有RAG服务都需要。不要为了架构完整而上全套我也犯过这个毛病。我实际交付过一个给运营团队用的知识库问答系统第一版就是同步FastAPI接口单机扛几十个内部用户毫无压力。后来业务量涨了再把生成部分改成异步加队列。循序渐进问题暴露一个解一个最后系统长什么样你心里清清楚楚。5. 从零部署一套可回归的评估体系5.1 为什么AI工程必须有自己的回归测试传统软件工程有单元测试、集成测试代码改动后跑一遍回归测试确保没把已有功能改坏。AI工程在这件事上天然落后因为它的输出是概率性的同样的输入两次调用结果可能不同。但从零构建的最大价值就是逼你把评估体系也亲手搭起来。我见过太多AI项目死在同一个地方每个人的口头反馈都是感觉回答质量变好了/变差了但没有人能量化这个感觉。没有量化就没办法做回归做不了回归就没办法放心地迭代模型、调参数。我的做法是维护一个黄金评测集Golden Set收集100到200条真实用户问题为每个问题标注标准答案或至少标注期望答案里必须包含的要点。评测集的质量比数量重要。我的三条筛选标准覆盖高频业务问题至少一半以上来自真实用户提问记录。刻意加入边界Case比如多条件组合问题、否定式问题、知识库中根本没有答案的问题。定期更新每周结构性地把线上涌现的疑难问题补充进去。这个标准集是AI工程里最值钱的数据资产。有了它你每次改动——不管是换Embedding模型、调chunk参数、改Prompt还是换生成模型——都能跑一遍评测量化对比改动前后的效果。5.2 常用的客观评估指标与计算方式RAG问答系统的评估指标不能只用大模型打分那太主观了。我通常分三个层面看检索层面核心指标是召回率RecallK和准确率PrecisionK。通俗解释就是在返回的前K个chunk里有多少个是真正包含答案内容的。这个指标完全可以用规则方法打分——只要标准答案句子和chunk有重叠就认为命中。计算方式很简单def recall_at_k(retrieved_chunks, golden_answer): hit sum(1 for c in retrieved_chunks if c.contains_any(golden_answer)) return hit / len(retrieved_chunks)生成层面核心指标是答案正确性和忠实度。业界常用做法是让GPT等大模型作为裁判对比模型生成答案和标准答案之间的语义相似度同时判断生成答案是否忠于给定的检索内容。这类打分有开源的评测工具可以用建议至少选一种跑通。工程层面首token延迟、总响应时间、请求成功率、上下文截断率。这些指标直接决定用户体感和系统稳定性。我见过不少效果很好的RAG项目因为应答太慢被业务方放弃所以这项千万别省。评估跑完后结果必须归档。我在工程里会维护一张历史评测记录表记录每次改动的版本号、变更内容、三项指标的变化。这张表是所有AI工程决策的底座你后面换模型、调参数时翻出来看才不会走回头路。5.3 基于评估结果的迭代标准有了评估体系还需要一个明确的迭代标准否则评测做了也白做。我的迭代原则是检索指标没有下降的情况下生成指标提升才算真提升检索指标下降了生成指标提升再大也要谨慎合入。原因很好理解检索是整个系统的上游上游召回变差哪怕这次生成模型恰好把答案凑对了也只是巧合。一旦换一个问法问题就会暴露。这个原则帮我挡掉了不少看似有效实则脆弱的改动。另外每次跑完评测不要只看总分要把Bad Case一条条拉出来看。总分提升1%可能只是锦上添花但某个关键场景的分数掉了一半那才是隐患。我在评测脚本里会专门输出分数下降最多的Top10样本每次迭代后先看这部分再决定是否合入。6. 部署上线与线上可观测性的工程实践6.1 模型服务的容器化与依赖管理到了部署这一步很多自己从零搭系统的人会踩同一个坑代码在本机能跑一到服务器就各种报错。根因基本都是依赖环境不一致。我的建议是从第一天就引入容器化让环境成为代码的一部分。一个最小可用的Dockerfile思路是这样的基础镜像固定Python版本比如3.11-slim不要用latest标签。依赖文件用requirements.txt锁定版本关键库torch、transformers建议精确到小版本。模型权重不打进镜像通过启动脚本挂载或下载这样镜像体积小、启动快。应用进程用非root用户运行这是基本的安全实践。如果你同时有向量库、缓存、模型服务等多个组件docker-compose是性价比最高的编排方式。我用docker-compose管理过Embedding服务、Rerank服务、应用API、向量库四个容器启动一条命令日志统一收集非常适合中等规模项目。6.2 性能优化与成本控制的组合拳模型部署上线之后的下一步就是解决性能和成本问题。我实测有效的手段按优先级排序如下这些我都试过模型量化是投入产出比最高的优化。把生成模型从FP16量化到INT4或INT8显存占用直接砍半甚至更多推理速度翻倍而回答质量在大多数内部场景几乎无感。量化工具用开源方案就能做得很成熟建议尽早引入。缓存是成本杀手。企业内部知识库问答有一个特点大家问的问题重复度很高。把命中缓存的请求直接返回能省掉90%以上的重复推理成本。我在Redis里做了带语义相似度的缓存方案实测命中率相当可观。上下文长度压缩带来的token成本节约同样明显。交付时可以用Prompt压缩组件把检索内容的冗余部分删掉哪怕只压缩20%累积下来也是很大一笔钱。注意压缩不能丢关键信息所以这一步要在评测集上回归验证后再上线。批量处理非实时场景。如果系统支持离线批量问答比如定期生成知识库摘要、报告等就不要走在线推理把请求排队成批处理GPU利用率会大幅提升。我踩过的最大的坑是只盯着延迟优化完全不顾成本结果模型越换越大推理越来越贵单次问答成本翻了几倍。延迟、成本、效果三者永远是三角制约关系上线前务必明确你最在意哪个维度再决定优化方向。6.3 全链路可观测性日志、指标与告警AI工程服务和传统后端服务有一个巨大的不同前者的每一个线上错误都可能不是报错而是静默地答错。所以可观测性不是锦上添花而是AI工程的必需品。我落地的方案是三层立体观测结构化日志每一次问答请求在入口生成一个request_id全链路检索、Rerank、生成、校验都在日志里带上这个ID。这样用户说刚才那个答案不对的时候你能顺着ID把每一步的中间结果翻出来。性能指标用Prometheus统计接口QPS、延迟分布、token消耗量、检索命中率等。其中最值得盯的是检索Top1命中率它下滑通常意味着知识库里的内容更新了但向量索引没同步。质量指标线上用户可以对回答点赞/点踩点踩的样本自动进疑难Case池每周抽出来补充进黄金评测集。这一步让评估体系有了持续迭代的活水而不是一潭死水。有人说这个体系太重了小项目不需要。我的看法是你不需要一上来就把三样全做齐但至少要在一个地方留下结构化日志。否则出了问题AI系统的排障难度会远超传统系统——你根本不知道是模型答错了、检索没找到、还是数据没更新。7. 常见问题与排查技巧实录在这一节我把从零搭建AI工程时最常遇到的典型问题整理成速查表。每个问题都是我或我的团队在实际项目中真实踩过的排查思路也经过验证现象可能原因排查路径检索召回的内容明显不相关chunk切分不合理 / embedding模型选型不对先看切分后的chunk是否语义完整再替换embedding模型做评测对比答案看起来流畅但事实错误幻觉 / 检索没有召回正确内容先看引用编号是否能对应再用内容校验器判断忠实度首token延迟很高模型太大未量化 / 上下文拼接过长优先做模型量化检查上下文长度是否超窗口比例并发一高接口就超时同步请求占满线程 / 生成模型推理堆积改为异步/流式必要时加队列削峰同一个问题两次答案差异很大采样参数温度过高 / 检索结果波动降低temperature检查向量检索是否有随机性更新了知识库文档但系统答的还是旧内容向量索引未重建 / 缓存命中先确认索引库同步任务是否执行再检查缓存策略Prompt改了之后某些场景变好、某些变差评测集覆盖不全把变差的Case补进黄金评测集跑完整回归再定模型服务偶尔OOM崩溃并发推理占用显存超限 / 无排队机制加推理并发限制设置最大等待队列必要时模型分片另外分享三个独家排查心得第一在追查AI系统答错问题时先看检索结果再看模型输出。90%的答错是检索环节出的问题模型只是忠实地基于错误信息作答。一上来就怀疑模型是不对的。第二所有线上问题的复现都要保留当时的完整上下文。我会在日志里记录检索命中的chunk内容和模型输出并把这些内容做版本快照。没有快照你改了代码、换了模型之后当时的错误可能永远无法复现。第三善用最小化复现的原则。当某个问题始终说不清楚时我会把链路拆到最细单独测试向量检索这一步的召回结果、单独调用生成模型看输出、单独检查数据切分逻辑。三分法定位问题比闷头调参快十倍。8. 从零到一是地基后续扩展是高楼很多人问我不写论文不搞算法做AI工程的价值到底在哪。我的回答是这个时代AI模型的能力已经很强真正稀缺的是能把模型能力稳定、可控、低成本地落地到业务里的人。而这样的人几乎都有过从零把一个AI系统搭起来的经历。我自己在带团队的时候也刻意保留一个习惯每个新成员入职的第一周我不让他们碰线上业务代码而是从零搭一个最小可用的问答链路。这个过程不为了产出什么就是为了让每个人亲手体验一遍数据、向量、检索、生成、评估、部署这个完整循环。只有当你亲手打通一次全链路你对AI工程的每个环节的敬畏感和掌控感才会真正长在你身上。最后再分享一个我个人的实操心得从零搭建时一定要养成每一步都留一个可验证的中间产物的习惯。文本切分后抽样看结果、embedding后挑几个句子看相似度、检索后打印召回内容、生成后记录引用出处。这些中间产物就像工程的脚手架平时看着多花时间但每次系统出问题、每次优化迭代它们都是你最可靠的参照物。这篇文章里的每一个环节都可以单独展开成一整篇长文。数据切分、向量选型、评测集构建、模型量化、缓存设计每一块都有大量细节值得深挖。但框架和主线就是这样从问题拆解到技术选型从数据链路到生成控制从部署上线到持续评估。把这条主线走通你就真正入了AI工程的门。
返回列表