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

206 阅读10分钟

课程介绍

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

前言

在提示模板的构建过程中加入了 partial_variables,也就是输出解析器指定的 format_instructions 之后,为什么能够让模型生成结构化的输出?

加入 partial_variables 和 format_instructions 使模型明确输出格式和结构。这些指令告诉模型遵循特定的 schema(如 JSON),从而生成符合预期的结构化数据。使用清晰的提示和示例,以及给模型思考的时间,能够提高输出的准确性和质量。这种方法展现了良好的提示工程,帮助模型理解任务并有效回应

提示的结构

  1. 指令(Instruction)明确告诉模型任务的目标和执行方法,比如如何利用外部信息和构造输出。这部分通常比较固定,常见的用法是让模型认清角色,比如“你是一个有用的XX助手”
  2. 上下文(Context)提供额外知识来源。可以是手动插入的信息、从矢量数据库检索的数据,或通过API等工具获取的内容。常用于将查询到的知识传递给模型
  3. 提示输入(Prompt Input)具体的问题或任务。这部分虽然可以与指令合并,但单独拆分后更具结构化,便于模板复用。通常作为变量在调用模型前传递给提示模板
  4. 输出指示器(Output Indicator)标记生成文本的开始,类似于数学题的“解”字。在编写代码时,像“import”这样的词可以指示模型开始生成代码。在与ChatGPT对话时,这部分通常是可选的,但在LangChain中,常用“Thought:”作为引导词以指定推理开始

LangChain 提示模板的类型

LangChain中提供 String(StringPromptTemplate)Chat(BaseChatPromptTemplate) 两种基本类型的模板,并基于它们构建了不同类型的提示模板

模板的导入方式如下:

from langchain.prompts.prompt import PromptTemplate
from langchain.prompts import FewShotPromptTemplate
from langchain.prompts.pipeline import PipelinePromptTemplate
from langchain.prompts import ChatPromptTemplate
from langchain.prompts import (
    ChatMessagePromptTemplate,
    SystemMessagePromptTemplate,
    AIMessagePromptTemplate,
    HumanMessagePromptTemplate,
)

使用 PromptTemplate

以下程序的功能是生成针对特定产品或市场的公司命名建议

from langchain import PromptTemplate

template = """\
你是业务咨询顾问。
你给一个销售{product}的电商公司,起一个好的名字?
"""
prompt = PromptTemplate.from_template(template)

print(prompt.format(product="鲜花"))

prompt = PromptTemplate(
    input_variables=["product", "market"],
    template="你是业务咨询顾问。对于一个面向{market}市场的,专注于销售{product}的公司,你会推荐哪个名字?",
)
print(prompt.format(product="鲜花", market="高端"))
  1. 定义了一个提示模板:“你是业务咨询顾问。你给一个销售{product}的电商公司,起一个好的名字?”其中的 {product} 是占位符
  2. 使用 PromptTemplate.from_template 方法创建模板对象,自动提取变量。通过 prompt.format 方法将 {product} 替换为具体产品(如“鲜花”),生成具体的提示
  3. 扩展提示模板以包括市场变量,这样可以为不同市场提供更精准的命名建议

使用 ChatPromptTemplate

# 导入聊天消息类模板
from langchain.prompts import (
    ChatPromptTemplate,
    SystemMessagePromptTemplate,
    HumanMessagePromptTemplate,
)

# 模板的构建
template = "你是一位专业顾问,负责为专注于{product}的公司起名。"
system_message_prompt = SystemMessagePromptTemplate.from_template(template)
human_template = "公司主打产品是{product_detail}。"
human_message_prompt = HumanMessagePromptTemplate.from_template(human_template)
prompt_template = ChatPromptTemplate.from_messages(
    [system_message_prompt, human_message_prompt]
)

# 格式化提示消息生成提示
prompt = prompt_template.format_prompt(
    product="鲜花装饰", product_detail="创新的鲜花设计。"
).to_messages()

# 下面调用模型,把提示消息传入模型,生成结果
import os

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

from langchain_openai import ChatOpenAI

chat = ChatOpenAI(
    model=os.environ.get("LLM_MODELEND"),
)
result = chat(prompt)
print(result)

