Skip to content

输出解析器(Output Parsers)

LangChain V1.x


1、为什么需要输出解析器

在对话场景中,大模型直接返回自然语言文本即可。但在实际生产环境中,我们通常需要将大模型用于非对话场景(如数据提取、内容生成流水线等),此时需要模型以结构化的格式(如JSON、列表)输出结果,以便程序进一步处理。

LangChain在 langchain_core.output_parsers 包中提供了一系列输出解析器,专门解决"如何将模型的自然语言输出转换为结构化数据"这一问题。

要让大模型输出结构化数据,有两种基本策略:

策略方式可靠性适用场景
Prompt约束在提示词中要求模型输出指定格式依赖模型能力,可能出现格式错误通用,任何模型都支持
厂商原生能力使用API提供的结构化输出参数由API层面保证格式正确需要厂商支持(OpenAI、Google等)

下面分别介绍这两种策略的具体实现。

2、策略一:通过Prompt约束(JsonOutputParser)

JsonOutputParser 的工作原理是:将你定义的JSON Schema转化为一段格式说明文字,插入到Prompt中,引导模型按照指定结构输出JSON。

使用步骤

  1. 通过Pydantic定义目标JSON结构
  2. 构造 JsonOutputParser 实例
  3. 调用 get_format_instructions() 获取格式说明,插入到Prompt中
  4. 调用模型并用解析器解析结果
python
import os
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import JsonOutputParser
from pydantic import BaseModel, Field

llm = ChatOpenAI(
    model="gpt-4o-mini",
    temperature=0.0,
    base_url=os.getenv("OPENAI_BASE_URL"),
    api_key=os.getenv("OPENAI_API_KEY")
)

# 1. 定义目标JSON结构
class Prime(BaseModel):
    prime: list[int] = Field(description="素数")
    count: list[int] = Field(description="小于该素数的素数个数")

# 2. 构造解析器
json_parser = JsonOutputParser(pydantic_object=Prime)

# 3. 将格式说明放入SystemMessage
res = llm.invoke([
    ("system", json_parser.get_format_instructions()),
    ("user", "任意生成5个1000-100000之间的素数,并标出小于该素数的素数个数")
])
print(res.content)

# 4. 解析为Python字典
parsed_res = json_parser.invoke(res)
print(type(parsed_res))  # <class 'dict'>

注意

1、Prompt约束依赖模型的理解能力。参数量较小的模型可能输出不规范的JSON,导致解析失败。对于生产环境,建议优先使用策略二。

2、partial 用来控制"是否允许解析不完整/中途的 JSON 输出"。

  • partial=True:用于流式输出/增量生成场景。此时模型可能只吐出了半段 JSON(还没闭合括号、还缺字段)。解析时:
    • 能解析成"当前已生成的部分 JSON"就返回(parse_json_markdown 尝试提取并解析)。
    • 还解析不了就返回 None,不抛异常,等后续 token 来了再继续解析。
  • partial=False(默认):用于最终结果场景。要求输出必须是完整合法 JSON
    • 解析失败就抛 OutputParserException,提示 Invalid json output,便于你立刻发现模型没按格式输出。

一句话:partial=True 适配"边生成边解析";partial=False 适配"生成完一次性严格校验"。

3、策略二:通过厂商原生能力

主流大模型厂商的API已经提供了专门的参数,在API层面强制模型输出符合指定Schema的结构化数据,比Prompt约束更加可靠。

OpenAI示例

python
from openai import OpenAI
from pydantic import BaseModel

client = OpenAI()

class CalendarEvent(BaseModel):
    name: str
    date: str
    participants: list[str]

response = client.chat.completions.parse(
    model="gpt-4o-mini",
    messages=[
        {"role": "user", "content": "Alice and Bob are going to a science fair on Friday."}
    ],
    response_format=CalendarEvent
)

print(response.choices[0].message.parsed)
# CalendarEvent(name='Science Fair', date='Friday', participants=['Alice', 'Bob'])

注意:它不是给模型"输入的参数",而是告诉 API:把模型生成的内容按你提供的结构(这里是 CalendarEvent 这个 Pydantic 模型)进行约束/引导生成 + 结果解析与校验,并把解析后的对象放到 response.choices[0].message.parsed

