langGraph学习指南1

LangGraph 学习教程

目录

  1. [为什么需要 LangGraph](#为什么需要 LangGraph "#1-%E4%B8%BA%E4%BB%80%E4%B9%88%E9%9C%80%E8%A6%81-langgraph")
  2. 核心概念
  3. 基础语法与示例
  4. 高级特性
  5. 设计模式与架构
  6. 最佳实践
  7. 优秀开源项目
  8. 完整案例:智能客服系统

1. 为什么需要 LangGraph

1.1 传统 LLM 应用的局限性

问题 说明
线性流程限制 简单的 prompt-response 模式无法处理复杂任务
无状态管理 无法跨步骤保持上下文和状态
无循环/重试 处理失败时无法自动重试或循环改进
无多代理协作 不同专长的 AI 无法协同工作

1.2 LangGraph 的优势

ini 复制代码
┌─────────────────────────────────────────────────────────────┐
│  LangGraph = 有状态 + 图结构 + 循环 + 多代理 + 持久化        │
└─────────────────────────────────────────────────────────────┘

1.3 应用场景

场景 说明
智能客服 需要路由、工具调用、多轮对话
代码助手 需要规划、编码、测试、修复循环
研究助手 需要搜索、分析、总结多步流程
多代理协作 不同专长的 AI 协作完成复杂任务

2. 核心概念

2.1 三大核心组件

scss 复制代码
┌─────────────────────────────────────────────────────────────┐
│  State (状态)  ←  共享数据结构,贯穿整个流程                  │
│       ↑                                                       │
│       ↓                                                       │
│  Nodes (节点)  ←  执行具体工作的函数(可以是LLM、工具等)       │
│       ↑                                                       │
│       ↓                                                       │
│  Edges (边)   ←  定义流程走向(固定边或条件边)                 │
└─────────────────────────────────────────────────────────────┘

2.2 State(状态)详解

2.2.1 状态定义方式
方式 优点 缺点 适用场景
TypedDict 简单、灵活 类型检查弱 快速原型
Pydantic 强类型、验证 稍复杂 生产环境
MessagesState 内置消息管理 只适合对话 聊天应用

示例:TypedDict 状态

python 复制代码
from typing import TypedDict, Annotated, Any
from langgraph.graph.message import add_messages
​
class AgentState(TypedDict):
    messages: Annotated[list, add_messages]  # 使用累加器
    current_step: str
    result: dict[str, Any]
    error_count: int
    max_steps: int

示例:Pydantic 状态

python 复制代码
from pydantic import BaseModel, Field
from typing import Optional, List, Dict, Any
from langchain_core.messages import BaseMessage
​
class AgentState(BaseModel):
    messages: List[BaseMessage] = Field(default_factory=list)
    current_step: Optional[str] = None
    result: Optional[Dict[str, Any]] = None
    error_count: int = 0
    max_steps: int = 10
    user_id: Optional[str] = None
2.2.2 Reducer(累加器)
yaml 复制代码
┌─────────────────────────────────────────────────────────────┐
│  Reducer: 决定新状态如何与旧状态合并                           │
│                                                               │
│  - add_messages: 消息列表自动追加                              │
│  - add_values: 数值自动累加                                   │
│  - 自定义: 完全控制合并逻辑                                    │
└─────────────────────────────────────────────────────────────┘

2.3 Nodes(节点)详解

2.3.1 节点类型
节点类型 说明 应用场景
LLM 节点 调用大模型 推理、决策
工具节点 执行外部工具 搜索、计算
路由节点 只做分类决策 任务分发
聚合节点 收集多个节点结果 结果汇总

示例:LLM 节点

python 复制代码
from langchain_openai import ChatOpenAI
​
llm = ChatOpenAI(model="doubao-seed-1-6-251015")
​
def llm_node(state: AgentState) -> dict:
    """调用 LLM 并返回更新"""
    messages = state["messages"]
    response = llm.invoke(messages)
    return {"messages": [response]}

示例:工具调用节点

python 复制代码
from langchain.agents import Tool
from langchain_experimental.utilities import PythonREPL
​
python_repl = PythonREPL()
tools = [
    Tool(
        name="PythonREPL",
        func=python_repl.run,
        description="用于执行 Python 代码"
    )
]
​
def tool_node(state: AgentState) -> dict:
    """执行工具并返回结果"""
    last_message = state["messages"][-1]
    tool_name = last_message.additional_kwargs.get("tool_calls", [])[0].get("name")
    result = execute_tool(tool_name, last_message.content)
    return {"messages": [result]}

2.4 Edges(边)详解

2.4.1 边的类型
sql 复制代码
┌─────────────────────────────────────────────────────────────┐
│  类型 1: 固定边 (Normal Edge)                                  │
│  START → node_a → node_b → END                                  │
│                                                               │
│  类型 2: 条件边 (Conditional Edge)                              │
│  START → router? → (success_node | error_node) → END            │
│                                                               │
│  类型 3: 循环边 (Cycle)                                         │
│  START → node → (loop? | end?) → END                            │
└─────────────────────────────────────────────────────────────┘

示例:固定边

sql 复制代码
from langgraph.graph import StateGraph, START, END

graph = StateGraph(AgentState)

graph.add_node("llm", llm_node)
graph.add_edge(START, "llm")  # 入口边
graph.add_edge("llm", END)    # 出口边

示例:条件边

python 复制代码
def route_after_llm(state: AgentState) -> str:
    """根据状态决定下一个节点"""
    last_message = state["messages"][-1]
    
    # 如果有工具调用,去工具节点
    if last_message.additional_kwargs.get("tool_calls"):
        return "tools"
    # 否则结束
    return END

graph.add_node("tools", tool_node)
graph.add_conditional_edges(
    "llm",
    route_after_llm,
    {
        "tools": "tools",
        END: END
    }
)
graph.add_edge("tools", "llm")  # 工具执行完回到 LLM

2.5 完整流程图

sql 复制代码
                    ┌─────────┐
                    │  START  │
                    └────┬────┘
                         │
                    ┌────▼────┐
                    │  LLM   │
                    └────┬────┘
                         │
             ┌───────────┴───────────┐
             ▼                       ▼
      ┌─────────────┐         ┌─────────┐
      │   Tools    │         │   END   │
      └─────┬─────┘         └─────────┘
            │
            └──────────┬──────────┘
                       │
              ┌────────▼────────┐
              │  回到 LLM (循环) │
              └─────────────────┘

3. 基础语法与示例

3.1 最简单的 LangGraph(Hello World)

python 复制代码
from typing import TypedDict
from langgraph.graph import StateGraph, START, END

# 1. 定义状态
class HelloState(TypedDict):
    name: str
    greeting: str

# 2. 定义节点
def say_hello(state: HelloState) -> dict:
    name = state["name"]
    greeting = f"你好, {name}! 欢迎学习 LangGraph!"
    return {"greeting": greeting}

# 3. 构建图
graph = StateGraph(HelloState)
graph.add_node("say_hello", say_hello)
graph.add_edge(START, "say_hello")
graph.add_edge("say_hello", END)

# 4. 编译图
app = graph.compile()

# 5. 运行图
result = app.invoke({"name": "张三"})
print(result["greeting"])

3.2 带循环的例子(反思改进)

scss 复制代码
┌─────────────────────────────────────────────────────────────┐
│  用户问:写一首诗 → 写诗句 → 反思评价 → (好?→ 结束 / 不好?→ 重写) │
└─────────────────────────────────────────────────────────────┘
python 复制代码
from typing import TypedDict, Annotated
from langchain_core.messages import BaseMessage
from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages

llm = ChatOpenAI(model="doubao-seed-1-6-251015")

class PoemState(TypedDict):
    messages: Annotated[list, add_messages]
    iterations: int
    max_iterations: int
    poem: str

def write_poem(state: PoemState) -> dict:
    """写诗句"""
    user_msg = state["messages"][0]
    prompt = f"{user_msg.content}\n\n请写一首诗:"
    response = llm.invoke(prompt)
    return {"poem": response.content, "iterations": state["iterations"] + 1}

def reflect(state: PoemState) -> dict:
    """反思评价"""
    poem = state["poem"]
    prompt = f"请评价这首诗,给出改进建议(用中文):\n\n{poem}"
    response = llm.invoke(prompt)
    return {"messages": [response]}

def should_continue(state: PoemState) -> str:
    """决定是否继续"""
    if state["iterations"] >= state["max_iterations"]:
        return END
    # 检查反思结果,如果说"满意"或"很好"就结束
    reflection = state["messages"][-1].content
    if "满意" in reflection or "很好" in reflection:
        return END
    return "write_poem"

# 构建图
graph = StateGraph(PoemState)
graph.add_node("write_poem", write_poem)
graph.add_node("reflect", reflect)

graph.add_edge(START, "write_poem")
graph.add_edge("write_poem", "reflect")
graph.add_conditional_edges(
    "reflect",
    should_continue,
    {
        "write_poem": "write_poem",
        END: END
    }
)

app = graph.compile()

# 运行
result = app.invoke({
    "messages": [{"role": "user", "content": "写一首关于春天的诗"}],
    "iterations": 0,
    "max_iterations": 3,
    "poem": ""
})

print(f"最终结果(迭代 {result['iterations']} 次):")
print(result["poem"])

3.3 多代理协作示例

sql 复制代码
                      ┌─────────┐
                      │  START  │
                      └────┬────┘
                           │
                    ┌──────▼──────┐
                    │  路由器    │
                    └──────┬──────┘
        ┌──────────────────┼──────────────────┐
        │                  │                  │
   ┌────▼────┐       ┌────▼────┐       ┌────▼────┐
   │  搜索器 │       │  计算器 │       │  聊天器 │
   └────┬────┘       └────┬────┘       └────┬────┘
        │                  │                  │
        └──────────────────┼──────────────────┘
                           │
                    ┌──────▼──────┐
                    │  总结器    │
                    └──────┬──────┘
                           │
                    ┌──────▼──────┐
                    │   END     │
                    └─────────────┘
python 复制代码
from typing import TypedDict, Annotated, List, Dict, Any
from langchain_core.messages import BaseMessage
from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages

llm = ChatOpenAI(model="doubao-seed-1-6-251015")

class MultiAgentState(TypedDict):
    messages: Annotated[list, add_messages]
    request: str
    task_type: str
    search_result: str
    calc_result: str
    chat_response: str
    final_summary: str

def router(state: MultiAgentState) -> str:
    """路由任务到不同代理"""
    request = state["request"].lower()
    
    if any(keyword in request for keyword in ["搜索", "查找", "查询"]):
        return "search_agent"
    elif any(keyword in request for keyword in ["计算", "数学"]):
        return "calculator_agent"
    else:
        return "chat_agent"

def search_agent(state: MultiAgentState) -> dict:
    """搜索代理"""
    prompt = f"请搜索以下内容并给出结果:{state['request']}"
    response = llm.invoke(prompt)
    return {"search_result": response.content, "task_type": "search"}

def calculator_agent(state: MultiAgentState) -> dict:
    """计算代理"""
    prompt = f"请解决以下数学问题并给出结果:{state['request']}"
    response = llm.invoke(prompt)
    return {"calc_result": response.content, "task_type": "calculator"}

def chat_agent(state: MultiAgentState) -> dict:
    """聊天代理"""
    prompt = f"{state['request']}"
    response = llm.invoke(prompt)
    return {"chat_response": response.content, "task_type": "chat"}

def summarizer(state: MultiAgentState) -> dict:
    """总结代理"""
    task_type = state["task_type"]
    result = ""
    
    if task_type == "search":
        result = state["search_result"]
    elif task_type == "calculator":
        result = state["calc_result"]
    else:
        result = state["chat_response"]
    
    prompt = f"请总结以下内容:\n\n{result}"
    summary = llm.invoke(prompt)
    return {"final_summary": summary.content}

# 构建图
graph = StateGraph(MultiAgentState)

graph.add_node("router", router)
graph.add_node("search_agent", search_agent)
graph.add_node("calculator_agent", calculator_agent)
graph.add_node("chat_agent", chat_agent)
graph.add_node("summarizer", summarizer)

# 边
graph.add_edge(START, "router")
graph.add_conditional_edges(
    "router",
    router,
    {
        "search_agent": "search_agent",
        "calculator_agent": "calculator_agent",
        "chat_agent": "chat_agent"
    }
)
graph.add_edge("search_agent", "summarizer")
graph.add_edge("calculator_agent", "summarizer")
graph.add_edge("chat_agent", "summarizer")
graph.add_edge("summarizer", END)

app = graph.compile()

# 测试
result = app.invoke({
    "messages": [],
    "request": "搜索关于人工智能最新发展的信息",
    "task_type": "",
    "search_result": "",
    "calc_result": "",
    "chat_response": "",
    "final_summary": ""
})

print(result["final_summary"])

4. 高级特性

4.1 持久化与检查点

scss 复制代码
┌─────────────────────────────────────────────────────────────┐
│  Checkpoint (检查点) = 保存执行状态,可以暂停和恢复              │
│                                                               │
│  使用场景:                                                      │
│  - 长时间运行的任务可以暂停后恢复                                │
│  - 出现错误时可以回滚到之前的状态                                │
│  - 人类介入时可以保存状态等待干预                                │
└─────────────────────────────────────────────────────────────┘

示例:SQLite 检查点

ini 复制代码
from langgraph.checkpoint.sqlite import SqliteSaver

# 创建检查点保存器
checkpointer = SqliteSaver.from_conn_string("checkpoints.sqlite")

# 编译时使用检查点
app = graph.compile(checkpointer=checkpointer)

# 配置线程 ID(用于区分不同会话)
config = {"configurable": {"thread_id": "thread_123"}}

# 第一次运行
result1 = app.invoke(
    {"messages": [("user", "我叫张三")]},
    config=config
)

# 第二次运行(会记住之前的状态)
result2 = app.invoke(
    {"messages": [("user", "我叫什么名字?")]},
    config=config
)

示例:PostgreSQL 检查点(生产环境)

ini 复制代码
from langgraph.checkpoint.postgres import PostgresSaver

# 生产环境用 PostgreSQL
DB_URI = "postgresql://user:password@localhost:5432/langgraph"
checkpointer = PostgresSaver.from_conn_string(DB_URI)

app = graph.compile(checkpointer=checkpointer)

4.2 人类介入(Human-in-the-Loop)

scss 复制代码
┌─────────────────────────────────────────────────────────────┐
│  人类介入模式:                                                │
│                                                               │
│  1. 中断模式 (Interrupt) - 运行到特定节点时暂停                  │
│  2. 审核模式 (Approval) - 执行前需要人类审核                     │
│  3. 回滚模式 (Rewind) - 可以回滚到之前的状态                     │
└─────────────────────────────────────────────────────────────┘

示例:中断模式

ini 复制代码
from langgraph.graph import StateGraph, END
from langgraph.checkpoint.sqlite import SqliteSaver

checkpointer = SqliteSaver.from_conn_string("checkpoints.sqlite")

app = graph.compile(
    checkpointer=checkpointer,
    interrupt_before=["critical_node"]  # 在 critical_node 前暂停
)

# 第一次运行:会在 critical_node 前暂停
result = app.invoke(initial_state, config={"configurable": {"thread_id": "1"}})

# 查看当前状态
state = app.get_state(config)

# 人类可以修改状态
new_state = state.values.copy()
new_state["user_approval"] = True

# 更新状态后继续运行
result = app.invoke(new_state, config=config)

示例:审核模式

python 复制代码
def need_human_approval(state: AgentState) -> bool:
    """决定是否需要人类审核"""
    last_action = state.get("last_action")
    return last_action in ["delete_data", "send_email"]

# 构建图时添加条件边
graph.add_conditional_edges(
    "action_node",
    need_human_approval,
    {
        True: "human_approval",
        False: END
    }
)

4.3 流式输出

python 复制代码
# 流式执行
for chunk in app.stream(initial_state, config):
    # 每个节点产生一个 chunk
    for node_name, node_output in chunk.items():
        print(f"节点 {node_name} 输出:")
        if "messages" in node_output:
            for msg in node_output["messages"]:
                print(f"  - {msg.content}")

4.4 发送 API(Send API)- Map-Reduce

rust 复制代码
┌─────────────────────────────────────────────────────────────┐
│  Send API: 向多个节点并行发送消息,然后聚合结果                  │
│                                                               │
│  应用:并行搜索多个来源,然后合并结果                            │
└─────────────────────────────────────────────────────────────┘
python 复制代码
from typing import List
from langgraph.constants import Send

def fan_out(state: MapReduceState) -> List[Send]:
    """分发任务到多个并行节点"""
    queries = state["queries"]
    return [Send("search_node", {"query": q}) for q in queries]

def search_node(state: dict) -> dict:
    """单个搜索节点"""
    return {"result": search(state["query"])}

def aggregate(state: MapReduceState) -> dict:
    """聚合所有搜索结果"""
    results = [s["result"] for s in state["search_results"]]
    return {"final_result": combine_results(results)}

# 构建图
graph = StateGraph(MapReduceState)
graph.add_node("fan_out", fan_out)
graph.add_node("search_node", search_node)
graph.add_node("aggregate", aggregate)

graph.add_edge(START, "fan_out")
graph.add_edge("search_node", "aggregate")
graph.add_edge("aggregate", END)

4.5 命令 API(Command API)

yaml 复制代码
┌─────────────────────────────────────────────────────────────┐
│  Command API: 同时返回状态更新和下一个节点跳转命令                │
│                                                               │
│  应用:动态路由到任意节点                                       │
└─────────────────────────────────────────────────────────────┘
python 复制代码
from langgraph.types import Command

def smart_router(state: AgentState) -> Command:
    """智能路由,可以跳转到任意节点"""
    intent = analyze_intent(state["messages"][-1].content)
    
    return Command(
        goto=intent["next_node"],  # 跳转到哪里
        update={                  # 状态更新
            "intent": intent,
            "last_step": "routed"
        }
    )

5. 设计模式与架构

5.1 ReAct 模式(Reasoning and Acting)

ini 复制代码
┌─────────────────────────────────────────────────────────────┐
│  ReAct = 思考 → 行动 → 观察 → 思考 → 行动 → ...                │
└─────────────────────────────────────────────────────────────┘
swift 复制代码
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder

prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个能使用工具的智能助手。"),
    MessagesPlaceholder(variable_name="messages"),
    ("system", "请按以下格式回复:\n"
               "1. 先思考该做什么\n"
               "2. 再决定使用什么工具\n"
               "3. 最后执行并总结")
])

