LangGraph 快速使用说明

一、LangGraph 是什么

LangGraph 是由 LangChain 团队开发的开源框架(MIT 协议),专门用于构建有状态的、基于图结构的 LLM 应用。它的核心思想很直接:把你的 AI 应用逻辑建模为一张有向图——节点(Node)是具体的操作步骤,边(Edge)是步骤之间的流转规则,状态(State)是在节点之间传递的共享数据。

与传统的链式调用(Chain)不同,LangGraph 天然支持循环和条件分支,这让它特别适合构建需要"思考-行动-观察"反复迭代的 Agent 系统。

LangGraph 可以独立于 LangChain 使用,但两者配合使用效果更好。LangChain 提供模型接口和工具集成,LangGraph 负责编排和流程控制。

LangGraph 的核心优势:

  • 有状态执行:自动管理和传递状态,节点之间无需手动传参
  • 支持循环:Agent 可以反复调用工具直到任务完成,不像简单 Chain 只能走一遍
  • 条件路由:根据运行时的结果动态决定下一步走哪个节点
  • 持久化:内置 checkpoint 机制,支持中断后恢复、回溯历史状态
  • 人工介入(Human-in-the-loop):可在关键节点暂停,等待人工审核后继续
  • 流式输出:原生支持 token 级别的流式响应

适用场景:

  • 需要调用外部工具的对话 Agent
  • 多步骤的 RAG 流水线(检索、评估、重写、生成)
  • 多 Agent 协作系统
  • 需要人工审批的自动化工作流
  • 任何需要循环决策的 LLM 应用

二、安装与环境准备

2.1 基础安装

pip install langgraph

如果要配合 LangChain 的模型接口使用(推荐),还需要安装对应的集成包:

# 使用 OpenAI
pip install langchain-openai

# 使用 Anthropic
pip install langchain-anthropic

# 使用 Google
pip install langchain-google-genai

2.2 环境变量

# 根据你使用的模型设置对应的 API Key
export OPENAI_API_KEY="sk-..."
export ANTHROPIC_API_KEY="sk-ant-..."

2.3 版本要求

LangGraph 需要 Python 3.9 及以上版本。建议使用 Python 3.11+,以获得更好的类型提示支持。截至 2025 年,LangGraph 的最新稳定版本在 PyPI 上持续更新,建议通过 pip install -U langgraph 获取最新版。


三、核心概念详解

理解以下四个概念,就能掌握 LangGraph 的 80% 用法。

3.1 State(状态)

State 是整个图的"记忆"。它是一个 TypedDict,定义了在图中流转的所有数据字段。每个节点都能读取和更新 State。

from typing import TypedDict

class MyState(TypedDict):
    query: str           # 用户输入
    result: str          # 最终结果
    steps: list[str]     # 中间步骤记录

关键机制——Reducer: 默认情况下,节点返回的字段值会直接覆盖 State 中的对应字段。但对于消息列表这种需要"追加"而非"覆盖"的场景,LangGraph 提供了 Reducer 机制:

from typing import Annotated
from langgraph.graph.message import add_messages

class ChatState(TypedDict):
    # add_messages 会把新消息追加到列表末尾,而不是替换整个列表
    messages: Annotated[list, add_messages]

add_messages 是 LangGraph 内置的 Reducer,它不仅追加消息,还能根据消息 ID 进行去重和更新。

3.2 Node(节点)

节点就是普通的 Python 函数,接收 State 作为输入,返回一个字典表示对 State 的更新。

def analyze(state: MyState) -> dict:
    query = state["query"]
    # 执行某些操作...
    return {"result": f"分析结果:{query}", "steps": ["完成分析"]}

节点函数的返回值不需要包含 State 的所有字段,只需返回本节点要更新的字段即可。

3.3 Edge(边)

边定义了节点之间的连接关系,分为两种:

普通边(Direct Edge):无条件地从一个节点流向下一个节点。

graph.add_edge("node_a", "node_b")  # 执行完 node_a 后,一定执行 node_b

条件边(Conditional Edge):根据一个函数的返回值,动态决定走哪条路。

