随着大模型技术的飞速发展,如何高效地利用它们构建实际应用成为了关键。本文将深入探讨利用 LangChain 框架构建生成式 AI 应用的核心概念:链(Chains)与输出解析器(Output Parsers)。我们将解析如何使用 LangChain 处理结构化输出,并探讨不同类型的输出解析器,以及如何在 LangChain 中构建链,从而打造更强大的 AI 应用。本文将基于英文文章 “Generative AI with LangChain: Chains & Output Parsers (Part 3)”,对其核心思想进行提炼和扩展,旨在帮助读者更好地理解和应用 LangChain。
1. 结构化输出与输出解析器 (Structured Output & Output Parsers)
1.1 结构化输出 (Structured Output)
在生成式 AI 应用中,仅仅获得自然语言文本的回答是不够的。许多应用场景需要模型以特定的、结构化的格式返回结果,例如 JSON、Python 字典或其他数据结构。结构化输出能够方便后续的数据处理、存储和进一步分析,极大地提高了 AI 应用的实用性。例如,一个电商应用可能需要大模型根据用户评论生成产品属性的汇总,并以 JSON 格式输出,方便系统自动更新产品信息。
1.2 使用结构化输出的场景与优势
使用结构化输出的优势在于:
- 机器可读性: 结构化数据易于被机器解析和处理,避免了从非结构化文本中提取信息的复杂性和不确定性。
- 数据一致性: 确保输出格式的一致性,避免了不同格式带来的兼容性问题。
- 简化下游任务: 结构化数据可以直接用于数据库存储、API 调用或其他数据处理任务,简化了后续开发流程。
举例来说,一个旅游推荐应用需要根据用户的旅行偏好(如预算、目的地类型、出行时间等)推荐酒店。如果大模型输出的酒店信息是自由文本格式,那么应用需要进行复杂的自然语言处理才能提取出酒店名称、价格、评分等关键信息。而如果大模型直接输出 JSON 格式的酒店信息,则应用可以轻松地获取并展示这些信息。
[
{
"hotel_name": "豪华海景酒店",
"price_per_night": 200,
"rating": 4.5,
"location": "海滨",
"amenities": ["游泳池", "健身房", "免费 Wi-Fi"]
},
{
"hotel_name": "经济型商务酒店",
"price_per_night": 80,
"rating": 4.0,
"location": "市中心",
"amenities": ["免费 Wi-Fi", "早餐"]
}
]
1.3 输出解析器的类型:TypedDict, Pydantic, JSON Schema
LangChain 提供了多种类型的输出解析器,用于将大模型的输出转换为结构化数据。
-
TypedDict: Python 的
typing
模块提供的一种类型提示,可以定义字典的键和对应的值类型。LangChain 可以利用TypedDict
来指定输出字典的结构。 -
Pydantic: 一个强大的数据验证和解析库,可以定义数据模型,并自动验证输入数据是否符合模型定义。LangChain 可以使用 Pydantic 模型来定义输出数据的结构,并自动进行数据验证。
-
JSON Schema: 一种描述 JSON 数据结构的标准化格式。LangChain 可以使用 JSON Schema 来定义输出数据的结构,并进行验证。
案例:使用 Pydantic 定义输出结构
from langchain.output_parsers import PydanticOutputParser
from pydantic import BaseModel, Field
class Hotel(BaseModel):
hotel_name: str = Field(description="酒店名称")
price_per_night: int = Field(description="每晚价格")
rating: float = Field(description="评分")
parser = PydanticOutputParser(pydantic_object=Hotel)
prompt = """
请根据以下信息生成酒店信息:
酒店名称:阳光海滩度假村
每晚价格:150
评分:4.8
{format_instructions}
"""
prompt_formatted = prompt.format(format_instructions=parser.get_format_instructions())
# 假设 llm 是一个大模型实例
output = llm(prompt_formatted)
hotel = parser.parse(output)
print(hotel) # 输出:hotel_name='阳光海滩度假村' price_per_night=150 rating=4.8
在这个例子中,我们使用 Pydantic 定义了一个 Hotel
类,指定了酒店名称、每晚价格和评分的类型和描述。然后,我们使用 PydanticOutputParser
将大模型的输出解析为 Hotel
对象。parser.get_format_instructions()
方法可以生成用于提示大模型的格式说明,帮助大模型按照指定的格式输出。
1.4 输出解析器 (Output Parsers)
输出解析器负责将大模型的原始输出转换为预定义的结构化格式。它接收大模型的输出字符串,并根据预定义的规则进行解析,最终返回一个结构化的数据对象。
LangChain 提供了多种内置的输出解析器,如 PydanticOutputParser
, CommaSeparatedListOutputParser
, JSONOutputParser
等。此外,用户还可以自定义输出解析器,以满足特定的需求。
2. 链 (Chains)
2.1 什么是链 (What are Chains?)
链 (Chains) 是 LangChain 中一个核心的概念,它代表一系列按特定顺序连接在一起的组件,用于处理数据并完成特定任务。链可以由多个模型、提示模板、输出解析器或其他链组成,形成一个复杂的工作流程。
链的设计思想是将复杂的任务分解为多个简单的步骤,每个步骤由一个组件负责,然后将这些组件连接起来,形成一个完整的流程。这使得开发人员可以更加灵活地构建 AI 应用,并更好地控制每个步骤的行为。
例如,一个问答链可能包含以下步骤:
- 检索 (Retrieval): 从知识库中检索与问题相关的文档。
- 生成 (Generation): 使用大模型根据检索到的文档生成答案。
- 输出 (Output): 将答案格式化并输出。
每个步骤都可以使用不同的组件来实现,例如可以使用 FAISS 作为知识库,使用 GPT-3 作为大模型,使用 StrOutputParser
作为输出解析器。
2.2 旧 vs 新 (Old vs New)
在早期的 LangChain 版本中,链的构建方式相对复杂,需要手动连接各个组件。而新版本的 LangChain 提供了更加简洁和灵活的链构建方式,例如可以使用 LangChain Expression Language (LCEL) 来定义链。
旧版本构建链的方式:
from langchain.llms import OpenAI
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain
llm = OpenAI(temperature=0.9)
prompt = PromptTemplate(
input_variables=["product"],
template="What is a good name for a company that makes {product}?",
)
chain = LLMChain(llm=llm, prompt=prompt)
print(chain.run("colorful socks"))
新版本使用 LCEL 构建链的方式:
from langchain.chat_models import ChatOpenAI
from langchain.prompts import PromptTemplate
from langchain.schema.output_parser import StrOutputParser
prompt_template = """你是一个命名专家,擅长为新公司起名字。
请为以下产品起一个好听的名字:{product}"""
prompt = PromptTemplate.from_template(prompt_template)
model = ChatOpenAI(temperature=0.8)
output_parser = StrOutputParser()
chain = prompt | model | output_parser
print(chain.invoke({"product": "智能家居设备"}))
新版本的 LCEL 使用管道符号 (|
) 将各个组件连接起来,更加简洁和直观。这使得开发人员可以更加方便地构建复杂的链,并快速地进行实验和迭代。
2.3 链的类型
LangChain 提供了多种内置的链类型,用于处理不同的任务。常见的链类型包括:
- LLMChain: 将提示模板和语言模型连接在一起,用于生成文本。
- SequentialChain: 将多个链按顺序连接在一起,用于执行一系列任务。
- RetrievalQAChain: 用于问答任务,结合了信息检索和生成模型。
- RouterChain: 根据输入选择不同的链来处理。
2.4 链的应用案例
案例 1:构建一个简单的问答链
from langchain.chat_models import ChatOpenAI
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import FAISS
from langchain.text_splitter import CharacterTextSplitter
from langchain.chains import RetrievalQA
# 1. 加载文档
with open("state_of_the_union.txt") as f:
state_of_the_union = f.read()
# 2. 将文档分割成小块
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
texts = text_splitter.split_text(state_of_the_union)
# 3. 创建嵌入
embeddings = OpenAIEmbeddings()
docsearch = FAISS.from_texts(texts, embeddings)
# 4. 创建问答链
qa = RetrievalQA.from_chain_type(llm=ChatOpenAI(temperature=0), chain_type="stuff", retriever=docsearch.as_retriever())
query = "What did the president say about Ketanji Brown Jackson"
print(qa.run(query))
这个例子展示了如何使用 LangChain 构建一个简单的问答链。首先,我们加载文档并将其分割成小块。然后,我们使用 OpenAIEmbeddings 创建文本嵌入,并将其存储在 FAISS 向量数据库中。最后,我们使用 RetrievalQA.from_chain_type
创建问答链,该链使用 stuff
类型的链来处理检索到的文档。
案例 2:使用 SequentialChain 构建一个故事生成器
from langchain.llms import OpenAI
from langchain.chains import LLMChain, SimpleSequentialChain
from langchain.prompts import PromptTemplate
# 1. 定义第一个链:生成故事概要
prompt_synopsis = PromptTemplate(
input_variables=["topic"],
template="Write a synopsis for a story about {topic}.",
)
chain_synopsis = LLMChain(llm=OpenAI(temperature=0.7), prompt=prompt_synopsis)
# 2. 定义第二个链:根据概要生成故事
prompt_story = PromptTemplate(
input_variables=["synopsis"],
template="Write a story based on the following synopsis: {synopsis}.",
)
chain_story = LLMChain(llm=OpenAI(temperature=0.9), prompt=prompt_story)
# 3. 将两个链连接在一起
overall_chain = SimpleSequentialChain(chains=[chain_synopsis, chain_story], verbose=True)
# 4. 运行链
topic = "a magical cat"
print(overall_chain.run(topic))
这个例子展示了如何使用 SimpleSequentialChain
构建一个故事生成器。首先,我们定义两个链:第一个链用于生成故事概要,第二个链用于根据概要生成故事。然后,我们将两个链连接在一起,形成一个完整的故事生成流程。
结论
本文深入探讨了 LangChain 中的两个核心概念:链(Chains)与输出解析器(Output Parsers)。通过合理地使用结构化输出和输出解析器,我们可以更加方便地处理大模型的输出,并将其用于各种下游任务。通过构建链,我们可以将多个组件连接在一起,形成一个复杂的工作流程,从而解决更加复杂的 AI 问题。掌握这些技术,将有助于开发者更好地利用 LangChain 构建强大的生成式 AI 应用,并在实际应用中发挥大模型技术的潜力。随着 LangChain 的不断发展,相信未来会出现更多强大而灵活的链和输出解析器,进一步简化 AI 应用的开发流程,并推动大模型技术的普及应用。