先给检索失败分类
“答案不对”不能直接归因于 Retriever。保存原查询、所有候选、每阶段排名、过滤原因和最终送入模型的 Context,才能区分:相关文档未被召回;已召回但被排到窗口外;权限过滤错误;上下文组装丢失;模型没有利用正确证据。
| 失败切片 | 诊断证据 | 优先策略 | Why Not 直接加多跳 |
|---|---|---|---|
| 错误码、版本、专名漏召回 | BM25 命中、Dense 未命中 | Sparse/短语检索 + Hybrid | 多跳增加成本但不修复词法盲区 |
| 同义表达漏召回 | Dense 命中、Sparse 未命中 | Dense/领域 Embedding | 先解决表示,不必引入规划 |
| 多个子意图互相稀释 | 单查询各意图召回不完整 | 分解 + 覆盖检查 | 自由循环难判断遗漏 |
| 正确候选排名低 | Recall 高、NDCG/Context Precision 低 | Reranker | 扩大 Top-K 会放大噪声 |
| 下一查询依赖前序实体 | Gold 证据形成链 | 受控多跳 | 一次改写无法获得未知实体 |
Dense 与 Sparse 解决不同盲区
Dense Retrieval 把查询和文档映射到向量空间,擅长语义相近、同义改写和概念匹配;Sparse/BM25 依赖词项匹配,擅长精确名称、版本、编号和低频专词。它们不是新旧替代关系,而是不同证据通道。
RRF 等排名融合
两种 Retriever 的原始分数通常不可直接相加:BM25 分数、Cosine 和不同向量索引的分布不同。Reciprocal Rank Fusion(RRF)只使用名次,常见形式为 Σ 1 / (k + rank),适合作为稳健基线。Microsoft Azure AI Search 的当前 Hybrid 实现也使用 RRF 合并并行排名。
type Hit = { id: string; text: string }
export function rrf(lists: Hit[][], k = 60): Array<Hit & { score: number }> {
const scores = new Map<string, { hit: Hit; score: number }>()
for (const list of lists) list.forEach((hit, index) => {
const current = scores.get(hit.id) ?? { hit, score: 0 }
current.score += 1 / (k + index + 1)
scores.set(hit.id, current)
})
return [...scores.values()].sort((a, b) => b.score - a.score)
}
RRF 不会自动纠正两个都差的候选集,也不表达业务重要度。候选深度、字段权重和融合常数仍需按任务切片验证;ACL 必须在内容进入融合和重排前执行。
查询变换要保留原始意图
变换后的查询是检索工具,不是对用户问题的重新定义。始终保存原始查询,并让每个变换携带目的、来源和预算。评测时检查它提升了哪些样本,又把哪些约束改没了。
消歧与上下文补全。对话中的“它支持吗?”需要从明确的历史状态补全对象;缩写可以映射到领域术语。不要让模型凭空补出产品版本和时间范围。关键歧义无法从状态确定时,应向用户澄清。
Step-back。实例问题可能需要先检索上位原则,例如“这个重试为何重复扣款”先查副作用与幂等原则。抽象查询适合补充原则证据,但不能替代原始对象、错误码和 Trace 的精确查询。
生成假设文档改善语义召回
HyDE 先由 LLM 生成一段“可能的答案文档”,再对其编码检索真实语料。原论文明确指出假设文档不真实且可能包含错误细节;它依赖 Dense Encoder 的瓶颈把表示映射回真实语料邻域。故 HyDE 结果只能帮助召回,不能作为回答证据。
多个视角提高召回
把同一问题改写为术语、用户语言、异常症状等多个视角,可以提高 Recall,但会放大候选与费用。限制查询数、并行执行、按 ID 去重,并评估每条额外查询的边际贡献。
复杂问题先分解或路由
问题包含多个独立条件时,先生成结构化子查询和依赖关系。例如“比较 A、B 两个版本在欧洲部署的权限和成本”至少包含版本能力、地域合规、成本三类证据。只用完整长句向量检索,某个强语义可能压住其他条件。
无依赖子查询可以并行;后续查询需要前序实体时串行。合并前做 Coverage Check:每个原始约束由哪个子查询覆盖、得到哪些证据、仍缺什么。路由按数据域选择索引和 Retriever,不能用路由结果扩大用户权限。
Reranker 用更高成本换候选精度
Bi-Encoder 适合大规模候选生成,因为查询与文档可分别编码;Cross-Encoder 让查询和每个文档共同编码,通常判断更细但成本随候选数增长。ColBERT 用 Late Interaction 在离线预计算与细粒度交互之间取平衡。选择依据是候选规模、延迟和目标任务,不是榜单名次。
Reranker 评分“与查询相关”不等于“包含足够答案”。训练数据和生产文档域不一致时,可能稳定地把风格相似内容排高。监控重排前后 Gold 证据的位置变化,并保留不过 Rerank 的对照通道;Top-N、Batch 和超时必须进入预算。
多跳与 Agentic Retrieval 需要受控循环
当下一次应检索什么依赖已获得证据时,一次性 Top-K 无法解决。IRCoT 等研究通过交错检索与中间推理处理多步问题;工程实现应把它改造成可观察的证据状态机,而不是开放式“搜到满意为止”。
限制跳数、查询数、重复 Query、候选总量和总延迟;通过查询哈希与证据图检测循环。证据不足是合法终态,不能用模型参数知识补齐检索链后仍声称 Grounded。
把专用 RAG 模式放回失败类型。Self-RAG 在原始研究中通过训练模型生成检索与反思 Token,按需检索并批判证据;它不是在普通 Prompt 后加一句“请反思”。CRAG 引入检索质量评估器,根据置信触发纠正或扩展检索;评估器本身会产生域外误判,必须单独校准。RAPTOR 构建递归聚类与摘要树,适合跨长文档、不同抽象层级的问题,但摘要错误会成为新的索引事实候选。GraphRAG 通过实体关系、社区和社区摘要处理全局 Sensemaking;Microsoft 的原始工作针对的是全局问题,不意味着任何企业问答都应承担图抽取和社区摘要的高昂索引成本。
这些名称对应不同的故障假设:是否需要检索、检索是否可信、证据是否跨层级、问题是否需要全局结构。先证明目标任务确有该失败,再决定是否引入;不要把研究架构名当成采购清单。
用消融决定复杂度是否值得
从 Hybrid 基线开始,在固定数据、权限和模型下逐项加入 Query Rewrite、Rerank、Decomposition、Multi-hop。对整体与失败切片分别记录 Recall@K、MRR/NDCG、Context Precision、端到端正确性、P50/P95 延迟、调用费用和无答案行为。
架构决策不应只保留赢家均值。记录哪些任务退化、增加了什么依赖、超时如何降级、哪一层可以独立回滚。若 Reranker 只在 4% 样本提升且让 P95 超标,可按查询分类路由,而不是全量开启。
故障判断:Recall@20 很高但答案持续引用无关文档,应优先改哪层?
架构 Review:如何阻止 Agentic Retrieval 变成无限搜索?
资料与事实边界
以下以官方文档、标准、论文和原始工程资料为主。厂商 API、价格、限额与模型能力会变化,落地时仍需按目标版本做能力探测和回归。
- Microsoft:Hybrid Search using RRF — RRF 在当前 Hybrid Search 中的计算与并行排名合并机制
- Gao et al.:Precise Zero-Shot Dense Retrieval without Relevance Labels — HyDE 的原始方法与“假设文档可能含错误”的事实边界
- Khattab、Zaharia:ColBERT — Late Interaction 在检索效率和细粒度匹配间的设计
- Trivedi et al.:IRCoT — 交错检索与多步推理的实验方法和适用任务
- Asai et al.:Self-RAG — 按需检索、生成与反思 Token 的训练和推理架构
- Yan et al.:Corrective Retrieval Augmented Generation — 检索评估器与纠正动作的原始方法
- Sarthi et al.:RAPTOR — 递归聚类、摘要树与多抽象层级检索
- Microsoft Research:From Local to Global — GraphRAG 面向全局语料 Sensemaking 的原始研究与适用问题
- Anthropic:Contextual Retrieval — 为 Chunk 补充文档上下文并结合语义/词法检索的工程实验