课程0基础Agent开发课 / RAG与向量数据库 / Agentic-RAG-让Agent自主决定何时检索什么
— 21 min read

Agentic-RAG-让Agent自主决定何时检索什么

> **[进阶选读]** 本篇适合已掌握基础 RAG 和 LangGraph 基础、希望构建智能化 RAG 系统的读者。Agentic RAG 让 Agent 自主判断"这个问题需要检索吗"、"检索结果够用吗"、"需要多次检索吗",是 RAG 走向智能化的关键一步。

Agentic RAG:让 Agent 自主决定何时检索什么

[进阶选读] 本篇适合已掌握基础 RAG 和 LangGraph 基础、希望构建智能化 RAG 系统的读者。Agentic RAG 让 Agent 自主判断"这个问题需要检索吗"、"检索结果够用吗"、"需要多次检索吗",是 RAG 走向智能化的关键一步。


1.1 标准 RAG 的"强迫症"问题

用户提问

意图分析

需要检索?

LLM 直接回答

选择检索策略

向量检索

关键词检索

图谱检索

Web 检索

检索结果

结果是否
充足?

生成回答

优化查询
重新检索

最终回答

Agentic RAG 决策流程——Agent 根据意图自主判断是否检索及选择检索策略

想象一位助手,无论你问他"今天几号?"还是"公司差旅报销流程是什么?",他都先跑去档案室翻文件,再回来回答。这显然效率低下——有些问题根本不需要查资料。

标准 RAG 就是这样一位"强迫症助手"。它的处理逻辑是固定的:无论什么问题,先检索,再生成。这个固定管道在简单场景下工作良好,但随着企业应用复杂度提升,局限越来越明显。

具体来说,标准 RAG 存在三个核心问题:

问题一:不该检索的时候也检索。 用户问"Python 的 for 循环怎么写?"——这是公开的编程知识,任何 LLM 都掌握,根本无需检索企业知识库。强制检索不仅增加延迟,还可能检索到不相关内容干扰模型。

问题二:检索一次往往不够。 用户问"根据 Q3 财报,我们的产品线中哪条盈利增长最快,这和竞品相比如何?"——这个问题需要先检索财报数据,再检索竞品分析,两次检索的结果还需要综合推理。标准 RAG 的单次检索无法应对这类多步骤问题。

问题三:不验证检索结果是否够用。 标准 RAG 检索到什么就用什么,不会评估"这几段文字能回答用户的问题吗?还需要补充信息吗?"

Agentic RAG 正是为了解决这三个问题而生的。它的核心思想是:给 RAG 系统装上一个"大脑",让它能自主判断、自主规划、自主验证


1.2 Agentic RAG 的三个核心能力

能力一:自主判断——这个问题需要检索吗?

Agent 在执行检索前先评估问题性质。常识性问题、计算问题、编程语法问题,直接用模型自身知识回答;涉及私有数据、实时信息、专业文档的问题,才触发检索。这一步节省了大量不必要的检索开销。

能力二:自主规划——需要检索哪些内容,分几步?

对于复杂问题,Agent 会分解任务。例如"对比我们的 A 产品和竞争对手 B 产品的定价策略",Agent 会拆分为:第一步检索内部产品定价文档,第二步检索竞品信息,第三步综合两次结果生成对比分析。

能力三:自主验证——检索结果够用吗?

每次检索后,Agent 评估:"拿到的这些文档能回答用户的问题吗?"如果不够,它会调整检索策略,换个角度或换个关键词再检索,而不是直接用不充分的信息生成可能出错的答案。


1.3 标准 RAG vs Agentic RAG 对比

维度 标准 RAG Agentic RAG
检索时机 每次必检索 按需检索,可跳过
检索次数 固定 1 次 动态 1~N 次
检索规划 无规划,直接用原始问题 自主分解问题,多步检索
结果验证 不验证,照单全收 评估质量,不够则继续
知识库选择 固定单一知识库 可路由到不同知识库
适用场景 简单问答,单跳推理 复杂推理,多源信息融合
实现复杂度 低,固定管道 高,需要状态机/图结构
延迟 稳定(固定开销) 可变(简单问题更快,复杂问题更慢)

1.4 三种 Agentic RAG 模式

1.4.1 模式一:Router 模式(路由到正确的知识库)

企业通常有多个知识库:产品文档、技术文档、HR 政策、财务数据。Router 模式的 Agent 先判断问题属于哪个领域,再路由到对应的知识库检索。

类比:去图书馆查资料,先到咨询台问"这类书在几楼",再去对应楼层找,而不是把每层都搜一遍。