5.2 Plan-and-Execute 模式

yaml 复制代码
┌─────────────────────────────────────────────────────────────┐
│  Plan-and-Execute = 规划 → 执行 → 执行 → 执行 → 总结           │
│                                                               │
│  步骤 1: 规划器 - 创建详细步骤计划                            │
│  步骤 2: 执行器 - 按计划执行每一步                            │
│  步骤 3: 总结器 - 汇总结果                                    │
└─────────────────────────────────────────────────────────────┘
python 复制代码
def planner(state: PlanState) -> dict:
    """规划节点:创建执行计划"""
    user_request = state["request"]
    plan = create_detailed_plan(user_request)
    return {"plan": plan, "current_step": 0}

def executor(state: PlanState) -> dict:
    """执行节点:执行计划中的当前步骤"""
    plan = state["plan"]
    step_idx = state["current_step"]
    result = execute_plan_step(plan, step_idx)
    return {
        "step_results": [*state["step_results"], result],
        "current_step": step_idx + 1
    }

def should_continue(state: PlanState) -> str:
    """检查是否还有步骤要执行"""
    if state["current_step"] >= len(state["plan"]):
        return "summarizer"
    return "executor"

5.3 反射模式(Reflection)

ini 复制代码
┌─────────────────────────────────────────────────────────────┐
│  Reflection = 执行 → 反思 → 改进 → 执行 → ...                  │
└─────────────────────────────────────────────────────────────┘

