跳转至

Structured Output

Abstract

Structured output 让 Agent 返回固定、可预测的数据结构,而不是让应用去解析自然语言。结果可以是 JSON object、Pydantic model、dataclass 或 TypedDict,应用可以直接使用。

在 Agent 场景中,LangChain 的 create_agent 通过 response_format 处理结构化输出。当模型生成符合 schema 的数据后,LangChain 会捕获、校验,并把结果放到最终 state 的 structured_response 字段中。

Tip

这一页关注 create_agent 中的 structured output。如果只是直接调用模型,可以看 Models 中的 Structured Output 部分。

Response Format

response_format 用来控制 Agent 如何返回结构化数据:

  • ToolStrategy[StructuredResponseT]:使用 tool calling 实现结构化输出。
  • ProviderStrategy[StructuredResponseT]:使用 provider 原生结构化输出能力。
  • type[StructuredResponseT]:直接传 schema 类型,由 LangChain 根据模型能力自动选择策略。
  • None:不显式请求结构化输出。
def create_agent(
    ...
    response_format: Union[
        ToolStrategy[StructuredResponseT],
        ProviderStrategy[StructuredResponseT],
        type[StructuredResponseT],
        None,
    ]
)

如果直接传入 schema 类型,LangChain 会自动选择:

  • 如果模型和 provider 支持原生结构化输出,使用 ProviderStrategy
  • 否则使用 ToolStrategy

最终结果通过 result["structured_response"] 获取。

ProviderStrategy

一些 provider 支持原生 structured output,例如 OpenAI、xAI、Gemini、Anthropic 等。可用时,这是更可靠的方式,因为 schema 约束由 provider API 执行。

ProviderStrategy 的核心参数:

class ProviderStrategy(Generic[SchemaT]):
    schema: type[SchemaT]
    strict: bool | None = None
  • schema:结构化输出格式。支持 Pydantic、dataclass、TypedDict、JSON Schema。
  • strict:是否启用更严格的 schema 遵循。部分 provider 支持,需要较新版本 LangChain。

Pydantic 示例

from pydantic import BaseModel, Field
from langchain.agents import create_agent

class ContactInfo(BaseModel):
    """Contact information for a person."""

    name: str = Field(description="The name of the person")
    email: str = Field(description="The email address of the person")
    phone: str = Field(description="The phone number of the person")

agent = create_agent(
    model="gpt-5.4",
    response_format=ContactInfo,
)

result = agent.invoke({
    "messages": [
        {
            "role": "user",
            "content": "Extract contact info from: John Doe, john@example.com, (555) 123-4567",
        }
    ]
})

print(result["structured_response"])
# ContactInfo(name='John Doe', email='john@example.com', phone='(555) 123-4567')

这里直接传 ContactInfo,如果模型支持 provider-native structured output,LangChain 会自动使用 ProviderStrategy

Dataclass 示例

from dataclasses import dataclass
from langchain.agents import create_agent

@dataclass
class ContactInfo:
    """Contact information for a person."""

    name: str
    email: str
    phone: str

agent = create_agent(
    model="gpt-5.4",
    tools=tools,
    response_format=ContactInfo,
)

result = agent.invoke({
    "messages": [
        {
            "role": "user",
            "content": "Extract contact info from: John Doe, john@example.com, (555) 123-4567",
        }
    ]
})

result["structured_response"]
# {'name': 'John Doe', 'email': 'john@example.com', 'phone': '(555) 123-4567'}

JSON Schema 示例

如果需要跨语言或更明确的 schema 控制,可以使用 JSON Schema。

from langchain.agents import create_agent
from langchain.agents.structured_output import ProviderStrategy

contact_info_schema = {
    "type": "object",
    "description": "Contact information for a person.",
    "properties": {
        "name": {"type": "string", "description": "The name of the person"},
        "email": {"type": "string", "description": "The email address of the person"},
        "phone": {"type": "string", "description": "The phone number of the person"},
    },
    "required": ["name", "email", "phone"],
}