适用场景: 多知识库、多部门的企业系统;问题类型明显分类的场景。

1.4.2 模式二:Multi-hop 模式(多跳推理)

Multi-hop(多跳推理)是指第二次检索依赖第一次检索的结果。"跳"是一个比喻,就像在信息之间跳格子——从一个信息跳到下一个关联信息。

示例: 用户问"负责 A 项目的团队,他们最近提交的需求文档在哪里?"

  • 第一跳:检索"A 项目负责团队是谁" → 得到"B 团队"
  • 第二跳:用"B 团队 + 需求文档"再检索 → 得到具体文档

适用场景: 需要通过中间结果推导最终答案的问题;知识库中有引用关系的文档体系。

1.4.3 模式三:Iterative 模式(迭代检索)

Iterative(迭代)模式是"检索 → 评估 → 不够用 → 再检索"的循环。每次检索后,Agent 评估信息的完整性和相关性,直到满足条件才停止。

这就像写论文时查文献——查到一篇,发现它引用了另一篇更重要的文章,于是再去找那篇,如此反复,直到觉得资料充分为止。

适用场景: 问题复杂度未知,无法预判需要几次检索;需要高质量、高置信度输出的场景。


1.5 Agentic RAG 决策流程图

不需要
常识/计算

需要

产品文档

技术文档

多个来源

足够

不够
缺失关键信息

未超限

已超限

用户提问

是否需要检索?
判断问题类型

直接用LLM生成答案

检索路由
哪个知识库?

检索产品知识库

检索技术知识库

分解子问题
逐一检索

聚合检索结果

评估检索质量
信息足够吗?

生成最终答案

迭代次数
是否超限?

调整检索策略
换关键词/换角度

用已有信息生成
并说明信息不完整

返回用户


1.6 用 LangGraph 实现 Agentic RAG

LangGraph 是 LangChain 生态中专门用于构建有状态 Agent 工作流的框架(详见第 5 章 LangGraph 基础)。它用"节点"(每个决策步骤)和"边"(步骤之间的流转逻辑)描述 Agent 的决策流程,天然适合实现 Agentic RAG。

以下为代码示例,非程序员可跳过代码,重点看文字说明。

整体设计思路:

  • 每个决策步骤是一个"节点"(Node)
  • 节点之间的跳转逻辑是"条件边"(Conditional Edge)
  • 整个流程的运行状态存储在一个"状态字典"中
python
from typing import TypedDict, Literal, List
from langgraph.graph import StateGraph, START, END
from langchain_openai import ChatOpenAI
from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings
from langchain_core.messages import HumanMessage, SystemMessage

# ============================================================
# 第一步:定义状态结构
# 状态就像一个"工作台",记录整个检索过程中的所有信息
# ============================================================
class AgenticRAGState(TypedDict):
    question: str                  # 用户的原始问题
    needs_retrieval: bool          # 是否需要检索
    retrieved_docs: List[str]      # 已检索到的文档内容
    retrieval_quality: str         # 检索质量评估结果: "sufficient" / "insufficient"
    iteration_count: int           # 已迭代次数(防止无限循环)
    search_query: str              # 当前使用的检索关键词
    answer: str                    # 最终生成的答案

# ============================================================
# 第二步:初始化工具
# ============================================================
llm = ChatOpenAI(model="gpt-4o", temperature=0)
embeddings = OpenAIEmbeddings()

# 假设已有一个向量数据库(见第01章:如何构建向量库)
vectorstore = Chroma(
    collection_name="company_docs",
    embedding_function=embeddings,
    persist_directory="./chroma_db"
)
retriever = vectorstore.as_retriever(search_kwargs={"k": 4})

# ============================================================
# 第三步:定义各个节点函数
# ============================================================

def assess_retrieval_need(state: AgenticRAGState) -> AgenticRAGState:
    """
    节点1:判断是否需要检索
    LLM评估问题类型,决定是否触发检索流程
    """
    response = llm.invoke([
        SystemMessage(content="""你是一个智能路由助手。
        判断用户问题是否需要检索企业知识库。
        需要检索的情况:涉及公司内部政策、产品信息、业务流程、私有数据。
        不需要检索的情况:通用常识、数学计算、编程语法、历史知识。
        只回答 yes 或 no。"""),
        HumanMessage(content=f"问题:{state['question']}")
    ])

    needs_retrieval = response.content.strip().lower() == "yes"
    return {**state, "needs_retrieval": needs_retrieval, "iteration_count": 0}


