Langchain教程四:OutputParser解析与实战
LangChain 的 OutputParser 组件能够高效地将大模型返回的自然语言解析为结构化数据,显著提升开发效率。本文围绕以下核心内容展开:
- OutputParser 的核心作用与主流类型详解
- StrOutputParser 的实际操作与代码演示
- 不同 OutputParser 的选型策略与业务适配建议
开发者在构建 LLM 应用时,常遇到一个棘手问题:模型输出的是自然语言字符串,但业务系统需要 JSON、字典、列表或 Python 数据类对象。LangChain 提供的 OutputParser 正是解决这一痛点的刚需组件。
OutputParser 是什么?
其实在前面的学习中我们已经接触过 OutputParser,它是 LangChain 专用于将 LLM 返回结果解析为结构化数据的模块。它能将模型输出的纯文本转换为特定数据结构(如 JSON、Dict、Pydantic 模型等),便于后续业务逻辑直接调用。
LangChain 内置了多种常用 OutputParser,以下列出主要类型:
| 名称 | 是否支持流式 | 是否有格式要求 | 输出类型 |
|---|---|---|---|
StrOutputParser | ✅ | ❌ | String(纯文本) |
JsonOutputParser | ✅ | ✅ | JSON Object |
XMLOutputParser | ✅ | ✅ | dict |
ListOutputParser | ✅ | ✅ | List[str] |
PydanticOutputParser | ❌ | ✅ | Pydantic.BaseModel 对象 |
YamlOutputParser | ❌ | ✅ | Pydantic.BaseModel 对象 |
此前我们仅接触过 StrOutputParser,但 LangChain 提供的其他 Parser 各有特色:有的支持流式输出,有的对格式要求严格,有的能直接生成 Python 对象或模型实例。
StrOutputParser
StrOutputParser 用于直接提取纯文本输出。沿用之前的代码示例即可快速理解:
from langchain.prompts import PromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI
template = "请将以下内容翻译成{language}:{input}"
prompt = PromptTemplate.from_template(template)
llm = ChatOpenAI(
model="Qwen/Qwen3-32B",
openai_api_key="your_key",
base_url="https://api.siliconflow.cn/v1",
temperature=0
)
parser = StrOutputParser()
chain = prompt | llm | parser
result = chain.invoke({
"language": "日文",
"input": "我是一个中国人"
})
print(result)
JsonOutputParser
JSON 格式是对开发者最为友好的结构化形式之一,以下展示其具体用法:
from langchain_core.output_parsers import JsonOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个中文翻译助手,请根据用户的要求翻译内容,请将内容的信息以 JSON 格式返回:original:xxx,translation:xxx"),
("user", "请翻译以下内容为英文:{text}")
])
llm = ChatOpenAI(
model="Qwen/Qwen3-32B",
openai_api_key="your_key",
base_url="https://api.siliconflow.cn/v1",
temperature=0
)
parser = JsonOutputParser()
chain = prompt | llm | parser
result = chain.invoke({
"text": "我是一名中国人"
})
print(result)
输出结果是一个标准的 dict:
{
'original': '我是一名中国人',
'translation': 'I am a Chinese person.'
}
看起来不错。细心的读者可能已经发现,上面的 prompt 中特意添加了 请将内容的信息以 JSON 格式返回:original:xxx,translation:xxx。那么,能否省去这句指令呢?
直接给出结论:上方示例中不可省略。
原因是 JsonOutputParser 的底层实现相当直接——它仅对 LLM 的完整输出执行 json.loads() 调用。因此只要输出符合 JSON 语法:
{
"original": "你好",
"translation": "Hello"
}
就能顺利解析。但如果不对模型明确指定格式,它很可能会输出自然语言风格的内容,比如 I am a Chinese,此时 json.loads() 必然报错。
Pydantic
每次在 prompt 中手动编写“请按 JSON 格式返回”显得冗余且扩展性差。有没有更优雅的方案?
当然有。在深入具体操作之前,先介绍 Pydantic。
Pydantic = TypeScript + Yup + 自动校验 + 智能提示 + 更高级的 Python 数据模型系统。
对于前端开发者而言,可能熟悉 TypeScript(用类型约束结构)和 Yup/Zod(用 schema 做数据校验)。而 Pydantic 在 Python 中整合了这些功能,且能力更强。
编写 React / Vue 组件时,我们通常定义 props 类型:
type User = {
name: string;
age: number;
}
在 Python 中,使用 Pydantic 则类似这样:
from pydantic import BaseModel
class User(BaseModel):
name: str
age: int
随后可以这样使用:
user = User(name="张三", age="18") # 自动将字符串 "18" 转换为整数
user = User(name="张三", age="abc") # ❌ 抛出异常:age 不是数字
在 LangChain 中,Pydantic 最常见的三种应用场景是:
- 描述结构化输出数据的格式
- 配合 OutputParser 将 LLM 返回的数据解析为 Python 对象
- 在 FastAPI/Backend 中定义请求/响应的 schema
例如上述翻译演示,若希望 LLM 返回翻译对象:
{
"original": "你好",
"translation": "Hello"
}
便可借助 Pydantic 预先定义数据结构:
class Translation(BaseModel):
original: str
translation: str
完整代码如下:
from langchain_core.output_parsers import JsonOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from pydantic import BaseModel, Field
# 核心调整 START
class Translation(BaseModel):
original: str = Field(description="用户输入的原文")
translation: str = Field(description="翻译后的译文")
parser = JsonOutputParser(pydantic_object=Translation)
format_instructions = parser.get_format_instructions()
# 核心调整 END
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个中文翻译助手,请将用户提供的中文翻译成英文。n {format_instructions}"),
("user", "请翻译以下内容为英文:{text}")
])
llm = ChatOpenAI(
model="Qwen/Qwen3-32B",
openai_api_key="your_key",
base_url="https://api.siliconflow.cn/v1",
temperature=0
)
chain = prompt | llm | parser
result = chain.invoke({
"text": "我是一名中国人",
"format_instructions": format_instructions
})
print(result, result.get("translation"))
通过这种方式,我们能更精确地指定输出格式。那么 format_instructions 具体注入了什么内容?打印查看:
The output should be formatted as a JSON instance that conforms to the JSON schema below.
As 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.
Here is the output schema:
{"properties": {
"original": {"description": "用户输入的原文", "title": "Original", "type": "string"},
"translation": {"description": "翻译后的译文", "title": "Translation", "type": "string"}
}, "required": ["original", "translation"]}
可见,它自动生成了关于返回内容格式的详细提示词,并注入了 schema 结构。这使大模型能更精准地理解我们期望的格式。
JSON 格式的流式输出
在这种场景下,是否仍支持流式返回?答案是支持。
result = chain.stream({
"text": "我是一名中国人",
"format_instructions": format_instructions
})
for s in result:
print(s)
只需将上述 demo 的调用方式改为上述代码即可。来看实际输出:
{}
{'original': ''}
{'original': '我'}
{'original': '我是一名'}
{'original': '我是一名中国人'}
{'original': '我是一名中国人', 'translation': ''}
{'original': '我是一名中国人', 'translation': 'I'}
{'original': '我是一名中国人', 'translation': 'I am'}
{'original': '我是一名中国人', 'translation': 'I am a'}
{'original': '我是一名中国人', 'translation': 'I am a Chinese'}
{'original': '我是一名中国人', 'translation': 'I am a Chinese person'}
{'original': '我是一名中国人', 'translation': 'I am a Chinese person.'}
可见 LangChain 在保证格式规范的同时,成功实现了流式输出。
重试和修复
当要求大模型“请返回 JSON 格式”时,99 次可能都正确,但总有某一次它可能输出怪异内容,例如:
这是翻译结果:
{
"original": "你好",
"translation": "Hello"
}
或者:
{ "original": "你好", "translation": Hello } # 缺少双引号,非合法 JSON
这些异常都会导致后续 JsonOutputParser 或 PydanticOutputParser 解析失败,抛出 JSONDecodeError 或 ValidationError。
为此,LangChain 提供了 RetryOutputParser 和 OutputFixingParser 来自动处理此类问题。这两个输出容错解析器非常实用,下面逐一介绍。
RetryOutputParser
若解析失败,它会自动让 LLM 重新执行一次,提示模型“你刚才的输出格式有误,请按此格式重新输出”。
适用场景:
- 要求模型输出严谨格式(JSON、CSV、Pydantic)
- 在格式偶尔不一致时提供兜底重试机制
使用方式:
from langchain.output_parsers import RetryOutputParser, PydanticOutputParser
from langchain.prompts import PromptTemplate
from langchain_openai import ChatOpenAI
from pydantic import BaseModel
class Translation(BaseModel):
original: str
translation: str
# 原始的解析器
base_parser = PydanticOutputParser(pydantic_object=Translation)
# 包裹 RetryOutputParser:失败时自动再执行一轮 LLM + parser
parser = RetryOutputParser.from_llm(parser=base_parser, llm=ChatOpenAI())
OutputFixingParser
若解析失败,它会尝试让另一个 LLM 来“修正”输出,而不是重新运行整个流程。
它的提示词大致为:“这是用户需要的格式,这是刚才输出的错误结果,请修复它。”
适用场景:
- 不希望主模型重新执行 prompt,仅想修复输出
- 尤其适合慢模型或成本敏感的环境
使用方式:
from langchain.output_parsers import OutputFixingParser, PydanticOutputParser
from langchain_openai import ChatOpenAI
from pydantic import BaseModel
class Translation(BaseModel):
original: str
translation: str
base_parser = PydanticOutputParser(pydantic_object=Translation)
# 用另一个 LLM 尝试修复格式错误
parser = OutputFixingParser.from_llm(parser=base_parser, llm=ChatOpenAI())
区别对比
| 项目 | RetryOutputParser | OutputFixingParser |
|---|---|---|
| 处理方式 | ❗重新让主 LLM 完整运行一遍 prompt | ? 让另一个 LLM 修复输出,不重跑主模型 |
| 调用 LLM 次数 | 1次失败 + 1次重试(最多两次) | 出错时调用 1 次小模型修复 |
| 成本 / 速度 | 成本略高、速度略慢 | 更快更便宜,适合优化部署 |
| 使用场景 | 格式错误率低,但偶尔出现异常 | 格式经常出错,希望仅修复而不重跑 prompt |
| 推荐 LLM 模型 | 同一个 ChatOpenAI(主模型) | 修复可用小模型,例如 Claude Haiku、Qwen-Mini |
简而言之:
RetryOutputParser 是“原 prompt 再跑一遍”,OutputFixingParser 是“找个助手帮忙修复”。
当然也可组合使用:
parser = RetryOutputParser.from_llm(
parser=OutputFixingParser.from_llm(base_parser, repair_llm),
llm=main_llm
)
含义为:若主解析器失败 → 先尝试修复 → 如果修复不了 → 再全量重跑 prompt。
总结
OutputParser 是 LangChain 中构建高质量、可控输出不可或缺的工具。它能够从模型自然语言输出中提取出机器可理解的数据结构,充当 LLM 与业务逻辑之间的关键桥梁,确保输出结果可靠且易于集成。