5.4 Tree of Thoughts(思维树)

markdown 复制代码
              ┌───────┐
              │ Idea1 │
              └───┬───┘
                  │
        ┌─────────┼─────────┐
        │         │         │
   ┌────▼───┐ ┌──▼────┐ ┌──▼────┐
   │ Idea1a │ │ Idea1b│ │ Idea1c│
   └────┬───┘ └───┬───┘ └───┬───┘
        │         │         │
   ┌────▼───┐ ┌──▼────┐ ┌──▼────┐
   │ Score1 │ │ Score2│ │ Score3│
   └────────┘ └───────┘ └───────┘

5.5 Supervisor 模式(监督者模式)

scss 复制代码
                      ┌───────────────┐
                      │  Supervisor   │
                      │   (监督者)    │
                      └───────┬───────┘
                ┌─────────────┼─────────────┐
                │             │             │
        ┌───────▼─────┐ ┌───▼────┐ ┌──────▼───────┐
        │ Researcher  │ │ Writer │ │ Critic       │
        │ (研究员)   │ │ (作家) │ │ (批评家)     │
        └───────┬─────┘ └───┬────┘ └──────┬───────┘
                │             │             │
                └─────────────┴─────────────┘
                              │
                      ┌───────▼───────┐
                      │  Result      │
                      └───────────────┘

