首页 项目 博客 简历 联系 English
返回列表
2026年7月3日 约 5,505 字 预计 12 分钟读完

05-一次 RAG 请求的完整链路:从用户问题到最终答案

前面几篇已经把离线侧的基础拆开了:原始文档要经过解析、清洗、结构化和 metadata 标注,随后按合适的粒度切成 chunk,再通过 embedding 和向量索引进入可检索状态。到这里,知识库具备了被系统调用的基础。

#LLM#检索增强#证据链#RAG

05-一次 RAG 请求的完整链路:从用户问题到最终答案

前面几篇已经把离线侧的基础拆开了:原始文档要经过解析、清洗、结构化和 metadata 标注,随后按合适的粒度切成 chunk,再通过 embedding 和向量索引进入可检索状态。到这里,知识库具备了被系统调用的基础。

但 RAG 真正面对用户时,关注点会从“知识怎么入库”转向“问题怎么被回答”。一个用户问题进入系统以后,不会直接变成一次向量检索和一次大模型调用。更完整的过程,是把自然语言问题转成可检索查询,从知识库中召回候选证据,对候选证据重新排序和整理,再把证据以合适的形态交给大模型生成答案。

这一篇的核心判断可以先放在前面:

一次 RAG 请求的质量,取决于正确证据能否进入候选池候选证据能否排到合适位置上下文能否被模型有效使用,以及最终回答能否忠实基于证据生成。

只看最后的答案,很容易把所有问题都归因到模型能力。按链路拆开以后会发现,很多失败其实发生在更早的位置。正确文档没有进入索引、query 表达不适合检索、metadata 过滤过窄、召回结果混入噪声、Rerank 排序不稳定、上下文被截断、Prompt 约束太弱,这些都会让最终答案出现偏差。

从离线索引走到在线请求

RAG 可以分成离线构建和在线查询两条链路。离线构建关注的是知识如何变成索引,在线查询关注的是用户问题如何借助索引变成答案。

简化后的在线链路可以这样看:

用户问题
  -> 查询理解与预处理
  -> 权限、租户、时间、版本等约束注入
  -> Query Rewrite 或 Query Expansion
  -> 向量检索、关键词检索、metadata 过滤
  -> 多路召回与结果融合
  -> Rerank 精排
  -> 上下文去重、压缩、排序、截断
  -> Prompt 组装
  -> 大模型基于证据生成答案
  -> 返回答案、引用、日志和反馈信号

这条链路里,每一层都有明确责任。

查询理解让系统知道用户到底在问什么,检索层负责把可能相关的证据拉进候选池,融合和 Rerank 负责把候选证据排出更合理的优先级,上下文工程负责把证据整理成模型能读懂、能利用的形态,生成阶段负责在证据边界内组织回答。日志和反馈则让系统在出错时可以回溯,而不是只看到一个“答错了”的结果。

阶段主要责任
查询理解把用户问题转成适合检索的查询表示
检索召回让可能相关的证据进入候选池
融合与 Rerank去重、合并、重新排序候选证据
上下文工程把证据整理成模型可读、可引用的形态
生成与返回在证据边界内回答,并保留引用和日志

RAG 的工程复杂度,也正是来自这些中间层。任何一层粗糙,最终都会投射到答案质量上。

用户问题进入系统后先做查询理解

用户输入通常不是检索友好的文本。

在真实场景里,用户可能会写得很短,例如“这个怎么配置”“报错怎么处理”“新版本有影响吗”。也可能带着上下文依赖,例如上一轮问了某个接口,下一轮只说“它的限制是什么”。还有一些问题会混合业务口语、缩写、产品名称、版本号、错误码和时间条件。

如果系统直接把原始 query 拿去向量化,检索质量会非常依赖 embedding 模型的语义泛化能力。模型可能抓到大概意图,也可能因为 query 太短、指代不清、关键信息缺失而召回一批看似相关但不能回答问题的 chunk。

所以在线链路的第一步往往是查询理解。这里要做的事情包括意图识别、上下文补全、指代消解、关键词提取、业务约束识别和权限信息注入。用户问“它支持灰度吗”,系统需要结合会话历史知道“它”指的是哪个功能、哪个产品线、哪个版本。用户问“7 月发布的接口变更”,系统需要把时间条件转成 metadata 过滤条件,而不是只把“7 月”当成普通文本。

用户表达查询理解要补上的信息
“它支持灰度吗”指代对象、产品线、版本
“7 月发布的接口变更”时间范围、文档类型、接口范围
“E1024 怎么处理”错误码、产品模块、当前版本

