LangGraph快速入门 —— Agent 是怎么转起来的

第二篇:Agent 是怎么转起来的

上一篇讲了骨架:状态、节点、边。这一篇给图装上一个会自己判断下一步的模型,涉及 test3.py、test2.py、test12.py 和 test13.py。

工具先要有说明书

复制代码
from langchain_core.tools import tool

@tool
def add_tool(a: int, b: int) -> int:
    """计算两个整数之和"""
    return a + b

test3.py 只做了一件事,验证工具定义能不能被模型看懂。docstring 是给模型看的说明,参数类型会被自动转成 JSON Schema。写不清楚说明,模型就调错参数,这个和提示词质量是一回事。

ReAct 是个回环

我原以为 ReAct 是某种特殊 Agent 结构。拆开看,它就是一个带条件边的循环:

复制代码
START → agent ──有 tool_calls──→ tools → agent
              └─没有──────────→ END

builder.add_node("agent", agent_node)
builder.add_node("tools", ToolNode(tools))
builder.add_edge(START, "agent")
builder.add_conditional_edges("agent", tools_condition)
builder.add_edge("tools", "agent")

agent 调模型,模型可能返回若干 tool_calls。tools_condition 看到这些调用就去 tools 节点,ToolNode 执行工具并把结果包成 ToolMessage 塞回消息列表。下一轮模型看到工具结果,自己决定继续调工具还是给答案。

test2.py 是完整版的这个循环,代码不到一百行。

并行与异常

test12.py 试了两件事。模型一次返回多个 tool_calls 时,ToolNode 会并行执行,不需要自己写并发。工具抛异常时,错误信息会作为工具结果回传给模型,模型看到参数错误会自己改一次再调。这个机制救过我几次,尤其是工具参数格式写错的时候。

四个护栏

test13.py 是我把几个工程细节塞在同一个脚本里的一次尝试:

复制代码
config = {"recursion_limit": 25}

recursion_limit 是回环上唯一的刹车。模型反复调用同一个工具时,没有这个上限就会一直转。我设 25,够两三次工具往返。

RetryPolicy(max_attempts=3) 挂在节点上,处理限流和网络抖动。它会重试节点本身,不是重试整个图。

llm.with_structured_output(Schema) 强制模型按 Pydantic 模型输出,省掉解析 JSON 的防御代码。test14.py 的 supervisor 路由就是靠它做出来的。

stream_mode="messages" 配合前端做流式展示,前面提过,这里不重复。

一个副作用

test4.py 在 ReAct 循环上加了一个 MemorySaver,用来维持多轮对话。代码很简单:

复制代码
graph = builder.compile(checkpointer=memory)

不加的时候,每次 invoke 都从空状态开始,用户说"再来一个",模型不知道"一个"指什么。加上之后,同一个 thread_id 的对话会接着上一轮继续。

这个 MemorySaver 只是临时方案,进程退出数据就没了。第三篇把 checkpointer 展开讲。

完整示例

这一段需要模型,把三个环境变量配好就能跑:

复制代码
OPENAI_API_KEY=sk-xxx
OPENAI_BASE_URL=https://api.siliconflow.cn/v1
MODEL_NAME=deepseek-ai/DeepSeek-V4-Flash

# demo_02_agent.py
import os
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph, START, MessagesState
from langgraph.prebuilt import ToolNode, tools_condition
from langgraph.checkpoint.memory import MemorySaver

@tool
def multiply(a: int, b: int) -> int:
    """计算两个整数的乘积"""
    return a * b

@tool
def divide(a: int, b: int) -> float:
    """计算 a 除以 b,b 不能为 0"""
    if b == 0:
        raise ValueError("除数不能为 0")
    return a / b

tools = [multiply, divide]

llm = ChatOpenAI(
    model=os.getenv("MODEL_NAME", "deepseek-ai/DeepSeek-V4-Flash"),
    api_key=os.getenv("OPENAI_API_KEY"),
    base_url=os.getenv("OPENAI_BASE_URL", "https://api.siliconflow.cn/v1"),
    temperature=0,
)
llm_with_tools = llm.bind_tools(tools)

def agent_node(state: MessagesState):
    return {"messages": [llm_with_tools.invoke(state["messages"])]}

builder = StateGraph(MessagesState)
builder.add_node("agent", agent_node)
builder.add_node("tools", ToolNode(tools))
builder.add_edge(START, "agent")
builder.add_conditional_edges("agent", tools_condition)
builder.add_edge("tools", "agent")

graph = builder.compile(checkpointer=MemorySaver())

config = {"configurable": {"thread_id": "demo-2"}, "recursion_limit": 25}
graph.invoke(
    {"messages": [("user", "先算 123 乘 456,再用结果除以 9")]},
    config,
)

for message in graph.get_state(config).values["messages"]:
    message.pretty_print()

运行 python demo_02_agent.py。输出里能看到四次消息:用户提问、模型发起 multiply 调用、ToolMessage 返回 56088、模型再发起 divide 调用,最后一轮给出答案。

改一下问题,比如"456 除以 0 等于几",会看到工具抛出的错误被包成消息,模型自己解释除数为 0。

第二篇收尾

到这里,图能跑、能调工具、能循环、能记住几轮对话。下一篇处理两个更硬的问题:快照怎么落到数据库,以及图怎么在中间停下来等人。

系列目录:

  • 第一篇:一张状态图是怎么跑起来的

  • 第二篇:Agent 是怎么转起来的

  • 第三篇:让图记住,也让它停下来

  • 第四篇:多个 Agent 怎么分工

  • 第五篇:从本地脚本到 HTTP 服务

相关推荐
莫西干式秃头2 小时前
用 AI + threejs 写了个浏览器里的 3D 动作游戏《武来》:砍怪、放技能、开 SU7
前端·人工智能·three.js
遇码2 小时前
2026-11-01-daw-retry-banner-body
人工智能·ai·rust·数据分析·大模型·agent
孟健3 小时前
用 WorkBuddy 做个人名片网页,挂上开源设计技能后效果完全不同了
前端·人工智能·ai编程
一点一木3 小时前
🚀 2026 年 10 月 GitHub 十大热门项目排行榜 🔥
人工智能·github
IT_陈寒3 小时前
SpringBoot自动配置的坑,这次终于爬出来了
前端·人工智能·后端
Martina_03213 小时前
AI生成的3D模型打印后沿层纹开裂?用6步检查方向、壁厚与填充
图像处理·人工智能·数学建模·3d·自然语言处理·游戏策划·1024程序员节
极限挑战哥3 小时前
共享电单车联营平台分账架构实践:二清合规与自动分账零人工方案
java·大数据·人工智能·微信小程序·架构·微信公众平台
回眸&啤酒鸭3 小时前
【回眸】Hy4 Preview 多模态内容生成实战指南
人工智能
阿里云大数据AI技术3 小时前
云栖实录|让每一次查询只触达自己的数据:阿里云 Elasticsearch Agent 引擎版重构多租户 AI 搜索基础设施
人工智能
QXWZ_IA3 小时前
公路边坡裂缝的自动化监测布设
大数据·人工智能·自动化