示例:Supervisor 代理

python 复制代码
from typing import Literal

members = ["Researcher", "Writer", "Critic"]

def supervisor_node(state: SupervisorState) -> dict:
    """监督者节点:协调多个专业代理"""
    prompt = f"""你是一个项目监督者,负责协调以下团队成员完成任务:
    - Researcher: 负责研究和收集信息
    - Writer: 负责写作和创作
    - Critic: 负责批评和改进

    当前任务:{state['task']}
    聊天历史:{state['messages']}

    接下来应该让谁工作?请选择:
    {members} 或 FINISH(如果任务完成)"""
    
    response = llm.invoke(prompt)
    next_agent = parse_response(response)
    
    return {
        "next": next_agent,
        "messages": [response]
    }

def route_supervisor(state: SupervisorState) -> Literal["Researcher", "Writer", "Critic", END]:
    """监督者路由"""
    next_agent = state["next"]
    if next_agent == "FINISH":
        return END
    return next_agent

# 构建图
graph = StateGraph(SupervisorState)
graph.add_node("Supervisor", supervisor_node)
graph.add_node("Researcher", researcher_node)
graph.add_node("Writer", writer_node)
graph.add_node("Critic", critic_node)

graph.add_edge(START, "Supervisor")
graph.add_edge("Researcher", "Supervisor")
graph.add_edge("Writer", "Supervisor")
graph.add_edge("Critic", "Supervisor")
graph.add_conditional_edges("Supervisor", route_supervisor)

