Skip to content

第 6 章 输出解析器 ​


本章课程目标:

  • 理解**输出解析器(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、强类型对象)。与 第 5 章 提示词(Format(输入格式化))、第 3 章模型(Predict(模型调用))组合,即形成「输入 → 模型 → 输出解析」完整链路,第 7 章 LCEL 会用管道符将三者串成一条链。

1.1 定义 ​

输出解析器(Output Parser),就是站在模型输出和程序最终要用的数据之间的一层转换器。

它的核心任务是:把模型返回的内容,从“面向人阅读”的文本,转成“面向程序处理”的结构化结果。

这条链可以拆成:

  • Prompt 负责把输入整理好,告诉模型“你要怎么回答”;
  • Model 负责生成结果,真正“生成回答”;
  • Output Parser 负责把结果转成字符串、JSON 或对象,把这份回答“整理成程序好用的样子”。
  • 第 15 章 LCEL 与链式调用 会把这条链真正串起来。

输出解析器在 Model I/O 中的位置:承接模型文本输出,解析为程序可用的结构化数据

放到实际使用中,根据解析器类型不同,输出解析器通常会承担下面几类工作:

  • 格式转换:把模型回答转成 str、dict、Pydantic 对象等。
  • 结构约束:引导模型尽量按指定字段输出。
  • 结果校验:检查字段类型、范围、必填项是否符合预期。
  • 工程衔接:让模型输出能被数据库、接口、前端、工作流直接消费。

1.2 输出解析器的作用 ​

初学大模型时,最容易把模型回答理解成“它回我一段文字就结束了”。但在真实项目里,模型输出通常还要继续流向别的程序模块,例如:

  • 存进数据库;
  • 返回给前端页面渲染;
  • 交给下一个工作流节点;
  • 作为 Agent、工具、接口调用的参数;
  • 进入风控、审核、统计、报表逻辑。

这时,“一段自然语言”通常就不够用了。程序更希望拿到的是:

  • 一个字符串;
  • 一个 JSON 字典;
  • 一个字段固定的对象;
  • 一个带校验规则的强类型数据结构。

如果只靠 split()、正则、字符串截取去拆模型输出,代码往往会变得脆弱:模型多加一句解释、少一个逗号、换一个字段名,程序就可能出错。

1.3 常见输出解析器分类 ​

解析器最终结果适用场景说明
StrOutputParser字符串 str只需要展示文本,不需要拆字段最简单,通常就是取出模型输出正文内容
JsonOutputParserPython dict / list希望模型返回 JSON,再交给程序继续处理适合字段抽取、接口返回、工作流参数传递
PydanticOutputParserPydantic 对象需要强类型和运行时校验适合对字段类型、长度、范围有明确要求的业务场景

1.4 常见结构化输出方案 ​

除了“输出解析器”以外,LangChain 还提供了更进一步的**结构化输出(Structured Output)**能力。
它的重点不是“事后把结果解析出来”,而是“事前就规定模型应该按什么结构输出”。这里会经常出现一个词:schema。

schema 可以看成“数据结构说明书”或“输出格式规范”。 它描述的是:一份数据应该包含哪些字段、每个字段是什么类型、哪些字段必填,以及有时还会补充字段长度、取值范围、枚举值等约束。

例如,如果规定模型输出一个人物信息:

json
{
  "name": "张三",
  "age": 20
}

那么这个 schema 想表达的其实就是:

  • 必须有 name 字段;
  • 必须有 age 字段;
  • name 应该是字符串;
  • age 应该是整数。

所以,结构化输出的本质,就是先定义 schema,再让模型按这个 schema 输出结果。

官方常见的结构化输出方案有三种:

方案定义方式最终结果是否支持运行时校验适用场景
TypedDictPython 标准库 typing.TypedDictdict否只需要固定字段结构,不需要严格校验
PydanticBaseModel + Field(...)Pydantic 对象是需要强类型、范围、长度、必填项校验
JSON Schema标准 JSON Schema 字典通常为 dict视具体实现而定需要跨语言、跨系统共享结构协议

在 LangChain 中,这些结构通常会和下面这种方式配合使用:

python
model.with_structured_output(...)

也就是说:

  • TypedDict / Pydantic / JSON Schema 是“定义输出结构”的方式;
  • with_structured_output(...) 是“让模型按这个结构输出并自动解析”的入口。

1.5 输出解析器与结构化输出的关系 ​

这两个概念很容易混,但可以这样区分:

  • 输出解析器:更强调“模型已经输出了,我怎么把它转成程序可用的数据”;
  • 结构化输出:更强调“在模型输出之前,我先规定好它应该长什么样”。

你也可以把它们理解成:

  • 输出解析器偏后处理;
  • 结构化输出偏前约束 + 自动解析。

但它们不是“必须二选一”的关系,也不是“必须同时使用”的关系。

常见情况主要有三种:

第一种:只用输出解析器 ​

适合模型先正常输出,再由程序把结果转成字符串、JSON 或对象。

python
from langchain_core.output_parsers import JsonOutputParser

parser = JsonOutputParser()

