LangChain输出解析器|第七届字节青训营

240 阅读9分钟

LangChain输出解析器

在 LangChain 中,输出解析器(Output Parsers)是非常重要的组成部分,尤其是在我们需要从语言模型获取可操作的结构化数据时。

1. Pydantic(JSON)解析器

Pydantic 是一个 Python 库,用于数据验证和解析,它通常用于定义和验证结构化数据。LangChain 中的 Pydantic(JSON)解析器利用 Pydantic 来处理模型输出,将其转化为符合预定义数据模型的 JSON 对象。

使用场景
  • 当你期望模型输出一个符合特定格式的 JSON 对象时使用。
  • 例如,假设你在问模型关于用户信息的问题,模型可能返回一个字典或者 JSON 对象。你可以使用 Pydantic 解析器来确保返回的数据符合你的预期结构。
工作原理

Pydantic 解析器使用 Pydantic 模型来定义你期望的 JSON 结构。通过指定字段和数据类型,Pydantic 会自动验证输出是否符合预期格式。如果输出不符合预期,Pydantic 会抛出验证错误。

示例

假设你希望模型返回一个包含 name(字符串)和 age(整数)字段的 JSON 对象:

pydantic import BaseModel

class UserInfo(BaseModel):
    name: str
    age: int

class PydanticOutputParser:
    def __init__(self, model: BaseModel):
        self.model = model

    def parse(self, model_output: str):
        # 解析模型输出,返回一个验证过的Pydantic对象
        try:
            return self.model.parse_raw(model_output)
        except Exception as e:
            raise ValueError(f"Output format error: {e}")

# 示例使用
output = '{"name": "Alice", "age": 30}'
parser = PydanticOutputParser(UserInfo)
parsed_data = parser.parse(output)
print(parsed_data)

在这个例子中,Pydantic 模型 UserInfo 定义了 JSON 对象的结构,PydanticOutputParser 解析模型的输出并确保它符合该结构。

2. 自动修复解析器(Auto-Fixing Parser)

自动修复解析器的目的是在模型输出不符合预期时自动修正它。它可以针对常见的错误(例如拼写错误、语法错误或格式错误)进行纠正。这对于提升模型的准确性和减少后续处理中的错误非常有用。

使用场景
  • 当你知道模型输出可能会出现拼写错误、语法错误或格式问题时,使用自动修复解析器来进行修正。
  • 例如,模型输出的一段文本可能含有拼写错误,而你不希望这些错误影响后续处理。
工作原理

自动修复解析器会对模型输出进行检查,识别常见的错误并进行修正。这些修复可能包括:

  • 拼写纠正
  • 语法检查和纠正
  • 数值格式化(例如日期、货币等)

3. 重试解析器(RetryWithErrorOutputParser)

重试解析器用于在模型的输出不符合预期时重新生成输出。这个解析器可以用来在遇到不合格的输出时重新请求模型生成新的回答,直到满足要求为止。

使用场景
  • 当模型的输出不符合要求(例如数据格式错误),你可以使用重试解析器来重新生成输出。
  • 重试解析器对于模型输出不确定、可能错误的情况非常有用。
工作原理

重试解析器通常会在以下情况下使用:

  • 当模型输出的格式不符合预期时,重试解析器会通过重新提示模型生成正确的输出。
  • 例如,如果模型返回的是文本,而你需要它返回一个日期格式,重试解析器会触发模型重新生成正确格式的输出。

Pydantic 应用

1. 基本设置和模型初始化

首先,我们需要做的就是设置一个 API 密钥来连接 OpenAI 的服务。然后,我们通过 LangChain 创建一个大语言模型实例来生成文本。

import os
os.environ["OPENAI_API_KEY"] = '你的OpenAI API Key'

from langchain import OpenAI
model = OpenAI(model_name='gpt-3.5-turbo-instruct')
  • 这里设置了 API 密钥,这样程序就能调用 OpenAI 的接口。
  • OpenAI 是 LangChain 提供的一个接口,它简化了与 OpenAI 模型的交互。

2. 定义输出格式

我们要定义好想要从模型获取的输出格式。这里,我们定义了一个 PydanticFlowerDescription,来规范我们希望模型生成的数据结构。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="为什么要这样写这个文案")
  • 这个类定义了四个字段,分别是花的种类、价格、描述和文案的理由。
  • 通过 Pydantic,我们可以确保传入的数据符合类型要求(例如,flower_type 应该是字符串,price 应该是整数等)。

3. 输出解析器的作用

LangChain 提供了 PydanticOutputParser,它的作用是解析模型的输出,确保输出符合我们预期的格式。我们通过这个解析器来告诉模型如何组织输出的数据。

from langchain.output_parsers import PydanticOutputParser
output_parser = PydanticOutputParser(pydantic_object=FlowerDescription)
format_instructions = output_parser.get_format_instructions()
  • output_parser 会根据我们定义的 FlowerDescription 类来解析模型的输出。
  • get_format_instructions() 会返回一段格式指示(JSON Schema),告诉模型输出数据时需要遵循什么结构。这部分指示实际上就是一个标准的模板,它明确了每个字段的数据类型和要求。

4. 构建模型的输入提示

为了让模型生成符合格式的内容,我们需要构建一个包含变量和格式指示的输入提示。这时就用到了 LangChainPromptTemplate。我们通过模板来动态填充花的种类和价格,同时将输出格式的要求嵌入提示中,确保模型知道如何格式化输出。

