LangChain 结构化输出:让模型返回可校验对象,而不是“看起来像 JSON 的文本“

简介: 本文详解LangChain结构化输出最佳实践:用Pydantic Schema强制模型返回可校验类型对象,替代脆弱的“请输出JSON”提示词;对比Provider/Tool两种策略、四种Schema表达差异,深入handle_errors五种错误处理形态,倡导“规则写进Schema、错误交给框架”的工程化方法论,并附合同抽取完整案例。

摘要:让模型"输出 JSON"是新手第一反应,也是生产事故的高发区——格式错乱、字段缺失、超范围取值,下游系统根本接不住。LangChain 的结构化输出用 Pydantic Schema 约束模型行为:返回带类型的可校验对象,校验失败自动把错误喂回模型重试。本文讲清四种 Schema 表达的取舍(Pydantic/dataclass/TypedDict/JSON Schema)、ProviderStrategy 与 ToolStrategy 两种策略的选择、handle_errors 的五种配置形态,以及"规则写进 Schema、错误交给框架"的核心方法论,附合同抽取的完整可运行案例。

关键词:LangChain、结构化输出、Pydantic、with_structured_output、ToolStrategy、ProviderStrategy、handle_errors、数据抽取、大模型应用开发


一、"请返回 JSON 格式"为什么是错的

几乎每个新手都写过这样的提示词:

请从以下合同文本中提取信息,以 JSON 格式返回:
{"party_a": "甲方名称", "amount": 金额, "expire_date": "YYYY-MM-DD"}

然后惊喜地发现:模型返回的是"看起来像 JSON 的文本"——键名换了、金额带单位成了字符串、日期是"明年三月"、外层还包了段 markdown 代码块。你写正则解析、加"不要 markdown"的提示、再补"严格 JSON"……最后用 try/except 兜底,失败就丢弃。

问题的根源不在提示词,在方法:你在"请求"模型输出结构,而不是"约束"它。LLM 的本质是文本生成器,没有类型系统,你不用 Schema 约束它,它就永远可能跑偏。

结构化输出(Structured Output)就是这个约束:用 Schema 定义结构,框架强制模型遵守,返回可校验的类型化对象。

在这里插入图片描述


二、两种使用姿势:单模型与 Agent

姿势一:直接绑在模型上——适合简单的抽取任务:

from pydantic import BaseModel, Field

class ContractInfo(BaseModel):
    party_a: str = Field(description="甲方全称")
    party_b: str = Field(description="乙方全称")
    amount: float = Field(description="合同金额,单位万元")
    expire_date: str = Field(description="到期日期,格式 YYYY-MM-DD")

structured_llm = llm.with_structured_output(ContractInfo)
info = structured_llm.invoke(contract_text)

info.party_a      # 直接点取,类型安全
info.amount       # float,可直接入库

姿势二:挂在 Agent 的 response_format 上——适合需要工具配合的复杂任务:

from langchain.agents import create_agent
from langchain.agents.structured_output import ToolStrategy

agent = create_agent(
    model=llm,
    tools=[search_kb],
    response_format=ToolStrategy(ContractInfo),   # 结构化输出并入主循环
    system_prompt="你是合同审查助手。",
)
result = agent.invoke({
   "messages": [...]})
print(result["structured_response"])   # ContractInfo 对象

v1.0 的关键改进:Agent 的结构化输出并入主循环,不再需要额外一次模型调用——延迟和成本同时下降。


三、四种 Schema 表达,验证强度完全不同

Schema 类型 返回 校验 适用
Pydantic BaseModel 校验后的对象 ✅ 完整校验(ge/le/Literal 全生效) 生产首选
dataclass / TypedDict dict ⚠️ 轻量检查 简单场景
原始 JSON Schema dict dict ❌ 不做校验 仅定形状

一个容易被忽略的官方说明:原始 JSON Schema dict 不做校验——模型的工具调用参数原样返回,handle_errors 的自动重试形同虚设。想要校验和自动重试,Schema 必须写成 Pydantic(或 dataclass/TypedDict)。

Pydantic 的 Field 约束就是你的业务规则:

from typing import Literal

class ProductReview(BaseModel):
    rating: int | None = Field(description="1-5 分", ge=1, le=5)
    sentiment: Literal["positive", "negative", "neutral"]
    key_points: list[str] = Field(description="关键点,每条 1-3 个词")

