RAG + Agent 架构模式:检索、推理与执行如何分工

0阅读12分钟

从一次失败的知识问答说起

我在做一个内部运维助手时踩过一个典型的坑:产品希望用户问「上个月订单系统的 P99 延迟是多少,如果超过 500ms 就帮我拉一个工单」。我把这句话直接丢给 RAG pipeline,结果它只回答了延迟数值——因为 RAG 的职责到「找到知识、生成回答」就结束了,后面的「拉工单」根本不在它的能力范围内。

这个场景暴露了一个根本问题:RAG 和 Agent 解决的是不同层面的事情。RAG 负责让模型看到正确的知识,Agent 负责让模型规划步骤、调用工具、推进任务。两者可以组合,但不能混为一谈。

后来我又遇到一个更隐蔽的问题:一个客户支持系统,用户问「我的退款为什么还没到账」,RAG 检索到了退款政策文档,生成了一段漂亮的解释。但用户需要的不是政策说明,而是有人帮他查一下退款卡在哪一步。这个问题本质上需要 Agent——先查订单状态,再查支付流水,如果发现异常就触发工单流程。把这类任务当成纯知识问答处理,答案再流畅也解决不了问题。

2025 年 arXiv 上一篇 Agentic RAG 综述(Singh et al.)把这类系统的演化分成三个阶段:朴素 RAG、高级 RAG、Agentic RAG。核心区别在于,检索是否还是一个固定的「一次取一次答」流程,还是变成了一个可以由 Agent 动态控制的多步推理环节。本文将从理论到工程,把 RAG 和 Agent 的分工边界讲清楚。

理论基础:检索与执行的分层

RAG 的本质

RAG 的核心思路并不复杂——在模型生成回答之前,先从外部知识库检索相关片段,拼到 prompt 里作为上下文。Lewis et al.(2020)在原始论文中把它定义为「retrieval-augmented generation」,重点解决模型知识过时和幻觉问题。

一条标准的 RAG pipeline 包含四个环节:

  1. 索引:文档切块 → 向量化 → 存入向量库
  2. 检索:用户 query 向量化 → 相似度搜索 → 取 top-k
  3. 增强:检索片段拼入 prompt
  4. 生成:LLM 基于上下文生成回答

这套流程对知识问答类任务足够用。但它的局限也很明显:检索只做一次,query 不做改写,结果不做验证,更谈不上执行动作。

一个常见的误解是把 RAG 当成万能药。实际上,RAG 的质量上限取决于检索质量——如果向量库里的 chunk 切分不合理、embedding 模型对领域术语不敏感、或者文档本身有权限隔离没处理好,再强的 LLM 也救不回来。我在实际项目中见过不少团队把精力花在换更强的模型上,而该解决的是 chunk 策略和权限过滤。

Agent 的本质

Agent 的核心是「感知-规划-行动」循环。模型不再只是回答问题,而是拿到一个目标之后,自己拆解步骤、选择工具、执行操作、观察结果、决定下一步。

学界常用的框架是 ReAct(Yao et al., 2022):Thought → Action → Observation 交替进行,直到 Agent 认为信息充足后输出最终答案。

Agent 需要三个基础能力:

  • 规划:把复杂目标拆成可执行的子任务
  • 工具使用:调用搜索、计算、API、数据库等外部能力
  • 记忆:短期记忆存当前对话上下文,长期记忆存历史经验

规划能力是最难做好的部分。简单的 Agent 可以用 chain-of-thought 让模型一步步推理,但遇到复杂的多步任务,往往需要更结构化的规划方法——比如先生成完整的执行计划,再逐步执行,执行过程中根据中间结果调整计划。这就是 Planning 和 Re-planning 的区别。

两层合在一起

当 RAG 和 Agent 组合时,它们的关系是这样的:

  • RAG 是 Agent 可以调用的一种工具(检索工具)
  • Agent 是 RAG pipeline 的控制层(决定什么时候检索、检索什么、检索几次、结果够不够好)

2026 年的主流架构共识是:RAG 层负责权限过滤、召回、重排和引用溯源;Agent 层负责目标拆解、工具选择、状态推进和失败处理。不要让 Agent 绕过 RAG 的权限控制直接读所有知识库,也不要把需要多步执行的任务硬塞进单次检索。

三种常见架构模式

模式一:先检索后回答(Classic RAG)

最基础的形态。用户提问 → 检索一次 → LLM 生成回答。适合 FAQ、文档问答、知识库查询。

# Classic RAG:一次检索,一次生成
def classic_rag(query: str) -> str:
    chunks = retriever.search(query, top_k=5)
    context = "\n".join(c.text for c in chunks)
    return llm.generate(
        system="根据以下上下文回答用户问题,如果上下文无法回答请说明。",
        user=f"上下文:\n{context}\n\n问题:{query}"
    )

