Skip to content

Agentic RAG:从检索到自主推理

超越传统 RAG 的被动检索模式——让 AI Agent 自主决策"要不要搜"、"搜什么"、"搜几次",实现多步推理、查询改写、工具增强的智能检索系统。


1. 从传统 RAG 到 Agentic RAG:为什么需要进化

1.1 传统 RAG 的局限:检索一次就生成

传统 RAG 的工作模式:

  用户提问 → 向量检索(1 次)→ 拼接 Context → LLM 生成
  
  局限性:
  ❌ 只检索一次,可能检索到错误内容
  ❌ 无法判断"需不需要检索"
  ❌ 复杂问题需要多步检索,但只做了一步
  ❌ 不会改写查询,用户问得模糊就搜得差
  ❌ 不会用其他工具(数据库、API、计算器)

1.2 Agentic RAG:让 Agent 自主决策检索

Agentic RAG 的工作模式:

  用户提问

    ├─→ Agent 思考:需要检索吗?
    │     │
    │     ├─→ 不需要 → 直接用已有知识回答
    │     │
    │     └─→ 需要 → 用什么工具检索?
    │           │
    │           ├─→ 改写查询 → 向量检索 → 结果够吗?
    │           │     │
    │           │     ├─→ 够了 → 生成答案
    │           │     └─→ 不够 → 换个角度再搜
    │           │
    │           ├─→ SQL 查询(结构化数据)
    │           └─→ API 调用(实时数据)

1.3 核心区别:被动 vs 自主

维度传统 RAGAgentic RAG
检索决策每次都检索Agent 决定要不要检索
检索次数固定 1 次动态 1~N 次
查询原始问题Agent 改写优化
数据源仅向量库向量库 + SQL + API + 搜索
推理单步多步推理、反思

第 1 章核心知识回顾:

概念一句话解释
传统 RAG被动检索一次就生成,无法应对复杂问题
Agentic RAGAgent 自主决策检索策略,支持多步推理
核心进化从"固定流水线"到"智能决策"

2. 检索决策:Agent 决定要不要搜

2.1 路由 Agent:分类问题类型

python
from openai import AsyncOpenAI

client = AsyncOpenAI()

ROUTER_PROMPT = """分析用户问题,决定如何处理。

分类规则:
- "direct": 通用知识或闲聊,直接回答即可
- "retrieve": 需要查询知识库(公司文档、产品信息等)
- "sql": 需要查询数据库(数字、统计、报表)
- "web_search": 需要搜索互联网(实时信息、新闻)

输出 JSON:{"action": "direct|retrieve|sql|web_search", "reason": "一句话理由"}"""

async def route_question(question: str) -> dict:
    response = await client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {"role": "system", "content": ROUTER_PROMPT},
            {"role": "user", "content": question},
        ],
        response_format={"type": "json_object"},
    )
    return json.loads(response.choices[0].message.content)

# 示例
# "你好" → {"action": "direct", "reason": "闲聊"}
# "我们公司退货政策是什么" → {"action": "retrieve", "reason": "需要查知识库"}
# "上个月销售额多少" → {"action": "sql", "reason": "需要查数据库"}
# "今天比特币价格" → {"action": "web_search", "reason": "需要实时信息"}

2.2 置信度评估:检索结果够不够好

python
async def evaluate_retrieval(question: str, documents: list[str]) -> dict:
    """评估检索结果是否足够回答问题"""
    prompt = f"""评估以下检索结果能否回答用户问题。

用户问题:{question}

检索到的文档:
{chr(10).join(f'[{i+1}] {doc[:200]}' for i, doc in enumerate(documents))}

输出 JSON:
{{"sufficient": true/false, "confidence": 0.0~1.0, "missing": "缺少什么信息"}}"""
    
    response = await client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": prompt}],
        response_format={"type": "json_object"},
    )
    return json.loads(response.choices[0].message.content)

2.3 自适应检索:动态决定检索策略