6. 最佳实践

6.1 状态设计最佳实践

实践 说明
保持状态简单 只放必要的信息,不要把临时变量塞进状态
使用类型定义 Pydantic 或 TypedDict,保持代码清晰
只返回更新 节点函数只返回改变的键,不返回整个状态
合理使用累加器 add_messages 等,避免手动管理列表
状态过大 避免把大对象塞进状态,用引用或外部存储

6.2 节点设计最佳实践

实践 说明
单一职责 每个节点只做一件事,保持简单
纯函数风格 不改变输入,只返回更新
错误处理 每个节点应该处理错误,把错误信息放进状态
可测试 独立测试每个节点函数

示例:带错误处理的节点

python 复制代码
def safe_node(state: AgentState) -> dict:
    """安全的节点函数,处理错误"""
    try:
        result = do_something(state)
        return {"result": result, "error_count": 0}
    except Exception as e:
        return {
            "last_error": {
                "type": type(e).__name__,
                "message": str(e),
                "timestamp": datetime.now().isoformat()
            },
            "error_count": state.get("error_count", 0) + 1
        }

6.3 边设计最佳实践

实践 说明
优先简单边 尽量用固定边,条件边只在必要时用
明确的路由函数 路由函数应该简单、清晰、易读
循环要有边界 max_steps 或类似机制防止无限循环
过度复杂的路由 避免一个路由函数处理太多逻辑