result = model.invoke("请用 JSON 返回:name、age")
data = parser.invoke(result)
print(data)

这种方式的特点很直接:先让模型生成结果,再由 parser 负责解析,所以它特别适合传统的 prompt | model | parser 链式调用。

第二种:只用结构化输出 ​

适合模型本身支持 with_structured_output(...),LangChain 直接约束模型输出结构并自动解析。

python
from typing import TypedDict


class Person(TypedDict):
    name: str
    age: int


structured_model = model.with_structured_output(Person)
result = structured_model.invoke("请返回一个人物信息")
print(result)

这种方式不再需要单独手写 parser,而是由 with_structured_output(...) 统一完成“约束输出 + 自动解析”,更适合现代模型已经支持原生结构化输出的场景。

第三种:结构化输出 + 额外校验 / 处理 ​

在更严格的工程场景中,也可以做结构化输出,再补额外处理逻辑。

python
structured_model = model.with_structured_output(Person)
result = structured_model.invoke("请返回一个人物信息")

# 这里可以继续做业务校验、入库前清洗、字段转换等处理
print(result)

这一类场景不一定非要再接一个 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 动作二:给模型“格式说明” ​

解析器不只是“事后处理”,很多时候还会事前帮你约束模型输出。

最常见的方法就是:

python
parser.get_format_instructions()

它会返回一段格式说明文字,告诉模型:应该输出什么结构;有哪些字段;每个字段是什么类型;是否只能返回 JSON;是否不能加额外解释文字。

这段说明通常会被拼进 Prompt 中,让模型一开始就尽量按可解析格式输出,从而降低解析失败率。


3、常见解析器用法与案例 ​

3.1 StrOutputParser ​

StrOutputParser 是 LangChain 里最简单的输出解析器。它做的事情非常直接:把模型返回内容取出来,当作字符串使用。它不做结构化解析,也不关心字段、键名、数据类型,只关心“把最终文本拿到手”。

它特别适合这些任务:

  • 问答机器人直接展示文本;
  • 文章摘要、标题生成、改写润色;
  • 翻译、续写、营销文案;
  • 只需要把结果显示到前端,不需要拆字段的场景。

一句话来说:如果下游只需要一段文本,而不是结构化字段,就用它。

【案例源码】案例与源码-2-LangChain框架/05_parser/StrOutputParserDemo.py

python
"""
【案例】StrOutputParser 字符串解析器

对应教程章节:第 6 章 - 输出解析器 → 2、常用输出解析器用法

知识点速览:
一、为什么需要「输出解析器」?
  - 大模型返回的通常是 AIMessage 等对象,直接拿到的可能是整块对象,不方便当字符串用。
  - 输出解析器的作用:把「模型输出」转成程序里好用的形式(如 str、dict、强类型对象)。

二、StrOutputParser 是什么?
  - LangChain 里最简单的解析器:从模型返回中取出 content 字段,转成 纯字符串,不做 JSON 等结构解析。
  - 适合:只关心「模型说了什么话」、不需要拆成字段的场景。

三、典型流程:构造 Prompt → model.invoke(prompt) 得到 result → parser.invoke(result) 得到 str。
"""

from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
import os
from dotenv import load_dotenv
from langchain.chat_models import init_chat_model
from loguru import logger

load_dotenv(encoding="utf-8")

# 构造对话模板(与第 5 章 ChatPromptTemplate 用法一致)
chat_prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "你是一个{role},请简短回答我提出的问题"),
        ("human", "请回答:{question}"),
    ]
)

# 填充占位符,得到消息列表,供模型使用
prompt = chat_prompt.invoke(
    {"role": "AI助手", "question": "什么是LangChain,简洁回答100字以内"}
)
logger.info(prompt)

# 初始化大模型(需配置 API Key 等)
model = init_chat_model(
    model="qwen-plus",
    model_provider="openai",
    api_key=os.getenv("aliQwen-api"),
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)

# 调用模型:传入 prompt,得到的是 AIMessage 等对象(原始输出)
result = model.invoke(prompt)
logger.info(f"模型原始输出:\n{result}")

# 创建字符串解析器:只做「从 result 里取 content 转成 str」
# 单条 AIMessage 时确实等价于 result.content;用解析器的好处:可链式组合(prompt | model | parser)、
# 流式时统一处理 chunk、多条消息时按约定取最后一条等,且与 JsonOutputParser 等接口一致便于替换。
parser = StrOutputParser()

# 解析:parser.invoke(result) 等价于从 result 中取 content,得到纯字符串
response = parser.invoke(result)
logger.info(f"解析后的结构化结果:\n{response}")
logger.info("\n")
logger.info(
    f"结果类型: {type(response)}"
)  # 输出示例: <class 'langchain_core.messages.base.TextAccessor'>