python
async def adaptive_retrieve(question: str, knowledge_base) -> list[str]:
    """自适应检索:根据结果质量决定是否继续"""
    all_documents = []
    queries_tried = [question]
    max_attempts = 3
    
    for attempt in range(max_attempts):
        # 检索
        query = queries_tried[-1]
        docs = await knowledge_base.search(query, top_k=5)
        all_documents.extend(docs)
        
        # 评估
        eval_result = await evaluate_retrieval(question, all_documents)
        
        if eval_result["sufficient"] or eval_result["confidence"] > 0.8:
            break
        
        # 不够好 → 改写查询重新搜
        new_query = await rewrite_query(question, eval_result["missing"])
        queries_tried.append(new_query)
    
    return all_documents

💡 "检索决策"是 Agentic RAG 最关键的一步——不该检索的时候检索(浪费成本),该检索的时候不检索(回答错误),都是问题。用小模型(GPT-4o-mini)做路由,成本极低。

第 2 章核心知识回顾:

概念一句话解释
路由 Agent分类问题→direct/retrieve/sql/search
置信度评估检索后判断结果够不够好
自适应检索不够好就改写查询、多搜几次

3. 查询改写:让检索更精准

3.1 查询分解:复杂问题拆成子问题

python
async def decompose_query(question: str) -> list[str]:
    """将复杂问题拆解为多个子查询"""
    prompt = f"""将以下复杂问题拆解为 2-4 个独立的子查询,每个子查询可以独立检索。

问题:{question}

输出 JSON:{{"sub_queries": ["子查询1", "子查询2", ...]}}"""
    
    response = await client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": prompt}],
        response_format={"type": "json_object"},
    )
    result = json.loads(response.choices[0].message.content)
    return result["sub_queries"]

# 示例
# "比较 React 和 Vue 在大型项目中的性能和学习曲线"
# → ["React 大型项目性能表现", "Vue 大型项目性能表现", 
#    "React 学习曲线", "Vue 学习曲线"]

3.2 HyDE:假设性文档嵌入

python
async def hyde_retrieval(question: str, knowledge_base) -> list[str]:
    """HyDE: 先让 LLM 生成假设答案,再用假设答案去检索"""
    
    # 1. 生成假设性文档
    prompt = f"请直接回答以下问题(即使你不确定):\n{question}"
    response = await client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": prompt}],
    )
    hypothetical_doc = response.choices[0].message.content
    
    # 2. 用假设文档作为查询去检索(语义更接近真实文档)
    docs = await knowledge_base.search(hypothetical_doc, top_k=5)
    return docs

3.3 Step-back Prompting:退一步思考

python
async def stepback_query(question: str) -> str:
    """生成一个更抽象/更高层次的查询"""
    prompt = f"""将以下具体问题转化为一个更通用的查询,帮助检索到更全面的背景信息。

具体问题:{question}
通用查询:"""
    
    response = await client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": prompt}],
        max_tokens=100,
    )
    return response.choices[0].message.content.strip()

# "FastAPI 中如何处理 WebSocket 连接超时" 
# → "FastAPI WebSocket 连接管理和生命周期"

3.4 多路查询:同一问题的多种表述

python
async def multi_query_retrieve(question: str, knowledge_base) -> list[str]:
    """生成多个变体查询,合并检索结果"""
    
    # 生成变体
    prompt = f"为以下问题生成 3 个不同的查询表述:\n{question}"
    response = await client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": prompt}],
    )
    queries = response.choices[0].message.content.strip().split("\n")
    queries = [question] + [q.strip().lstrip("0123456789.-) ") for q in queries]
    
    # 并行检索所有查询
    import asyncio
    tasks = [knowledge_base.search(q, top_k=3) for q in queries]
    results = await asyncio.gather(*tasks)
    
    # 去重合并
    all_docs = []
    seen = set()
    for docs in results:
        for doc in docs:
            doc_hash = hash(doc[:100])
            if doc_hash not in seen:
                seen.add(doc_hash)
                all_docs.append(doc)
    
    return all_docs

💡 HyDE 是最反直觉但最有效的检索增强技术——先让 LLM"瞎编"一个答案,用这个答案去搜。因为假设答案和真实文档的语义更接近,检索效果比用原始问题好得多。

第 3 章核心知识回顾:

技术核心思路
查询分解复杂问题 → 2-4 个子查询,分别检索
HyDE先生成假设答案,用答案去检索
Step-back具体问题 → 更抽象的查询,获取背景
多路查询同一问题多种表述,合并结果

4. 多步推理:检索-思考-再检索

4.1 ReAct 模式:推理 + 行动交替

