本篇博客主要介绍Langchain
中的Message和Prompt封装
1. Message封装
1.1 Messages
Messages对象可以用在提示词和聊天信息中。目前主要有Message
和MessageChunk
两种类型,这里仅以Message
类为主进行介绍。Message
类主要有以下几种:
- AIMessage: 大模型返回的信息,类似于OpenAI模型中
assistant
类型的消息。 - ChatMessage: ChatMessage有一个
role
字段,用于标识消息发送者的角色或类型。 - FunctionMessage/ToolMessage:向LLM返回函数或工具得到的信息。FunctionMessage是ToolMessage的旧版本。
- SystemMessage: 系统角色信息,类似于OpenAI模型中
system
类型的消息。 - HumanMessage:用户角色的信息,类似于OpenAI模型中
user
类型的消息。
关于Message
类信息其用法举例如下(目前只能用到content
参数):
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage,SystemMessage,ToolMessage
from dotenv import load_dotenv,find_dotenv
_=load_dotenv(find_dotenv())
client=ChatOpenAI(
model_kwargs={"tools":[{
"type": "function",
"function": {
"name": "sum",
"description": "加法器,计算一组数的和",
"parameters": {
"type": "object",
"properties": {
"numbers": {"type": "array", "items": { "type": "number"}}
}
}
}
}]})
messages=[
SystemMessage(content="你是一个数学家,可以计算所有整数的和。"),
HumanMessage(content="请计算12343、4363、984773这三个整数的和"),
]
response=client.invoke(messages)
#response的类型为AIMessage类型
if response.additional_kwargs['tool_calls']:
tool=response.additional_kwargs['tool_calls'][0]
arguments=eval(tool['function']['arguments'])
fun_message=ToolMessage(
content=str(sum(arguments['numbers'])),
tool_call_id=tool['id'])
#ToolMessage向大模型返回function calling执行的结果
messages.extend([response,fun_message])
response2=client.invoke(messages)
print(response2)
其执行结果如下(返回的类型是AIMessage类型):
content='这三个整数的和为1001479。' response_metadata={'token_usage': {'completion_tokens': 13, 'prompt_tokens': 114, 'total_tokens': 127}, 'model_name': 'gpt-3.5-turbo', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None} id='run-38d36c7c-3b4f-49a9-9711-4b6d79347311-0'
注意,只有使用Chat模型的时候才会输出上述结果。
当模型采用流式输出时会返回MessageChunk
类型,具体举例如下:
prompt=PromptTemplate.from_template("输出1到{max_value}之间的所有整数。每个数字之间用逗号,分隔。")
llm=ChatOpenAI(model_kwargs={"stream":True})
stream_chain=prompt|llm
for chunk in stream_chain.stream({"max_value":"4"}):
print(chunk)
其执行结果展示如下:
content='' id='run-d90bbeb4-ff10-491a-9a86-953ce08dbdf6'
content='1' id='run-d90bbeb4-ff10-491a-9a86-953ce08dbdf6'
content=',' id='run-d90bbeb4-ff10-491a-9a86-953ce08dbdf6'
content=' ' id='run-d90bbeb4-ff10-491a-9a86-953ce08dbdf6'
content='2' id='run-d90bbeb4-ff10-491a-9a86-953ce08dbdf6'
content=',' id='run-d90bbeb4-ff10-491a-9a86-953ce08dbdf6'
content=' ' id='run-d90bbeb4-ff10-491a-9a86-953ce08dbdf6'
content='3' id='run-d90bbeb4-ff10-491a-9a86-953ce08dbdf6'
content=',' id='run-d90bbeb4-ff10-491a-9a86-953ce08dbdf6'
content=' ' id='run-d90bbeb4-ff10-491a-9a86-953ce08dbdf6'
content='4' id='run-d90bbeb4-ff10-491a-9a86-953ce08dbdf6'
content='' response_metadata={'finish_reason': 'stop'} id='run-d90bbeb4-ff10-491a-9a86-953ce08dbdf6'
2. Prompts封装
Prompt提示通常由多个组件和提示值构成。Langchain提供的prompt子类可以在以下页面上查询到: https://api.python.langchain.com/en/latest/core_api_reference.html#module-langchain_core.prompts 。这里按照这些prompt的父类的分类分别介绍:
2.1 StringPromptTemplate类
StringPromptTemplate类主要包括以下几种prompt类:PromptTemplate
、 FewShotPromptTemplate
和FewShotPromptWithTemplates
。
2.1.1 PromptTemplate类
这里先来看PromptTemplate
类的基本用法。PromptTemplate
的方法主要有以下(这里不会介绍所有方法):
from_examples
、from_file
和from_template
。这三个是类方法,主要用来生成提示词模板PromptTemplate
(提示词模板中存在未被替换的变量)。具体用法举例如下:
from langchain_core.prompts import PromptTemplate
prompt_temp1=PromptTemplate.from_template(template="""
你是一个专业的翻译官,现在你需要将以下英文文本翻译成中文:
{text}
""",template_format="f-string")
print(prompt_temp1)
prompt_temp2=PromptTemplate.from_file("./test_langchain/template.txt")
print(prompt_temp2)
prompt_temp3=PromptTemplate.from_examples(
examples=[r"{text}\n"],
suffix="用Markdown格式输出",
prefix="你会一个专业的翻译官,现在你需要将以下英文文本翻译成中文:",
input_variables=["text"]
)
print(prompt_temp3)
其输出结果如下:
input_variables=['text'] template='\n你是一个专业的翻译官,现在你需要将以下英文文本翻译成中文:\n{text}\n'
input_variables=['text'] template='你是一个专业的翻译官,现在你需要将以下英文文本翻译成中文:\n{text}'
input_variables=['text'] template='你会一个专业的翻译官,现在你需要将以下英文文本翻译成中文:\n\n{text}\\n\n\n用Markdown格式输出'
Tips:如果提示词模板中没有待填充的变量,就尽量不要使用提示词模板类
format_prompt
和format
:将PromptTemplate
模板中的变量填充生成正式的提示词(但是返回值类型不同)。这个方法还有同作用的异步方法aformat
和aformat_prompt
。
from langchain_core.prompts import PromptTemplate
import asyncio
prompt_temp=PromptTemplate.from_template(template="""
你是一个专业的翻译官,现在需要你帮助翻译以下英文文本:
{text}
请翻译成中文,并输出为Markdown格式。
""",template_format="f-string")
#template_format还可以接受jinja2类型
prompt1=prompt_temp.format(text="hello world")
print(prompt1)
prompt2=prompt_temp.format_prompt(text="good morning")
print(prompt2)
##async方法
prompt_temp=PromptTemplate.from_template(template="""
你是一个专业的翻译官,现在需要你帮助翻译以下英文文本:
{text}
请翻译成中文,并输出为Markdown格式。
""",template_format="f-string")
#template_format还可以接受jinja2类型
async def prompt_format():
prompt1=await prompt_temp.aformat(text="hello world")
prompt2=await prompt_temp.aformat(text="good morning")
print(prompt1)
print(prompt2)
asyncio.run(prompt_format())
其结果如下:
你是一个专业的翻译官,现在需要你帮助翻译以下英文文本:
hello world
请翻译成中文,并输出为Markdown格式。
text='\n你是一个专业的翻译官,现在需要你帮助翻译以下英文文本:\ngood morning\n请翻译成中文,并输出为Markdown格式。\n'
你是一个专业的翻译官,现在需要你帮助翻译以下英文文本:
hello world
请翻译成中文,并输出为Markdown格式。
你是一个专业的翻译官,现在需要你帮助翻译以下英文文本:
good morning
请翻译成中文,并输出为Markdown格式。
invoke
:与format
方法相比,当用户需要创建链的时候,并且这个链的一部分涉及到使用提示模板时,则可以使用该方法。invoke
不仅仅可以格式化字符串,还可以处理输入、输出和其他复杂的逻辑。具体举例如下(运行结果不展示):
from langchain_core.prompts import PromptTemplate
from langchain_openai import OpenAI
from dotenv import load_dotenv,find_dotenv
_=load_dotenv(find_dotenv())
template=PromptTemplate.from_template(template="""
你是一个专业的翻译官,现在需要你帮助翻译以下英文文本:
{text}
请翻译成中文,并输出为Markdown格式。
""",template_format="f-string")
llm=OpenAI()
chain = template|llm
print(chain.invoke("hello world"))
batch
和batch_as_completed
:这两个方法都用于批量处理多个输入数据,并且每个输入数据使用invoke
方法执行,但二者略有不同。batch
使用thread线程池执行,而batch_as_completed
方法当每个输入数据处理完成后立即返回结果。这种方法适用于输入数据量较大或希望逐步处理并获取结果的场景。它可以提高处理效率,尤其是在处理时间较长的任务时。同时这两个方法也有async类方法。其用法举例如下:
from langchain.prompts import PromptTemplate
template = PromptTemplate(
input_variables=["location", "activity"],
template="What is the best time to visit {location} for {activity}?"
)
inputs = [
{"location": "Paris", "activity": "sightseeing"},
{"location": "New York", "activity": "shopping"},
{"location": "Tokyo", "activity": "cultural experiences"}
]
batch_results = template.batch(inputs)
for result in batch_results:
print(result)
其结果如下:
text='What is the best time to visit Paris for sightseeing?'
text='What is the best time to visit New York for shopping?'
text='What is the best time to visit Tokyo for cultural experiences?'
partial
该方法可以对提示词模板进行部分填充,该方法可以帮助用户逐步构建最终的提示词模板。注意该方法返回的仍是提示词模板,其用法举例如下:
from langchain_core.prompts import PromptTemplate
template=PromptTemplate(template="{name},{age}",
input_variables=["name","age"],)
print(template)
template=template.partial(name="John")
print(template)
template=template.format(age=30)
print(template)
其结果如下:
input_variables=['age', 'name'] template='{name},{age}'
input_variables=['age'] partial_variables={'name': 'John'} template='{name},{age}'
John,30
2.1.2 FewShotPromptTemplate类
FewShotPromptTemplate
类是LangChain内置的一个少样本提示词模板类,其独特之处在于支持动态添加示例和选择器。其用法举例如下:
from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate
samples = [
{"flower_type": "玫瑰", "occasion": "浪漫", "ad_copy": "玫瑰,象征着浪漫,是你向心爱的人表达爱意的最佳选择"},
{"flower_type": "康乃馨", "occasion": "母亲节", "ad_copy": "康乃馨,代表着孝心,是献给母亲最好的礼物"},
]
example_prompt = PromptTemplate(
input_variables=["flower_type", "occasion", "ad_copy"],
template="鲜花类型: {flower_type} 场合: {occasion} 广告文案: {ad_copy}"
)
few_shot_prompt_template = FewShotPromptTemplate(
examples=samples,
example_prompt=example_prompt, # 如果不需要自定义示例模板,也可以省略
prefix="以下是一些鲜花广告文案的示例:\n",
suffix="现在,请为以下鲜花类型和场合生成广告文案:\n鲜花类型: {flower_type} 场合: {occasion} 广告文案:",
input_variables=["flower_type", "occasion"]
)
few_shot_prompt=few_shot_prompt_template.format(flower_type="红玫瑰", occasion="情人节")
print(few_shot_prompt)
其执行结果如下:
以下是一些鲜花广告文案的示例:
鲜花类型: 玫瑰 场合: 浪漫 广告文案: 玫瑰,象征着浪漫,是你向心爱的人表达爱意的最佳选择
鲜花类型: 康乃馨 场合: 母亲节 广告文案: 康乃馨,代表着孝心,是献给母亲最好的礼物
现在,请为以下鲜花类型和场合生成广告文案:
鲜花类型: 红玫瑰 场合: 情人节 广告文案:
2.2 BaseStringMessagePromptTemplate类
BaseStringMessagePromptTemplate的主要包括以下几种prompt子类:ChatMessagePromptTemplate
、HumanMessagePromptTemplate
、AIMessagePromptTemplate
和SystemMessagePromptTemplate
。这几种prompt类经过值填充后会生成Message
类数据。其用法举例如下(以千问大模型为例):
from langchain_community.chat_models import ChatTongyi
from langchain.schema import SystemMessage,AIMessage
from langchain_core.prompts import (ChatMessagePromptTemplate,
HumanMessagePromptTemplate,
PromptTemplate)
ChatMessage=ChatMessagePromptTemplate(role='system',prompt=PromptTemplate.from_template('{content}'))
sys_msg = ChatMessage.format(content="你是一个客服助手,你叫小爱。")
usr_template = HumanMessagePromptTemplate.from_template("{usr_question}")
usr_msg=usr_template.format(usr_question="你是谁?")
chat_Model=ChatTongyi(model_name="qwen-turbo")
messages=[sys_msg,usr_msg]
res=chat_Model.invoke(messages)
print(res.content)
其返回结果如下:
我是小爱,一个客服助手,专门在这里为您提供帮助和解答问题。有什么可以帮到您的吗?
2.3 PipelinePromptTemplate类
PipelinePromptTemplate
类可以组合不同的提示词模板,在希望重用部分提示词的时非常有效。其用法举例如下:
from langchain_core.prompts import PromptTemplate,PipelinePromptTemplate
ask_name_prompt = PromptTemplate(
template="What is your name?,my name is {name}",
input_variables=[]
)
# 定义第二个提示模板,根据用户的名字进行问候
greet_user_prompt = PromptTemplate(
template="Hello, {name}! How can I assist you today?",
input_variables=["name"]
)
final_prompt=PromptTemplate(template="{user},\n{greet}",input_variables=["name","greet"])
# 将这两个提示连接成一个 PipelinePrompt
pipeline_prompt = PipelinePromptTemplate(
pipeline_prompts=[("user",ask_name_prompt),
("greet",greet_user_prompt)],
final_prompt=final_prompt
)
print(pipeline_prompt.format(name="Alice"))
其结果如下:
What is your name?,my name is Alice,
Hello, Alice! How can I assist you today?