6.4 生产环境最佳实践

6.4.1 使用 Postgres 检查点
ini 复制代码
from langgraph.checkpoint.postgres import PostgresSaver

# 生产环境必须用 Postgres
DB_URI = "postgresql://user:password@localhost:5432/langgraph"
checkpointer = PostgresSaver.from_conn_string(DB_URI)

app = graph.compile(checkpointer=checkpointer)
6.4.2 监控与日志
python 复制代码
import logging
from langsmith import traceable

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

@traceable
def monitored_node(state: AgentState) -> dict:
    """带监控的节点"""
    logger.info(f"开始执行节点,状态: {state}")
    result = do_something(state)
    logger.info(f"节点执行完成,结果: {result}")
    return result
6.4.3 错误边界与重试
python 复制代码
def safe_execute(func, state, max_retries=3):
    """带重试的安全执行"""
    for i in range(max_retries):
        try:
            return func(state)
        except Exception as e:
            if i == max_retries - 1:
                raise
            time.sleep(2 ** i)  # 指数退避
6.4.4 完整生产配置示例
ini 复制代码
from langgraph.checkpoint.postgres import PostgresSaver
from langchain_core.globals import set_debug
import logging
import sys

# 1. 日志配置
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.StreamHandler(sys.stdout),
        logging.FileHandler('langgraph.log')
    ]
)

# 2. 调试模式(开发环境)
set_debug(True)

# 3. 检查点(生产环境)
DB_URI = "postgresql://user:password@localhost:5432/langgraph"
checkpointer = PostgresSaver.from_conn_string(DB_URI)

# 4. 编译图
app = graph.compile(
    checkpointer=checkpointer,
    interrupt_before=["critical_step"],  # 关键步骤需要人工审核
)

# 5. 配置
config = {
    "configurable": {
        "thread_id": "production_thread",
        "user_id": "user_123",
    },
    "recursion_limit": 50  # 提高递归限制
}

7. 优秀开源项目

7.1 langgraph-complete-guide

地址github.com/mkassaf/lan...

特点

  • 包含 ReAct、Plan-and-Execute、Reflection 等模式的完整实现
  • 多代理协作示例
  • 研究人员视角的深入讲解

学习点

ini 复制代码
# 仓库中的 ReAct 模式实现
from langgraph_examples import ReActAgent

agent = ReActAgent(
    llm=llm,
    tools=tools,
    max_iterations=10
)

result = agent.invoke({"input": "What's 123 * 456?"})

7.2 storyteller

地址github.com/mayflower/s...

特点

  • 用 LangGraph 构建的故事创作工具
  • 体现了状态管理和复杂流程控制
  • 优秀的重构经验分享

学习点

bash 复制代码
# 仓库中的最佳实践:
# 1. 避免自定义路由,用 LangGraph 原生条件边
# 2. 保持状态与 LangGraph 系统一致
# 3. 节点只做一件事,职责单一

7.3 langgraphgo

地址github.com/smallnest/l...

特点

  • LangGraph 的 Go 语言实现
  • 包含完整的预构建代理(ReAct、Supervisor、Planning 等)
  • 优秀的内存管理策略

学习点

bash 复制代码
# Go 版本的预构建代理(概念类似)
agent, _ := prebuilt.CreateSupervisor(
    llm,
    {
        "analyst": analystAgent,
        "coder": coderAgent,
        "reviewer": reviewerAgent
    },
    "router"
)

7.4 LangChain 官方示例

地址github.com/langchain-a...

特点

  • 官方维护的最权威示例
  • 涵盖所有特性的完整展示
  • 持续更新

8. 完整案例:智能客服系统

8.1 系统架构

sql 复制代码
                      ┌─────────┐
                      │  START  │
                      └────┬────┘
                           │
                    ┌──────▼──────┐
                    │  意图识别   │
                    └──────┬──────┘
        ┌──────────────────┼──────────────────┐
        │                  │                  │
   ┌────▼────┐       ┌────▼────┐       ┌────▼────┐
   │ 技术支持 │       │  订单查询 │       │  闲聊   │
   └────┬────┘       └────┬────┘       └────┬────┘
        │                  │                  │
        └──────────────────┼──────────────────┘
                           │
                    ┌──────▼──────┐
                    │  满意度调查 │
                    └──────┬──────┘
                           │
                    ┌──────▼──────┐
                    │   END      │
                    └─────────────┘

8.2 完整代码

python 复制代码
"""
智能客服系统 - LangGraph 完整案例
"""

