LangChain框架入门05:输出解析器使用技巧
在实际的AI应用开发中,可能经常遇到这样的问题:很多时候大语言模型输出格式不够标准化,有时候返回的是纯文本,有时候是JSON格式,甚至还可能包含一些不需要的冗余信息,如:
Human:帮我生成一个商品信息的json字符串,格式是:{"name": "苹果", "price": "1000"}
AI:好的,已经为你生成:{"name": "橘子", "price": "500"}
但实际上,我们期望的是只返回一个JSON字符串,AI却返回了一些多余的文字,为了解决这个问题,LangChain提供了输出解析器(Output Parsers)组件,可以帮助我们将模型的原始输出转换为结构化的、易于处理的数据格式,这一步不仅仅涉及数据类型的转换,也包括对返回数据的处理。
学习完输出解析器,LangChain框架中最核心的组件就已经讲解完毕,整个与大语言模型交互的流程非常的清晰完整,流程图如下:
一、什么是输出解析器
输出解析器是LangChain框架中的重要组件,它的作用是将大语言模型的原始输出内容解析为如JSON、XML、YAML等结构化数据。在LangChain中,输出解析器位于模型和最终数据输出之间,作为数据处理的中间层。通过输出解析器,可以实现如下目的:
- 指定格式输出:将模型的文本输出转换指定格式
- 数据校验:确保输出内容符合预期的格式和类型
- 错误处理:当解析失败时,进行错误修复和重试
- 输出格式提示词:生成对应格式要求的提示词,如要生成JSON的具体描述,可以通过提示词传递给大模型,达到返回特定格式数据的目的
二、输出解析器类型
LangChain提供了多种输出解析器,以下是常见的输出解析器及使用场景:
解析器类型 | 适用场景 | 输出格式 |
---|---|---|
StrOutputParser | 简单文本输出 | 字符串 |
JsonOutputParser | JSON格式数据 | 字典/列表 |
PydanticOutputParser | 复杂结构化数据 | Pydantic模型对象 |
ListOutputParser | 列表数据 | Python列表 |
DatetimeOutputParser | 时间日期数据 | datetime对象 |
BooleanOutputParser | 布尔值输出 | True/False |
输出解析器核心方法:
parse:将大模型输出的内容,格式化成指定的格式返回。
format_instructions:它会返回一段清晰的格式说明字符串,告诉 LLM 希望输出成什么格式(比如 JSON,或者特定格式)。
三、常用输出解析器用法
3.1 StrOutputParser用法
StrOutputParser
是LangChain中最简单的输出解析器,它直接从AIMessage的content中提取纯文本内容。在前面的章节中,其实已经使用过它,代码示例如下,在示例中使用了链式调用,在后续的文章中会详细讲解。
import dotenv
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
# 读取env配置
dotenv.load_dotenv()
# 1.构建提示词
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个资深文学家"),
("human", "请简短赏析{name}这首诗,并给出评价")
])
# 2.创建模型
llm = ChatOpenAI()
# 3.创建字符串输出解析器
parser = StrOutputParser()
# 4.构建链式调用
chain = prompt | llm | parser
# 5.执行链式调用
result = chain.invoke({"name": "静夜思"})
print(f"输出类型: {type(result)}")
print(f"输出内容: {result}")
执行结果:
输出类型: <class 'str'>
输出内容: 《静夜思》是唐代诗人李白创作的一首脍炙人口的诗。这首诗以简练的语言,表达了诗人夜晚孤寂时对故乡的思念之情。
全诗四句:
**床前明月光,疑是地上霜。**
**举头望明月,低头思故乡。**
从诗的内容看,李白通过月光照在床前的景象,唤起了对故乡的无限思念。第一句“床前明月光”描绘了清冷、静谧的夜景,紧接着“疑是地上霜”巧妙地将月光与霜相比较,增加了诗的意境层次。接下来的“举头望明月”是对月亮的凝视,而“低头思故乡”则自然流露出诗人内心的乡愁。月亮作为传统的象征,承载了思乡之情,情感真挚而直接。
评价:
此诗语言简练、意境深远,既表现了李白的孤独感,也反映了他心中对故乡的深深依恋。其凝练的情感、清新的画面和强烈的乡愁,成就了这首诗的永恒魅力。
3.2 JsonOutputParser用法
当我们需要模型输出JSON格式数据时,可以使用 JsonOutputParser
。这个解析器不仅能解析JSON格式,还能为模型提供输出指定格式的提示词,使用示例如下,这里要注意的是案例中使用的模型是gpt-4o
,因为经过多次测试,使用gpt-3.5-turbo
输出结果会产生幻觉,幻觉可能表现为格式错误或字段遗漏。
import dotenv
from langchain_core.output_parsers import JsonOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.pydantic_v1 import BaseModel, Field
from langchain_openai import ChatOpenAI
# 读取env配置
dotenv.load_dotenv()
# 1.定义输出的对象结构
class Poetry(BaseModel):
name: str = Field(description="古诗名字")
content: str = Field(description="古诗内容")
author: str = Field(description="古诗作者")
# 1.构建提示词
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个资深文学家"),
("human", "请你输出题目为:{name}这首诗的内容\n{format_instructions}")
])
# 2.构建llm
llm = ChatOpenAI(model="gpt-4o")
# 3.构建输出解析器
parser = JsonOutputParser(pydantic_object=Poetry)
# 4.构建链式调用
chain = prompt | llm | parser
# 5.执行链式调用
result = chain.invoke({"name": "登鹳雀楼", "format_instructions": parser.get_format_instructions()})
print(f"输出类型: {type(result)}")
print(f"输出内容: {result}")
执行结果:
输出类型: <class 'dict'>
输出内容: {'name': '登鹳雀楼', 'content': '白日依山尽,黄河入海流。欲穷千里目,更上一层楼。', 'author': '王之涣'}
3.3 PydanticOutputParser用法
对于结构更复杂、具有强类型约束的需求,PydanticOutputParser
则是最佳选择。它结合了Pydantic模型的强大功能,提供了类型验证、数据转换等高级功能,使用示例如下:
import dotenv
from langchain_core.output_parsers import PydanticOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.pydantic_v1 import BaseModel, Field, validator
from langchain_openai import ChatOpenAI
# 读取env配置
dotenv.load_dotenv()
# 诗人信息模型
class Poetry(BaseModel):
name: str = Field(description="古诗名字")
content: str = Field(description="古诗内容")
author: str = Field(description="古诗作者")
# 诗歌信息模型
class Poet(BaseModel):
name: str = Field(description="诗人姓名")
age: int = Field(description="诗人年龄")
sex: int = Field(description="性别,0女,1男")
poetries: list[Poetry] = Field(description="诗歌信息列表")
# 数据验证器
@validator('poetries')
def validate_priority(cls, value):
if len(value) < 1 :
raise ValueError('诗歌列表必须大于等于1')
return value
@validator('age')
def validate_hours(cls, value):
if value <= 0:
raise ValueError('年龄必须大于0')
return value
# 1.构建提示词
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个管理中国古代诗人信息的专家"),
("human", "请你介绍一下{name}这位诗人的情况\n{format_instructions}")
])
# 2.构建llm
llm = ChatOpenAI(model="gpt-4o")
# 3.构建输出解析器
parser = PydanticOutputParser(pydantic_object=Poet)
# 4.构建链式调用
chain = prompt | llm | parser
# 5.执行链式调用
result = chain.invoke({"name": "李白", "format_instructions": parser.get_format_instructions()})
print(f"输出类型: {type(result)}")
print(f"输出内容: {result}")
执行结果:
输出类型: <class '__main__.Poet'>
输出内容: name='李白' age=61 sex=1 poetries=[Poetry(name='静夜思', content='床前明月光,疑是地上霜。举头望明月,低头思故乡。', author='李白'), Poetry(name='将进酒', content='君不见黄河之水天上来,奔流到海不复回。君不见高堂明镜悲白发,朝如青丝暮成雪。人生得意须尽欢,莫使金樽空对月。', author='李白'), Poetry(name='望庐山瀑布', content='日照香炉生紫烟,遥看瀑布挂前川。飞流直下三千尺,疑是银河落九天。', author='李白')]
四、自定义输出解析器
在某些情况下,LangChain提供的内置的解析器无法满足业务的要求,这时我们可以创建自定义的输出解析器,如下示例,在代码中创建了一个书名号自定义输出解析器,在 get_format_instructions
方法中,提供输出格式的提示词,并在parse
方法中通过正则提取书名号包裹的内容,最终返回一个列表。
import dotenv
from langchain_core.output_parsers.base import BaseOutputParser
from langchain_core.exceptions import OutputParserException
import re
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
# 读取env配置
dotenv.load_dotenv()
class BookTitleParser(BaseOutputParser):
"""自定义书名号内容解析器,用于提取《书名》格式的文本内容"""
def get_format_instructions(self) -> str:
return "请在回答中包含一个或多个被中文书名号《》包裹的内容,例如:《三体》《活着》"
def parse(self, text: str) -> list:
# 使用正则提取被《》包裹的内容
pattern = r'《(.*?)》'
titles = re.findall(pattern, text)
if not titles:
raise OutputParserException(f"未找到任何《书名号》格式的内容: {text}")
return titles
# 1.构建提示词
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个资深文学家"),
("human", "请你推荐5本关于{subject}的好书\n{format_instructions}")
])
# 2.构建llm
llm = ChatOpenAI(model="gpt-3.5-turbo")
# 3.构建输出解析器
parser = BookTitleParser()
# 4.构建链式调用
chain = prompt | llm | parser
# 5.执行链式调用
result = chain.invoke({"subject": "Python编程", "format_instructions": parser.get_format_instructions()})
print(f"输出类型: {type(result)}")
print(f"输出内容: {result}")
执行结果:
输出类型: <class 'list'>
输出内容: ['Python编程:从入门到实践', '流畅的Python', 'Python核心编程', 'Python数据科学手册', 'Python编程快速上手——让繁琐工作自动化']
五、错误修复与重试机制
LangChain 提供两种常见的错误处理机制:
OutputFixingParser
:用于修复格式不规范的输出(再次调用大模型)RetryOutputParser
:在初次解析失败时自动附带提示词重试生成过程
首先是错误修复机制,使用示例如下,将大模型输出的结果传递给fixing_parser,调用parse()方法时,在方法内,首先尝试调用 PydanticOutputParser
的 parse()
方法;若抛出异常,才会触发 OutputFixingParser
的修复逻辑,需要注意的是这里多了一次大模型调用的成本。
import dotenv
from langchain_openai import ChatOpenAI
from langchain.output_parsers import OutputFixingParser, RetryOutputParser
from langchain_core.output_parsers import JsonOutputParser
from langchain_core.pydantic_v1 import BaseModel, Field
# 读取env配置
dotenv.load_dotenv()
# 定义输出的对象结构
class Poetry(BaseModel):
name: str = Field(description="古诗名字")
content: str = Field(description="古诗内容")
author: str = Field(description="古诗作者")
llm4o = ChatOpenAI(model="gpt-4o")
# 1.构建输出解析器
base_parser = JsonOutputParser(pydantic_object=Poetry)
fixing_parser = OutputFixingParser.from_llm(parser=base_parser, llm=llm4o)
# 2.模拟错误的输出
error_str = "{'content': '白日依山尽,黄河入海流。欲穷千里目,更上一层楼。', 'author': '王之涣'}"
# 3.对比修复前后结果
print(f"修复前的内容:{error_str}")
print(f"修复后的内容:{fixing_parser.parse(error_str)}")
执行结果:
修复前的内容:{'content': '白日依山尽,黄河入海流。欲穷千里目,更上一层楼。', 'author': '王之涣'}
修复后的内容:{'name': '登鹳雀楼', 'content': '白日依山尽,黄河入海流。欲穷千里目,更上一层楼。', 'author': '王之涣'}
除了错误修复机制,LangChain还提供了重试机制,使用示例如下,将大模型输出结果传递给retry_parser,内部会调用PydanticOutputParser
的parse()
方法,与 OutputFixingParser
不同,RetryOutputParser
会在解析失败时,将原始提示词与输出一并传回模型,尝试重新生成符合格式的内容,如果parse_with_prompt
没有报错,不会去触发重试的,同样重试也会多调用一次大模型。
import dotenv
from langchain_openai import ChatOpenAI
from langchain.output_parsers import RetryOutputParser
from langchain_core.output_parsers import JsonOutputParser
from langchain_core.pydantic_v1 import BaseModel, Field
from langchain_core.prompts import ChatPromptTemplate
# 读取env配置
dotenv.load_dotenv()
# 定义输出的对象结构
class Poetry(BaseModel):
name: str = Field(description="古诗名字")
content: str = Field(description="古诗内容")
author: str = Field(description="古诗作者")
# 1.定义提示模板
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个资深文学家"),
("human", "请你输出题目为:{name}这首诗的内容\n{format_instructions}")
])
llm4o = ChatOpenAI(model="gpt-4o")
# 2.构建输出解析器
base_parser = JsonOutputParser(pydantic_object=Poetry)
retry_parser = RetryOutputParser.from_llm(parser=base_parser, llm=llm4o)
# 3.生成prompt_value
prompt_value = prompt.invoke({"name": "登鹳雀楼", "format_instructions": base_parser.get_format_instructions()})
# 4.模拟错误的输出
error_str = "{'content': '白日依山尽,黄河入海流。欲穷千里目,更上一层楼。', 'author': '王之涣'}"
# 5.对比重试前后结果
print(f"重试前的内容:{error_str}")
print(f"重试后的内容:{retry_parser.parse_with_prompt(error_str, prompt_value)}")
执行结果:
重试前的内容:{'content': '白日依山尽,黄河入海流。欲穷千里目,更上一层楼。', 'author': '王之涣'}
重试后的内容:{'name': '登鹳雀楼', 'content': '白日依山尽,黄河入海流。欲穷千里目,更上一层楼。', 'author': '王之涣'}
六、总结
输出解析器是LangChain框架中不可或缺的组件,它解决了大语言模型输出格式不规范的问题。通过本文的介绍,我们学习了基础解析器的使用从简单的StrOutputParser
到复杂的PydanticOutputParser
。
我们还了解了结构化数据处理,通过JsonOutputParser
和PydanticOutputParser
,可以将模型输出转换为结构化的Python对象、JSON对象,便于后续处理。当LangChain提供的内置解析器无法满足需求时,可以创建自定义解析器来处理特殊格式的数据。
在实际项目中,选择合适的输出解析器能够大大提升开发效率和数据处理的准确性。建议根据具体的业务需求,选择最适合的解析器类型,并结合错误处理机制确保系统的稳定性。
下一篇文章我们将继续深入LangChain的其他核心组件,探索更多实用的开发技巧,敬请期待。
LangChain框架入门05:输出解析器使用技巧的更多相关文章
- SpringMVC框架中的异常解析器-ExceptionHandler和HandlerExceptionResolver
SpringMVC框架中,处理异常还是挺方便的,提供了一个异常解析器. 处理局部异常 @Controller public class AccessController { /** * 处理这个Con ...
- EasyUI基础入门之Parser(解析器)
前言 JQuery EasyUI提供的组件包含功能强大的DataGrid,TreeGrid.面板.下拉组合等.用户能够组合使用这些组件,也能够单独使用当中一个.(使用的形式是以插件的方式提供的) Ea ...
- Mybatis框架基础支持层——解析器模块(2)
解析器模块,核心类XPathParser /** * 封装了用于xml解析的类XPath.Document和EntityResolver */ public class XPathParser { / ...
- SpringMVC入门案例及请求流程图(关于处理器或视图解析器或处理器映射器等的初步配置)
SpringMVC简介:SpringMVC也叫Spring Web mvc,属于表现层的框架.Spring MVC是Spring框架的一部分,是在Spring3.0后发布的 Spring结构图 Spr ...
- 使用XWAF框架(5)——XML解析器:CXDP
XWAF推出了自己的组合式XML文档解析器,英文名叫:“CXDP”,是“Combined XML Document Parser”的缩写.核心代码属XWAF原创,注释.日志和帮助文档采用全中文描述,特 ...
- Shell脚本 (一) 概述、解析器、脚本入门
个人博客网:https://wushaopei.github.io/ (你想要这里多有) 一. Shell 脚本概述 1. Shell 的 含义: Shell 是一个用C语言编写的程序,它是用户 ...
- iOS开发——网络篇——JSON和XML,NSJSONSerialization ,NSXMLParser(XML解析器),NSXMLParserDelegate,MJExtension (字典转模型),GDataXML(三方框架解析XML)
一.JSON 1.JSON简介什么是JSONJSON是一种轻量级的数据格式,一般用于数据交互服务器返回给客户端的数据,一般都是JSON格式或者XML格式(文件下载除外) JSON的格式很像OC中的字典 ...
- 无废话Android之android下junit测试框架配置、保存文件到手机内存、android下文件访问的权限、保存文件到SD卡、获取SD卡大小、使用SharedPreferences进行数据存储、使用Pull解析器操作XML文件、android下操作sqlite数据库和事务(2)
1.android下junit测试框架配置 单元测试需要在手机中进行安装测试 (1).在清单文件中manifest节点下配置如下节点 <instrumentation android:name= ...
- python爬虫主要就是五个模块:爬虫启动入口模块,URL管理器存放已经爬虫的URL和待爬虫URL列表,html下载器,html解析器,html输出器 同时可以掌握到urllib2的使用、bs4(BeautifulSoup)页面解析器、re正则表达式、urlparse、python基础知识回顾(set集合操作)等相关内容。
本次python爬虫百步百科,里面详细分析了爬虫的步骤,对每一步代码都有详细的注释说明,可通过本案例掌握python爬虫的特点: 1.爬虫调度入口(crawler_main.py) # coding: ...
- Android Multimedia框架总结(八)Stagefright框架之AwesomePlayer及数据解析器
转载请把头部出处链接和尾部二维码一起转载,本文出自逆流的鱼:http://blog.csdn.net/hejjunlin/article/details/52503057 前言:前面一篇分析了medi ...
随机推荐
- CentOS Stream 10安装部署Zabbix 7.0网络监控平台和设备配置添加
以下是在CentOS Stream 10上部署Zabbix 7.0监控平台,并配置多种监控目标的完整操作指南: 一.Zabbix 7.0 基础环境部署 1. 系统准备 # 更新系统 sudo dnf ...
- ChatGPT 相关资料
ChatGPT是基于GPT-3.5的语言模型且并未开源.对ChatGPT的资料搜索主要来自于兄弟模型InstrucGPT的相关资料. 相比较于InstrucGPT,ChatGPT采用多轮对话形式,符合 ...
- 2.3.net core 工作流WorkFlow流程(流程节点附件设置)
流程节点附件设置 WikeFlow官网:http://www.wikesoft.com 有些流程要求某些节点必须上传附件. 你只需要在流程节点中配置附件的Key,附件名称,是否必传. 如下图: 文件存 ...
- ESP32S3内网实现 WebSocket
ESP32S3内网实现 WebSocket WebSocket 是一种网络通信协议,它提供了在单个 TCP 连接上进行全双工.双向通信的通道.它是为了在 Web 浏览器和服务器之间实现实时.高效的数据 ...
- Java并发利器:CountDownLatch深度解析与实战应用
Java并发利器:CountDownLatch深度解析与实战应用 多线程编程中,让主线程等待所有子任务完成是个常见需求.CountDownLatch就像一个倒计时器,当所有任务完成后,主线程才继续执行 ...
- 未能加载文件或程序集“System.Runtime.WindowsRuntime, Version=4.0.14.0, Culture=neutral, PublicKeyToken=b77a5c561934e089”或它的某一个依赖项。不应出于执行的目的加载引用程序集。只能在仅限反射的加载程序上下文中加载引用程序集。 (异常来自 HRESULT:0x80131058)
VS项目编译时报错: 未能加载文件或程序集"System.Runtime.WindowsRuntime, Version=4.0.14.0, Culture=neutral, PublicK ...
- HyperWorks变形域和控制柄方法
变形域和控制柄方法 使用变形域和控制柄方法进行网格变形时,网格模型被分割成若干个变形子域,位于变形域上的控制柄常常用来控制变形域形状的变化.当控制柄移动时,变形域的形状随之变化,进而影响变形域内部节点 ...
- About me and the blog
About me and the blog About me 坐标\(CQ\),可以叫我\(Luoyu\)/洛雨/呆猫(似乎混入了奇怪的东西,时常模仿呆猫说话故而得名)/猫老大(???不知道啥时候下一 ...
- [abc309 G] Ban Permutation
G - Ban Permutation 首先看到绝对值,很烦,考虑取掉绝对值得到\(p_i\leq i-X\)或\(p_i\geq i+X\) 然后我们就自然而然有了一个暴力的想法,设\(dp[i][ ...
- 在esm中优雅的使用__dirname
在esm中没有这些 __dirname.require,因为这是cjs的规范. 但是通过如下代码,你即可使用上 import path from "node:path"; impo ...