OpenAI对传输到gpt-3.5-turbo和GPT-4的messsage格式说明如下:

消息必须是消息对象的数组,其中每个对象都有一个角色(系统、用户或助理)和内容。对话可以短至一条消息,也可以来回多次。  

通常,对话首先由系统消息格式化,然后是交替的用户消息和助理消息。  

系统消息有助于设置助手的行为。例如,你可以修改助手的个性或提供有关其在整个对话过程中应如何表现的具体说明。但请注意,系统消息是可选的,并且没有系统消息的模型的行为可能类似于使用通用消息,例如“你是一个有用的助手”。  

用户消息提供助理响应的请求或评论。  

助理消息存储以前的助理响应,但也可以由你编写以给出所需行为的示例。

LangChain的ChatPromptTemplate这一系列的模板,就是跟着这一系列角色而设计的

使用 FewShotPromptTemplate

  • Zero-Shot Learning:无须任何示例,依赖语义描述进行推理
  • One-Shot Learning:仅有一个示例,能够学习新类别
  • Few-Shot Learning:使用少量示例(通常几个)进行学习

这些概念在机器学习和深度学习领域中逐渐发展,尤其是在处理现实世界问题时具有重要意义

下面我们来通过LangChain中的FewShotPromptTemplate构建出最合适的鲜花文案

1. 创建示例样本

# 1. 创建一些示例
samples = [
  {
    "flower_type": "玫瑰",
    "occasion": "爱情",
    "ad_copy": "玫瑰,浪漫的象征,是你向心爱的人表达爱意的最佳选择。"
  },
  {
    "flower_type": "康乃馨",
    "occasion": "母亲节",
    "ad_copy": "康乃馨代表着母爱的纯洁与伟大,是母亲节赠送给母亲的完美礼物。"
  },
  {
    "flower_type": "百合",
    "occasion": "庆祝",
    "ad_copy": "百合象征着纯洁与高雅,是你庆祝特殊时刻的理想选择。"
  },
  {
    "flower_type": "向日葵",
    "occasion": "鼓励",
    "ad_copy": "向日葵象征着坚韧和乐观,是你鼓励亲朋好友的最好方式。"
  }
]

这些示例样本是构建FewShotPrompt时,作为例子传递给模型的参考信息

2. 创建提示模板

# 2. 创建一个提示模板
from langchain.prompts.prompt import PromptTemplate
template="鲜花类型: {flower_type}\n场合: {occasion}\n文案: {ad_copy}"
prompt_sample = PromptTemplate(input_variables=["flower_type", "occasion", "ad_copy"], 
                               template=template)
print(prompt_sample.format(**samples[0]))
  1. 配置一个提示模板,将一个示例格式化为字符串,这个格式化程序是一个PromptTemplate对象
  2. 这个对象是根据指定的输入变量和模板字符串来生成提示的
  3. 输入变量包括 "flower_type""occasion""ad_copy"
  4. 模板是一个字符串,其中包含了用大括号包围的变量名,它们会被对应的变量值替换

到这里,我们就把字典中的示例格式转换成了提示模板,可以形成一个个具体可用的LangChain提示。

3. 创建 FewShotPromptTemplate 对象

# 3. 创建一个FewShotPromptTemplate对象
from langchain.prompts.few_shot import FewShotPromptTemplate
prompt = FewShotPromptTemplate(
    examples=samples,
    example_prompt=prompt_sample,
    suffix="鲜花类型: {flower_type}\n场合: {occasion}",
    input_variables=["flower_type", "occasion"]
)
print(prompt.format(flower_type="野玫瑰", occasion="爱情"))
  1. 通过使用上一步骤中创建的prompt_sample,以及samples列表中的所有示例, 创建一个更复杂的提示模板FewShotPromptTemplate
  2. 它包含了多个示例和一个提示,可以使用多个示例来指导模型生成对应的输出
  3. 创建一个新提示,其中包含了根据指定的花的类型“野玫瑰”和场合“爱情”

4. 调用大模型创建新文案

# 4. 把提示传递给大模型
import os
os.environ["OPENAI_API_KEY"] = '你的Open AI Key'
from langchain.llms import OpenAI
model = OpenAI(model_name='gpt-3.5-turbo-instruct')
result = model(prompt.format(flower_type="野玫瑰", occasion="爱情"))
print(result)

