LangChain实战课-基础篇:深入 6 大组件 (三) | 豆包MarsCode AI刷题

142 阅读10分钟

课程介绍

  • 启程篇:从 0 到 1
    • 内容:LangChain的安装流程和快速入门操作
    • 实践:构建基于“易速鲜花”本地知识库的智能问答系统
    • 目标:直观感受LangChain的强大功能
  • 基础篇:深入 6 大组件
    • 模型(Models) :各大语言模型的接口、调用细节以及输出解析机制
    • 提示模板(Prompts) :提示工程流线化,进一步激发大语言模型的潜力
    • 数据检索(Indexes) :建立和操作文档,返回相关文档以搭建知识库
    • 记忆(Memory) :通过短时和长时记忆存储和检索对话数据
    • 链(Chains)封装功能的核心机制,灵活完成常见用例
    • 代理(Agents)另一个LangChain中的核心机制,使大模型自主调用工具,形成智能自主Agent
  • 应用篇:积累场景中的智慧
    • 内容:LangChain组件在实际场景中的应用
    • 实践:嵌入式存储、数据库连接、异步通信、智能代理的角色扮演等
    • 目标:通过实际案例展示组件了解如何共同完成复杂任务,提升实战能力
  • 实战篇:动手!
    • 项目:部署鲜花网络电商的人脉工具,开发易速鲜花聊天客服机器人
    • 实践:从模型调用到数据连接,再到记忆的存储与检索,掌握构建智能系统的每个环节
    • 目标:能够独立利用LangChain构建智能问答系统,适用于企业或个人需求

LangChain 中的输出解析器

一、输出解析器

语言模型输出文本供人类阅读,但有时需获取程序能处理的结构化信息,输出解析器便发挥作用。它是处理和构建语言模型响应的类,基本的输出解析器类通常要实现两个核心方法:

  1. get_format_instructions:返回字符串,指导语言模型如何格式化输出
  2. parse:接收语言模型输出的字符串,解析成特定数据结构或格式,确保符合预期以便后续处理 还有可选方法parse_with_prompt,它接收语言模型输出和提示,基于原始提示解析输出为特定数据结构,让信息更准确贴合要求。并给出了简单伪代码示例展示其类结构

二、LangChain 中的各类输出解析器

  1. 列表解析器:用于模型输出应为列表的情况,比如询问鲜花库存时,期望模型回答是列表形式
  2. 日期时间解析器:处理与日期、时间相关输出,保证格式正确
  3. 枚举解析器:处理模型输出应为预定义一组值之一的情况,如答案限定 “是” 或 “否” 时确保符合要求
  4. 结构化输出解析器:处理复杂、结构化输出,像生成报告、文章等情况可使用
  5. Pydantic(JSON)解析器:模型输出应为符合特定格式的 JSON 对象时使用,借助 Pydantic 库验证构建复杂数据模型,确保输出符合预期数据模型
  6. 自动修复解析器:能自动修复常见模型输出错误,比如纠正文本中的语法、拼写错误
  7. 重试解析器:在模型初次输出不符合预期时,尝试修复或重新生成正确输出,如日期格式不符时可重新提示模型生成正确格式

Pydantic(JSON)解析器实战

Pydantic解析器在诸多解析器中是常用且重要的一种。它依托于Python数据验证和设置管理库Pydantic,该库主要基于Python类型提示构建。虽然Pydantic本身并非专为JSON所设计,但JSON在现代Web应用以及API交互里属于常见的数据格式,所以Pydantic在处理与验证JSON数据方面有着特别的用处,可在相关程序重构等场景中发挥重要作用,比如用来重构鲜花文案生成程序

# ------Part 1
import os
# os.environ["OPENAI_API_KEY"] = 'Your OpenAI API Key'

# 创建模型实例
from langchain_openai import ChatOpenAI

model = ChatOpenAI(
    model=os.environ.get("LLM_MODELEND"),
)

# ------Part 2
# 创建一个空的DataFrame用于存储结果
import pandas as pd

df = pd.DataFrame(columns=["flower_type", "price", "description", "reason"])

# 数据准备
flowers = ["玫瑰", "百合", "康乃馨"]
prices = ["50", "30", "20"]

# 定义我们想要接收的数据格式
from pydantic.v1 import BaseModel, Field

