Agent AtlasAI Agent 知识库
04.03记忆与知识 · Memory & Knowledge

Agentic RAG 与高级检索

查询改写、多跳检索、自我反思、GraphRAG 与上下文压缩:当一次检索不够时怎么办。

用户的问题往往不是「一次检索就能回答」的。它可能含糊、需要多跳、资料分散,甚至自相矛盾。Agentic RAG 让模型参与检索决策,而不是被动接收 top-k。

一次检索为什么不够

问题类型例子普通 RAG 的失败
指代/省略「那它的价格呢?」查询缺上下文,检不到
多跳「CEO 母校所在城市的天气」需要先查 CEO,再查城市
对比「A 和 B 的政策差异」只召回一方
覆盖不足答案分散在 5 份文档top-k 只取到 2 份
冲突两份文档说法不同随机选一个,不说明冲突
需要计算「按新政策能退多少」检索到政策,但没算

Agentic RAG 的核心是:把检索当成一个可以反复调用的工具,由模型判断何时检索、检索什么、结果够不够。

五种模式

1. 查询改写

把口语化、含指代的问题改写成独立、可检索的查询:

text
对话:用户问「A1024 能退吗」,客服答「可以」,用户问「那要多久?」
改写:「订单 A1024 的退款到账需要多久?」

也可以生成多个查询变体(Multi-Query),分别检索后合并。

2. 查询分解

把复杂问题拆成子问题:

text
原问题:对比 ACME 与 Beta 的退款政策,并说明对跨境订单的影响。
子问题:
1. ACME 的退款政策是什么?
2. Beta 的退款政策是什么?
3. 两家政策对跨境订单分别怎么规定?

每个子问题独立检索,再综合。注意子问题之间可能有依赖,需要按顺序。

3. 迭代检索

图 1 · 迭代检索循环
01判断现有资料够不够?缺什么?
02检索改写查询,调用检索工具
03评估结果相关吗?是否矛盾?
04收敛够了就回答,不够继续或停止

关键是停止条件:

  • 最多 N 轮(通常 2–4 轮);
  • 新检索没有带来新信息;
  • 预算耗尽;
  • 已能回答且引用充分。

没有停止条件,Agent 会一直「再查一下」,成本失控。

4. 自我反思(Self-RAG 风格)

让模型对检索结果和草稿做检查:

  • 这个段落与问题相关吗?(相关性)
  • 它支持这个结论吗?(支撑性)
  • 回答是否引入了资料外的内容?(忠实性)
  • 是否还有未覆盖的子问题?(完整性)

不通过就补充检索或修改答案。注意:反思必须有外部信号,否则只是自我肯定(见〈反思〉)。

5. 多源路由

不同问题查不同来源:

text
政策问题   → 政策库
订单问题   → 订单 API
产品问题   → 产品文档
时效问题   → 公告/FAQ

路由器可以是规则、分类模型或 LLM。路由错误比检索错误更致命——查错库,怎么排都好不了。

GraphRAG:什么时候值得

当问题需要跨文档的实体关系或全局总结时,向量检索很吃力:

  • 「这个供应商和哪些子公司有交易?」
  • 「过去一年所有客户投诉的共同主题是什么?」

GraphRAG 先从文档抽取实体与关系,构建知识图谱,再用图遍历或社区摘要回答。代价:

  • 抽取成本高,且会出错;
  • 图谱维护复杂;
  • 更新不如向量索引灵活。

适合实体关系密集、问题相对稳定的领域(法律、供应链、金融)。不适合快速变化的通用问答。

一个最小实现

python
def agentic_rag(question: str, max_rounds: int = 3):
    queries = rewrite(question)
    evidence = []
    seen = set()

    for round_no in range(max_rounds):
        for q in queries:
            for doc in hybrid_search(q, top_k=8):
                if doc.id not in seen:
                    seen.add(doc.id)
                    evidence.append(doc)

        assessment = assess(question, evidence)
        if assessment.sufficient:
            break
        if not assessment.missing:
            break
        queries = rewrite_for_gaps(question, evidence, assessment.missing)

    if not evidence:
        return "根据现有资料无法回答。", []

    answer = generate(question, evidence, require_citations=True)
    if not citations_valid(answer, evidence):
        answer = generate(question, evidence, strict=True)
    return answer, evidence

assess 是一个结构化输出调用:

json
{
  "sufficient": false,
  "missing": ["Beta 的跨境订单条款"],
  "conflicts": [],
  "next_queries": ["Beta refund policy cross-border"]
}

成本控制

Agentic RAG 的调用次数可能是普通 RAG 的 3–5 倍。控制手段:

  1. 按难度路由:简单问题走普通 RAG,复杂问题才启用多轮;
  2. 限制轮数:2–4 轮,硬上限;
  3. 缓存:相同查询的检索结果、重排结果缓存;
  4. 并行子查询:独立的子问题同时检索;
  5. 小模型做判断:充分性评估、查询改写用便宜模型;
  6. 预算:按任务设置 token/金额上限。

一个实用策略:先用一次普通 RAG 生成答案,同时让模型判断置信度;只有低置信或检测到多跳时才进入迭代。这样大多数请求保持低成本。

评估

除了普通 RAG 的指标,还要看:

  • 检索轮数分布:平均值与 P95;
  • 新增证据的边际收益:第 2、3 轮是否真的提升了答案;
  • 停止决策准确率:该停时停、该继续时继续;
  • 子问题覆盖率:分解是否遗漏;
  • 冲突处理:是否识别并说明矛盾。

构造测试集时,专门加入需要 2–3 跳的问题,以及「资料不足」的问题。后者能检验它是否会编。

反模式

无脑多轮

每轮都检索、每轮都重排,成本翻倍但答案没变好。用「新信息量」判断是否继续。

让模型自己判断「够了」

模型倾向于高估自己掌握的信息。用外部信号:引用覆盖、子问题清单、检索分数。

忽略冲突

两份文档说法不同,直接选一个。应该明确指出冲突、给出来源与时间,让用户判断。

子问题爆炸

把问题拆成 12 个子问题,每个都检索。限制分解数量,优先合并。

小结

  • Agentic RAG 把检索变成可决策、可迭代的动作。
  • 五种手段:改写、分解、迭代、反思、路由;GraphRAG 用于关系密集场景。
  • 停止条件与预算比「多查几轮」更重要。
  • 简单问题不要用复杂管线,按难度路由。
  • 评估要看轮数收益、停止准确率和冲突处理。

下一篇讲连接外部世界的标准协议:MCP。

读完了?进度会保存在本地浏览器。
内容会持续修订。发现错误或想补充,欢迎反馈。 遇到问题?查排查手册 →