LangChain 中的输出解析器与 Pydantic 实战

388 阅读7分钟

一、LangChain 中的输出解析器

输出解析器的作用是将语言模型生成的非结构化文本,转换为结构化数据,以便程序进一步处理。输出解析器主要有以下几个核心方法:

  • get_format_instructions:返回模型输出的格式化指示,用于告诉模型如何组织回答。
  • parse:将语言模型输出的字符串解析为特定的数据结构。
  • parse_with_prompt(可选):在考虑原始提示的基础上解析输出,用于纠正模型生成结果中的潜在偏差。

二、输出解析器的分类

  1. 列表解析器(List Parser) :处理生成列表类型的输出。
  2. 日期时间解析器(Datetime Parser) :确保输出是正确的日期或时间格式。
  3. 枚举解析器(Enum Parser) :从预定义值中选择输出。
  4. 结构化输出解析器(Structured Output Parser) :适合复杂的结构化输出。
  5. Pydantic(JSON)解析器:利用 Pydantic 库定义和验证 JSON 数据格式。
  6. 自动修复解析器(Auto-Fixing Parser) :修复输出中的常见错误。
  7. 重试解析器(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="为什么要这样写这个文案")
第三步:创建输出解析器

利用 LangChainPydanticOutputParser 实现解析器:

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 解析器的核心价值

  1. 输出格式约束:明确规定字段名称、类型、格式,减少模型生成无效或错误数据的概率。
  2. 与提示模板联动:解析器的格式化指示(format_instructions)和提示模板保持一致,确保输入输出逻辑的完整性。
  3. 自动化验证:通过 Pydantic 自动检测和转换数据类型,避免人为校验的复杂性。

五、思考题

1. 为什么大模型能够返回 JSON 格式的数据,输出解析器用了什么“魔法”让大模型做到了这一点?

大模型能够返回 JSON 格式的数据的关键在于提示工程(Prompt Engineering) 。输出解析器并没有改变大模型的核心能力,而是通过以下方式使其按预期返回 JSON 格式的数据:

  • 格式化指示:解析器通过 get_format_instructions() 方法,将目标输出格式的说明(例如字段名、类型、JSON 树结构等)添加到提示中,确保模型生成的输出符合 JSON 格式的约束。
  • 示例引导:通过在提示中提供明确的 JSON 输出示例,模型更容易模仿生成符合结构的结果。
  • 领域特化:通过上下文提示对模型行为进行约束,例如明确告诉模型“返回 JSON 格式,不包含额外信息”。

解析器的作用
输出解析器的“魔法”主要是将输出格式化需求转换为模型能够理解的提示语言。以下是 PydanticOutputParser 的工作流程:

  1. 使用 Pydantic 定义结构化数据模型,并生成字段约束描述。
  2. 调用 get_format_instructions() 自动生成提示字符串,插入到模型提示中。
  3. 在生成结果后,利用 Pydantic 校验并解析 JSON 数据。

2. 自动修复解析器的“修复”功能具体来说是怎样实现的?

自动修复解析器(AutoFixingParser)的修复功能通过以下方式实现:

  • 初次尝试解析:尝试用预定义解析器(如 Pydantic)解析模型的原始输出。如果成功,直接返回结果;如果失败,进入修复逻辑。

  • 对输出进行格式化调整

    1. 检测语法错误、缺失字段、字段类型不匹配等问题。
    2. 通过预定义规则修复(如填补缺失字段、修改数据类型)。
  • 反馈修正提示:将原始输出与格式化指示结合,再次通过模型生成修正版的结果。例如:

    您的输出未符合以下格式要求,请重新生成:
    {"flower_type": string, "price": integer, "description": string, "reason": string}
    以下是您的初次输出,请调整为正确的格式:
    ...
    
  • 递归尝试:经过一轮修复后再次调用解析器,直到输出符合格式或达到最大修复次数。

调试提示内容:LangChain 通过将错误信息和解析失败的原因添加到新提示中,让模型修复其输出,从而提高解析成功率。


3. 重试解析器的原理是什么?它主要实现了解析器类的哪个可选方法?

重试解析器(RetryWithErrorOutputParser 的原理是基于失败恢复机制的输出修正,其核心逻辑如下:

  1. 初次解析失败:如果初次解析器(如 PydanticOutputParser)未能解析成功,则捕获错误。
  2. 错误反馈生成提示:将解析失败的原因及正确输出格式说明加入提示中,重新请求模型生成输出。
  3. 重试次数限制:设置最大重试次数,避免无限循环。

实现的可选方法:重试解析器主要扩展了 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 的解析器进一步增强了生成的可靠性,尤其是在自动修复和重试机制的加持下,即使模型初次输出有误,也能通过反馈与多次尝试提升最终结果的质量。