为什么需要输出解析器?
LLM 的原始输出是一个 AIMessage 对象(包含 content 字符串)。在实际应用中,我们通常需要将这个字符串转换成结构化的 Python 数据——字典、列表、Pydantic 模型等——以便后续的业务逻辑处理。
输出解析器(Output Parser) 承担两个职责:
- 格式化指令注入:向 Prompt 中添加说明,告诉模型应该用什么格式输出(JSON、特定 schema 等)
- 输出转换:将模型的字符串输出解析为 Python 对象
对于支持 Function Calling / Tool Calling 的模型(GPT-4o、Claude 3.5+),优先使用 model.with_structured_output(schema) 而非手工解析 JSON 字符串。它通过原生 API 特性约束输出格式,可靠性更高,不会因为模型"多说了一句话"而解析失败。
StrOutputParser:最简单的解析器
从 AIMessage 中提取 content 字符串,是 99% 的 LCEL 链的收尾步骤:
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
model = ChatOpenAI(model="gpt-4o-mini")
prompt = ChatPromptTemplate.from_template("解释:{concept}")
# 没有 StrOutputParser:返回 AIMessage 对象
chain_raw = prompt | model
result = chain_raw.invoke({"concept": "闭包"})
print(type(result)) # <class 'langchain_core.messages.AIMessage'>
print(result.content)
# 有 StrOutputParser:返回纯字符串
chain = prompt | model | StrOutputParser()
result = chain.invoke({"concept": "闭包"})
print(type(result)) # <class 'str'>
print(result)
JsonOutputParser:解析 JSON 输出
JsonOutputParser 要求模型输出合法的 JSON,并将其解析为 Python dict:
from langchain_core.output_parsers import JsonOutputParser
parser = JsonOutputParser()
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个数据提取助手,始终以 JSON 格式返回结果,不要有任何额外文字。"),
("human", """
从以下文本中提取信息,返回 JSON 格式:
{{"name": "姓名", "age": 年龄数字, "job": "职业"}}
文本:{text}
"""),
])
chain = prompt | model | parser
result = chain.invoke({
"text": "张伟是一位35岁的软件工程师,在北京工作。"
})
print(result) # {"name": "张伟", "age": 35, "job": "软件工程师"}
print(result["name"]) # 张伟
# JsonOutputParser 支持流式:边生成边解析(yield 部分 JSON)
for partial in chain.stream({"text": "李娜,28岁,设计师。"}):
print(partial) # 逐步构建的部分字典
PydanticOutputParser:类型安全的结构化输出
PydanticOutputParser 基于 Pydantic v2 模型定义输出 schema,自动生成格式化指令并在解析时做类型验证:
from langchain_core.output_parsers import PydanticOutputParser
from pydantic import BaseModel, Field
from typing import List
# 定义输出结构
class BookReview(BaseModel):
title: str = Field(description="书名")
rating: float = Field(description="评分,1到10", ge=1, le=10)
pros: List[str] = Field(description="优点列表")
cons: List[str] = Field(description="缺点列表")
summary: str = Field(description="一句话总结")
parser = PydanticOutputParser(pydantic_object=BookReview)
# parser.get_format_instructions() 生成插入提示词的格式说明
print(parser.get_format_instructions()[:200])
prompt = ChatPromptTemplate.from_messages([
("system", "你是一位书评家。\n\n{format_instructions}"),
("human", "请为《{book_title}》写一篇书评。"),
]).partial(format_instructions=parser.get_format_instructions())
chain = prompt | model | parser
review = chain.invoke({"book_title": "三体"})
print(type(review)) # <class 'BookReview'> — Pydantic 对象!
print(review.title) # 三体
print(review.rating) # 9.5
print(review.pros) # ['宏大的宇宙观', '独特的科学概念', ...]
with_structured_output:现代推荐方案
model.with_structured_output(schema) 是 LangChain 0.3.x 对支持原生工具调用的模型(GPT-4o、Claude 3.5+)提供的最可靠结构化输出方案。它通过模型原生的 Function Calling / Tool Calling 特性约束输出,不依赖文本解析,可靠性远高于 PydanticOutputParser。
from pydantic import BaseModel, Field
from typing import Literal
class SentimentResult(BaseModel):
"""情感分析结果"""
sentiment: Literal["positive", "negative", "neutral"] = Field(
description="情感倾向"
)
confidence: float = Field(description="置信度 0-1", ge=0, le=1)
reason: str = Field(description="判断理由,一句话")
# 绑定结构化输出 schema
structured_model = model.with_structured_output(SentimentResult)
prompt = ChatPromptTemplate.from_template(
"分析以下评论的情感:{review}"
)
chain = prompt | structured_model
result = chain.invoke({
"review": "这家餐厅的菜很好吃,但服务有点慢。"
})
print(type(result)) # <class 'SentimentResult'>
print(result.sentiment) # neutral
print(result.confidence) # 0.7
print(result.reason)
with_structured_output 的 method 参数
# method="function_calling"(默认):通过工具调用约束输出
m1 = model.with_structured_output(SentimentResult, method="function_calling")
# method="json_mode":要求模型输出 JSON,再解析
# 适用于不支持工具调用的模型
m2 = model.with_structured_output(SentimentResult, method="json_mode")
# include_raw=True:同时返回原始 AIMessage 和解析结果
m3 = model.with_structured_output(SentimentResult, include_raw=True)
raw_result = m3.invoke("分析:产品质量不错,价格偏贵。")
print(raw_result["raw"]) # 原始 AIMessage
print(raw_result["parsed"]) # SentimentResult 对象
OutputFixingParser:解析失败自动修复
当模型偶尔输出格式不符合要求时,OutputFixingParser 会自动发起第二次 LLM 调用来修复输出:
from langchain.output_parsers import OutputFixingParser
base_parser = PydanticOutputParser(pydantic_object=BookReview)
fixing_parser = OutputFixingParser.from_llm(
parser=base_parser,
llm=model
)
# 即使模型输出有小错误(多余的注释、字段名错误等),也会自动修复
chain = prompt | model | fixing_parser
OutputFixingParser 修复失败时会发起额外的 LLM 调用,增加延迟和成本。在生产环境中,优先选择 with_structured_output 从根本上避免解析失败,而不是依赖修复机制。
CommaSeparatedListOutputParser
解析逗号分隔的列表,是最轻量级的解析器之一:
from langchain_core.output_parsers import CommaSeparatedListOutputParser
parser = CommaSeparatedListOutputParser()
print(parser.get_format_instructions())
# "Your response should be a list of comma separated values, ..."
prompt = ChatPromptTemplate.from_messages([
("system", "{format_instructions}"),
("human", "列举5个关于{topic}的关键词"),
]).partial(format_instructions=parser.get_format_instructions())
chain = prompt | model | parser
result = chain.invoke({"topic": "机器学习"})
print(result) # ['神经网络', '监督学习', '过拟合', '特征工程', '梯度下降']
各解析器对比总结
| 解析器 | 输出类型 | 可靠性 | 适用场景 |
|---|---|---|---|
StrOutputParser | str | 极高 | 所有需要纯文本输出的场景 |
JsonOutputParser | dict | 中 | 简单 JSON,不需要严格 schema |
PydanticOutputParser | Pydantic 对象 | 中 | 需要类型验证,模型不支持工具调用 |
with_structured_output | Pydantic/dict | 高 | 生产推荐,模型支持工具调用 |
CommaSeparatedListOutputParser | list[str] | 高 | 简单列表输出 |
OutputFixingParser | 包装其他 | 中 | 对解析失败有容错需求 |
本章小结
- 输出解析器承担两个职责:向 Prompt 注入格式指令 + 解析模型输出为 Python 对象
StrOutputParser是最常用的解析器,提取 AIMessage.content 为纯字符串- 对于支持工具调用的模型,
model.with_structured_output(schema)是最可靠的结构化输出方案 PydanticOutputParser提供类型验证,适合不支持工具调用的模型- 生产环境应避免依赖 OutputFixingParser,根治方案是使用原生工具调用约束输出