第 9 篇我们徒手搭了 ReAct:agent 节点、tool 节点、条件回边,从零跑通了"推理 → 行动 → 观察"的循环。轮子怎么造已经心里有数,这一篇反过来看另一面——什么时候该直接用现成的轮子。会用轮子、会选轮子,跟会造轮子一样是工程能力。
本文是系列的速查篇:先把预构建(prebuilt)Agent 的三代入口理清,再把 create_agent 的关键参数逐个过手,最后给一份"什么情况下不要用"的判断清单。全文按"家族一览 → 参数全解 → 结构化输出 → 子代理 → 反面清单 → 对照手写版 → 总结"组织,代码基于 langchain 1.x 与 langgraph 1.2.x(2026-10 基线)。
1、prebuilt 家族一览
预构建 Agent 指官方把模型节点、工具节点、条件回边封装成现成的工厂函数:传进模型和工具,返回一张编译好的图,invoke 即用。到今天这条产品线上值得认识的有三代,一张表说清。
| 入口 | 所属包 | 状态 | 一句话定位 |
|---|---|---|---|
create_agent |
langchain.agents |
主推 | 1.x 统一入口,中间件体系 |
create_react_agent |
langgraph.prebuilt |
弃用中,仍可用 | 前身,前后钩子扩展 |
create_tool_calling_agent 与 AgentExecutor |
langchain-classic |
遗留 | 0.x 旧世界,仅迁移期 |
三代是一条演进线。LangChain 1.0(2025-10 发布)确立了 langchain.agents.create_agent 的正统地位:基于 LangGraph、自带中间件(middleware)体系,Agents 官方文档以它为核心展开,连 Deep Agents 这样的高层方案也是在它之上组装的。create_react_agent 是它的前身——第 2 篇跑第一个 Agent 时提过一句"已标记弃用",官方弃用说明的意思是"提供等价的 Agent 工厂与更灵活的中间件系统";目前它仍可正常导入运行,老代码不必连夜改。
至于 0.x 时代的 AgentExecutor 一族,1.0 里整体挪进了独立的 langchain-classic 包,再从 langchain.agents import 会直接 ImportError,只有维护老项目时才会装它。有意思的是,前两代并非推倒重写:create_agent 与 create_react_agent 用的都是第 9 篇手写的那套积木——模型节点、ToolNode、条件回边,差别只在封装层。所以上一篇的手写经验不会浪费,它就是这两个工厂函数的内部结构图。
选型一句话:新代码一律 create_agent;读懂 create_react_agent 是为了维护存量与读源码;langchain-classic 只出现在迁移场景。两份 API 参考:create_agent 与 create_react_agent。
2、create_agent 参数全解
create_agent 的完整签名有十几个参数,日常高频的是六个。先看调用骨架:
from langchain.agents import create_agent
agent = create_agent(
model, # 模型:字符串标识或实例
tools=[...], # 工具列表
system_prompt="...", # 系统提示词
middleware=(), # 中间件序列,扩展行为的统一入口
response_format=None, # 结构化输出 schema,见第 3 节
checkpointer=None, # 持久化器,多轮记忆的开关
)
解读:model 与 tools 是位置参数,其余全部关键字传参。返回值是编译好的 CompiledStateGraph,invoke、stream、ainvoke 一应俱全——它就是第 6 篇以来一直在用的那种图,只是节点与边都替你搭好了。签名里还有 state_schema、context_schema、store、interrupt_before 这类低频参数,本篇先聚焦六个高频项,需要时查 API 参考即可。下面按参数逐个过。
2.1、model 与 tools
model 接受两种形态:init_chat_model 返回的实例,或 "openai:gpt-5" 这样的字符串标识(自动初始化对应模型)。tools 是个宽容的参数,接受三种形态:@tool 装饰过的 BaseTool 实例、普通函数、以及 dict 形式的 OpenAI 工具定义。普通函数会被自动包装:
def add(a: int, b: int) -> int:
"""两数相加,返回整数和。"""
return a + b
agent = create_agent(model, tools=[add]) # 裸函数自动包成工具
自动包装的规则与 @tool 一致:函数名当工具名,类型注解生成参数 schema,docstring 当说明书,模型看不到函数体。区别只是少写一行装饰器;正式项目仍建议显式 @tool,便于挂配置与复用。
2.2、system_prompt
系统提示词在 create_agent 里叫 system_prompt——前身 API 里叫 prompt,这是两个工厂最直观的差异。传字符串即可:
agent = create_agent(
model,
tools=tools,
system_prompt="你是后端运维助手,回答前必须调用工具核实集群状态。",
)
字符串会被自动包成 SystemMessage 插到消息列表最前,每轮模型调用都带着。这里补一条边界:system_prompt 是静态的,想按会话状态动态生成提示词,正确姿势是中间件,第 6 节展开。
2.3、checkpointer
checkpointer 是多轮记忆的开关——同一个 thread_id 下的对话共享消息历史。最小可运行示例:
from langgraph.checkpoint.memory import InMemorySaver
agent = create_agent(
model,
tools=tools,
system_prompt="你是贴身助手。",
checkpointer=InMemorySaver(), # 内存版,重启即失
)
config = {"configurable": {"thread_id": "user-42"}}
agent.invoke({"messages": [{"role": "user", "content": "我叫张三"}]}, config)
r2 = agent.invoke({"messages": [{"role": "user", "content": "我叫什么?"}]}, config)
print(r2["messages"][-1].text) # 答得出"张三"
第二次提问的消息里没有任何上下文,答案来自 checkpoint 存下的第一轮历史。InMemorySaver 只适合开发调试,生产要换 SQLite 或 Postgres 版本——存储后端、时间旅行、会话管理是第 12 篇的主题,这里只记一件事:多轮记忆不是 Agent 自带的,是 checkpointer 给的。
3、结构化输出模式
第 4 篇讲过 with_structured_output,那是"单次调用吐 JSON";Agent 场景要的是"先干完工具的活,最后按 schema 交作业"。create_agent 用 response_format 做这件事:
from pydantic import BaseModel, Field
class ResearchReport(BaseModel):
"""调研报告的固定结构。"""
topic: str = Field(description="调研主题")
findings: list[str] = Field(description="关键发现,3 到 5 条")
confidence: float = Field(description="置信度,0 到 1", ge=0, le=1)
agent = create_agent(
model,
tools=[web_search],
system_prompt="先搜索核实,再给结论。",
response_format=ResearchReport,
)
result = agent.invoke(
{"messages": [{"role": "user", "content": "调研 LangGraph 1.2 的新特性"}]}
)
print(result["structured_response"].findings)
解读:工具循环照常运转,全部干完后模型再按 schema 交出结构化结果,落在 result["structured_response"]——那是校验过的 Pydantic 实例,字段不合规会抛验证错误,下游直接 .model_dump_json() 拿 JSON,不用再从自然语言里抠。注意 result["messages"] 仍照常返回完整对话,两条输出通道并存:自然语言给人看,结构化数据给程序用。
裸传 Pydantic 类时框架自动选策略;想显式控制就包一层,比如强制走工具调用模式:
from langchain.agents.structured_output import ToolStrategy
agent = create_agent(
model,
tools=[web_search],
response_format=ToolStrategy(
ResearchReport,
tool_message_content="报告已收到。",
),
)
ToolStrategy 把 schema 伪装成一个特殊工具,模型调用它即视为提交结果,任何支持工具调用的模型都能用,兼容性最好;ProviderStrategy 则走供应商原生的结构化输出通道,要求模型支持 with_structured_output。ToolStrategy 还带了两个实用开关:tool_message_content 定制提交成功后回给模型的提示语(示例里那句"报告已收到"),handle_errors 控制校验失败时是抛异常还是回喂错误让模型重试。细节见结构化输出文档。顺带一提,create_react_agent 也有 response_format,但实现是循环结束后单独再发一次模型调用,语义有细微差异——又一枚"新代码别用它"的砝码。
4、子代理模式
第 2.1 节说过 tools 里能塞函数;它还能塞另一个 Agent。为什么这么设计?直接给主编十几个工具不行吗?实践中工具一多,选择质量就往下掉——十几个 schema 全塞进每次模型调用,模型开始选错工具、漏看参数。子代理把一组工具收拢成一项职责,主编眼里只剩一个 research,决策面瞬间干净。这就是官方所称的 agent as tool 模式,也是多 Agent 架构的种子:
search_agent = create_agent(
model,
tools=[web_search],
system_prompt="你是检索专员,只负责搜索并汇总事实。",
)
@tool
def research(topic: str) -> str:
"""深入调研一个主题,返回带来源的事实汇总。"""
result = search_agent.invoke(
{"messages": [{"role": "user", "content": topic}]}
)
return result["messages"][-1].text
chief = create_agent(
model,
tools=[research],
system_prompt="你是主编,调研类任务一律派给 research。",
)
解读三个要点。其一,包装函数的 docstring 就是主编眼里的工具说明书,它据此决定何时派活——写清楚比什么都重要。其二,只回传最终回答:子代理的中间推理与工具调用对主编是黑盒,既省 token 也省注意力。其三,子代理默认不挂 checkpointer,每次调用独立无状态,需要审批或记忆时在子代理自己的 create_agent 里配中间件与持久化。
提醒:子代理必须在最终回答里写全关键事实——中间过程不会透传,漏了就丢在包装层,主编永远看不到。
完整的多 Agent 编排(supervisor、handoff、层级团队)是第 24 篇的主题,官方教程用子代理搭个人助理可以先跑为敬。
5、何时不该用 prebuilt
速查篇讲"怎么用",工程师更该会问"什么时候不用"。四条判断,命中任意一条就考虑手写 StateGraph:
- 标准循环装不下固定流程:先规划、再执行、再评审的管线
- 中断逻辑长在循环中段:工具执行中途
interrupt,恢复时注入状态 - 性能敏感要动 State:默认全量存消息、全量重发,需逐轮裁剪
- 图结构要并联分支:prebuilt 是串行循环,分支汇合是 StateGraph 主场
第一条展开说:多阶段流程硬塞进单循环,要靠提示词模拟阶段切换,模型一旦"忘了"自己在哪个阶段就全乱;直接搭图则是每个阶段一个节点,结构即约束。第三条同理——prebuilt 的默认 State 存全量消息,每轮模型调用都全量重发,token 敏感的场景必须自定义 State 精打细算。
都没命中就别手写。判断口诀一句话:改循环的"结构"才手写图,改循环的"行为"用中间件。第 9 篇的手写循环从来不是为了替代 prebuilt,而是为了在需要下沉的那天,你知道下面是什么。
6、与手写版对照
把第 9 篇的手写版和 create_agent 摆在一起,看清官方封装到底多送了什么:
| 能力 | 第 9 篇手写 | create_agent |
|---|---|---|
| 工具报错 | 异常冒泡,循环直接崩 | 默认包成错误 ToolMessage 回喂模型修复 |
| 流式输出 | 自己改造节点 | 开箱支持 stream_mode="messages" |
| 行为扩展 | 动手改图 | 中间件:压历史、人工审批、工具重试 |
| 失控保护 | 自己记得设上限 | 默认 recursion_limit=25 兜底 |
| 结构化输出 | 自己加节点 | response_format 一个参数 |
第二行值得展开。手写版做流式要在节点里自己处理 token 增量与工具调用消息的交织,稍不留神就把流式退化成整段返回;prebuilt 直接支持 stream_mode="messages",token 级增量开箱即有,接前端的姿势与第 13 篇讲的通用流式完全一致。
最能拉开差距的是中间件。手写版想加"历史过长自动摘要"要动图;create_agent 传一个 SummarizationMiddleware 就完事,危险工具加 HumanInTheLoopMiddleware 拦截审批——第 11 篇的人机协同会正面用到它。模型调用前后的钩子(before_model 等)覆盖了前身 pre_model_hook / post_model_hook 的全部能力,目录见中间件文档。
手写与 prebuilt 不是对立选项,而是同一套积木的不同封装层级——出问题时,第 9 篇的经验决定你能不能读懂 trace、判断要不要下沉。
7、总结
这篇速查把 prebuilt 的使用边界画圆了:三代入口里新代码只认 create_agent;model、tools、system_prompt、checkpointer、response_format 五个参数,加上一套按需挂载的中间件,覆盖九成场景;Agent 包 Agent 是多 Agent 的起点;四条判断命中才值得手写图。与第 9 篇合起来,你同时握着造轮子和用轮子两种能力。
留一个练习:用 create_agent 加 response_format 做"调研并出报告"——定义带 topic、findings、sources、confidence 四个字段的 Pydantic 模型,挂上搜索与时间两个工具,让 Agent 调研一个你关心的技术主题,最后把 result["structured_response"].model_dump_json() 写入文件。跑通后做加分项:把调研 Agent 包成工具交给一个主编 Agent 调度,提前摸一下第 24 篇的门。
对于日常业务,建议姿势是固定的:从 create_agent 起步,撞到结构天花板再下沉到手写图。下一篇讲人机协同,让 Agent 在关键动作前停下来等你点头。