ge=1, le=5 意味着模型返回 10 分会被直接打回;Literal 意味着情感只能是三个值之一。规则写进 Schema,而不是写在提示词里求模型遵守——这是结构化输出的核心方法论。

Field 的 description 同样重要:它既是给模型的提示(怎么填这个字段),也是校验的语义依据(什么算填对)。


四、两种策略:ProviderStrategy 与 ToolStrategy

LangChain 在底层用两种方式实现约束:

策略 机制 可靠性 适用
ProviderStrategy 厂商原生结构化输出 最高(厂商强制 Schema) OpenAI/Anthropic/Gemini 等原生支持时
ToolStrategy 借道工具调用强制形状 高(自动重试兜底) 任何支持工具调用的模型

直接传 Schema 类型时框架自动选择:模型原生支持就用 ProviderStrategy,否则回退 ToolStrategy。日常实践建议显式指定 ToolStrategy——跨厂商行为一致,且能配错误处理。

v1.0 还支持 Union 多 Schema:模型根据内容自选最合适的结构返回(比如一段文本可能是联系人信息也可能是日程),校验仍然完整。

在这里插入图片描述


五、handle_errors:校验失败的自动闭环

这是结构化输出最有生产价值的部分。模型返回 rating=10 违反 le=5 约束时会发生什么?

ToolStrategy 默认行为(handle_errors=True):

  1. Pydantic 校验失败;
  2. 框架把具体错误写进 ToolMessage 喂回模型:"rating 输入应为 ≤5";
  3. 模型基于错误提示重新生成;
  4. 循环直到通过校验。

这不是模糊的"再试一次",而是带具体错误信息的定向重试——模型知道错在哪,修正的成功率极高。

五种配置形态,按需选择:

# 1. 默认:捕获所有错误,用默认模板重试
ToolStrategy(ProductReview)

# 2. 自定义重试提示
ToolStrategy(ProductReview, handle_errors="请按 1-5 分给出 rating,key_points 用中文短句")

# 3. 只对特定异常重试,其余抛出
ToolStrategy(ProductReview, handle_errors=(ValueError, TypeError))

# 4. 回调函数:按错误类型定制消息
def err_handler(e: Exception) -> str:
    if isinstance(e, MultipleStructuredOutputsError):
        return "只返回一个结构化对象,不要同时调多个"
    return f"格式错误:{e}"

ToolStrategy(ProductReview, handle_errors=err_handler)

# 5. 关闭重试,异常直接抛给调用方
ToolStrategy(ProductReview, handle_errors=False)

另一类常见错误是 MultipleStructuredOutputsError:模型一次调了两个结构工具(比如同时返回联系人和日程),框架同样会以错误反馈提示模型只保留一个。生产上建议保持默认开启,但在重试次数上做好监控——重试风暴说明 Schema 设计或模型选择有问题。


六、完整案例:合同要素抽取

串起全部概念的最小生产案例:

from typing import Literal
from pydantic import BaseModel, Field
from langchain.chat_models import init_chat_model
from langchain.agents import create_agent
from langchain.agents.structured_output import ToolStrategy
from dotenv import load_dotenv
import os

load_dotenv(override=True)

llm = init_chat_model(
    "deepseek:deepseek-chat",
    api_key=os.getenv("DEEPSEEK_API_KEY"),
    base_url=os.getenv("DEEPSEEK_BASE_URL"),
    temperature=0.0,          # 抽取任务必用低温
)


class ContractInfo(BaseModel):
    """合同要素抽取结果"""
    party_a: str = Field(description="甲方全称,工商注册名称")
    party_b: str = Field(description="乙方全称,工商注册名称")
    amount: float = Field(description="合同金额,单位万元", gt=0)
    contract_type: Literal["采购", "销售", "服务", "租赁", "其他"]
    expire_date: str = Field(description="到期日期,格式 YYYY-MM-DD,原文没有则填 unknown")


extractor = create_agent(
    model=llm,
    tools=[],
    response_format=ToolStrategy(ContractInfo, handle_errors=True),
    system_prompt="你是合同审查助手。只依据原文抽取,不得编造任何字段。",
)

result = extractor.invoke(
    {
   "messages": [{
   "role": "user", "content": contract_text}]}
)
info = result["structured_response"]
print(info.party_a, info.amount, info.contract_type)   # 类型安全,直接入库

三个细节值得抄走:temperature=0.0(抽取要确定性);docstring 写给模型看(它是 Schema 的一部分);system_prompt 明确"不得编造"(配合 Schema 的 unknown 兜底值,防止模型硬填)。