输入阶段仍然是 messagesresponse_format 影响的是模型该如何组织输出

Google Gemini示例

python
from google import genai
from pydantic import BaseModel

class CalendarEvent(BaseModel):
    name: str
    date: str
    participants: list[str]

client = genai.Client(
        api_key="sk-OdqRypFlfJLKYvLmV6GsG9j0u6CRFBYKErn4xV1Wm0R3q0y9"
        http_options={
            "base_url": "https://api.openai-proxy.org/google"
        },
    )

response = client.models.generate_content(
    model="gemini-2.5-flash-lite",
    contents="Alice and Bob are going to a science fair on Friday.",
    config={
        "response_mime_type": "application/json",
        "response_json_schema": CalendarEvent.model_json_schema(),
    },
)

event = CalendarEvent.model_validate_json(response.text)
print(event)

CalendarEvent.model_json_schema()输出阶段的约束/说明。把 Pydantic 模型 CalendarEvent 转成 JSON Schema(字段名、类型、必填项等),传给模型接口用于要求模型按该结构生成 JSON。

CalendarEvent.model_validate_json(response.text)输出阶段的解析+校验。把模型返回的 response.text(JSON 字符串)解析成 CalendarEvent 实例,并按模型规则做类型/必填校验;不符合会抛 ValidationError

可以看到,不同厂商的调用方式各不相同。这正是LangChain封装 with_structured_output 的动机。

4、LangChain统一封装:with_structured_output

LangChain提供了 with_structured_output() 方法,将不同厂商的结构化输出能力统一到同一个接口下。无论底层使用哪家模型,调用方式完全一致:

python
import os
from langchain_openai import ChatOpenAI
from pydantic import BaseModel

# 1. 初始化LLM
llm = ChatOpenAI(
    model="gpt-4o-mini",
    temperature=0.0,
    base_url=os.getenv("OPENAI_BASE_URL"),
    api_key=os.getenv("OPENAI_API_KEY")
)

# 2. 定义Pydantic模型
class CalendarEvent(BaseModel):
    name: str
    date: str
    participants: list[str]

# 3. 使用with_structured_output,返回一个新的Runnable
structured_llm = llm.with_structured_output(schema=CalendarEvent)

# 4. 调用,直接返回Pydantic对象
result = structured_llm.invoke("Alice and Bob are going to a science fair on Friday.")
print(result)       # CalendarEvent(name='Science Fair', date='Friday', participants=['Alice', 'Bob'])
print(type(result)) # <class 'CalendarEvent'>

核心优势:如果后续需要更换模型(如从OpenAI切换到Anthropic),只需修改LLM的初始化代码,with_structured_output 的调用方式无需任何改动。

5、其他常用输出解析器

除了JSON解析,LangChain还提供了多种面向不同数据格式的输出解析器。

https://reference.langchain.com/python/langchain-core/output-parsers

5.1 StrOutputParser

最简单的解析器,将模型输出的 AIMessage 对象提取为纯字符串。它是构建链时最常用的解析器:

python
from langchain_core.output_parsers import StrOutputParser

parser = StrOutputParser()
# 通常在LCEL链中使用:chain = prompt | llm | StrOutputParser()

5.2 PydanticOutputParser

在介绍这个解析器之前,我们需要先了解 Pydantic 是什么。

Pydantic简介

Pydantic 是 Python 中最流行的数据验证库,它通过 Python 的类型注解自动进行数据校验和转换。简单来说,你可以用它定义一个"数据模型类",Pydantic 会确保传入的数据符合你定义的结构和类型。

最小使用案例

python
from pydantic import BaseModel, Field

# 定义一个数据模型
class User(BaseModel):
    name: str                    # 名字必须是字符串
    age: int                     # 年龄必须是整数
    email: str = Field(description="用户的邮箱地址")

# 创建实例 - 数据会自动校验和转换
user = User(name="张三", age=25, email="zhangsan@example.com")
print(user.name)   # 张三
print(user.age)    # 25

# 类型不匹配时,Pydantic会自动尝试转换
user2 = User(name="李四", age="30")  # age是字符串"30",会自动转为整数30
print(user2.age)   # 30 (已自动转换为int)

# 数据不合法时会抛出错误
# User(name="王五", age="abc")  # 报错:无法将"abc"转为整数