python
REACT_PROMPT = """你是一个研究助手,可以使用以下工具:

1. search(query) - 搜索知识库
2. sql_query(sql) - 查询数据库  
3. calculate(expr) - 数学计算

按以下格式思考和行动:
Thought: 我需要...
Action: search("查询内容")
Observation: [工具返回结果]
Thought: 根据结果,我还需要...
Action: ...
...
Thought: 我现在有足够信息了
Answer: 最终答案"""

async def react_agent(question: str, tools: dict, max_steps: int = 5) -> str:
    messages = [
        {"role": "system", "content": REACT_PROMPT},
        {"role": "user", "content": question},
    ]
    
    for step in range(max_steps):
        response = await client.chat.completions.create(
            model="gpt-4o",
            messages=messages,
            stop=["Observation:"],  # 在 Action 后停下来
        )
        content = response.choices[0].message.content
        messages.append({"role": "assistant", "content": content})
        
        # 解析 Action
        if "Answer:" in content:
            return content.split("Answer:")[-1].strip()
        
        if "Action:" in content:
            action = parse_action(content)
            result = await execute_tool(action, tools)
            messages.append({"role": "user", "content": f"Observation: {result}"})
    
    return "超过最大推理步数"

4.2 自我反思:检查答案质量并修正

python
async def self_reflect(question: str, answer: str, sources: list[str]) -> dict:
    """反思答案质量:是否有依据、是否完整、是否准确"""
    prompt = f"""评估以下答案的质量:

问题:{question}
答案:{answer}
参考来源:{chr(10).join(sources[:3])}

检查项:
1. 答案是否有来源支撑(不是编造的)?
2. 答案是否完整回答了问题?
3. 答案是否有明显错误?

输出 JSON:{{"quality": "good|needs_improvement|poor", "issues": ["问题1", ...], "suggestion": "改进建议"}}"""
    
    response = await client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": prompt}],
        response_format={"type": "json_object"},
    )
    return json.loads(response.choices[0].message.content)

4.3 CRAG:纠正性检索增强生成

python
async def corrective_rag(question: str, knowledge_base) -> str:
    """CRAG:检索后评估,不靠谱就换数据源"""
    
    # Step 1: 初始检索
    docs = await knowledge_base.search(question, top_k=5)
    
    # Step 2: 评估检索质量
    eval_result = await evaluate_retrieval(question, docs)
    
    if eval_result["confidence"] > 0.8:
        # 高置信度:直接用
        return await generate_answer(question, docs)
    
    elif eval_result["confidence"] > 0.4:
        # 中等置信度:知识库结果 + 网络搜索补充
        web_docs = await web_search(question)
        all_docs = docs + web_docs
        return await generate_answer(question, all_docs)
    
    else:
        # 低置信度:完全依赖网络搜索
        web_docs = await web_search(question)
        return await generate_answer(question, web_docs)

4.4 多跳推理:链式检索回答复杂问题

python
async def multi_hop_reasoning(question: str, knowledge_base) -> str:
    """多跳推理:需要多次检索才能回答的问题"""
    
    context = []
    current_question = question
    
    for hop in range(3):  # 最多 3 跳
        # 检索
        docs = await knowledge_base.search(current_question, top_k=3)
        context.extend(docs)
        
        # 判断是否需要继续
        eval_prompt = f"""基于已有信息,判断是否能回答原始问题。

原始问题:{question}
已有信息:{chr(10).join(context)}

如果能回答,输出 {{"can_answer": true}}
如果还需要更多信息,输出 {{"can_answer": false, "next_query": "下一步应该查什么"}}"""
        
        result = await llm_json(eval_prompt)
        
        if result["can_answer"]:
            break
        current_question = result["next_query"]
    
    return await generate_answer(question, context)

💡 CRAG 的核心洞察:不是所有检索结果都值得信任——低质量的检索结果喂给 LLM,不如不检索。CRAG 加了一层"纠正"——结果不好就换数据源(网络搜索),而不是硬用差结果。

第 4 章核心知识回顾:

模式核心思路
ReActThought→Action→Observation 循环
自我反思生成答案后检查质量,不行就重来
CRAG检索质量差→切换到网络搜索
多跳推理逐步检索,前一步的结果指导下一步

5. 工具增强检索:超越向量搜索