class FlowerDescription(BaseModel):
    flower_type: str = Field(description="鲜花的种类")
    price: int = Field(description="鲜花的价格")
    description: str = Field(description="鲜花的描述文案")
    reason: str = Field(description="为什么要这样写这个文案")

# ------Part 3
# 创建输出解析器
from langchain.output_parsers import PydanticOutputParser

output_parser = PydanticOutputParser(pydantic_object=FlowerDescription)

# 获取输出格式指示
format_instructions = output_parser.get_format_instructions()
# 打印提示
print("输出格式:", format_instructions)

# ------Part 4
# 创建提示模板
from langchain import PromptTemplate

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

# 根据模板创建提示,同时在提示中加入输出解析器的说明
prompt = PromptTemplate.from_template(
    prompt_template, partial_variables={"format_instructions": format_instructions}
)

# 打印提示
print("提示:", prompt)

# ------Part 5
for flower, price in zip(flowers, prices):
    # 根据提示准备模型的输入
    input = prompt.format(flower=flower, price=price)
    # 打印提示
    print("提示:", input)

    # 获取模型的输出
    output = model.predict(input)
    # 解析模型的输出
    parsed_output = output_parser.parse(output)
    parsed_output_dict = parsed_output.dict()  # 将Pydantic格式转换为字典

    # 将解析后的输出添加到DataFrame中
    df.loc[len(df)] = parsed_output.dict()

# 打印字典
print("输出的数据:", df.to_dict(orient="records"))
  1. 模型实例创建(Part 1)
  • 设置 OpenAI API 密钥(示例中未给出真实密钥),从 langchain_openai 导入 ChatOpenAI 并创建名为 model 的模型实例
  1. 数据准备(Part 2)
  • 导入 pandas 库创建空的 DataFrame 用于存储结果,列名包含 “flower_type”“price”“description”“reason”
  • 定义鲜花种类列表 flowers 和价格列表 prices,同时利用 pydantic.v1 创建 FlowerDescription 类,规定了鲜花相关信息的数据格式
  1. 输出解析器相关操作(Part 3)
  • 从 langchain.output_parsers 导入 PydanticOutputParser,基于 FlowerDescription 类创建 output_parser
  • 获取输出格式指示 format_instructions 并打印展示,用于指导后续模型输出的格式
  1. 提示模板创建(Part 4)
  • 从 langchain 导入 PromptTemplate,构建 prompt_template 提示模板,内容围绕撰写鲜花店文案的要求,并融入输出格式指示
  • 根据模板创建 prompt 提示,然后打印展示该提示内容
  1. 循环处理与结果存储(Part 5)
  • 循环遍历鲜花种类和价格列表,根据 prompt 准备模型输入并打印,获取模型输出后,用 output_parser 解析输出,将 Pydantic 格式转换为字典形式
  • 把解析后的输出字典添加到之前创建的 DataFrame 中,最后打印 DataFrame 转换后的字典形式数据,即得到包含鲜花相关文案信息的数据结果

自动修复解析器(OutputFixingParser)实战

# 导入所需要的库和模块
from langchain.output_parsers import PydanticOutputParser
from pydantic import BaseModel, Field
from typing import List

# 使用Pydantic创建一个数据格式,表示花
class Flower(BaseModel):
    name: str = Field(description="name of a flower")
    colors: List[str] = Field(description="the colors of this flower")

# 定义一个用于获取某种花的颜色列表的查询
flower_query = "Generate the charaters for a random flower."

# 定义一个格式不正确的输出
misformatted = "{'name': '康乃馨', 'colors': ['粉红色','白色','红色','紫色','黄色']}"

# 创建一个用于解析输出的Pydantic解析器,此处希望解析为Flower格式
parser = PydanticOutputParser(pydantic_object=Flower)
# 使用Pydantic解析器解析不正确的输出
# parser.parse(misformatted) # 这行代码会出错

# 从langchain库导入所需的模块
from langchain.output_parsers import OutputFixingParser
from langchain_openai import ChatOpenAI

# 设置OpenAI API密钥
import os

# os.environ["OPENAI_API_KEY"] = 'Your OpenAI API Key'

# 使用OutputFixingParser创建一个新的解析器,该解析器能够纠正格式不正确的输出
new_parser = OutputFixingParser.from_llm(
    parser=parser,
    llm=ChatOpenAI(
        model=os.environ.get("LLM_MODELEND"),
    ),
)

