在使用大型语言模型 (LLM) 的过程中,我们经常会遇到一个挑战:LLM 的原始输出往往难以直接使用,包含了大量冗余信息,不利于数据提取、链式调用以及集成到生产系统中。本文将深入探讨 LangChain Output Parsers 的作用,并提供详细的代码示例,帮助你将 LLM 产生的非结构化文本转换为结构化的、开发者友好的格式,例如 JSON、列表或类型化对象,从而更高效地利用 LLM 的强大能力。

什么是 LangChain Output Parsers?

LangChain Output Parsers 扮演着桥梁的角色,它接收来自 LLM 的原始、有时甚至是不可预测的响应,并将其转换成预定义的、结构化的格式。这种转换极大地简化了从 LLM 响应中提取特定数据的过程,避免了繁琐的字符串解析工作,使得 LLM 的输出可以更方便地在不同的应用场景中复用。 简单来说,它将LLM返回的内容按照我们预先定义的格式进行解析,比如JSON、Pydantic对象等,从而方便下游应用的使用。

为什么需要 LangChain Output Parsers?

LLM 擅长生成文本,但这些文本通常并非完美适用于后续的数据处理或应用集成。以下是一些需要使用 LangChain Output Parsers 的关键原因:

  • 数据提取的可靠性: LLM 的输出格式可能不稳定,直接解析字符串容易出错。 Output Parsers 确保输出符合预期格式,提高数据提取的可靠性。想象一下,如果你需要从LLM生成的食谱中提取食材列表,没有Output Parser,你可能需要编写复杂的正则表达式来处理各种可能的输出格式。
  • 链式调用的便利性: 在 LangChain 中,多个 LLM 可以串联起来形成一个工作流。 Output Parsers 确保每个 LLM 的输出格式符合下一个 LLM 的输入要求,简化链式调用的流程。例如,第一个 LLM 负责生成客户反馈总结,第二个 LLM 负责根据总结提取关键情感。
  • 生产系统集成的效率: 将 LLM 集成到生产系统时,需要确保输出格式的一致性。 Output Parsers 可以将 LLM 的输出转换为系统可以理解的格式,例如 JSON,从而简化集成过程。例如,一个电商网站使用LLM根据用户搜索生成产品描述,Output Parser确保产品描述信息按照预定义的JSON格式返回,方便网站展示。

LangChain Output Parsers 的常见格式

LangChain 提供了多种 Output Parser 格式,以满足不同的需求:

  • String Output Parser: 这是最简单的 Output Parser,它仅返回 LLM 响应的内容部分,去除额外的元数据。它适用于只需要原始文本的场景,比如生成文章摘要。
  • Pydantic Output Parser: 它基于 Pydantic 模型定义输出的 schema。LLM 的响应将按照 schema 进行解析,只包含 schema 中定义的变量。这对于需要严格控制输出格式的场景非常有用,比如生成结构化的用户信息。
  • JSON Output Parser: 类似于 Pydantic Output Parser,但它返回的是 JSON 格式的输出。它适用于需要将 LLM 的输出直接用于 Web 应用的场景,比如 API 接口的数据交换。

实际案例分析:超级英雄生成器

为了更好地理解 LangChain Output Parsers 的用法,我们以一个“超级英雄生成器”为例,演示如何使用不同的 Output Parser 格式来结构化 LLM 的输出。

首先,我们需要安装必要的库:

pip install langchain langchain-google-genai python-dotenv pydantic

并设置 Google Gemini API 密钥:

import os
from dotenv import load_dotenv

load_dotenv()
GEM_API = os.getenv('GEM_API')

原始 LLM 输出

让我们先看看未经处理的 LLM 输出是什么样的:

from langchain_google_genai import ChatGoogleGenerativeAI

# 定义模型
llm = ChatGoogleGenerativeAI(model="gemini-2.0-flash", google_api_key=GEM_API)

# 调用 LLM
res = llm.invoke('Create a superhero and define its features')

# 打印 LLM 响应
print(res)

这段代码会生成一个包含超级英雄信息的文本,但同时也会包含 additional_kwargsresponse_metadataid 等额外的元数据,这些信息对于普通用户来说是无关紧要的。

String Output Parser 示例

现在,让我们使用 String Output Parser 来提取 LLM 响应的纯文本内容:

from langchain_core.prompts import PromptTemplate
from langchain_core.output_parsers import StrOutputParser

# 定义模型 (前面已定义,此处省略)

# 设置 Prompt
prompt = PromptTemplate.from_template(
    template="Anwser the users query:\n{query}"
)

# Output parser
parser = StrOutputParser()

# 创建 chain
chain = prompt | llm | parser

# 调用 Chain
result = chain.invoke({'query': 'Create a superhero and define its features'})

# 打印最终结果
print(result)

这段代码将只打印 LLM 生成的关于超级英雄的描述文本,而不会包含任何额外的元数据。

Pydantic Output Parser 示例

接下来,我们使用 Pydantic Output Parser 来定义输出的 schema:

