1. 先搞清楚:你的RAG为什么效果差?——全链路优化的起点
做RAG(检索增强生成)这个方向的人,多半都经历过这种尴尬:辛辛苦苦把文档灌进知识库,上线一问,答得前言不搭后语,引用源张冠李戴,甚至直接开始一本正经地编。问题出在哪?很多人第一反应是“模型不行”“Embedding选得不好”,一顿调参猛如虎,结果一问还是老样子。其实RAG是个典型的全链路系统,每一环都在往下一环递交付错误,最终答案烂,往往是好几个环节的误差叠加出来的结果,根本不是某一个点的问题。
我自己的体会是,与其痛苦地“面向疑问调参”,不如先把整条链路拆开来看。一条标准的RAG流水线可以拆成三段:入库阶段(文档解析、分块、向量化)、检索阶段(召回、重排)和生成阶段(上下文组织、Prompt、生成策略)。用户只看见你最终吐的那一句回答,但决定回答质量的,是这一整条链路上的每一个决策。
理解了这一点,你就知道为什么“换个模型”治标不治本了——如果文档本身分块分得乱七八糟,再强的生成模型也救不回来;如果召回的数据根本不相关,模型就是把你给它的垃圾当宝贝在下笔。所以下面这套全链路优化流程,真正的起点不是某个参数,而是先建立“基线+诊断”的意识,把所有环节都摆在台面上逐一排查。
1.1 先给自己泼盆冷水:没有评估,就没有优化
做优化之前,我强烈建议你先回答一个问题:**你的RAG系统现在到底多差?**别笑,很多人真的答不上来。你说“感觉不太好”,但“不太好”是哪里不好?是搜不到东西,还是搜到了不会用,还是用户问的它根本理解偏了?没有量化指标,你的优化就是在黑灯瞎火里乱摸。
通用的评估框架一般围绕三个核心维度展开:
- 忠实度(Faithfulness):回答是否严格基于检索到的上下文,有没有自由发挥编造内容。这是RAG最要命的指标,答错不可怕,答得言之凿凿却是错的才可怕。
- 答案相关性(Answer Relevance):回答是否针对用户问题本身,有没有答非所问、绕圈子。
- 上下文相关性(Context Relevance):你召回回来的那一坨文本片断,到底跟用户问题有多相关。这一步直接决定后续生成的原料质量。
这三项下来,你基本就能知道问题大头在哪。如果上下文相关性就极低,那是检索和分块的锅,你先别去折腾Prompt;如果上下文相关还行但答案相关性差,那是Prompt和生成策略的问题。
1.2 搭一套最少投入的测试集,先把分数跑起来
很多人一听“评估”就觉得要上复杂框架,其实不用。第一次做,你只需要20-30条真实用户问题就够了。关键是这20-30条必须覆盖你业务里的典型场景,别挑简单的,也别回避那些让系统“翻车”的刁钻问题——它们就是你未来优化的靶子。
给每道题配上标准参考答案(自己能接受的范围即可),然后跑一轮预测,按上面三个指标打分。不需要一次性就搞成自动化流水线,前期手动打分完全够用。我能理解这个活儿挺枯燥的,但一份稳定的测试集,是你后续所有优化动作的唯一“裁判”:你说你优化了分块,怎么证明?同一份题测出来分数涨了,才算数。
2. 喂给RAG的原料决定上限:文档预处理与分块策略
检索增强生成系统里有一句话我很认同:垃圾进,垃圾出。很多人把重心全放在Embedding模型、大模型选择上,却忽略了最上游的原料加工环节。但实际上,如果文档进了知识库之后,检索系统拿不到语义完整的片段,后面的一切都是空中楼阁。你优化Prompt一百遍,也救不了一片被拦腰截断的上下文。
文档预处理这件事,看起来是纯体力活,做起来全是细节黑洞。以最常见的PDF为例,你以为PDF里的文字能直接抽取出来用?天真了。双栏排版会被抽成跨栏乱序,公式和图表直接消失,表格结构全部坍缩成一串无意义的字符。你把这些东西切成块之后送进Embedding模型,向量表达的质量可想而知。业界有个段子:RAG工程师一半的时间不是在调模型,而是在和PDF搏斗。这话不完全准确,但至少三分之一是有的。
2.1 分块策略:不同切法,效果天差地别
分块是预处理环节最大的变量。常见思路有这么几种:
- 固定长度切分:按字符数或Token数硬切,比如每512个字符一块,块间留20%重叠。优点是实现成本低、速度飞快;缺点是语义容易被切断,一句话刚说一半就被拦腰截断到下一块。
- 递归分隔切分:按段落、句子、标点逐级回退,优先保持自然语言边界。这是目前性价比最高的做法,适合绝大多数通用场景。
- 结构感知切分:针对Markdown标题、HTML标签、表格行列关系做定向切分。适合规则明确的半结构化文档,比如产品文档、百科条目,切出来的块天然具有层级结构。
怎么选?我的建议是,如果你只有精力做一件事,就做“递归分隔+按语义主题聚合”。纯按固定长度切出来的块,非常容易出现“半个表格”“只讲了一半的流程”这种残废块,导致检索经常拉到次优片段。
2.2 块大小和重叠率:先记住一组经验值
块大小这件事,不同场景的答案并不一样。一般来说,如果做的是开放问答,块偏小更好——比如128-256个Token,因为问题往往指向具体的某个知识点,小块的精确匹配率更高;如果是总结归纳类场景,块要偏大——512-1024个Token也不过分,因为你需要给模型提供相对完整的上下文来凝练内容。
重叠率(Overlap)通常设置在10%-20%之间。别小看这段重叠,它能有效避免“问题刚好卡在块与块的缝隙里”的尴尬——关键句被切成两半,两边都不完整,召回哪个都差点意思。
提示:分块完之后,一定要做一次“人工巡检”。随机抽几十个块读一遍,问自己:如果你是一个用户,拿着问题去匹配这些块,能不能找到答案?如果块本身内容就是断裂的、莫名其妙的,那后面的工作都白搭。
2.3 元数据是你最容易忽略的“隐藏外挂”
很多人的知识库就是一个大平层:文本往里一灌,向量一算,完事。但你想想,用户可能想限定“只看近一年的政策文件”,或者只想搜“某产品的故障排查手册”——如果没有元数据支撑,这种诉求在检索阶段根本无从下手。
合理的做法是,在入库阶段就给每条文档打上结构化元数据:来源(哪个业务线)、类型(FAQ、操作手册、公告)、时间、适用版本、产品线。这些元数据一方面可以配置成预过滤条件,实现业务维度隔离;另一方面可以拼进文本作为向量化的一部分,例如在块内容前面加上“[产品A-操作手册-2024版]”,让Embedding模型在编码时就能感知到出处信息。这算是一个低成本高收益的动作,做过的都懂。
3. 检索链路:从“搜得到”到“排得对”
到这一步,你的知识库已经有了一批质量还不错的文本块。接下来就是用户发出问题之后,如何从几百上千万个块里把真正有用的那一小撮捞出来。检索这件事,核心矛盾就一个:把“语义相似”和“关键词精准”结合起来,并且把最靠前的那几个位置,留给真正能回答问题的内容。
很多人最开始只用纯向量检索,也就是把用户问题Embedding成一个高维向量,然后找余弦相似度最高的几个块。这种方式在各种评测集上表现确实不错,但一上真实业务就露怯:大量专业术语、产品名、缩写,用户在提问时往往用的是口语化表达或模糊描述,Embedding模型一旦在训练时没吃透这些领域词汇,你召回回来的东西就会“语义上沾边,事实上跑偏”。
3.1 混合检索:关键词与向量的双保险
线上RAG系统里,混合检索(Hybrid Search)基本是标配了。它把BM25这种传统的稀疏检索和向量稠密检索拼接在一起,各自捞回一批候选,再做合流去重。原理也不难理解:BM25擅长捕捉“用户精确命中术语”的场景,向量检索擅长捕捉“语义替换、抽象概括”的场景。二者互补性极强。
具体实现时,两条路各取多少比例的结果,是个需要仔细调的参数。我习惯的先手配置是:向量召回Top 50,关键词召回Top 50,合并之后取并集,再去重。后续的排序交给重排模型,而不靠简单相加。为什么?因为向量相似度和BM25得分根本不是同一量纲,直接加和没有意义。既然没有意义,就别自己造公式,让重排模型去学“哪个块才是真正有用的”。
3.2 查询改写:用户的问法,别指望它有多规整
真实的用户问题,习惯讲究吗?讲究的人不多。用户会问“那个东西怎么设?”,会问“发票上写的那个金额错了咋处理”,甚至一个问题里夹杂好几个意图。你原封不动拿这样的query去做检索,效果能好才怪。
所以一个务实的做法是在检索之前加一层查询改写:把口语问题翻译成更加适合检索的书面描述,或者改写成多个子查询分别执行。比如“那个东西怎么设”,改写模型会补全成“XX功能的设置方法”“XX功能的配置步骤”,然后分头去检索,把结果合并回来。这一步投入小、见效快,尤其是C端客服类场景,属于必做项。
注意:查询改写务必控制在你所使用模型的能力边界内。如果生成模型的逻辑能力一般,改写出来的查询反而会丢失原意,那不如不用。如果你发现“改写掉了”“搜不出来了”,请果断回退,退回到“原查询+同义词扩展”这种轻量策略。
3.3 重排序:检索的最后一道精筛
召回阶段讲究的是“宁可多捞,不能漏”,所以Top 50甚至Top 100的结果里,真正有用的可能只有三五个。这时候如果你直接把这一大坨喂给生成模型,且不说价格和时间,光是噪声就让模型“晕”了。怎么办?加一道重排序(Rerank)。
重排模型的任务,是拿用户问题和候选文档做深度交互编码,逐条计算“到底有多相关”,然后按分数从高到低排。重排之后,一般只保留Top 5-10作为最终上下文。这一步对最终答案质量的提升是肉眼可见的——你想想,从50条里挑5条,跟从5条里挑5条,信息密度完全不一样。
我自己实操时,重排和召回的TopK取舍是这样的:召回阶段稍微多召回一些,比如50-100条,确保不漏;重排之后只留3-8条,确保不乱。别舍不得,宁可少给,也别把不相关的内容塞进Prompt里。这里补充一句,在配置重排策略的目标时,我会设一个“回答必须完全基于引用内容”的硬性要求,这个放到下一节展开。
4. 生成环节:Prompt、上下文与幻觉控制
很多人认为RAG既然“检索”了,生成模型就一定会老老实实按检索内容回答。这是天大的误会。生成模型拿到你给的上下文,只是“参考一下”,它该发挥还是发挥,该编还是编。如果你不给它划清边界,它就能把检索到的内容和你训练语料里见过的内容混在一起,构成一个看似流畅但细究起来完全站不住脚的答案。
所以生成阶段的核心,就是围绕“如何让模型克制地使用上下文”来设计Prompt策略。这一步没有标准答案,但我可以分享一套经过验证、通用性很强的多段式Prompt框架。
4.1 上下文呈现:一股脑儿全塞进去是对模型的不尊重
把重排后的Top结果全部拼成一段长文本丢给模型,是最粗糙的用法。一方面,不同块之间可能观点矛盾;另一方面,模型面对长篇大论,注意力容易涣散,抓不住核心。
更稳妥的做法是为每一条检索结果标注来源编号,把上下文组织成“有序清单”而不是“大段文章”。比如:
”候选资料1:... 候选资料2:... 候选资料3:... 基于以上候选资料,回答用户问题。“
这样模型在执行“引用某个来源”的时候,就有了一个明确的可指对象。后续即使要做引用溯源,也是呼之欲出。
4.2 Prompt里必须写清楚的五件事
一个合格的RAG生成Prompt,至少包含以下几层信息:
- 角色边界:你是客服助手,不是AI顾问;不要回答超出资料范围的问题。
- 资料使用规则:只基于“候选资料”里的内容回答;当候选资料不足以回答问题时,直接说“资料不足”,禁止自行补全。
- 输出结构:先给结论,再给理由,最后附引用编号;保持段落短小。
- 矛盾处理:若多个资料冲突,如实告诉用户存在不同说法,不要自行调和。
- 拒答口径:不聊与业务无关的话题,不回答涉及主观判断的内容。
这段Prompt不一定要一次写到位。我习惯先写一版,拿测试集跑一版,专门挑那些“模型编答案”“模型绕圈子”的问题逐条看,再根据失败样本去修订Prompt措辞。这词关键有效。
4.3 幻觉控制:让回答真正“有据可查”
有一类幻觉特别隐蔽:模型说的每一个字看起来都像是从资料里来的,但实际上是它把多个不相关片段“脑补”成了一个符合逻辑但不属于原文的结论。要对付这种问题,最好的抓手还是强制引用溯源。
做法是:在Prompt里要求模型回答时,每一句话或每一个关键结论后面都带上参考资料编号,例如“根据资料2,设置步骤为……”。如果某句话完全无法挂靠到任何资料编号,那要么是模型在编,要么是资料里根本没说——无论哪种,都是不允许的。
线上实践下来,这个强制引用设计对于降低幻觉特别有效,它像一道“证据链”约束,逼着模型回到原文。它有一个附带的好处:用户看到引用编号后,会信任你的系统,不像以前那样一口咬定是AI瞎编的。哪怕回答效果一般,有可查的来源,用户的容忍度也高得多了。
5. 工程落地:评估闭环与持续迭代
优化做完一轮,不等于事情就完了。RAG系统最麻烦的地方在于:它是一个随业务数据、用户问题不断漂移的系统。你今天优化好了,明天文档更新了一版、用户问法换了一种,效果可能又崩了。
所以,最后一环也是最重要的一环,是把上面做过的所有工作沉淀成一整套可持续运转的流程,让优化不再是“救火”,而是“常态化”。
5.1 把“感觉”变成“指标”:搭一份最小评估流水线
对个人开发者或小团队来说,不需要马上搞复杂的在线评估平台,一个最小评估流水线就够了。我建议的目录结构大概是:
- 一份测试集:固定30-50条高质量问题+参考答案,覆盖各业务场景。
- 一个评测脚本:跑完全部问题后,按三个指标打分,输出报告。
- 一个基线记录:每次改完参数,把分数记录到一个表格里,备注改动点。
每次迭代时,拿着新参数跑一轮测试集,和基线对比。分数涨了,就更新基线;分数跌了,果断回滚。听起来很朴素,但我见过太多团队“改着改着,忘了之前为什么这么改”,最后整个系统变成了一个没有人说得清的黑盒。有一份基线记录,至少能保证你的每一步优化都有迹可循。
5.2 增量更新和缓存:别让知识库变成一个“死库”
很多RAG系统上线半年后效果变差,不是模型退化了,而是知识库没跟上。文档更新了、产品下线了、政策调整了,你的向量库里还是老内容,用户搜出来的答案当然跟不上现状。
建议在建库时就把“文档版本”作为元数据管理起来。每次更新文档,不是简单往库里插新数据,而是要先下线旧版本对应的向量,再入库新版本。这一步如果省了,大量旧数据会和新数据混在一起,检索结果出现古今混杂的情况。
另外,对高频相似问题做缓存是非常有效的一招。用户问“怎么退款”和“退钱流程”,如果经过查询改写后生成的向量距离很近,完全可以走缓存直接返回历史最优答案,省一次检索和生成的耗时,成本降一大截,响应速度还能起飞。
5.3 换一个思路:RAG并不总是最好的解法
写到最后,我想留一个很多人不爱听但很真实的观察:RAG不是银弹,有些问题根本不需要RAG。
如果一个知识库只有几十篇文档,总量不大且内容相对固定,那么关键词搜索加上一层规则匹配可能比RAG更稳;如果用户问题高度统一,比如就是几十个常见FAQ,那意图分类+固定回答就足够了。RAG这套链路本身有延迟、有成本、有复杂度,如果业务规模根本撑不起来这套复杂度,它反而不划算。
我做过的多个项目中,纯粹靠RAG打天下的只有一半,另一半是用“小模型分类+FAQ匹配+兜底RAG”的混合架构拿下的。这给人的启发是,架构设计永远是为了解决问题,不是为了让技术炫酷。能用一个简单方案解决的,就不要引入复杂方案;必须上RAG的,才把所有优化做扎实。
这套全链路优化体系,核心就一句话:**在每一步都尽量少传递错误,让最终生成的模型在尽可能高质量的信息上作答。**文档预处理时多想一步分块策略,检索时多花点成本做混合召回和重排,生成时把Prompt边界写清楚,再搭一个闭环评估兜底——这五点都做到位,你的RAG效果不会差到哪里去。纸上谈兵不如跑一遍,建议你现在就打开自己的测试集,按这个顺序逐环过一遍,大概率能找到一两个立刻就能收获的改进点。