def route(state: MyState) -> str:
    if "错误" in state["result"]:
        return "retry"     # 走 retry 节点
    return "finish"        # 走 finish 节点

graph.add_conditional_edges("check", route, ["retry", "finish"])

3.4 StateGraph(状态图)

StateGraph 是把上述概念组合在一起的构建器。完整的构建流程是:创建 StateGraph → 添加节点 → 添加边 → 编译 → 调用。

from langgraph.graph import StateGraph, START, END

graph = StateGraph(MyState)
graph.add_node("analyze", analyze)
graph.add_edge(START, "analyze")     # START 是内置起点
graph.add_edge("analyze", END)       # END 是内置终点

app = graph.compile()
result = app.invoke({"query": "你好", "result": "", "steps": []})

STARTEND 是 LangGraph 的特殊节点,分别标记图的入口和出口。


四、从零构建第一个 Graph

下面通过一个完整的最小示例,展示 LangGraph 的基本用法。

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

# 1. 定义 State
class State(TypedDict):
    text: str
    word_count: int

# 2. 定义节点函数
def process_text(state: State) -> dict:
    text = state["text"]
    cleaned = text.strip().lower()
    return {"text": cleaned}

def count_words(state: State) -> dict:
    words = state["text"].split()
    return {"word_count": len(words)}

# 3. 构建图
graph = StateGraph(State)
graph.add_node("process", process_text)
graph.add_node("count", count_words)

# 4. 定义执行流程
graph.add_edge(START, "process")
graph.add_edge("process", "count")
graph.add_edge("count", END)

# 5. 编译并执行
app = graph.compile()
result = app.invoke({"text": "  Hello World LangGraph  ", "word_count": 0})

print(result)
# {'text': 'hello world langgraph', 'word_count': 3}

执行过程非常清晰:STARTprocess(清洗文本)→ count(统计字数)→ END。State 在节点之间自动传递和更新。


五、条件分支——让图会"思考"

条件边是 LangGraph 区别于简单 Chain 的核心特性。通过条件边,图可以在运行时根据实际情况选择不同的执行路径。

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

class State(TypedDict):
    query: str
    category: str
    response: str

def classify(state: State) -> dict:
    query = state["query"].lower()
    if "价格" in query or "多少钱" in query:
        return {"category": "pricing"}
    elif "故障" in query or "报错" in query:
        return {"category": "technical"}
    else:
        return {"category": "general"}

def handle_pricing(state: State) -> dict:
    return {"response": "关于价格问题,请访问我们的定价页面或联系销售团队。"}

def handle_technical(state: State) -> dict:
    return {"response": "关于技术问题,请提供错误日志,我们的工程师会帮您排查。"}

def handle_general(state: State) -> dict:
    return {"response": "感谢您的咨询,请问还有什么可以帮助您的?"}

# 路由函数:根据 category 决定走哪个节点
def route_query(state: State) -> Literal["pricing", "technical", "general"]:
    return state["category"]

# 构建图
graph = StateGraph(State)
graph.add_node("classify", classify)
graph.add_node("pricing", handle_pricing)
graph.add_node("technical", handle_technical)
graph.add_node("general", handle_general)

graph.add_edge(START, "classify")
graph.add_conditional_edges("classify", route_query, ["pricing", "technical", "general"])
graph.add_edge("pricing", END)
graph.add_edge("technical", END)
graph.add_edge("general", END)

app = graph.compile()

result = app.invoke({"query": "这个产品多少钱?", "category": "", "response": ""})
print(result["response"])
# 关于价格问题,请访问我们的定价页面或联系销售团队。

路由函数的返回值必须与条件边中定义的目标节点名称一一对应。LangGraph 会根据返回值找到对应的节点并执行。


六、构建 Tool-Calling Agent(实战重点)

这是 LangGraph 最常见也最重要的用法:构建一个能调用外部工具的 Agent。Agent 会自主判断是否需要调用工具,调用后根据结果决定是否继续调用或直接回复用户。

6.1 定义工具

from langchain_core.tools import tool