查询理解做得越稳,后面的检索就越容易。它不追求把用户问题改写成优美的回答,而是把问题转成适合检索系统消费的查询表示。

权限和 metadata 约束应该尽早进入链路

RAG 很容易被理解成“先找相关内容,再让模型总结”。在生产系统里,相关性通常只是第一层要求。文档是否属于当前租户、用户是否有权限查看、内容是否来自有效版本、是否已经过期、是否适用于当前地区或产品线,这些约束同样会影响答案是否可用。

因此,在线链路不能等到生成阶段才考虑权限和版本。更稳的做法,是在检索前或检索时把这些约束作为 metadata filter 加进去,让不该进入候选池的内容尽早被排除。

权限尤其敏感。只要模型上下文里出现了用户无权访问的内容,即使最终回答没有直接引用原文,也已经产生了信息泄露风险。版本和时间也类似。旧接口文档、废弃配置项、历史公告、草稿内容如果混入候选池,模型很可能会把它们当成仍然有效的证据。

metadata 的价值在这里会真正体现出来。它让检索从单纯的相似度搜索,变成带业务边界的证据筛选。前面文章里强调文档入库时要保留来源、标题、层级、时间、版本、权限和质量信息,原因就在这里。没有这些字段,在线链路只能依赖文本相似度猜测,很多生产约束就很难稳定落地。

检索层先解决候选覆盖

检索层的首要目标,是让正确证据进入候选池。这个阶段关注的是覆盖面,而不是最终排序。

向量检索会把 query 转成向量,到向量索引里找语义相似的 chunk。它适合处理表达不同但含义接近的问题。例如用户问“账号找不回来了怎么办”,文档写的是“忘记登录凭证后的账户恢复流程”,向量检索有机会把它们匹配到一起。

但向量相似度无法覆盖所有场景。错误码、接口名、配置项、产品型号、合同条款编号、法律条文、命令参数这类信息,经常依赖精确匹配。用户输入一个错误码 E1024,关键词检索可能比语义向量更可靠。用户输入一个具体函数名,向量模型未必能准确理解它的业务含义,但倒排索引可以直接找到文本出现位置。

所以检索层通常不会只依赖单一路径。向量检索负责语义召回,BM25 或其他关键词检索负责词面召回,metadata filter 负责业务范围约束。更复杂的系统还会加入结构化查询、图谱查询、FAQ 检索、历史工单检索或代码搜索。

这一层的重点是把可能有用的证据尽量拉进来。候选池太窄,后面的 Rerank 和生成都无从补救。正确证据没有被召回,模型再强也只能在错误上下文里组织答案。

Query Rewrite 让问题更适合检索

用户问题和文档表达之间经常存在缝隙。用户说的是需求、症状和口语表达,文档写的是规范、标题和技术术语。Query Rewrite 的作用,就是在检索前把这个缝隙缩小。

改写可以很轻量,例如补全上下文、替换同义词、展开缩写、抽取关键词、加入产品版本。也可以更复杂,例如根据会话历史生成一个完整查询,或者把一个宽泛问题拆成多个子查询。

比如用户问“这个功能怎么打开”,会话历史里提到的是“知识库权限继承”。改写后的检索 query 可以变成“知识库权限继承功能 如何开启 配置步骤”。这个 query 对搜索系统更友好,也更容易命中文档标题和正文里的关键表达。

需要注意的是,Query Rewrite 服务于检索,不服务于聊天体验。它不需要把问题写得像最终答案,只需要让搜索系统更容易找到证据。改写过度也会带来噪声。如果模型在改写时擅自加入用户没有表达的条件,检索范围可能被带偏。

因此,Query Rewrite 最适合和日志一起使用。系统应该保留原始 query、改写后的 query、实际召回结果和最终答案。这样在排查时才能判断,是原始问题难以检索,还是改写阶段引入了错误方向。

多路召回和结果融合扩大证据入口

当系统同时使用向量检索、关键词检索、改写 query、结构化过滤和其他检索器时,就进入了多路召回。

多路召回的价值在于覆盖不同类型的相关性。向量检索找到语义接近内容,关键词检索找到精确词面匹配,metadata 过滤缩小业务范围,Query Expansion 带来更多表达方式,HyDE 这类方法通过生成假想答案或假想文档来帮助检索抽象问题。不同路径各有偏差,组合后可以提高正确证据进入候选池的概率。

但多路召回也会扩大噪声。召回路径越多,候选池里重复、过期、片面、相似但不回答问题的内容也越多。系统需要做结果融合和去重,把来自不同来源的候选整合成一个统一候选集。

