LangChain 输出解析器指南:从 StrOutputParser 到 PydanticOutputParser
2026/9/19 4:07:51 网站建设 项目流程

我挖掘了一个巨牛的 人工智能 学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。

引言:

“你的 AI 应用,是不是还在‘能对话’的阶段就停下了?”

做过 LLM 应用开发的朋友一定遇到过这个场景:模型能说会道,但一涉及到“我要把结果存进数据库”、“我要调用下一个函数”,就卡壳了。因为模型给你的,是一段看得懂但用不了的自然语言。

别急,LangChain 里有一类组件,专门负责把模型的“废话”变成程序的“资产”——它就是输出解析器(Output Parser)

这篇文章,我会带你彻底搞懂它的原理,并手写一个能输出结构化笑话的 Agent。

1.输出解析器核心概念

输出解析器负责接收大模型原始输出文本,把无格式自由文本转换为标准结构化数据。在需要让模型输出规范、可被程序直接读取的数据场景下十分关键。

大模型原生输出是无约束的自然语言文本,而开发应用时往往需要机器可识别的规整数据,常见目标格式包含:

  • JSON 字符串 / Python 字典、列表
  • Pydantic 模型实例
  • 固定布尔值、枚举类等限定结果

输出解析器承担转换桥梁作用:把 LLM 非结构化文本转为结构化对象,让大模型交互像标准化接口调用,是开发工业级 LLM 应用的核心组件。

1.1 输出解析器 vs with_structured_output () 区别

二者最终都能实现结构化输出,但本质层级、使用方式、设计定位完全不同:

  1. 所属维度不同

    • 输出解析器:独立通用功能组件,属于 LangChain 单独封装的 Runnable,和 Prompt、LLM 是平级组件;
    • with_structured_output(schema):聊天模型 ChatModel 内置的实例方法,作用是包装原有模型,生成一个融合了强制结构化输出逻辑的全新模型 Runnable。
  2. 链式调用能力二者均支持|管道链式写法:

    • 输出解析器三段式链式(组件完全解耦、可单独复用)
      chain = prompt | llm | parser
      Prompt、原始大模型、解析器三者相互独立,可单独抽离、替换、复用任意一段。
    • with_structured_output链式写法(解析逻辑内置绑定模型)
      structured_llm = llm.with_structured_output(schema) chain = prompt | structured_llm
      结构化解析逻辑被封装在模型内部,不存在独立可拆分的解析器组件。
  3. 适用场景区分

    • 选用独立输出解析器:需要灵活控制解析逻辑、单独复用解析器、多链路共用同一套解析规则,追求组件解耦;
    • 选用with_structured_output:快速开发、希望由模型强制约束输出格式,不需要单独操作解析流程,代码更简洁。

总结

  1. 二者核心区别不在「能不能写链」,而在解析器是否独立可拆分
    • 三段式:解析器是独立 Runnable,灵活可替换;
    • with_structured_output:解析逻辑内置在模型内部,无法单独提取使用。

2.解析文本输出StroutputParser

作用:把大语言模型(LLM)返回的复杂输出对象,提取成最纯粹的 Python 字符串(str),也就是自动将content的内容读取输出出来

其实对于使用StroutputParser输出解析器输出文本,我们已经使用过多次了。对于StrOutputParser,它也实现了标准的Runnable接口。

StroutputParse就是一个典型的输出解析器!

示例如下:

from langchain.chat_models import init_chat_model from langchain_core.messages import SystemMessage, HumanMessage from langchain_core.output_parsers import StrOutputParser # 定义⼤模型 # 默认从系统环境变量中读取 OPENAI_API_KEY model = init_chat_model("deepseek-v4-flash", model_provider="deepseek") messages = [ SystemMessage(content="你是一个计算器"), HumanMessage(content="1+1=?") ] result = model.invoke(messages) #没有解析的 print(result) #输出结果: #content='2' additional_kwargs={'refusal': None, 'reasoning_content': '我们被问到:"1+1=?" 这是一个简单的数学问题。答案显然是2。但作为计算器,我应该给出精确答案。所以回答:2。'} response_metadata={'token_usage': {'completion_tokens': 36, 'prompt_tokens': 12, 'total_tokens': 48, 'completion_tokens_details': {'accepted_prediction_tokens': None, 'audio_tokens': None, 'reasoning_tokens': 34, 'rejected_prediction_tokens': None}, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}, 'prompt_cache_hit_tokens': 0, 'prompt_cache_miss_tokens': 12}, 'model_provider': 'deepseek', 'model_name': 'deepseek-v4-flash', 'system_fingerprint': 'fp_8b330d02d0_prod0820_fp8_kvcache_20260402', 'id': '5f26b1ad-7734-4c37-92d5-ebd52e7f99fa', 'finish_reason': 'stop', 'logprobs': None} id='lc_run--019f6f4c-cc06-7de3-9d4f-4814eb4c62b6-0' tool_calls=[] invalid_tool_calls=[] usage_metadata={'input_tokens': 12, 'output_tokens': 36, 'total_tokens': 48, 'input_token_details': {'cache_read': 0}, 'output_token_details': {'reasoning': 34}} parser = StrOutputParser() chain = model | parser #已经解析的 print(chain.invoke(messages)) #输出结果 #2