agent = create_agent(
    model="gpt-5.4",
    tools=tools,
    response_format=ProviderStrategy(contact_info_schema),
)

如果 provider 支持原生 structured output,response_format=ProductReviewresponse_format=ProviderStrategy(ProductReview) 在功能上通常等价。若不支持,Agent 会回退到 tool calling strategy。

ToolStrategy

对于不支持 provider-native structured output 的模型,LangChain 可以通过 tool calling 实现相同目标。只要模型支持 tool calling,就可以使用这种方式。

ToolStrategy 的核心参数:

class ToolStrategy(Generic[SchemaT]):
    schema: type[SchemaT]
    tool_message_content: str | None
    handle_errors: Union[
        bool,
        str,
        type[Exception],
        tuple[type[Exception], ...],
        Callable[[Exception], str],
    ]
  • schema:结构化输出 schema。支持 Pydantic、dataclass、TypedDict、JSON Schema、Union types。
  • tool_message_content:结构化输出生成后写入 conversation history 的 ToolMessage 内容。
  • handle_errors:结构化输出校验失败时的错误处理策略,默认 True

Pydantic 示例

from typing import Literal
from pydantic import BaseModel, Field
from langchain.agents import create_agent
from langchain.agents.structured_output import ToolStrategy

class ProductReview(BaseModel):
    """Analysis of a product review."""

    rating: int | None = Field(description="The rating of the product", ge=1, le=5)
    sentiment: Literal["positive", "negative"] = Field(description="The sentiment of the review")
    key_points: list[str] = Field(description="The key points of the review. Lowercase, 1-3 words each.")

agent = create_agent(
    model="gpt-5.4",
    tools=tools,
    response_format=ToolStrategy(ProductReview),
)

result = agent.invoke({
    "messages": [
        {
            "role": "user",
            "content": "Analyze this review: 'Great product: 5 out of 5 stars. Fast shipping, but expensive'",
        }
    ]
})

result["structured_response"]
# ProductReview(rating=5, sentiment='positive', key_points=['fast shipping', 'expensive'])

Union Types

ToolStrategy 支持 Union schema,让模型根据上下文选择最合适的结构。

from typing import Literal, Union
from pydantic import BaseModel, Field
from langchain.agents import create_agent
from langchain.agents.structured_output import ToolStrategy

class ProductReview(BaseModel):
    """Analysis of a product review."""

    rating: int | None = Field(description="The rating of the product", ge=1, le=5)
    sentiment: Literal["positive", "negative"] = Field(description="The sentiment of the review")
    key_points: list[str] = Field(description="The key points of the review")

class CustomerComplaint(BaseModel):
    """A customer complaint about a product or service."""

    issue_type: Literal["product", "service", "shipping", "billing"] = Field(description="The type of issue")
    severity: Literal["low", "medium", "high"] = Field(description="The severity of the complaint")
    description: str = Field(description="Brief description of the complaint")

agent = create_agent(
    model="gpt-5.4",
    tools=tools,
    response_format=ToolStrategy(Union[ProductReview, CustomerComplaint]),
)

Union 很适合分类 + 抽取场景,例如同一段用户输入可能是产品评价,也可能是投诉。

自定义 ToolMessage 内容

tool_message_content 可以自定义结构化输出生成后,写入对话历史的 tool message 内容。

from typing import Literal
from pydantic import BaseModel, Field
from langchain.agents import create_agent
from langchain.agents.structured_output import ToolStrategy

class MeetingAction(BaseModel):
    """Action items extracted from a meeting transcript."""

    task: str = Field(description="The specific task to be completed")
    assignee: str = Field(description="Person responsible for the task")
    priority: Literal["low", "medium", "high"] = Field(description="Priority level")

agent = create_agent(
    model="gpt-5.4",
    tools=[],
    response_format=ToolStrategy(
        schema=MeetingAction,
        tool_message_content="Action item captured and added to meeting notes!",
    ),
)