def generate_search_query(state: AgenticRAGState) -> AgenticRAGState:
    """
    节点2:生成优化的检索关键词
    将用户问题转化为更适合向量检索的查询语句
    """
    response = llm.invoke([
        SystemMessage(content="将用户问题转化为适合文档检索的简洁关键词,只输出关键词,不要解释。"),
        HumanMessage(content=state["question"])
    ])
    search_query = response.content.strip()
    return {**state, "search_query": search_query}


def retrieve_documents(state: AgenticRAGState) -> AgenticRAGState:
    """
    节点3:执行检索
    """
    docs = retriever.invoke(state["search_query"])
    doc_contents = [doc.page_content for doc in docs]
    # 合并新旧检索结果(迭代检索时会累积)
    all_docs = state.get("retrieved_docs", []) + doc_contents
    return {**state, "retrieved_docs": all_docs}


def evaluate_retrieval_quality(state: AgenticRAGState) -> AgenticRAGState:
    """
    节点4:评估检索质量
    判断已有文档是否足以回答用户问题
    """
    docs_text = "\n---\n".join(state["retrieved_docs"])

    response = llm.invoke([
        SystemMessage(content="""评估检索到的文档是否足以回答用户问题。
        如果文档包含足够的相关信息,回答 sufficient。
        如果文档信息不足、不相关或缺少关键内容,回答 insufficient。
        只回答 sufficient 或 insufficient。"""),
        HumanMessage(content=f"用户问题:{state['question']}\n\n检索到的文档:\n{docs_text}")
    ])

    quality = response.content.strip().lower()
    return {**state, "retrieval_quality": quality}


def generate_answer(state: AgenticRAGState) -> AgenticRAGState:
    """
    节点5:生成最终答案
    """
    docs_text = "\n---\n".join(state.get("retrieved_docs", []))
    context_prompt = f"参考文档:\n{docs_text}\n\n" if docs_text else ""

    response = llm.invoke([
        SystemMessage(content=f"你是企业知识库助手。根据提供的文档回答问题,如信息不完整请说明。{context_prompt}"),
        HumanMessage(content=state["question"])
    ])
    return {**state, "answer": response.content}


def refine_search_query(state: AgenticRAGState) -> AgenticRAGState:
    """
    节点6:优化检索策略(用于迭代检索)
    在前一次检索不够时,换一个角度重新生成查询词
    """
    response = llm.invoke([
        SystemMessage(content="前次检索结果不足以回答问题。请从不同角度生成新的检索关键词。只输出关键词。"),
        HumanMessage(content=f"原始问题:{state['question']}\n已用查询词:{state['search_query']}")
    ])
    new_query = response.content.strip()
    return {
        **state,
        "search_query": new_query,
        "iteration_count": state["iteration_count"] + 1
    }

# ============================================================
# 第四步:定义条件边(决策逻辑)
# ============================================================

def route_after_assessment(state: AgenticRAGState) -> Literal["generate_search_query", "generate_answer"]:
    """判断是否跳过检索直接生成"""
    if state["needs_retrieval"]:
        return "generate_search_query"
    return "generate_answer"


def route_after_evaluation(state: AgenticRAGState) -> Literal["generate_answer", "refine_search_query"]:
    """判断是否需要继续检索"""
    MAX_ITERATIONS = 3  # 最多迭代3次,防止无限循环
    if state["retrieval_quality"] == "sufficient" or state["iteration_count"] >= MAX_ITERATIONS:
        return "generate_answer"
    return "refine_search_query"

# ============================================================
# 第五步:构建图结构
# ============================================================
graph_builder = StateGraph(AgenticRAGState)

# 添加节点
graph_builder.add_node("assess_retrieval_need", assess_retrieval_need)
graph_builder.add_node("generate_search_query", generate_search_query)
graph_builder.add_node("retrieve_documents", retrieve_documents)
graph_builder.add_node("evaluate_retrieval_quality", evaluate_retrieval_quality)
graph_builder.add_node("generate_answer", generate_answer)
graph_builder.add_node("refine_search_query", refine_search_query)

# 添加边(流程连接)
graph_builder.add_edge(START, "assess_retrieval_need")
graph_builder.add_conditional_edges("assess_retrieval_need", route_after_assessment)
graph_builder.add_edge("generate_search_query", "retrieve_documents")
graph_builder.add_edge("retrieve_documents", "evaluate_retrieval_quality")
graph_builder.add_conditional_edges("evaluate_retrieval_quality", route_after_evaluation)
graph_builder.add_edge("refine_search_query", "retrieve_documents")  # 迭代回到检索节点
graph_builder.add_edge("generate_answer", END)

# 编译成可执行的图
agentic_rag = graph_builder.compile()

