概述
前面几篇我们已经分别讲了:
- 模型如何初始化。
- Prompt 如何工程化。
- LCEL 如何组织调用流程。
- RAG 如何接外部知识。
- Memory 如何保留会话状态。
- Tool 如何连接外部动作。
- 结构化输出如何返回可验证数据。
到了这里,你会发现一个自然的问题:
这些能力最后怎么组合成一个真正能干活的 Agent?
LangChain 当前的答案是 create_agent()。
它不是"再包一层"的语法糖那么简单。
它更像一个 harness,把模型、工具、系统提示词、状态、上下文、中间件和执行循环组装起来。
可以把 Agent 理解成:
text
Agent = Model + Harness
其中 harness 负责的事情是:
- 在合适的时机给模型合适的上下文。
- 让模型在需要时调用工具。
- 把工具结果回传给模型继续推理。
- 持久化会话状态。
- 在必要时做重试、摘要、人工审批、限流、guardrail。
create_agent() 的价值,不是让你少写一行代码,而是把 Agent 的整套运行机制标准化。
先看最小例子:一行创建一个 Agent
最小版本可以长这样:
python
from langchain.agents import create_agent
agent = create_agent(
"openai:gpt-4o-mini",
tools=[],
)
这已经是一个 Agent 了。
如果只看这一行,你可能觉得没什么。
但实际上,这一行已经隐含了一个完整的执行框架:
- 模型调用。
- 工具选择。
- 状态管理。
- 运行时配置。
- 逐步执行。
如果再加上工具和系统提示词:
python
from langchain.agents import create_agent
from langchain.tools import tool
@tool
def get_weather(city: str) -> str:
"""查询指定城市的实时天气。用户问天气、温度、降雨、出行建议时使用。"""
return f"{city} 今天晴,26 摄氏度。"
agent = create_agent(
"openai:gpt-4o-mini",
tools=[get_weather],
system_prompt="你是一个出行助手。用户问天气时必须调用天气工具。",
)
这个 Agent 就能根据用户问题决定是否调用 get_weather。
最小 Agent = 模型 + 工具 + 系统提示词。
Agent 的核心循环:思考、工具调用、观察、再思考
Agent 和普通模型调用最大的区别,是它不是只执行一次。
它会在一个循环里不断推进,直到任务完成。
一个简化循环如下:
#mermaid-svg-tCAyffgVIF3nyQiF{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-tCAyffgVIF3nyQiF .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-tCAyffgVIF3nyQiF .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-tCAyffgVIF3nyQiF .error-icon{fill:#552222;}#mermaid-svg-tCAyffgVIF3nyQiF .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-tCAyffgVIF3nyQiF .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-tCAyffgVIF3nyQiF .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-tCAyffgVIF3nyQiF .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-tCAyffgVIF3nyQiF .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-tCAyffgVIF3nyQiF .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-tCAyffgVIF3nyQiF .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-tCAyffgVIF3nyQiF .marker{fill:#333333;stroke:#333333;}#mermaid-svg-tCAyffgVIF3nyQiF .marker.cross{stroke:#333333;}#mermaid-svg-tCAyffgVIF3nyQiF svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-tCAyffgVIF3nyQiF p{margin:0;}#mermaid-svg-tCAyffgVIF3nyQiF .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-tCAyffgVIF3nyQiF .cluster-label text{fill:#333;}#mermaid-svg-tCAyffgVIF3nyQiF .cluster-label span{color:#333;}#mermaid-svg-tCAyffgVIF3nyQiF .cluster-label span p{background-color:transparent;}#mermaid-svg-tCAyffgVIF3nyQiF .label text,#mermaid-svg-tCAyffgVIF3nyQiF span{fill:#333;color:#333;}#mermaid-svg-tCAyffgVIF3nyQiF .node rect,#mermaid-svg-tCAyffgVIF3nyQiF .node circle,#mermaid-svg-tCAyffgVIF3nyQiF .node ellipse,#mermaid-svg-tCAyffgVIF3nyQiF .node polygon,#mermaid-svg-tCAyffgVIF3nyQiF .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-tCAyffgVIF3nyQiF .rough-node .label text,#mermaid-svg-tCAyffgVIF3nyQiF .node .label text,#mermaid-svg-tCAyffgVIF3nyQiF .image-shape .label,#mermaid-svg-tCAyffgVIF3nyQiF .icon-shape .label{text-anchor:middle;}#mermaid-svg-tCAyffgVIF3nyQiF .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-tCAyffgVIF3nyQiF .rough-node .label,#mermaid-svg-tCAyffgVIF3nyQiF .node .label,#mermaid-svg-tCAyffgVIF3nyQiF .image-shape .label,#mermaid-svg-tCAyffgVIF3nyQiF .icon-shape .label{text-align:center;}#mermaid-svg-tCAyffgVIF3nyQiF .node.clickable{cursor:pointer;}#mermaid-svg-tCAyffgVIF3nyQiF .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-tCAyffgVIF3nyQiF .arrowheadPath{fill:#333333;}#mermaid-svg-tCAyffgVIF3nyQiF .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-tCAyffgVIF3nyQiF .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-tCAyffgVIF3nyQiF .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tCAyffgVIF3nyQiF .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-tCAyffgVIF3nyQiF .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tCAyffgVIF3nyQiF .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-tCAyffgVIF3nyQiF .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-tCAyffgVIF3nyQiF .cluster text{fill:#333;}#mermaid-svg-tCAyffgVIF3nyQiF .cluster span{color:#333;}#mermaid-svg-tCAyffgVIF3nyQiF div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-tCAyffgVIF3nyQiF .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-tCAyffgVIF3nyQiF rect.text{fill:none;stroke-width:0;}#mermaid-svg-tCAyffgVIF3nyQiF .icon-shape,#mermaid-svg-tCAyffgVIF3nyQiF .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tCAyffgVIF3nyQiF .icon-shape p,#mermaid-svg-tCAyffgVIF3nyQiF .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-tCAyffgVIF3nyQiF .icon-shape .label rect,#mermaid-svg-tCAyffgVIF3nyQiF .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tCAyffgVIF3nyQiF .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-tCAyffgVIF3nyQiF .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-tCAyffgVIF3nyQiF :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否
是
用户输入
模型
需要工具?
直接回答
生成 tool call
执行工具
把结果写回 messages
可以把这个循环理解成:
- 模型先"想一想"下一步。
- 如果需要外部信息,就调用工具。
- 系统执行工具。
- 工具结果回到上下文。
- 模型根据新信息继续想,直到不再需要工具。
这也是官方文档对 Agent 的基本定义。
Agent 不是一次生成,而是"模型 + 工具"的循环执行。
create_agent() 到底接收哪些核心参数?
最常用的参数其实就几个。
| 参数 | 作用 | 什么时候用 |
|---|---|---|
model |
指定模型 | 必须 |
tools |
指定可调用工具 | 大多数 Agent 都需要 |
system_prompt |
定义 Agent 角色和任务边界 | 基本都会用 |
response_format |
定义结构化输出 schema | 需要稳定返回数据时用 |
checkpointer |
持久化会话状态 | 需要多轮记忆时用 |
middleware |
定制模型/工具调用行为 | 需要摘要、审批、重试等能力时用 |
name |
给 Agent 命名 | 作为子图嵌入多 Agent 系统时很有用 |
state_schema |
扩展状态字段 | 需要额外 state 时用 |
context_schema |
定义每次调用的上下文 | 需要把 user_id、feature flag 等传给工具和 middleware 时用 |
你可以把这些参数分成三层:
- 核心层:model、tools、system_prompt
- 输出层:response_format、name
- 运行层:checkpointer、state_schema、context_schema、middleware
create_agent() 的参数不是乱的,它们分别对应 Agent 的核心能力、输出约束和运行时治理。
model:可以是字符串,也可以是模型实例
官方文档支持两种写法:
写法一:直接传模型标识字符串
python
agent = create_agent(
"openai:gpt-4o-mini",
tools=[],
)
写法二:传已经初始化好的模型实例
python
from langchain.chat_models import init_chat_model
from langchain.agents import create_agent
model = init_chat_model(
"gpt-4o-mini",
model_provider="openai",
temperature=0,
)
agent = create_agent(
model,
tools=[],
)
两种方式都可以。
如果你只是快速原型,字符串形式更短。
如果你已经对模型做了额外配置,比如 temperature、timeout、max_retries、bind,传实例会更清晰。
model 既可以是 "provider:model" 字符串,也可以是已经配置好的模型对象。
tools:模型能用哪些外部能力
工具可以是:
- 普通 Python 可调用对象。
@tool装饰器定义的工具。- 工具字典。
最简单的例子:
python
from langchain.tools import tool
@tool
def search_faq(query: str) -> str:
"""搜索常见问题知识库。用户问规则、流程、说明时使用。"""
faq = {
"退款": "退款需要在 7 天内申请,并保留订单号。",
"发货": "订单通常在 24 小时内发出。",
}
return faq.get(query, "没有找到相关 FAQ。")
然后交给 Agent:
python
agent = create_agent(
"openai:gpt-4o-mini",
tools=[search_faq],
)
模型会根据问题判断是否要调用 search_faq。
如果工具很多,模型的选择空间也会变大。
这不一定是好事。
工具越多,描述冲突、选择错误、调试困难的概率越高。
所以不要把几十个工具无脑塞给一个 Agent。
tools 决定 Agent 能做什么,但工具越多不代表 Agent 越强。
system_prompt:定义 Agent 的角色、边界和风格
system_prompt 是 Agent 的第一层约束。
它可以是字符串,也可以是 SystemMessage。
python
agent = create_agent(
"openai:gpt-4o-mini",
tools=[search_faq],
system_prompt="你是一个严谨的客服助手。回答要准确、简洁,不能编造信息。",
)
这个提示词通常应该写清楚:
- Agent 是什么角色。
- 优先遵循什么规则。
- 哪些事情必须调用工具。
- 哪些事情不能编造。
- 什么时候转人工。
- 输出风格是什么。
例如:
python
system_prompt = (
"你是一个企业客服助手。"
"用户询问订单、退款、发货、售后时,优先调用工具。"
"如果工具没有答案,明确说明不知道,不要编造。"
"回答要简洁、礼貌、准确。"
)
system_prompt 解决的是"Agent 应该怎么思考和说话"。
但如果你要动态变换提示词,例如根据用户等级、租户、语言切换,就应该考虑 middleware,而不是把所有动态逻辑都塞进静态 system prompt。
system_prompt 负责稳定角色和边界,动态变化的部分更适合放到 middleware 或 context。
response_format:让 Agent 最终返回结构化结果
如果你希望 Agent 最后不是只返回自然语言,而是返回可以直接进入程序逻辑的对象,就用 response_format。
例如:
python
from pydantic import BaseModel, Field
class Answer(BaseModel):
summary: str = Field(description="Short answer")
confidence: float = Field(description="Confidence from 0 to 1", ge=0, le=1)
Agent:
python
agent = create_agent(
"openai:gpt-4o-mini",
tools=[],
response_format=Answer,
)
result = agent.invoke({
"messages": [
{"role": "user", "content": "Summarize why RAG is useful"}
]
})
print(result["structured_response"])
返回值会包含:
python
result["structured_response"]
而不是让你从最后一条消息里手动扒 JSON。
这一点和第 09 篇的结构化输出是一致的。
如果 Agent 需要先调用工具,再输出结构化结果,这也可以统一在 response_format 里处理。
response_format 让 Agent 的最终输出可以直接进入业务代码。
name:当 Agent 作为子图时尤其有用
name 是一个容易被忽略,但在多 Agent 里很有用的参数。
python
agent = create_agent(
"openai:gpt-4o-mini",
tools=[search_faq],
name="customer_support_agent",
)
它的作用不是给用户看标题,而是给系统看名字。
当 Agent 被嵌入到更大的图或多 Agent 系统里时,name 可以帮助:
- 识别子 Agent。
- 调试日志。
- 追踪调用路径。
- 做多 Agent 协作。
这部分在第 13 篇会更详细。
name 主要是给系统和图结构看的,不是给终端用户看的。
checkpointer:让 Agent 记住 thread
如果你想让 Agent 记住上一轮对话,就需要 checkpointer。
本地开发最简单的是 InMemorySaver。
python
from langgraph.checkpoint.memory import InMemorySaver
agent = create_agent(
"openai:gpt-4o-mini",
tools=[search_faq],
checkpointer=InMemorySaver(),
)
然后调用时传 thread_id:
python
config = {"configurable": {"thread_id": "support-001"}}
result = agent.invoke(
{"messages": [{"role": "user", "content": "我的名字是 Bob。"}]},
config=config,
)
下一轮仍然传同一个 thread_id:
python
result = agent.invoke(
{"messages": [{"role": "user", "content": "我叫什么名字?"}]},
config=config,
)
如果 checkpointer 存在,Agent 就能沿着同一个线程继续。
如果没有 checkpointer,thread_id 就没有持久化意义。
checkpointer + thread_id 是 Agent 会话记忆的核心组合。
context_schema:把每次运行时的数据传给 Agent
有些数据不是会话历史,而是每次调用都可能变化的运行时上下文。
例如:
user_idlocaletenant_idfeature_flagsapi_keyenvironment
这些数据不应该硬塞进 messages。
更好的方式是定义 context_schema。
python
from dataclasses import dataclass
@dataclass
class SupportContext:
user_id: str
locale: str = "zh-CN"
Agent:
python
agent = create_agent(
"openai:gpt-4o-mini",
tools=[search_faq],
context_schema=SupportContext,
)
调用时传 context:
python
result = agent.invoke(
{"messages": [{"role": "user", "content": "我有哪些订单?"}]},
config={"configurable": {"thread_id": "support-001"}},
context=SupportContext(user_id="user_123", locale="zh-CN"),
)
工具和 middleware 可以通过运行时上下文读取这些值。
这比让模型从用户自然语言里猜 user_id 安全得多。
context 是运行时参数,不是聊天历史。
state_schema:当你要扩展 AgentState 时
如果默认状态不够用,可以扩展 state_schema。
这适合在状态里保存额外字段,例如:
- 当前任务阶段。
- 用户偏好。
- 评分结果。
- 中间摘要。
- 自定义标记。
示例:
python
from langchain.agents import AgentState
from typing_extensions import NotRequired
class SupportState(AgentState):
user_tier: NotRequired[str]
current_ticket_id: NotRequired[str]
然后:
python
agent = create_agent(
"openai:gpt-4o-mini",
tools=[search_faq],
state_schema=SupportState,
)
这和第 07 篇的记忆系统是连在一起的。
state_schema 解决的是"Agent 当前状态还能带什么额外信息"。
state_schema 是给 Agent 状态加字段,不是给用户加消息。
middleware:把 Agent 变成可治理的 harness
如果说 system_prompt 负责内容,middleware 负责行为治理。
LangChain 官方把 middleware 看成 Agent 的控制层。
常见用途包括:
- 动态 prompt。
- 动态模型选择。
- 动态工具选择。
- 重试。
- fallback。
- 限流。
- PII 检测。
- 人工审批。
- 摘要压缩。
- 早停。
最简单的用法:
python
from langchain.agents.middleware import SummarizationMiddleware
agent = create_agent(
"openai:gpt-4o-mini",
tools=[search_faq],
middleware=[
SummarizationMiddleware(model="openai:gpt-4o-mini"),
],
)
更明确地说,middleware 插在 Agent loop 的关键位置上:
before_agentbefore_modelafter_modelafter_agentwrap_model_callwrap_tool_call
这部分第 11 篇会深讲。
这里先记住一句话:
create_agent()不是固定死的 Agent,它是一个可以通过 middleware 持续扩展的 harness。
thread_id 和 context 的区别
这两个概念很容易混。
| 字段 | 作用 | 是否持久化 |
|---|---|---|
thread_id |
识别同一段对话线程 | 是 |
context |
每次调用携带的运行时数据 | 否 |
例如:
python
config = {"configurable": {"thread_id": "support-001"}}
context = SupportContext(user_id="user_123", locale="zh-CN")
thread_id 让 Agent 记住这段对话。
context 告诉 Agent 这次调用的用户是谁、环境是什么、feature flag 是什么。
一个是"会话历史",一个是"运行时输入"。
不要把它们混用。
thread_id 管历史,context 管当前运行环境。
Streaming:Agent 也可以边跑边看
如果 Agent 需要多步工具调用,用户可能不想等结果全部完成才看到反馈。
可以流式读取:
python
from langchain.agents import create_agent
agent = create_agent(
"openai:gpt-4o-mini",
tools=[search_faq],
)
for chunk in agent.stream(
{"messages": [{"role": "user", "content": "搜索 AI 新闻并总结"}]},
stream_mode="values",
):
latest_message = chunk["messages"][-1]
if latest_message.content:
print(latest_message.content)
stream_mode="values" 的意思是每个 chunk 都包含当前 state 的完整快照。
你可以从里面取最后一条消息,判断:
- 是用户消息。
- 是模型消息。
- 还是工具调用消息。
如果 Agent 要执行多步动作,流式输出对用户体验很重要。
invoke 拿最终结果,stream 看 Agent 一步步怎么跑。
一个完整的 Agent 执行图
可以把 create_agent() 返回的 Agent 想成这样:
text
输入 messages
|
v
模型调用
|
+-- 若需要工具 -> 工具执行 -> messages 更新
|
+-- 若不需要工具 -> 直接结束
|
v
结构化输出 / 最终回答
如果再加上状态和中间件:
#mermaid-svg-ByQHLiEw8BxeJVsk{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ByQHLiEw8BxeJVsk .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ByQHLiEw8BxeJVsk .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ByQHLiEw8BxeJVsk .error-icon{fill:#552222;}#mermaid-svg-ByQHLiEw8BxeJVsk .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ByQHLiEw8BxeJVsk .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ByQHLiEw8BxeJVsk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ByQHLiEw8BxeJVsk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ByQHLiEw8BxeJVsk .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ByQHLiEw8BxeJVsk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ByQHLiEw8BxeJVsk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ByQHLiEw8BxeJVsk .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ByQHLiEw8BxeJVsk .marker.cross{stroke:#333333;}#mermaid-svg-ByQHLiEw8BxeJVsk svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ByQHLiEw8BxeJVsk p{margin:0;}#mermaid-svg-ByQHLiEw8BxeJVsk .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ByQHLiEw8BxeJVsk .cluster-label text{fill:#333;}#mermaid-svg-ByQHLiEw8BxeJVsk .cluster-label span{color:#333;}#mermaid-svg-ByQHLiEw8BxeJVsk .cluster-label span p{background-color:transparent;}#mermaid-svg-ByQHLiEw8BxeJVsk .label text,#mermaid-svg-ByQHLiEw8BxeJVsk span{fill:#333;color:#333;}#mermaid-svg-ByQHLiEw8BxeJVsk .node rect,#mermaid-svg-ByQHLiEw8BxeJVsk .node circle,#mermaid-svg-ByQHLiEw8BxeJVsk .node ellipse,#mermaid-svg-ByQHLiEw8BxeJVsk .node polygon,#mermaid-svg-ByQHLiEw8BxeJVsk .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ByQHLiEw8BxeJVsk .rough-node .label text,#mermaid-svg-ByQHLiEw8BxeJVsk .node .label text,#mermaid-svg-ByQHLiEw8BxeJVsk .image-shape .label,#mermaid-svg-ByQHLiEw8BxeJVsk .icon-shape .label{text-anchor:middle;}#mermaid-svg-ByQHLiEw8BxeJVsk .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ByQHLiEw8BxeJVsk .rough-node .label,#mermaid-svg-ByQHLiEw8BxeJVsk .node .label,#mermaid-svg-ByQHLiEw8BxeJVsk .image-shape .label,#mermaid-svg-ByQHLiEw8BxeJVsk .icon-shape .label{text-align:center;}#mermaid-svg-ByQHLiEw8BxeJVsk .node.clickable{cursor:pointer;}#mermaid-svg-ByQHLiEw8BxeJVsk .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ByQHLiEw8BxeJVsk .arrowheadPath{fill:#333333;}#mermaid-svg-ByQHLiEw8BxeJVsk .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ByQHLiEw8BxeJVsk .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ByQHLiEw8BxeJVsk .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ByQHLiEw8BxeJVsk .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ByQHLiEw8BxeJVsk .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ByQHLiEw8BxeJVsk .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ByQHLiEw8BxeJVsk .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ByQHLiEw8BxeJVsk .cluster text{fill:#333;}#mermaid-svg-ByQHLiEw8BxeJVsk .cluster span{color:#333;}#mermaid-svg-ByQHLiEw8BxeJVsk div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ByQHLiEw8BxeJVsk .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ByQHLiEw8BxeJVsk rect.text{fill:none;stroke-width:0;}#mermaid-svg-ByQHLiEw8BxeJVsk .icon-shape,#mermaid-svg-ByQHLiEw8BxeJVsk .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ByQHLiEw8BxeJVsk .icon-shape p,#mermaid-svg-ByQHLiEw8BxeJVsk .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ByQHLiEw8BxeJVsk .icon-shape .label rect,#mermaid-svg-ByQHLiEw8BxeJVsk .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ByQHLiEw8BxeJVsk .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ByQHLiEw8BxeJVsk .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ByQHLiEw8BxeJVsk :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否
是
输入 messages + config + context
before_agent
before_model
模型调用
是否有 tool_calls?
after_model
wrap_tool_call / tools
after_agent
最终 state / structured_response
这个图可以帮你理解:
- Agent 不是单个模型。
- Agent 是一套可配置执行流。
- middleware 可以在关键节点修改行为。
- checkpointer 让这个流可恢复。
create_agent() 为什么比手写循环更稳?
你当然可以自己写 Agent 循环:
python
while True:
response = model.invoke(messages)
if not response.tool_calls:
break
tool_result = run_tool(response.tool_calls[0])
messages.append(tool_result)
这没错,但一旦你开始加这些东西,复杂度就上来了:
- 多工具。
- 多个 tool_calls。
- 状态持久化。
- 流式输出。
- 结构化输出。
- 中间件。
- 重试。
- Fallback。
- 人工审批。
- 上下文管理。
create_agent() 的价值,就是把这些通用问题收敛成一个稳定 harness。
你不用每个项目都自己重写一套 Agent 框架。
create_agent() 让 Agent 的样板逻辑标准化,让你把精力集中在业务能力上。
一个实际场景:客服 Agent
下面给一个更完整的例子。
定义工具
python
from langchain.tools import tool
@tool
def query_order_status(order_id: str) -> str:
"""根据订单号查询订单状态。用户问订单、物流、发货、送达时间时使用。"""
orders = {
"A1001": "订单 A1001 已发货,物流单号 SF123456,预计明天送达。",
"A1002": "订单 A1002 正在仓库拣货,预计今天 18:00 前发出。",
}
return orders.get(order_id, f"没有查到订单 {order_id}。")
@tool
def search_faq(query: str) -> str:
"""搜索客服 FAQ。用户问退款、发货、售后、流程规则时使用。"""
faq = {
"退款": "退款需要在 7 天内申请,并保留订单号。",
"发货": "订单通常在 24 小时内发出。",
"售后": "售后问题请先提供订单号和问题描述。",
}
return faq.get(query, "没有找到相关 FAQ。")
定义结构化输出
python
from pydantic import BaseModel, Field
class SupportReply(BaseModel):
summary: str = Field(description="Concise answer")
need_human: bool = Field(description="Whether to transfer to human support")
confidence: float = Field(description="Confidence from 0 to 1", ge=0, le=1)
创建 Agent
python
from dataclasses import dataclass
from langchain.agents import create_agent
from langgraph.checkpoint.memory import InMemorySaver
@dataclass
class SupportContext:
user_id: str
locale: str = "zh-CN"
agent = create_agent(
model="openai:gpt-4o-mini",
tools=[query_order_status, search_faq],
system_prompt=(
"你是一个企业客服助手。"
"用户询问订单、发货、退款、售后时,优先调用工具。"
"如果工具没有答案,不要编造。"
"如果涉及高风险操作,提示人工客服介入。"
),
response_format=SupportReply,
checkpointer=InMemorySaver(),
context_schema=SupportContext,
name="support_agent",
)
调用 Agent
python
config = {"configurable": {"thread_id": "support-001"}}
context = SupportContext(user_id="user_123", locale="zh-CN")
result = agent.invoke(
{"messages": [{"role": "user", "content": "帮我查一下 A1001 到哪了?"}]},
config=config,
context=context,
)
print(result["structured_response"])
这个例子同时展示了:
tools:订单查询和 FAQ 检索。system_prompt:定义客服边界。response_format:最后输出结构化回复。checkpointer+thread_id:保留会话。context_schema+context:传入运行时用户信息。name:便于后续作为子图使用。
这就是 create_agent() 最实际的一面。
常见错误一:没加 checkpointer 就想要多轮记忆
错误:
python
agent = create_agent("openai:gpt-4o-mini", tools=[search_faq])
然后:
python
config = {"configurable": {"thread_id": "support-001"}}
你以为会记住上下文,但没有 checkpointer,状态不会持久化。
正确做法:
python
agent = create_agent(
"openai:gpt-4o-mini",
tools=[search_faq],
checkpointer=InMemorySaver(),
)
常见错误二:把所有业务字段塞进 messages
错误:
python
{"messages": [{"role": "user", "content": "user_id=user_123, locale=zh-CN, 帮我查订单"}]}
这样会把运行时上下文和用户输入混在一起。
更好的方式是:
python
context = SupportContext(user_id="user_123", locale="zh-CN")
消息只放用户真正说的话。
常见错误三:工具太多,Agent 不知道选哪个
工具太多时,模型会在相似工具之间犹豫。
如果你有:
query_order_statusquery_order_detailquery_order_historysearch_faqsearch_docssearch_policy
模型可能会选错。
更好的做法:
- 合并相近工具。
- 提高描述质量。
- 按场景拆 Agent。
- 复杂场景让上层路由,而不是让单个 Agent 盲猜。
常见错误四:把高风险动作直接交给 Agent
不要让 Agent 直接执行:
- 删除数据。
- 发邮件。
- 改权限。
- 转账。
- 生产配置变更。
这类动作要加:
- 人工审批。
- 工单确认。
- 二次校验。
- 白名单。
- 审计日志。
Agent 能建议动作,但不能替代权限系统。
常见错误五:忽略输出结构
如果业务需要后续代码处理,就不要只看最后一句自然语言。
用:
python
result["structured_response"]
而不是:
python
result["messages"][-1].content
这是很多人把 Agent 当聊天框用时最容易忽略的问题。
总结
create_agent() 的本质是什么?如果只记住一句话:
create_agent()是 LangChain 提供的高层 Agent harness,用来把模型、工具、提示词、状态、上下文、中间件和结构化输出组合成一个可运行的 Agent。
再具体一点:
model决定 Agent 用什么模型。tools决定 Agent 能做什么。system_prompt决定 Agent 的角色和边界。response_format决定最终输出是否结构化。checkpointer+thread_id决定多轮状态能否持久化。context_schema+context决定运行时数据如何注入。state_schema决定状态还能扩展哪些字段。middleware决定 Agent 如何被控制、增强和治理。name决定这个 Agent 在更大图里如何被识别。
如果你把这几个参数想清楚,create_agent() 就不再是黑盒。