如果不设置 tool_message_content,默认 ToolMessage 会包含类似 Returning structured response: {...} 的内容。

错误处理

使用 tool calling 生成结构化输出时,模型可能犯错,例如返回多个结构化输出,或字段不符合 schema。LangChain 可以把错误反馈成 ToolMessage,让模型自动重试。

Multiple Structured Outputs

当模型错误地调用多个结构化输出工具,而 Agent 只期望一个结果时,会触发 multiple structured outputs error。Agent 会把错误反馈给模型,并要求它修正。

from typing import Union
from pydantic import BaseModel, Field
from langchain.agents import create_agent
from langchain.agents.structured_output import ToolStrategy

class ContactInfo(BaseModel):
    name: str = Field(description="Person's name")
    email: str = Field(description="Email address")

class EventDetails(BaseModel):
    event_name: str = Field(description="Name of the event")
    date: str = Field(description="Event date")

agent = create_agent(
    model="gpt-5.4",
    tools=[],
    response_format=ToolStrategy(Union[ContactInfo, EventDetails]),
)

如果模型同时返回 ContactInfoEventDetails,Agent 会生成错误 ToolMessage,并促使模型只选择一个合适结构。

Schema Validation Error

当输出不符合 schema 约束时,会触发 validation error。例如 rating 要求 1 到 5,但模型返回 10。

from pydantic import BaseModel, Field
from langchain.agents import create_agent
from langchain.agents.structured_output import ToolStrategy

class ProductRating(BaseModel):
    rating: int | None = Field(description="Rating from 1-5", ge=1, le=5)
    comment: str = Field(description="Review comment")

agent = create_agent(
    model="gpt-5.4",
    tools=[],
    response_format=ToolStrategy(ProductRating),
    system_prompt="You are a helpful assistant that parses product reviews. Do not make any field or value up.",
)

默认情况下,Agent 会把校验错误写入 ToolMessage,让模型重试生成符合 schema 的结果。

Error Handling Strategies

handle_errors 可以控制错误处理方式:

ToolStrategy(
    schema=ProductRating,
    handle_errors="Please provide a valid rating between 1-5 and include a comment.",
)

常见取值:

  • True:捕获所有错误,并使用默认错误模板。
  • str:捕获所有错误,并使用这段自定义错误消息。
  • type[Exception]:只捕获指定异常类型。
  • tuple[type[Exception], ...]:只捕获多个指定异常类型。
  • Callable[[Exception], str]:用函数根据异常生成错误消息。
  • False:不自动重试,直接抛出异常。

自定义错误处理函数示例:

from langchain.agents.structured_output import (
    MultipleStructuredOutputsError,
    StructuredOutputValidationError,
)

def custom_error_handler(error: Exception) -> str:
    if isinstance(error, StructuredOutputValidationError):
        return "There was an issue with the format. Try again."
    if isinstance(error, MultipleStructuredOutputsError):
        return "Multiple structured outputs were returned. Pick the most relevant one."
    return f"Error: {str(error)}"

response_format = ToolStrategy(
    schema=Union[ContactInfo, EventDetails],
    handle_errors=custom_error_handler,
)

选择建议

优先级可以这样理解:

  1. 如果 provider 和模型支持原生 structured output,优先使用 ProviderStrategy 或直接传 schema 类型。
  2. 如果模型不支持原生结构化输出,但支持 tool calling,使用 ToolStrategy
  3. 如果需要多个可能 schema,让模型自行选择,使用 ToolStrategy(Union[...])
  4. 对生产系统,尽量用 Pydantic 定义 schema,因为它提供运行时校验和更明确的字段约束。

小结

Structured output 让 Agent 输出可直接被程序消费的数据。ProviderStrategy 更可靠,适合 provider 原生支持的模型;ToolStrategy 更通用,适合依赖 tool calling 的模型。无论使用哪种方式,schema 设计、字段描述和错误处理都会直接影响结构化输出的稳定性。