上一篇学了 Input(模板拼接) 和 Model(invoke / stream / batch)。大模型返回的是 AIMessage 对象,业务里往往要 字符串、JSON、结构化对象。

本文讲 Model I/O 的第三环 —— Output 输出解析器:它是什么、为什么要用、常用有哪些、各自怎么用。
注意先 申请大模型的秘钥和配置环境(与系列前文相同)。
LangChain 把调大模型拆成三段:
Input(Prompt 模板) → Model(LLM) → Output(输出解析器)
拼问题 生成回复 转成可用数据
输出解析器(Output Parser) 负责最后一步:把 AIMessage 转成程序方便用的 字符串、字典、强类型对象。
response = llm.invoke(messages) # AIMessage
text = parser.invoke(response) # str / dict / Pydantic 对象
解析器 = 模型输出 → 业务数据的适配器。 价值不只是少写一行 .content,而是 统一接口、可替换、可接 chain。
| 解析器 / 方式 | 产出类型 | 常用度 | 典型场景 |
|---|---|---|---|
StrOutputParser | str | ⭐⭐ 必学 | 聊天、摘要、任何要纯文本 |
JsonOutputParser | dict / list | ⭐ 常用 | 抽信息、对接 API 的 JSON |
PydanticOutputParser | Pydantic 模型 | ⭐ 常用 | 固定字段 + 类型校验(学习用) |
with_structured_output | Pydantic 模型 | ⭐ 生产推荐 | 同上,由 API 约束 schema |
CommaSeparatedListOutputParser | list[str] | 特定 | Prompt 要求「逗号分隔列表」 |
XMLOutputParser 等 | XML / 工具 JSON | 了解即可 | 老 Prompt、Agent |
学习路径:StrOutputParser → JsonOutputParser → PydanticOutputParser + Field → with_structured_output。
import os
from langchain.chat_models import init_chat_modelos.environ["OPENAI_API_KEY"] = os.getenv("SILICON_KEY")
os.environ["OPENAI_BASE_URL"] = os.getenv("SILICON_BASE_URL")
llm = init_chat_model("openai:deepseek-ai/DeepSeek-V3")
场景:聊天、问答、写文章——下游只要 字符串。
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplateprompt = ChatPromptTemplate.from_messages([
("system", "你现在是一个小学{subject}老师,尽量用两三句话解释"),
("human", "请介绍{question}"),
]).invoke({"subject": "数学", "question": "数独是啥"})answer = llm.invoke(prompt)
answerFormated = StrOutputParser().invoke(answer) # 等同于 answer.content
print(answerFormated)
流式:
for chunk in StrOutputParser().transform(llm.stream(prompt)):
print(chunk, end="")
场景:下游要 dict 或 list,不是字符串。
from langchain_core.output_parsers import JsonOutputParser
from langchain_core.prompts import ChatPromptTemplateprompt = ChatPromptTemplate.from_messages([
("system", "你只输出 JSON 不要 markdown 不要解释。"),
("human", "用 JSON 描述城市,字段:name, population。城市:{city}"),
]).invoke({"city": "北京"})data = JsonOutputParser().invoke(llm.invoke(prompt))
print(data) # {'name': '北京', 'population': '2189万'}
关键点:
```json 通常仍能解析。with_structured_output。场景:字段固定、要 类型校验——下游要 Pydantic 对象,不是裸 dict。
from pydantic import BaseModel, Field, RootModel
from langchain_core.output_parsers import PydanticOutputParser
from langchain_core.prompts import ChatPromptTemplate# ① 定义 schema
class Animal(BaseModel):
name: str = Field(description="动物中文名称,不超过8个字", min_length=1, max_length=8)
emoji: str = Field(description="单个 emoji 表情")
age: int = Field(default=0, ge=0, le=150, description="年龄,0~150 整数")class AnimalArray(RootModel[list[Animal]]):
pass# ② 先建 parser,注入 format_instructions
parser = PydanticOutputParser(pydantic_object=AnimalArray)
prompt = ChatPromptTemplate.from_messages([
("system", "只输出 JSON,不要 markdown。n{format_instructions}"),
("human", "用 JSON 数组描述三个动物"),
]).invoke({"format_instructions": parser.get_format_instructions()})# ③ 调模型 → 解析
result = parser.invoke(llm.invoke(prompt))
print(result.model_dump())
get_format_instructions() 是什么?把 Pydantic 模型的 JSON Schema 自动生成成说明文字,塞进 Prompt 的 {format_instructions}。
易错点:必须先建 parser,再传入 get_format_instructions();顺序反了或传空字典,模型看不到 schema。
result.root[0].name # RootModel:数组在 .root 里
result.model_dump() # 转 list[dict],方便打印/存库
result.model_dump_json() # 转 JSON 字符串
Field 必须从 pydantic 导入(不是 dataclasses.Field)。做两件事:
description 进入 format_instructions| 参数 | 用途 | 建议 |
|---|---|---|
description | 字段说明 → 进 Prompt | ⭐ 每个字段都写 |
default / default_factory | 缺字段时的默认值 | 可选字段时用 |
ge / le / min_length / max_length | 解析后硬校验 | 配合 description 一起写 |
name: str = Field(description="动物中文名称,不超过8个字", min_length=1, max_length=8)
age: int = Field(default=0, ge=0, le=150, description="年龄,0~150 整数")
对比 Json:Json 在 Prompt 里手写字段名;Pydantic 把说明写在 Field 里,不用维护两套文案。
chain = prompt.partial(format_instructions=parser.get_format_instructions()) | llm | parser
result = chain.invoke({"city": "上海"}) # 按你的 prompt 变量调整
with_structured_output — 生产更推荐场景:和 Pydantic 一样要结构化输出,但 不想手写 format_instructions。
from pydantic import BaseModel, Field, RootModel# Animal / AnimalArray 定义同上一节,此处省略llm_struct = llm.with_structured_output(AnimalArray)
result = llm_struct.invoke("用 JSON 数组描述三个动物,每项含名称、emoji 和年龄")
print(result.model_dump())
和 PydanticOutputParser 的对比:
| 维度 | PydanticOutputParser | with_structured_output |
|---|---|---|
| 原理 | 模型输出文本 → 后解析 | 模型/API 按 schema 约束输出 |
| Prompt | 必须注入 {format_instructions} | 不用 |
| 典型写法 | parser.invoke(llm.invoke(prompt)) | llm_struct.invoke(prompt) |
| chain | prompt | llm | parser | prompt | llm_struct |
| 适用 | 学习 Output 环、兼容弱 API | 生产环境优先 |
| 现象 | 处理 |
|---|---|
忘了传 format_instructions | 检查 Prompt;或改用 with_structured_output |
| 字段缺失 / 类型错误 | 加强 Field(description=...);降低 temperature |
| Prompt 手写 JSON 又用了 Pydantic | 统一用 {format_instructions} 或改用 with_structured_output |
with_structured_output 报 schema 错误 | method="json_mode" 或退回 Parser |
场景:Prompt 明确要求「用逗号分隔返回多个词」。
from langchain_core.output_parsers import CommaSeparatedListOutputParseritems = CommaSeparatedListOutputParser().invoke(llm.invoke(messages))
print(items) # ['苹果', '香蕉', '橙子']
适合标签、关键词;结构复杂时用 Json 或 Pydantic。
只要文字? → StrOutputParser ⭐ 默认
要 JSON / dict? → JsonOutputParser + Prompt 约束
要固定字段 + 校验? → 学习:PydanticOutputParser + Field
生产:with_structured_output ⭐ 推荐
逗号分隔列表? → CommaSeparatedListOutputParser
| 你的目标 | 用哪个 |
|---|---|
| 聊天、日志、纯文本 | StrOutputParser |
| 抽实体、对接 API JSON | JsonOutputParser |
| 结构化 + 校验(学习) | PydanticOutputParser |
| 结构化 + 校验(生产) | with_structured_output |
| 标签 / 关键词 | CommaSeparatedListOutputParser |
模板 → llm.invoke → parser.invoke → str / dict / Model
模板 → llm.with_structured_output(...).invoke → Model(跳过 Parser)
新手路径:Str 跑通 → Json → Pydantic + Field → with_structured_output。