用 LangGraph 改造单一 RAG 架构:让 Agent 决定调用向量、图遍历还是网络搜索

简介: 本文介绍如何用LangGraph构建智能体,突破传统RAG流水线局限:针对不同问题(如政策查询→向量搜索、关系分析→图遍历、实时资讯→网络搜索),自动路由至最适工具,并通过状态机实现记忆、决策、重试与幻觉校验,让AI真正“思考”而非机械执行。

向量搜索、图遍历还是网络搜索 -- 本文介绍如何用 LangGraph 让智能体为每个问题选择合适的工具。

语义搜索、向量数据库迁移、Graph RAG。这些系统有一个共同缺陷——都是 Pipeline,固定执行序列,问题进、答案出,不论输入是什么。

真实问题并不符合这个假设。

"我们的退款政策是什么?" 需要向量搜索。

"顶级客户和这家供应商之间有什么关联?" 需要图遍历。

"美联储今早宣布了什么?" 需要网络搜索。

Pipeline 只能处理其中一种。Agent 三种都能做,而且能自己决定用哪种。

思维转变:传统 RAG 是一个函数——输入 → 输出。Agentic RAG 是一个状态机——它有记忆、能做决策、可以回溯,只有在对答案有把握时才会终止。

什么让它变得"Agentic"?

Agent 与 Pipeline 的区别体现在四种能力上:

路由实际效果演示:

"公司的数据留存政策是什么?"——Vector(向量搜索)

"排名前 3 的客户与哪些供应商有关联?"——Graph(图搜索)

"美联储今天关于利率的最新公告是什么?"——Web(网络搜索)

"340 的 15% 是多少?"——DirectLLM(直接调用 LLM)

LangGraph 状态机

LangGraph 将 Agent 建模为有向图,节点是函数,边是决策。与 LangChain 的关键差异在于:边可以形成循环。Agent 能不断重试、改写、自我纠错,直到生成一个有把握的答案。

Router:入口节点

Router 是 Agent 的决策中枢。它接收原始查询通过结构化 LLM 调用(

with_structured_output

)将其分为四类:vector、graph、web 或 direct。分类结果在 LangGraph 中成为一条条件边,决定接下来运行哪个检索节点。每次查询、每次改写后都会经过 Router。

Retriever:Vector / Graph / Web

三个检索节点占各自槽位:Vector(FAISS/pgvector 余弦相似度)、Graph(Neo4j 自动生成 Cypher)、Web(Tavily API)。每个节点返回一组 Document 对象,路由决策决定哪个节点被激活。在改写循环中,Router 可能切换到不同的检索器。

Grader:相关性评估

Grader 逐一评估每个检索到的文档:相关或不相关,每份文档各触发一次结构化 LLM 调用。判断逻辑如下:至少 1 个文档相关 → 路由至 Generator;全部不相关且

rewrite_count < 3

→ 路由至 Rewriter;全部不相关且已改写 3 次 → 路由至 web 兜底。这是整个自我纠错机制的核心。

Rewriter:检索失败时的查询修复

检索失败后 Rewriter 启动,接收原始查询(或之前的改写版本),要求 LLM 重新表述——更具体的词语、不同的同义词、更清晰的范围。每次改写后

rewrite_count

递增,上限 3 次,防止无限循环。改写完成后重新路由回 Router。

Generator:LLM 生成答案

Generator 只接收被评为相关的文档,用严格的 Prompt 综合生成有依据的答案:"仅根据以下上下文作答。"答案和来源文档存入 Agent 状态,供后续幻觉检测使用,不相关的文档完全隔离在外。

Hallucination Checker:最终验证

Hallucination Checker 拿到生成的答案和来源文档,判断"答案中的每一个论断是否都有上下文支撑"。如果是 → 返回答案(END);如果否 → 以更严格的 Prompt 路由回 Generator 重新生成。这个节点专门拦截那些表面自信、实则错误的输出。

完整架构——节点如何连接


Router 节点:对查询进行分类

LLM 将输入问题分类为四类之一:

vector_search

(文档事实查询)、

graph_search

(关系查询)、

web_search

(实时信息)或

direct

(LLM 已知的内容)。这是 LangGraph 中的条件边,输出结果决定下一个运行的节点。

Retriever 节点:执行对应工具

三个工具作为 LangGraph 节点接入:FAISS/pgvector 用于语义相似度,Neo4j Cypher 用于图遍历,Tavily/SerpAPI 用于实时网络结果。每个节点返回带有相关性元数据的文档列表。

Grader 节点:文档是否真正相关?

LLM 对每个检索到的文档打分:相关或不相关。若所有文档均不通过,边路由至 Rewriter;若足够多的文档通过,则路由至 Generator。

Rewriter 节点:修复查询,重新尝试

