青训营X豆包MarsCode技术训练营第五课|豆包MarsCode Al刷题

15 阅读8分钟

LangChain的输出解析器偷偷的在提示中加了一段话,也就是 {format_instructions} 中的内容。这段由LangChain自动添加的文字,就清楚地指示着我们希望得到什么样的回答以及回答的具体格式。提示指出,模型需要根据一个schema来格式化输出文本,这个 schema 从 ```json 开始,到 ``` 结束

这就是在告诉模型,你就follow这个schema(schema,可以理解为对数据结构的描述)的格式,就行啦!

这就是一个很棒、很典型的提示工程。有了这样清晰的提示,智能程度比较高的模型(比如GPT3.5及以上版本),肯定能够输出可以被解析的数据结构,如JSON格式的数据。

那么这节课我就带着你进一步深究,如何利用LangChain中的提示模板,做好提示工程。

上节课我说过,针对大模型的提示工程该如何做,吴恩达老师在他的 ChatGPT Prompt Engineering for Developers 公开课中,给出了两个大的原则:第一条原则是写出清晰而具体的指示,第二条原则是给模型思考的时间。

无独有偶,在Open AI的官方文档 GPT 最佳实践中,也给出了和上面这两大原则一脉相承的6大策略。分别是:

  1. 写清晰的指示
  2. 给模型提供参考(也就是示例)
  3. 将复杂任务拆分成子任务
  4. 给GPT时间思考
  5. 使用外部工具
  6. 反复迭代问题

怎么样,这些原则和策略是不是都是大白话?这些原则其实不仅能够指导大语言模型,也完全能够指导你的思维过程,让你处理问题时的思路更为清晰。所以说,大模型的思维过程和我们人类的思维过程,还是蛮相通的。

提示的结构

当然了,从大原则到实践,还是有一些具体工作需要说明,下面我们先看一个实用的提示框架。

图片

在这个提示框架中:

  • 指令(Instuction)告诉模型这个任务大概要做什么、怎么做,比如如何使用提供的外部信息、如何处理查询以及如何构造输出。这通常是一个提示模板中比较固定的部分。一个常见用例是告诉模型“你是一个有用的XX助手”,这会让他更认真地对待自己的角色。
  • 上下文(Context)则充当模型的额外知识来源。这些信息可以手动插入到提示中,通过矢量数据库检索得来,或通过其他方式(如调用API、计算器等工具)拉入。一个常见的用例时是把从向量数据库查询到的知识作为上下文传递给模型。
  • 提示输入(Prompt Input)通常就是具体的问题或者需要大模型做的具体事情,这个部分和“指令”部分其实也可以合二为一。但是拆分出来成为一个独立的组件,就更加结构化,便于复用模板。这通常是作为变量,在调用模型之前传递给提示模板,以形成具体的提示。
  • 输出指示器(Output Indicator)标记​​要生成的文本的开始。这就像我们小时候的数学考卷,先写一个“解”,就代表你要开始答题了。如果生成 Python 代码,可以使用 “import” 向模型表明它必须开始编写 Python 代码(因为大多数 Python 脚本以import开头)。这部分在我们和ChatGPT对话时往往是可有可无的,当然LangChain中的代理在构建提示模板时,经常性的会用一个“Thought:”(思考)作为引导词,指示模型开始输出自己的推理(Reasoning)。

下面,就让我们看看如何使用 LangChain中的各种提示模板做提示工程,将更优质的提示输入大模型。

LangChain 提示模板的类型

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

这些模板的导入方式如下:

plain
复制代码
AI练中学

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,
)

我发现有时候不指定 .prompts,直接从LangChain包也能导入模板。

plain
复制代码
AI练中学
from langchain import PromptTemplate

下面我们通过示例来介绍上面这些模版,前两个我们简单了解就好,其中最典型的FewShotPromptTemplate会重点讲。至于PipelinePrompt和自定义模板,使用起来比较简单,请你参考LangChain文档自己学习。

使用 PromptTemplate

下面通过示例简单说明一下PromptTemplate的使用。

plain
复制代码
AI练中学
from langchain import PromptTemplate

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

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

输出:

plain
复制代码
AI练中学
你是业务咨询顾问。
你给一个销售鲜花的电商公司,起一个好的名字?

这个程序的主要功能是生成适用于不同场景的提示,对用户定义的一种产品或服务提供公司命名建议。

在这里,"你是业务咨询顾问。你给一个销售{product}的电商公司,起一个好的名字?" 就是原始提示模板,其中 {product} 是占位符。

然后通过PromptTemplate的from_template方法,我们创建了一个提示模板对象,并通过prompt.format方法将模板中的 {product} 替换为 "鲜花"

这样,就得到了一句具体的提示:你是业务咨询顾问。你给一个销售鲜花的电商公司,起一个好的名字? ——这就要求大语言模型,要有的放矢。

在上面这个过程中,LangChain中的模板的一个方便之处是from_template方法可以从传入的字符串中自动提取变量名称(如product),而无需刻意指定。上面程序中的product自动成为了format方法中的一个参数

当然,也可以通过提示模板类的构造函数,在创建模板时手工指定input_variables,示例如下:

plain
复制代码
AI练中学
prompt = PromptTemplate(
    input_variables=["product", "market"], 
    template="你是业务咨询顾问。对于一个面向{market}市场的,专注于销售{product}的公司,你会推荐哪个名字?"
)
print(prompt.format(product="鲜花", market="高端"))

输出:

plain
复制代码
AI练中学
你是业务咨询顾问。对于一个面向高端市场的,专注于销售鲜花的公司,你会推荐哪个名字?

上面的方式直接生成了提示模板,并没有通过from_template方法从字符串模板中创建提示模板。二者效果是一样的。

使用 ChatPromptTemplate

对于OpenAI推出的ChatGPT这一类的聊天模型,LangChain也提供了一系列的模板,这些模板的不同之处是它们有对应的角色。

下面代码展示了OpenAI的Chat Model中的各种消息角色。

plain
复制代码
AI练中学
import openai
openai.ChatCompletion.create(
  model="gpt-3.5-turbo",
  messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "Who won the world series in 2020?"},
        {"role": "assistant", "content": "The Los Angeles Dodgers won the World Series in 2020."},
        {"role": "user", "content": "Where was it played?"}
    ]
)

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

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

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

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

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

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

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

下面,我给出一个示例。

plain
复制代码
AI练中学
# 导入聊天消息类模板
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 Key'
from langchain.chat_models import ChatOpenAI
chat = ChatOpenAI()
result = chat(prompt)
print(result)

输出:

plain
复制代码
AI练中学
content='1. 花语创意\n2. 花韵设计\n3. 花艺创新\n4. 花漾装饰\n5. 花语装点\n6. 花翩翩\n7. 花语之美\n8. 花馥馥\n9. 花语时尚\n10. 花之魅力' 
additional_kwargs={} 
example=False

好吧,尽管模型成功地完成了任务,但是感觉没有咱“易速鲜花”响亮!

讲完上面两种简单易用的提示模板,下面开始介绍今天的重点内容,FewShotPromptTemplate。FewShot,也就是少样本这一概念,是提示工程中非常重要的部分,对应着OpenAI提示工程指南中的第2条——给模型提供参考(也就是示例)。