5.1 多数据源 Agent:向量库 + SQL + API

python
# 定义 Agent 可用的工具
tools = [
    {
        "type": "function",
        "function": {
            "name": "vector_search",
            "description": "搜索知识库文档(产品文档、FAQ、技术手册)",
            "parameters": {
                "type": "object",
                "properties": {"query": {"type": "string"}},
                "required": ["query"],
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "sql_query",
            "description": "查询业务数据库(订单、用户、销售数据)",
            "parameters": {
                "type": "object",
                "properties": {"sql": {"type": "string", "description": "SQL 查询语句"}},
                "required": ["sql"],
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "web_search",
            "description": "搜索互联网(实时信息、最新新闻)",
            "parameters": {
                "type": "object",
                "properties": {"query": {"type": "string"}},
                "required": ["query"],
            },
        },
    },
]

5.2 Text-to-SQL:自然语言查数据库

python
async def text_to_sql(question: str, schema: str) -> str:
    """将自然语言转为 SQL"""
    prompt = f"""根据数据库结构,将用户问题转为 SQL 查询。

数据库结构:
{schema}

规则:只用 SELECT,禁止 DELETE/UPDATE/DROP

用户问题:{question}
SQL:"""
    
    response = await client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": prompt}],
    )
    sql = response.choices[0].message.content.strip().strip("```sql").strip("```")
    return sql

5.3 混合检索编排

python
async def hybrid_agent_search(question: str) -> str:
    """Agent 自主选择数据源进行混合检索"""
    
    response = await client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {"role": "system", "content": "你是一个研究助手,根据问题类型选择合适的工具检索信息。"},
            {"role": "user", "content": question},
        ],
        tools=tools,
    )
    
    # 执行 Agent 选择的工具
    all_results = []
    for tool_call in response.choices[0].message.tool_calls or []:
        name = tool_call.function.name
        args = json.loads(tool_call.function.arguments)
        
        if name == "vector_search":
            result = await knowledge_base.search(args["query"])
        elif name == "sql_query":
            result = await db.execute(args["sql"])
        elif name == "web_search":
            result = await tavily_search(args["query"])
        
        all_results.append({"tool": name, "result": result})
    
    # 基于所有结果生成最终答案
    return await generate_final_answer(question, all_results)

💡 Agentic RAG 的真正力量在于"多工具编排"——向量搜索擅长语义匹配,SQL 擅长精确数据查询,网络搜索擅长实时信息。Agent 根据问题类型自动选择最合适的工具组合。

第 5 章核心知识回顾:

工具擅长场景
向量搜索文档语义匹配(FAQ、技术手册)
SQL 查询精确数据(销售额、用户数)
网络搜索实时信息(新闻、价格)
Agent自动选择最合适的工具

6. 完整实现:构建 Agentic RAG 系统

6.1 系统架构

Agentic RAG 完整架构:

  用户提问

    ├─→ [路由 Agent] 分类问题类型
    │     │
    │     ├─→ direct → LLM 直接回答
    │     │
    │     ├─→ retrieve → [检索 Agent]
    │     │     │
    │     │     ├─→ 查询改写(HyDE/分解/多路)
    │     │     ├─→ 向量检索
    │     │     ├─→ 评估结果置信度
    │     │     ├─→ 不够?→ 改写重搜 / 网络搜索
    │     │     └─→ 生成答案 + 反思
    │     │
    │     ├─→ sql → Text-to-SQL → 执行 → 解释结果
    │     │
    │     └─→ web → 网络搜索 → 提取 → 生成

    └─→ 返回答案 + 引用来源

6.2 完整代码实现

