为什么用提示词模板

通常情况下,模板的诞生,往往就是为了提取出公共的部分,提高重复工作的效率。提示词模板亦是如此,它被广泛应用与构建LLM应用的各个环节

简单来说,只要是需要动态、批量、或有结构地向⼤语⾔模型【发送请求】的地⽅,⼏乎都会⽤到提⽰词模板。

简单的例子

就比如我老家在浙江绍兴,大学在湖南长沙,实习地点在广东深圳,我需要询问大模型各个地方的天气,那我会有如下消息要发送:

  • 绍兴的天气怎么样
  • 长沙的天气怎么样
  • 深圳的天气怎么样

然后我们发现这几次询问都有大量重复内容,我们就可以把它模板化为如下描述

1
2
3
4
5
6
template = {
template_content="{city}的城市天气怎么样",
variables: {
"city"="绍兴"
}
}

然后我们在使用模板的时候,依次替换城市即可。这样子的模板工具,我们就可以把它封装成简单易用的前端接口暴露给用户使用,比如概念图如下

提⽰词模板就是⼀个可复⽤的提⽰词蓝图,它允许我们动态地⽣成提⽰词,⽽不是每次都⼿动编写完整的提⽰词。它类似于编程中的字符串格式化功能。

提示词模板解决了以下几个核心问题:

  1. 可复⽤性:只需定义⼀个模板,就可以⽤于⽆数个类似的查询。
  2. 关注点分离:将提⽰词的结构和逻辑(⼯程)与具体的内容和数据分离开。提⽰⼯程师可以专注于优化模板,⽽应⽤程序则负责提供变量值。
  3. ⼀致性:确保发送给LLM的提⽰词结构统⼀,这有助于获得更稳定、可预测的输出结果。
  4. 可维护性:如果需要修改提⽰词的⻛格或结构,只需修改⼀个模板⽂件,⽽不⽤在代码的⽆数个地⽅进⾏修改

如何使用

字符串模板

这可以算是最简单的一类模板实现方式了。我们可以使用PromptTemplate创建runnable实例

1
2
3
4
5
6
7
8
from langchain_core.prompts import PromptTemplate

prompt_template = PromptTemplate.from_template("Say twice the following sentence. {text_sentence}")

print(prompt_template.invoke({"text_sentence": "man!"}))

print(prompt_template.invoke({"text_sentence": "HAHA!"}))

输出如下,就跟f字符串差不多

1
2
text='Say twice the following sentence. man!'
text='Say twice the following sentence. HAHA!'

聊天消息模板

ChatPromptTemplate 模板:专为LangChain聊天模型设计。可以⽅便地构建包含

  • SystemMessage
  • HumanMessage
  • AIMessage
    的消息模板。

比如我们要指定大模型把输入的{user_input}重复{repeat_time}次, 我们就可以一次性在模板中声明两条消息:一条系统消息,一条用户消息(Human Message)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from langchain_core.prompts import ChatPromptTemplate

# 1. 创建模板,这里是消息列表,有一个系统消息和一个用户消息
prompt_template = ChatPromptTemplate(
[
("system", "Say {repeat_time} times everything that follows after"),
("user", "{user_input}")
]
)

# 实例化模板,但还不是消息列表类型
msg_val = prompt_template.invoke(
{
"repeat_time":"3",
"user_input":"HAHA!"
}
)
# 3.转换成消息队列
messages = msg_val.to_messages()
print(messages)
from langchain_openai import ChatOpenAI
model = ChatOpenAI(model="gpt-4o-mini")
print(model.invoke(messages).content)

输出如下,可以看到实例化的消息列表,和大模型HAHA!了三次

1
2
[SystemMessage(content='Say 3 times everything that follows after', additional_kwargs={}, response_metadata={}), HumanMessage(content='HAHA!', additional_kwargs={}, response_metadata={})]
HAHA! HAHA! HAHA!

消息模板实现了标准Rubbable接口,因此也可以组成链式调用,例如上面的代码可以修改成:

1
2
3
4
5
6
7
8
9
10
# 2.组成链式调用
parser = StrOutputParser()
model = ChatOpenAI(model="gpt-4o-mini")
chain = prompt_template | model | parser
print(chain.invoke(
{
"repeat_time": "3",
"user_input": "HAHA!"
}
))

消息占位符

这个名字可能一下子看不出用途。解释一下就是把消息列表也当作参数传入到提示词模板中,就像其它字符串参数一样。

先前我们都是声明好指定数量的消息,然后往消息内插入参数,现在有了消息占位符,我们可以往消息列表中的特定位置插入消息或者消息列表了。

要使用消息占位符,我们可以声明MessagesPlaceholder()对象来生成指定名称的消息类型的占位符,当然我们也可以统一语法地使用("placeholder", "{msgs}")

示例代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from langchain_core.prompts import ChatPromptTemplate,MessagesPlaceholder
from langchain_core.messages import HumanMessage,AIMessage

# 1. 创建模板,这里是消息列表,有一个系统消息和一个用户消息
prompt_template = ChatPromptTemplate(
[
("system", "回答世界地理问题"),
# MessagesPlaceholder("msgs")
("placeholder", "{msgs}") # 两种表述是等价的
]

)

param_msgs = [
HumanMessage("亚洲第一大国是谁?"),
AIMessage("是印度,因为它人杰地灵,人口最多"),
HumanMessage("那俄罗斯为什么不是亚洲第一大国?")
]

from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser

# 2.组成链式调用
parser = StrOutputParser()
model = ChatOpenAI(model="gpt-4o-mini")
chain = prompt_template | model | parser
print(chain.invoke(
{
"msgs": param_msgs,
}
))

输出如下

1
俄罗斯虽然地理上有很大一部分位于亚洲,但其面积和人口分布的情况使得它通常被视为跨洲国家。俄罗斯的领土面积确实是世界上最大的,但由于其整体经济和政治中心主要位于欧洲部分,因此在地理上,印度通常被认为是亚洲第一大国。实际上,俄罗斯也被认为是一个在亚洲和欧洲都有显著影响力的国家。

使⽤LangChainHub的提⽰词模板

LangChain Hub是⼀个⽤于上传、浏览、拉取和管理提⽰词(prompts)的地⽅,

随着LLM的发展,提⽰变得越来越重要。LangChain正在打造⼀个与像GitHub这样的传统平台,
GitHub⻓期以来⼀直是共享和协作代码的⾸选平台。于是推出了LangChainHub平台🔗