常见做法是按来源权重、分数归一化或排序融合来处理。RRF 这类排序融合方法会利用各路结果中的名次信息,而不是直接比较不同检索器的原始分数。因为向量相似度分数、BM25 分数和结构化查询得分通常不在同一尺度上,直接相加容易失真。

融合阶段还要处理 chunk 去重和文档级聚合。同一篇文档里的相邻 chunk 可能都被召回,如果原样进入后续阶段,会浪费上下文窗口,也会让模型反复看到相近内容。更合理的方式,是保留最相关片段,同时利用标题、父级章节和相邻片段补全上下文。

Rerank 负责从候选中挑出更相关的证据

召回阶段重覆盖,Rerank 阶段重精度。

向量检索和关键词检索通常追求快速拉取候选,判断粒度相对粗。Rerank 会对 query 和候选 chunk 做更细致的相关性判断,把真正能回答问题的证据排到前面。

在工程上,Rerank 可以使用专门的 reranker 模型,也可以用 Cross-Encoder 类模型,让 query 和 chunk 放在一起输入模型,由模型判断相关性。相比双塔 embedding 检索,Cross-Encoder 的计算成本更高,但相关性判断往往更精细。它适合作为第二阶段排序器,对召回出的几十或几百个候选重新排序。

Rerank 的价值在于处理**“看起来相关”“真正回答问题”**之间的差异。一个 chunk 可能和 query 语义接近,却只是介绍背景,没有给出答案。另一个 chunk 词面重合不高,却包含关键步骤或限制条件。Rerank 要尽量把后者排到前面。

它也能缓解多路召回带来的噪声。召回层为了覆盖可能会放宽条件,Rerank 则把候选重新压缩成更高质量的证据序列。这个阶段的输出,直接决定后续上下文工程能拿到什么材料。

上下文工程决定证据能否被模型使用

Rerank 之后,系统手里有了一批排序较好的候选证据。但这些证据还不能简单拼接进 Prompt。

大模型的上下文窗口有限,即使窗口足够大,也不代表塞入越多内容效果越好。重复片段会浪费 token,噪声片段会干扰模型判断,互相冲突的证据会让答案摇摆,过长的上下文还可能导致关键信息被忽略。

所以上下文工程要继续做整理。它通常包括去重、合并相邻 chunk、保留标题层级、压缩冗余内容、按证据重要性排序、控制总长度、给来源编号、处理冲突和截断策略。这里的目标,是让模型看到一组清晰、紧凑、可引用的证据。

上下文处理解决的问题
去重和合并相邻 chunk减少重复内容占用窗口
保留标题、章节和 metadata帮助模型理解证据适用范围
压缩和截断控制总长度,减少噪声
来源编号支持答案引用和后续复核
冲突处理避免模型把不同版本强行合并

标题和层级信息很重要。一个 chunk 单独看可能只有一段配置说明,放回章节标题下才能知道它属于哪个产品、哪个功能、哪个版本。metadata 也应该跟随证据一起进入上下文,例如来源文档、更新时间、版本号和权限范围。否则模型只能读到正文,很难判断证据的适用边界。

上下文顺序也会影响生成。最相关证据通常应该靠前放置,结论性材料和操作步骤需要靠近问题。对于长上下文,系统还要注意“中间信息被忽略”的风险。与其把大量候选平均塞进去,不如把高置信证据组织得更清楚。

上下文工程是 RAG 里经常被低估的一层。检索到了正确证据,但答案仍然不稳定,很多时候就是因为证据没有以适合模型使用的方式呈现

Prompt 负责把证据边界说清楚

Prompt 在 RAG 中的作用,不只是把问题和上下文拼到一起。它要明确告诉模型,当前回答应该如何使用证据、如何引用来源、如何处理证据不足、如何面对冲突信息。

一个稳定的 RAG Prompt 通常会包含任务说明、证据内容、用户问题、回答格式和约束规则。约束规则里最关键的是证据边界:只能基于给定上下文回答,无法从证据中推出时要说明信息不足,涉及步骤和结论时要标注来源,发现证据冲突时要指出冲突而不是强行合并。

Prompt 还应该配合上下文编号。给每段证据加上 source_id、标题、链接或文档 ID,模型才能在回答中引用来源。没有引用编号,后续的可追溯性会变弱,用户也很难判断答案依据。

不过,Prompt 不能替代前面的检索质量。如果上下文里没有正确证据,Prompt 再严格也只能让模型更倾向于拒答。它可以约束生成阶段的行为,但无法凭空补上缺失证据。

生成阶段要保留证据意识