from langchain import PromptTemplate
prompt_template = """您是一位专业的鲜花店文案撰写员。
对于售价为 {price} 元的 {flower} ,您能提供一个吸引人的简短中文描述吗?
{format_instructions}"""

prompt = PromptTemplate.from_template(prompt_template, 
       partial_variables={"format_instructions": format_instructions})
  • {flower}{price} 是模板中的占位符,这些会在运行时被具体的鲜花种类和价格替换。
  • {format_instructions} 是之前生成的格式指示,它告诉模型输出的数据需要遵循 JSON 格式的要求。

5. 生成输入、获取输出并解析

现在,我们可以使用模板生成具体的输入提示并传给模型,模型根据提示生成一个文案描述。接着,我们通过输出解析器来验证和解析模型的输出,确保它符合我们定义的结构。然后,解析后的数据将被添加到一个 DataFrame 中,方便后续操作。

for flower, price in zip(flowers, prices):
    input = prompt.format(flower=flower, price=price)
    print("提示:", input)

    output = model(input)

    parsed_output = output_parser.parse(output)
    parsed_output_dict = parsed_output.dict()

    df.loc[len(df)] = parsed_output.dict()
  • 对于每一种花和价格,我们生成一个输入提示并传递给模型。
  • output 是模型生成的文案,例如鲜花的描述和理由。
  • output_parser.parse(output) 负责解析模型的输出,确保它符合我们预设的 FlowerDescription 格式。
  • 最后,解析后的数据会被添加到 DataFrame 中,形成一个结构化的数据表。

OutputFixingParser应用

在很多情况下,模型的输出格式可能不完全符合我们预期的数据格式。比如说,JSON 格式中的键值对使用了单引号而不是双引号,这会导致解析错误。我们可以使用 OutputFixingParser 来修复这种格式错误,而不需要手动调整输出内容。

步骤 1: 模拟格式错误

首先,我们定义了一个 Flower 类(使用 Pydantic)来表示花的信息:

from pydantic import BaseModel, Field
from typing import List

class Flower(BaseModel):
    name: str = Field(description="name of a flower")
    colors: List[str] = Field(description="the colors of this flower")

然后,我们模拟一个格式错误的输出 misformatted,这个输出中的键值对是用单引号包围的,而正确的 JSON 应该使用双引号:

misformatted = "{'name': '康乃馨', 'colors': ['粉红色','白色','红色','紫色','黄色']}"

如果直接使用 PydanticOutputParser 来解析这个输出,将会抛出一个格式错误的异常,提示 JSON 格式不正确:

parser = PydanticOutputParser(pydantic_object=Flower)
parser.parse(misformatted)
步骤 2: 使用 OutputFixingParser 自动修复

为了修复这种格式错误,我们可以使用 OutputFixingParser。它不仅会尝试解析输出,还会在格式错误时通过调用大语言模型(LLM)来自动修复这些错误。

from langchain.output_parsers import OutputFixingParser
from langchain.chat_models import ChatOpenAI

new_parser = OutputFixingParser.from_llm(parser=parser, llm=ChatOpenAI())
result = new_parser.parse(misformatted)  # 错误被自动修正
print(result)  # 打印解析后的输出结果

在这里,OutputFixingParser 会将原始的格式错误的 JSON 输出传递给 OpenAI 模型,并请求模型修复格式问题。

RetryWithErrorOutputParser应用

有时,模型的输出不仅存在格式错误,还可能缺少某些必要的信息。比如,在解析时,模型返回的 JSON 仅包含了 action 字段,而缺少了 action_input 字段,这会导致解析失败。此时,OutputFixingParser 可能无法完全修复问题,因为它只负责修复格式错误,而不处理数据缺失的情况。

步骤 1: 模拟数据缺失

假设我们有一个期望包含 actionaction_input 字段的 Action 类:

class Action(BaseModel):
    action: str = Field(description="action to take")
    action_input: str = Field(description="input to the action")

然后,我们定义一个不完整的输出 bad_response,它仅包含 action 字段,没有 action_input 字段:

bad_response = '{"action": "search"}'

此时,如果直接解析,解析器会抛出一个错误,因为 action_input 字段缺失了。

步骤 2: 使用 OutputFixingParser 解决不完整数据

我们可以使用 OutputFixingParser 来自动修复这个问题,它会为缺失的字段提供默认值。比如,如果 action_input 字段缺失,它可能会自动补充一个默认值(比如 'query')。

fix_parser = OutputFixingParser.from_llm(parser=parser, llm=ChatOpenAI())
parse_result = fix_parser.parse(bad_response)
print('OutputFixingParser的parse结果:', parse_result)

输出将是:

action='search' action_input='query'

这里,OutputFixingParser 填充了缺失的 action_input 字段,但默认填充的值 'query' 并不具有描述性,它只是一个通用的占位符。

步骤 3: 使用 RetryWithErrorOutputParser 进行更智能的修复

如果我们希望模型更智能地恢复缺失的字段并提供更具体的信息,可以使用 RetryWithErrorOutputParser。这个解析器不仅可以修复格式错误,还能通过原始的提示重新查询模型,以填补缺失的数据。

from langchain.output_parsers import RetryWithErrorOutputParser
from langchain.llms import OpenAI

retry_parser = RetryWithErrorOutputParser.from_llm(
    parser=parser, llm=OpenAI(temperature=0)
)

parse_result = retry_parser.parse_with_prompt(bad_response, prompt_value)
print('RetryWithErrorOutputParser的parse结果:', parse_result)

RetryWithErrorOutputParser 会重新生成一个查询,并将其传递给模型,以获得更准确、更完整的输出。