目录
- 前言
- 一、什么是结构化输出?
- 二、response_format的三种配置方式
-
- [2.1 直接传入模式(自动选择策略)](#2.1 直接传入模式(自动选择策略))
- [2.2 提供商策略(ProviderStrategy)](#2.2 提供商策略(ProviderStrategy))
- [2.3 工具调用策略(ToolStrategy)](#2.3 工具调用策略(ToolStrategy))
- 三、定义模式的四种常用方法
-
- [3.1 Pydantic模型(推荐)](#3.1 Pydantic模型(推荐))
- [3.2 数据类(Dataclass)](#3.2 数据类(Dataclass))
- [3.3 TypedDict](#3.3 TypedDict)
- [3.4 JSON Schema](#3.4 JSON Schema)
- 四、工具调用策略的高级功能
-
- [4.1 自定义工具消息内容(tool_message_content)](#4.1 自定义工具消息内容(tool_message_content))
- [4.2 智能错误处理(handle_errors)](#4.2 智能错误处理(handle_errors))
- 五、联合类型:让模型选择输出模式
前言
- 在构建智能体应用、大模型应用时,一个常见的痛点是如何从模型的自然语言回复中可靠地提取关键信息。比如,你希望模型返回一个包含姓名、邮箱、电话的JSON对象,但它可能回复"好的,这是你要的信息:张三,邮箱是zhangsan@example.com,电话是123456"。为了在程序中使用这些数据,你不得不编写复杂的正则表达式或提示工程来解析。这不仅低效,而且容易出错。
- LangChain的智能体(Agent)提供了结构化输出功能,允许我们直接要求模型以预定义的格式(如Pydantic模型、JSON、数据类)返回数据。这样,我们获得的就是一个可以直接使用的Python对象,无需任何解析。
一、什么是结构化输出?
-
结构化输出是指让智能体在生成最终回答时(中间过程大模型自由发挥),严格按照你定义的模式(Schema) 返回数据。LangChain的create_agent函数通过response_format参数支持这一功能。
pythonfrom langchain.agents import create_agent from pydantic import BaseModel, Field class ContactInfo(BaseModel): name: str = Field(description="姓名") email: str = Field(description="邮箱") phone: str = Field(description="电话") agent = create_agent( model="openai:gpt-4", # 这里用openai:gpt-4示例 tools=[], # 工具列表,非必须 response_format=ContactInfo ) result = agent.invoke({ "messages": [{"role": "user", "content": "提取联系人信息:张三,zhangsan@example.com,13800138000"}] }) # 输出直接就是 ContactInfo 对象 print(result["structured_response"]) # ContactInfo(name='张三', email='zhangsan@example.com', phone='13800138000')
当response_format被设置后,智能体最终状态中会增加一个structured_response键,其值就是按照模式验证后的数据。
二、response_format的三种配置方式
LangChain为response_format提供了灵活的配置方式,以适应不同的模型能力和需求。
2.1 直接传入模式(自动选择策略)
-
最常用的方式:直接将一个模式类(Pydantic模型、数据类等)传给response_format。LangChain会根据所选模型自动判断使用提供商原生策略 还是工具调用策略。
-
提供商原生策略,说白了就是模型厂商Api端天然支持格式化输出的功能,仅支持 OpenAI、Grok 这类自带该功能的模型
-
工具调用策略是 LangChain 的通用方案,适配绝大多数无原生结构化输出的模型,本质上langchain会自动生成一个格式化输出的工具,并把该工具信息和要求最终结果要调用这个工具等信息封装进系统提示词,最后智能体的最终输出来调用这个工具进行格式化。
-
本人亲测,阿里云百联的 的服务器api端,这两种方式都不支持,不支持工具调用策略原因是,langchain自动生成的http入参中,tool_choice这个字段是个object,而阿里api端这个字段只支持auto和none,说白了就是langchain对阿里的兼容不是很好
python# 使用 Pydantic 模型 from pydantic import BaseModel class WeatherRequest(BaseModel): city: str date: str agent = create_agent( model="openai:gpt-4", response_format=WeatherRequest )
2.2 提供商策略(ProviderStrategy)
-
某些模型(如OpenAI、Grok)通过API原生支持结构化输出。这种模式下,模型直接返回符合JSON Schema的数据,可靠性最高。你可以显式使用ProviderStrategy来启用,但通常直接传模式类时,如果模型支持,LangChain会自动采用此策略。
pythonfrom langchain.agents.structured_output import ProviderStrategy from pydantic import BaseModel class MovieReview(BaseModel): title: str rating: int # 1-10 summary: str agent = create_agent( model="openai:gpt-4", response_format=ProviderStrategy(schema=MovieReview) )
2.3 工具调用策略(ToolStrategy)
-
对于所有支持工具调用的模型,LangChain可以利用工具调用来模拟结构化输出。它会创建一个特殊的工具,让模型以工具参数的形式返回结构化数据。这是通用的后备方案。
pythonfrom langchain.agents.structured_output import ToolStrategy from pydantic import BaseModel from typing import Literal class OrderInfo(BaseModel): order_id: str status: Literal["pending", "shipped", "delivered"] items: list[str] agent = create_agent( model="openai:gpt-3.5-turbo", # 也支持工具调用 response_format=ToolStrategy(schema=OrderInfo) )
三、定义模式的四种常用方法
无论使用哪种策略,你都需要定义一个模式来描述期望的输出结构。LangChain支持多种方式,你可以根据偏好和需求选择。
3.1 Pydantic模型(推荐)
Pydantic模型提供了强大的数据验证功能,支持字段描述、类型约束、默认值等,是Python生态中处理结构化数据的首选。
python
from pydantic import BaseModel, Field
from typing import Optional
class Product(BaseModel):
name: str = Field(description="产品名称")
price: float = Field(description="价格", gt=0)
in_stock: bool = Field(description="是否有库存")
tags: Optional[list[str]] = Field(None, description="标签列表")
3.2 数据类(Dataclass)
如果你喜欢Python原生的数据类,也可以直接使用,注意:数据类不支持字段描述,但可以通过注释或文档字符串补充。
python
from dataclasses import dataclass
from typing import Optional
@dataclass
class Product:
name: str
price: float
in_stock: bool
tags: Optional[list[str]] = None
3.3 TypedDict
如果你只需要类型提示而不需要运行时验证,可以使用TypedDict。
python
from typing_extensions import TypedDict
from typing import Optional
class Product(TypedDict):
name: str
price: float
in_stock: bool
tags: Optional[list[str]]
3.4 JSON Schema
如果你希望以纯字典的形式定义模式,可以直接提供JSON Schema字典。这在需要动态生成模式或与外部系统交互时很有用。
python
product_schema = {
"type": "object",
"properties": {
"name": {"type": "string"},
"price": {"type": "number", "minimum": 0},
"in_stock": {"type": "boolean"},
"tags": {"type": "array", "items": {"type": "string"}}
},
"required": ["name", "price", "in_stock"]
}
agent = create_agent(
model="openai:gpt-4",
response_format=product_schema # 直接传入字典
)
四、工具调用策略的高级功能
当使用ToolStrategy时,你可以获得两个额外的控制能力:自定义工具消息和错误处理。
4.1 自定义工具消息内容(tool_message_content)
在工具调用过程中,Agent会在对话历史中添加一条工具消息(ToolMessage)。默认情况下,这条消息会显示返回的结构化数据。你可以通过tool_message_content自定义这条消息的内容,让对话更自然。
python
from langchain.agents.structured_output import ToolStrategy
from pydantic import BaseModel
class Task(BaseModel):
description: str
assignee: str
due_date: str
agent = create_agent(
model="openai:gpt-4",
tools=[],
response_format=ToolStrategy(
schema=Task,
tool_message_content="任务已创建并分配给相关人员。"
)
)
result = agent.invoke({
"messages": [{"role": "user", "content": "安排张三在周五前完成市场调研报告"}]
})
# 最终的工具消息将显示 "任务已创建并分配给相关人员。" 而不是原始数据
4.2 智能错误处理(handle_errors)
-
模型在调用工具、生成结构化输出时可能会犯错。ToolStrategy内置了重试机制,可以捕获错误并通过工具消息反馈给模型,提示其修正。我们还可以通过handle_errors参数自定义错误处理行为。
-
默认行为:handle_errors=True,捕获所有异常并使用默认错误模板提示模型重试。
pythonresponse_format = ToolStrategy(schema=Task) # 默认启用错误处理 -
自定义错误消息:将handle_errors设为字符串,当发生任何错误时,模型将收到该字符串作为工具消息。
pythonresponse_format = ToolStrategy( schema=Task, handle_errors="格式不正确,请按照要求的JSON格式重新生成。" ) -
仅处理特定异常:可以指定只对某些异常类型进行重试,其他异常则抛出。
pythonfrom pydantic import ValidationError response_format = ToolStrategy( schema=Task, handle_errors=ValidationError # 只在验证错误时重试 )
五、联合类型:让模型选择输出模式
-
ToolStrategy支持使用Union类型来定义多个格式化模板类,模型从多个可能的输出模式中选择一个
pythonfrom typing import Union from pydantic import BaseModel class ContactInfo(BaseModel): name: str email: str class EventDetails(BaseModel): event_name: str date: str agent = create_agent( model="openai:gpt-4", response_format=ToolStrategy(schema=Union[ContactInfo, EventDetails]) ) result = agent.invoke({ "messages": [{"role": "user", "content": "记住:下周五下午3点开产品评审会"}] }) # 输出可能是 EventDetails 对象 print(result["structured_response"]) # EventDetails(event_name='产品评审会', date='下周五下午3点')