# ============================================================
# 第六步:使用
# ============================================================
result = agentic_rag.invoke({
    "question": "我们公司差旅报销的上限是多少,出租车票需要发票吗?",
    "retrieved_docs": [],
    "search_query": "",
    "answer": ""
})

print(result["answer"])

代码关键点说明:

route_after_assessment 函数是第一个关键决策点——它检查 needs_retrieval 字段,决定是直接调用 LLM 生成答案(绕过整个检索流程),还是进入检索规划环节。

route_after_evaluation 函数是第二个关键决策点——检索质量评估后,如果文档不够用且迭代次数未超限,就回到检索节点(refine_search_queryretrieve_documents),形成一个循环;直到质量评估通过或达到最大迭代次数才退出循环生成答案。

MAX_ITERATIONS = 3 是一个安全阀,防止 Agent 陷入无限检索循环,这在实际生产中非常重要。


1.7 与第 11 章 Self-RAG、CRAG 的关系

本系列第 11 章介绍了 Self-RAG 和 CRAG,第 8 章前沿方向也涉及 Agentic RAG 的概念。这几篇文章有所重叠,但侧重点不同:

  • 第 11 章(Self-RAG/CRAG):聚焦"自我纠错"机制,即如何评估和修正检索结果的质量;更偏学术原理。
  • 第 8 章前沿方向:从宏观角度介绍 Agentic RAG 的发展趋势和前沿进展。
  • 本章(第 15 章):聚焦工程实现,用 LangGraph 构建完整的 Agentic RAG 系统;更偏生产落地。

三者结合看,能对 Agentic RAG 形成完整认识:为什么要做(第 11 章的问题)→ 未来方向(第 8 章)→ 怎么做(本章)。


1.8 实战案例:企业内部问答系统

以下是一个接近真实场景的企业问答系统实现,展示 Agentic RAG 如何处理不同类型的问题。

以下为代码示例,非程序员可跳过代码,重点看文字说明。

python
# 场景模拟:HR 助手,对接公司 HR 知识库
# 展示三种不同问题的处理路径

test_questions = [
    # 场景1:不需要检索(LLM直接回答)
    "Python 列表和元组的区别是什么?",

    # 场景2:简单检索(一次检索即可)
    "公司的年假天数政策是什么?",

    # 场景3:多跳推理(需要多步检索)
    "上海研发中心的试用期考核标准和北京总部一样吗?",
]

for question in test_questions:
    print(f"\n{'='*50}")
    print(f"问题:{question}")

    result = agentic_rag.invoke({
        "question": question,
        "retrieved_docs": [],
        "search_query": "",
        "answer": ""
    })

    print(f"是否触发检索:{result['needs_retrieval']}")
    print(f"检索迭代次数:{result['iteration_count']}")
    print(f"答案:{result['answer'][:200]}...")

实际运行效果说明:

第一个问题(Python 语法)——Agent 判断不需要检索,needs_retrieval=False,直接由 LLM 回答,整个流程跳过了向量数据库,延迟从约 1.5 秒降低到约 0.5 秒。

第二个问题(年假政策)——触发检索,一次检索即评估为"足够",iteration_count=0,正常流程完成。

第三个问题(跨地区对比)——触发检索,第一次只检索到上海政策,评估为"不够",第二次补充检索北京政策,两次结果合并后生成对比答案,iteration_count=1


1.9 注意事项与最佳实践

关于延迟: Agentic RAG 的延迟不稳定。简单问题因跳过检索而更快,复杂问题因多轮检索而更慢。建议在 UI 上展示"正在思考/正在检索"的进度提示,提升用户体验。

关于成本: 每次评估检索质量都要调用一次 LLM,成本比标准 RAG 高。可以用小模型(如 GPT-4o-mini)做评估,用大模型只做最终答案生成,以此控制成本。

关于幻觉防控: 迭代上限(MAX_ITERATIONS)非常重要。超过上限后应明确告知用户"信息不足,以下答案可能不完整",而不是用不足的信息强行生成看起来完整的答案。

关于适用场景: 不是所有 RAG 系统都需要升级为 Agentic RAG。如果业务场景简单、问题类型固定,标准 RAG 的稳定性和可预测性反而是优点。Agentic RAG 适合:问题类型多样、需要多步推理、有多个知识库需要路由的场景。


Agentic RAG 要解决的核心问题是:标准 RAG 不会思考。它不管问什么都先检索,检索到什么都往 Prompt 里塞,不验证够不够用。加上决策能力后,它能判断要不要检索、检索什么、检索够了吗——对应 Router、Multi-hop、Iterative 三种模式。

LangGraph 的图结构适合做这件事,节点执行、条件边决策、状态字典传上下文。

本页目录