从零搭建你的第一个 AI Agent:LangGraph 完全上手指南

从零搭建你的第一个 AI Agent:LangGraph 完全上手指南

本文适合:有 Python 基础、想动手搭 Agent 但不知从哪开始的开发者。读完你将掌握 LangGraph 的核心模型,并能独立跑通一个带工具调用的 Claude Agent。


为什么是 LangGraph?

现在 Agent 框架一抓一大把,为什么选 LangGraph?

我踩过几个坑之后总结了一句话:其他框架帮你把 Agent 封装好了,LangGraph 帮你把 Agent 的骨架搭出来。

封装好的框架上手快,但一旦要做复杂逻辑------多步推理、条件分支、循环调用、状态回滚------你就开始跟框架的"魔法"较劲。LangGraph 没有这个问题,因为它把控制权彻底交给你:Agent 的每一步做什么、什么条件跳哪里、状态怎么传递,全是你自己定义的图结构。

核心优势总结:

  • 图结构编排:循环、分支、并行,复杂流程用图来描述天然清晰
  • 状态持久化:内置 checkpoint,多轮对话、断点续跑开箱即用
  • 流式输出:原生支持 streaming,做实时响应的产品不用另外折腾
  • 生产就绪:LangSmith 一键接入,调试和监控都有
  • 与 LangChain 生态无缝集成:几百个现成工具直接用

核心概念:三句话讲清楚

LangGraph 把 Agent 的运行过程建模成一个有向图,只有三个核心概念:

State(状态):一个贯穿整个流程的数据结构,所有节点都从这里读数据、往这里写数据。你可以把它理解为 Agent 的"工作内存"。

Node(节点):每个节点是一个 Python 函数,接收当前 state,处理后返回更新后的 state。节点可以是 LLM 调用、工具执行、数据处理------任何逻辑都行。

Edge(边):决定一个节点执行完之后去哪个节点。边可以是固定的(永远走这条路),也可以是条件判断的(根据 state 的内容决定走哪条路)。

css 复制代码
用户输入 → [Node: 分类] → [条件边] → [Node: 工具调用] → [Node: 生成回答] → 输出
                                    ↘ [Node: 直接回答] ↗

就这三个东西,组合起来可以描述任意复杂度的 Agent 流程。


环境准备

bash 复制代码
pip install langgraph langchain-anthropic

设置 API Key(以 Claude 为例):

bash 复制代码
export ANTHROPIC_API_KEY="sk-ant-api03-你的key"

验证安装:

python 复制代码
import langgraph
print(langgraph.__version__)  # 0.2.x

第一步:跑通最简单的图

先不管 LLM,写一个纯 Python 的图,理解数据怎么流动:

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

# 定义 State:这个图里流转的所有数据
class State(TypedDict):
    input: str
    result: str

# 节点函数:接收 state,返回要更新的字段
def process(state: State) -> dict:
    text = state["input"].upper()
    return {"result": f"处理完成:{text}"}

# 构建图
builder = StateGraph(State)
builder.add_node("process", process)   # 注册节点
builder.set_entry_point("process")     # 设置入口
builder.add_edge("process", END)       # process 完成后结束

graph = builder.compile()

# 运行
output = graph.invoke({"input": "hello langgraph"})
print(output)
# {'input': 'hello langgraph', 'result': '处理完成:HELLO LANGGRAPH'}

注意几个细节:

  • 节点函数只需要返回要更新的字段,不需要返回完整 state,LangGraph 会自动合并
  • END 是内置的终止标记
  • graph.invoke() 是同步运行,graph.stream() 是流式运行

第二步:接入 Claude,做一个对话 Agent

python 复制代码
from typing import TypedDict, Annotated
from langgraph.graph import StateGraph, END
from langchain_anthropic import ChatAnthropic
from langchain_core.messages import HumanMessage, AIMessage
import operator

# messages 字段用 operator.add 做合并策略
# 这样每次更新是追加消息,而不是覆盖
class State(TypedDict):
    messages: Annotated[list, operator.add]

model = ChatAnthropic(model="claude-sonnet-4-20250514")

def call_model(state: State) -> dict:
    response = model.invoke(state["messages"])
    return {"messages": [response]}

builder = StateGraph(State)
builder.add_node("agent", call_model)
builder.set_entry_point("agent")
builder.add_edge("agent", END)

graph = builder.compile()

result = graph.invoke({
    "messages": [HumanMessage(content="用一句话介绍 LangGraph")]
})

print(result["messages"][-1].content)

这里有个重要设计:Annotated[list, operator.add] 告诉 LangGraph,当多个节点都更新 messages 字段时,用 operator.add(列表追加)而不是直接覆盖。这是 LangGraph 的 reducer 机制,让状态合并的行为完全可控。


第三步:加工具,实现 ReAct Agent

