尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

LangChain 输出解析器指南:从 StrOutputParser 到 PydanticOutputParser

LangChain 输出解析器指南:从 StrOutputParser 到 PydanticOutputParser 我挖掘了一个巨牛的 人工智能 学习网站通俗易懂风趣幽默忍不住分享一下给大家。点击跳转到网站。引言“你的 AI 应用是不是还在‘能对话’的阶段就停下了”做过 LLM 应用开发的朋友一定遇到过这个场景模型能说会道但一涉及到“我要把结果存进数据库”、“我要调用下一个函数”就卡壳了。因为模型给你的是一段看得懂但用不了的自然语言。别急LangChain 里有一类组件专门负责把模型的“废话”变成程序的“资产”——它就是输出解析器Output Parser。这篇文章我会带你彻底搞懂它的原理并手写一个能输出结构化笑话的 Agent。1.输出解析器核心概念输出解析器负责接收大模型原始输出文本把无格式自由文本转换为标准结构化数据。在需要让模型输出规范、可被程序直接读取的数据场景下十分关键。大模型原生输出是无约束的自然语言文本而开发应用时往往需要机器可识别的规整数据常见目标格式包含JSON 字符串 / Python 字典、列表Pydantic 模型实例固定布尔值、枚举类等限定结果输出解析器承担转换桥梁作用把 LLM 非结构化文本转为结构化对象让大模型交互像标准化接口调用是开发工业级 LLM 应用的核心组件。1.1 输出解析器 vs with_structured_output () 区别二者最终都能实现结构化输出但本质层级、使用方式、设计定位完全不同所属维度不同输出解析器独立通用功能组件属于 LangChain 单独封装的 Runnable和 Prompt、LLM 是平级组件with_structured_output(schema)聊天模型 ChatModel 内置的实例方法作用是包装原有模型生成一个融合了强制结构化输出逻辑的全新模型 Runnable。链式调用能力二者均支持|管道链式写法输出解析器三段式链式组件完全解耦、可单独复用chain prompt | llm | parserPrompt、原始大模型、解析器三者相互独立可单独抽离、替换、复用任意一段。with_structured_output链式写法解析逻辑内置绑定模型structured_llm llm.with_structured_output(schema) chain prompt | structured_llm结构化解析逻辑被封装在模型内部不存在独立可拆分的解析器组件。适用场景区分选用独立输出解析器需要灵活控制解析逻辑、单独复用解析器、多链路共用同一套解析规则追求组件解耦选用with_structured_output快速开发、希望由模型强制约束输出格式不需要单独操作解析流程代码更简洁。总结二者核心区别不在「能不能写链」而在解析器是否独立可拆分三段式解析器是独立 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_providerdeepseek) messages [ SystemMessage(content你是一个计算器), HumanMessage(content11?) ] result model.invoke(messages) #没有解析的 print(result) #输出结果 #content2 additional_kwargs{refusal: None, reasoning_content: 我们被问到11? 这是一个简单的数学问题。答案显然是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} idlc_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)) #输出结果 #23.PydanticOutputParser 解析结构化对象输出3.1核心定位想要让大模型输出可被程序直接读取、带类型校验的结构化 Python 对象使用PydanticOutputParser专门搭配继承BaseModel的 Pydantic 模型使用。 类完整路径langchain_core.output_parsers.pydantic.PydanticOutputParser3.2构造参数pydantic_object传入你自定义的 Pydantic 模型类如示例中的Joke代码在下面 解析器会自动读取该类所有字段、字段类型、描述、必填 / 可选规则作为解析与格式提示的依据。3.3、内置核心方法1. invoke(input)功能接收大模型返回的原始文本字符串完成解析并返回 Pydantic 模型实例。 执行步骤从文本中提取 JSON 片段根据pydantic_object做字段、类型、必填项校验校验通过返回模型对象校验失败直接抛出解析异常。2. get_format_instructions () → str重中之重功能自动生成一段自然语言指令文本教大模型如何返回使用方式必须把这段文本拼入 Prompt 模板随用户问题一起发给 LLM核心目的用文字约束大模型输出规范明确告知模型必须返回 JSON 格式需要包含哪些字段每个字段的数据类型、含义哪些字段必填、哪些可选禁止输出多余解释、前言、后语只返回纯净 JSON。示例生成文本简化版输出格式要求 返回一个JSON对象包含以下字段 setup字符串必填笑话的开头 punchline字符串必填笑话的妙语 rating整数或null可选1~10分为笑话打分 不要输出任何额外文字只返回JSON。3.4.完整配套使用流程定义继承BaseModel的 Pydantic 结构体规定输出字段规范实例化解析器parser PydanticOutputParser(pydantic_object自定义类)调用parser.get_format_instructions()获取格式指令通过partial_variables将格式指令注入 Prompt 模板和任务、用户提问拼接构建链式prompt | model | parser调用chain.invoke()parser 自动完成文本→结构化对象转换。3.5.关键注意点若不将get_format_instructions()的内容写入 Prompt大模型不受格式约束会自由输出自然语言invoke()解析时必然报错该解析器属于独立 Runnable 组件和 Prompt、LLM 三层解耦支持管道链式写法和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_providerdeepseek, # 关键配置关闭思考模式非思考模式 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(defaultNone,description1~10分给这个笑话打几分) #定义解析器 parser PydanticOutputParser(pydantic_objectJoke) # 提示模板 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 该怎么输出 JSON4.完整代码讲解重点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(defaultNone,description1~10分给这个笑话打几分)setup、punchline无默认值必填字段rating可选可以不给值为数字或空 后续解析器会读取这个类生成格式约束、校验 AI 返回内容。2. 输出解析器parser PydanticOutputParser(pydantic_objectJoke)两大核心能力get_format_instructions()自动生成一大段英文规则 JSON Schema告诉 AI 该怎么输出 JSONinvoke()接收 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. 三部分参数拆解template文本骨架包含两个占位符{format_instructions}填充 JSON 输出强制规则{query}填充用户动态提问partial_variables存放固定不变的内容parser.get_format_instructions()生成的格式规则创建模板时就填充好不用每次调用重复传入。input_variables[query]声明动态变量每次调用必须手动传入query用来替换用户不同的提问。partial_variables、input_variables是占位符的数据源配置不是注释说明一个存固定内容一个存每次可变内容。它们只是告诉模板每个{}占位符去哪里拿真实文本去替换3.prompt.invoke({query: 讲一个关于跳舞的笑话})做了什么匹配占位符自动拼接固定话术 JSON 格式规则 用户问题生成一段完整长文本就是控制台打印出来的那段英文 Schema 中文指令输出纯字符串这个字符串就是后续要丢给大模型的全部内容。4. Prompt 不可替代的两个核心价值传递业务需求把用户的提问讲跳舞笑话发给 AI告诉 AI 要完成什么任务强制约束输出格式把解析器生成的 JSON 规范拼进文本强制 AI 只返回标准 JSON否则后面解析器会报错。5、链式调用chain prompt | model | parser完整规则与执行流程1.|管道通用规则|是 LangChain LCEL 管道运算符仅支持 Runnable 对象Prompt、模型、解析器都是 Runnable 执行顺序从左到右前一段的输出 后一段的输入。整条链三层固定顺序不能调换prompt组装文本 | modelAI生成 | parser结构化解析2. 分步流转全过程chain.invoke({query: 讲一个关于跳舞的笑话})第 1 层prompt 执行输入字典{query: 讲一个关于跳舞的笑话}内部逻辑用 partial 固定规则 传入的 query 渲染完整提示文本输出一整段纯文本发给 AI 的完整指令第 2 层model 执行输入上一层输出的完整提示字符串内部逻辑把文本发送 DeepSeekAI 按照格式要求返回只含 JSON 的文本输出带 JSON 的原始字符串第 3 层parser 执行输入AI 返回的 JSON 文本内部逻辑提取 JSON对照 Joke 类校验字段类型、必填项生成 Joke 实例输出结构化对象Joke(setupxxx, punchlinexxx, ratingxxx)3. 传参规则.invoke({query: xxx})字典的 keyquery必须和input_variables完全对应format_instructions属于 partial 固定变量不需要在 invoke 中传入传错变量名 / 少传变量模板渲染直接报错。4. 流转示意图chain.invoke({query: 讲一个关于跳舞的笑话}) ↓ prompt渲染完整提示长文本任务JSON规则用户问题 ↓ 文本输入模型 model调用DeepSeek返回纯JSON字符串 ↓ JSON文本输入解析器 parser解析、校验输出Joke结构化对象5. 顺序不能颠倒的原因model | prompt模型只能接收字符串无法处理字典变量parser | model解析器输出是对象模型只能接收文本 只有prompt → model → parser数据流完全匹配。解析 JSON 输出要输出 JSON 格式需要用到的输出解析器是JsonOutputParser只需要替换一行代码即可可涵的问题parser.get_format_instructions() 已经生成了一段格式约束说明塞进提示词告诉 LLM 应该按什么结构返回那么LLM已经按照正确结构返回了为什么还要在链式调用的最后把 LLM 返回的原始文本解析成 Pydantic 对象| parser就是这部分代码的作用解答为什么最后一步解析必不可少类型转换与安全校验LLM 返回的本质上只是一段符合 JSON 格式的文本字符串。而Python 代码需要的是一个可以操作的对象。PydanticOutputParser在解析时会自动将字符串转换成 Pydantic 对象并对字段类型进行校验。比如LLM 可能把评分rating返回成字符串8解析器会帮你转成整数如果返回成十分解析器会直接报错阻止脏数据进入你的业务逻辑。总而言之parser.get_format_instructions()是写给大模型的信告知它该如何回答而链尾的parser则是为你的代码准备的保险确保收到的数据安全、可靠、好用。这一步将大模型不可控的文本输出转化为了可控的、结构化的程序数据。6.LangChain 输出解析器精简总结核心作用统一接收 LLM 无格式文本转为程序可用规范数据均为 Runnable支持prompt | llm | parser链式写法需将get_format_instructions()格式规则注入 Prompt 约束模型输出。内置常用解析器PydanticOutputParser复杂多字段结构化数据生成 Pydantic 对象最常用CommaSeparatedListOutputParser逗号分割简单列表EnumOutputParser限定固定枚举分类结果DatetimeOutputParser提取并转为 datetime 时间对象XMLOutputParser/YamlOutputParser适配 XML、YAML 格式场景自定义解析器内置解析器不满足需求时继承BaseOutputParser重写parse解析逻辑与格式提示方法即可。 还没关注的朋友点个关注下一期干货不迷路。如果你在配置解析器时遇到报错或者对partial_variables的用法还有疑问欢迎在评论区留言我会逐条回复。
返回列表