📖 Day 7 学习页:LangGraph 入门
Day 7:LangGraph 入门——把 ReAct 循环装进"图"里
🎯 今日目标:理解为什么生产级 Agent 都用"图"来组织:状态显式、路由可控、可持久化。 用 LangGraph 的 StateGraph 重写 Day 6 的 Agent(没装库也有模拟器先跑通逻辑)。
⏱ 今日安排(约 2 小时)
| 时间 | 内容 | 方式 |
|---|---|---|
| 40min | 理论学习:图的核心概念 + 与手写循环的对应 | 阅读 |
| 50min | 动手实操:State / Nodes / Router / run_agent | 编码 |
| 30min | 升级真实库:装 langgraph 重写 build_agent_graph | 编码 |
📖 第一步:理论学习
1. 为什么 Day 6 的 while 循环不够用
手写循环 hardcode 了"调 LLM → 执行工具"这一种流转。真实 Agent 需要: 多 Agent 切换、人工审批节点、失败重试分支、断点恢复……全塞进 for 循环就是面条地狱。 图(Graph)把"有哪些步骤"和"步骤间怎么流转"显式声明出来,框架负责执行流转。
2. LangGraph 四个核心概念
| 概念 | 是什么 | 对应 Day 6 的 |
|---|---|---|
| State(状态) | 一个 TypedDict,所有节点共享读写 | 你手写的 steps 列表 + messages |
| Node(节点) | 一个函数:读 state → 干活 → 返回 state 更新 | react_agent 循环体里的一段 |
| Edge(边) | 固定流转:A 完了必到 B | 循环体里的先后顺序 |
| Conditional Edge(条件边) | 路由函数决定下一步去哪 | if 有 tool_calls: 执行工具 else: 结束 |
from langgraph.graph import StateGraph, END
from typing import TypedDict, List, Dict
class AgentState(TypedDict): # ① 状态
messages: List[Dict]
step_count: int
def node_call_model(state): # ② 节点:调 LLM
response = mock_llm_response(state["messages"])
return {"messages": state["messages"] + [response],
"step_count": state["step_count"] + 1}
def node_call_tool(state): # ② 节点:执行工具
...把每个 tool_call 的结果以 role=tool 追加进 messages...
def router(state): # ③ 路由函数
if state["messages"][-1].get("tool_calls"):
return "call_tool" # 还有工具要执行
return "end" # 没有了 → 结束
graph = StateGraph(AgentState) # ④ 组装
graph.add_node("call_model", node_call_model)
graph.add_node("call_tool", node_call_tool)
graph.set_entry_point("call_model")
graph.add_conditional_edges("call_model", router,
{"call_tool": "call_tool", "end": END})
graph.add_edge("call_tool", "call_model") # 执行完工具 → 回到模型
app = graph.compile()
3. 手写循环 vs 图(一一对应,帮助理解)
Day 6: for step in range(max_steps): Day 7: 图的自动流转
prompt = build(...) node_call_model
response = llm(prompt) │
if 有 tool_calls: router ─┤ call_tool → 回 call_model
execute + 回传 └ end → END
else: return 最终回答
🛠 第二步:动手实操
- 先跑模拟器:模板内置
simulate_langgraph_loop(),不需要装库。 完成 TODO 让它正确执行工具并给出最终回答。 - 打开模板:day07_practice_template.py:
- TODO 1.1:AgentState 加 messages / step_count 字段
- TODO 2.2 / 2.3:工具列表转换 + 执行(Day 4 原题)
- TODO 4.1 / 4.2:
node_call_model/node_call_tool(读 state → 返回更新) - TODO 5.1:
router_should_continue(有 tool_calls → "call_tool",否则 "end") - TODO 7.1:
run_agent()—— 装了 langgraph 就 graph.invoke(state),没装降级调模拟器
- 升级真实库(推荐):
pip install langgraph,把build_agent_graph()里的注释展开成真实实现—— 组件函数你都已经写好,组装只需 8 行。 - 验收:
python day07_practice_validator.py—— 验收会构造 state 逐个调用你的节点函数、 试探你的路由、并端到端跑一遍(回答里必须真的包含工具结果)。
⚠️ 避坑
- ❌ LangChain 系版本更新快(0.1→0.2→0.3),看文档先对版本;本练习核心是图思想,API 细节随时查
- ❌ 节点函数直接改传入的 state(原地修改)—— 正确姿势是返回更新字段,由框架合并
- ❌ 路由返回值和 add_conditional_edges 的映射表对不上 → 图编译完一跑就报错
- ✅ 先用 mock/模拟器把流转跑对,再换真实 LLM——排查问题时能省一半时间