做RAG应用做到一定阶段,大家都会撞上一面墙:单次向量检索的召回质量撑死了就是这么高,你再怎么调 embedding、换 chunk 策略,天花板都看得见。我在这上面折腾了快一年,试过各种重排、各种混合检索,最后真正把效果拉上去的,是换了一套思路——不再把检索当成“一次查询、一次召回”的静态流程,而是交给一个带规划能力的 Agent 去动态决定“怎么找、找几次、从哪里找”。这套方案里最关键的抓手,就是 Agentic-R 的双视角检索。这篇文章把我在实操中踩过的坑、调过的参数、最终跑通的方案完整拆给你。
双视角检索,说白了就是让 RAG 系统同时站在两个完全不同的立场上理解同一个问题:一个看“语义”,一个看“结构”。语义视角负责理解你话里的意图和表达,结构视角负责精确锁定文档里的实体、关系、数值条件。两个视角的结果做融合,再交给生成模型。这套东西对做知识库问答、企业文档助手、复杂事实核查这类场景非常有效,适合已经跑通基础 RAG、正在为准确率停滞不前发愁的团队参考。
1. 传统RAG的瓶颈在哪里
1.1 检索失效的三种典型场景
先回到老问题上。传统 RAG 最常见的形态是:用户 query 进来,拿 embedding 模型编码成向量,去向量库里做相似度检索,取 top-k 个片段拼进 prompt,让大模型基于这些片段做回答。这个流程写起来简单,但生产环境跑几个月后,你会发现它稳定失效于三种场景。
第一种是实体精确匹配。比如用户问“某某设备的工作温度范围是多少”,文档里写的是“该装置可在 -40℃ 至 85℃ 环境下正常工作”。语义检索很可能把“设备”“温度”这些词对应的向量相似的段落捞回来,但捞回来的是不是包含精确数值的那一段,完全看运气。向量相似度对字面信息是不敏感的,它擅长的是“意思差不多”,不是“数值精确命中”。
第二种是关系型问题。比如“哪些仓库存储了过期批次”、“A 方案和 B 方案的区别是什么”,这类问题需要把文档中的实体、属性、关系连接起来,形成一条证据链。向量检索本质上是在做“独立片段召回”,它没有能力跨片段建立连接,你拿回三个片段可能各自正确,但组合起来根本回答不了问题。
第三种是多跳推理。用户问题里的关键实体没有直接出现在最终答案所在的那段文档里,需要先找到中间实体,再顺着关系跳一次。单次向量召回根本不会触发第二次检索,因为传统 RAG 的流程是写死的,一次查询,一次召回,然后生成。
这三种场景的共同点是:问题出在“检索策略是静态的”这件事上。而 Agent 化改造解决的核心问题,正是把静态召回变成一个有判断、可迭代、多路验证的动态过程。
1.2 从"单次召回"到"智能体流程"
Agentic-R 的第一步,是把检索流程重新拆成几个可编排的环节。我这里把整个流程划分为四个能力模块:查询理解、检索策略规划、执行与验证、生成与自检。
查询理解模块先对用户问题做一次预分析,判断这个问题是事实型、关系型、数值型还是主观意见型,同时抽取里面的关键实体和约束条件。这一步的输出是一份结构化的查询画像,而不是一个简单的 query 字符串。
检索策略规划模块拿到查询画像后,决定采用哪种检索组合。比如说,数值型问题用结构化视角为主、语义视角做补全;开放式问题反过来;关系型问题则两路并进,还要额外安排一次扩展检索用来补足证据链。
执行与验证模块就是真正跑检索的地方,跑完以后不是直接拼 prompt,而是要校验召回的结果够不够回答问题。如果证据不足,Agent 会决定是否触发第二次检索,或者把问题拆分成子问题分别检索。
生成与自检模块在最终生成答案之前,还会要求模型对照召回片段逐条核对事实,标记出哪些话是有文档支撑的、哪些话是模型自己推断出来的。
这套流程跑起来之后,RAG 的“天花板”问题就从“召回算法不够强”变成了“Agent 会不会规划”,而后者在工程上是可以通过约束和调优不断压榨潜力的。
2. 双视角检索的整体设计思路
2.1 第一视角:语义意图检索
双视角的第一个视角,是我称之为“语义意图检索”的通道。它的职责不是找“字面上最像的段落”,而是找“意图上最匹配的内容”。
这个视角在实现上仍然用向量检索,但有几个关键细节和普通 RAG 不同。第一,query 不直接拿去编码,而是先经过意图改写。比如说用户问“这东西扛不扛冻”,直接去做向量检索,召回的结果会很散,因为“扛冻”这个口语化表达在文档里对应的可能是“低温耐受”“工作环境温度下限”这类工程术语。意图改写先把它转成“在哪些环境温度下可以正常工作”,再去编码,召回质量立刻不一样。
第二,这个视角要做多路召回,不能只出一路 top-k。我实际跑下来的配置是:原始 query 一路、改写后 query 一路、加上从查询画像里拆出来的核心实体扩展词一路,三路召回的结果做去重合并。因为单一路径的向量检索在高维空间里存在明显的“近邻迷失”问题,你只取 top-20 可能正好把所有正确片段全部排除在边界外,多路径召回可以显著降低这种风险。
第三,语义视角需要一个比较高的召回上限。我的经验是初选阶段 top-k 取 50 到 80 之间,后面再通过重排压到 10 以内。很多人一上来就把 top-k 设成 5,检索管线看起来快,但召回率低得吓人,后面做啥都救不回来。这一步的理念是宁滥勿缺,把判断留到融合阶段去做。
2.2 第二视角:结构化证据检索
双视角的第二个视角,是结构化证据检索。它的存在是为了补齐语义视角在精确性上的先天短板。这个视角不关心“表达相似”,只关心“事实是否匹配”。
结构化证据检索在我看来要做好三件事。第一件事是构建领域内的实体索引表。把所有文档中出现的设备名、物料编号、人员姓名、部门名称、项目代号等实体统一归一化后建表,并记录每个实体出现在哪些文档片段里。这个过程可以用规则加模型的方式半自动化,实体识别模型负责抽,规则负责把同义写法归并成统一 ID。
第二件事是构建关系映射。这一步不一定上完整的知识图谱,那种重型方案对大多数业务场景来说太重了。我建议用一个轻量化的方式:从文档里抽取出“(实体A,关系谓词,实体B)”形式的三元组,存成一张关系表。比如“仓库A-存储-批次X”、“批次X-失效时间-2025Q3”。这张关系表不需要像知识图谱那样精细建模,只要能支撑两层关系跳转就够用。
第三件事是把数值条件变成过滤条件。用户问“哪些物料库存低于安全线”,语义视角会把“库存”“安全线”这两个词对应的语义片段捞出来,而结构化视角直接查结构化表,把所有满足“库存数量 < 安全阈值”的记录一条不落全部命中。
这三件事做下来,结构化视角返回的结果天然具备“可验证性”和“精确性”。在实际项目中我观察到,很多用户对回答的不信任感,不是因为大模型写出来的文字不通顺,而是因为它经常给不出精确的数字和名单。结构化视角解决掉的正是这一层信任问题。
2.3 双视角融合策略
两个视角拆开都好理解,难点在融合。我在项目里实验过三种融合方式,最后形成了自己的取舍标准。
第一种是结果拼接法,把两个视角召回的结果直接拼在一起进重排。这个方式实现成本最低,但问题是语义视角召回量远大于结构化视角时,重排模型会偏向语义路径的结果,结构化视角的精确命中片段很容易被淹没。
第二种是互斥路由法,根据查询画像判断这个问题属于哪种类型,只走其中一路。这个方法节省资源,但判断错误时没有挽回余地,尤其当问题同时包含实体条件和开放语义时,单路必漏。
第三种是我现在主力使用的加权融合法。两个视角各自独立召回,各自打分归一化后,按动态权重合并成总分,再进重排。关键点在于权重不是手工固定的,而是由查询画像决定:检测到强实体标记和数值约束时结构化权重拉高到 0.6 甚至 0.7;检测到开放式意图时语义权重占 0.7;两种特征同时存在时五五开。
融合分数我用的是这么个算式:
final_score = w_sem * normalized_sem_score + w_struct * normalized_struct_score + rrf_score其中rrf_score是 Reciprocal Rank Fusion 的加成项,两个视角都排进前 10 的片段获得额外加分。这个设计的含义是:如果一个片段同时被两个视角认为是重要的,它的可信度远高于只被一个视角捞中的,分数上有必要体现这种共识。
3. 核心环节实现与参数选择
3.1 系统架构与数据流
我跑通的这套 Agentic-R 架构,整体上分成四层。第一层是接入层,接收 query 做基础清洗;第二层是 Agent 编排层,负责意图分析和检索规划;第三层是双路检索层,语义和结构化两个通道并行执行;第四层是融合生成层,做重排、证据校验和最终生成。
数据流是这样走的:query 进入后先做意图分析,产出格式化的规划指令。这个指令包含三样东西:检索模式(并行还是串行)、权重配置(语义占比多少)、扩展检索策略(是否需要二次检索,二次检索的目标是什么)。然后调度器按指令同时发出两个检索请求,各自返回带分数的片段列表,进入融合打分环节,合并排完序之后再取前 5 到 8 个片段作为上下文,交给生成模块。
在这个架构里,我把 Agent 的“自主性”限制在很小的范围内。它只能决定怎么检索、检索几轮、权重怎么配,但不能决定最终答案的内容。答案的事实依据永远只在检索片段里找。这个约束非常关键,去掉它你就不是在做一个 RAG 系统,而是在做一个行为不可控的自动生成工具。
3.2 意图分析模块的实现要点
意图分析模块是整个双视角检索的决策大脑。我的实现里它不是一个单独的大模型调用,而是一个轻量分类器加一个实体抽取器的组合,只在分类置信度低时才升级到大模型兜底。
轻量分类器负责四分类:事实型、数值型、关系型、开放型。训练数据不需要太多,我准备了三千条标注样本,加上五百条伪标注样本,效果已经够用。伪标注的方法很朴素:把过往用户日志里包含明确数字的归为数值型,包含对比词的归为关系型,用规则先打一批弱标签,再人工抽检修正。
实体抽取即使用一个通用的序列标注模型,在领域语料上做了增量训练。这里面有个经验值得记一下:泛化的识别模型漏掉的不只是领域专属名词,更麻烦的是它对“缩写”的识别很差。我后来加了两个规则兜底,一是维护一份全量缩写映射表,二是对文档中首次出现的“(全称,简称)”模式做自动注册,实体识别命中率明显上来了。
意图分析还有一个容易被忽略的产出物,叫做“检索约束条件”。比如数值型问题会附带“范围区间”约束,关系型问题会附带“关系类型”约束。这些约束会直接传给结构化检索模块,转成查询条件,而不是在向量检索里做模糊匹配。这一步是整个准确率提升最明显的杠杆之一。
3.3 双路检索的并行执行与超时控制
双路检索在实现上要考虑工程层面的问题,特别是并行调度和超时控制。
语义检索这条链路相对标准:embedding 编码、向量库检索、粗排。结构化检索的链路则是:把查询画像转成结构化查询语句,去查实体索引和关系表,拿到命中的文档片段 ID 列表,再映射回原始片段。
两条链路跑完后再做片段级去重。同一个物理片段可能被语义路径和结构化路径同时召回,但在最终列表里必须只保留一份,同时保留两个视角各自的命中标记。这个信息在后面会用到,因为生成阶段可以明确告诉模型“这段内容是通过精确事实匹配命中的,另一段是通过语义扩展召回的”,让模型在引用时对不同来源的片段区别对待。
超时控制我采用的是两级策略:单路检索最长 1.5 秒,超出就返回当前已有结果并按低置信度标记;整体编排最长 6 秒,超出就跳过反思与扩展检索环节,直接进入生成。网上很多人喜欢给 Agent 很长的思考时间,认为想得越久答案越准,但在检索这个场景里我强烈建议设置硬性截止时间,因为用户对回答延迟的容忍度,远低于对回答质量的容忍度。
3.4 融合重排与最终上下文拼装
融合重排环节我建议用两层结构。第一层是粗排,用上一节提到的加权融合公式把两路召回结果混合成一个统一列表,取前 20 个片段。第二层是精排,用一个 cross-encoder 模型对 20 个片段逐条与 query 编码后打分,取前 6 个作为最终上下文。
精排模型的选择上没有太玄学的东西,一个中等规模的双塔交叉编码器在这个任务上足够了。如果训练数据有限,可以直接在通用语料预训练的基础上,用几百条“问题-片段-是否相关”标注做微调。我在实测中发现,精排这一步哪怕只提升几个点的 NDCG,最终生成答案的事实准确率都能有明显改善,因为生成模型吃到的上下文干净度直接决定了它的输出约束强度。
上下文拼装有个很容易被忽视的细节:要给每个片段加上来源标签和视角标签。比如“【来源:产品手册 第3章|结构化命中】”,这行小字不花多少 token,但它对生成质量有实打实的帮助。模型看到“结构化命中”的片段时,倾向直接引用其中的数值和名单;看到“语义命中”的片段时,更倾向做概括转述。这就是在显式地告诉模型,什么该照抄、什么该理解,而不是让它自己去猜。
4. 实操过程中的问题与调优
4.1 权重怎么调才不玄学
双视角融合权重是整个系统里最容易被玄学化的参数。很多人一上来就拍脑袋定 0.5 和 0.5,然后发现效果时好时坏,又改成 0.7 和 0.3,还是说不清楚为什么。我的做法是不手工拍,而是用一个很小的验证集做参数扫描。
具体方法是准备一百条带标准答案的评测样本,每条样本包含正确片段 ID。然后在权重网格上做遍历,每一步都计算融合后前 10 片段的平均召回率,画一条曲线。你会发现曲线通常有一个明显的平台区间,只要权重落在这个区间里,效果差不了太多。这个平台区间的中心位置,可以再用两三个点微调确认一下。整个过程一上午就能跑完,比凭感觉调参靠谱得多。
另外一个原则值得写在这里:两路检索的质量差得太多时,权重怎么调都救不回来。如果你的语义检索本身召回率只有百分之三十,把语义权重拉到 0.8 只会让融合结果更差。先各自优化单路召回,再谈融合权重,这个顺序不能反。
4.2 延迟预算的分配策略
Agentic-R 相比传统 RAG 在延迟上是天然有劣势的,因为它多走了意图分析、双路检索、融合重排、甚至可能的二次检索。但延迟预算这碗水是可以端平的,关键是知道钱花在哪里。
我的生产配置里,总延迟预算 6 秒,分配方式是:意图分析 0.3 秒、双路检索并行 2 秒、粗排加精排 2.5 秒、生成 2 秒。注意双路检索是并行发出的,所以两条链路各自的耗时不会简单相加。结构化检索通常在几百毫秒内响应,真正的大头在语义检索的向量查询上,所以我把向量库的索引参数调得偏“快”,牺牲一点召回精度换时间,后续靠精排把精度找回来。
有一个我反复踩过的坑是:为了省精排时间,把 top-20 改成 top-10,节省的时间不到零点几秒,但融合阶段本来能捞回来的长尾正确片段直接被截掉了。延迟优化的正确姿势是砍掉不必要的二次检索,或者优化意图分析的模型推理时间,而不是在前置召回阶段省那几毫秒。
4.3 幻觉控制在双视角下的新解法
双视角检索给幻觉控制带来一个传统 RAG 没有的优势:你可以对回答内容做“视角归因”。生成时我在 prompt 里要求模型对每句关键论断标注依据片段编号,如果某句话没有任何片段支撑,必须显式输出“未找到直接依据”。这个约束配合双视角的结构化命中标记,效果出乎意料地好。
实际跑下来大概把严重幻觉率从百分之十几压到了百分之五左右。剩余的幻觉主要发生在两个地方:一是模型对片段信息做了不当外推,二是跨片段组合信息时产生了矛盾结论。针对前者,我在生成后加一个规则检查器,把回答中的数字和专有名词与源片段做简单比对,不一致就触发一次“重新生成并强制引用原数字”的兜底。针对后者,我加了一步证据链校验:如果回答里引用了多个片段的组合信息,要求模型把组合逻辑显式写出来,人工抽查时能看到它的推理路径是否合理。
提示:幻觉永远是压到零的难度极大,目标应该是“可归因”。每一个论断都能追溯到具体片段,这就是企业级 RAG 可以接受的状态。
5. 常见问题与排查技巧实录
5.1 结构化视角一直没有命中记录怎么办
这是双视角上线初期最常遇到的问题。排查思路从三个方向入手:第一查实体识别有没有把关键实体抽出来,第二查关系表里有没有覆盖该实体的三元组,第三查查询条件转换环节有没有把自然语言约束错误地映射成查询条件。我遇到过最隐蔽的一个坑是实体归一化把两个不同编码的物料归到了同一个 ID 下,导致结构化检索返回了大量错误片段,排查了两天才定位到归一化映射表。
5.2 两路检索结果重合度极高怎么处理
如果你发现语义视角和结构化视角召回的重合率超过百分之七十,说明这两个视角实际上已经退化成了同一个视角。常见原因是文档本身就是高度结构化、且每种实体都频繁出现在同现语境中,导致语义向量空间里也天然形成了实体簇。此时建议降低结构化视角的召回量,把省下来的时间用在语义视角的多路召回上,或者干脆引入第三个视角,比如时间线视角、代码片段视角,重新拉开信息覆盖面。
5.3 精排模型把正确片段排在后面怎么办
首先要检查精排的训练数据是否有标签偏置。我遇到的情况是标注人员习惯给“包含完整答案文本”的片段打高分,但很多正确片段只是间接证据,导致模型学偏了。解决方案是扩充“间接相关”类型的标注比例。其次要检查精排输入是否包含了视角标签,我加上视角特征后,精排在“结构化命中但语义得分低”的片段上判得更准了。
我整理了一份问题排查速查表,方便直接对照:
| 现象 | 可能原因 | 排查方向 |
|---|---|---|
| 数值型问题答非所问 | 结构化视角权重过低或实体识别失败 | 检查查询画像中的实体列表,调高结构化权重 |
| 开放型问题答案干瘪 | 语义视角召回量不足或改写失效 | 增加改写路径,提高 top-k 初选值 |
| 生成内容脱离片段 | 上下文拼装缺少来源归因约束 | 加入片段编号引用要求,开启自检 |
| 两路结果高度重合 | 语义空间与实体空间趋同 | 降低结构化召回量,考虑引入新视角 |
| 检索延迟飙升 | 二次检索频繁触发 | 收紧反思触发条件,增加最大轮数硬限制 |
| 实体识别漏抽缩写 | 泛化模型对领域缩写不敏感 | 增强缩写映射表,注册全称-简称对 |
5.4 二次检索的触发条件怎么设计
二次检索是 Agentic-R 消耗资源和时间的主要来源,必须给触发条件立规矩。我的配置里有三条触发规则:第一,融合后的 top 片段中没有包含查询画像里的全部核心实体;第二,结构化检索零命中且查询画像判定为强实体类问题;第三,精排最高分低于预设阈值,说明两路召回都不太有信心。三条规则任意触发就做一次扩展检索,扩展目标分两类:一是基于缺失实体直接查实体索引,二是把 query 拆成子问题重新走一遍双路检索。每轮扩展后强制检查是否补上了缺失实体,补上了就立即终止,不再继续扩展。
我建议把最大扩展轮数限制在 2 轮。超过 2 轮还没找到充分证据,基本上就是文档本身没写,继续检索只是在反复捞同一批片段。
6. Agentic-R 后续还可以怎么扩展
双视角这套框架跑稳定之后,我的体会是它的扩展方向非常明确,也很自然。
第一个方向是把“视角”从两个扩展成多个。比如代码问答场景可以加一个“调用链视角”,从代码的调用关系图里检索函数上下游的信息。合同审查场景可以加一个“时间线视角”,把条款的变更历史串起来。视角越多,融合权重的定义就越重要,到这一步就不需要靠人工设权重了,可以直接把视角选择和权重分配全都交给 Agent 来做,本质上成了一个动态多视角路由系统。
第二个方向是引入记忆机制。现在的系统每次检索都是从零开始,但同一类问题在不同时间被问到时,检索策略往往是相似的。把“查询模式-检索策略-命中效果”存成记忆,下次遇到相似查询时直接复用策略,延迟和准确率都会受益。这就是 RAG 从无状态走向有状态的进化路径。
第三个方向是自我评估闭环。让生成模块在产出答案后给出一个置信度评分,评分低的答案自动打回,触发更激进的检索策略或更全面的证据收集。这相当于给整个系统加了反馈回路,实话说工程量大不少,但对准确性要求高的场景非常值得投入。
我自己的做法是先从第二个方向开始做,因为收益直接且实现成本低。先把检索策略复用做起来,再逐步加新视角,一点一点把 RAG 的天花板往上顶。
说回双视角本身,我觉得它最有价值的地方不是多召回了几条结果,而是让 RAG 系统第一次有了“视角意识”——知道自己是在凭感觉找,还是在凭证据找,并且能根据问题性质动态切换重心。这种能力,比单纯堆参数重要得多。如果你也在 RAG 准确率上卡了很久,我建议你找个测试集,把语义和结构化两条路的独立召回率跑出来看看,再针对性做 Agent 化改造,会比盲目调参有效得多。