检索失败时,LLM 将原始查询改写得更具体,或切换检索策略,然后循环回第 2 步。最多重试 3 次,之后回退到网络搜索。

Generator 节点:综合生成答案

LLM 基于检索到的文档生成有依据的答案,答案和来源文档存入 Agent 状态,供幻觉检测使用。

Hallucination Checker:最终关卡

LLM 验证:答案中的每个论断是否都有检索文档支撑?如果是 → 返回答案;如果否 → 以更严格的 Prompt 循环回 Generator。这一机制消除了"自信却错误"的答案。

代码实现——LangGraph 混合 RAG Agent

第 1 步——定义 Agent 状态

 from typing import TypedDict, List, Literal  
from langchain_core.documents import Document  
from langgraph.graph import StateGraph, END  
from langchain_openai import ChatOpenAI  
from langchain_community.vectorstores import FAISS  
from langchain_openai import OpenAIEmbeddings  
from langchain_community.tools.tavily_search import TavilySearchResults  
from langchain_community.graphs import Neo4jGraph  
from pydantic import BaseModel  

llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)  

# ── Agent 状态——在所有节点间持久保存 ────────────  
class AgentState(TypedDict):  
    question:       str  
    route:          str                   # vector | graph | web | direct  
    documents:      List[Document]  
    generation:     str  
    rewrite_count:  int  
    grade_results:  List[str]  

# ── 工具初始化 ──────────────────────────────────────────────  
embeddings = OpenAIEmbeddings()  
vector_store = FAISS.load_local("faiss_index", embeddings)  
vector_retriever = vector_store.as_retriever(search_kwargs={"k": 4})  

neo4j_graph = Neo4jGraph(url="bolt://localhost:7687",  
                          username="neo4j", password="password")  
 web_search = TavilySearchResults(max_results=3)

第 2 步——Router 节点

 class RouteDecision(BaseModel):  
    route: Literal["vector", "graph", "web", "direct"]  
    reasoning: str  

router_llm = llm.with_structured_output(RouteDecision)  

ROUTER_PROMPT = """You are a query router for a hybrid RAG system.  
Classify the query into ONE category:  

- vector: factual questions answerable from internal documents  
  (policies, reports, product info, static knowledge)  
- graph: questions about relationships between entities  
  (how X connects to Y, who knows whom, supplier chains)    
- web: requires real-time or current information  
  (news, stock prices, today's events, recent releases)  
- direct: simple calculations or general knowledge the LLM already knows  

Query: {question}"""  

def router_node(state: AgentState) -> AgentState:  
    decision = router_llm.invoke(  
        ROUTER_PROMPT.format(question=state["question"])  
    )  
    print(f"→ Router: {decision.route} | {decision.reasoning}")  
    return {**state, "route": decision.route}  

# 条件边——路由到对应的检索节点  
def route_edge(state: AgentState) -> str:  
     return state["route"]   # "vector" | "graph" | "web" | "direct"

第 3 步——三个 Retriever 节点

 # ── Vector 检索器 ────────────────────────────────────  
def vector_node(state: AgentState) -> AgentState:  
    docs = vector_retriever.invoke(state["question"])  
    return {**state, "documents": docs}  

# ── Graph 检索器(Neo4j,LLM 自动生成 Cypher)──────────────  
from langchain.chains import GraphCypherQAChain  

graph_chain = GraphCypherQAChain.from_llm(  
    llm=llm, graph=neo4j_graph, verbose=True,  
    allow_dangerous_requests=True  
)  

def graph_node(state: AgentState) -> AgentState:  
    result = graph_chain.invoke(state["question"])  
    doc = Document(page_content=result["result"],  
                   metadata={"source": "neo4j_graph"})  
    return {**state, "documents": [doc]}  

# ── Web 搜索检索器 ─────────────────────────────────  
def web_node(state: AgentState) -> AgentState:  
    results = web_search.invoke(state["question"])  
    docs = [Document(page_content=r["content"],  
                     metadata={"source": r["url"]})  
            for r in results]  
    return {**state, "documents": docs}  

# ── 直接调用 LLM(无需检索)────────────────────────────────  
def direct_node(state: AgentState) -> AgentState:  
    answer = llm.invoke(state["question"]).content  
     return {**state, "generation": answer, "documents": []}

第 4 步——Grader、Rewriter、Generator 和 Hallucination Checker

 # ── Grader ───────────────────────────────────────────────  
class GradeDoc(BaseModel):  
    score: Literal["relevant", "irrelevant"]  

grader_llm = llm.with_structured_output(GradeDoc)  

def grader_node(state: AgentState) -> AgentState:  
    grades = []  
    for doc in state["documents"]:  
        result = grader_llm.invoke(  
            f"Question: {state['question']}\nDocument: {doc.page_content[:400]}\n"  
            "Is this document relevant to answering the question? Score: relevant/irrelevant"  
        )  
        grades.append(result.score)  
    return {**state, "grade_results": grades}  

