一、LangChain 中的输出解析器
输出解析器的作用是将语言模型生成的非结构化文本,转换为结构化数据,以便程序进一步处理。输出解析器主要有以下几个核心方法:
get_format_instructions:返回模型输出的格式化指示,用于告诉模型如何组织回答。parse:将语言模型输出的字符串解析为特定的数据结构。parse_with_prompt(可选):在考虑原始提示的基础上解析输出,用于纠正模型生成结果中的潜在偏差。
二、输出解析器的分类
- 列表解析器(List Parser) :处理生成列表类型的输出。
- 日期时间解析器(Datetime Parser) :确保输出是正确的日期或时间格式。
- 枚举解析器(Enum Parser) :从预定义值中选择输出。
- 结构化输出解析器(Structured Output Parser) :适合复杂的结构化输出。
- Pydantic(JSON)解析器:利用 Pydantic 库定义和验证 JSON 数据格式。
- 自动修复解析器(Auto-Fixing Parser) :修复输出中的常见错误。
- 重试解析器(RetryWithErrorOutputParser) :在初次输出不符要求时尝试修复或重新生成。
三、Pydantic 解析器详解与实战
Pydantic 是一个基于 Python 类型提示的数据验证库,其特点如下:
- 自动验证和转换数据格式。
- 友好的 JSON 支持,可以直接解析 JSON 数据或将其转换为 JSON。
1. 实现步骤
第一步:创建模型实例
通过设置 OpenAI 的 API 密钥,实例化模型(如 gpt-3.5-turbo-instruct)。
第二步:定义输出数据格式
使用 Pydantic 定义期望的输出格式,例如:
from pydantic import BaseModel, Field
class FlowerDescription(BaseModel):
flower_type: str = Field(description="鲜花的种类")
price: int = Field(description="鲜花的价格")
description: str = Field(description="鲜花的描述文案")
reason: str = Field(description="为什么要这样写这个文案")
第三步:创建输出解析器
利用 LangChain 的 PydanticOutputParser 实现解析器:
from langchain.output_parsers import PydanticOutputParser
output_parser = PydanticOutputParser(pydantic_object=FlowerDescription)
format_instructions = output_parser.get_format_instructions()
第四步:创建提示模板
将输出解析器的 format_instructions 融入提示模板中,使模型的输入和解析器需求相匹配:
from langchain import PromptTemplate
prompt_template = """您是一位专业的鲜花店文案撰写员。
对于售价为 {price} 元的 {flower} ,您能提供一个吸引人的简短中文描述吗?
{format_instructions}"""
prompt = PromptTemplate.from_template(
prompt_template,
partial_variables={"format_instructions": format_instructions}
)
第五步:生成提示并解析输出
将每种花和价格传入提示,获取模型输出并用解析器解析:
for flower, price in zip(flowers, prices):
final_prompt = prompt.format_prompt(flower=flower, price=price)
model_output = model(final_prompt.to_string())
parsed_output = output_parser.parse(model_output)
df.loc[len(df)] = parsed_output.dict()
四、Pydantic 解析器的核心价值
- 输出格式约束:明确规定字段名称、类型、格式,减少模型生成无效或错误数据的概率。
- 与提示模板联动:解析器的格式化指示(
format_instructions)和提示模板保持一致,确保输入输出逻辑的完整性。 - 自动化验证:通过 Pydantic 自动检测和转换数据类型,避免人为校验的复杂性。
五、思考题
1. 为什么大模型能够返回 JSON 格式的数据,输出解析器用了什么“魔法”让大模型做到了这一点?
大模型能够返回 JSON 格式的数据的关键在于提示工程(Prompt Engineering) 。输出解析器并没有改变大模型的核心能力,而是通过以下方式使其按预期返回 JSON 格式的数据:
- 格式化指示:解析器通过
get_format_instructions()方法,将目标输出格式的说明(例如字段名、类型、JSON 树结构等)添加到提示中,确保模型生成的输出符合 JSON 格式的约束。 - 示例引导:通过在提示中提供明确的 JSON 输出示例,模型更容易模仿生成符合结构的结果。
- 领域特化:通过上下文提示对模型行为进行约束,例如明确告诉模型“返回 JSON 格式,不包含额外信息”。
解析器的作用:
输出解析器的“魔法”主要是将输出格式化需求转换为模型能够理解的提示语言。以下是 PydanticOutputParser 的工作流程:
- 使用 Pydantic 定义结构化数据模型,并生成字段约束描述。
- 调用
get_format_instructions()自动生成提示字符串,插入到模型提示中。 - 在生成结果后,利用 Pydantic 校验并解析 JSON 数据。
2. 自动修复解析器的“修复”功能具体来说是怎样实现的?
自动修复解析器(AutoFixingParser)的修复功能通过以下方式实现:
-
初次尝试解析:尝试用预定义解析器(如 Pydantic)解析模型的原始输出。如果成功,直接返回结果;如果失败,进入修复逻辑。
-
对输出进行格式化调整:
- 检测语法错误、缺失字段、字段类型不匹配等问题。
- 通过预定义规则修复(如填补缺失字段、修改数据类型)。
-
反馈修正提示:将原始输出与格式化指示结合,再次通过模型生成修正版的结果。例如:
您的输出未符合以下格式要求,请重新生成: {"flower_type": string, "price": integer, "description": string, "reason": string} 以下是您的初次输出,请调整为正确的格式: ... -
递归尝试:经过一轮修复后再次调用解析器,直到输出符合格式或达到最大修复次数。
调试提示内容:LangChain 通过将错误信息和解析失败的原因添加到新提示中,让模型修复其输出,从而提高解析成功率。
3. 重试解析器的原理是什么?它主要实现了解析器类的哪个可选方法?
重试解析器(RetryWithErrorOutputParser) 的原理是基于失败恢复机制的输出修正,其核心逻辑如下:
- 初次解析失败:如果初次解析器(如
PydanticOutputParser)未能解析成功,则捕获错误。 - 错误反馈生成提示:将解析失败的原因及正确输出格式说明加入提示中,重新请求模型生成输出。
- 重试次数限制:设置最大重试次数,避免无限循环。
实现的可选方法:重试解析器主要扩展了 parse 方法,其功能逻辑如下:
def parse(self, output: str) -> Any:
for attempt in range(self.max_attempts):
try:
# 初次尝试解析
return self.base_parser.parse(output)
except Exception as e:
# 捕获错误,生成修正提示
repair_prompt = f"解析失败。错误原因:{e}。请重新格式化以下输出:{output}"
output = call_model(repair_prompt)
raise Exception("解析重试次数达到上限")
调试提示内容:在重试过程中,LangChain 会将原始输出与解析失败的原因一起反馈给模型,形成如下提示:
解析失败:错误原因:字段 price 类型错误。
原始输出:
{"flower_type": "玫瑰", "price": "二十元", "description": "浪漫又经典的选择"}
请重新生成符合以下格式的输出:
{"flower_type": string, "price": integer, "description": string, "reason": string}
通过这些提示,大模型能够在后续生成中更好地遵守格式约束。
五、个人思考与记录
在完成这一任务时,我深入了解了大模型如何生成结构化数据,以及解析器在 LangChain 框架中的核心作用。通过研究发现,大模型生成 JSON 格式数据的关键在于提示设计,解析器利用明确的格式指令和示例引导模型生成符合要求的输出。LangChain 的解析器进一步增强了生成的可靠性,尤其是在自动修复和重试机制的加持下,即使模型初次输出有误,也能通过反馈与多次尝试提升最终结果的质量。