Agent 检索优化:召回质量、预算与缓存
检索质量不能只用“搜到了相似段落”衡量。Agent 最终需要的是能够支撑当前结论、满足权限与时效要求,并在可接受成本内获得的证据。优化时既要解决漏召回,也要避免重复、旧版本、错误上下文和无止境追加查询。
数据源路由与分块是前提,见Agent 检索设计:数据组织与分块。本文从候选结果进入系统开始,讨论融合、验证、动态检索和缓存如何协作。
多路与多源召回先统一结果结构
关键词检索适合错误码、接口名、产品型号等精确表达;向量检索帮助跨越不同措辞;图查询适合明确的实体关系和依赖路径;业务 Action 用于获取实时或结构化事实。没有一种通道能替代所有其他通道,也不必在每个请求里全部启用。
多路召回强调方法不同,多源召回强调数据来源不同。两者都需要融合,但多源还涉及时间覆盖、字段语义和权威来源的差异,不能仅把列表拼接起来。
建议统一保留以下字段:
content_ref / doc_id / chunk_id
source_id / retrieval_method
original_rank / original_score
document_version / observed_at / valid_until
tenant_scope / permission_scope
evidence_type / matched_requirement
matched_requirement 说明它补的是哪个信息缺口;evidence_type 区分实时观测、正式规则、历史案例和派生摘要。这样,历史故障案例就不会因语义最相似,被当作当前环境已经发生的事实。
先过滤与去重,再比较相关性
权限、租户、对象、时间和明确版本要求决定内容能否使用。尽量在数据源查询阶段施加这些过滤,返回后再复核。无权访问的内容不能只降权,也不能传给重排模型后再删除;当用户问某个历史版本时,则应使用相应版本,不能机械地将所有旧文档排除。
去重至少分三个层次:同一 Chunk 被不同通道召回,合并身份并保留各路排名;相邻 Chunk 因 Overlap 重复,合并相交原文区间;同一事实有多个副本,保留来源关系并减少重复注入。
内容相似不一定代表等价。两个片段可能只在“不允许”和“允许”、版本号或适用范围上不同,这正是重要冲突,不能用相似度去重抹掉。去重后也应保留多路命中信号,否则后续融合会丢失候选曾出现在哪些列表的信息。
RRF 负责融合排名,Rerank 再看证据价值
向量相似度与 BM25 分数尺度不同,直接加权相加通常缺乏可比性。即便都归一到零至一,相同数值也不一定表示相同相关程度。
RRF 按每一路的排名融合。令 R 为检索列表集合,rank_r(d) 为文档在列表中的位置,排名从一开始:
RRF(d) = Σ 1 / (k + rank_r(d))
r ∈ R,且 d 出现在 r 中
没有出现在某一路的文档,该路贡献为零;k 为平滑常数。若需通道权重,可以尝试 Σ w_r / (k + rank_r(d)),但需明确这是加权扩展,并检查所用搜索引擎的实际接口支持。标准计算方式可参考 Elasticsearch RRF 文档。
RRF 不使用原始分数差距,因此也会丢失某些强弱信息;它无法发现召回阶段完全遗漏的证据,更不能证明排名靠前的文档事实正确。多路列表高度相关时,重复信号也可能被过度强化。
融合后,可以让 Cross-Encoder 或其他重排方法重新判断问题与候选的相关性,再按明确业务规则组织证据。应关注直接支持程度、适用范围、版本、可信来源和新增信息量。权限等硬条件仍在排序之外,不是可被其他高分抵消的一项权重。
最终选入工作集的结果,不必等于得分最高的前几段。如果问题需要“适用条件、操作方法、例外”三类证据,而前三段都在重复操作方法,就需要按证据覆盖选择互补结果。
迭代检索由证据缺口驱动
一次检索适合边界清晰的事实问题。复杂排障、综合对比和多文档解释,可能只有看过首批结果后才知道下一步该找什么。这时可以建立“查询、观察、更新缺口”的有限循环。
充分性判断应输出结构化结果,不只是一个模型自报的置信度:
{
"sufficient": false,
"covered": ["本次发布变更", "新增耗时所在阶段"],
"missing": ["相关配置是否一致", "是否存在独立数据库异常"],
"conflicts": [],
"next_query_reason": "需要区分缓存回源与数据库独立故障",
"stop_reason": null
}
下一轮只针对缺口查找,使用新获得的实体、时间范围和假设生成 Query,同时查询可能推翻当前解释的证据。已经确认的部分保留在状态里,避免每轮从头开始。
以一个教学排障场景为例:第一轮发现发布后某个业务 Span 耗时增加;第二轮发现缓存未命中与数据库回源同时增多;第三步查询配置和预热任务,验证是否存在前缀不一致。只有把配置变化、命中下降、回源及耗时连起来,并检查替代解释,才能支持更强的判断。并不是规定必须查三轮,也不能因历史复盘出现过同类问题就直接断定根因。
每轮记录 Query、数据源、结果版本、新增证据、剩余缺口和停止理由。内容数量增加不等于证据增加:同一结论换个说法仍然是重复信息。新增证据应按预先定义的事实或条件槽位判断,同时允许发现新的必要缺口。
停止条件应同时包含证据充分、没有实质新增、来源穷尽、需要用户补充,以及资源上限。资源耗尽与证据充分是两种不同状态:前者必须保留未决项,不能写成已经找到完整答案。Agentic Retrieval 通常还涉及查询与工具路由等自主决策;仅增加检索次数,并不会自动得到更可靠的 Agent。
用动态预算约束完整链路
用户感知的是获得可信结果的时间,不只是向量库的查询耗时。需要把 Query 改写、Embedding、多源访问、融合、重排、充分性判断和生成纳入端到端 Trace,并观察 P50、P95、P99 以及并发下的排队时间。
以下仅是教学预算,实际值由业务响应要求和模型吞吐确定:
| 预算项 | 示例限制 | 达到限制时的行为 |
|---|---|---|
| 检索轮次 | 最多 3 轮 | 返回当前证据与未决问题 |
| 总 Query 数 | 最多 6 条 | 合并重复查询,优先关键缺口 |
| 重排候选 | 最多 24 个 | 先过滤与去重,再选择候选 |
| 证据工作集 | 最多 8,000 token | 保留直接证据,按需加载背景 |
| 时间 | 请求级截止时间 | 取消非必要慢请求,保留超时状态 |
独立的日志、指标和 Trace 查询可以并行;依赖前一轮实体的查询必须等到结果明确。每个来源需要超时、取消和并发上限,不能让一个辅助源拖住整条链。超时表示没有得到结果,不能改写为“该来源没有发现异常”。
优化顺序先从明显浪费开始:复用同任务内重复 Query 的 Embedding,合并重复检索,在入库时计算文档摘要和标签,检查结构化过滤与索引是否有效,再决定是否增加复杂控制器。简单问题走轻量路径,必要证据不足时逐步扩展通道或候选数量。
降级也有边界。可以先去掉装饰性背景,跳过收益不高的重排,或给出证据限定的局部结论;不能丢掉权限校验、关键限制和决定结论的证据。评价速度变化时,应在相同任务质量要求下比较单位成功任务的成本,不能只报告总调用减少。
最小充分检索控制当前步骤,不削掉用户目标
用户说“先确认设备能运行这套软件,能运行再比较购买渠道”,可以先核验兼容性;若不兼容,后续价格查询自然没有必要。这是在利用任务依赖减少浪费。
但“统计订单后计算退款率”中,拿到订单只是中间步骤,退款率才是目标。用户已要求完整报告时,系统可以按子目标逐段检索、逐段输出进展,却应继续完成全部已授权范围,不能每完成一段就停下来反复询问。
因此,“最小”指当前步骤所需的最少证据集合,“充分”指它足以支持这一步的有效产出。它不等于少查几条,也不意味着把复杂结论中的必要部分删掉。依赖强、遗漏会改变结论的问题,应保留完整证据要求;到达截止时间时如实说明缺口。
缓存先守住语义、权限和版本
可以分别缓存 Query 的 Embedding、候选文档 ID、融合结果、原文片段,或最终答案。层级越接近最终答案,依赖通常越多,错误复用的影响也越直接。
精确缓存是合适的起点。缓存键不应只有 Query 文本,还应包含会影响结果的条件,例如:
cache_key = hash(
normalized_query,
tenant_and_authorized_scope,
filters_and_time_range,
index_or_document_version,
embedding_and_retrieval_strategy_version
)
查询规范化可以统一同义表达,却不能消掉不同订单号、否定词、时间范围和版本。权限范围可用稳定标识及权限版本表示;返回缓存结果时仍要确认当前用户具有访问权。权限撤销、文档删除、策略升级或索引版本变化,都可能要求失效,TTL 只提供额外兜底。
语义缓存用向量相似性发现可能等价的问题,它是可选优化,不是必须用向量库替代普通缓存。高相似度不能证明等价:“允许修改接口吗”和“不允许修改接口怎么办”词语接近,需求却不同。语义命中之后还需校验实体、约束、作用域、时间及答案所依赖的条件,无法确认就回源。
稳定的公共 FAQ 可能适合答案缓存;依赖个人画像、实时库存或当前状态的答案通常更适合缓存可复用材料,再重新获取易变部分。即便只缓存文档 ID,也要处理文档已删除、版本被替换、父节点权限变化等情况。
缓存上线除了看命中率,还要检查错误复用率、陈旧证据率、权限撤销后的失效时延和回源成本。高命中但频繁给错答案,没有业务价值。
用分层指标与失败样本推动优化
离线样本应包含问题、授权范围、时间版本、标准证据集合、可接受替代证据及相关性等级。没有答案的问题也应纳入,防止系统总能找到相似文本却无法承认缺证据。
| 指标 | 主要回答的问题 |
|---|---|
| Recall@K | 已标注相关证据中,前 K 个覆盖了多少 |
| Precision@K | 前 K 个结果中,相关证据占多少 |
| Hit Rate@K | 是否至少找到一条相关证据 |
| MRR | 第一条相关结果是否足够靠前 |
| NDCG | 不同相关等级的排序质量如何 |
| 必需证据覆盖率 | 完成当前判断的必要证据是否齐全 |
| 重复率与 token | 是否用大量空间重复相同信息 |
| 充分性误判率 | 是否在缺证据时过早停止或反复追加 |
例如一个纯教学样本需要 A、B、C 三份互补证据,前五个结果为 A、B 和三段无关内容。若相关证据集合恰为这三份,Recall@5 为 2/3、Precision@5 为 2/5,Hit Rate@5 已经命中,但完成结论所需的 C 仍然缺失。这个差异说明单一命中指标不适合替代复杂任务的证据充分性判断。相同测试还应包含 C 根本不存在或当前无权读取的变体,以检查系统是否能说明限制而不是补造证据。
分块策略变化时,Chunk ID 与数量也变化。为公平比较,应将标注关联到原始事实或证据区间,再映射到各版 Chunk,不能直接用旧 Chunk ID 评估新切法。
也可以定义“修正候选列表所需成本”,分别惩罚漏证据、噪声、错序和重复,但这属于自定义诊断指标。加入移动、合并及不同权重后,就不再是标准字符串编辑距离;应写明规则和可接受的等价顺序,不替代成熟指标。
线上重问、纠错、追加检索和引用使用情况适合收集失败样本,不能直接当作答案正确的标签。回归时逐段定位 Query、路由、分块、召回、过滤、融合、重排、上下文和生成的变化。召回率提高而答案变差,可能是新加入的噪声或冲突证据影响了生成,继续扩大 Top-K 未必有效。
保留固定基线与分类型测试集,每次只围绕明确问题调整策略,记录质量、延迟和成本的共同变化。最终应证明的是用户任务得到更可靠的完成,而不是链路里增加了更多检索组件。