大模型生成答案时,最理想的状态是把证据转化成对用户有用的表达,同时保留证据边界。

在问答场景中,模型需要直接回答问题,必要时给出步骤、条件、限制和来源。在知识库助手场景中,模型还要把分散证据整理成结构清晰的说明。在客服或内部工具场景中,模型可能需要区分“可以直接执行的建议”和“需要人工确认的信息”。

生成阶段常见的问题包括过度概括、把多个证据混成一个结论、忽略时间和版本限制、在证据不足时补充常识、引用来源和正文不对应。前面几层越清楚,生成阶段越容易稳定。证据来源清楚、冲突被提前标记、上下文没有过多噪声,模型就更容易在边界内回答。

因此,生成阶段的评价不能只看语言是否流畅。RAG 的答案还要看事实是否被证据支持,引用是否能回到原文,结论是否保留适用条件,证据不足时是否能够拒答。这些要求会在后面的幻觉治理和评测篇继续展开。

返回结果时要带上引用、日志和反馈入口

用户看到的是答案,系统内部应该保存的是整条链路。

一次完整的 RAG 请求,至少应该记录原始 query、改写 query、metadata filter、各路召回结果、融合结果、Rerank 分数、最终进入上下文的证据、Prompt 版本、模型输出、引用映射和用户反馈。对外展示时可以只呈现答案和引用,对内排查时这些日志非常关键。

日志内容排查价值
原始 query / 改写 query判断问题是否被错误改写
metadata filter判断权限、版本、时间条件是否过窄
各路召回和融合结果判断正确证据是否进入候选池
Rerank 分数和最终上下文判断正确证据是否被排上来、是否被截断
Prompt 版本和模型输出判断生成约束是否生效

如果用户反馈“答案不对”,系统需要知道问题出在哪一层。正确文档是否在知识库里,正确 chunk 是否被切出来,embedding 是否召回到它,关键词检索是否命中,metadata filter 是否误过滤,Rerank 是否把它排到前面,上下文截断是否把它删掉,模型是否忽略了证据。没有链路日志,这些问题只能靠猜。

反馈入口同样重要。用户点赞、点踩、选择“没有解决问题”、手动标记正确答案、点击引用来源,这些信号都可以回流到评测集和优化流程中。RAG 不是一次上线就结束的能力,它需要通过真实问题持续修正知识库、检索策略和生成约束。

按链路定位 RAG 故障

实际排查中,经常会遇到一句笼统描述:“RAG 效果不好”。这句话本身信息量很低。更有价值的做法,是把问题拆到链路上的具体位置。

如果正确证据没有进入候选池,应该优先检查文档是否入库、chunk 是否合理、embedding 是否适配、query 是否需要改写、metadata filter 是否过窄、关键词检索是否缺失。

如果正确证据进入候选池但没有进入最终上下文,问题可能在融合、去重、Rerank、topK 设置或截断策略上。此时继续调 Prompt 往往收益有限,因为模型压根没有看到关键证据。

如果正确证据已经进入上下文,但答案仍然错误,就要看上下文是否有噪声或冲突,Prompt 是否明确要求基于证据回答,模型是否忽略引用,输出是否需要后校验和拒答机制。

这种排查方式会比单纯“换一个模型”“调大 topK”“加长 Prompt”更稳定。RAG 的每个阶段都有自己的责任边界,定位越准确,优化越容易收敛。

失败位置优先排查
正确证据没进候选池入库、chunk、embedding、query rewrite、metadata filter、关键词检索
正确证据没进上下文融合、去重、Rerank、topK、截断策略
正确证据已进上下文但答案错上下文噪声、证据冲突、Prompt 约束、后校验和拒答机制

小结

一次 RAG 请求可以看成一条证据流转链路。用户问题先被理解和规范化,再进入带权限、版本和业务约束的检索系统。召回层负责扩大候选覆盖,融合层负责整合不同来源,Rerank 负责提高排序精度,上下文工程负责把证据整理成模型可用的形态,Prompt 和生成阶段负责在证据边界内输出答案。

这条链路的关键,不在于某个单点技术听起来多先进,而在于每一层是否承担了正确的职责。检索解决候选覆盖,Rerank 解决相关排序,上下文工程解决证据组织,生成约束解决忠实回答,日志反馈解决持续优化。

理解这一点以后,后面再看 Query Rewrite、Hybrid Search、多路召回、Rerank、幻觉治理和评测体系,就不会把它们看成零散技巧。它们都是在帮助 RAG 把**“可能相关的文本”变成“可以支撑答案的证据”**。