让你的 Agent 不止于"聊天",还能精准提取数据、实时反馈进度
LangChain 的 create_agent 让我们能轻松构建具备工具调用能力的智能体。但很多同学只停留在"一问一答"的层面,对于 Agent 的身份标识、结构化输出、流式控制 这些高级特性却了解甚少。
本文将带你深入 Agent 的三大高级用法:
- 名称与系统提示词 ------ 多智能体场景的身份标记
- 结构化输出(
response_format) ------ 让 Agent 返回类型安全的数据 - 流式输出(
stream_mode) ------ 实现打字机效果与进度监控
每个部分都会配合完整代码示例,并揭示底层原理,助你写出更健壮、更可控的 Agent 应用。
环境准备
我们仍使用 OpenRouter 托管的 DeepSeek 模型,你可以替换成任意支持 create_agent 的模型。
python
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from dotenv import load_dotenv
import os
load_dotenv(override=True)
model = init_chat_model(
model="deepseek-v4-flash",
model_provider="openai",
api_key=os.getenv("OPENROUTER_API_KEY"),
base_url=os.getenv("OPENROUTER_BASE_URL")
)
1. 命名你的 Agent:多智能体场景的身份标识
当系统中有多个 Agent 协作时,给每个 Agent 起个名字能极大提升日志可读性和调试效率。
create_agent 的 name 参数就是干这个的:
python
agent = create_agent(
model=model,
name="OpenAI Gym", # 自定义名称
)
messages = [{"role": "user", "content": "你好"}]
response = agent.invoke({"messages": messages})
for msg in response["messages"]:
msg.pretty_print()
输出中会看到 AI 消息带上了 Name: OpenAI Gym,在多 Agent 系统中一眼就能区分消息来源。
2. 系统提示词:定制 Agent 的"人设"
通过 system_prompt 参数,你可以为 Agent 设置系统级指令,既可以是字符串,也可以是 SystemMessage 对象。
python
agent = create_agent(
model=model,
name="江西文旅助手",
system_prompt="你是一个热心的江西文旅推介系统的客服助手"
)
messages = [{"role": "user", "content": "帮我推荐南昌一日游路线"}]
response = agent.invoke({"messages": messages})
结合工具使用时,系统提示词还能指导 Agent 的调用流程,例如"如果是 VIP 客户则发送感谢邮件"。
3. 结构化输出:让 Agent 返回类型安全的对象
这是 Agent 高级用法中最关键的功能。create_agent 提供了 response_format 参数,让我们可以要求 Agent 最终输出一个符合特定 Schema 的结构化数据。
3.1 ProviderStrategy ------ 原生结构化支持
对于本身就支持结构化输出(如 OpenAI 的 response_format 参数)的模型,使用 ProviderStrategy 最为高效。
python
from pydantic import BaseModel, Field
from langchain.agents.structured_output import ProviderStrategy
class ContactInfo(BaseModel):
name: str = Field(description="姓名")
email: str = Field(description="邮箱")
phone: str = Field(description="电话")
agent = create_agent(
model=model,
name="信息提取助手",
response_format=ProviderStrategy(ContactInfo)
)
response = agent.invoke({
"messages": [
{"role": "user", "content": "小明的电话是12874384973,邮箱是12347923@qq.com"}
]
})
# 直接取结构化结果
print(response["structured_response"]) # ContactInfo(name='小明', email='...', phone='...')
返回结果中会多出一个 structured_response 字段,里面就是解析好的 Pydantic 实例(或其他类型,取决于你的 Schema)。
3.2 ToolStrategy ------ 通用回退方案
如果模型本身不支持原生的结构化输出(例如某些开源模型),LangChain 会采用 ToolStrategy,将你的 Schema 伪装成一个"工具",让模型通过调用这个工具来"传递"结构化数据。
python
from langchain.agents.structured_output import ToolStrategy
agent = create_agent(
model=model,
response_format=ToolStrategy(ContactInfo)
)
底层流程:
- Agent 运行过程中,模型会生成一个
tool_calls,其name就是你的 Schema 名称(如ContactInfo),args里就是提取到的字段值。 - LangChain 拦截这个调用,并不实际执行工具,而是将
args转为structured_response,同时向消息历史中添加一条ToolMessage(伪工具消息),告诉模型"结构化输出成功"。 - 最终,
structured_response就是你想要的对象。
关键点 :
ToolStrategy适用于所有模型,是兜底方案。如果你的模型支持原生结构化,优先用ProviderStrategy,更省 token。
3.3 支持哪些 Schema 类型?
ToolStrategy 的 schema 参数支持多种定义方式,满足不同场景:
| Schema 类型 | 示例 | 特点 |
|---|---|---|
| Pydantic BaseModel | class ContactInfo(BaseModel): ... |
运行时校验,推荐 |
| TypedDict | class ContactInfo(TypedDict): ... |
轻量,无校验 |
| JSON Schema 字典 | {"type":"object","properties":...} |
动态定义 |
| @dataclass | @dataclass class ContactInfo: ... |
标准库,无校验 |
TypedDict 示例:
python
from typing import TypedDict, Annotated, Optional
class ContactInfo(TypedDict):
name: Annotated[Optional[str], None, "姓名"]
email: str
phone: str
JSON Schema 示例:
python
json_schema = {
"type": "object",
"properties": {
"name": {"type": "string", "description": "姓名"},
"email": {"type": "string"},
"phone": {"type": "string"}
},
"required": ["name", "email", "phone"]
}
@dataclass 示例 (需配合 Pydantic 的 Field):
python
from dataclasses import dataclass
from pydantic import Field
@dataclass
class ContactInfo:
name: str = Field(description="姓名")
email: str = Field(description="邮箱")
phone: str = Field(description="电话")
联合类型(Union) :当输出可能是多种结构时,可使用 Union[Schema1, Schema2],Agent 会根据上下文选择最合适的一个。
3.4 自定义 Tool 消息内容(tool_message_content)
默认情况下,ToolStrategy 会在 ToolMessage 中返回完整的结构化数据(如 "Returning structured response: name='张三' ...")。如果你不想让这段冗长数据进入对话上下文(节省 token),可以自定义消息内容:
python
agent = create_agent(
model=model,
response_format=ToolStrategy(ContactInfo, tool_message_content="提取成功")
)
这样模型收到的 ToolMessage 仅含"提取成功",而 structured_response 依然正常返回,不影响最终结果。
3.5 错误处理(handle_errors)
模型有时会生成不符合 Schema 的数据。ToolStrategy 的 handle_errors 参数提供了多重策略:
| 参数值 | 行为 |
|---|---|
True(默认) |
捕获所有异常,用 LangChain 内置错误提示让模型重试,直到成功 |
False |
直接抛出异常,中断执行 |
"自定义字符串" |
用你指定的字符串作为错误消息喂给模型重试 |
ExceptionType(如 ValueError) |
只捕获指定类型异常重试,其余抛出 |
callable 函数 |
自定义异常处理逻辑,根据不同异常返回不同提示 |
python
# 自定义错误消息
agent = create_agent(
model=model,
response_format=ToolStrategy(ContactInfo, handle_errors="请严格按照格式返回姓名、邮箱和电话")
)
4. 流式输出:实时掌控 Agent 执行过程
create_agent 提供了 .stream() 方法,通过 stream_mode 参数你可以获得不同粒度的输出,适应不同场景。
4.1 messages 模式 ------ 打字机效果(聊天机器人最爱)
python
for chunk in agent.stream(
{"messages": [{"role": "user", "content": "查询客户 CUST123456 的信息"}]},
stream_mode="messages"
):
# chunk 是一个元组 (AIMessageChunk, metadata)
print(chunk[0].content, end="", flush=True)
输出会以 token 流的形式逐字打印,非常适合需要实时交互的聊天应用。
4.2 updates 模式 ------ 观察 Agent 的"思考"步骤(默认)
updates 是默认模式,每次节点(模型、工具)执行完毕,都会增量返回该节点产生的新消息。你可以看到 Agent 调用了哪些工具、拿到了什么结果。
python
for chunk in agent.stream(..., stream_mode="updates"):
# chunk 形如 {"model": {"messages": [...]}} 或 {"tools": {"messages": [...]}}
print(chunk)
适合做执行监控或调试。
4.3 values 模式 ------ 获取每一步的完整状态
每次迭代都会输出整个状态字典(包含所有消息),适合需要状态持久化或快照的场景。
4.4 tasks / debug 模式 ------ 任务生命周期监控
tasks 模式会在每个任务(task)开始和结束时输出信息;debug 模式更详细,包含时间戳和任务类型。适合分布式追踪。
4.5 checkpoints 模式 ------ 状态检查点
当 LangGraph 检查点被创建时触发,适合工作流恢复。
4.6 custom 模式 ------ 工具内部自定义进度
这是最灵活的模式。在工具函数内部,通过 get_stream_writer() 获取 writer,可以发送任意自定义数据(如进度百分比),然后在外层以 stream_mode="custom" 接收。
python
from langgraph.config import get_stream_writer
from langchain.tools import tool
import time
@tool
def generate_sales_report():
writer = get_stream_writer()
writer({"type": "进度", "message": "开始生成销售报告"})
for i in range(1, 4):
time.sleep(0.5)
writer({"type": "进度", "message": f"进度:{i*25}%"})
return "报告完成"
agent = create_agent(model=model, tools=[generate_sales_report])
for chunk in agent.stream(
{"messages": [{"role": "user", "content": "生成销售报告"}]},
stream_mode="custom"
):
print(chunk)
这在长耗时工具(如批量数据处理、爬虫)中尤其有用,能给用户实时反馈。
5. 组合使用多种模式
你还可以传入列表,同时启用多种模式,例如:
python
for chunk in agent.stream(..., stream_mode=["messages", "updates"]):
# 根据 chunk 类型分别处理
这样既能看到 token 流,又能知道执行到哪个步骤了。
📊 模式对比一览
| 模式 | 输出内容 | 适用场景 |
|---|---|---|
messages |
增量 token + 元数据 | 聊天打字机效果 |
updates(默认) |
每个节点(model/tools)产生的增量消息 | 监控 Agent 执行步骤 |
values |
每一步的完整状态 | 状态持久化、快照 |
tasks |
任务开始/结束事件 | 任务生命周期监控 |
debug |
tasks 基础上加时间戳和类型 | 深度调试 |
checkpoints |
检查点创建时触发 | 工作流恢复、分布式 |
custom |
工具内 writer 自定义数据 | 进度反馈、自定义日志 |
🧠 进阶技巧与避坑指南
- 结构化输出与工具调用的顺序:在系统提示词中,如果先要求结构化输出,Agent 可能提前结束,导致后续工具不再调用。建议把"生成结构化报告"放在最后一步。
ToolStrategy的伪工具消息 :即使你没有真正定义那个工具,LangChain 也会在消息历史中插入一条ToolMessage。如果你不在意历史记录里的细节,可以通过tool_message_content精简它。- 错误重试的代价 :开启
handle_errors=True会消耗额外 token,如果对数据质量要求极高且不介意成本,可以保持;否则可关闭并自己捕获异常。 ProviderStrategyvsToolStrategy:ProviderStrategy更高效(少一轮工具调用),但要求模型支持原生response_format;不确定时直接用ToolStrategy最稳妥。
总结
LangChain Agent 的高级用法远不止"调用工具"。通过:
- 命名 和系统提示词管理身份与行为
- 结构化输出 (
response_format)获取类型安全的结果 - 流式模式 (
stream_mode)实现实时交互与进度监控
你能让 Agent 从"黑盒"变成"可观测、可控制"的可靠组件。希望这篇文章能帮你在实际项目中灵活运用这些特性,构建更强大的 AI 应用。
如果觉得有用,欢迎点赞收藏,也欢迎在评论区聊聊你的 Agent 实战经验! 🚀