python
class AgenticRAG:
    """Agentic RAG 系统"""
    
    def __init__(self, knowledge_base, db=None):
        self.kb = knowledge_base
        self.db = db
        self.client = AsyncOpenAI()
    
    async def answer(self, question: str) -> dict:
        # Step 1: 路由
        route = await route_question(question)
        
        if route["action"] == "direct":
            return await self._direct_answer(question)
        elif route["action"] == "retrieve":
            return await self._agentic_retrieve(question)
        elif route["action"] == "sql":
            return await self._sql_answer(question)
        elif route["action"] == "web_search":
            return await self._web_answer(question)
    
    async def _agentic_retrieve(self, question: str) -> dict:
        # Step 2: 查询改写 + 多路检索
        docs = await multi_query_retrieve(question, self.kb)
        
        # Step 3: 评估质量
        eval_result = await evaluate_retrieval(question, docs)
        
        # Step 4: 不够好就补充
        if eval_result["confidence"] < 0.6:
            hyde_docs = await hyde_retrieval(question, self.kb)
            docs.extend(hyde_docs)
        
        # Step 5: 生成答案
        answer = await generate_answer(question, docs)
        
        # Step 6: 自我反思
        reflection = await self_reflect(question, answer, docs)
        if reflection["quality"] == "poor":
            # 重新检索 + 生成
            docs = await adaptive_retrieve(question, self.kb)
            answer = await generate_answer(question, docs)
        
        return {"answer": answer, "sources": docs, "confidence": eval_result["confidence"]}

6.3 评测:传统 RAG vs Agentic RAG

指标传统 RAGAgentic RAG
简单问题准确率85%88%
复杂问题准确率45%78%
多跳推理准确率20%65%
平均检索次数1 次1.8 次
平均延迟2s5s
Token 成本1x3x

💡 Agentic RAG 用成本换质量——简单问题差距不大,但复杂问题和多跳推理的提升巨大。建议用路由 Agent 分流:简单问题走传统 RAG(省钱),复杂问题走 Agentic RAG(质量)。


7. 进阶模式与优化

7.1 Graph RAG:知识图谱增强

python
async def graph_rag(question: str, graph_db, vector_db) -> str:
    """Graph RAG:知识图谱 + 向量检索"""
    
    # 1. 提取实体
    entities = await extract_entities(question)
    
    # 2. 图谱检索(实体关系)
    graph_context = await graph_db.query(
        f"MATCH (n)-[r]-(m) WHERE n.name IN {entities} RETURN n, r, m LIMIT 20"
    )
    
    # 3. 向量检索(语义相关文档)
    vector_docs = await vector_db.search(question, top_k=5)
    
    # 4. 合并上下文生成答案
    return await generate_answer(question, graph_context + vector_docs)

7.2 缓存与成本优化

python
import hashlib

class RAGCache:
    """语义缓存:相似问题复用答案"""
    
    def __init__(self, vector_db):
        self.cache_db = vector_db
    
    async def get(self, question: str, threshold: float = 0.95) -> str | None:
        results = await self.cache_db.search(question, top_k=1)
        if results and results[0]["score"] > threshold:
            return results[0]["answer"]
        return None
    
    async def set(self, question: str, answer: str):
        await self.cache_db.insert({"question": question, "answer": answer})

7.3 流式 Agentic RAG

python
async def stream_agentic_rag(question: str):
    """流式输出:让用户看到思考过程"""
    
    yield {"type": "thinking", "content": "正在分析问题类型..."}
    route = await route_question(question)
    yield {"type": "route", "content": f"问题类型: {route['action']}"}
    
    yield {"type": "thinking", "content": "正在检索知识库..."}
    docs = await multi_query_retrieve(question, kb)
    yield {"type": "sources", "content": docs}
    
    yield {"type": "thinking", "content": "正在生成答案..."}
    async for chunk in generate_answer_stream(question, docs):
        yield {"type": "answer", "content": chunk}

附录:Agentic RAG 速查手册

A.1 查询改写技术对比

技术原理适用场景
查询分解复杂问题→子问题多方面比较类问题
HyDE假设答案→检索专业术语不匹配时
Step-back具体→抽象需要背景知识时
多路查询同义改写→合并提高召回率

A.2 Agentic RAG vs 传统 RAG 决策表

场景推荐方案理由
简单 FAQ传统 RAG成本低、速度快
复杂分析Agentic RAG需要多步推理
数据查询Text-to-SQL结构化数据
实时信息Web Search知识库没有
混合问题完整 Agentic RAG自动路由

A.3 核心 Prompt 模板

python
# 路由分类
ROUTER = "分析问题类型: direct/retrieve/sql/web_search"

# 检索评估
EVALUATOR = "评估检索结果是否足够回答问题"

# 自我反思
REFLECTOR = "评估答案质量: good/needs_improvement/poor"

# 查询分解
DECOMPOSER = "将复杂问题拆为 2-4 个子查询"

坚持是一种品格