"""
【输出示例】
2026-02-27 14:32:50.639 | INFO     | __main__:<module>:37 - messages=[SystemMessage(content='你是一个AI助手,请简短回答我提出的问题', additional_kwargs={}, response_metadata={}), HumanMessage(content='请回答:什么是LangChain,简洁回答100字以内', additional_kwargs={}, response_metadata={})]
2026-02-27 14:32:54.776 | INFO     | __main__:<module>:49 - 模型原始输出:
content='LangChain 是一个开源框架,用于构建基于大语言模型(LLM)的应用程序。它提供模块化组件(如链、代理、记忆、工具等),支持提示工程、数据检索增强(RAG)、多步推理和外部工具调用,简化 LLM 应用的开发与集成。' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 67, 'prompt_tokens': 38, 'total_tokens': 105, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_provider': 'openai', 'model_name': 'qwen-plus', 'system_fingerprint': None, 'id': 'chatcmpl-7f8b2cc0-7b6a-98c7-8180-4d8fe4433fd3', 'finish_reason': 'stop', 'logprobs': None} id='lc_run--019c9dcc-cdc5-71d3-9b0e-8ca64d9ae28d-0' tool_calls=[] invalid_tool_calls=[] usage_metadata={'input_tokens': 38, 'output_tokens': 67, 'total_tokens': 105, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}}
2026-02-27 14:32:54.777 | INFO     | __main__:<module>:56 - 解析后的结构化结果:
LangChain 是一个开源框架,用于构建基于大语言模型(LLM)的应用程序。它提供模块化组件(如链、代理、记忆、工具等),支持提示工程、数据检索增强(RAG)、多步推理和外部工具调用,简化 LLM 应用的开发与集成。
2026-02-27 14:32:54.777 | INFO     | __main__:<module>:57 -
"""

你可能会问:既然 AIMessage.content 也能直接拿文本,为什么还要 StrOutputParser?

原因主要有三点:

  • 链式统一:后续可以自然写成 prompt | model | parser。
  • 接口一致:今天是字符串,明天想切成 JSON,只要换 parser,不必重写整体结构。
  • 可读性更强:代码语义变成“这里是输出解析环节”,对初学者和团队协作都更友好。

3.2 JsonOutputParser ​

JsonOutputParser(JSON 解析器)可以把模型输出中可解析的 JSON 内容,转换成程序可直接使用的结构化数据。

这里要注意:它不是“任意一段自然语言都能稳定转成 JSON”的魔法。Prompt 仍然需要提前说明输出格式;如果模型输出完全偏离 JSON,解析依然可能失败。

在项目开发里,JSON 是最常见的结构化数据格式。因为它:适合前后端传输;适合接口返回;适合数据库中间层处理;适合继续转成对象或表单数据。

所以很多时候,我们希望模型不要只回答一句话,而是直接返回:

json
{
  "q": "...",
  "a": "..."
}

或者:

json
{
  "time": "...",
  "person": "...",
  "event": "..."
}

这时就可以使用 JsonOutputParser。

3.2.1 用法一:直接在提示词里手写 JSON 要求 ​

这是最直观的方式:在 Prompt 中明确写出“请返回 JSON,并包含哪些字段”。

【案例源码】案例与源码-2-LangChain框架/05_parser/JsonOutputParserDemo.py

python
"""
【案例】JsonOutputParser 基础用法:在提示词里直接要求返回 JSON

对应教程章节:第 6 章 - 输出解析器 → 2、常用输出解析器用法

知识点速览:
一、JsonOutputParser 是什么?
  - 把模型的自由文本输出解析成结构化 JSON(Python 里就是 dict / list)。
  - 模型若返回的是「像 JSON 的文本」,解析器会尝试解析,得到字典后便于后续逻辑、入库或展示。

二、本案例做法:在 system 提示词里手写要求,例如「结果返回 json 格式,q 字段表示问题,a 字段表示答案」。
  - 不依赖 get_format_instructions(),适合结构简单、自己说清楚就够用的场景。
  - 进阶做法见 JsonOutputParser_GetFormatInstructions.py:用 get_format_instructions() 生成格式说明再拼进提示词。
"""

from langchain_core.output_parsers import JsonOutputParser
from langchain_core.prompts import ChatPromptTemplate
import os
from dotenv import load_dotenv
from langchain.chat_models import init_chat_model
from loguru import logger

load_dotenv(encoding="utf-8")

# 在系统消息里直接写明:返回 json,且包含 q(问题)、a(答案)字段
chat_prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            "你是一个{role},请简短回答我提出的问题,结果返回json格式,q字段表示问题,a字段表示答案。",
        ),
        ("human", "请回答:{question}"),
    ]
)

prompt = chat_prompt.invoke(
    {"role": "AI助手", "question": "什么是LangChain,简洁回答100字以内"}
)
logger.info(prompt)

model = init_chat_model(
    model="qwen-plus",
    model_provider="openai",
    api_key=os.getenv("aliQwen-api"),
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)

# 模型返回的可能是带 JSON 的文本
result = model.invoke(prompt)
logger.info(f"模型原始输出:\n{result}")

print("*" * 60)

# 创建 JSON 解析器(不绑 Pydantic 时,解析结果为 dict/list)
parser = JsonOutputParser()
# 尝试从 result 的 content 中解析出 JSON
response = parser.invoke(result)
logger.info(f"解析后的结构化结果:\n{response}")
logger.info("\n")
logger.info(f"结果类型: {type(response)}")  # <class 'dict'>


