🏗️ 预构建 Agent

LangGraph 系统的收官章!前 7 章你学了所有底层机制。本章用 create_react_agent + ToolNode——一行代码组装完整 ReAct Agent。这是 LG1-LG7 所有知识的集大成,也是日常最常用的 API。

本章目标

  • create_react_agent 快速构建工具调用 Agent
  • 理解它内部如何用 LG1-LG7 的知识组装图(agent↔tools 循环)
  • 掌握 ToolNodetools_condition 的配合
  • 能配置 checkpointer、interrupt、hooks 增强预构建 Agent
  • 对比它和 LC8 的 AgentExecutor,看 LangGraph 的优势如何体现

create_react_agent:一行成 Agent

这是最常用的预构建 Agent。它内部自动构建了 LG3 讲的 agent↔tools 循环图,你只需提供模型和工具:

📄 langgraph/prebuilt/chat_agent_executor.py:278 · create_react_agent python
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 的集大成:

📄 chat_agent_executor.py:57 · 内部状态 + 图结构(还原) python
# 内部状态:就是 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 负责路由判断:

📄 langgraph/prebuilt/tool_node.py:622 / 1582 · ToolNode + tools_condition python
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 → 结束
START agent model.invoke 有tc? tools (ToolNode) 执行工具 END 否→END 工具结果回流 → 循环
图 LG8.1 · create_react_agent 的内部图:agent ↔ tools 循环(tools_condition 路由)

可运行代码

🚀 一行构建完整 Agent

需要 OpenAI API Key。pip install langgraph langchain-openai。对比 LC8 的 AgentExecutor,看多简洁。

📄 lg8_react_agent.py · create_react_agent 完整实战 python
# 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 实现记忆

📄 加上记忆(LG6) python
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 实现人在回路

📄 工具执行前需人工批准(LG6) python
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 前裁剪消息:

📄 pre_model_hook 裁剪历史(F2 的记忆管理) python
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_agent

源码标注 create_react_agent 已废弃,推荐 langchain.agents.create_agent——它用 middleware 系统(更灵活的钩子链)。但底层仍是 LangGraph 图。学完本章你已具备理解任何 Agent 工厂的能力。

LangGraph 阶段总结

🎉 第三阶段完成!

恭喜系统学完 LangGraph 8 章。回顾这条进阶之路:

  1. StateGraph(LG1)★ —— 节点+边+状态,告别隐式循环
  2. Reducer(LG2)—— Annotated + add_messages,状态正确累积
  3. 条件边+Send(LG3)—— 动态路由 + map-reduce 并行
  4. Functional API(LG4)—— @entrypoint/@task 命令式写法
  5. Pregel 引擎(LG5)★ —— BSP 三阶段 + Channel,看透底层
  6. 检查点+HITL(LG6)—— interrupt + Command,人在回路
  7. 流式(LG7)—— 7 种 StreamMode 实时输出
  8. 预构建 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 HITLPermission.ask(OC4)
pre_model_hook 裁剪compaction(上下文压缩,OC5)
prompt 系统提示system.ts 按模型选提示文件

小结

下一阶段 · OpenCode 生产实践

进入 第四阶段 · OpenCode 生产实践!看一个真实开源的 AI 编码产品如何把理论落地。第一站 OC1 · 整体架构:鸟瞰 OpenCode 的 Agent 循环数据流全景,从用户输入到 LLM 调用到工具执行到返回的完整链路。