from pydantic import BaseModel, Field
from langchain_core.output_parsers import PydanticOutputParser

# 定义模型 (前面已定义,此处省略)

# Schema
class SuperHero(BaseModel):
    superhero_name: str = Field(description='Superhero name of the person')
    real_name: str = Field(description='Real name of the person')
    age: int = Field(gt=18, description='Age of the person')
    superpower: str = Field(description='Superpower of the person')
    arch_nemesis: str = Field(description="Arch Nemesis of the person")
    background: str = Field(description='Background story of the person')
    city: str = Field(description='Name of the city the person belongs to')

# 设置 Output Parser
parser = PydanticOutputParser(pydantic_object=SuperHero)

# 设置 Prompt
prompt_template = PromptTemplate(
    template="Anwser the users query:\n{format_instruction}\n{query}",
    input_variables=["query"],
    partial_variables={'format_instruction': parser.get_format_instructions()}
)

# Invoke Chain
chain = prompt_template | llm | parser
final_result = chain.invoke({'query': 'Create a superhero and define its features'})

# Print Data
for data in final_result:
    print(data)

在这个例子中,我们定义了一个 SuperHero 类,它继承自 BaseModel,并定义了超级英雄的各个属性,例如 superhero_namereal_nameage 等。通过 Pydantic Output Parser,我们可以确保 LLM 的输出符合这个 schema,并且只包含这些属性。parser.get_format_instructions() 会生成一段文本,指导 LLM 按照指定的格式生成输出。 这段文本会被添加到 Prompt 中,以提高LLM生成符合Schema的能力。

JSON Output Parser 示例

最后,我们使用 JSON Output Parser 来生成 JSON 格式的输出:

import json
from langchain_core.output_parsers import JsonOutputParser

# 定义模型 (前面已定义,此处省略)

# Schema (与 Pydantic Output Parser 示例相同,此处省略)

# 设置 Output Parser
parser = JsonOutputParser(pydantic_object=SuperHero)

# 设置 Prompt
prompt = PromptTemplate(
    template="Answer the user query:\n{format_instructions}\n{query}",
    input_variables=['query'],
    partial_variables={'format_instructions': parser.get_format_instructions()}
)

# Invoke Chain
chain = prompt | llm | parser
output = chain.invoke({"query": 'Create a superhero and define its features'})

# Print Data
print(json.dumps(output, indent=4))

这段代码与 Pydantic Output Parser 的示例非常相似,不同之处在于我们使用了 JsonOutputParser,并且最终的输出是 JSON 格式的。这使得我们可以轻松地将 LLM 的输出用于 Web 应用或其他需要 JSON 数据的场景。

深入理解 format_instructions

format_instructions 是一个非常重要的概念,它告诉 LLM 如何格式化输出。 parser.get_format_instructions() 方法会根据你定义的 Pydantic 模型生成一段文本,这段文本会被添加到 Prompt 中,引导 LLM 按照指定的格式生成输出。

例如,对于上面的 SuperHero 类,parser.get_format_instructions() 可能会生成如下文本:

The output should be formatted as a JSON instance that conforms to the JSON schema below.

As an example, for the schema {"properties": {"foo": {"title": "Foo", "description": "a list of strings", "type": "array", "items": {"type": "string"}}}, "required": ["foo"]}}
the object {"foo": ["bar", "baz"]} is a well-formatted instance of the schema. The object {"properties": {"foo": ["bar", "baz"]}} is not well-formatted.

Here is the schema:
{"properties": {"arch_nemesis": {"title": "Arch Nemesis", "description": "Arch Nemesis of the person", "type": "string"}, "age": {"title": "Age", "description": "Age of the person", "type": "integer", "exclusiveMinimum": 18}, "background": {"title": "Background", "description": "Background story of the person", "type": "string"}, "city": {"title": "City", "description": "Name of the city the person belongs to", "type": "string"}, "real_name": {"title": "Real Name", "description": "Real name of the person", "type": "string"}, "superhero_name": {"title": "Superhero Name", "description": "Superhero name of the person", "type": "string"}, "superpower": {"title": "Superpower", "description": "Superpower of the person", "type": "string"}}, "required": ["superhero_name", "real_name", "age", "superpower", "arch_nemesis", "background", "city"]}

这段文本详细描述了输出的 JSON schema,并给出了一个示例,帮助 LLM 理解如何生成符合要求的输出。

总结与展望

LangChain Output Parsers 是一个强大的工具,它可以帮助你将 LLM 的输出转换为结构化的、开发者友好的格式。通过使用不同的 Output Parser 格式,你可以轻松地从 LLM 响应中提取数据,简化链式调用,以及集成到生产系统中。随着 LLM 技术的不断发展,Output Parsers 将会在 LLM 应用开发中扮演越来越重要的角色。 理解和掌握 LangChain Output Parsers 对于开发高效、可靠的 LLM 应用至关重要。 希望本文能够帮助你更好地理解和使用 LangChain Output Parsers,并在实际项目中发挥其强大的能力。