"""
【输出示例】
2026-02-26 12:00:54.190 | INFO     | __main__:<module>:29 - messages=[SystemMessage(content='你是一个AI助手,请简短回答我提出的问题,结果返回json格式,q字段表示问题,a字段表示答案。', additional_kwargs={}, response_metadata={}), HumanMessage(content='请回答:什么是LangChain,简洁回答100字以内', additional_kwargs={}, response_metadata={})]
2026-02-26 12:00:56.500 | INFO     | __main__:<module>:40 - 模型原始输出:
content='{"q": "什么是LangChain,简洁回答100字以内", "a": "LangChain是一个开源框架,用于构建基于大语言模型(LLM)的应用程序,支持链式调用、数据连接、记忆管理与工具集成,简化提示工程、RAG和Agent开发。"}' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 65, 'prompt_tokens': 54, 'total_tokens': 119, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_provider': 'openai', 'model_name': 'qwen-plus', 'system_fingerprint': None, 'id': 'chatcmpl-f940cf51-c792-904e-9ed9-d2808c5ab264', 'finish_reason': 'stop', 'logprobs': None} id='lc_run--019c981b-56ff-73b0-92fd-48ed999ee369-0' tool_calls=[] invalid_tool_calls=[] usage_metadata={'input_tokens': 54, 'output_tokens': 65, 'total_tokens': 119, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}}
************************************************************
2026-02-26 12:00:56.501 | INFO     | __main__:<module>:48 - 解析后的结构化结果:
{'q': '什么是LangChain,简洁回答100字以内', 'a': 'LangChain是一个开源框架,用于构建基于大语言模型(LLM)的应用程序,支持链式调用、数据连接、记忆管理与工具集成,简化提示工程、RAG和Agent开发。'}
2026-02-26 12:00:56.501 | INFO     | __main__:<module>:49 -
"""

# 2026-02-26 12:00:56.501 | INFO     | __main__:<module>:50 - 结果类型: <class 'dict'>

这个案例适合帮助你理解最基础的工作流:

  1. Prompt 明确要求输出 JSON;
  2. 模型返回一段“长得像 JSON 的文本”;
  3. JsonOutputParser 把它解析成 Python 里的 dict。

这种方式适合:字段很少;结构很简单;自己能一句话把格式说明白;主要目的是快速演示、快速打通流程。

3.2.2 用法二:用 get_format_instructions() 自动生成格式说明 ​

当结构开始复杂时,手写“请返回 JSON,包含 a、b、c 字段……”就容易写漏、写乱、写不严谨。

这时更稳妥的办法是:让解析器自己生成格式说明,再拼进 Prompt。

【案例源码】案例与源码-2-LangChain框架/05_parser/JsonOutputParser_GetFormatInstructions.py

python
"""
【案例】JsonOutputParser + get_format_instructions():用格式说明引导模型输出

对应教程章节:第 6 章 - 输出解析器 → 2、常用输出解析器用法

知识点速览:
一、get_format_instructions() 做什么?
  - 返回一段格式说明字符串,描述「希望模型输出成什么样子」(例如 JSON 里有哪些键、类型是什么)。
  - 把这段说明拼进 Prompt(如放在 {format_instructions} 占位符),模型更容易输出可被解析的 JSON,减少格式错误。

二、本案例做法:用 Pydantic 模型 Person 定义「时间、人物、事件」结构 → 用 JsonOutputParser(pydantic_object=Person) 创建解析器 → 用 parser.get_format_instructions() 得到说明 → 把说明拼进 human 消息,再调用模型与解析器。
  - 模型会按 Person 的 schema 生成 JSON;当前 JsonOutputParser 解析结果为 dict(若要 Pydantic 实例与完整校验,见 PydanticOutputParser / StructuredOutput_Pydantic.py)。
"""

from langchain_core.output_parsers import JsonOutputParser
from langchain_core.prompts import ChatPromptTemplate
import os
from dotenv import load_dotenv
from langchain.chat_models import init_chat_model
from loguru import logger
from pydantic import BaseModel, Field

load_dotenv(encoding="utf-8")


class Person(BaseModel):
    """定义一条「新闻」的结构:时间、人物、事件。用于约束模型输出的 JSON 形状。"""

    time: str = Field(description="时间")
    person: str = Field(description="人物")
    event: str = Field(description="事件")


# 绑定 Pydantic 模型:主要驱动 get_format_instructions() 的 schema;invoke 后得到 dict
parser = JsonOutputParser(pydantic_object=Person)

# 获取「格式说明」:描述 Person 各字段,便于拼进提示词让模型按此输出
format_instructions = parser.get_format_instructions()

# 在 human 消息里加入 {format_instructions},模型会看到「请按如下格式输出 JSON …」
chat_prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "你是一个AI助手,你只能输出结构化JSON数据。"),
        ("human", "请生成一个关于{topic}的新闻。{format_instructions}"),
    ]
)