@tool
def search_web(query: str) -> str:
    """搜索互联网获取最新信息"""
    # 实际项目中这里会调用搜索 API(如 Tavily、SerpAPI 等)
    return f"搜索结果:关于'{query}'的最新信息..."

@tool
def calculate(expression: str) -> str:
    """计算数学表达式"""
    try:
        result = eval(expression)  # 生产环境请使用安全的表达式解析器
        return str(result)
    except Exception as e:
        return f"计算错误:{e}"

tools = [search_web, calculate]

@tool 装饰器来自 LangChain,它会自动将函数签名和 docstring 转换为 LLM 能理解的工具描述格式。

6.2 初始化模型并绑定工具

from langchain_openai import ChatOpenAI

llm = ChatOpenAI(model="gpt-4o", temperature=0)
llm_with_tools = llm.bind_tools(tools)

bind_tools 会把工具列表注册到模型中,使模型在回复时可以选择调用这些工具。

6.3 定义 Agent 节点

from typing import Annotated
from langgraph.graph import MessagesState

# MessagesState 是 LangGraph 内置的快捷状态,等价于:
# class MessagesState(TypedDict):
#     messages: Annotated[list, add_messages]

def agent_node(state: MessagesState) -> dict:
    """调用 LLM,让它决定是直接回复还是调用工具"""
    response = llm_with_tools.invoke(state["messages"])
    return {"messages": [response]}

6.4 定义工具执行节点

from langchain_core.messages import ToolMessage

# 构建工具名称到工具对象的映射
tools_by_name = {t.name: t for t in tools}

def tool_node(state: MessagesState) -> dict:
    """执行 LLM 决定调用的工具"""
    results = []
    # 获取最后一条消息中的工具调用请求
    for tool_call in state["messages"][-1].tool_calls:
        tool = tools_by_name[tool_call["name"]]
        observation = tool.invoke(tool_call["args"])
        results.append(
            ToolMessage(content=str(observation), tool_call_id=tool_call["id"])
        )
    return {"messages": results}

6.5 定义路由逻辑

from typing import Literal
from langgraph.graph import END

def should_continue(state: MessagesState) -> Literal["tools", END]:
    """判断是否需要继续调用工具"""
    last_message = state["messages"][-1]
    # 如果 LLM 的回复中包含工具调用请求,就走 tools 节点
    if last_message.tool_calls:
        return "tools"
    # 否则结束
    return END

6.6 组装完整的 Agent Graph

from langgraph.graph import StateGraph, START, END, MessagesState

graph = StateGraph(MessagesState)

# 添加节点
graph.add_node("agent", agent_node)
graph.add_node("tools", tool_node)

# 定义流程
graph.add_edge(START, "agent")
graph.add_conditional_edges("agent", should_continue, ["tools", END])
graph.add_edge("tools", "agent")  # 工具执行完后回到 agent,形成循环

agent = graph.compile()

注意 graph.add_edge("tools", "agent") 这行——它形成了一个循环。这正是 Agent 的核心模式:LLM 调用工具 → 拿到结果 → 再次让 LLM 判断 → 可能继续调用工具,直到 LLM 认为可以直接回复。

6.7 运行 Agent

from langchain_core.messages import HumanMessage

result = agent.invoke({
    "messages": [HumanMessage(content="帮我算一下 (15 + 27) * 3,然后搜索一下今天的天气")]
})

for msg in result["messages"]:
    msg.pretty_print()

典型的执行流程是:

  1. 用户发送消息
  2. Agent 节点:LLM 分析后决定先调用 calculate 工具
  3. Tools 节点:执行计算,返回结果
  4. Agent 节点:LLM 拿到计算结果,决定再调用 search_web
  5. Tools 节点:执行搜索,返回结果
  6. Agent 节点:LLM 拿到所有结果,组织最终回复
  7. 路由判断没有更多工具调用,流向 END

6.8 使用预构建组件简化代码

LangGraph 提供了预构建的 ToolNodetools_condition,可以大幅简化上面的代码:

from langgraph.prebuilt import ToolNode, tools_condition
from langgraph.graph import StateGraph, START, END, MessagesState