这个模式的问题是:如果用户的问题涉及多个文档,或者需要计算、对比、汇总,单次检索很难覆盖所有信息。

我在一个产品手册问答系统里验证过这一点。用户问「A 产品和 B 产品在权限管理上有什么区别」,Classic RAG 经常只检索到 A 产品的文档,因为向量相似度搜索倾向于返回语义最接近的少数几个 chunk,而不是覆盖两个对比对象的所有相关内容。

模式二:先检索后推理(Agentic RAG)

Agent 先拿到检索结果,判断信息够不够。不够就改写 query 再检索,或者调用其他工具补充。循环直到信息充足才生成最终答案。

# Agentic RAG:多轮检索 + 自主判断
def agentic_rag(query: str, max_hops: int = 3) -> str:
    state = {"question": query, "evidence": [], "hops": 0}
 
    while state["hops"] < max_hops:
        # Agent 决策:继续检索还是直接回答
        decision = agent.decide(state)
 
        if decision.action == "answer":
            return agent.answer(state)
 
        if decision.action == "retrieve":
            chunks = retriever.search(decision.query, top_k=5)
            state["evidence"].extend(chunks)
            state["hops"] += 1
 
        elif decision.action == "call_tool":
            result = tools.execute(decision.tool, decision.params)
            state["evidence"].append(result)
            state["hops"] += 1
 
    # 超过最大轮次,用已有信息尽力回答
    return agent.answer(state)

IBM 在 2025 年的技术报告中把这种模式叫做「self-corrective RAG」——Agent 能自我评估检索质量,发现结果不相关就重新检索,发现信息有矛盾就交叉验证。

Weaviate 的技术博客把这种架构叫做「Agentic RAG」,强调 Agent 对检索流程的控制权。核心区别是:Classic RAG 的检索是确定性流程(固定 query、固定 top-k、固定一轮),Agentic RAG 的检索是动态流程(Agent 决定何时停、何时继续、何时换策略)。

模式三:多 Agent 协作检索(Multi-Agent RAG)

复杂任务拆给多个专业 Agent,每个 Agent 负责一个子领域或子任务,最后由一个编排 Agent 汇总结果。

# Multi-Agent RAG:分工检索 + 汇总
def multi_agent_rag(query: str) -> str:
    # 编排 Agent 拆解任务
    sub_tasks = orchestrator.decompose(query)
    results = {}
 
    for task in sub_tasks:
        # 根据子任务类型分配给专业 Agent
        if task.domain == "internal_docs":
            results[task.id] = internal_agent.execute(task)
        elif task.domain == "public_web":
            results[task.id] = web_agent.execute(task)
        elif task.domain == "database":
            results[task.id] = sql_agent.execute(task)
 
    # 编排 Agent 汇总所有子结果
    return orchestrator.synthesize(query, results)

Singh et al. 的综述把多 Agent 架构分成两类:一类是主从式(一个 orchestrator 调度多个 specialist),另一类是对等式(Agent 之间互相传递消息协商)。企业场景里主从式更常见,因为可控性更好。

多 Agent 架构的核心挑战是协调成本。Agent 数量增加后,orchestrator 的 prompt 越来越长,子任务之间的依赖关系越来越复杂,出错的概率也随之上升。Dedicatted 的企业实践文章建议保持 Agent 团队精简,每个 Agent 的能力描述要足够清晰,否则 orchestrator 会分配错误。

架构流程图

下面是一个典型的 Agentic RAG 系统的完整流程:

流程图画布 · 115%
Mermaid 流程图加载中...

这个图展示了三种路径的分流逻辑:简单问题走 Classic RAG,复杂知识问题走 Agentic 多轮检索,需要执行动作的问题走 Agent + Tool 路径,且执行过程中可以按需触发 RAG。

关键决策对比

RAG vs Fine-tuning vs Agent

维度RAGFine-tuningAgent
核心能力让模型看到外部知识让模型内化特定能力让模型规划和执行多步任务
数据更新改索引即可,无需重训需要重新训练或 LoRA 微调运行时动态获取
延迟检索增加 100-300ms无额外延迟多轮调用,延迟最高
成本向量库 + 检索 APIGPU 训练成本多次 LLM 调用 token 消耗
幻觉风险可通过引用溯源降低知识可能过拟合可通过验证步骤降低
适合场景知识问答、文档搜索固定风格/格式、高频重复任务多步推理、工具调用、流程自动化
不适合需要执行动作的任务知识频繁变化的场景简单 FAQ(杀鸡用牛刀)

四种 RAG 架构模式对比

模式检索次数是否需要 Agent延迟适合问题类型典型场景
Classic RAG1 次单文档事实问答FAQ、产品手册查询
Query Rewriting RAG1 次(改写后)可选模糊/复杂 query用户表述不清的知识搜索
Multi-hop RAG2-N 次(顺序)可选中高组合性问题「A 的 B 的 C 是什么」
Agentic RAG动态(Agent 控制)开放复杂问题研究报告、故障排查

