TellWorldAI 世界档案
问档案English

首页›实践›实践

实践

用 LangGraph quickstart 搭一个会调工具的智能体

LangGraph 把智能体写成由状态、节点和边组成的图。本文沿用快速开始的例子,用 Claude 搭一个会调用工具的智能体,讲清运行流程,并介绍如何加上记忆和等待人工输入的中断。

这篇适合谁

这篇适合想用显式的组件搭第一个智能体,而不是直接用黑盒的读者。文章沿用 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]

  1. 定义工具和模型。模型用 init_chat_model("claude-sonnet-4-6", temperature=0) 初始化。工具是用 langchain.tools 中的 @tool 装饰器标记的函数,model.bind_tools(tools) 把它们绑定到模型上。[1]
  2. 定义状态。状态是 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. 定义模型节点。节点是读取状态并返回更新的函数。模型节点调用模型,并把回复作为新消息返回。[3]
  4. 定义工具节点。工具节点负责执行模型请求的工具调用。工作流的示例把它写成普通函数:遍历 tool_calls,逐个调用工具,并为每个结果返回带有对应 tool_call_id 的 ToolMessage。[6] 预置的 ToolNode 做同样的事,还内置了并行执行和错误处理,用法是 builder.add_node("tools", ToolNode([search, calculator]))。[6]
  5. 定义结束逻辑。路由函数 should_continue(state: MessagesState) -> Literal["tool_node", END] 在最后一条消息带有 tool_calls 时返回 "tool_node",否则返回 END。[1]
  6. 构建并编译。快速开始创建 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]
  7. 运行。用 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]

来源

  1. 1
    QuickstartLangChain Docs
  2. 2
    LangGraph overviewLangChain Docs
  3. 3
    Graph API overviewLangChain Docs
  4. 4
  5. 5
  6. 6
    Workflows and agentsLangChain Docs
  7. 7
  8. 8
    PersistenceLangChain Docs
  9. 9
    CheckpointersLangChain Docs
  10. 10
    InterruptsLangChain Docs
  11. 11
    MemoryLangChain Docs
  12. 12
    StreamingLangChain Docs
  13. 13
  14. 14
    LangGraph READMELangChain (GitHub)

引用本页: 用 LangGraph quickstart 搭一个会调工具的智能体. TellWorld, https://www.tellworld.net/zh/practice/practice-langgraph-quickstart. 更新于 2026-11-14

相关动态