graph = StateGraph(MessagesState)
graph.add_node("agent", agent_node)
graph.add_node("tools", ToolNode(tools))  # 替代手写的 tool_node

graph.add_edge(START, "agent")
graph.add_conditional_edges("agent", tools_condition)  # 替代手写的 should_continue
graph.add_edge("tools", "agent")

agent = graph.compile()

ToolNodetools_condition 封装了上面手写的逻辑,是生产环境的推荐做法。


七、持久化与会话记忆

在实际应用中,Agent 通常需要记住之前的对话内容。LangGraph 通过 Checkpointer 机制实现持久化。

7.1 使用内存 Checkpointer

from langgraph.checkpoint.memory import InMemorySaver

memory = InMemorySaver()
agent = graph.compile(checkpointer=memory)

# 使用 thread_id 来区分不同的会话
config = {"configurable": {"thread_id": "user-123"}}

# 第一轮对话
result1 = agent.invoke(
    {"messages": [HumanMessage(content="我叫小明")]},
    config=config
)

# 第二轮对话——同一个 thread_id,Agent 能记住之前的内容
result2 = agent.invoke(
    {"messages": [HumanMessage(content="你还记得我叫什么吗?")]},
    config=config
)

InMemorySaver 适合开发和测试。生产环境中,LangGraph 支持 SQLite、PostgreSQL 等持久化后端:

from langgraph.checkpoint.sqlite import SqliteSaver

# SQLite 持久化
memory = SqliteSaver.from_conn_string("checkpoints.db")
agent = graph.compile(checkpointer=memory)

7.2 查看和操作历史状态

# 获取当前状态快照
snapshot = agent.get_state(config)
print(snapshot.values["messages"])

# 查看所有历史状态
for state in agent.get_state_history(config):
    print(state.config, len(state.values["messages"]))

八、Human-in-the-Loop(人工介入)

在很多业务场景中,AI 的某些决策需要人工确认后才能执行。LangGraph 通过 interrupt_beforeinterrupt_after 参数实现这一点。

# 在执行 tools 节点之前暂停,等待人工确认
agent = graph.compile(
    checkpointer=memory,
    interrupt_before=["tools"]  # 在工具执行前中断
)

config = {"configurable": {"thread_id": "review-session"}}

# 第一次调用——会在 tools 节点前暂停
result = agent.invoke(
    {"messages": [HumanMessage(content="帮我删除所有过期数据")]},
    config=config
)

# 此时可以查看 Agent 打算做什么
snapshot = agent.get_state(config)
pending_tool_calls = snapshot.values["messages"][-1].tool_calls
print(f"Agent 打算调用:{pending_tool_calls}")

# 人工确认后,继续执行(传入 None 表示继续)
result = agent.invoke(None, config=config)

如果人工审核后不同意执行,可以修改状态再继续:

from langchain_core.messages import AIMessage

# 用一个不包含工具调用的消息替换,让 Agent 直接回复
agent.update_state(
    config,
    {"messages": [AIMessage(content="抱歉,该操作需要管理员权限,我无法执行。")]},
    as_node="agent"
)

result = agent.invoke(None, config=config)

九、流式输出

LangGraph 支持多种粒度的流式输出,方便前端实时展示 Agent 的执行过程。

9.1 节点级别流式

# stream 返回每个节点执行后的状态更新
for event in agent.stream(
    {"messages": [HumanMessage(content="搜索最新的 AI 新闻")]},
    config=config
):
    for node_name, output in event.items():
        print(f"--- {node_name} ---")
        if "messages" in output:
            for msg in output["messages"]:
                print(msg.content[:100])

9.2 Token 级别流式

# stream_mode="messages" 提供 token 级别的流式输出
for msg, metadata in agent.stream(
    {"messages": [HumanMessage(content="给我写一首诗")]},
    config=config,
    stream_mode="messages"
):
    if msg.content:
        print(msg.content, end="", flush=True)

十、子图(Subgraph)——复杂系统的模块化

当系统变得复杂时,可以把部分逻辑封装为子图,然后在主图中作为一个节点使用。

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

