14-输出解析器
14 - 输出解析器
本章课程目标:
- 理解**输出解析器(Output Parser)**是什么、为什么需要它,以及它在 Model I/O 中所处的位置。
- 会使用 StrOutputParser、JsonOutputParser 处理字符串与 JSON 输出。
- 理解 LangChain 官方的**结构化输出(Structured Output)**思路,掌握 TypedDict、Pydantic、JSON Schema 三种 schema 方式的差异。
学习建议: 这章要解决的是“模型说的话,程序怎么放心使用”。可以从 StrOutputParser 跑到 JsonOutputParser,再跑到 Pydantic,边跑边看哪里只是解析,哪里才发生校验。读完后能分清 Parser、Structured Output、TypedDict、Pydantic、JSON Schema 的边界,就已经抓住重点了。
官方文档与资源:详见 工具导航与参考资料索引 - 提示词与结构化输出。
1、输出解析器简介
本章对应 Model I/O 中的输出解析(Parse)部分:把模型的文本输出转成程序易用的结构化数据(如字符串、JSON、强类型对象)。与 第 13 章 提示词(Format(输入格式化))、第 11 章模型(Predict(模型调用))组合,即形成「输入 → 模型 → 输出解析」完整链路,第 15 章 LCEL 会用管道符将三者串成一条链。
1.1 定义
输出解析器(Output Parser),就是站在模型输出和程序最终要用的数据之间的一层转换器。
它的核心任务是:把模型返回的内容,从“面向人阅读”的文本,转成“面向程序处理”的结构化结果。
这条链可以拆成:
- Prompt 负责把输入整理好,告诉模型“你要怎么回答”;
- Model 负责生成结果,真正“生成回答”;
- Output Parser 负责把结果转成字符串、JSON 或对象,把这份回答“整理成程序好用的样子”。
- 第 15 章 LCEL 与链式调用 会把这条链真正串起来。

