🏗️ 预构建 Agent ★
LangGraph 系统的收官章!前 7 章你学了所有底层机制。本章用 create_react_agent + ToolNode——一行代码组装完整 ReAct Agent。这是 LG1-LG7 所有知识的集大成,也是日常最常用的 API。
本章目标
- 用
create_react_agent快速构建工具调用 Agent - 理解它内部如何用 LG1-LG7 的知识组装图(agent↔tools 循环)
- 掌握
ToolNode和tools_condition的配合 - 能配置 checkpointer、interrupt、hooks 增强预构建 Agent
- 对比它和 LC8 的 AgentExecutor,看 LangGraph 的优势如何体现
create_react_agent:一行成 Agent
这是最常用的预构建 Agent。它内部自动构建了 LG3 讲的 agent↔tools 循环图,你只需提供模型和工具:
def create_react_agent(
model, # LLM(静态实例 或 动态选择函数)
tools, # 工具列表 或 ToolNode
*,
prompt=None, # 系统提示(字符串 / 模板 / 函数)
response_format=None, # 结构化输出 schema
pre_model_hook=None, # 模型调用前的钩子(如消息裁剪)
post_model_hook=None, # 模型调用后的钩子
state_schema=None, # 自定义状态 schema
checkpointer=None, # 检查点(记忆/中断,LG6)
interrupt_before=None, # 在某节点前中断(HITL,LG6)
interrupt_after=None,
debug=False,
) -> CompiledStateGraph:
"""创建一个循环调用工具直到停止的 agent 图。
返回 CompiledStateGraph(继承 Pregel,LG5)。
内部构建:agent(调LLM)↔ tools(ToolNode)循环,
用 tools_condition 条件边路由(LG3)。
⚠️ 已废弃,建议迁移到 langchain.agents.create_agent(middleware 系统)。
但仍是学习 ReAct 模式的最佳参考。
"""
内部结构:它做了什么
看它内部如何用前面学的知识组装。这正是 LG1-LG7 的集大成:
# 内部状态:就是 MessagesState(LG2 的 add_messages reducer!)
class AgentState(TypedDict):
messages: Annotated[Sequence[BaseMessage], add_messages] # ← LG2
remaining_steps: NotRequired[RemainingSteps] # 步数限制
# create_react_agent 内部等价于构建这个图(LG1 + LG3):
def _build_graph(model, tools):
graph = StateGraph(AgentState)
# 节点1:agent(调 LLM,绑定工具 LC3+LC6)
def call_model(state):
response = model.bind_tools(tools).invoke(state["messages"])
return {"messages": [response]}
graph.add_node("agent", call_model)
# 节点2:tools(执行工具,用 ToolNode)
graph.add_node("tools", ToolNode(tools))
# 边:START → agent
graph.add_edge(START, "agent")
# 条件边(LG3):agent 后由 tools_condition 路由
graph.add_conditional_edges("agent", tools_condition) # ← LG3 的核心!
# tools 执行完回到 agent,形成循环(F3 的 Agent 循环!)
graph.add_edge("tools", "agent")
return graph.compile(checkpointer=checkpointer)
create_react_agent 的内部就是 LG1(StateGraph)+ LG2(add_messages)+ LG3(条件边循环)。没有任何新东西!它只是把这套标准模式封装成一行调用。理解了前 7 章,你就完全看透了这个"魔法函数"。
ToolNode + tools_condition
两个关键组件:ToolNode 负责执行工具,tools_condition 负责路由判断:
class ToolNode(RunnableCallable):
"""处理工具执行的节点。
从状态读 AIMessage 的 tool_calls,并行执行对应工具,
把结果作为 ToolMessage 返回。支持错误处理。"""
def tools_condition(state, messages_key="messages") -> Literal["tools", "__end__"]:
"""条件路由函数:实现 ReAct agent 的标准逻辑。
如果最后一条 AIMessage 含 tool_calls → 路由到 "tools";
否则 → 路由到 "__end__"(结束)。
这就是 F3 章的 Action/Finish 二态判断!(LC7)"""
last_msg = state[messages_key][-1]
if last_msg.tool_calls:
return "tools" # AgentAction → 去执行工具
return "__end__" # AgentFinish → 结束
可运行代码
需要 OpenAI API Key。pip install langgraph langchain-openai。对比 LC8 的 AgentExecutor,看多简洁。
# pip install langgraph langchain-openai
# export OPENAI_API_KEY="sk-..."
from langgraph.prebuilt import create_react_agent
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
# ① 定义工具(LC6 的 @tool)
@tool
def add(a: int, b: int) -> int:
"""计算两个数的和。"""
return a + b
@tool
def multiply(a: int, b: int) -> int:
"""计算两个数的乘积。"""
return a * b
@tool
def get_word_length(word: str) -> int:
"""返回单词的字符长度。"""
return len(word)
# ② 一行构建完整 Agent!(对比 LC8 的 create_tool_calling_agent + AgentExecutor)
model = ChatOpenAI(model="gpt-4o-mini")
agent = create_react_agent(
model=model,
tools=[add, multiply, get_word_length],
prompt="你是一个数学和文字助手,可以使用工具。",
)
# ③ 直接用!invoke / stream 都行
result = agent.invoke({
"messages": [("human", "(3+5)乘以单词hello的长度是多少?")]
})
print("答案:", result["messages"][-1].content)
# → "结果是 40"
# 查看完整轨迹(LC7 的 messages step 都在 messages 列表里)
print("\n轨迹:")
for m in result["messages"]:
tc = getattr(m, "tool_calls", [])
print(f" [{m.type}] {m.content[:40] if m.content else '(调工具)'}"
+ (f" → {tc}" if tc else ""))
加 checkpointer 实现记忆
from langgraph.checkpoint.memory import InMemorySaver
agent = create_react_agent(
model=model,
tools=[add, multiply],
checkpointer=InMemorySaver(), # ← 加记忆
)
config = {"configurable": {"thread_id": "chat-1"}}
agent.invoke({"messages": [("human", "我叫张三")]}, config)
result = agent.invoke({"messages": [("human", "我叫什么?")]}, config)
print(result["messages"][-1].content) # → "你叫张三"
加 interrupt 实现人在回路
agent = create_react_agent(
model=model,
tools=[dangerous_tool],
checkpointer=InMemorySaver(),
interrupt_before=["tools"], # ← 工具执行【前】暂停,等人确认!
)
config = {"configurable": {"thread_id": "t1"}}
# 第一次:执行到 tools 前暂停
agent.invoke({"messages": [("human", "删除文件")]}, config)
# 检查即将执行的工具,人工确认后继续
state = agent.get_state(config)
print("即将执行:", state.tasks)
agent.invoke(None, config) # 传 None 继续执行(人工批准后)
pre/post model hooks:消息裁剪
F2 章提过 context window 限制。pre_model_hook 可以在调 LLM 前裁剪消息:
def trim_messages(state):
"""调 LLM 前,只保留最近 20 条消息。"""
return {"messages": state["messages"][-20:]}
agent = create_react_agent(
model=model,
tools=tools,
pre_model_hook=trim_messages, # ← 每次调 LLM 前自动裁剪
)
对比 LC8 的 AgentExecutor
| 维度 | AgentExecutor(LC8) | create_react_agent(LG8) |
|---|---|---|
| 底层 | while 循环 | StateGraph → Pregel |
| 记忆 | 需手动管理 | ✓ checkpointer 一键开启 |
| 中断/HITL | ✗ | ✓ interrupt_before/after |
| 流式 | 仅文本 | ✓ 7 种 StreamMode |
| 消息裁剪 | ✗ | ✓ pre_model_hook |
| 可视化 | ✗ | ✓ draw_ascii / 时间旅行 |
| 状态 | intermediate_steps | 任意 TypedDict |
| 并行 | ✗ | ✓(Pregel BSP) |
结论:新项目优先用 LangGraph 的 create_react_agent(或更新的 langchain.agents.create_agent)。AgentExecutor 主要用于理解原理和维护旧代码。
源码标注 create_react_agent 已废弃,推荐 langchain.agents.create_agent——它用 middleware 系统(更灵活的钩子链)。但底层仍是 LangGraph 图。学完本章你已具备理解任何 Agent 工厂的能力。
LangGraph 阶段总结
恭喜系统学完 LangGraph 8 章。回顾这条进阶之路:
- StateGraph(LG1)★ —— 节点+边+状态,告别隐式循环
- Reducer(LG2)—— Annotated + add_messages,状态正确累积
- 条件边+Send(LG3)—— 动态路由 + map-reduce 并行
- Functional API(LG4)—— @entrypoint/@task 命令式写法
- Pregel 引擎(LG5)★ —— BSP 三阶段 + Channel,看透底层
- 检查点+HITL(LG6)—— interrupt + Command,人在回路
- 流式(LG7)—— 7 种 StreamMode 实时输出
- 预构建 Agent(LG8)★ —— create_react_agent 一行成 Agent
你现在从底层到应用层完整掌握了 LangGraph。下一步,用 OpenCode 看生产级产品如何把这些概念落地。
与生产实践对照
| LangGraph 概念 | OpenCode 对应 | |
|---|---|---|
| create_react_agent 整体 | → | build agent(默认主 agent,agent.ts:140) |
| agent↔tools 循环 | → | runLoop 的 while(true)(prompt.ts:1081) |
| ToolNode 工具执行 | → | SessionTools.resolve + tool.execute(tools.ts:39) |
| tools_condition 路由 | → | hasToolCalls 判断(llm/request.ts:216) |
| checkpointer 记忆 | → | Session DB 持久化 |
| interrupt_before HITL | → | Permission.ask(OC4) |
| pre_model_hook 裁剪 | → | compaction(上下文压缩,OC5) |
| prompt 系统提示 | → | system.ts 按模型选提示文件 |
小结
create_react_agent(model, tools)一行构建完整 ReAct Agent,内部是 agent↔tools 循环图。- 它的内部就是 LG1-LG7 的集大成:StateGraph + add_messages + 条件边 + tools_condition。
ToolNode执行工具,tools_condition判断"调工具 vs 完成"(Action/Finish 二态)。- 相比 AgentExecutor,它支持记忆、中断、流式、裁剪、可视化——全面胜出。
进入 第四阶段 · OpenCode 生产实践!看一个真实开源的 AI 编码产品如何把理论落地。第一站 OC1 · 整体架构:鸟瞰 OpenCode 的 Agent 循环数据流全景,从用户输入到 LLM 调用到工具执行到返回的完整链路。