检索策略对比

策略原理优点缺点适用场景
Dense Retrieval向量相似度搜索语义匹配能力强对精确关键词不敏感概念性、描述性问题
BM25 关键词检索词频统计精确匹配、速度快不理解语义产品编号、人名、专有名词
Hybrid (BM25 + Dense)两路结果融合 (RRF)兼顾语义和精确匹配需要调融合权重2026 年的生产默认选项
Hybrid + Reranker检索后用交叉编码器重排精度最高增加约 50ms 延迟对准确率要求高的场景
Graph RAG实体关系图遍历擅长跨文档全局问题构建图成本高主题综述、知识图谱查询

工具执行与知识检索的协作方式

协作方式描述优点风险
检索 → 生成先查知识再生成,不涉及工具简单可控无法执行动作
检索 → 规划 → 执行RAG 提供背景,Agent 规划后调用工具知识驱动决策规划可能出错
执行 → 检索 → 继续执行工具执行中发现需要知识,临时检索按需获取,减少冗余检索中断执行流
生成 → 验证 → 检索先生成答案,再检索验证引用减少幻觉验证失败需要回退

工程实现要点

查询预处理

Agent 在检索之前先理解 query,这一步对召回率影响很大。

用户输入往往是模糊的、口语化的,直接拿去做向量检索效果很差。查询预处理的目标是把用户的原始输入转化成更适合检索的形式。常见的策略有三种:HyDE(让模型先生成一个假设性答案,用这个答案去检索)、Step-back(把具体问题抽象化,检索更宽泛的知识)、Decomposition(把组合问题拆成多个子查询分别检索)。

# 查询预处理:意图识别 + query 改写
def preprocess_query(query: str) -> dict:
    analysis = llm.generate(
        system="分析用户查询的意图、领域、复杂度。",
        user=query
    )
 
    result = {
        "original": query,
        "intent": analysis.intent,       # "search" | "execute" | "mixed"
        "domain": analysis.domain,        # "internal" | "public" | "mixed"
        "complexity": analysis.complexity # "simple" | "multi_hop" | "agentic"
    }
 
    # 如果 query 模糊,做 step-back 改写
    if analysis.complexity == "ambiguous":
        result["rewritten"] = step_back_rewrite(query)
 
    # 如果是组合问题,拆分子查询
    if analysis.complexity == "multi_hop":
        result["sub_queries"] = decompose_query(query)
 
    return result

混合检索 + 重排序

2026 年的生产系统基本都用 hybrid retrieval 作为默认方案。

纯向量检索的问题是对精确匹配不敏感——用户搜一个产品编号、一个错误码、一个专有名词,embedding 模型很可能把它映射到语义相近但不相关的文档上。BM25 正好相反,它靠词频统计,对精确匹配很在行,但完全不理解语义。两路检索结果用 Reciprocal Rank Fusion 融合,再送 reranker 精排,是目前精度最高的方案。

Anthropic 在 2025 年的技术报告中提到,给每个 chunk 前面加一句父文档的摘要,可以把检索准确率提升 35-50%。这个技巧成本很低,值得所有 RAG 系统采用。

# 混合检索:BM25 + Dense + RRF 融合 + Reranker
def hybrid_search(query: str, top_k: int = 20) -> list[Chunk]:
    # 两路并行检索
    dense_results = vector_db.search(query, top_k=top_k)
    bm25_results = bm25_index.search(query, top_k=top_k)
 
    # Reciprocal Rank Fusion 融合
    fused = reciprocal_rank_fusion(
        [dense_results, bm25_results],
        k=60
    )
 
    # 取 top-10 送 reranker 精排
    candidates = sorted(fused.items(), key=lambda x: -x[1])[:10]
    reranked = reranker.rerank(query, candidates)
 
    return reranked[:5]
 
 
def reciprocal_rank_fusion(
    rankings: list[list[str]],
    k: int = 60
) -> dict[str, float]:
    """融合 N 个排序列表为统一分数。"""
    scores: dict[str, float] = {}
    for ranking in rankings:
        for rank, doc_id in enumerate(ranking, start=1):
            scores[doc_id] = scores.get(doc_id, 0.0) + 1.0 / (k + rank)
    return scores

Agent 检索循环的终止条件

Agentic RAG 最怕的是死循环。必须设定明确的退出机制。

退出条件通常有三个:置信度达标(Agent 认为信息足够回答问题)、达到最大轮次(防止无限循环)、或者触发降级(检测到重复检索相同内容)。生产环境中,我见过因为没设 max_hops 导致 Agent 在某个模糊 query 上循环几十次、烧掉大量 token 的案例。

