ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

RAG调优六大分水岭:从玩具到生产力工具的实战指南

RAG调优六大分水岭:从玩具到生产力工具的实战指南 1. 为什么“RAG 烂大街”是个伪命题这两年但凡跟大模型沾点边的团队几乎人手一套 RAG 流水线。文档切片、向量化、存库、检索、拼 prompt、丢给模型生成六步走完一个“知识库问答”就上线了。于是圈子里开始流行一句话RAG 已经烂大街了。我一开始也这么觉得。直到去年帮三个不同行业的团队做检索增强的调优才发现一个扎心的事实烂大街的从来不是 RAG 本身而是那条被无数教程抄来抄去的最简流水线。真正决定一套 RAG 系统是“玩具”还是“生产力工具”的是流水线之外那六处分水岭。这篇文章就把这六处掰开揉碎讲清楚顺带把 Agentic RAG、Contextual Retrieval、GraphRAG、LangGraph 这些热词背后的真实用途和落地方式说透。不管你是刚跑通第一个 demo 的新手还是正在为召回率发愁的老手都能从里面找到能直接抄作业的东西。先说结论免得你看到一半觉得我在灌水。那六处分水岭分别是切分策略、上下文补全、检索路由、图谱增强、Agent 编排、评估闭环。前三个决定你的召回质量后三个决定你的系统能不能从“问答”进化成“干活”。下面逐个拆。2. 分水岭一切分策略决定召回天花板2.1 固定长度切分为何是灾难的开始几乎所有入门教程的第一步都是RecursiveCharacterTextSplitterchunk_size 设个 500 或 1000overlap 设个 50 或 100。这套配置在 demo 文档上跑得挺欢一上真实业务就露馅。问题出在语义完整性上。固定长度切分是纯字符维度的操作它根本不知道自己在切什么。一个完整的操作步骤可能被拦腰截断前半段在 chunk A后半段在 chunk B。检索时只召回了 A模型拿到半截指令生成出来的答案要么缺步骤要么直接编。我踩过最典型的一个坑一份设备维修手册某个故障排查流程跨了三个自然段。固定切分后关键的那句“若指示灯闪烁三次则更换主板”被切到了下一个 chunk 的开头。用户问“指示灯闪三次怎么办”检索命中的是上一个 chunk里面只有故障现象描述没有处置方案。模型很“聪明”地根据现象编了一个处置建议差点造成误操作。2.2 结构化切分的三种实战打法真正靠谱的切分得先理解文档的结构。我一般按文档类型分三套打法第一套标题层级切分。适用于 Markdown、Word、PDF 里有明确标题层级的文档。用MarkdownHeaderTextSplitter或者自己写解析逻辑按 H1/H2/H3 切保证每个 chunk 自带完整的标题路径。这样检索出来的片段天然带上下文模型一看就知道这段内容属于哪个章节。第二套语义切分。适用于没有明显结构的长文本比如会议纪要、访谈记录。核心思路是计算相邻句子的 embedding 相似度在相似度骤降的地方切一刀。LangChain 里有SemanticChunker可以直接用但要注意阈值调参太敏感会切得太碎太迟钝又等于没切。第三套父子文档切分。这是我目前最推荐的方案。思路是检索时用小的子 chunk 保证精准命中生成时把子 chunk 所属的父 chunk 一起喂给模型保证上下文完整。LangChain 的ParentDocumentRetriever就是干这个的。子 chunk 可以切到 200 字左右父 chunk 保留 2000 字检索精度和生成质量同时兼顾。提示切分参数没有万能值。我一般会先拿 20 条真实用户问题做一轮召回测试看命中率再反过来调 chunk_size 和 overlap。凭感觉设参数十有八九要返工。2.3 表格和图片到底怎么处理热词里有人问“rag 知识库能存储图片嘛”这个问题问到了点子上。纯文本 RAG 确实存不了图片但真实业务文档里表格和图片占比极高。表格的处理思路是转成结构化文本再入库。比如一张设备参数表不要直接 OCR 成一行行文字而是转成“设备型号 X 的额定功率是 Y 千瓦额定转速是 Z 转每分钟”这样的自然语言描述。这样检索时才能被语义匹配到。我一般用pandas读表格然后按行拼成描述句。图片的处理分两种。如果图片里有文字走 OCR 提取后按文本处理。如果图片是示意图、流程图那就得用多模态模型生成图片描述把描述文本入库同时保留图片路径。检索命中描述后把图片一起返回给前端展示。这样用户既能看到文字答案也能看到原图。3. 分水岭二上下文补全让检索不再“断章取义”3.1 Contextual Retrieval 到底在补什么Anthropic 提出的 Contextual Retrieval 是这两年 RAG 领域最实用的改进之一。它的核心洞察很简单一个 chunk 脱离原文后很多指代和背景信息就丢失了。举个例子。原文是“该公司 2023 年营收增长 15%主要得益于新产品的市场表现”。切分后chunk 里只剩“营收增长 15%”。检索时用户问“哪家公司营收增长 15%”这个 chunk 根本匹配不上因为它压根没提公司名。Contextual Retrieval 的做法是在入库前用大模型给每个 chunk 生成一段简短的上下文说明然后把这段说明拼在 chunk 前面一起做 embedding。比如上面那个 chunk补全后变成“本段来自 XX 公司 2023 年财报该公司营收增长 15%”。这样检索时就能命中了。3.2 补全上下文的实操流程具体操作分四步把整篇文档和当前 chunk 一起喂给模型让模型生成一段 50 到 100 字的上下文说明。把说明拼接到 chunk 开头形成“增强 chunk”。对增强 chunk 做 embedding 入库。检索时命中的是增强 chunk但返回给生成模型时可以只返回原始 chunk 加说明避免冗余。这里有个成本问题每个 chunk 都要调一次模型文档多了 token 消耗不小。我的优化做法是批量处理加缓存。同一篇文档的所有 chunk 一次性喂给模型让它批量输出上下文说明比逐个调用省一半以上的 token。另外对于结构清晰的文档其实可以用规则生成上下文比如直接把标题路径拼上去不一定非要调模型。3.3 元数据过滤是隐形的召回加速器上下文补全之外元数据是另一个被严重低估的环节。很多团队入库时只存文本和向量把文档来源、创建时间、部门、版本这些信息全扔了。结果检索时没法做过滤只能全库扫既慢又不准。我的习惯是入库时强制带上五类元数据来源文件、章节路径、创建时间、文档类型、权限标签。检索时先按元数据做一轮粗筛再在候选集里做向量匹配。这样召回率和响应速度都能明显提升。尤其是权限标签多租户场景下没有它检索结果可能把 A 部门的机密文档推给 B 部门的用户这是要出大事的。4. 分水岭三检索路由决定系统聪不聪明4.1 为什么单一向量检索不够用向量检索擅长语义匹配但有几个硬伤。第一它对精确匹配不敏感。用户问“错误码 E5021 怎么解决”向量检索可能召回一堆讲错误处理的通用文档就是找不到那个具体错误码。第二它对数值比较无能为力。用户问“功率大于 100 千瓦的设备有哪些”向量检索理解不了“大于”这个操作。第三它对多跳推理束手无策。用户问“A 产品的供应商的母公司是哪家”这需要两步检索单次向量匹配做不到。4.2 混合检索加路由的落地方式我的标准配置是三路召回加一个路由向量检索处理语义相似的问题。关键词检索用 BM25 或 Elasticsearch处理精确匹配和专有名词。结构化查询对数值、日期、枚举值直接转成 SQL 或过滤条件查元数据。路由层用一个小模型或者规则引擎来判断用户问题该走哪一路。简单问题走单路复杂问题走多路然后融合排序。融合排序我一般用 RRFReciprocal Rank Fusion它不需要调权重对多路结果的合并效果很稳。LangGraph 在这里特别好用。你可以把每一路召回定义成一个节点路由判断定义成条件边整个检索流程就是一张有向图。这样逻辑清晰调试时也能看到每个节点的输入输出比一坨 if-else 好维护得多。4.3 查询改写让召回率再上一个台阶用户的问题往往口语化、有歧义、缺上下文。直接拿原始问题去检索召回率很难看。查询改写就是解决这个问题的。我常用的改写策略有三种。第一种指代消解。多轮对话里用户说“它多少钱”得结合上文把“它”替换成具体产品名。第二种查询扩展。用户问“怎么重启”扩展成“重启方法、重启步骤、重启操作”。第三种子问题拆解。复杂问题拆成多个简单问题分别检索再合并结果。这些改写动作都可以用 LangGraph 编排成流水线。改写节点、检索节点、融合节点、生成节点各司其职出问题也好定位。5. 分水岭四GraphRAG 补上关系推理的短板5.1 GraphRAG 解决的是什么问题传统 RAG 把文档切成孤立的 chunkchunk 之间的关系全丢了。但很多业务问题的答案恰恰藏在实体之间的关系里。比如“哪些供应商同时给 A 产品和 B 产品供货”这需要跨文档、跨 chunk 做关系推理纯向量检索根本做不到。GraphRAG 的思路是先从文档里抽取实体和关系构建知识图谱检索时同时在图谱上做遍历。这样就能回答“A 的供应商还有谁”“B 和 C 有什么关联”这类关系型问题。5.2 图谱构建的实操要点GraphRAG 落地最大的坑在实体抽取。用大模型抽实体召回率还行但准确率和一致性堪忧。同一个实体在不同文档里可能被抽成不同的名字比如“华为”“华为技术”“Huawei”被当成三个实体。我的做法是先定义本体ontology再让模型按本体抽取。本体就是实体类型和关系类型的清单比如“公司”“产品”“供应商”是实体类型“供应”“竞争”“合作”是关系类型。模型抽取时只能从本体里选不能自由发挥。这样一致性大幅提升。抽取完之后还要做实体对齐把指向同一实体的不同名称合并。简单场景用字符串相似度加规则复杂场景用 embedding 相似度加人工审核。这一步很费功夫但省不得图谱质量直接决定检索质量。5.3 GraphRAG 和向量检索怎么配合GraphRAG 不是要取代向量检索而是互补。我的标准架构是双通道向量通道负责语义匹配图谱通道负责关系推理最后把两路结果融合。具体来说用户问题先过路由。如果问题是“XX 是什么”“XX 怎么用”这类事实型问题走向量通道。如果是“XX 和 YY 什么关系”“XX 还关联了哪些 ZZ”这类关系型问题走图谱通道。如果是混合型问题两路都走结果合并。这套架构在 LangGraph 里实现起来很自然。向量检索和图谱检索各是一个节点路由是一个条件边融合是一个汇聚节点。整个流程可视化调优时一目了然。6. 分水岭五Agentic RAG 让系统从“问答”变“干活”6.1 Agentic RAG 和普通 RAG 的本质区别普通 RAG 是单轮的用户问系统检索生成答案结束。Agentic RAG 是多轮的系统会自己判断要不要检索、检索几次、检索什么、结果够不够、要不要换个角度再检。它把 RAG 从“一次检索”变成了“一个检索决策过程”。这个区别在简单问题上体现不明显但在复杂任务上就是天壤之别。比如用户说“帮我对比 A 产品和 B 产品的技术参数然后推荐一个适合户外场景的”。普通 RAG 可能只检索一次拿到什么算什么。Agentic RAG 会先检索 A 的参数再检索 B 的参数再检索户外场景的选型建议最后综合生成对比和推荐。6.2 用 LangGraph 编排 Agentic RAGLangGraph 是目前编排 Agentic RAG 最顺手的框架。它的核心概念是状态图每个节点是一个处理步骤边是步骤之间的流转条件整个图共享一个状态对象。一个典型的 Agentic RAG 图长这样入口节点接收用户问题初始化状态。规划节点判断问题类型决定检索策略。检索节点执行检索把结果写入状态。评估节点判断检索结果是否充分。不充分就回到规划节点换策略充分就进入生成节点。生成节点基于检索结果生成答案。工具调用节点如果问题需要计算、查询数据库、调 API在这里执行。这个图的关键在于评估节点和回环。评估节点决定了系统会不会“再试一次”这是 Agentic 的核心。没有回环就还是单轮 RAG。6.3 工具调用让 RAG 真正“下地干活”热词里有个说法叫“让 AI 真的下地干活”这说的就是工具调用。纯 RAG 只能回答文档里有的东西工具调用能让它执行文档外的操作。比如用户问“帮我查一下上个月 A 产品的销量然后和 B 产品对比”。RAG 只能检索到产品介绍文档查不到实时销量。但如果系统挂了数据库查询工具它就能先调工具查销量再检索产品文档做对比分析最后生成完整答案。LangGraph 的工具调用节点支持绑定任意 Python 函数。我的经验是工具描述要写得极其清楚包括功能、参数、返回值、适用场景。模型靠描述来判断该不该调、怎么调。描述写得含糊模型就会乱调或者不调。注意工具调用一定要加权限校验和参数校验。模型可能生成非法参数也可能调用它不该调的工具。生产环境里每个工具入口都要做一层防护不能裸奔。7. 分水岭六评估闭环决定系统能不能持续进化7.1 没有评估的 RAG 就是盲人摸象我见过太多团队RAG 上线后全靠用户反馈来发现问题。用户不反馈就以为系统没问题。实际上召回率可能只有 60%只是用户懒得说而已。RAG 的评估要分检索质量和生成质量两层。检索质量看召回率、准确率、MRR平均倒数排名。生成质量看忠实度答案是否基于检索内容、相关性、完整性。两层都要评只评一层会漏掉问题。7.2 评估数据集的构建方法评估的第一步是有一批带标注的问题-答案对。没有标注数据一切评估都是空谈。我的做法是先从真实用户日志里抽 100 到 200 条问题然后人工标注每条问题的标准答案和应该命中的文档片段。这个工作量不小但一次投入长期受益。标注完之后每次系统改动都跑一遍评估看指标是涨是跌。如果实在没有人工标注资源可以用大模型做自动评估。让模型判断生成的答案是否忠实于检索内容、是否回答了问题。虽然不如人工准但作为快速迭代的参考足够了。7.3 用 LangSmith 做全链路追踪评估之外链路追踪是排查问题的利器。LangSmith 可以记录每一次调用的输入输出、耗时、token 消耗出问题时能快速定位是检索环节还是生成环节的锅。我一般会在关键节点打上标签检索节点记录召回了哪些 chunk评估节点记录判断结果生成节点记录最终答案。这样一条链路看下来问题出在哪一目了然。没有追踪调优就是碰运气。8. 常见问题与排查技巧实录8.1 召回不准的排查顺序召回不准是最常见的问题。我的排查顺序是先看切分再看 embedding再看检索策略最后看查询改写。切分问题占召回问题的六成以上。chunk 太大噪声多chunk 太小语义不全。先拿几条 bad case 看看命中的 chunk 长什么样基本就能判断是不是切分的问题。embedding 问题占两成。中文场景下通用 embedding 模型对专业术语的区分度可能不够。换个领域微调过的模型或者用多向量检索往往能改善。检索策略和查询改写各占一成。单一向量检索换成混合检索或者加上查询改写通常能再提几个点。8.2 生成答案胡编的三种解法模型胡编本质是检索内容不足以支撑答案但模型硬要答。解法有三种第一种prompt 里明确要求“不知道就说不知道”。这能挡住一部分但模型有时候会“假装知道”。第二种加忠实度校验。生成完答案后用另一个模型判断答案是否基于检索内容。不忠实就重新生成或者返回“未找到相关信息”。第三种提高检索门槛。检索结果的相关性分数低于阈值时直接不生成返回“没有找到相关内容”。宁可说不知道也不要胡编。8.3 响应太慢的优化方向RAG 响应慢通常是三个原因检索慢、模型慢、链路长。检索慢一般是向量库索引没建好或者候选集太大。加元数据过滤缩小候选集或者换更快的向量库能明显改善。模型慢是 token 太多。检索回来的 chunk 不要全塞给模型按相关性排序取 top 3 到 top 5 就够了。上下文补全的说明也可以精简。链路长是 Agentic RAG 的通病。多轮检索加评估确实慢但可以通过并行检索和缓存来优化。多路召回并行执行常见问题的答案缓存起来都能省时间。问题现象可能原因排查动作解决方向召回不准切分不合理查看命中 chunk 内容调整切分策略召回不准embedding 不适配测试相似度分数换模型或微调答案胡编检索内容不足检查召回结果加忠实度校验答案胡编prompt 约束弱检查 prompt强化“不知道”指令响应慢候选集太大看检索耗时加元数据过滤响应慢链路太长看各节点耗时并行化加缓存8.4 几个容易忽略的细节第一个chunk 的去重。多路召回时同一个 chunk 可能被多路命中。不去重的话生成时同一段内容出现多次浪费 token 还干扰模型。第二个检索结果的重排序。初步召回的结果排序往往不够准。加一个 cross-encoder 重排序模型能把最相关的排到前面生成质量提升明显。第三个对话历史的处理。多轮对话里历史太长会挤占检索内容的 token 空间。我的做法是只保留最近三轮对话更早的做摘要压缩。第四个冷启动问题。新文档入库后embedding 可能和已有文档不在一个分布上。定期做全量重 embedding或者用增量学习的方式更新能缓解这个问题。9. 我在实际项目中的几点体会这套六分水岭的框架我在三个项目里反复验证过。最深的体会是RAG 的调优是个系统工程没有银弹。切分改好了召回可能还是不行因为 embedding 不适配。embedding 换了生成可能还是胡编因为 prompt 没约束好。每一处都要调每一处都要评估。另一个体会是不要过度设计。不是每个场景都需要 GraphRAG也不是每个场景都需要 Agentic。简单问答场景把切分和上下文补全做好效果就已经很能打了。图谱和 Agent 是给复杂场景准备的用错了地方就是徒增复杂度。最后分享一个实用技巧建一个 bad case 库。每次发现召回或生成的问题就把 case 记下来标注问题类型和修复方式。这个库积累到几十条之后你会发现大部分问题都是重复的修复起来有章可循。这比每次从头排查高效得多。
返回列表