Part 1 · 起步
03 手搭一个会调用工具的客服助手
03.1 我们最终做成什么
做一个客服助手:能聊天、能查天气、能把用户信息存库。
做完你就能看清一张图的骨架:节点、边、状态。后面所有复杂系统都是这个骨架长出来的。
03.2 定义状态:图里的"共享白板"
from typing import Annotated, TypedDict
from langchain_core.messages import AnyMessage
import operator
class AgentState(TypedDict):
messages: Annotated[list[AnyMessage], operator.add]
operator.add 是"归约器"(reducer)。新消息加到旧列表后面,不是覆盖。这点很关键。
03.3 搭节点和边
from langgraph.graph import StateGraph, START, END
from langgraph.prebuilt import ToolNode, tools_condition
from langchain_core.messages import HumanMessage
tools = [get_weather, save_user_info]
tool_node = ToolNode(tools)
llm_with_tools = llm.bind_tools(tools)
def call_model(state):
resp = llm_with_tools.invoke(state["messages"])
return {"messages": [resp]}
g = StateGraph(AgentState)
g.add_node("agent", call_model) # AI 决策节点
g.add_node("tools", tool_node) # 工具执行节点
g.add_edge(START, "agent") # 入口
g.add_conditional_edges("agent", tools_condition, {"tools": "tools", END: END})
g.add_edge("tools", "agent") # 工具跑完回到 AI 总结
graph = g.compile()
tools_condition 是 LangGraph 预置的路由函数。它看最后一条消息有没有 tool_calls,有就走 tools,没有就 END。
03.4 跑一下
out = graph.invoke(
{"messages": [HumanMessage(content="北京今天天气怎么样?")]},
{"recursion_limit": 10},
)
print(out["messages"][-1].content)
预期结果:Agent 调 get_weather,再总结成一句人话。
注意:一定要设递归上限 没设
recursion_limit,AI 一旦陷进"调工具—不满意—再调"的循环,进程就卡死。我默认设 10。
03.5 回顾
我们从零搭了一张带循环的真图。你看到了图的三个核心零件:节点(算什么)、边(走哪条)、状态(存什么)。
核心建议:先手搭,再偷懒 手搭一遍,你就懂
create_react_agent内部在干嘛。之后用预置的,心里才有底。
▸ 向前桥接 这张图还很简单。下一章,我把"节点、边、状态"拆开讲透。