# 填 topic 和 format_instructions,得到消息列表
prompt = chat_prompt.format_messages(
    topic="小米su7跑车", format_instructions=format_instructions
)
logger.info(prompt)

model = init_chat_model(
    model="qwen-plus",
    model_provider="openai",
    api_key=os.getenv("aliQwen-api"),
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)

result = model.invoke(prompt)
logger.info(f"模型原始输出:\n{result}")

# 用同一解析器解析,得到符合 Person 结构的数据(dict,或可转成 Person 实例)
response = parser.invoke(result)
logger.info(f"解析后的结构化结果:\n{response}")
logger.info(f"结果类型: {type(response)}")

"""
【输出示例】
2026-02-27 15:01:43.675 | INFO     | __main__:<module>:47 - [SystemMessage(content='你是一个AI助手,你只能输出结构化JSON数据。', additional_kwargs={}, response_metadata={}), HumanMessage(content='请生成一个关于小米su7跑车的新闻。STRICT OUTPUT FORMAT:\n- Return only the JSON value that conforms to the schema. Do not include any additional text, explanations, headings, or separators.\n- Do not wrap the JSON in Markdown or code fences (no ``` or ```json).\n- Do not prepend or append any text (e.g., do not write "Here is the JSON:").\n- The response must be a single top-level JSON value exactly as required by the schema (object/array/etc.), with no trailing commas or comments.\n\nThe output should be formatted as a JSON instance that conforms to the JSON schema below.\n\nAs an example, for the schema {"properties": {"foo": {"title": "Foo", "description": "a list of strings", "type": "array", "items": {"type": "string"}}}, "required": ["foo"]} the object {"foo": ["bar", "baz"]} is a well-formatted instance of the schema. The object {"properties": {"foo": ["bar", "baz"]}} is not well-formatted.\n\nHere is the output schema (shown in a code block for readability only — do not include any backticks or Markdown in your output):\n```\n{"description": "定义一条「新闻」的结构:时间、人物、事件。用于约束模型输出的 JSON 形状。", "properties": {"time": {"description": "时间", "title": "Time", "type": "string"}, "person": {"description": "人物", "title": "Person", "type": "string"}, "event": {"description": "事件", "title": "Event", "type": "string"}}, "required": ["time", "person", "event"]}\n```', additional_kwargs={}, response_metadata={})]
2026-02-27 15:01:47.540 | INFO     | __main__:<module>:57 - 模型原始输出:
content='{"time": "2024年3月28日", "person": "雷军", "event": "小米正式发布首款高性能纯电动轿车SU7,百公里加速2.78秒,续航达800公里,开启预订后24小时订单突破10万辆"}' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 64, 'prompt_tokens': 385, 'total_tokens': 449, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_provider': 'openai', 'model_name': 'qwen-plus', 'system_fingerprint': None, 'id': 'chatcmpl-2ca1b345-6e47-9595-ad8a-37df86762530', 'finish_reason': 'stop', 'logprobs': None} id='lc_run--019c9de7-3f73-7081-bd8c-977d405da6ab-0' tool_calls=[] invalid_tool_calls=[] usage_metadata={'input_tokens': 385, 'output_tokens': 64, 'total_tokens': 449, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}}
2026-02-27 15:01:47.540 | INFO     | __main__:<module>:61 - 解析后的结构化结果:
{'time': '2024年3月28日', 'person': '雷军', 'event': '小米正式发布首款高性能纯电动轿车SU7,百公里加速2.78秒,续航达800公里,开启预订后24小时订单突破10万辆'}
2026-02-27 15:01:47.540 | INFO     | __main__:<module>:62 - 结果类型: <class 'dict'>
"""

这个案例里,用了一个 Person Pydantic 模型来描述 JSON 结构,再让:

python
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 交付结果。”

从普通文本、Parser、JSON 到结构化输出和 Pydantic 校验的逐步增强路线

LangChain 官方现在特别强调:很多现代模型已经支持原生结构化输出。这意味着,在支持的模型上,优先使用:

python
model.with_structured_output(...)

往往会比传统“手写 Prompt + Parser 解析”的方式更稳、更省心。

但输出解析器并没有过时,它仍然很有价值,尤其是在:使用不支持原生结构化输出的模型时;需要把结果继续做解析 / 清洗时;需要用 Pydantic 追加严格校验时;想把输出解析作为 LCEL 链的一环统一管理时。

4.2 常见方式 ​

LangChain 官方文档里,结构化输出常见有三种 schema 方式:

方式返回结果常见形态是否自带运行时校验适合什么场景
TypedDictdict否结构清晰、字段固定,但校验要求不高
PydanticPydantic 对象是需要强类型、范围校验、长度校验、字段验证
JSON Schemadict取决于具体使用方式需要与外部协议对齐、跨语言协作

这里还经常会配合一个写法:Annotated,它不是第四种 schema,而是给字段补充说明信息的一种方式。


5、TypedDict 与 Annotated ​

5.1 TypedDict:描述“这个字典长什么样” ​

TypedDict 来自 Python 标准库 typing,它的作用是:描述一个字典应该有哪些键、每个键是什么类型。它更像是一张结构说明书。

