ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

RAG生产落地真正分水岭:数据源、检索、评估等六处关键细节全解析

RAG生产落地真正分水岭:数据源、检索、评估等六处关键细节全解析 聊 RAG 的人越来越多真正把它送上生产的却没几个。去年我自己搭第一条 RAG 流水线时LangChain、Embedding、向量库一套组合拳打下来三小时就通了。但跑通之后才发现那条流水线本身就是最不值钱的部分——加载、分块、向量化、检索、生成哪个教程里都有照着抄就行。真正拉开差距的是流水线之外那六个看不见的分水岭。这篇文章不打算再重复一遍“RAG 入门流程”而是想跟你把这六处掰开揉碎讲清楚。它们决定了你的 RAG 是玩具还是能抗住真实业务打磨的生产工具。如果你正准备在团队里落地知识库问答或者已经在用 Dify、LangChain 之类的工具搭 RAG却发现“demo 很香、上线即翻车”那这篇文章应该能帮你定位到问题出在哪一环。这六处我不会按教科书顺序写而是按我实际踩坑的频率排序数据源、分块、检索、查询理解、更新维护、评估调优。1. 数据源与知识形态你的RAG从起点就输了这条流水线的输入端是“文档”。很多人觉得只要能往知识库里塞文档RAG 就成了。但真实业务里知识源是五花八门的有 PDF 手册、有客服聊天记录、有 Excel 参数表、有 API 接口返回的结构化数据甚至还有图片。如果在一开始不区分这些形态后面再调分块和检索都是白费功夫。1.1 纯文本流水线只解决了一半问题最典型的情况团队拿一堆 PDF 往 Dify 里一拖选好 Embedding 模型就开始欢呼“知识库建好了”。但跑起来之后用户问“设备型号 A 和型号 B 的校准周期差多少”系统从两段被切断的表格文字里搜出几坨碎片答案是错的。问题出在经典 RAG 流水线是为非结构化文本设计的它对“关系”和“字段值”没有感知。而企业知识里大量是半结构化甚至完全结构化的东西——产品参数、维修记录、客户信息、告警规则。把这些东西硬塞进向量库相当于让一个只会读散文的人去查数据库表。所以我在做任何 RAG 项目前会先做一件事给知识源分类。大致分三类非结构化操作手册、制度文档、会议纪要、聊天记录适合经典 RAG。半结构化带表格的 PDF、Excel 报表、HTML 页面需要先做表格解析必要时转为 Markdown 或结构化行。结构化业务系统里的字段、关系、状态机这类数据更适合走 SQL 查询或知识图谱而不是向量检索。一句话先别急着分块和 Embedding先把“知识是什么形态”想清楚。这一步决定上限。1.2 结构化知识、知识图谱与ontology的取舍热词里有人搜“RAG 知识库和结构知识库区分”其实就是在问这个。我的理解很简单向量知识库存的是“文档原文”适合按语义找相似段落结构知识库存的是“事实和关系”适合做精确匹配和多跳推理。两者不是替代关系而是互补关系。比如有一条业务规则“当 A 设备的温度超过阈值时会自动触发 B 设备重启。” 这个关系如果放在普通文档里向量检索可以把它找出来。但如果用户问“哪些设备会因为温度告警而联动重启”你可能需要跨多个文档才能拼出完整答案。这种场景适合把实体和关系抽出来做成知识图谱。另外ontology 也是最近 RAG 圈子里重新被提起来的词。简单说ontology 就是一套领域概念模型设备属于资产类资产有维护记录维护记录包含故障类型。提前用 ontology 约束信息的组织方式图谱构建会更可控查询也能做得更准。但它同样有成本——建模、抽取、维护都需要人力。我一般只在实体关系密集、且多跳查询需求明确的场景才引入 GraphRAG 或 ontology 图谱普通文档问答就不必上了否则边际收益很低。1.3 “图片能不能进知识库”背后的多模态门道有人搜“RAG 知识库能存储图片嘛”答案是能存但光存图片没意义。原因很简单常规 Embedding 模型可以给图片生成向量但用户问问题的时候拿到的是一张图片文本模型根本“看”不了这张图只能看到一行图片路径。所以真正能落地的做法不是存图而是把图变成文本空间里可检索的东西。我常用的处理方式是这样图片里的文字先用 OCR 抽出来图片内容再用图像描述模型比如多模态模型生成一段摘要OCR 结果和摘要一起作为图片的“文本替代物”参与分块和 Embedding原始图片放进对象存储向量库里只存路径和摘要文本。这样用户问“那张设备的接线图里写了什么”系统能通过摘要和 OCR 文字把图片路径找回来再由多模态模型或前端把图展示出来。如果你没有多模态生成能力至少也要保证图片有完整的文字说明否则图片只会成为检索噪声。2. 分块与索引策略一个看似简单却决定下限的环节流水线里最不起眼的一步就是分块。大多数新手用的是固定窗口分块——设一个 chunk_size500按字符硬切。我自己一开始也这么干后来在合同审查场景里发现很多“答案明明在库里但就是找不到”的 badcase根因全在分块。2.1 为什么固定窗口分块不够用固定窗口最大的问题是它会打断语义的天然边界。一个段落讲完一个完整的维修步骤结果被拦腰切成两截一张表格的列名和值被分到不同 chunk一句话的前半段和后半段隔了 200 个字符。向量化之后这些碎片各自承担不了完整信息检索时很难被准确命中。我做过一次小实验同一批文档用固定 512 字符切块和用标题层级切块分别跑同一组问题。前者 Top-5 召回率大概只有 60%后者能到 80% 以上。代价是后者代码稍微复杂一点要先解析文档结构。所以我的建议很直接能按标题、段落、列表天然边界切就尽量按边界切。只有在文档结构不明显的场景比如聊天记录、长日志才用固定窗口而且要配合重叠区。2.2 父子块、滑动窗口、语义分块怎么选按边界切只是第一步。实际业务里还会遇到“这个问题需要看上下文才能回答”的情况。比如用户问“这个条款的例外情况有哪些”答案可能不在子块里而是在它所属的整个章节里。这时候有三个常用方案父子块小块负责被检索大块负责提供给 LLM。检索时先命中子块再返回对应父块让模型有足够上下文。适合条款、问答对这类引用型场景。滑动窗口在固定窗口基础上向前后各扩展一部分重叠内容。实现简单能缓解句子被切断的问题但窗口大小要调太长了噪声多。语义分块用 Embedding 的相似度把句子逐步拼成语义完整的块。质量最高但计算成本也最高适合离线处理不适合高频更新。我的经验是不要迷信某一种方式。大多数生产级 RAG 用的是组合拳先按文档标题层级做结构分块再对小 chunk 做滑动窗口重叠最后把父块 ID 存进 metadata。这样检索和生成各取所需。2.3 索引不是只有向量倒排索引、关键词与稠密检索的双轨这是另一个被大量教程忽略的点很多人把“RAG”默认等于“向量检索”。但专有名词、型号、人名、工单编号这类信息向量检索效果并不好。你拿 Embedding 去查“设备型号 PLC-3000”经常不如直接 CtrlF 来得准。所以现在主流做法是混合索引同时建向量索引和倒排索引比如 BM25。检索时两条路都走再合并结果。加上元数据过滤效果会有明显提升。我常用的索引流程是文档分块后每块存原始文本、向量、metadata来源、标题、父块 ID 等关键词用 BM25 建立倒排索引或者直接用支持全文检索的数据库查询阶段向量召回 Top-50BM25 召回 Top-50合并去重后统一送 Rerank。这段可以用一个伪代码示意# 混合检索示意 def hybrid_search(query, top_k20): vector_hits vector_store.query(query, top_k50) keyword_hits bm25_index.search(query, top_k50) candidates merge_and_dedupe(vector_hits, keyword_hits) reranked reranker.rank(query, candidates, top_ntop_k) return reranked关键字和语义双轨跑下来能解决掉至少一半“明显该搜到却搜不到”的问题。3. 检索质量的分水岭从“召回”到“排准”流水线里大家喜欢把 Top-K 设成 4 或 5然后把招回来的 chunks 一股脑塞给大模型。但 Top-K 里的内容有没有用排序准不准很少有人量化。真实场景里检索阶段才是效果差异最大的地方。烂大街的 demo 只做到“能召回”生产系统要做到“排得准”。3.1 向量检索为什么经常召不回关键信息先说一个问题向量相似度不等于相关性。Embedding 模型是按语义向量空间来做相似度计算的它对否定句、数字条件、逻辑关系都不敏感。比如用户问“哪些设备不需要定期校准”向量检索很容易把“需要校准”的段落排到前面因为语义上它们和“校准”太近了。再比如用户问“上季度某客户投诉了三次”如果文档里写的是“2023Q3 该客户有 3 起投诉”Embedding 也不擅长做这种跨表达的数字对应。这是模型的天然短板靠换一个更大的 Embedding 模型能缓解但不能根除。还有一个经常踩的坑Top-K 设太大。K5 时还好K10 以后噪声概率明显上升K 太小又容易漏。生产上我的做法是召回阶段先拿大候选集比如 50 条再靠重排序模型把真正相关的 3 到 5 条顶到前面而不是直接用向量相似度截断。3.2 混合检索与重排序把向量、BM25和Rerank组合起来标题里说的“分水岭”在这里体现得最明显。一个完整检索链路通常有三层向量召回找语义相近的内容。关键词召回找精确匹配的专有名词、编号。重排序用 Rerank 模型对合并后的候选集做精细打分。前三层里Rerank 是最容易被省掉的。很多人觉得有向量就够用但实际对比下来效果差很多。我用 bge-m3 做 Embedding、用 bge-reranker 做重排在同一组测试集上仅向量的 Recall5 大概 70%加上 BM25 后到 78%再加上 Rerank 后到了 88%。代价是每条查询多 100 到 300 毫秒但对多数内部知识库场景完全可以接受。Rerank 模型本质是一个 Cross-Encoder会同时把 query 和候选 chunk 拼接起来打相关性分所以比纯向量相似度准得多。如果你的候选集已经压到 20 条左右重排很快如果候选集 100 条还是要先粗排一遍再重排。3.3 字段过滤、元数据过滤和多路召回的实际配置这一节是实操细节。索引数据时保留 metadata 非常关键。我做知识库时每一条 chunk 至少会带这些字段字段示例用途document_iddoc_001关联原始文档source产品手册_2024版来源展示doc_type手册 / FAQ / 条款按类型过滤department售后部按部门权限过滤versionv2.1版本过滤parent_idchunk_88关联父块检索时把这些 metadata 当过滤器用。比如用户问售后服务问题filter 直接指定 doc_typeFAQ 和 department售后部能减少一半以上无关候选召回质量更稳。多路召回则适合多知识源场景。比如同一个助手同时对接 FAQ 库、产品手册库、订单系统库。我会按 query 的意图路由到不同通路FAQ 类问题主走关键词说明书类问题主走向量订单数据直接走数据库 SQL。把所有结果合并后统一重排。这里最难的不是技术而是怎么设计路由规则。先用 LLM 做粗粒度意图分类再用规则兜底比纯靠 prompt 更靠谱。4. 查询理解与多轮对话让RAG知道用户真正在问什么很多 RAG 应用只做到“拿用户当前说的话去检索”这在单轮问答里勉强能跑一旦遇到多轮对话和口语化表达效果直线下降。你搜“RAG 实战”里的教程很少会教查询理解但它是个真正的分水岭。4.1 直接拿用户问题去检索是最常见的错误用户不会像搜索引擎那样输入标准关键词他们说的是大白话还经常带着指代。比如对话历史里聊的是“那份合同”下一句问“它什么时候到期”如果直接把“它什么时候到期”拿去检索系统根本不知道“它”指哪个合同。很多 badcase 不是召回没做好而是查询本身就被理解错了。我在生产项目里的做法是在检索前加一个查询理解层。这层通常承担三个任务意图识别判断这个问题需要不需要检索。查询改写把口语、指代、省略补成适合检索的表达。对话上下文压缩把前面若干轮对话浓缩成当前问题需要的背景。4.2 查询改写、意图识别与对话压缩查询改写我会交给大模型做prompt 大致长这样你是一个查询理解模块。根据历史对话和当前用户问题输出一个独立的自包含检索查询。 要求 1. 补全指代词例如“它”“那批”“这个” 2. 保留关键约束例如时间、型号、状态 3. 不添加知识库中不存在的事实 4. 输出 JSON{search_query: ..., need_search: true/false}实际运行时我不会只把改写后的 query 拿去检索而是把原 query 和改写后的 query 做加权混合召回。原因是 LLM 改写有时候会“脑补”过头把用户本意扭曲了。原始 query 至少能保证覆盖用户字面意思。多轮对话压缩也是一样道理不是把所有历史都塞进去而是让模型把历史里与当前问题相关的实体、时间、状态抽出来拼进当前查询。这样可以省 token也更聚焦。4.3 什么时候该检索什么时候不该检索——拒答与跳过机制“需要检索才检索”这句话看着像废话但大多数 RAG 系统都没有这个判断。用户上来问一句“你好”系统都要跑一遍 Embedding检索Rerank把一堆不相关的东西塞给模型。不仅浪费算力还会让模型被无关片段带偏。所以查询理解层里我专门加了一个判断need_search。只有用户问题涉及知识库内容时才走检索链路。如果是闲聊、常识、或者无法回答的问题就直接走通用对话或拒答。拒答也很重要知识库里没有答案时不要强行编可以在 prompt 里设置“找不到相关材料时明确说明‘当前知识库未覆盖该问题’”。这一步能把幻觉率压下来不少。5. 知识更新、去重与存储管理动态知识库的日常维护我这里说的“动态”是指知识不是静止的文档会改版、会过期、会删除。很多人搭 RAG 时只在第一次导入文档跑了一遍之后就再也没更新过。等到业务方发现答案过时了才想起来“哦要重新导入”。这个分水岭决定你的 RAG 能不能长期跑。5.1 增量更新、删除和版本回滚怎么设计向量数据库支持 upsert但不是简单“插一下”就完事。要做的其实是一套同步机制定期扫描源目录或源系统计算文档 hashhash 没变的跳过变了才重新解析和 Embedding删除掉的文档要连向量和元数据一起清否则残留旧答案每次更新打一个版本号发现新版本效果更差时可以按版本回滚。我最初做增量更新时直接全量重建索引一个几万段的知识库要跑四五个小时非常不现实。后来改成按 document_id 做增量更新每次只处理有变化的文档耗时降到分钟级。另外要注意 Embedding 模型版本。如果换了 Embedding 模型理论上所有向量都要重新算否则新旧向量不在同一语义空间相似度不可比。这个问题很容易被忽略。5.2 重复文档与“近重复”内容的清洗重复数据是知识库的隐形杀手。同一个文档被导入三遍只是文件名不同用户提问时 Top-5 里可能全是同一份材料的副本挤占了其他有效回答的位置。我一般分两步清洗精确去重按文件内容 hashMD5 或 SHA-256 都一样就只索引一份。近重复去重用 SimHash 或者 Embedding 相似度超过 0.95 的可以视为近似重复再做人工或规则判断。清洗放在入库前更新时也要做。不然知识库越滚越大检索效果反而越来越差。5.3 存储选型向量数据库、关系库、对象存储各管哪一段这个本来是架构问题但直接影响 RAG 的可靠性。我的存储划分是这样的存储组件存什么说明向量数据库向量 文本片段 metadata负责检索主体如 Milvus、Qdrant、pgvector关系库 / 文档库文档源信息、版本、去重状态、任务日志管理知识库元数据便于审计和回滚对象存储原始文件、图片、附件承接多模态内容和原始证据当然数据量小的时候不用这么复杂。几万段以下用 SQLite 向量插件或者在 Dify 自带知识库里都能跑。但一旦你准备上生产建议按这个思路拆开不然以后“哪个版本在用、原始文件在哪”都会变成灾难。6. 评估与调优没有指标的RAG都是碰运气最后这个分水岭是我见过最多团队栽跟头的地方。很多人上线 RAG 靠“感觉”问几个测试问题觉得还行就推给业务方。结果业务方一用全是毛病。没有评估集和指标你根本不知道改动是变好了还是变坏了。6.1 离线评估召回率、MRR、Faithfulness怎么测做评估之前先得有一份评估集。我一般从真实用户问题里挑 100 到 200 条让业务方标注两样东西一是相关文档片段标准召回结果二是标准答案。然后以这份评估集为基准跑离线测试。几个常用指标我简单解释RecallK标准答案所在的片段有没有出现在 Top-K 里。这是 RAG 的底线指标召回都没有生成一定不对。MRR标准答案排在第几位取倒数再平均。衡量排序质量。Faithfulness生成答案是否忠于检索到的上下文有没有自己编内容。可以人工判断也可以用大模型做参考评估。评估代码不算复杂比如算 RecallKdef recall_at_k(retrieved_ids, relevant_ids, k): retrieved set(retrieved_ids[:k]) return len(retrieved relevant_ids) / len(relevant_ids) if relevant_ids else 0.0重点是每次改动分块、检索、Rerank、prompt都要跑一遍同样的评估集形成对比记录。我曾经靠这个方式发现某次把 Top-K 从 5 调到 3Faithfulness 涨了 5 个点但召回下降了 8 个点最后综合判断还是保留 K5 更合理。6.2 在线效果追踪用户反馈与badcase回溯离线评估不是全部线上用户反馈同样关键。我在系统里会埋一个简单的反馈按钮用户可以对每次回答点“有用”或“没用”。每次请求同时记录 query、改写后的 query、召回的片段 ID、Rerank 分数、最终答案。这些记录存到一张日志表方便后面做 badcase 分析。收集到 badcase 之后按原因分类排查badcase 示例可能原因排查方向答案遗漏关键细节分块截断了上下文检查父块策略答案明显错误召回结果不相关检查混合检索、过滤条件答案啰嗦且跑题贪多塞了太多不相关片段降低 Top-K强化 Rerank同一个问题两次答得不一样生成随机性检索不稳定检查检索排序是否稳定固定 prompt 和模型参数每周做一次 badcase 聚类比天天调参数有效得多。6.3 从分水岭到工程化一套可落地的调优路径我这几年下来总结出一个调优顺序供参考先补数据覆盖看 badcase 是不是知识库里根本没这个东西。没有数据怎么调都没用。再调分块按结构切分做父子块解决上下文断裂问题。再调检索混合召回Rerank解决“找得到但排不对”的问题。再调查询理解处理口语化、指代和多轮。再调 prompt约束幻觉、格式、拒答。最后才是换模型换更大的 Embedding 或生成模型收益不一定这么明显但成本上升很快。每一步只动一个变量并且用评估集做回归。不要一次改三处出了问题你根本不知道是谁的锅。RAG 优化不是玄学靠的是一份评估集加一套可重复的实验流程。这套“六处”的框架是我自己从多个项目里踩坑踩出来的。不同场景可能侧重不一样比如纯 FAQ 客服可能更吃查询理解垂直领域问答更吃数据结构和分块。但大方向是稳定流水线人人都会搭真正让 RAG 从“能跑”变成“好用”的永远是这些流水线之外的破事。最后再分享一个很个人化的经验如果你现在只准备做一件事先去做评估集把 badcase 攒起来。光有这条你的 RAG 迭代速度就能超过 90% 的同行。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进