def grade_edge(state: AgentState) -> str:  
    relevant = sum(1 for g in state["grade_results"] if g == "relevant")  
    if relevant > 0:  
        return "generate"  
    elif state["rewrite_count"] < 3:  
        return "rewrite"  
    else:  
        return "web_fallback"   # 三次失败后的最终兜底  

# ── Rewriter ─────────────────────────────────────────────  
def rewriter_node(state: AgentState) -> AgentState:  
    rewritten = llm.invoke(  
        f"The query '{state['question']}' returned no relevant results. "  
        "Rewrite it to be more specific and searchable. Return only the rewritten query."  
    ).content  
    print(f"→ Rewriter: '{state['question']}' → '{rewritten}'")  
    return {**state, "question": rewritten,  
            "rewrite_count": state["rewrite_count"] + 1}  

# ── Generator ────────────────────────────────────────────  
def generator_node(state: AgentState) -> AgentState:  
    context = "\n\n".join(d.page_content for d in state["documents"]  
                          if "relevant" in state.get("grade_results",[]))  
    answer = llm.invoke(  
        f"Answer using only the context below.\n\nContext:\n{context}\n\nQuestion: {state['question']}"  
    ).content  
    return {**state, "generation": answer}  

# ── Hallucination Checker ────────────────────────────────  
class HallucinationCheck(BaseModel):  
    grounded: Literal["yes", "no"]  

halluc_llm = llm.with_structured_output(HallucinationCheck)  

def hallucination_node(state: AgentState) -> AgentState:  
    context = "\n\n".join(d.page_content for d in state["documents"])  
    result = halluc_llm.invoke(  
        f"Context:\n{context}\n\nAnswer:\n{state['generation']}\n\n"  
        "Is the answer fully supported by the context? grounded: yes/no"  
    )  
    return {**state, "hallucination_check": result.grounded}  

def halluc_edge(state: AgentState) -> str:  
     return "end" if state.get("hallucination_check") == "yes" else "regenerate"

第 5 步——连接图结构并运行

 # ── 构建 LangGraph 状态机 ────────────────────────────────  
workflow = StateGraph(AgentState)  

# 添加节点  
workflow.add_node("router",       router_node)  
workflow.add_node("vector",       vector_node)  
workflow.add_node("graph",        graph_node)  
workflow.add_node("web",          web_node)  
workflow.add_node("direct",       direct_node)  
workflow.add_node("grader",       grader_node)  
workflow.add_node("rewriter",     rewriter_node)  
workflow.add_node("generator",    generator_node)  
workflow.add_node("hallucination",hallucination_node)  

# 入口节点  
workflow.set_entry_point("router")  

# router → 检索器(条件边)  
workflow.add_conditional_edges("router", route_edge, {  
    "vector": "vector",  
    "graph":  "graph",  
    "web":    "web",  
    "direct": "direct",  
})  

# 检索器 → grader  
for node in ["vector", "graph", "web"]:  
    workflow.add_edge(node, "grader")  

# grader → 生成 / 改写 / web 兜底  
workflow.add_conditional_edges("grader", grade_edge, {  
    "generate":     "generator",  
    "rewrite":      "rewriter",  
    "web_fallback": "web",  
})  

# rewriter → 回到 router  
workflow.add_edge("rewriter", "router")  

# generator → hallucination 检测  
workflow.add_edge("generator", "hallucination")  

# hallucination 检测 → 结束或重新生成  
workflow.add_conditional_edges("hallucination", halluc_edge, {  
    "end":        END,  
    "regenerate": "generator",  
})  
workflow.add_edge("direct", END)  

# 编译并运行  
agent = workflow.compile()  

result = agent.invoke({  
    "question":      "What is our data retention policy for EU customers?",  
    "rewrite_count": 0,  
    "documents":     [],  
    "grade_results": [],  
    "generation":    "",  
    "route":         "",  
})  
 print(result["generation"])

总结

通过引入 LangGraph 构建状态机,我们将传统的“单向直通车”式 RAG 成功升级为了具备记忆、决策和自我纠错能力的智能体系统。这种混合 Agent 架构彻底打破了单一 Pipeline 的局限:它不仅能通过 Router 扮演决策中枢,针对不同问题智能分发最匹配的检索工具(向量相似度、图谱关联、网络实时数据或直接调用大模型);更重要的是,它建立了一套严密的自校验闭环。

借助 Grader 的相关性过滤和 Rewriter 的查询修复,系统在面对检索失败时可以主动寻找替代方案;而最后的 Hallucination Checker 拦截一切缺乏上下文支撑的“自信妄想”。这种架构让 AI 像人一样,在不确定中不断试错、交叉验证,直到确信无疑才交付结果。

