LangChain 系列:Structured Output结构化输出与源码解析
01 先把问题讲透:为什么需要结构化输出?
大模型最擅长的是“像人一样说话”。这也是它最大的问题。人能看懂一段话,系统不一定能看懂。系统要的是字段、类型、枚举、范围、布尔值、数组。不是一段情绪饱满的解释。
比如用户问:“帮我看一下这个订单是不是要退款。”模型如果回答:“用户大概率是退款意图,订单号是 20260601,建议查询订单状态。”这对人来说没问题,对程序来说就很麻烦。后端要用正则抠订单号,要判断意图词,要猜置信度,还要防止模型换一种说法。
结构化输出解决的就是这个问题:你提前定义好 Schema,模型必须按照这个结构返回。后端拿到的不是作文,而是可以直接解析、校验、入库、调接口的对象。
02 一句话理解 Structured Output
Structured Output = 让模型按照指定 Schema 返回稳定、可校验、可编程使用的数据。
它的重点不是“返回 JSON”这么简单。真正的重点有三个:
第一,字段固定。比如 intent、order_id、need_tool、confidence。
第二,类型固定。比如字符串、数字、布尔值、列表、枚举。
第三,可以校验。比如 confidence 必须在 0 到 1 之间,rating 必须在 1 到 5 之间。
03 第一个例子:订单意图识别
先看一个非常贴近业务的例子。假设我们要从用户问题中抽取意图,判断是否需要调用工具。
from typing import Literal |
这段代码不是普通 Python 类。它是一个“数据合同”。你告诉模型:最终输出必须长成这个样子。
04 Agent 级用法:create_agent 的 response_format
在 LangChain 新版 Agent 体系里,最直接的方式是在 create_agent 里传 response_format。官方文档说明,Agent 的结构化输出最终会放到 final state 的 structured_response 字段里。
from langchain.agents import create_agent |
这里最关键的是 response_format=OrderIntent。你没有写“请返回 JSON”这类脆弱提示词,而是直接把 Schema 交给 LangChain。LangChain 会根据模型能力选择合适的结构化输出策略。
05 三种实现路线:不是所有模型都一样
结构化输出有三条主路。不同模型能力不同,走的路也不同。
第一条路是 ProviderStrategy。模型厂商原生支持结构化输出时,这是最稳的方式。Schema 会进入模型提供商的 API 参数,服务端直接约束输出。
第二条路是 ToolStrategy。模型不支持原生结构化输出,但支持工具调用时,LangChain 会把 Schema 包成一个“虚拟工具”。模型不是随便返回文本,而是“调用这个工具”,把字段填进工具参数里。
第三条路是 OutputParser。它更传统:Prompt 里告诉模型按 JSON 输出,模型生成文本后,Parser 再解析、校验。它适合简单场景,也适合非 Agent 链路。
方案 | 原理 | 优点 | 注意点 |
ProviderStrategy | 使用模型厂商原生结构化输出 | 可靠性高,输出更稳定 | 依赖模型能力,兼容性有限 |
ToolStrategy | 把 Schema 转成结构化输出工具 | 兼容多数支持 tool calling 的模型 | 可能多一轮工具调用和重试 |
OutputParser | 提示词要求 JSON,后处理解析 | 简单,适合轻量场景 | 模型不守格式时需要重试兜底 |
06 模型级用法:with_structured_output
如果你不想创建 Agent,只想让某个模型调用返回结构化对象,可以直接使用 with_structured_output。它适合分类、抽取、数据清洗、字段提取这类单步任务。
from langchain.chat_models import init_chat_model |
如果你希望同时拿到原始 AIMessage 和解析后的对象,可以设置 include_raw=True。这样方便记录 token、模型原始返回、解析异常。
structured_model = model.with_structured_output(OrderIntent, include_raw=True) |
07 PydanticOutputParser 的执行链路
先看最传统、也最容易理解的一条链路:PydanticOutputParser。它的核心思想是:先让模型输出 JSON 字符串,再把 JSON 转成 Pydantic 对象。
源码里,PydanticOutputParser 继承自 JsonOutputParser。也就是说,它不是自己从头解析 JSON,而是先复用 JsonOutputParser 把文本变成 dict,再调用 Pydantic 做模型校验。
# 伪源码:便于理解,保留核心逻辑 |
这条链路里有三个关键点。
第一,JsonOutputParser 负责把模型文本解析成 JSON 对象。
第二,PydanticOutputParser 负责把 JSON 对象变成 Pydantic 实例。
第三,get_format_instructions 会根据 Pydantic schema 生成格式提示,让模型知道该按什么结构输出。
08 JsonOutputParser 怎么处理 JSON?
JsonOutputParser 的核心是 parse_result。它先拿到模型输出文本,strip 去掉两边空白,然后调用 parse_json_markdown。为什么叫 markdown?因为模型经常会这样输出:
```json |
如果你直接 json.loads,这种带 ```json 代码块的内容会报错。LangChain 的解析函数会尽量从 Markdown 代码块里提取 JSON。
# 伪源码:便于理解 |
这也是为什么 OutputParser 比自己手写正则要靠谱:它不是只处理最理想的 JSON,而是兼容模型常见的 Markdown JSON 输出。
09 Agent 的 response_format 是怎么选策略的?
在 Agent 级别,response_format 有四种形式:ToolStrategy、ProviderStrategy、Schema 类型、None。直接传 Pydantic 类时,LangChain 会自动选择策略。
create_agent 里 response_format 的源码思路
def create_agent(..., response_format=None): |
直接传 Schema,等价于让框架自动判断:模型支持原生结构化输出,就走 ProviderStrategy;否则走 ToolStrategy。这个设计的好处是:业务代码不需要关心底层模型能力,只要定义好输出结构。
10 ToolStrategy 为什么能“逼”模型返回结构化数据?
ToolStrategy 的思路很巧。它把你的 Schema 变成一个工具。这个工具不是真的业务工具,不查数据库,也不调接口。它只是一个“结构化输出工具”。模型要完成任务,就必须调用这个工具,并把结果填到 args 里。
from langchain.agents.structured_output import ToolStrategy |
如果模型填错字段怎么办?比如 confidence 输出成 1.8,Pydantic 校验失败。ToolStrategy 默认会把错误信息作为 ToolMessage 放回对话,让模型再修一次。
ToolStrategy 通过 ToolMessage 反馈错误,让模型重试
11 ProviderStrategy 为什么更可靠?
ProviderStrategy 使用模型提供商的原生结构化输出能力。简单理解:不是你在 Prompt 里“求模型别乱写”,而是模型 API 本身提供了 response_format / JSON schema 这类能力。
from langchain.agents.structured_output import ProviderStrategy |
它的优点是可靠性高。缺点是依赖模型提供商能力。不同模型、不同 provider、不同版本支持程度并不完全一致。企业项目里不要盲目相信“都支持结构化输出”,一定要压测。
12 老方案:Prompt + PydanticOutputParser 仍然有价值
虽然新版更推荐 with_structured_output 或 Agent 的 response_format,但 OutputParser 不是过时废物。它适合简单任务,也适合你想完全控制 Prompt 的场景。
from langchain_core.output_parsers import PydanticOutputParser |
这条链路很直观:Prompt 负责告诉模型格式,Model 负责生成,Parser 负责解析和校验。
13 结构化输出在真实业务里的价值
结构化输出不是为了代码好看,而是为了把 AI 接进真实业务链路。
业务场景 | 结构化输出能做什么 |
智能客服 | 识别意图、抽取订单号、判断是否转人工、决定调用哪个工具。 |
股票分析助手 | 输出 action、risk_level、buy_zone、stop_loss、reason,方便前端展示和风控校验。 |
智能营销 | 输出人群标签、活动类型、文案风格、触达渠道、风险提醒。 |
文档抽取 | 从合同、公告、研报里抽取公司名、金额、日期、风险点。 |
工作流自动化 | 把模型结果变成状态机里的下一步动作。 |
14 生产环境的五个坑
坑一:以为“返回 JSON”就等于结构化输出:真正可靠的是 Schema + 校验 + 重试 + 兜底。
坑二:Schema 设计太复杂:层级太深、Union 太多、字段约束太细,都会增加模型失败概率。
坑三:不记录 raw 输出:上线排查时必须知道模型原始返回了什么。include_raw 或日志链路很重要。
坑四:所有模型都走同一策略:不同模型对 ProviderStrategy、ToolStrategy、json_schema、json_mode 支持不同。要按模型压测。
坑五:让模型直接决定高危动作:模型可以输出建议,真正执行退款、下单、转账、删数据,必须经过后端规则和人工确认。
15 企业级建议:怎么设计一个稳定的输出 Schema?
写 Schema 的时候,不要把它当成技术细节,要把它当成接口协议。一个好的 Schema 应该满足四点。
字段少而准。能不返回的字段就不要返回。
枚举明确。比如 action 只能是 buy、sell、hold、watch。
允许不确定。无法提取时用 None,不要逼模型瞎编。
业务可校验。金额、评分、置信度、日期,都要有后端校验。
from typing import Literal |
16 和 Java 后端怎么配合?
你如果是 Java 主服务 + Python AI 服务架构,推荐这样做:
Java 负责用户、权限、业务表、审计、最终执行。
Python AI 服务负责 LangChain、Prompt、模型调用、结构化输出。
Python 返回 JSON 给 Java,Java 再用 DTO 接住。
Java 再做一次 Bean Validation,不能完全相信模型。
高危动作只返回建议,不直接执行。
// Java DTO 示例 |
结构化输出的最终目的,就是让 AI 服务像普通微服务一样返回稳定 DTO。这样 Java 后端才能继续走权限、风控、事务、审计、报警。
17 总结
结构化输出让大模型从“聊天机器人”变成“系统接口”。
Pydantic 是最常用的数据模具,既能定义字段,也能做运行时校验。
Agent 级推荐使用 create_agent 的 response_format。
模型级简单任务可以使用 with_structured_output。
传统链路可以使用 Prompt + PydanticOutputParser。
ProviderStrategy 更稳定,但依赖模型原生能力。ToolStrategy 兼容性强,但依赖工具调用。
生产环境必须保留 raw 输出、错误重试、兜底策略、日志追踪和后端二次校验。
一句话收尾:没有结构化输出,AI 只是会说话;有了结构化输出,AI 才能真正接入业务系统。
内容来源:LangChain 系列:Structured Output结构化输出与源码解析:功能变化与行业影响解析_热闻岛