# 使用新的解析器解析不正确的输出
result = new_parser.parse(misformatted)  # 错误被自动修正
print(result)  # 打印解析后的输出结果
  • 导入所需库和模块,包括 langchain 相关输出解析器、pydantic 相关类以及 typing 模块等
  • 利用 pydantic 创建 Flower 类定义花的数据格式,含花名和颜色列表两个属性
  • 定义查询语句 flower_query 以及格式不正确的输出 misformatted
  • 基于 Flower 类创建 PydanticOutputParser 解析器,若直接用其解析错误输出会报错
  • 从 langchain 库导入相关模块,准备设置 OpenAI API 密钥(示例中未给出真实密钥)
  • 使用 OutputFixingParser 结合已有解析器和 ChatOpenAI 创建新解析器,旨在纠正格式错误的输出

重试解析器(RetryWithErrorOutputParser)实战

OutputFixingParser 虽有一定作用,但仅能做简单格式修复。若出现如输出内容不完整、有缺失等更复杂的错误,仅凭输出与格式本身,该解析器无法修复。而 LangChain 提供的重试解析器,通过实现 parse_with_prompt 方法,可借助大模型的推理能力,依据原始提示找回相关信息

# 定义一个模板字符串,这个模板将用于生成提问
template = """Based on the user question, provide an Action and Action Input for what step should be taken.
{format_instructions}
Question: {query}
Response:"""

# 定义一个Pydantic数据格式,这个格式描述了一个"行动"类及其属性
from pydantic import BaseModel, Field

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


# 使用Pydantic格式Action来初始化一个输出解析器
from langchain.output_parsers import PydanticOutputParser

parser = PydanticOutputParser(pydantic_object=Action)

# 定义一个提示模板,它将用于向模型提问
from langchain.prompts import PromptTemplate

prompt = PromptTemplate(
    template="Answer the user query.\n{format_instructions}\n{query}\n",
    input_variables=["query"],
    partial_variables={"format_instructions": parser.get_format_instructions()},
)
prompt_value = prompt.format_prompt(query="What are the colors of Orchid?")

# 定义一个错误格式的字符串
bad_response = '{"action": "search"}'
# parser.parse(bad_response) # 如果直接解析,它会引发一个错误

# 设置OpenAI API密钥
import os
from langchain_openai import ChatOpenAI

# 尝试用OutputFixingParser来解决这个问题
from langchain.output_parsers import OutputFixingParser

fix_parser = OutputFixingParser.from_llm(
    parser=parser,
    llm=ChatOpenAI(
        model=os.environ.get("LLM_MODELEND"),
    ),
)
parse_result = fix_parser.parse(bad_response)
print("OutputFixingParser的parse结果:", parse_result)

# 初始化RetryWithErrorOutputParser,它会尝试再次提问来得到一个正确的输出
from langchain.output_parsers import RetryWithErrorOutputParser

retry_parser = RetryWithErrorOutputParser.from_llm(
    parser=parser, llm=ChatOpenAI(model=os.environ.get("LLM_MODELEND"), temperature=0)
)
parse_result = retry_parser.parse_with_prompt(bad_response, prompt_value)
print("RetryWithErrorOutputParser的parse结果:", parse_result)
  • 定义提问模板,含格式化指令与用户问题占位符,用于引导模型回复 “行动” 及 “行动输入” 相关内容
  • 用 Pydantic 创建 Action 类定义数据格式,约束输出结构
  • 基于 Action 类初始化输出解析器,确保模型输出符合格式要求
  • 构建提示模板,结合解析器生成具体的提问内容
  • 定义一个格式错误的字符串模拟不符合预期的模型输出
  • 先使用 OutputFixingParser 尝试修复错误格式的输出并查看结果
  • 再用 RetryWithErrorOutputParser 结合提示重新获取正确输出并查看结果,整体展示处理模型输出不符合预期情况的流程

写在最后

结构化解析器与 Pydantic 解析器用于获取大语言模型格式化输出,前者适配简单文本,后者能应对复杂数据结构;自动修复解析器可纠正小格式错误,较为 “被动”,重试解析器能处理如格式错误、内容缺失等复杂问题,靠重新与模型交互完善输出。选择解析器时需考量应用场景,仅存在格式问题可选自动修复解析器,若重视输出完整性和准确性则重试解析器更合适……无论如何,有一点毋庸置疑。我们正站在一个崭新的历史节点上。