LangChain structured output 怎样固定工具返回 schema
来源:17golang原创
时间:2026-11-01 03:29:24 165浏览 收藏
如果 agent 调用工具后仍然返回一段不稳定的自然语言,问题通常不在“提示词写得不够长”,而在 schema 绑定错了位置。LangChain 里至少有两个边界:工具的输入参数 schema,以及 agent 最终交给业务代码的结构化响应。要固定后者,应把 Pydantic 模型交给 response_format,需要明确走工具调用时使用 ToolStrategy,再从结果的 structured_response 读取。
官方地址:https://docs.langchain.com/
下面用一个订单查询工具作为例子。示例中的指标是用于设计验收的基线,不代表 LangChain 官方性能承诺。
先把两个 schema 边界分开
工具参数描述“模型怎样调用工具”,例如订单号是什么类型;结构化响应描述“agent 最终交给应用的结果”,例如订单状态、是否需要人工处理和解释。只给工具函数写返回类型,并不会自动把整个 agent 的最终输出固定成这个类型。
| 边界 | 绑定位置 | 业务代码读取 |
|---|---|---|
| 工具输入 | 类型注解或 args_schema | 工具函数参数 |
| agent 最终结果 | response_format + Pydantic | state["structured_response"] |

用 Pydantic 把业务结果写成可校验结构
先定义一个足够小的结果模型。字段描述会进入模型可见的 schema,枚举和范围约束则让错误结果有明确的修正方向。
from typing import Literal
from pydantic import BaseModel, Field
class OrderDecision(BaseModel):
"""订单查询后交给业务层的固定结果。"""
order_id: str = Field(description="用户提供的订单号")
status: Literal["pending", "paid", "shipped", "cancelled"]
needs_human: bool = Field(description="是否需要人工跟进")
reason: str = Field(description="给业务人员看的简短原因")
# 约束写在模型中,后续的 agent 不必依赖字符串解析。
print(OrderDecision.model_json_schema()["properties"].keys())
这里的 Literal 限制状态集合,needs_human 明确为布尔值。不要为了“兼容模型”把所有字段都改成字符串,否则结构看似固定,业务层仍要二次猜测。
用 ToolStrategy 固定 agent 的最终响应
当模型支持工具调用、但不一定支持 provider-native structured output 时,显式使用 ToolStrategy 最直观。工具只负责查数据,response_format 负责把 agent 的最终结果收口。
from langchain.agents import create_agent
from langchain.agents.structured_output import ToolStrategy
def lookup_order(order_id: str) -> str:
"""按订单号查询脱敏后的订单状态。"""
# 示例返回固定文本;真实项目应在这里访问订单服务并处理超时。
return f"订单 {order_id} 已发货,物流单号为 SF123456"
agent = create_agent(
model="openai:gpt-5.5",
tools=[lookup_order],
response_format=ToolStrategy(OrderDecision),
system_prompt=(
"你是订单助手。只能根据工具返回的信息填写 schema,"
"不能补造订单号、状态或物流事实。"
),
)
result = agent.invoke({
"messages": [{
"role": "user",
"content": "查询订单 A-1007 的状态,并告诉我是否需要人工处理。"
}]
})
# 业务层读取结构化对象,而不是解析最后一条自然语言消息。
decision = result["structured_response"]
print(decision.order_id, decision.status, decision.needs_human)
这个组合形成两步约束:模型先按工具定义生成调用参数,工具执行后再按 OrderDecision 生成唯一的结构化响应。若模型或 provider 不支持原生结构化输出,LangChain 会用工具调用策略完成这一步。
让校验失败反馈给模型,而不是静默吞掉
ToolStrategy 默认会处理结构化输出错误。生产代码可以把提示改成业务语言,让重试针对字段约束,而不是无限重复同一个请求。
from langchain.agents import create_agent
from langchain.agents.structured_output import ToolStrategy
agent = create_agent(
model="openai:gpt-5.5",
tools=[lookup_order],
response_format=ToolStrategy(
schema=OrderDecision,
# 只说明如何修正格式,不把业务事实写死在错误提示里。
handle_errors="请重新输出单个 OrderDecision:status 必须是允许的四个值,needs_human 必须是布尔值。",
tool_message_content="订单结果已按固定 schema 整理。",
),
)
# 真实项目应记录重试次数,避免把模型错误变成无上限成本。
result = agent.invoke({
"messages": [{"role": "user", "content": "查询订单 A-1007"}]
})
print(result["structured_response"].model_dump())
如果应用希望所有异常直接抛出,可以把 handle_errors 设为 False;如果只想捕获某一类异常,也可以传入异常类型或自定义处理函数。注意:重试只能修正结构,不会替你验证订单服务返回的事实。

用一组小指标判断 schema 是否真的稳定
可以把验收拆成三项,而不是只看“这次返回成功”。例如对一组包含已发货、待付款和信息不足的固定样例,记录结构通过率、人工兜底率和平均重试次数:
| 指标 | 基线写法 | 目标判断 |
|---|---|---|
| 结构通过率 | 自然语言 + 手写解析 | 应接近全部样例都能通过模型校验 |
| 人工兜底率 | 字段缺失时靠业务猜测 | 只在事实缺失或规则冲突时触发 |
| 平均重试次数 | 默认提示不记录 | 按模型、字段约束和输入类型分组观察 |
不要把“结构通过率提高”直接等同于“答案正确”。schema 只能保证形状、类型和部分范围;订单状态是否真实,仍要由工具服务、权限和业务规则负责。
工具多、模型能力不同的时候怎么选
如果模型供应商原生支持结构化输出,可以直接传 schema 类型,让 LangChain 自动选择 provider strategy;需要兼容更多支持 tool calling 的模型时,显式使用 ToolStrategy。当 agent 同时绑定普通工具和结构化响应时,模型还必须支持两者并用。
最后记住三个边界:
- 工具的返回字符串不是最终 schema;要固定 agent 结果,就配置
response_format。 structured_response是结果读取入口,不能再从自然语言消息中二次正则解析。- 多个结构化结果、字段校验失败和真实业务事实错误是三类问题,应分别记录和处理。
常见问题
只给工具函数加返回类型可以吗? 可以帮助阅读和静态检查,但不能替代 agent 的 response_format。
为什么用了 Pydantic 仍然会重试? Pydantic 负责校验结果;模型第一次生成的字段仍可能越界或缺失,handle_errors 会把错误反馈给模型。
工具本身也需要输入 schema 怎么办? 给工具参数添加类型注解,或使用 args_schema 绑定一个独立的 Pydantic 输入模型;它和最终响应模型可以不同。
-
文章 · python教程 | 5个月前 | 性能优化 · fastapi · 生产实践 · Python教程 · Python 性能优化 FastAPI Pydantic v2 TypeAdapter validate_json342 收藏
-
149 收藏
-
460 收藏
-
269 收藏
-
195 收藏
-
248 收藏
-
253 收藏
-
211 收藏
-
424 收藏
-
435 收藏
-
265 收藏
-
249 收藏
-
471 收藏
-
329 收藏
-
120 收藏
-
422 收藏
-
284 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习