这篇适合谁
这篇适合想用显式的组件搭第一个智能体,而不是直接用黑盒的读者。文章沿用 LangGraph 快速开始的例子:一个写在单个 Python 文件里的小计算器智能体。读完你能讲清状态、节点、边和工具循环,也知道怎样加上记忆和暂停。
LangGraph 是什么
LangGraph 是面向长时间运行、有状态的智能体的底层编排框架和运行时。它由 LangChain Inc 开发,但 README 说明它也可以脱离 LangChain 单独使用。[14] 它的核心模型是图:工作流由共享的状态、读取状态并返回更新的节点,以及决定下一步运行什么的边组成,边分为固定边和条件边。[3] 循环不用藏在提示词里,而是画成图。
准备工作
用 pip install -U langgraph langchain langchain-anthropic 安装依赖,也可以用 uv add langgraph langchain langchain-anthropic。[1]
pip install -U langgraph langchain langchain-anthropic快速开始需要模型提供商的 API key。在 shell 里用 export ANTHROPIC_API_KEY="your-api-key" 设置,或者在 .env 文件里写入 ANTHROPIC_API_KEY=your-api-key。[1]
搭建智能体
快速开始把整个智能体放在一个文件 agent.py 里,分八步完成:定义工具和模型;定义状态;定义模型节点;定义工具节点;定义结束逻辑;构建并编译智能体;可选地加上 LangSmith 追踪;运行智能体。[1]
- 定义工具和模型。模型用
init_chat_model("claude-sonnet-4-6", temperature=0)初始化。工具是用langchain.tools中的@tool装饰器标记的函数,model.bind_tools(tools)把它们绑定到模型上。[1] - 定义状态。状态是
MessagesState(TypedDict),其中有messages: Annotated[list[AnyMessage], operator.add]和llm_calls: int。Annotated加上operator.add的作用是追加新消息,而不是替换整个列表。[1][3] LangGraph 还内置了归约器add_messages(从langgraph.graph.message导入),它会按 ID 更新已有消息,并接受简写的消息格式;单纯的operator.add只能追加。[3] - 定义模型节点。节点是读取状态并返回更新的函数。模型节点调用模型,并把回复作为新消息返回。[3]
- 定义工具节点。工具节点负责执行模型请求的工具调用。工作流的示例把它写成普通函数:遍历
tool_calls,逐个调用工具,并为每个结果返回带有对应tool_call_id的ToolMessage。[6] 预置的ToolNode做同样的事,还内置了并行执行和错误处理,用法是builder.add_node("tools", ToolNode([search, calculator]))。[6] - 定义结束逻辑。路由函数
should_continue(state: MessagesState) -> Literal["tool_node", END]在最后一条消息带有tool_calls时返回"tool_node",否则返回END。[1] - 构建并编译。快速开始创建
StateGraph(MessagesState),用add_node添加节点并连接。add_edge(START, "llm_call")定义起点;add_conditional_edges("llm_call", should_continue, ["tool_node", END])决定每次模型调用后的走向;add_edge("tool_node", "llm_call")把工具结果送回模型。最后compile()把图变成可运行的对象。[1] 编译时会检查图的结构,比如有没有没被任何边连接的孤立节点。[3] - 运行。用
python agent.py启动文件,通过agent.invoke({"messages": messages})调用智能体,快速开始中工具的结果是7。[1]
python agent.py图长什么样
概览页给出的最小的图只有一个节点和两条边,它展示了 LangGraph 程序共有的骨架:[2]
from langgraph.graph import StateGraph, MessagesState, START, END
def mock_llm(state: MessagesState):
return {"messages": [{"role": "ai", "content": "hello world"}]}
graph = StateGraph(MessagesState)
graph.add_node(mock_llm)
graph.add_edge(START, "mock_llm")
graph.add_edge("mock_llm", END)
graph = graph.compile()
graph.invoke({"messages": [{"role": "user", "content": "hi!"}]})START 是虚拟的入口节点,END 表示终止边,入口就是从 START 出发的一条边。[3] 构建清单推荐了两种更复杂场景的模式:Send 用于对数量在运行时才知道的一组条目,各自执行同一个节点(扇出再汇总);Command 用于一个步骤既要更新状态,又要决定下一个节点。[13]
循环需要有终止条件
计算器智能体就是一个循环:模型调用工具,工具结果回到模型,模型再决定要不要继续调用。循环必须有终止条件,这里由通向 END 的条件边提供。LangGraph 还用递归上限限制超级步(super-step)的数量。超过上限会抛出 GraphRecursionError,它从 langgraph.errors 导入,上限用配置里的 recursion_limit 设置。[5]
从 1.1.4 版本开始,默认递归上限是 10,007 个超级步;1.0.6 到 1.1.3 之间是 10,000。环境变量 LANGGRAPH_DEFAULT_RECURSION_LIMIT 或配置里顶层的 recursion_limit 可以覆盖默认值。[3]
加上记忆
快速开始的智能体每次运行结束后就忘了一切。LangGraph 有两套持久化机制来解决这个问题。检查点(checkpointer)保存一个线程(thread)的图状态,实现同一段对话内的短期记忆;存储(store)保存应用自定义的数据,可以跨线程共享,实现长期记忆。[8]
要在多次调用之间保留状态,就用检查点编译图。持久化的快速开始是这样写的:[8]
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.store.memory import InMemoryStore
checkpointer = InMemorySaver()
store = InMemoryStore()
graph = builder.compile(checkpointer=checkpointer, store=store)
result = graph.invoke(
{"messages": [{"role": "user", "content": "Hi, my name is Bob."}]},
{"configurable": {"thread_id": "thread-1"}},
)用检查点调用图时,必须在配置的 configurable 部分传入 thread_id,例如 {"configurable": {"thread_id": "1"}}。检查点用它作为存取检查点的主键。[9] 多轮对话靠的是 compile 时传入 checkpointer=checkpointer,检查点用 langgraph.checkpoint.memory 中的 InMemorySaver。[11]
持久化还有一个 durability= 参数:"exit" 只在图退出时保存,"async" 在下一步执行时异步保存,"sync" 在下一步开始前同步保存。[9]
暂停等待人工输入
interrupt()(从 langgraph.types 导入)可以在节点内暂停执行。它会用检查点保存状态,然后无限期等待。使用它需要检查点、配置中的 thread_id,以及可以 JSON 序列化的负载。[10] 恢复时用 Command(resume=...) 调用,传入的值会成为被暂停的 interrupt() 调用的返回值,并且必须沿用同一个 thread_id。[10]
图恢复时,节点会从头重新运行。因此,节点里 interrupt() 之前的操作必须是幂等的,也就是重复执行也不会产生副作用。[7]
限制和常见问题
内存检查点是第一个坑。MemorySaver 和 InMemorySaver 把检查点存在内存里,进程一重启就全部丢失。正式环境应该用持久化的检查点,比如 PostgresSaver 或 SqliteSaver。[8]
中断既需要检查点,也需要线程 ID。没有检查点,图无法暂停再恢复。[7]
循环如果永远走不到 END,达到上限后会以 GraphRecursionError 结束。解决办法是在路由函数里写清楚终止条件,而不只是把上限调大。[5]
下一步
最好的下一步是把快速开始完整读一遍,它把整个计算器智能体拆开讲了。[1] 想了解状态和路由规则,读 Graph API 概览和状态页面。[3][4] 想了解记忆和暂停,读持久化、检查点和中断三页。[8][9][10] 设计更大的图之前,先看构建清单。[13]
来源
- 1QuickstartLangChain Docs
- 2LangGraph overviewLangChain Docs
- 3Graph API overviewLangChain Docs
- 4Define and update stateLangChain Docs
- 5Build sequences, branches, and loopsLangChain Docs
- 6Workflows and agentsLangChain Docs
- 7Thinking in the Graph APILangChain Docs
- 8PersistenceLangChain Docs
- 9CheckpointersLangChain Docs
- 10InterruptsLangChain Docs
- 11MemoryLangChain Docs
- 12StreamingLangChain Docs
- 13Build checklist for the Graph APILangChain Docs
- 14LangGraph READMELangChain (GitHub)
引用本页: 用 LangGraph quickstart 搭一个会调工具的智能体. TellWorld, https://www.tellworld.net/zh/practice/practice-langgraph-quickstart. 更新于 2026-11-14