Pydantic的核心价值:

  • 类型安全:自动校验数据类型
  • 自动转换:能转换的类型会自动处理(如字符串"30" → 整数30)
  • 清晰的错误提示:校验失败时给出详细的错误信息
  • JSON支持:可以轻松与JSON数据互相转换

了解了Pydantic之后,PydanticOutputParser 的作用就很容易理解了——它让大模型输出符合Pydantic模型定义的结构化数据,并返回经过校验的Pydantic对象实例。

JsonOutputParser 返回普通字典不同,PydanticOutputParser 返回的是Pydantic对象,因此支持字段级别的校验规则:

python
from langchain_core.output_parsers import PydanticOutputParser
from langchain_core.prompts import ChatPromptTemplate
from pydantic import BaseModel, Field, field_validator
from langchain_openai import ChatOpenAI

class MovieReview(BaseModel):
    """电影评论结构"""
    title: str = Field(description="电影标题")
    rating: int = Field(description="评分,1-10分", ge=1, le=10)
    summary: str = Field(description="剧情简介")
    recommended: bool = Field(description="是否推荐")

    @field_validator('rating')
    @classmethod
    def rating_must_be_valid(cls, v):
        if v < 1 or v > 10:
            raise ValueError('评分必须在1-10之间')
        return v

llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
parser = PydanticOutputParser(pydantic_object=MovieReview)

# 将格式说明注入到Prompt中
prompt = ChatPromptTemplate.from_messages([
    ("system", parser.get_format_instructions()),
    ("human", "评价电影《盗梦空间》")
])

chain = prompt | llm | parser
result = chain.invoke({})
print(f"电影: {result.title}, 评分: {result.rating}/10")
  • chain = prompt | llm | parser
  • parserPydanticOutputParser)拿到 LLM 输出后,会把文本解析成 dict/JSON,然后执行 MovieReview.model_validate(...)
  • model_validate 过程中,Pydantic 会对字段做校验:先做内置约束(ge=1, le=10),再运行你定义的 @field_validator('rating'),因此 rating_must_be_valid 此时被调用
  • 校验失败会抛异常(LangChain 通常包装成 OutputParserException

_PYDANTIC_FORMAT_INSTRUCTIONS vs JSON_FORMAT_INSTRUCTIONS 区别

两者都是给模型的"写作要求",目的都是让模型输出"符合 schema 的 JSON 文本"。

  • _PYDANTIC_FORMAT_INSTRUCTIONS:告诉你"按 schema 输出 JSON",但没强力禁止你加解释、加 Markdown code block。

  • JSON_FORMAT_INSTRUCTIONS

    :更严格,明确要求:

    • 只能输出 JSON
    • 不能有任何额外文字
    • 不能用 ``` 包起来
    • 必须是单一顶层 JSON 值

所以关键点是: 两个都是提示词(输入的一部分);它们约束的是模型的文本输出格式。输出先是 JSON 文本,之后才可能被解析成 Python 对象。

6、自定义输出解析器

当内置解析器无法满足需求时,可以继承 BaseOutputParser 创建自定义解析器:

python
from langchain_core.output_parsers import BaseOutputParser

class CommaListParser(BaseOutputParser):
    """将逗号分隔的文本解析为列表"""

    def parse(self, text: str):
        # 去除空白后按逗号分割
        return [item.strip() for item in text.split(",")]

# 使用
parser = CommaListParser()
result = parser.parse("苹果, 香蕉, 橘子")
print(result)  # ['苹果', '香蕉', '橘子']

7、输出解析器选型指南

场景推荐方案原因
生产环境结构化输出with_structured_output()厂商原生支持,格式有保证
通用JSON提取JsonOutputParser灵活,不依赖特定厂商
需要字段校验PydanticOutputParser支持Pydantic校验规则
简单文本输出StrOutputParser最轻量,无额外开销
特殊格式自定义 BaseOutputParser完全可控

小结:围绕"如何解析模型输出",介绍了从Prompt约束到厂商原生能力,再到LangChain统一封装的多种方案。在实际开发中,我们需要将提示词模板、模型调用、输出解析这三个步骤串联起来使用。如何优雅地组合这些组件?这就是Chains链式调用的核心主题。