# 定义一个子图:负责数据验证
class ValidationState(TypedDict):
    data: str
    is_valid: bool

def check_format(state: ValidationState) -> dict:
    is_valid = len(state["data"]) > 0 and "@" in state["data"]
    return {"is_valid": is_valid}

validation_graph = StateGraph(ValidationState)
validation_graph.add_node("check", check_format)
validation_graph.add_edge(START, "check")
validation_graph.add_edge("check", END)
validation_subgraph = validation_graph.compile()

# 在主图中使用子图
class MainState(TypedDict):
    data: str
    is_valid: bool
    result: str

def process_result(state: MainState) -> dict:
    if state["is_valid"]:
        return {"result": "数据有效,处理成功"}
    return {"result": "数据无效,请检查格式"}

main_graph = StateGraph(MainState)
main_graph.add_node("validate", validation_subgraph)  # 子图作为节点
main_graph.add_node("process", process_result)

main_graph.add_edge(START, "validate")
main_graph.add_edge("validate", "process")
main_graph.add_edge("process", END)

app = main_graph.compile()
result = app.invoke({"data": "user@example.com", "is_valid": False, "result": ""})
print(result["result"])  # 数据有效,处理成功

子图需要和主图共享相同的 State 字段名(或者部分重叠的字段),LangGraph 会自动处理 State 的映射。


十一、实战案例:RAG 质量控制 Agent

下面是一个更贴近生产环境的例子——一个带质量评估的 RAG Agent。它会检索文档、评估检索质量,如果质量不达标,会重写查询后重新检索。

from typing import TypedDict, Literal, Annotated
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langchain_openai import ChatOpenAI

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

class RAGState(TypedDict):
    question: str
    context: str
    answer: str
    quality_score: float
    retry_count: int

def retrieve(state: RAGState) -> dict:
    """模拟文档检索"""
    question = state["question"]
    # 实际项目中这里调用向量数据库
    context = f"检索到的与'{question}'相关的文档内容..."
    return {"context": context}

def evaluate_quality(state: RAGState) -> dict:
    """评估检索质量"""
    prompt = f"请评估以下检索结果与问题的相关性,返回0-1之间的分数。\n问题:{state['question']}\n检索结果:{state['context']}"
    # 简化示例,实际应调用 LLM 并解析分数
    score = 0.8  # 假设得分
    return {"quality_score": score}

def generate_answer(state: RAGState) -> dict:
    """基于检索结果生成回答"""
    prompt = f"根据以下上下文回答问题。\n上下文:{state['context']}\n问题:{state['question']}"
    response = llm.invoke(prompt)
    return {"answer": response.content}

def rewrite_query(state: RAGState) -> dict:
    """重写查询以改善检索质量"""
    prompt = f"原始问题检索效果不佳,请重写以下问题使其更适合检索:\n{state['question']}"
    response = llm.invoke(prompt)
    return {
        "question": response.content,
        "retry_count": state["retry_count"] + 1
    }

def quality_check(state: RAGState) -> Literal["generate", "rewrite", "force_generate"]:
    """根据质量分数决定下一步"""
    if state["quality_score"] >= 0.7:
        return "generate"
    if state["retry_count"] >= 2:
        return "force_generate"  # 重试次数超限,强制生成
    return "rewrite"

# 构建图
graph = StateGraph(RAGState)

graph.add_node("retrieve", retrieve)
graph.add_node("evaluate", evaluate_quality)
graph.add_node("generate", generate_answer)
graph.add_node("rewrite", rewrite_query)

graph.add_edge(START, "retrieve")
graph.add_edge("retrieve", "evaluate")
graph.add_conditional_edges(
    "evaluate",
    quality_check,
    {
        "generate": "generate",
        "rewrite": "rewrite",
        "force_generate": "generate"
    }
)
graph.add_edge("rewrite", "retrieve")  # 重写后重新检索,形成循环
graph.add_edge("generate", END)

rag_agent = graph.compile()

result = rag_agent.invoke({
    "question": "LangGraph 的核心特性是什么?",
    "context": "",
    "answer": "",
    "quality_score": 0.0,
    "retry_count": 0
})

print(result["answer"])