import os
from typing import TypedDict, Annotated, Optional, List, Dict, Any
from langchain_core.messages import BaseMessage, HumanMessage, AIMessage
from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langgraph.checkpoint.sqlite import SqliteSaver
from dotenv import load_dotenv
import json

# 加载环境变量
load_dotenv()

# 初始化 LLM
llm = ChatOpenAI(
    model="doubao-seed-1-6-251015",
    temperature=0,
    api_key=os.getenv("ARK_API_KEY"),
    base_url="https://ark.cn-beijing.volces.com/api/v3"
)

# ==================== 1. 状态定义 ====================

class CustomerServiceState(TypedDict):
    messages: Annotated[list, add_messages]
    user_id: Optional[str]
    intent: Optional[str]
    order_id: Optional[str]
    technical_issue: Optional[str]
    support_result: Optional[str]
    satisfaction: Optional[int]
    conversation_ended: bool
    step_count: int
    max_steps: int

# ==================== 2. 节点定义 ====================

def intent_recognition(state: CustomerServiceState) -> dict:
    """意图识别节点"""
    user_msg = state["messages"][-1].content
    
    prompt = f"""请分析用户的意图,从以下选项中选择一个:
    - technical_support: 技术问题
    - order_query: 订单查询
    - chat: 闲聊
    - satisfaction: 满意度评价
    - end_conversation: 结束对话
    
    用户输入: {user_msg}
    
    只返回意图名称,不要其他内容。"""
    
    response = llm.invoke(prompt)
    intent = response.content.strip()
    
    return {
        "intent": intent,
        "step_count": state["step_count"] + 1
    }

def technical_support(state: CustomerServiceState) -> dict:
    """技术支持节点"""
    user_msg = state["messages"][-1].content
    
    prompt = f"""你是一个技术支持专家,请帮助用户解决技术问题。
    用户问题: {user_msg}
    
    请给出清晰的解决方案。"""
    
    response = llm.invoke(prompt)
    
    return {
        "support_result": response.content,
        "messages": [response],
        "technical_issue": user_msg,
        "step_count": state["step_count"] + 1
    }

def order_query(state: CustomerServiceState) -> dict:
    """订单查询节点"""
    user_msg = state["messages"][-1].content
    
    # 模拟从数据库查询订单
    prompt = f"""你是一个订单查询助手。
    用户查询: {user_msg}
    
    请给出礼貌的回复。(注:实际应用中这里会调用数据库)"""
    
    response = llm.invoke(prompt)
    
    return {
        "support_result": response.content,
        "messages": [response],
        "step_count": state["step_count"] + 1
    }

def chat(state: CustomerServiceState) -> dict:
    """闲聊节点"""
    user_msg = state["messages"][-1].content
    
    response = llm.invoke(user_msg)
    
    return {
        "support_result": response.content,
        "messages": [response],
        "step_count": state["step_count"] + 1
    }

def satisfaction_survey(state: CustomerServiceState) -> dict:
    """满意度调查节点"""
    if state.get("satisfaction") is not None:
        # 已经评价过了
        return {"conversation_ended": True}
    
    survey_prompt = AIMessage(
        content="对我们的服务满意吗?请用 1-5 分评价(5 分最满意)"
    )
    
    return {
        "messages": [survey_prompt],
        "step_count": state["step_count"] + 1
    }

def record_satisfaction(state: CustomerServiceState) -> dict:
    """记录满意度节点"""
    last_msg = state["messages"][-1].content
    
    # 提取评分
    score = None
    import re
    match = re.search(r'[1-5]', last_msg)
    if match:
        score = int(match.group())
    
    thank_you = AIMessage(
        content=f"感谢您的{'满意' if score and score >=4 else '宝贵'}评价!欢迎下次光临!"
    )
    
    return {
        "satisfaction": score,
        "messages": [thank_you],
        "conversation_ended": True,
        "step_count": state["step_count"] + 1
    }

# ==================== 3. 路由函数 ====================

def route_after_intent(state: CustomerServiceState) -> str:
    """根据意图路由"""
    intent = state["intent"]
    
    if intent == "technical_support":
        return "technical_support"
    elif intent == "order_query":
        return "order_query"
    elif intent == "chat":
        return "chat"
    elif intent == "satisfaction":
        return "record_satisfaction"
    elif intent == "end_conversation":
        return "satisfaction_survey"
    else:
        return "chat"

def route_after_service(state: CustomerServiceState) -> str:
    """服务后路由"""
    if state["step_count"] >= state["max_steps"]:
        return "satisfaction_survey"
    return END  # 等待用户下一个问题

def should_continue_survey(state: CustomerServiceState) -> str:
    """调查后续路由"""
    if state["conversation_ended"]:
        return END
    return "record_satisfaction"

# ==================== 4. 构建图 ====================

graph = StateGraph(CustomerServiceState)