# 带终止条件的 Agent 检索循环
class AgenticRetriever:
    def __init__(self, max_hops=3, min_confidence=0.8):
        self.max_hops = max_hops
        self.min_confidence = min_confidence
 
    def search(self, query: str) -> SearchResult:
        state = SearchState(query=query)
 
        for hop in range(self.max_hops):
            # 检索
            chunks = hybrid_search(state.current_query)
            state.add_evidence(chunks)
 
            # Agent 评估:信息够不够?
            assessment = self.agent.assess(state)
 
            if assessment.confidence >= self.min_confidence:
                return SearchResult(
                    answer=assessment.answer,
                    evidence=state.all_evidence,
                    hops=hop + 1,
                    confidence=assessment.confidence
                )
 
            if assessment.action == "rewrite_query":
                state.current_query = assessment.new_query
            elif assessment.action == "call_tool":
                tool_result = execute_tool(assessment.tool_call)
                state.add_evidence([tool_result])
 
        # 超过最大轮次,用已有信息尽力回答
        return SearchResult(
            answer=self.agent.force_answer(state),
            evidence=state.all_evidence,
            hops=self.max_hops,
            confidence=state.best_confidence,
            truncated=True  # 标记为未完全解决
        )

引用溯源

生产系统必须做到每个回答都能追溯到来源。

引用溯源不只是在回答末尾加一个「参考资料」列表。正确的做法是:在 prompt 里给每个 chunk 编号,要求模型在每个事实陈述后面标注引用编号,然后在后处理阶段验证这些编号是否真实存在于检索结果中。如果模型引用了不存在的编号,说明出现了引用幻觉,需要触发重新生成或标记警告。

这一步在生产环境中非常关键。没有引用溯源的 RAG 系统,用户无法判断回答是否可靠,出了问题也无法回溯是哪一步检索出了偏差。

# 生成时强制引用,验证时检查引用合法性
def generate_with_citation(query: str, evidence: list[Chunk]) -> Answer:
    # 给每个 chunk 编号,要求模型在回答中标注引用
    numbered = "\n".join(
        f"[{i+1}] {c.text} (来源: {c.source}, 页码: {c.page})"
        for i, c in enumerate(evidence)
    )
 
    response = llm.generate(
        system=(
            "根据上下文回答。每个事实陈述后标注引用编号,"
            "格式:[1][2]。如果上下文无法支持你的回答,明确说明。"
        ),
        user=f"上下文:\n{numbered}\n\n问题:{query}"
    )
 
    # 验证引用:模型引用的编号必须真实存在于证据中
    cited_ids = extract_citations(response.text)
    valid_ids = set(range(1, len(evidence) + 1))
    invalid = [cid for cid in cited_ids if cid not in valid_ids]
 
    if invalid:
        # 引用有幻觉,触发重新生成或标记警告
        response.warning = f"无效引用编号: {invalid}"
 
    return response

生产环境检查清单

部署 RAG + Agent 系统之前,逐项检查:

  • 权限隔离:RAG 层实现了文档级或租户级权限过滤,Agent 无法绕过
  • 检索质量基线:用标注数据集跑过 recall@k 和 MRR,有明确数值
  • 混合检索:同时使用向量检索和关键词检索,用 RRF 或加权融合
  • Reranker:top-k 结果经过交叉编码器重排
  • 最大轮次限制:Agentic loop 有 max_hops 上限,不会无限循环
  • 置信度阈值:Agent 评估置信度低于阈值时,明确告知用户「不确定」而非硬答
  • 引用溯源:每个回答标注信息来源,可追溯到原始文档和位置
  • 超时和降级:单次请求有总超时,超时后降级到简化回答
  • 日志完整:记录每轮检索的 query、返回结果、Agent 决策、工具调用参数
  • 评测管线:定期跑 groundedness、answer relevance、context precision 指标
  • 成本监控:跟踪每次请求的 token 消耗和检索调用次数,设告警阈值
  • 人工兜底:Agent 置信度低或涉及高风险操作时,转人工审核

什么时候不需要 Agent

不是所有场景都需要引入 Agent。我的经验法则是:

只用 RAG 就够了:用户问的是知识问题,答案能从文档里找到,不需要执行动作、不需要多步推理。

需要引入 Agent:用户的请求包含动作(「帮我创建一个工单」「把这份报告发给张三」),或者需要跨多个数据源组合信息,或者需要多步计算和判断。

连 RAG 都不太需要:问题是通用的、模型本身就能回答的,加检索反而增加延迟和噪声。

RAG + Agent 最适合的场景是企业知识助手、客服流程自动化、内部运维、合规审查和复杂研究任务。不适合早期简单 FAQ,也不适合强事务一致性的核心业务操作——那种场景下,传统规则引擎或工作流系统更可靠。

参考资料

评论 0

0 / 1000