这个图的执行流程是:检索 → 评估 → 如果质量高就生成答案,如果低就重写查询后重新检索。retry_count 作为安全阀,防止无限循环。


十二、调试技巧与最佳实践

12.1 可视化图结构

# 生成 Mermaid 图(在 Jupyter 中直接显示)
from IPython.display import Image, display
display(Image(app.get_graph().draw_mermaid_png()))

# 或者输出 Mermaid 文本格式
print(app.get_graph().draw_mermaid())

可视化是调试复杂图的最有效手段,强烈建议在开发阶段始终打开。

12.2 日志和追踪

LangGraph 可以与 LangSmith 集成,实现完整的运行追踪:

export LANGSMITH_API_KEY="ls__..."
export LANGSMITH_TRACING=true

设置环境变量后,每次 invokestream 的调用都会自动上报到 LangSmith 平台,可以看到每个节点的输入输出、耗时、token 消耗等。

12.3 错误处理

节点函数中的异常不会被 LangGraph 自动捕获,建议在关键节点中加入 try-except:

def safe_tool_node(state: MessagesState) -> dict:
    try:
        # 执行工具调用...
        return {"messages": results}
    except Exception as e:
        error_msg = ToolMessage(
            content=f"工具执行失败:{str(e)}",
            tool_call_id=state["messages"][-1].tool_calls[0]["id"]
        )
        return {"messages": [error_msg]}

12.4 实践建议

State 设计原则:

  • 保持 State 精简,只放必要的字段
  • 对于需要追加的列表字段,使用 Annotated + Reducer
  • 考虑加入控制字段如 retry_counterror_message 用于流程控制

图结构设计原则:

  • 每个节点只做一件事,职责单一
  • 避免在节点中做复杂的条件判断,把路由逻辑放到条件边中
  • 循环一定要有退出条件,防止无限循环
  • 使用子图封装可复用的逻辑模块

生产环境清单:

  • 使用持久化 Checkpointer(PostgreSQL / SQLite),不要用 InMemorySaver
  • 关键操作加入 Human-in-the-loop 审核
  • 配置合理的超时和重试机制
  • 接入 LangSmith 进行线上监控
  • 对 Agent 的工具调用做权限控制和输入校验

十三、LangGraph 与其他框架的对比

LangGraph vs LangChain Agents: LangChain Agents 是更高层的抽象,底层已经基于 LangGraph 实现。如果你只需要标准的 ReAct 模式(LLM + 工具循环),直接用 LangChain 的 create_react_agent 更方便。如果需要自定义流程控制、多 Agent 协作、复杂的条件路由,则用 LangGraph。

LangGraph vs CrewAI / AutoGen: CrewAI 和 AutoGen 侧重于多 Agent 角色扮演和对话,抽象层级较高,上手快但灵活性有限。LangGraph 是底层编排框架,可以实现上述框架的所有模式,但需要自己编写更多代码。适合需要精细控制的生产场景。

一句话总结: 如果你需要的是"积木"级别的灵活组装能力,选 LangGraph;如果你需要的是开箱即用的特定场景方案,可以先看高层框架是否满足需求。


十四、总结

LangGraph 的核心就三件事:用 State 管理数据,用 Node 封装逻辑,用 Edge 编排流程。理解了这些,剩下的就是根据业务需要组合这些积木。

入门路径建议:

  1. 先跑通上面"第一个 Graph"的例子,理解 State 的流转
  2. 加入条件边,理解路由机制
  3. 构建 Tool-Calling Agent,这是最核心的实战模式
  4. 加入 Checkpointer 实现会话记忆
  5. 根据需要引入 Human-in-the-loop、子图、流式输出等高级特性

官方资源:

  • 文档:https://docs.langchain.com/oss/python/langgraph/overview
  • GitHub:https://github.com/langchain-ai/langgraph
  • 免费课程:https://academy.langchain.com/courses/intro-to-langgraph
  • 示例代码:GitHub 仓库的 examples 目录

LangGraph 仍在快速迭代中,API 可能随版本更新有所变化,建议开发时参考官方文档确认最新用法。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