https://avoid.overfit.cn/post/6f3ab183695548b3bef0e96109eaa37f
By Sivasundharam

目录
相关文章
|
3天前
|
云安全 人工智能 运维
阿里云SecOps Agent,全新安全跨产品执行体验
自然语言驱动 云安全中心/WAF/CFW/ 等多款安全产品联动
1592 2
|
3天前
|
机器学习/深度学习 人工智能 调度
🐴 HappyHorse 1.1 现已上线阿里云百炼!快来查收模型使用指南,现在调用享 6 折~
HappyHorse 1.1 是新一代视频生成大模型,全面升级动态表现力、角色一致性、指令遵循、视觉质感与音画协同能力。支持I2V/T2V/R2V三类生成,适配短剧、电商广告、品牌营销等场景,提供高质、流畅、可控的AI视频生产力。
557 3
🐴 HappyHorse 1.1 现已上线阿里云百炼!快来查收模型使用指南,现在调用享 6 折~
|
14天前
|
缓存 测试技术 API
Qwen 3.7 Plus 与 Max 实测:性价比与多模态能力差异解析(2026)
2026 年 6 月 1 日,阿里悄无声息地发布了 Qwen 3.7 Plus,距 Qwen 3.7 Max 上线刚好 11 天。同样的 1M 上下文,同样的 35 小时自治上限。但价格才是头条:Plus 是 0.40/M输入,Max是 2.50/M——便宜约 6 倍——并且还能看图、看视频。Vision Arena 上 Plus 已经排到 #16。所以这周真正值得讨论的问题不是”要不要为视觉能力买单”,而是”Max 凭什么用 6 倍价格换来 2 个百分点的 benchmark 领先”。
|
15天前
|
JavaScript 定位技术 API
CodeGraph 爆火:编程 Agent 需要的不是更多上下文,而是一张提前画好的代码地图
CodeGraph 是一款爆火的本地代码智能工具,通过 tree-sitter 解析 AST 构建结构化知识图谱(存于 SQLite),为编程 Agent 提前生成“代码地图”。它显著降低 Agent 在中大型项目中的探索成本——实测工具调用减少71%、Token 降57%、速度提升46%,支持19+语言及主流框架路由识别,完全离线、无需 API Key。
899 11
CodeGraph 爆火:编程 Agent 需要的不是更多上下文,而是一张提前画好的代码地图
|
2天前
|
人工智能 监控 前端开发
Electron 监控:让桌面 Agent 监控触手可及
一行代码实现Electron桌面端全景监控,自动还原崩溃现场、预警内存泄漏、全链路追踪、 SSE流式响应与交互埋点,让 AI 助手运行状态清晰可见,助力快速恢复稳定与流畅。
177 125
|
2天前
|
消息中间件 人工智能 Kafka
AI 时代,实时入湖正在告别 ETL:从 Kafka 到 Iceberg 的架构减法
本文围绕“零 ETL”这一趋势,讨论流数据入湖为什么需要做架构减法,并结合 Kafka × Table Bucket 的实践,分析一种将通用入湖能力前移到消息与表存储链路中的方案,如何在降低复杂度的同时,兼顾实时性、一致性、Schema 演进、CDC 语义与开放生态兼容。
182 121
|
7天前
|
缓存 人工智能 运维
GLM 5.2自托管全流程实战:硬件选型、vLLM/SGLang部署与成本盈亏测算
2026年智谱发布GLM 5.2超大混合专家模型,区别于以往仅开放API的闭源大模型,该模型权重以MIT开源协议对外发布,企业与开发者可完整下载、本地审计、私有化部署,实现数据不出环境、自定义微调、自主调度推理资源。GLM 5.2拥有753B总参数,原生支持百万级上下文窗口,在代码生成、长文档推理、数学逻辑等多项基准测试中对标国际顶尖商用模型,是首款可完整自托管的前沿代码向大模型。
613 0
|
15天前
|
人工智能 运维 JavaScript
阿里云Qoder CN(原通义灵码)全解析 产品形态、版本划分与技术适配说明
在AI辅助开发与智能办公工具持续普及的当下,阿里云旗下原通义灵码正式更名为Qoder CN,同时延伸出QoderWork CN、Qoder CN CLI、Qoder CN Mobile等多款配套产品,形成覆盖代码开发、日常办公、终端交互、移动端使用的完整工具矩阵。Qoder CN核心定位为AI智能编码助手,深度适配主流代码编辑器、集成开发环境以及终端场景;QoderWork CN则偏向桌面端综合办公辅助,二者面向不同使用场景,划分了多个版本档位,搭配差异化资源配额、功能权限与计费规则,同时兼容多款主流大模型。
974 8