七、给 Java / 大数据工程师的对照

  • Pydantic Schema ≈ DTO + Bean Validation:Field(ge=1, le=5) 就是 @Min(1) @Max(5),Literal 就是枚举校验——只是校验对象从用户输入换成了模型输出;
  • handle_errors 闭环 ≈ 消息重试队列:校验失败不是丢弃而是带着错误信息重新入队,直到成功或到达上限;
  • "规则写进 Schema" ≈ "约束写进 DDL":靠应用代码兜底和靠 Schema 约束的差别,数据库工程师最懂。

八、小结

结构化输出的方法论三句话:

  1. 别请求,要约束——提示词里的"请返回 JSON"换成 Schema 定义;
  2. 规则写进 Schema——范围、枚举、必填都是 Pydantic 约束,不是提示词里的恳求;
  3. 错误交给框架——handle_errors 自动闭环,比你自己写解析重试可靠得多。

凡是"模型输出要给程序消费"的场景——抽取、分类、路由、评估——这一套都是标配能力。从第一行 Schema 开始,把输出从"祈祷"变成"工程"。


本文基于 LangChain v1.0 官方文档与社区实践整理,代码在 Python 3.13 + langchain 1.2.0 环境下验证通过。

目录
相关文章
|
4天前
|
JavaScript API iOS开发
DeepSeek Harness下载、安装、配置一篇搞定(2026整理,送常用插件)
DeepSeek Harness(DSH)是DeepSeek于2026年开源的MIT协议AI Agent框架,支持插件化组装模型、工具、沙箱与UI,兼容主流大模型。本文提供免Node.js的桌面端安装包(Win/macOS),含详细图文教程及热门插件推荐。
|
云安全 安全 网络安全
|
5天前
|
iOS开发 Docker MacOS
【2026实测】Docker Desktop下载、安装、汉化、配置教程(附官网安装包)
Docker Desktop 是 Docker 官方推出的桌面容器管理工具,集成引擎、CLI、图形界面与 Kubernetes,支持 Win/macOS/Linux。2026 最新版 4.88.1 强化 ARM 原生运行、镜像加速及资源可视化,附中文汉化与国内镜像配置指南。
861 1
|
17天前
|
人工智能 API 开发工具
DeepSeek Harness 神插件:白嫖 13 家大厂模型
`dsh-our-free-model` 是 DeepSeek Harness 神级插件:零配置、零API Key、零账号,聚合13家大厂免费模型(含DeepSeek V4.1 Flash、Kimi K3等),支持本地网关、用量看板与自动降级,真正实现“白嫖即用”。
2971 49
|
11天前
|
JavaScript 安全 前端开发
Nodejs Brotli decoder 代码执行漏洞 (CVE-2026-111497)10629
Node.js发布安全更新修复Brotli解码器高危RCE漏洞(CVE-2026-111497),建议受影响版本用户立即升级。
2100 2
|
5天前
|
安全 算法 定位技术
《高性价比人生指南》完整原版:普通人用最少的资源,过好这一生的底层逻辑~How To Live Better
📖《高性价比人生指南》是一份基于实证、拒绝焦虑的生存手册,倡导以“人生时薪”为视角,重新计算时间、金钱与精力的成本与收益。它不承诺逆袭,只教人算清账:省下的钱若耗掉时间便亏;买体验比买物品更值;睡眠是最高杠杆的健康投资;关系贵在情绪价值而非表面热络;学习重在改变行为而非自我安慰。核心理念:用低成本、高回报的微小行动,换取长期的选择权——不是成为谁,而是掌控自己人生的主动权。
|
10天前
|
人工智能 JSON Linux
【保姆级】ComfyUI部署教程(下载+安装+配置+工作流,2026最新)
ComfyUI 是一款开源节点式AI生成工具,支持图像、视频、音频及3D内容创作。通过拖拽连线组合模型加载、提示词、采样等节点,构建可复用、易修改的工作流,兼容SD1.5、SDXL、Flux、Qwen Image等主流模型。
|
4天前
|
Linux 开发工具 iOS开发
Codex保姆级教程(下载+安装+登录+使用,含所有版本,2026最新)
Codex是OpenAI推出的开源AI编程智能体,本地运行,可理解整个代码库,支持自然语言指令改代码、执行命令、跑测试、修Bug,兼容Win/macOS/Linux。(239字)

热门文章

最新文章