3.PydanticOutputParser 解析结构化对象输出

3.1核心定位

想要让大模型输出可被程序直接读取、带类型校验的结构化 Python 对象,使用PydanticOutputParser,专门搭配继承BaseModel的 Pydantic 模型使用。 类完整路径:langchain_core.output_parsers.pydantic.PydanticOutputParser

3.2构造参数

  • pydantic_object传入你自定义的 Pydantic 模型类(如示例中的Joke,代码在下面); 解析器会自动读取该类所有字段、字段类型、描述、必填 / 可选规则,作为解析与格式提示的依据。

3.3、内置核心方法

1. invoke(input)

功能:接收大模型返回的原始文本字符串,完成解析并返回 Pydantic 模型实例。 执行步骤:

  1. 从文本中提取 JSON 片段;
  2. 根据pydantic_object做字段、类型、必填项校验;
  3. 校验通过,返回模型对象;校验失败直接抛出解析异常。

2. get_format_instructions () → str(重中之重)

  1. 功能:自动生成一段自然语言指令文本,教大模型如何返回
  2. 使用方式:必须把这段文本拼入 Prompt 模板,随用户问题一起发给 LLM;
  3. 核心目的:用文字约束大模型输出规范,明确告知模型:
    • 必须返回 JSON 格式;
    • 需要包含哪些字段;
    • 每个字段的数据类型、含义;
    • 哪些字段必填、哪些可选;
    • 禁止输出多余解释、前言、后语,只返回纯净 JSON。

示例生成文本简化版:

输出格式要求: 返回一个JSON对象,包含以下字段: setup:字符串,必填,笑话的开头 punchline:字符串,必填,笑话的妙语 rating:整数或null,可选,1~10分为笑话打分 不要输出任何额外文字,只返回JSON。

3.4.完整配套使用流程

  1. 定义继承BaseModel的 Pydantic 结构体,规定输出字段规范;
  2. 实例化解析器:parser = PydanticOutputParser(pydantic_object=自定义类)
  3. 调用parser.get_format_instructions()获取格式指令;
  4. 通过partial_variables将格式指令注入 Prompt 模板,和任务、用户提问拼接;
  5. 构建链式prompt | model | parser
  6. 调用chain.invoke(),parser 自动完成文本→结构化对象转换。

3.5.关键注意点

  1. 若不将get_format_instructions()的内容写入 Prompt,大模型不受格式约束,会自由输出自然语言,invoke()解析时必然报错;
  2. 该解析器属于独立 Runnable 组件,和 Prompt、LLM 三层解耦,支持管道链式写法;
  3. llm.with_structured_output()区分:
    • PydanticOutputParser:靠提示词文本约束输出,链路三段式;
    • with_structured_output:模型接口底层强制结构化,无需手动拼接格式指令。

代码示例:

