Agentic RAG vs 简单RAG:2026年选型实战

3 阅读3分钟

简单RAG能解决80%的问题,但剩下的20%才是生产环境的噩梦——多文档对比、复杂推理,这些场景简单RAG根本扛不住。

调研了两个月,踩了不少坑,记录一下。


结论先行

场景方案
FAQ机器人简单RAG
单文档问答简单RAG + reranker
多文档综合Agentic RAG
对比类问题Agentic RAG
延迟敏感简单RAG
质量优先Agentic RAG

延迟敏感用简单RAG,质量优先用Agentic RAG。


简单RAG vs Agentic RAG

简单RAG:
用户问题 → 检索 → 上下文 → LLM生成

Agentic RAG:
用户问题 → Agent规划 → 多次检索 → 反思 → 可能重试 → 综合答案

Agentic RAG会"思考":当前结果够不够?要不要换个角度再查?

代价:延迟和成本。Agentic RAG一次查询可能触发3-5次LLM调用。


简单RAG的瓶颈

把简单RAG调优到83%准确率后,遇到三个瓶颈:

1. 多文档对比

问"比较A产品和B产品",简单RAG各检索5条塞给LLM,结果经常混乱漏维度。

Agentic RAG做法:先规划"从哪些维度比较",分头查,再汇总。

2. 模糊查询

问"那个去年Q3的性能优化文档",简单RAG搜"性能优化"返回一堆结果。

Agentic RAG做法:分析为"性能优化 + 2025Q3",精准定位。

3. 复杂推理

问"切换架构需要哪些准备",需要多跳推理,简单RAG很难处理。


框架选型

框架特点
LangChain LCEL生态成熟
LlamaIndex Workflows索引能力强
LangGraph状态流清晰,可视化
OpenAI Assistants托管快速

选了LangGraph——状态管理清晰,支持可视化调试。


LangGraph实现

from langgraph.graph import StateGraph, END
from typing import TypedDict

class RAGState(TypedDict):
    query: str
    plan: list[str]
    current_step: int
    retrieved_docs: list
    response: str
    iteration: int

def planning_node(state: RAGState) -> RAGState:
    """规划:决定检索什么"""
    plan = llm.predict(f"""
    分析这个问题,决定需要检索的关键词:
    {state['query']}
    输出JSON数组。
    """)
    state["plan"] = json.loads(plan)
    state["current_step"] = 0
    return state

def retrieval_node(state: RAGState) -> RAGState:
    """检索:根据plan执行"""
    keyword = state["plan"][state["current_step"]]
    docs = vector_store.similarity_search(keyword, k=5)
    state["retrieved_docs"].extend(docs)
    state["current_step"] += 1
    return state

def evaluation_node(state: RAGState) -> RAGState:
    """评估:判断是否继续检索"""
    is_enough = llm.predict(f"""
    问题:{state['query']}
    已检索:{state['retrieved_docs']}
    是否足够?(是/否)
    """)
    state["needs_more"] = "否" not in is_enough
    state["iteration"] += 1
    return state

def generation_node(state: RAGState) -> RAGState:
    """生成:综合结果"""
    docs = deduplicate_and_rank(state["retrieved_docs"])
    context = "\n".join([d.content for d in docs[:5]])

    response = llm.predict(f"""
    问题:{state['query']}
    内容:{context}
    """)
    state["response"] = response
    return state

# 构建图
workflow = StateGraph(RAGState)
workflow.add_node("planning", planning_node)
workflow.add_node("retrieval", retrieval_node)
workflow.add_node("evaluation", evaluation_node)
workflow.add_node("generation", generation_node)

workflow.set_entry_point("planning")
workflow.add_edge("planning", "retrieval")
workflow.add_edge("retrieval", "evaluation")

# 条件边:评估结果决定下一步
workflow.add_conditional_edges(
    "evaluation",
    lambda s: "retrieval" if s["needs_more"] and s["iteration"] < 3 else "generation",
    {"retrieval": "retrieval", "generation": "generation"}
)
workflow.add_edge("generation", END)

app = workflow.compile()

踩坑实录

坑一:延迟失控

P99延迟飙到几秒。

解法:降级策略

async def query_with_fallback(query: str, timeout: float = 3.0):
    try:
        return await asyncio.wait_for(agentic_rag(query), timeout=timeout)
    except asyncio.TimeoutError:
        return await simple_rag(query)  # 超时降级

坑二:成本爆炸

成本是简单RAG的3-5倍。

解法:路由层

def route(query: str) -> str:
    # 简单问句走简单RAG
    if len(query) < 20 and is_simple(query):
        return "simple"
    # 复杂问句走Agentic RAG
    if any(k in query for k in ["比较", "哪些", "为什么"]):
        return "agentic"
    return "simple"

现在30%走Agentic RAG,70%走简单RAG,成本可控。

坑三:调试困难

Agent行为不透明。

解法:执行日志

def log_step(step: str, state: dict):
    logger.info({
        "step": step,
        "query": state["query"],
        "docs": len(state.get("retrieved_docs", [])),
        "needs_more": state.get("needs_more"),
    })

配合LangGraph可视化,排查效率高很多。


踩坑小结

  1. 简单RAG调优到极致再上Agentic——80%的问题简单RAG能解决
  2. 加路由层——别所有query都走Agentic,延迟敏感场景受不了
  3. 限制重试次数——最多3次,避免无限循环
  4. 加降级策略——超时自动切简单RAG
  5. 日志要详细——Agent行为不透明,排查全靠日志

选型代码

def select_rag_approach(query: str, latency_sensitive: bool) -> str:
    """
    query: 用户问题
    latency_sensitive: 是否延迟敏感
    """
    if latency_sensitive:
        return "simple_rag"

    is_complex = any(k in query for k in ["比较", "哪些", "为什么", "如何", "多久"])

    if is_complex:
        return "agentic_rag"
    return "simple_rag"