对 LangChain 来说,这张说明书很有用,因为它可以据此引导模型输出固定结构,再解析成 Python 字典。

但要特别注意:**TypedDict 不负责真正的运行时校验。**也就是说,它更偏“说明结构”,不是“强制验证”。

5.2 Annotated:给字段加解释说明 ​

Annotated 也是 Python 标准库 typing 里的能力。它的作用不是“换一种类型”,而是:在原有类型上附加一段元数据或说明。

例如:

python
Annotated[str, "动物名称"]

它本质上还是 str,只是附带了一段“这是动物名称”的说明。LangChain 可以利用这些说明生成更清晰的 schema 描述,让模型更容易理解每个字段该填什么。

5.3 案例:TypedDict 版结构化输出 ​

【案例源码】案例与源码-2-LangChain框架/05_parser/StructuredOutput_TypedDict.py

python
"""
【案例】用 TypedDict + Annotated 做结构化输出

对应教程章节:第 6 章 - 输出解析器 → 3、结构化输出(TypedDict / Pydantic / Annotated)

知识点速览:
一、什么是「结构化输出」?
  - 希望模型返回的不是随便一段话,而是固定结构的数据(如指定字段的 dict),方便程序后续处理。
  - LangChain 支持用 TypedDict(Python 标准库)或 Pydantic 定义结构,再由模型按该结构生成并解析。

二、with_structured_output 做什么?
  - 在模型上调用 llm.with_structured_output(某个类型),会返回一个「带了结构化输出能力」的可调用对象。
  - 调用 .invoke(messages) 时,模型会被引导输出符合该结构的 JSON,并自动解析成 Python 的 dict(或对应类型),无需再手写 Parser。

三、本案例:用 typing.TypedDict 定义 Animal(动物名 + emoji)、AnimalList(动物列表);用 Annotated 给字段加描述,便于模型理解各字段含义;最后用 llm.with_structured_output(AnimalList) 一次得到结构化结果。
"""

import os
from typing import TypedDict, Annotated
from dotenv import load_dotenv
from langchain.chat_models import init_chat_model

load_dotenv(encoding="utf-8")

llm = init_chat_model(
    model="qwen-plus",
    model_provider="openai",
    api_key=os.getenv("aliQwen-api"),
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)


# 用 TypedDict 定义「一个动物」的结构;Annotated 里的字符串是给模型看的描述,便于生成合适内容
class Animal(TypedDict):
    animal: Annotated[str, "动物"]
    emoji: Annotated[str, "表情"]


# 定义「动物列表」:一个字段 animals,类型是 Animal 的列表
class AnimalList(TypedDict):
    animals: Annotated[list[Animal], "动物与表情列表"]


# 普通对话消息
messages = [{"role": "user", "content": "任意生成三种动物,以及他们的 emoji 表情"}]

# 给模型绑定「结构化输出」:按 AnimalList 的结构返回并解析
llm_with_structured_output = llm.with_structured_output(AnimalList)
resp = llm_with_structured_output.invoke(messages)
print(
    resp
)  # 得到符合 AnimalList 的 dict,如 {"animals": [{"animal": "猫", "emoji": "🐱"}, ...]}

"""
【输出示例】
{'animals': [{'animal': '狗', 'emoji': '🐶'}, {'animal': '猫', 'emoji': '🐱'}, {'animal': '鸟', 'emoji': '🐦'}]}
"""

这个案例可以作为“现代 LangChain 结构化输出”的第一课:

  1. 用 TypedDict 定义 Animal 与 AnimalList;
  2. 用 Annotated 给字段添加说明;
  3. 用 llm.with_structured_output(AnimalList) 直接告诉模型输出目标结构;
  4. 调用 .invoke(...) 后直接得到解析好的 dict。

这比“先要求 JSON,再手动解析”更像真实项目中的推荐写法。

5.4 案例:Annotated 只是描述,不是校验 ​

【案例源码】案例与源码-2-LangChain框架/05_parser/AnnotatedTypedDict.py

python
"""
【案例】Python 入门:Annotated 与 TypedDict(仅类型提示、无运行时校验)

对应教程章节:第 6 章 - 输出解析器 → 3、结构化输出(TypedDict / Pydantic / Annotated)

知识点速览:
- Annotated[类型, "描述"] 中的描述只是元数据,供文档、类型检查或 LangChain 生成提示用;Python 运行时不会按描述做校验,故 age2=188 不会报错。
- 类型提示在运行时总体是「装饰性」的;若需运行时范围校验,要用 Pydantic 的 Field(见 AnnotatedPydantic.py)。
"""

from typing import Annotated, TypedDict

# Annotated[int, "年龄,范围0-150"]:类型仍是 int,后面的字符串只是元数据,运行时不会做 0–150 的校验
Age = Annotated[int, "年龄,范围0-150"]


class Person(TypedDict):
    name: str
    age: int
    age2: Age  # 本质还是 int,元数据 "年龄,范围0-150" 不参与运行时校验