使用示例选择器

LangChain给我们提供了示例选择器,来选择最合适的样本

# 5. 使用示例选择器
from langchain.prompts.example_selector import SemanticSimilarityExampleSelector
from langchain.vectorstores import Chroma
from langchain.embeddings import OpenAIEmbeddings

# 初始化示例选择器
example_selector = SemanticSimilarityExampleSelector.from_examples(
    samples,
    OpenAIEmbeddings(),
    Chroma,
    k=1
)

# 创建一个使用示例选择器的FewShotPromptTemplate对象
prompt = FewShotPromptTemplate(
    example_selector=example_selector, 
    example_prompt=prompt_sample, 
    suffix="鲜花类型: {flower_type}\n场合: {occasion}", 
    input_variables=["flower_type", "occasion"]
)
print(prompt.format(flower_type="红玫瑰", occasion="爱情"))
  1. 创建示例选择器:创建一个 SemanticSimilarityExampleSelector 对象,用于根据语义相似性选择最相关的示例
  2. 创建提示模板:使用上述选择器创建一个新的 FewShotPromptTemplate 对象,以便生成提示
  3. 生成具体提示:在生成提示时,示例选择器根据余弦相似度找到与“红玫瑰”相关的示例(如“玫瑰”),并用它来构建Few-Shot模板
  4. 优化Token使用:通过选择相关示例,避免传递过多无关模板,节省了Token的用量

Chain of Thought 实战

“给模型一些示例做参考,模型才能明白你要什么”

COT介绍:

  • Chain of Thought:通过中间推理步骤增强模型的推理能力
  • Few-Shot CoT:在提示中加入示例和思考步骤,引导模型更好地解决问题
  • Zero-Shot CoT:通过简单指令引导模型逐步推理,无需示例

CoT的核心思想是通过生成一系列中间推理步骤来增强模型的推理能力。在Few-Shot CoTZero-Shot CoT两种应用方法中,前者通过提供链式思考示例传递给模型,后者则直接告诉模型进行要按部就班的推理

程序的完整框架

from langchain.prompts import (
    ChatPromptTemplate,
    HumanMessagePromptTemplate,
    SystemMessagePromptTemplate,
)

system_prompt_role = SystemMessagePromptTemplate.from_template(role_template)
system_prompt_cot = SystemMessagePromptTemplate.from_template(cot_template)

# 用户的询问
human_template = "{human_input}"
human_prompt = HumanMessagePromptTemplate.from_template(human_template)

# 将以上所有信息结合为一个聊天提示
chat_prompt = ChatPromptTemplate.from_messages(
    [system_prompt_role, system_prompt_cot, human_prompt]
)

prompt = chat_prompt.format_prompt(
    human_input="我想为我的女朋友购买一些花。她喜欢粉色和紫色。你有什么建议吗?"
).to_messages()

# 接收用户的询问,返回回答结果
response = llm(prompt)
print(response)
  1. 创建聊天模型:使用 ChatOpenAI 类创建一个聊天模型实例。将 temperature 设置为 0,以生成更确定性的回答,输出结果更倾向于最可能的选择
  2. 定义 CoT 模板:AI 的角色和目标描述,思考链条示例,展示 AI 如何理解问题并给出建议
  3. 生成询问模板:使用 PromptTemplate.from_template 方法生成询问模板,SystemMessagePromptTemplate 用于指导模型的指令,HumanMessagePromptTemplate 用于传递用户的问题
  4. 整合聊天提示:使用 ChatPromptTemplate.from_messages 方法整合角色、CoT 模板和用户询问,生成最终的聊天提示

写在最后

提供示例对于解决某些任务至关重要,通常情况下,FewShot的方式能够显著提高模型回答的质量。不过,当少样本提示的效果不佳时,这可能表示模型在任务上的学习不足。在这种情况下,我们建议对模型进行微调或尝试更高级的提示技术。此外,还介绍了Chain of Thought,探讨了如何利用它引导大型语言模型进行更深入的推理……无论如何,有一点毋庸置疑。我们正站在一个崭新的历史节点上。