from typing import Optional from langchain.chat_models import init_chat_model from langchain_core.output_parsers import PydanticOutputParser from langchain_core.prompts import PromptTemplate from pydantic import BaseModel, Field model = init_chat_model( "deepseek-v4-flash", model_provider="deepseek", # 关键配置:关闭思考模式(非思考模式) model_kwargs={ "extra_body": { "thinking": {"type": "disabled"} } } ) #创建一个pydantic对象 class Joke(BaseModel):#继承BaseModel类 """ 给用户讲的一个笑话 """ setup:str = Field(description="这个笑话的开头") punchline:str = Field(description="这个笑话的妙语") #下面表示rating这个参数可选,不是必填项,而上面两个参数是必传选项 rating:Optional[int] = Field(default=None,description="1~10分,给这个笑话打几分") #定义解析器 parser = PydanticOutputParser(pydantic_object=Joke) # 提示模板 prompt = PromptTemplate( template="回复用户问题。\n返回结构说明:{format_instructions}\n用户问题:{query}\n", partial_variables={"format_instructions": parser.get_format_instructions()}, # 将返回的结构作为提示词发送给大模型 input_variables=["query"], ) # print(prompt.invoke({"query": "讲一个关于跳舞的笑话"})) # 定义链 chain = prompt | model | parser result = chain.invoke({"query": "讲一个关于跳舞的笑话"}) print(result)

输出结果:

可涵的理解和总结:

输出解析器是为了模板准备的,输出解析器调用get_format_instructions方法能够获得Joke这个pydantic类返回一段自然语言格式约束提示词。这样把模板传递给大模型,大模型就知道如何返回了。partial_variables就是存放固定不变的内容,正好就存储这个内容,而input_variables存放动态内容,正好满足用户不同的提问需求!

理解上面的语句的关键在于要弄懂get_format_instructions的作用!

get_format_instructions()作用:自动生成一大段英文规则 + JSON Schema,告诉 AI 该怎么输出 JSON;

4.完整代码讲解,重点:Prompt 作用 + 链式执行全过程

4.1、整体功能概述

定义笑话的数据格式(Pydantic),通过提示词约束大模型只能输出标准 JSON,利用prompt | model | parser链式流水线,自动完成「拼接提示→调用 AI→解析结构化对象」整套流程。

4.2、分段基础代码简要说明

1. Pydantic 结构体 Joke(规定 AI 输出格式)

class Joke(BaseModel): """给用户讲的一个笑话""" setup:str = Field(description="这个笑话的开头") punchline:str = Field(description="这个笑话的妙语") rating:Optional[int] = Field(default=None,description="1~10分,给这个笑话打几分")
  • setup、punchline:无默认值,必填字段
  • rating:可选,可以不给,值为数字或空 后续解析器会读取这个类,生成格式约束、校验 AI 返回内容。

2. 输出解析器

parser = PydanticOutputParser(pydantic_object=Joke)

两大核心能力:

  1. get_format_instructions():自动生成一大段英文规则 + JSON Schema,告诉 AI 该怎么输出 JSON;
  2. invoke():接收 AI 返回的文本,提取 JSON、校验字段,最终生成Joke对象。

3、PromptTemplate 完整作用(核心重点)

prompt = PromptTemplate( template="回复用户问题。\n返回结构说明:{format_instructions}\n用户问题:{query}\n", partial_variables={"format_instructions": parser.get_format_instructions()}, input_variables=["query"], )

1. Prompt 总作用

拼接一段完整、带强制规则的文本,作为输入传给大模型。 AI 本身不知道两件事:①要做什么 ②必须输出 JSON,全靠 Prompt 传递这两条信息。

2. 三部分参数拆解

  1. template:文本骨架,包含两个占位符
    • {format_instructions}:填充 JSON 输出强制规则
    • {query}:填充用户动态提问
  2. partial_variables:存放固定不变的内容parser.get_format_instructions()生成的格式规则,创建模板时就填充好,不用每次调用重复传入。
  3. input_variables=["query"]:声明动态变量次调用必须手动传入query,用来替换用户不同的提问。

partial_variablesinput_variables占位符的数据源配置,不是注释说明;一个存固定内容,一个存每次可变内容。它们只是告诉模板:每个{}占位符去哪里拿真实文本去替换

3.prompt.invoke({"query": "讲一个关于跳舞的笑话"})做了什么

  1. 匹配占位符,自动拼接:固定话术 + JSON 格式规则 + 用户问题;
  2. 生成一段完整长文本(就是控制台打印出来的那段英文 Schema + 中文指令);
  3. 输出纯字符串,这个字符串就是后续要丢给大模型的全部内容。

4. Prompt 不可替代的两个核心价值

  1. 传递业务需求:把用户的提问(讲跳舞笑话)发给 AI,告诉 AI 要完成什么任务;
  2. 强制约束输出格式:把解析器生成的 JSON 规范拼进文本,强制 AI 只返回标准 JSON,否则后面解析器会报错。

5、链式调用chain = prompt | model | parser完整规则与执行流程

1.|管道通用规则

|是 LangChain LCEL 管道运算符,仅支持 Runnable 对象(Prompt、模型、解析器都是 Runnable); 执行顺序从左到右前一段的输出 = 后一段的输入

整条链三层固定顺序,不能调换:prompt(组装文本) | model(AI生成) | parser(结构化解析)

2. 分步流转全过程:chain.invoke({"query": "讲一个关于跳舞的笑话"})

第 1 层:prompt 执行

  • 输入:字典{"query": "讲一个关于跳舞的笑话"}
  • 内部逻辑:用 partial 固定规则 + 传入的 query 渲染完整提示文本
  • 输出:一整段纯文本(发给 AI 的完整指令)

第 2 层:model 执行

  • 输入:上一层输出的完整提示字符串
  • 内部逻辑:把文本发送 DeepSeek,AI 按照格式要求返回只含 JSON 的文本
  • 输出:带 JSON 的原始字符串

第 3 层:parser 执行

  • 输入:AI 返回的 JSON 文本
  • 内部逻辑:提取 JSON,对照 Joke 类校验字段类型、必填项,生成 Joke 实例
  • 输出:结构化对象Joke(setup=xxx, punchline=xxx, rating=xxx)

3. 传参规则.invoke({"query": "xxx"})

  1. 字典的 keyquery必须和input_variables完全对应;
  2. format_instructions属于 partial 固定变量,不需要在 invoke 中传入;
  3. 传错变量名 / 少传变量,模板渲染直接报错。

4. 流转示意图

chain.invoke({"query": "讲一个关于跳舞的笑话"}) ↓ prompt渲染完整提示长文本(任务+JSON规则+用户问题) ↓ 文本输入模型 model调用DeepSeek,返回纯JSON字符串 ↓ JSON文本输入解析器 parser解析、校验,输出Joke结构化对象

5. 顺序不能颠倒的原因

  1. model | prompt:模型只能接收字符串,无法处理字典变量;
  2. parser | model:解析器输出是对象,模型只能接收文本; 只有prompt → model → parser数据流完全匹配。

解析 JSON 输出

要输出 JSON 格式,需要用到的输出解析器是JsonOutputParser

只需要替换一行代码即可

可涵的问题:

parser.get_format_instructions() 已经生成了一段"格式约束说明"塞进提示词,告诉 LLM 应该按什么结构返回,那么LLM已经按照正确结构返回了,为什么还要在链式调用的最后,把 LLM 返回的原始文本解析成 Pydantic 对象?| parser就是这部分代码的作用

解答:为什么最后一步解析必不可少

  1. 类型转换与安全校验
    LLM 返回的本质上只是一段符合 JSON 格式的文本字符串。而Python 代码需要的是一个可以操作的对象。PydanticOutputParser在解析时,会自动将字符串转换成 Pydantic 对象,并对字段类型进行校验。比如,LLM 可能把评分rating返回成字符串"8",解析器会帮你转成整数;如果返回成"十分",解析器会直接报错,阻止脏数据进入你的业务逻辑。

总而言之,parser.get_format_instructions()写给大模型的信,告知它该如何回答;而链尾的parser则是为你的代码准备的保险,确保收到的数据安全、可靠、好用。这一步将大模型不可控的文本输出,转化为了可控的、结构化的程序数据。

6.LangChain 输出解析器精简总结

  1. 核心作用:统一接收 LLM 无格式文本,转为程序可用规范数据,均为 Runnable,支持prompt | llm | parser链式写法,需将get_format_instructions()格式规则注入 Prompt 约束模型输出。
  2. 内置常用解析器
  • PydanticOutputParser:复杂多字段结构化数据,生成 Pydantic 对象(最常用)
  • CommaSeparatedListOutputParser:逗号分割简单列表
  • EnumOutputParser:限定固定枚举分类结果
  • DatetimeOutputParser:提取并转为 datetime 时间对象
  • XMLOutputParser/YamlOutputParser:适配 XML、YAML 格式场景
  1. 自定义解析器内置解析器不满足需求时,继承BaseOutputParser,重写parse解析逻辑与格式提示方法即可。

👉 还没关注的朋友点个关注,下一期干货不迷路。

如果你在配置解析器时遇到报错,或者对partial_variables的用法还有疑问,欢迎在评论区留言,我会逐条回复。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询