这是最核心的部分。ReAct 模式让 Agent 在"推理"和"行动"之间循环:

  1. Claude 看到问题,决定要不要调用工具
  2. 如果要调用,执行工具,把结果返回给 Claude
  3. Claude 继续推理,直到得出最终答案
python 复制代码
from typing import TypedDict, Annotated
from langgraph.graph import StateGraph, END
from langgraph.prebuilt import ToolNode
from langchain_anthropic import ChatAnthropic
from langchain_core.messages import HumanMessage
from langchain_core.tools import tool
import operator

# --- 定义工具 ---

@tool
def search_weather(city: str) -> str:
    """查询指定城市的实时天气"""
    # 实际项目里接真实天气 API
    weather_data = {
        "北京": "晴天,26°C,东南风 3 级",
        "上海": "多云,22°C,东风 2 级",
        "广州": "小雨,28°C,南风 1 级",
    }
    return weather_data.get(city, f"暂无 {city} 的天气数据")

@tool
def calculate(expression: str) -> str:
    """
    计算数学表达式。
    支持加减乘除和基本函数,例如:'(100 + 200) * 0.8'
    """
    try:
        result = eval(expression, {"__builtins__": {}}, {})
        return f"计算结果:{result}"
    except Exception as e:
        return f"计算失败:{e}"

tools = [search_weather, calculate]

# --- 搭建 Agent ---

class State(TypedDict):
    messages: Annotated[list, operator.add]

# bind_tools 让 Claude 知道有哪些工具可以调用
model = ChatAnthropic(model="claude-sonnet-4-20250514").bind_tools(tools)

def agent_node(state: State) -> dict:
    """核心节点:Claude 推理,决定下一步"""
    response = model.invoke(state["messages"])
    return {"messages": [response]}

def should_continue(state: State) -> str:
    """条件边:判断 Claude 是否发起了工具调用"""
    last_msg = state["messages"][-1]
    if hasattr(last_msg, "tool_calls") and last_msg.tool_calls:
        return "tools"
    return END

# --- 构建图 ---

builder = StateGraph(State)
builder.add_node("agent", agent_node)
builder.add_node("tools", ToolNode(tools))  # 内置工具执行节点

builder.set_entry_point("agent")

# 条件边:agent 执行完后,根据 should_continue 的返回值决定走向
builder.add_conditional_edges("agent", should_continue)

# 工具执行完毕,回到 agent 继续推理(这里形成了循环)
builder.add_edge("tools", "agent")

graph = builder.compile()

# --- 运行 ---

result = graph.invoke({
    "messages": [
        HumanMessage(content="帮我查一下北京和上海的天气,然后告诉我 (26 + 22) / 2 是多少")
    ]
})

# 打印完整对话链路
for msg in result["messages"]:
    role = msg.__class__.__name__
    content = str(msg.content)[:150]
    print(f"\n[{role}]\n{content}")

运行后你会看到完整的推理链路:

scss 复制代码
[HumanMessage]
帮我查一下北京和上海的天气,然后告诉我 (26 + 22) / 2 是多少

[AIMessage]
(Claude 决定调用工具)

[ToolMessage]
晴天,26°C,东南风 3 级

[ToolMessage]
多云,22°C,东风 2 级

[ToolMessage]
计算结果:24.0

[AIMessage]
北京今天晴天,气温 26°C;上海多云,22°C。两地平均气温是 24°C。

这个循环结构是 ReAct Agent 的精髓,可以支持任意轮次的工具调用。


第四步:加记忆,支持多轮对话

Agent 本身是无状态的,每次 invoke 都是全新对话。加上 checkpoint 之后,同一个 thread_id 的历史消息会自动保留:

python 复制代码
from langgraph.checkpoint.memory import MemorySaver

# 内存版 checkpointer(进程退出后消失)
memory = MemorySaver()
graph = builder.compile(checkpointer=memory)

# 用 thread_id 区分不同用户 / 会话
config = {"configurable": {"thread_id": "user_001"}}

# 第一轮
graph.invoke(
    {"messages": [HumanMessage(content="我是小明,我在北京")]},
    config=config
)

# 第二轮:自动携带上文
result = graph.invoke(
    {"messages": [HumanMessage(content="帮我查一下我所在城市的天气")]},
    config=config
)
print(result["messages"][-1].content)
# Claude 会自动关联上文,查询北京的天气

生产环境建议用 SQLite 持久化,重启服务后对话历史不丢失:

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

memory = SqliteSaver.from_conn_string("./checkpoints.db")
graph = builder.compile(checkpointer=memory)

第五步:流式输出

做产品时几乎必用,让用户实时看到 Agent 的思考过程:

python 复制代码
# stream 返回每个节点执行后的状态快照
for chunk in graph.stream(
    {"messages": [HumanMessage(content="北京天气怎么样?")]},
    config={"configurable": {"thread_id": "stream_demo"}}
):
    for node_name, output in chunk.items():
        print(f"\n>>> 节点 [{node_name}] 输出:")
        if "messages" in output:
            last = output["messages"][-1]
            print(last.content[:200] if last.content else "(工具调用中...)")