# TypedDict 实例化时不会校验 age2 是否在 0–150,只要类型是 int 即可
p = Person(name="z3", age=111, age2=188)
print(p)

# p = Person(name="z3", age="1111")  # 若用字符串赋给 age,部分环境可能报错,取决于具体运行时

"""
【输出示例】
{'name': 'z3', 'age': 111, 'age2': 188}
"""

这个案例解决的是一个容易混淆的问题:

python
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

python
"""
【案例】Python 入门:Pydantic + Annotated 实现带范围的运行时校验

对应教程章节:第 6 章 - 输出解析器 → 3、结构化输出(TypedDict / Pydantic / Annotated)

知识点速览:
一、和 AnnotatedTypedDict 的区别?
  - AnnotatedTypedDict.py:用 typing.Annotated 只加了「描述」元数据,**运行时不会**按「0–150」校验。
  - 本文件:用 **Pydantic** 的 Field(ge=0, le=150) 放在 Annotated 里,即 Annotated[int, Field(ge=0, le=150, ...)],Pydantic 会在**运行时**校验数值是否在 0–150,超出则抛 ValidationError。

二、适用场景:需要「强类型 + 取值范围校验」时,用 Pydantic 模型 + Field 约束;LangChain 的结构化输出也可用这类模型做解析与校验。
"""

from typing import Annotated
from pydantic import BaseModel, Field, ValidationError

# 用 Annotated 结合 Pydantic 的 Field:ge=0, le=150 会在运行时校验,不在范围内会触发 ValidationError
Age = Annotated[int, Field(ge=0, le=150, description="年龄,范围0-150")]


class Person(BaseModel):
    name: str
    age: int
    age2: Age  # 这里 age2 会被 Pydantic 按 Field(ge=0, le=150) 校验


try:
    p = Person(name="z3", age=11, age2=188)  # age2=188 超出 0–150,会抛 ValidationError
    print(p)
except ValidationError as e:
    print("数据校验失败:")
    print(e)

"""
【输出示例】
数据校验失败:
1 validation error for Person
age2
  Input should be less than or equal to 150 [type=less_than_equal, input_value=188, input_type=int]
    For further information visit https://errors.pydantic.dev/2.12/v/less_than_equal
"""

这个案例和上一个 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

python
"""
【案例】PydanticOutputParser:用 Pydantic 模型定义输出结构并校验

对应教程章节:第 6 章 - 输出解析器 → 3、结构化输出(TypedDict / Pydantic / Annotated)

知识点速览:
一、PydanticOutputParser 和 JsonOutputParser 的区别?
  - JsonOutputParser:把模型文本解析成「任意」JSON(dict/list),或可绑一个 Pydantic 模型约束形状。
  - PydanticOutputParser:**专门**配合 Pydantic 模型,解析结果会转成** Pydantic 实例**,并可利用 Pydantic 的校验(如字段类型、validator),不合格会抛错。

二、本案例流程:用 Pydantic 定义 Product(name、category、description),其中 description 用 field_validator 校验长度 ≥ 10 → 用 PydanticOutputParser(pydantic_object=Product) 创建解析器 → 用 get_format_instructions() 得到格式说明并拼进 Prompt → 模型返回后 parser.invoke(result) 得到 Product 对象。
  - 适合:需要强类型、需要在校验失败时明确报错的场景。
"""

import os
from dotenv import load_dotenv
from langchain.chat_models import init_chat_model
from langchain_core.output_parsers import PydanticOutputParser
from langchain_core.prompts import ChatPromptTemplate
from loguru import logger
from pydantic import BaseModel, Field, field_validator

load_dotenv(encoding="utf-8")


class Product(BaseModel):
    """产品信息:名称、类别、简介。简介长度需 ≥ 10,由下方 validator 校验。"""

    name: str = Field(description="产品名称")
    category: str = Field(description="产品类别")
    description: str = Field(description="产品简介")

    @field_validator("description")
    def validate_description(cls, value):
        """Pydantic 校验器:description 长度必须 ≥ 10,否则抛 ValueError。"""
        if len(value) < 10:
            raise ValueError("产品简介长度必须大于等于10")
        return value


# 创建 Pydantic 输出解析器:解析结果会转成 Product 实例并做校验
parser = PydanticOutputParser(pydantic_object=Product)

# 生成「格式说明」字符串,拼进 Prompt,引导模型按 Product 的字段输出 JSON
format_instructions = parser.get_format_instructions()

# 在 system 里放入 {format_instructions},human 里放 {topic}
prompt_template = ChatPromptTemplate.from_messages(
    [
        ("system", "你是一个AI助手,你只能输出结构化的json数据\n{format_instructions}"),
        ("human", "请你输出标题为:{topic}的新闻内容"),
    ]
)

prompt = prompt_template.format_messages(
    topic="华为Mate X7", format_instructions=format_instructions
)
logger.info(prompt)

model = init_chat_model(
    model="qwen-plus",
    model_provider="openai",
    api_key=os.getenv("aliQwen-api"),
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)

result = model.invoke(prompt)
logger.info(f"模型原始输出:\n{result.content}")

