输出解析
LangChain提供的解析模型输出的功能,使你能够更容易地从模型输出中获取结构化的信息,这将大大加快基于语言模型进行应用开发的效率。
概述
大型语言模型 (LLM) 正在成为为信息提取应用程序提供动力的极其强大的技术。
经典的信息提取解决方案依赖于人员、(大量)手工制作的规则(例如正则表达式)和自定义微调的 ML 模型的组合。
这些系统随着时间的推移往往会变得复杂,维护成本越来越高,并且越来越难以增强。
LLM 可以通过为其提供适当的指令和适当的参考示例,快速适应特定的提取任务。
方法
使用 LLM 进行信息提取有 3 种主要方法
- 工具/函数调用模式:某些 LLM 支持工具或函数调用模式。这些 LLM 可以根据给定的模式结构化输出。通常,这种方法最易于使用,预计会产生良好的结果。
- JSON 模式:某些 LLM 可以被强制输出有效的 JSON。这类似于工具/函数调用方法,只是模式作为提示的一部分提供。通常,我们的直觉是,这比工具/函数调用方法表现更差,但不要相信我们,请验证您自己的用例!
- 基于提示:可以很好地遵循指令的 LLM 可以被指示以所需格式生成文本。生成的文本可以使用现有的 输出解析器 或使用 自定义解析器 下游解析为结构化格式,例如 JSON。这种方法可用于不支持 JSON 模式或工具/函数调用模式的 LLM。这种方法适用范围更广,但可能比经过微调用于提取或函数调用的模型产生更差的结果。
输出解析器是帮助构建语言模型响应的类。输出解析器必须实现两种主要方法
- "获取格式说明":一种方法,返回一个包含关于语言模型输出应如何格式化的说明的字符串。
- "解析":一种方法,接收一个字符串(假定为来自语言模型的响应)并将其解析为某种结构。
还有一个可选的方法
- "使用提示解析":一种方法,接收一个字符串(假定为来自语言模型的响应)和一个提示(假定为生成此响应的提示)并将其解析为某种结构。提示主要是在输出解析器想要重试或以某种方式修复输出时提供的,并且需要来自提示的信息才能做到这一点。
下面我将在代码中介绍这个基本的用法
# 导入结构化输出解析器和ResponseSchema
import os
from langchain.output_parsers import StructuredOutputParser, ResponseSchema
# 从 langchain_core 中导入 PromptTemplate,用于创建自定义的提示模板
from langchain_core.prompts import PromptTemplate
# 导入智谱AI的模型接口
from langchain_community.chat_models import ChatZhipuAI
def set_api_key():
os.environ["ZHIPUAI_API_KEY"] = "Your Key"
"""Set the ZhipuAI API key from environment variable or prompt the user."""
if not os.getenv("ZHIPUAI_API_KEY"):
os.environ["ZHIPUAI_API_KEY"] = input("Enter your ZhipuAI API key: ")
# 设置API Key
set_api_key()
# 初始化智谱AI模型
chat = ChatZhipuAI(model="glm-4", temperature=0.5)
# 创建原始提示模板
prompt_template = """您是一位专业的鲜花店文案撰写员。
对于售价为 {price} 元的 {flower_name} ,您能提供一个吸引人的简短描述吗?
{format_instructions}"""
# 定义我们想要接收的响应模式
response_schemas = [
ResponseSchema(name="description", description="鲜花的描述文案"),
ResponseSchema(name="reason", description="问什么要这样写这个文案")
]
# 创建输出解析器
output_parser = StructuredOutputParser.from_response_schemas(response_schemas)
# 获取格式指示
format_instructions = output_parser.get_format_instructions()
# 根据原始模板创建提示,同时在提示中加入输出解析器的说明
prompt = PromptTemplate.from_template(prompt_template,
partial_variables={"format_instructions": format_instructions})
# 数据准备
flowers = ["玫瑰", "百合", "康乃馨"]
prices = ["50", "30", "20"]
for flower, price in zip(flowers, prices):
# 根据提示准备模型的输入
putin = prompt.format(flower_name=flower, price=price)
# 获取模型的输出
output = chat.invoke(putin)
# 解析模型的输出(这是一个字典结构)
output.model_dump()
parsed_output = output_parser.parse(output.content)
# 在解析后的输出中添加“flower”和“price”
parsed_output['flower'] = flower
parsed_output['price'] = price
print(parsed_output)
下面我们逐步看这段代码
# 初始化智谱AI模型
chat = ChatZhipuAI(model="glm-4", temperature=0.5)
- 初始化智谱AI模型:使用
ChatZhipuAI创建一个名为chat的模型实例。指定model="glm-4"表示使用glm-4模型,temperature=0.5控制生成的文本的创造性程度。temperature=0.5表示输出会有适度的多样性,而不是完全确定性。
# 创建原始提示模板
prompt_template = """您是一位专业的鲜花店文案撰写员。
对于售价为 {price} 元的 {flower_name} ,您能提供一个吸引人的简短描述吗?
{format_instructions}"""
- 创建原始提示模板:定义一个多行字符串
prompt_template作为提示模板。模板中使用了占位符{price}和{flower_name},它们将根据实际输入替换成相应的鲜花价格和名称。此外,{format_instructions}也会在稍后被替换为输出格式的说明。
# 定义我们想要接收的响应模式
response_schemas = [
ResponseSchema(name="description", description="鲜花的描述文案"),
ResponseSchema(name="reason", description="问什么要这样写这个文案")
]
- 定义响应模式:定义
response_schemas列表,包含两个ResponseSchema对象。ResponseSchema用来描述模型输出的结构。第一个ResponseSchema用于描述鲜花的文案(description),第二个用于描述为什么要这样写文案(reason)。这些响应将用于输出解析时的格式化。
# 创建输出解析器
output_parser = StructuredOutputParser.from_response_schemas(response_schemas)
- 创建输出解析器:通过
StructuredOutputParser.from_response_schemas方法创建一个解析器output_parser,该解析器根据response_schemas中定义的响应模式来解析模型的输出。解析器负责将模型返回的原始文本转换为结构化的输出格式(例如字典形式)。
# 获取格式指示
format_instructions = output_parser.get_format_instructions()
- 获取格式指示:调用
output_parser.get_format_instructions()方法获取模型输出的格式化指令。格式指令告诉模型如何将输出组织成结构化的数据格式,这样output_parser可以正确解析。
# 根据原始模板创建提示,同时在提示中加入输出解析器的说明
prompt = PromptTemplate.from_template(prompt_template,
partial_variables={"format_instructions": format_instructions})
- 根据原始模板创建提示:使用
PromptTemplate.from_template方法根据之前定义的prompt_template创建一个PromptTemplate实例。在这里,partial_variables被用来替换模板中的{format_instructions}占位符,填入获取的格式指示。最终prompt将包含完整的提示模板,可以直接传递给模型。
# 数据准备
flowers = ["玫瑰", "百合", "康乃馨"]
prices = ["50", "30", "20"]
- 数据准备:定义两个列表
flowers和prices,分别存储不同类型的鲜花和它们对应的价格。稍后将通过这些数据创建输入,以请求模型生成相应的文案。
for flower, price in zip(flowers, prices):
- 遍历鲜花和价格:使用
zip(flowers, prices)将两个列表打包成一个迭代器,并同时遍历flowers和prices。flower变量代表当前鲜花的名称,price变量代表对应的价格。
# 根据提示准备模型的输入
putin = prompt.format(flower_name=flower, price=price)
- 准备模型输入:根据遍历到的
flower和price,使用prompt.format方法将模板中的占位符{flower_name}和{price}替换成实际值,生成模型的输入字符串putin。该字符串即为模型的输入提示。
# 获取模型的输出
output = chat.invoke(putin)
- 获取模型的输出:调用
chat.invoke(putin),将生成的输入字符串putin传递给智谱AI模型,得到模型的输出(通常为生成的文本或其他数据格式)。
# 解析模型的输出(这是一个字典结构)
output.model_dump()
- 解析模型输出:调用
output.model_dump(),可能是用来打印或者获取output对象的详细内容,以便进一步处理。这里假设它会输出模型的原始响应,通常是一个包含生成文本的字典。
parsed_output = output_parser.parse(output.content)
- 解析模型的输出内容:通过
output_parser.parse(output.content)将模型的原始输出(output.content)解析为结构化的数据格式(如字典)。output_parser根据之前定义的response_schemas模式来组织输出数据。
# 在解析后的输出中添加“flower”和“price”
parsed_output['flower'] = flower
parsed_output['price'] = price
- 在解析后的输出中添加额外信息:将当前鲜花的名称
flower和价格price添加到解析后的输出parsed_output中。这确保了每个输出字典包含了生成文案的鲜花信息和价格信息,便于后续处理或显示。
print(parsed_output)
- 打印解析后的输出:将解析后的字典
parsed_output打印出来。输出包含生成的文案、理由以及鲜花名称和价格等信息。每次迭代都会打印当前鲜花和价格的处理结果。
预期结果
现在,你应该能够对模型的响应进行基本的解析了,这在构建ai问答库时十分方便,我们可以将结构化的数据方便的存放到数据库里
下一节,我将介绍提示工程,你会明白在提示模板的构建过程中加入了partial_variables,也就是输出解析器指定的format_instructions之后,为什么能够让模型生成结构化的输出?