放到实际使用中,根据解析器类型不同,输出解析器通常会承担下面几类工作:
- 格式转换:把模型回答转成
str、dict、Pydantic 对象等。 - 结构约束:引导模型尽量按指定字段输出。
- 结果校验:检查字段类型、范围、必填项是否符合预期。
- 工程衔接:让模型输出能被数据库、接口、前端、工作流直接消费。
1.2 输出解析器的作用
初学大模型时,最容易把模型回答理解成“它回我一段文字就结束了”。但在真实项目里,模型输出通常还要继续流向别的程序模块,例如:
- 存进数据库;
- 返回给前端页面渲染;
- 交给下一个工作流节点;
- 作为 Agent、工具、接口调用的参数;
- 进入风控、审核、统计、报表逻辑。
这时,“一段自然语言”通常就不够用了。程序更希望拿到的是:
- 一个字符串;
- 一个 JSON 字典;
- 一个字段固定的对象;
- 一个带校验规则的强类型数据结构。
如果只靠 split()、正则、字符串截取去拆模型输出,代码往往会变得脆弱:模型多加一句解释、少一个逗号、换一个字段名,程序就可能出错。
1.3 常见输出解析器分类
| 解析器 | 最终结果 | 适用场景 | 说明 |
|---|---|---|---|
StrOutputParser |
字符串 str |
只需要展示文本,不需要拆字段 | 最简单,通常就是取出模型输出正文内容 |
JsonOutputParser |
Python dict / list |
希望模型返回 JSON,再交给程序继续处理 | 适合字段抽取、接口返回、工作流参数传递 |
PydanticOutputParser |
Pydantic 对象 | 需要强类型和运行时校验 | 适合对字段类型、长度、范围有明确要求的业务场景 |
1.4 常见结构化输出方案
除了“输出解析器”以外,LangChain 还提供了更进一步的**结构化输出(Structured Output)**能力。
它的重点不是“事后把结果解析出来”,而是“事前就规定模型应该按什么结构输出”。这里会经常出现一个词:schema。
schema 可以看成“数据结构说明书”或“输出格式规范”。
它描述的是:一份数据应该包含哪些字段、每个字段是什么类型、哪些字段必填,以及有时还会补充字段长度、取值范围、枚举值等约束。
例如,如果规定模型输出一个人物信息:
1 | { |
那么这个 schema 想表达的其实就是:
- 必须有
name字段; - 必须有
age字段; name应该是字符串;age应该是整数。
所以,结构化输出的本质,就是先定义 schema,再让模型按这个 schema 输出结果。
官方常见的结构化输出方案有三种:
| 方案 | 定义方式 | 最终结果 | 是否支持运行时校验 | 适用场景 |
|---|---|---|---|---|
TypedDict |
Python 标准库 typing.TypedDict |
dict |
否 | 只需要固定字段结构,不需要严格校验 |
Pydantic |
BaseModel + Field(...) |
Pydantic 对象 | 是 | 需要强类型、范围、长度、必填项校验 |
JSON Schema |
标准 JSON Schema 字典 | 通常为 dict |
视具体实现而定 | 需要跨语言、跨系统共享结构协议 |
在 LangChain 中,这些结构通常会和下面这种方式配合使用:
1 | model.with_structured_output(...) |
也就是说:
TypedDict/Pydantic/JSON Schema是“定义输出结构”的方式;with_structured_output(...)是“让模型按这个结构输出并自动解析”的入口。
1.5 输出解析器与结构化输出的关系
这两个概念很容易混,但可以这样区分:
- 输出解析器:更强调“模型已经输出了,我怎么把它转成程序可用的数据”;
- 结构化输出:更强调“在模型输出之前,我先规定好它应该长什么样”。
你也可以把它们理解成:
- 输出解析器偏后处理;
- 结构化输出偏前约束 + 自动解析。
但它们不是“必须二选一”的关系,也不是“必须同时使用”的关系。
常见情况主要有三种:
第一种:只用输出解析器
适合模型先正常输出,再由程序把结果转成字符串、JSON 或对象。
1 | from langchain_core.output_parsers import JsonOutputParser |
这种方式的特点很直接:先让模型生成结果,再由 parser 负责解析,所以它特别适合传统的 prompt | model | parser 链式调用。
第二种:只用结构化输出
适合模型本身支持 with_structured_output(...),LangChain 直接约束模型输出结构并自动解析。
1 | from typing import TypedDict |
这种方式不再需要单独手写 parser,而是由 with_structured_output(...) 统一完成“约束输出 + 自动解析”,更适合现代模型已经支持原生结构化输出的场景。
第三种:结构化输出 + 额外校验 / 处理
在更严格的工程场景中,也可以做结构化输出,再补额外处理逻辑。
1 | structured_model = model.with_structured_output(Person) |
这一类场景不一定非要再接一个 Output Parser,但常常会继续补:业务规则校验;字段清洗;入库前转换;接口返回前二次封装。
所以这里的关键结论是:
- 输出解析器和结构化输出可以独立使用;
- 也可以组合使用;
- 是否同时使用,取决于模型能力和业务要求。
它们的目标是一致的:让模型输出稳定地进入程序系统,而不是只停留在自然语言层面。
1.6 实际使用场景
| 场景 | 如果不做解析,常见问题 | 更合适的做法 |
|---|---|---|
| 聊天问答、文案润色、摘要展示 | 只需要显示文本,结构要求低 | StrOutputParser |
| 从文本里抽取字段,如“问题/答案”“时间/人物/事件” | 文本不稳定,后续逻辑难写 | JsonOutputParser |
| 给前端卡片、表单、列表页返回固定字段 | 字段缺失或字段名漂移会影响渲染 | with_structured_output(TypedDict) |
| 给数据库、审批流、订单系统写入严格数据 | 需要校验类型、范围、长度 | PydanticOutputParser 或 with_structured_output(Pydantic) |
| 与外部系统按统一协议对接 | 需要语言无关、协议明确 | JSON Schema |
2、输出解析器常用方法
2.1 动作一:解析输出
在 LangChain 里,解析器最常见的两种使用方式是:
parser.invoke(...):更偏 LangChain / Runnable 风格,适合和prompt | model | parser链式组合。本章案例大多是这种写法。parser.parse(text):更偏“我已经拿到一段纯文本了,现在只想解析这段文本”。
这两个方法的区别很简单:
- 如果你还在 LangChain 的调用链里,常用
invoke(...); - 如果你手里已经是
result.content这种字符串,常用parse(text)。
说明:很多入门教程会笼统说“
parse(result)用来解析模型结果”。从概念上这样理解没问题,但parse(...)更偏向解析文本字符串,而链式开发中我们常直接使用parser.invoke(result)。
2.2 动作二:给模型“格式说明”
解析器不只是“事后处理”,很多时候还会事前帮你约束模型输出。
最常见的方法就是:
1 | parser.get_format_instructions() |
它会返回一段格式说明文字,告诉模型:应该输出什么结构;有哪些字段;每个字段是什么类型;是否只能返回 JSON;是否不能加额外解释文字。
这段说明通常会被拼进 Prompt 中,让模型一开始就尽量按可解析格式输出,从而降低解析失败率。
3、常见解析器用法与案例
3.1 StrOutputParser
StrOutputParser 是 LangChain 里最简单的输出解析器。它做的事情非常直接:把模型返回内容取出来,当作字符串使用。它不做结构化解析,也不关心字段、键名、数据类型,只关心“把最终文本拿到手”。
它特别适合这些任务:
- 问答机器人直接展示文本;
- 文章摘要、标题生成、改写润色;
- 翻译、续写、营销文案;
- 只需要把结果显示到前端,不需要拆字段的场景。
一句话来说:如果下游只需要一段文本,而不是结构化字段,就用它。
【案例源码】案例与源码-2-LangChain框架/05_parser/StrOutputParserDemo.py
你可能会问:既然 AIMessage.content 也能直接拿文本,为什么还要 StrOutputParser?
原因主要有三点:
- 链式统一:后续可以自然写成
prompt | model | parser。 - 接口一致:今天是字符串,明天想切成 JSON,只要换 parser,不必重写整体结构。
- 可读性更强:代码语义变成“这里是输出解析环节”,对初学者和团队协作都更友好。
3.2 JsonOutputParser
JsonOutputParser(JSON 解析器)可以把模型输出中可解析的 JSON 内容,转换成程序可直接使用的结构化数据。
这里要注意:它不是“任意一段自然语言都能稳定转成 JSON”的魔法。Prompt 仍然需要提前说明输出格式;如果模型输出完全偏离 JSON,解析依然可能失败。
在项目开发里,JSON 是最常见的结构化数据格式。因为它:适合前后端传输;适合接口返回;适合数据库中间层处理;适合继续转成对象或表单数据。
所以很多时候,我们希望模型不要只回答一句话,而是直接返回:
1 | { |
或者:
1 | { |
这时就可以使用 JsonOutputParser。
3.2.1 用法一:直接在提示词里手写 JSON 要求
这是最直观的方式:在 Prompt 中明确写出“请返回 JSON,并包含哪些字段”。
【案例源码】案例与源码-2-LangChain框架/05_parser/JsonOutputParserDemo.py
这个案例适合帮助你理解最基础的工作流:
- Prompt 明确要求输出 JSON;
- 模型返回一段“长得像 JSON 的文本”;
JsonOutputParser把它解析成 Python 里的dict。
这种方式适合:字段很少;结构很简单;自己能一句话把格式说明白;主要目的是快速演示、快速打通流程。
3.2.2 用法二:用 get_format_instructions() 自动生成格式说明
当结构开始复杂时,手写“请返回 JSON,包含 a、b、c 字段……”就容易写漏、写乱、写不严谨。
这时更稳妥的办法是:让解析器自己生成格式说明,再拼进 Prompt。
【案例源码】案例与源码-2-LangChain框架/05_parser/JsonOutputParser_GetFormatInstructions.py
JsonOutputParser_GetFormatInstructions.py
这个案例里,用了一个 Person Pydantic 模型来描述 JSON 结构,再让:
1 | parser.get_format_instructions() |
自动生成一段规范的格式说明给模型看。
你要抓住的重点是:
JsonOutputParser本质上还是把结果解析成 JSON / dict;- 这里引入 Pydantic 模型,主要是为了更方便地描述输出结构;
- 如果你要的最终结果是 Pydantic 实例 而不是
dict,通常应该看下一节的PydanticOutputParser或with_structured_output(Pydantic模型)。
4、结构化输出
4.1 定义
所谓结构化输出(Structured Output),就是不满足于“模型输出一段 JSON 文本”,而是进一步要求:
- 输出必须符合某个明确 schema;
- LangChain 直接帮你解析成字典或对象;
- 必要时还能做字段验证。
也就是说,普通 JSON 解析更像是:
“请尽量按这个格式说。”
而结构化输出更像是:
“你必须按这个 schema 交付结果。”
LangChain 官方现在特别强调:很多现代模型已经支持原生结构化输出。这意味着,在支持的模型上,优先使用:
1 | model.with_structured_output(...) |
往往会比传统“手写 Prompt + Parser 解析”的方式更稳、更省心。
但输出解析器并没有过时,它仍然很有价值,尤其是在:使用不支持原生结构化输出的模型时;需要把结果继续做解析 / 清洗时;需要用 Pydantic 追加严格校验时;想把输出解析作为 LCEL 链的一环统一管理时。
4.2 常见方式
LangChain 官方文档里,结构化输出常见有三种 schema 方式:
| 方式 | 返回结果常见形态 | 是否自带运行时校验 | 适合什么场景 |
|---|---|---|---|
| TypedDict | dict |
否 | 结构清晰、字段固定,但校验要求不高 |
| Pydantic | Pydantic 对象 | 是 | 需要强类型、范围校验、长度校验、字段验证 |
| JSON Schema | dict |
取决于具体使用方式 | 需要与外部协议对齐、跨语言协作 |
这里还经常会配合一个写法:Annotated,它不是第四种 schema,而是给字段补充说明信息的一种方式。
5、TypedDict 与 Annotated
5.1 TypedDict:描述“这个字典长什么样”
TypedDict 来自 Python 标准库 typing,它的作用是:描述一个字典应该有哪些键、每个键是什么类型。它更像是一张结构说明书。
对 LangChain 来说,这张说明书很有用,因为它可以据此引导模型输出固定结构,再解析成 Python 字典。
但要特别注意:**TypedDict 不负责真正的运行时校验。**也就是说,它更偏“说明结构”,不是“强制验证”。
5.2 Annotated:给字段加解释说明
Annotated 也是 Python 标准库 typing 里的能力。它的作用不是“换一种类型”,而是:在原有类型上附加一段元数据或说明。
例如:
1 | Annotated[str, "动物名称"] |
它本质上还是 str,只是附带了一段“这是动物名称”的说明。LangChain 可以利用这些说明生成更清晰的 schema 描述,让模型更容易理解每个字段该填什么。
5.3 案例:TypedDict 版结构化输出
【案例源码】案例与源码-2-LangChain框架/05_parser/StructuredOutput_TypedDict.py
这个案例可以作为“现代 LangChain 结构化输出”的第一课:
- 用
TypedDict定义Animal与AnimalList; - 用
Annotated给字段添加说明; - 用
llm.with_structured_output(AnimalList)直接告诉模型输出目标结构; - 调用
.invoke(...)后直接得到解析好的dict。
这比“先要求 JSON,再手动解析”更像真实项目中的推荐写法。
5.4 案例:Annotated 只是描述,不是校验
【案例源码】案例与源码-2-LangChain框架/05_parser/AnnotatedTypedDict.py
这个案例解决的是一个容易混淆的问题:
1 | Age = Annotated[int, "年龄,范围0-150"] |
这句话并不意味着 Python 会自动帮你检查“年龄必须在 0 到 150 之间”。它只是说:这个字段的类型是 int;附带一段说明“年龄,范围 0-150”。如果没有额外的校验框架,这段说明不会自动变成校验规则。
所以在 TypedDict 场景下:Annotated 更像提示词增强器;它不是运行时验证器。
5.5 使用场景
TypedDict 适合这些情况:
- 前端页面需要固定字段,但字段值不用做复杂校验;
- 工作流节点之间传递结构化数据;
- 想要结构清晰,但不想引入太重的校验逻辑;
- 模型本身支持较好的结构化输出能力。
一句话总结:TypedDict 适合“我要稳定结构”,但暂时不要求“严格数据合法性校验”。
6、Pydantic:从结构说明到校验
6.1 定义
如果说 TypedDict 主要解决的是“这个字典应该长什么样”,那么 Pydantic 解决的是:“这个数据不仅要长得像,而且必须真的合法。”
Pydantic 是 Python 生态里很常用的数据校验库。它可以在创建对象时对字段做:类型检查;范围检查;长度检查;自定义校验。真实业务里只要涉及结构化数据,就经常会用到它。
6.2 案例:Annotated + Pydantic 触发校验
【案例源码】案例与源码-2-LangChain框架/05_parser/AnnotatedPydantic.py
这个案例和上一个 AnnotatedTypedDict.py 正好形成对照:
- 在 TypedDict 里,
Annotated[int, "年龄范围0-150"]只是描述; - 在 Pydantic 里,
Annotated[int, Field(ge=0, le=150)]会真正变成运行时校验。
记住一句话就够了:Annotated 本身不校验;真正发生校验的是 Pydantic 的 Field(...) 规则。
6.3 案例:PydanticOutputParser 的完整流程
【案例源码】案例与源码-2-LangChain框架/05_parser/StructuredOutput_Pydantic.py
这个案例体现了 Pydantic 路线最完整、最经典的工作流:
- 定义一个
ProductPydantic 模型; - 用
Field(...)给字段加说明; - 用
field_validator(...)写更细的校验逻辑; - 创建
PydanticOutputParser(pydantic_object=Product); - 用
get_format_instructions()生成格式说明; - 把说明拼入 Prompt;
- 模型输出后,解析成 Pydantic 实例。
这个流程比单纯的 JsonOutputParser 更强,因为它不只是“转成字典”,而是“转成一个经过校验的对象”。
6.4 使用场景
当你遇到下面这些需求时,通常就该优先考虑 Pydantic:
- 要把结果写进数据库,不能容忍字段类型乱掉;
- 要把模型输出接到订单、审批、风控、报表等业务系统;
- 某些字段必须满足范围、枚举、长度等限制;
- 希望一旦数据不合法,就立刻抛错,而不是悄悄放过。
一句话总结:
Pydantic 适合“我要的不只是结构化,而是可验证、可托底、可工程化的数据”。
7、JSON Schema:和外部协议对齐时很有用
7.1 定义
JSON Schema 是一种专门用来描述 JSON 结构和约束规则的标准。
它最大的特点是语言无关,前后端都能理解,也因此很适合跨团队、跨系统、跨语言协作。
LangChain 官方也把它列为结构化输出的三种主流方式之一。
7.2 使用场景
如果你的场景是:
- 后端和前端已经约定了一份 JSON 协议;
- 你的 Python 服务要和 Java、Go、Node 等系统对接;
- 想把数据结构标准化、文档化;
- 不想强依赖 Pydantic 或 Python 类型系统;
那么 JSON Schema 就会比 TypedDict 更通用。
7.3 和 TypedDict、Pydantic 的区别
可以简单这样记:
- TypedDict:最像 Python 内部的“结构说明书”。
- Pydantic:最像 Python 内部的“结构 + 校验模型”。
- JSON Schema:最像系统之间共享的“协议文档”。
8、实际开发选择
这一节给出本章的落地选择。
8.1 从简单到严格的选择路径
你可以按下面这条路径做选择:
- 只需要文本展示
用StrOutputParser - 需要简单 JSON 字段
用JsonOutputParser - 需要固定结构,且模型支持结构化输出
优先with_structured_output(TypedDict) - 需要严格校验
用Pydantic+PydanticOutputParser,或with_structured_output(Pydantic模型) - 需要跨语言 / 外部协议对齐
用JSON Schema
落到工程选型上,可以记住这句话:普通文本用 Parser,固定结构优先 with_structured_output,强校验上 Pydantic,Agent 最终结果再看 response_format。
8.2 一个更贴近项目落地的选型表
| 需求 | 推荐方案 |
|---|---|
| 聊天机器人回复、文章摘要、翻译 | StrOutputParser |
| 抽取“问题-答案”“时间-人物-事件”等简单字段 | JsonOutputParser |
| 给前端接口返回一个固定字段字典 | with_structured_output(TypedDict) |
| 给数据库、业务系统写入高可靠数据 | with_structured_output(Pydantic) / PydanticOutputParser |
| 与其它语言系统共享统一数据协议 | JSON Schema |
| 输出格式很特殊,内置解析器覆盖不了 | 自定义 BaseOutputParser |
8.3 官方建议
LangChain 官方当前的整体方向是:
- 如果模型原生支持结构化输出,优先用原生能力;
- 如果模型不支持,或你还需要额外解析/校验,再使用输出解析器。
8.4 自定义解析器
大多数场景不需要自己写解析器。优先级一般是:先看 with_structured_output(...),再看内置 Parser,最后才考虑自定义。
但有些输出确实比较特殊,例如模型返回的是一段固定格式的清单、日志、命令块,既不是标准 JSON,也不适合上 Pydantic。这时可以继承 BaseOutputParser,只实现最关键的 parse() 方法。
1 | from langchain_core.output_parsers import BaseOutputParser |
它适合做“很轻的格式清洗”。如果结果后面要进数据库、审批流、自动执行工具,仍然建议继续接 Pydantic 或业务校验,不要只靠字符串拆分。
9、常见误区与排错建议
9.1 误区一:能解析成 JSON,就说明数据没问题
不对。JSON 只说明“格式像样”,不说明“业务正确”。
比如用户年龄输出成了 999,它依然是合法 JSON,但显然不合理。
9.2 误区二:Annotated 自带校验
不对。Annotated 只是附加元数据。
真正让“范围、长度、约束”生效的是 Pydantic 的 Field(...)、validator 等机制。
9.3 误区三:输出解析器能完全代替 Prompt 设计
不对。如果 Prompt 没说清楚字段含义、输出边界、不要加额外说明,解析器很可能仍然失败。
9.4 误区四:所有项目都应该直接上 Pydantic
也不一定。如果你只是做文本展示,或者只是一个简单 JSON 演示,直接上 Pydantic 可能过重。
工程化不是“越复杂越好”,而是“够用且稳定”。
9.5 误区五:学了 Parser 就和结构化输出是两套体系
不是。它们本质上解决的是同一个问题:让模型输出可被程序稳定消费。
区别只是在于:
- 有的方式偏“后处理解析”;
- 有的方式偏“原生结构约束 + 自动解析”;
- 有的方式还能进一步做严格校验。
章节思考题:
为什么“模型输出了 JSON”不等于“程序可以放心使用”?
参考思路: JSON 语法正确只是第一步,字段是否齐全、类型是否正确、值是否符合业务规则还需要校验。解析解决“能读”,校验解决“能不能信”。
什么时候
StrOutputParser就够了,什么时候应该上 Pydantic?参考思路: 只需要纯文本展示时,字符串解析足够;结果要进入数据库、接口、流程分支或自动执行时,应考虑 Pydantic 这类强校验。越靠近业务动作,越不能只靠自然语言。
Structured Output 和 Output Parser 的关系应该怎么理解?
参考思路: Structured Output 更偏让模型按结构生成,Parser 更偏在输出后解析和转换。两者可以配合使用:前面约束生成,后面兜底解析和校验。
TypedDict、Pydantic、JSON Schema 的选择取决于什么?
参考思路: TypedDict 适合轻量类型说明,Pydantic 适合 Python 内部强校验和错误提示,JSON Schema 适合跨语言、接口协议或外部系统对齐。不是越重越好,要看边界在哪里。
本章小结:
- 输出解析器是 Model I/O 里的 Parse 环节,负责把模型输出转成程序可直接使用的数据。
- 本章的重点不在于背 API,而在于建立工程认知:大模型输出往往还要继续进入程序链路,因此必须结构化。
- StrOutputParser 适合纯文本场景;JsonOutputParser 适合快速得到
dict;PydanticOutputParser 适合强类型、强校验场景。 - 结构化输出是本章真正的重点。LangChain 官方常见的三种 schema 方式是:TypedDict、Pydantic、JSON Schema。
- TypedDict 适合描述结构;Pydantic 适合描述结构并做运行时校验;Annotated 主要用于补充字段说明,本身不是校验器。
- 现代 LangChain 更推荐在支持的模型上优先考虑
with_structured_output(...),输出解析器则继续在兼容性、后处理、附加校验等方面发挥作用。 - 从掌握结果看,学完本章后,你至少应该:能分清 输出解析器 和 结构化输出 的关系,知道它们都在解决“模型结果如何稳定交给程序”这个问题;知道
StrOutputParser、JsonOutputParser、PydanticOutputParser三类方案各自适用什么场景;理解TypedDict、Annotated、Pydantic、JSON Schema在“描述结构”和“运行时校验”上的边界。
建议下一步: 学习 第 15 章 LCEL 与链式调用,把本章的解析器与 第 13 章 的 Prompt、第 11 章 的 Model 串成真正的 prompt | model | parser 链。