# 解析:把 result 转成 Product 实例,若格式或校验不通过会抛错
response = parser.invoke(result)
logger.info(f"解析后的结构化结果:\n{response}")
logger.info(f"结果类型: {type(response)}")  # <class 'Product'>

"""
【输出示例】
2026-02-27 15:02:47.018 | INFO     | __main__:<module>:53 - [SystemMessage(content='你是一个AI助手,你只能输出结构化的json数据\nThe output should be formatted as a JSON instance that conforms to the JSON schema below.\n\nAs an example, for the schema {"properties": {"foo": {"title": "Foo", "description": "a list of strings", "type": "array", "items": {"type": "string"}}}, "required": ["foo"]}\nthe object {"foo": ["bar", "baz"]} is a well-formatted instance of the schema. The object {"properties": {"foo": ["bar", "baz"]}} is not well-formatted.\n\nHere is the output schema:\n```\n{"description": "产品信息:名称、类别、简介。简介长度需 ≥ 10,由下方 validator 校验。", "properties": {"name": {"description": "产品名称", "title": "Name", "type": "string"}, "category": {"description": "产品类别", "title": "Category", "type": "string"}, "description": {"description": "产品简介", "title": "Description", "type": "string"}}, "required": ["name", "category", "description"]}\n```', additional_kwargs={}, response_metadata={}), HumanMessage(content='请你输出标题为:华为Mate X7的新闻内容', additional_kwargs={}, response_metadata={})]
2026-02-27 15:02:51.175 | INFO     | __main__:<module>:63 - 模型原始输出:
{"name": "华为Mate X7", "category": "智能手机", "description": "华为Mate X7是一款全新折叠屏旗舰手机,搭载麒麟9010芯片与HarmonyOS 4.2系统,配备8英寸内屏与6.5英寸外屏,支持5G网络与超高速充电技术。"}
2026-02-27 15:02:51.175 | INFO     | __main__:<module>:67 - 解析后的结构化结果:
name='华为Mate X7' category='智能手机' description='华为Mate X7是一款全新折叠屏旗舰手机,搭载麒麟9010芯片与HarmonyOS 4.2系统,配备8英寸内屏与6.5英寸外屏,支持5G网络与超高速充电技术。'
2026-02-27 15:02:51.175 | INFO     | __main__:<module>:68 - 结果类型: <class '__main__.Product'>
"""

这个案例体现了 Pydantic 路线最完整、最经典的工作流:

  1. 定义一个 Product Pydantic 模型;
  2. 用 Field(...) 给字段加说明;
  3. 用 field_validator(...) 写更细的校验逻辑;
  4. 创建 PydanticOutputParser(pydantic_object=Product);
  5. 用 get_format_instructions() 生成格式说明;
  6. 把说明拼入 Prompt;
  7. 模型输出后,解析成 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 从简单到严格的选择路径 ​

你可以按下面这条路径做选择:

  1. 只需要文本展示 用 StrOutputParser
  2. 需要简单 JSON 字段 用 JsonOutputParser
  3. 需要固定结构,且模型支持结构化输出 优先 with_structured_output(TypedDict)
  4. 需要严格校验 用 Pydantic + PydanticOutputParser,或 with_structured_output(Pydantic模型)
  5. 需要跨语言 / 外部协议对齐 用 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() 方法。

python
from langchain_core.output_parsers import BaseOutputParser


class LineListParser(BaseOutputParser[list[str]]):
    def parse(self, text: str) -> list[str]:
        return [
            line.strip("- ").strip()
            for line in text.splitlines()
            if line.strip()
        ]

它适合做“很轻的格式清洗”。如果结果后面要进数据库、审批流、自动执行工具,仍然建议继续接 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 就和结构化输出是两套体系 ​

不是。它们本质上解决的是同一个问题:让模型输出可被程序稳定消费。
区别只是在于:

  • 有的方式偏“后处理解析”;
  • 有的方式偏“原生结构约束 + 自动解析”;
  • 有的方式还能进一步做严格校验。

章节思考题:

  1. 为什么“模型输出了 JSON”不等于“程序可以放心使用”?

    参考思路: JSON 语法正确只是第一步,字段是否齐全、类型是否正确、值是否符合业务规则还需要校验。解析解决“能读”,校验解决“能不能信”。

  2. 什么时候 StrOutputParser 就够了,什么时候应该上 Pydantic?

    参考思路: 只需要纯文本展示时,字符串解析足够;结果要进入数据库、接口、流程分支或自动执行时,应考虑 Pydantic 这类强校验。越靠近业务动作,越不能只靠自然语言。

  3. Structured Output 和 Output Parser 的关系应该怎么理解?

    参考思路: Structured Output 更偏让模型按结构生成,Parser 更偏在输出后解析和转换。两者可以配合使用:前面约束生成,后面兜底解析和校验。

  4. 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 在“描述结构”和“运行时校验”上的边界。

建议下一步: 学习 第 7 章 LCEL 与链式调用,把本章的解析器与 第 5 章 的 Prompt、第 3 章 的 Model 串成真正的 prompt | model | parser 链。