概述
LangChain 的结构化输出功能允许智能体(Agent)以特定、可预测的格式返回数据,无需解析自然语言响应,直接生成 JSON 对象、Pydantic 模型或数据类(dataclasses)等可被应用直接使用的结构化数据。其核心通过 <code>create_agent</code> 函数自动处理,用户仅需设置期望的结构化输出 schema,模型生成数据后会被捕获、验证,并最终存储在智能体状态的 structured_response键中。
本质是告诉智能体 “以什么格式返回结构化数据”。在 LangChain 中,智能体默认可能返回自然语言文本(需手动解析),而通过参数配置后,智能体可直接生成符合预设格式的结构化数据(如 Pydantic 模型实例、JSON 对象),并自动完成验证,最终存储在智能体状态的 <code>structured_response</code> 键中,供应用直接调用(无需解析自然语言)。
响应格式
在 LangChain 结构化输出(Structured Output)体系中,“响应格式” 是核心配置项,用于定义智能体(Agent)如何生成、组织和返回结构化数据,直接决定了输出是否符合应用需求(如能否被直接解析为 JSON、Pydantic 模型等)。
响应格式(<code>response_format</code>)是 <code>create_agent</code> 函数的关键参数,其本质是为智能体提供 “结构化输出的规则手册”:
- 替代 “自然语言响应”:避免后续手动解析非结构化文本(如从句子中提取字段),直接生成可机器读取的格式(JSON、模型实例等);
- 统一输出标准:通过预设规则确保每次输出的字段、类型、约束(如数值范围)一致;
- 衔接策略逻辑:不同格式类型对应不同的实现方案(原生功能 / 工具调用),适配不同能力的模型。
响应格式控制智能体返回结构化数据的方式,共支持四种类型,且会根据模型能力自动选择最优策略:
| 格式类型 | 完整表达式 | 核心逻辑 | 适用场景 |
|---|---|---|---|
| 工具调用策略 | <code>ToolStrategy[StructuredResponseT]</code> | 借助模型的 “工具调用能力” 模拟结构化输出:智能体通过调用预设工具,将结构化数据封装为工具调用结果返回 | 模型不支持原生结构化输出,但支持工具调用(如大部分开源模型 Llama 3、Mistral,或部分闭源模型) |
| 提供商原生策略 | <code>ProviderStrategy[StructuredResponseT]</code> | 利用模型厂商 API 自带的结构化输出功能(如 OpenAI 的 <code>response_format</code> 参数、Anthropic 的结构化生成能力),由厂商直接保证格式正确性 | 模型支持原生结构化输出(如 OpenAI GPT-4/5、Anthropic Claude 3、Grok、Gemini) |
| Schema 类型直传 | <code>type[StructuredResponseT]</code> | 直接传入结构化数据的 “格式模板”(如 Pydantic 模型类、dataclass 类),LangChain 自动判断模型能力并选择最优策略(原生 / 工具) | 所有场景,推荐优先使用(简化配置,无需手动判断模型能力) |
| 无结构化输出 | <code>None</code> | 不启用结构化输出,智能体返回常规自然语言文本,无 <code>structured_response</code> 键 | 无需机器解析输出,仅需人类可读文本的场景(如闲聊、通用问答) |
Provider strategy 提供商策略
在上面提到的几种类型中,若大模型本身支持结构化输出,则可以使用“Provider strategy 提供商策略”。代码层面需要使用进行声明,如下:
class ProviderStrategy(Generic[SchemaT]):
schema: type[SchemaT]
在声明中,提到了Scheme的概念;下面将介绍schema及其类型。
什么是schema
<code>schema</code> 本质是一份 “结构化输出的规则说明书”,它告诉智能体两件关键事:
- “输出什么”:必须包含哪些字段(如 “联系人信息” 需包含 <code>name</code>、<code>email</code>、<code>phone</code>);
- “怎么输出才对”:字段的类型(如 <code>name</code> 是字符串、<code>rating</code> 是整数)、约束规则(如 <code>rating</code> 必须在 1-5 之间、<code>email</code> 需符合邮箱格式)。
有了 <code>schema</code>,智能体生成的结构化数据就会 “有章可循”,避免出现 “字段缺失”“类型错误”“数值超出范围” 等问题,最终输出能被应用直接使用的规范数据(无需额外解析或修正)。
4 种支持的 schema类型
LangChain 支持 4 种主流的 <code>schema</code> 定义方式,覆盖不同开发习惯(如 “追求强验证”“追求简洁”“需兼容 JSON 生态”),具体如下:
Pydantic models(Pydantic 模型):强验证、最推荐
from pydantic import BaseModel, Field, EmailStr # 导入Pydantic核心类
# 定义“联系人信息”的schema(Pydantic模型)
class ContactInfo(BaseModel):
"""联系人信息的结构化输出格式""" # 模型描述(可选,提升可读性)
name: str = Field(description="联系人姓名,必填", min_length=1) # 字符串,至少1个字符
email: EmailStr = Field(description="联系人邮箱,需符合邮箱格式(如xxx@xxx.com)") # 邮箱格式验证
phone: str | None = Field(description="联系人电话,可选(如无则为None)", default=None) # 可选字段,默认None
age: int = Field(description="联系人年龄,18-60岁之间", ge=18, le=60) # 整数,18≤age≤60
其通过 <code>Field</code> 对字段进行类型声明 + 详细验证规则(如数值范围、格式校验、必填 / 可选),是 LangChain 中最推荐的 <code>schema</code> 类型。适用于需要严格验证字段规则的场景(如用户信息提取、产品评分解析、表单数据生成),是 LangChain 结构化输出的 “首选方案”。
支持几十种内置验证规则(如数值范围 <code>ge=1, le=5</code>、邮箱格式 <code>EmailStr</code>、字符串长度 <code>min_length=3</code>),能从源头过滤无效数据;可通过字段描述(<code>description</code>)明确字段含义,帮助模型更准确理解需求。
Dataclasses(Python 数据类):简洁、轻量
from dataclasses import dataclass # 导入Python内置的dataclass
# 定义“产品信息”的schema(数据类)
@dataclass
class ProductInfo:
"""产品信息的结构化输出格式"""
product_name: str # 字符串类型(仅声明类型,无额外验证)
price: float # 浮点型(如19.99)
in_stock: bool # 布尔型(True=有货,False=缺货)
category: str | None = None # 可选字段,默认None
通过类型注解声明字段类型,不支持复杂验证(仅基础类型检查,如 “是否为整数”),特点是 “简洁、无需额外安装依赖”。
TypedDict(类型字典):兼容字典、强类型
from typing import TypedDict, Optional # 导入Python内置的TypedDict
# 定义“订单信息”的schema(类型字典)
class OrderInfo(TypedDict):
"""订单信息的结构化输出格式"""
order_id: str # 订单ID(字符串,必填)
total_amount: float # 订单总金额(浮点型,必填)
is_paid: bool # 是否支付(布尔型,必填)
shipping_address: Optional[str] # 收货地址(可选,值为字符串或None)
基于 Python 标准库 <code>typing.TypedDict</code> 的类,本质是 “带类型注解的字典”—— 既保留字典的灵活性(可通过键值对访问),又通过类型注解约束 “键的名称” 和 “值的类型”,不支持字段验证(仅类型检查)。
JSON Schema(JSON 模式):跨语言、通用
# 定义“文章信息”的schema(JSON Schema字典)
ArticleSchema = {
"$schema": "http://json-schema.org/draft-07/schema#", # 声明遵循的JSON Schema版本
"type": "object", # 整体是一个对象(对应Python的字典)
"title": "文章信息的结构化输出格式",
"properties": { # 定义字段(键)及其规则
"title": {
"type": "string", # 字段类型:字符串
"description": "文章标题,必填",
"minLength": 5 # 字符串至少5个字符
},
"author": {
"type": "string",
"description": "文章作者,必填"
},
"word_count": {
"type": "integer",
"description": "文章字数,必填,且≥100",
"minimum": 100 # 数值最小为100
},
"tags": {
"type": "array", # 字段类型:数组
"description": "文章标签,可选,每个标签是字符串",
"items": {"type": "string"} # 数组元素必须是字符串
}
},
"required": ["title", "author", "word_count"], # 声明必填字段
"additionalProperties": False # 不允许出现schema中未定义的额外字段
}
基于 JSON 格式的 “模式描述字典”,遵循 JSON Schema 规范,通过键(如 <code>type</code>、<code>properties</code>、<code>required</code>、<code>minimum</code>)定义字段的类型、约束和结构,是跨语言通用的 schema 格式(不仅 Python,Java、JavaScript 等都支持)。
Tool calling strategy 工具调用策略
“工具调用策略” 是针对不支持原生结构化输出的模型设计的核心方案,让模型生成符合预设格式的结构化数据。当模型不支持厂商原生结构化输出(如大部分开源模型 Llama 3、Mistral,或部分闭源模型),但支持 “工具调用” 功能时,LangChain 会将 “结构化输出” 包装成一个 “虚拟工具”—— 模型通过调用这个 “工具”,将结构化数据以 “工具参数” 的形式返回,再由 LangChain 解析、验证后,转化为最终的结构化结果(如 Pydantic 模型实例)。
简单来说:工具调用策略 = 用 “工具调用” 的通用能力,模拟 “结构化输出” 的专属效果,实现 “让更多模型具备结构化输出能力” 的兼容性目标。
代码层面,要使用此策略,请配置一个<code>ToolStrategy</code>:
class ToolStrategy(Generic[SchemaT]):
schema: type[SchemaT] # 必需:结构化输出的格式规则(schema)
tool_message_content: str | None # 可选:生成结构化输出后的自定义反馈消息
handle_errors: Union[bool, str, type[Exception], tuple[type[Exception], ...], Callable[[Exception], str]] # 可选:错误处理策略
在代码中,可以看见三个重要参数:schema、tool_message_content、handle_errors。
schema 模式
schema<code> 的作用与 Provider Strategy 中的 </code>schema<code> 完全一致 —— 定义结构化输出的 “字段、类型、约束”,是智能体判断 “输出是否正确” 的核心依据。支持的 </code>schema` 类型与 Provider Strategy 相同,覆盖主流格式需求:
- Pydantic 模型(<code>BaseModel</code> 子类,带字段验证,如 <code>rating: int = Field(ge=1, le=5)</code>);
- Python 数据类(<code>@dataclass</code> 装饰,带类型注解,如 <code>product_name: str</code>);
- TypedDict(类型化字典,如 <code>class OrderInfo(TypedDict): order_id: str</code>);
- JSON Schema(字典格式的 JSON 规范,如 <code>{"type": "object", "properties": {"name": {"type": "string"}}}</code>);
- Union 类型(工具调用策略独有的灵活能力):允许指定多个 <code>schema</code> 选项(如 <code>Union[ContactInfo, EventDetails]</code>),模型会根据用户输入的上下文,自动选择最匹配的 <code>schema</code> 生成输出。
tool_message_content自定义工具反馈消息
该参数用于自定义 “模型生成结构化输出后” 的反馈内容,默认情况下,反馈消息会显示原始结构化数据(如 <code>Returning structured response: {'name': 'John', 'email': 'john@xxx.com'}</code>);通过该参数可替换为更友好、更贴合业务场景的提示。
核心价值:让工具调用的 “反馈过程” 更符合业务逻辑(如 “行动项已记录到会议笔记”“用户信息已存入数据库”),而非暴露技术细节。示例代码如下:
strategy = ToolStrategy(
schema=MeetingAction, # MeetingAction是定义“会议行动项”的Pydantic模型
tool_message_content="✅ 会议行动项已捕获并添加到待办列表!" # 自定义反馈
)
# 调用智能体后,工具反馈消息会显示:
# "✅ 会议行动项已捕获并添加到待办列表!"
# 而非默认的“Returning structured response: {...}”
当然,这一步如果不是在Agent体系内,也可以用工程方法基于大模型返回进行特殊提示文案的处理。
handle_errors错误处理
该参数定义了 “当模型生成的结构化数据不符合 <code>schema</code> 时,如何处理”,是提升结构化输出可靠性的关键配置。支持 5 种配置方式,覆盖不同错误处理需求:
| 配置类型 | 具体含义 | 示例 |
|---|---|---|
| <code>True</code>(默认) | 捕获所有错误,使用 LangChain 内置的默认错误模板提示模型重试 | <code>handle_errors=True</code> |
| 字符串(str) | 捕获所有错误,使用自定义的固定文本提示模型重试 | <code>handle_errors="请确保评分在1-5分,且包含评论!"</code> |
| 异常类型 | 仅捕获指定类型的错误(其他错误直接抛出),用默认模板提示重试 | <code>handle_errors=ValueError</code>(仅处理值错误) |
| 异常元组 | 仅捕获元组中指定的多种错误,用默认模板提示重试 | <code>handle_errors=(ValueError, TypeError)</code> |
| 自定义函数 | 捕获所有错误,按函数逻辑返回自定义提示(支持根据错误类型差异化处理) | 见下文示例 |
from langchain.agents.structured_output import StructuredOutputValidationError, MultipleStructuredOutputsError
# 自定义错误处理函数:根据错误类型返回不同提示
def custom_error_handler(error: Exception) -> str:
if isinstance(error, StructuredOutputValidationError):
return "❌ 格式错误!请检查字段是否完整(如评分需1-5分),重新生成。"
elif isinstance(error, MultipleStructuredOutputsError):
return "❌ 仅允许返回1个结构化结果,请删除多余内容后重试。"
else:
return f"❌ 未知错误:{str(error)},请重新生成。"
# 配置策略时指定自定义函数
strategy = ToolStrategy(
schema=ProductRating,
handle_errors=custom_error_handler
)
代码示例
下面的代码示例将串联起上方提到的参数,还是用代码例子来理解实现细节吧:
from pydantic import BaseModel, Field, EmailStr
from langchain.agents import create_agent
from langchain.agents.structured_output import ToolStrategy
# 1. 定义Pydantic模型(强验证schema)
class UserInfo(BaseModel):
"""用户信息的结构化输出格式"""
username: str = Field(description="用户名,3-20个字符", min_length=3, max_length=20)
email: EmailStr = Field(description="用户邮箱,需符合格式(如xxx@xxx.com)")
age: int | None = Field(description="用户年龄,18-60岁(可选,无则为None)", ge=18, le=60)
# 2. 自定义错误处理函数
def error_handler(e: Exception) -> str:
if isinstance(e, StructuredOutputValidationError):
return f"格式错!请检查:1. 用户名3-20字符;2. 邮箱格式正确;3. 年龄18-60。详情:{e}"
return f"未知错:{e},请重新生成!"
# 3. 创建ToolStrategy(配置所有参数)
strategy = ToolStrategy[UserInfo]( # 显式声明泛型,提升可读性
schema=UserInfo,
tool_message_content="✅ 用户信息已验证通过,可用于账号注册!",
handle_errors=error_handler
)
# 4. 创建智能体并调用
agent = create_agent(
model="llama-3-70b", # 不支持原生结构化输出,用ToolStrategy
tools=[], # 无真实工具,仅用工具策略实现结构化输出
response_format=strategy
)
# 5. 调用智能体提取用户信息
result = agent.invoke({
"messages": [{"role": "user", "content": "提取用户信息:小明(username: xiaoming123),邮箱xiaoming@test.com,年龄25岁"}]
})
# 6. 输出结果(结构化数据)
print(result["structured_response"])
# 输出:UserInfo(username='xiaoming123', email='xiaoming@test.com', age=25)
知识点拆解
| 模块 | 关键信息 | 产品关注 |
|---|---|---|
| Response Format 参数 | <code>response_format=ContactInfo</code>、<code>ToolStrategy(schema=...)</code> | PRD 中必须定义输出 schema,明确字段含义、必填程度 |
| ProviderStrategy | 模型原生 structured output;自动校验、可靠性高 | 选择支持度较好的模型,减少二次解析 |
| ToolStrategy | 利用 tool calling 返回结构化数据,几乎所有现代模型可用 | 需考虑 tool message 内容、错误提示语、重试策略 |
| Schema 类型 | 支持 Pydantic、Dataclass、TypedDict、JSON Schema、Union | 输出 schema 可直接复用业务数据模型,便于研发对接 |
| 结果获取 | <code>result["structured_response"]</code> 即为校验完的对象 | 需求设计中说明后续系统直接消费该字段 |
| Tool Message | <code>tool_message_content</code> 自定义提示,默认展示 structured data | 适合低代码场景,让用户看到友好的提示语 |
| Error Handling | Default 自动重试;可自定义捕获异常类型或自定义函数 | PRD 中说明错误文案、重试次数、容错场景 |
| 多结构输出 | Union schema 允许在多个结构间选择(如联系人 vs 事件) | 需设计判定逻辑及错误反馈,避免多 schema 同时返回 |
| Provider 能力探测 | <code>response_format=Schema</code> 时 LangChain 自动判断;也可自定义 profile | 明确 fallback 机制,以免切换模型后失效 |
适用场景(问题 → 方案 → 价值)
- 合同要素抽取
- 问题:法务工具需从自然语言合同中提取签约主体、金额、有效期。
- 方案:定义 <code>ContractInfo</code> Pydantic 模型并设置为 <code>response_format</code>;模型输出直接进入 <code>structured_response</code>,无须再写解析器。
- 价值:减少手写解析逻辑,字段缺失能即时通过 schema 校验捕获。
- 会议纪要行动项
- 问题:运维会议需要自动生成任务列表(task/assignee/priority)。
- 方案:使用 ToolStrategy + 自定义 <code>tool_message_content</code>,确保 UI 展示“Action item captured”而非原始 JSON。
- 价值:输出结果更友好,可直接同步至任务系统,提升闭环效率。
常见问题(FAQ)
- ProviderStrategy 与 ToolStrategy 何时选?→ 模型支持原生 structured output 时优先 Provider;否则使用 ToolStrategy。
- 可以同时使用工具和 structured output 吗?→ 可以,但需确认模型支持“Tool + Structured Output”组合。
- Schema 更新后如何兼容?→ 版本化 schema,并在 PRD 中记录字段变化;若字段必填需同步调整验证逻辑。
- Union schema 如何避免多输出?→ LangChain检测到多 schema 同时返回会报错并提示模型重试;错误文案可自定义。
- JSON Schema vs Pydantic 选择?→ Pydantic 便于 Python 项目直接使用;JSON Schema 对跨语言友好。
- 能否自定义错误提示?→ <code>handle_errors</code> 支持字符串或函数,覆盖默认提示。
- Structured response 与普通 <code>messages</code> 冲突吗?→ 不冲突;结构化数据存在 state 中,同时仍可查看自然语言回答。
- 如何调试 schema 校验?→ 在日志中打印 <code>ToolMessage</code> 内容,或暂时关闭 <code>handle_errors</code> 直接抛异常。
- 大模型会“编造字段”吗?→ Schema 会强制校验类型/必填;必要时在系统提示强调“禁止杜撰”。
- 输出需要国际化吗?→ schema 字段通常英文,若需中文可在后处理阶段转换;关键是保证字段名稳定。
扩展信息
- Structured Output 文档:https://docs.langchain.com/oss/python/langchain/structured-output
- ToolStrategy/ProviderStrategy 参考:https://reference.langchain.com/python/langchain/agents/structured_output