如果要做字符级流式(像 ChatGPT 那种逐字显示),用 astream_events

python 复制代码
async for event in graph.astream_events(
    {"messages": [HumanMessage(content="介绍一下 LangGraph")]},
    config=config,
    version="v2"
):
    if event["event"] == "on_chat_model_stream":
        chunk = event["data"]["chunk"]
        if chunk.content:
            print(chunk.content, end="", flush=True)

完整项目结构

当 Agent 逻辑复杂起来,建议这样组织代码:

bash 复制代码
my_agent/
├── main.py           # 入口:运行 / 启动服务
├── graph.py          # 图的定义和编译
├── state.py          # State 数据结构定义
├── nodes/
│   ├── agent.py      # LLM 推理节点
│   └── router.py     # 条件路由逻辑
└── tools/
    ├── weather.py    # 天气工具
    ├── search.py     # 搜索工具
    └── calculator.py # 计算工具

graph.py 示例:

python 复制代码
from langgraph.graph import StateGraph, END
from langgraph.prebuilt import ToolNode
from langgraph.checkpoint.sqlite import SqliteSaver
from state import State
from nodes.agent import agent_node, should_continue
from tools import all_tools

def build_graph():
    builder = StateGraph(State)
    builder.add_node("agent", agent_node)
    builder.add_node("tools", ToolNode(all_tools))
    builder.set_entry_point("agent")
    builder.add_conditional_edges("agent", should_continue)
    builder.add_edge("tools", "agent")
    
    memory = SqliteSaver.from_conn_string("./data/checkpoints.db")
    return builder.compile(checkpointer=memory)

graph = build_graph()

常见问题

Q:节点函数返回完整 state 还是只返回更新的字段?

只需要返回要更新的字段。LangGraph 会把返回值和当前 state 合并。如果某个字段定义了 reducer(比如 operator.add),会按 reducer 规则合并;否则直接覆盖。

Q:条件边的函数返回值有什么要求?

返回字符串,对应 add_conditional_edges 第三个参数字典的 key。也可以直接返回节点名称字符串,省略映射字典。

Q:怎么中断 Agent,等待人工确认再继续?

python 复制代码
graph = builder.compile(
    checkpointer=memory,
    interrupt_before=["sensitive_node"]  # 执行这个节点前暂停
)

暂停后用 graph.invoke(None, config=config) 继续执行。

Q:LangGraph 和 LangChain Agents 有什么区别?

LangChain 的 AgentExecutor 是封装好的黑盒,适合简单场景;LangGraph 是底层图引擎,适合需要精细控制的复杂 Agent。两者不是替代关系,LangGraph 现在是 LangChain 官方推荐的 Agent 构建方式。


总结

LangGraph 的学习路径很清晰:

  1. 理解 State / Node / Edge 三个概念
  2. 跑通线性图,搞清楚数据怎么流动
  3. 接入 LLM,实现基础问答
  4. 加工具 + 条件循环,实现 ReAct Agent
  5. 加 checkpoint,支持多轮对话
  6. 按需扩展:并行节点、子图、人工审核节点

整个框架思路非常直接,没有太多魔法,读懂了源码你会发现它做的事情就是"按照你定义的图,一步步执行节点,传递状态"。

代码都是可以直接运行的,换上你自己的 API Key 就能跑。有问题欢迎评论区交流。


参考资料

相关推荐
宋哥转AI1 小时前
Java后端转AI Agent:技术栈全景图与从ReAct到多Agent协作实战
java·人工智能·agent
樱花的浪漫1 小时前
Typescript、Zod基础
前端·javascript·人工智能·语言模型·自然语言处理·typescript
媒介发稿小能手1 小时前
合规优化与API接口协同:2026年靠谱GEO媒介资源平台推荐清单
大数据·人工智能
用户337922545681 小时前
基于 SAM3 + FastAPI 搭建智能图像标注工具实战
人工智能
Y敲键盘的地方1 小时前
第10章 上下文工程——有限窗口的无限智慧
人工智能
ylscode1 小时前
谷歌Gemini Go正式登场:轻量级AI助手让低端手机也能玩转生成式智能
网络·人工智能·安全·chatgpt
moonsims1 小时前
基于Lattice Mesh的AI 的分布式共识与动态任务分配架构的无人机群“去中心化无声协同”技术和极低带宽下的韧性通信技术
人工智能·分布式·架构
七牛云行业应用1 小时前
GitHub Copilot 2026年6月新计费实战:AI Credits怎么算、怎么省
人工智能·github·copilot
薛定猫AI1 小时前
【技术干货】DeepSeek 桌面智能体应用全解析:开源 AI Agent 平台实战部署与 API 调用指南
人工智能·microsoft