# 添加节点
graph.add_node("intent_recognition", intent_recognition)
graph.add_node("technical_support", technical_support)
graph.add_node("order_query", order_query)
graph.add_node("chat", chat)
graph.add_node("satisfaction_survey", satisfaction_survey)
graph.add_node("record_satisfaction", record_satisfaction)

# 添加边
graph.add_edge(START, "intent_recognition")

# 条件边:意图识别 → 各服务节点
graph.add_conditional_edges(
    "intent_recognition",
    route_after_intent,
    {
        "technical_support": "technical_support",
        "order_query": "order_query",
        "chat": "chat",
        "satisfaction_survey": "satisfaction_survey",
        "record_satisfaction": "record_satisfaction"
    }
)

# 服务节点 → 满意度调查 或 结束
graph.add_conditional_edges(
    "technical_support",
    route_after_service,
    {
        "satisfaction_survey": "satisfaction_survey",
        END: END
    }
)
graph.add_conditional_edges(
    "order_query",
    route_after_service,
    {
        "satisfaction_survey": "satisfaction_survey",
        END: END
    }
)
graph.add_conditional_edges(
    "chat",
    route_after_service,
    {
        "satisfaction_survey": "satisfaction_survey",
        END: END
    }
)

# 满意度调查流程
graph.add_conditional_edges(
    "satisfaction_survey",
    should_continue_survey,
    {
        "record_satisfaction": "record_satisfaction",
        END: END
    }
)
graph.add_edge("record_satisfaction", END)

# ==================== 5. 编译图 ====================

# 使用 SQLite 检查点
checkpointer = SqliteSaver.from_conn_string("customer_service.sqlite")

app = graph.compile(checkpointer=checkpointer)

# ==================== 6. 使用示例 ====================

if __name__ == "__main__":
    print("=== 智能客服系统 ===")
    print("输入 'quit' 退出\n")
    
    # 用户会话配置
    user_id = "user_001"
    config = {
        "configurable": {
            "thread_id": user_id
        }
    }
    
    while True:
        user_input = input("用户: ")
        
        if user_input.lower() in ['quit', '退出', 'q']:
            print("客服: 再见!")
            break
        
        # 调用 LangGraph
        result = app.invoke({
            "messages": [HumanMessage(content=user_input)],
            "user_id": user_id,
            "step_count": 0,
            "max_steps": 10,
            "conversation_ended": False
        }, config=config)
        
        # 显示 AI 回复
        last_message = result["messages"][-1]
        print(f"客服: {last_message.content}")
        
        # 检查是否结束
        if result["conversation_ended"]:
            print(f"\n对话结束。满意度: {result.get('satisfaction', '未评价')}")
            break

8.3 系统特性

特性 说明
意图识别 自动识别用户意图并路由
多轮对话 支持长时间会话
持久化 用 SQLite 保存对话状态
满意度调查 对话结束后收集用户反馈
可扩展 轻松添加新的服务类型

总结

LangGraph 学习路径

复制代码
第 1 天: 基础概念 → Hello World 示例
第 2-3 天: 节点和边 → 简单对话系统
第 4-5 天: 循环和条件 → 反思改进、ReAct
第 6-7 天: 高级特性 → 持久化、人类介入
第 2 周:  设计模式 → 多代理、Supervisor
第 3 周:  最佳实践 → 生产环境配置
第 4 周:  实际项目 → 构建自己的应用

核心要点回顾

  1. State(状态) : 整个系统的共享数据结构,贯穿所有节点
  2. Nodes(节点) : 执行实际工作,返回状态更新
  3. Edges(边) : 定义流程走向,支持条件分支和循环
  4. Checkpoint(检查点) : 持久化状态,支持暂停/恢复
  5. Design Patterns(设计模式) : ReAct、Plan-and-Execute、Supervisor 等

祝你学习愉快! 🚀


参考资源

相关推荐
zh25261 小时前
深入 OpenViking:字节开源的 Agent 上下文数据库,解决了5 个问题
人工智能·开源
EMA1 小时前
智旅云图(一个智能旅游规划项目)学习指南
人工智能·后端
硬件学长森哥1 小时前
AI编程下程序员生存探索
人工智能
果汁华1 小时前
LangChain 深度解析:从 Prompt 调用到 Agent 应用编排框架
人工智能·langchain·prompt
zuozewei1 小时前
AI-7D-SATS平台的harness engineering设计:让 AI Agent 从“工具堆叠”长成“工程制品”
大数据·人工智能
songroom1 小时前
Opencode: 创建自定义Skill,以基金公司实习日报为例
人工智能
Anastasiozzzz1 小时前
万字深度解析 AI 时代的“USB-C接口”:Model Context Protocol (MCP) 核心架构与底层逻辑
人工智能
勇往直前plus1 小时前
RAG 知识体系梳理
人工智能
深度学习lover1 小时前
<数据集>yolo 缆绳识别<目标检测>
人工智能·python·yolo·目标检测·计算机视觉·缆绳识别