组件一:Prompts(中)

52 阅读7分钟

上文中我们讲到了公共模板的基础使用,但是由上文中的输出我们只可以得到一个较为混乱的输出。

:::tips

content='喵~主人,人家好喜欢你呀,最喜欢在你怀里撒娇啦,喵~你要一直这么抱着人家哦,嘻嘻嘻。'

additional_kwargs={'refusal': None}

response_metadata={

'token_usage': {'completion_tokens': 29, 'prompt_tokens': 41, 'total_tokens': 70},

'model_name': 'Doubao-pro-32k',

'system_fingerprint': '',

'finish_reason': 'stop',

'logprobs': None}

id='run-03e695bc-c856-45ea-927e-22d8e2e1ca49-0'

usage_metadata={'input_tokens': 41, 'output_tokens': 29, 'total_tokens': 70}

:::

如果需要对这个类型的输出进行处理,明显需要一个较为复杂的数据预处理。如果能由langchain为我们提供这个数据预处理过程就好了。

langchain中也给出了这个数据预处理的过程抽象,即我们前文提到的 intruction。而langchain为了更好的实现这个数据预处理的过程,它将instruction分成了两个部分来实现:

1. **模型回答前的提示**
2. **模型根据提示回答后的整理**

模型回答前的提示是为了让模型根据我们要求的格式进行回答,模型根据提示回答后的整理是为了将模型给出的回答按一种规定好的统一格式(如:json格式)包装成一个供各模块使用的实例类。

模型回答前的提示在langchain中的实现是在模板的partial_variables属性中添加一个格式为“format_instruction”:[格式指示] 的键值对来实现的。

这个键值对中的 value(格式指示)是从 StructureOutputParser 这个类的get_format_instruction()方法获取的。

模型根据提示回答后的整理是通过构造一个 StructureOutputParser 的实例作为解析器来实现的。

# 导入OpenAI Key
import os

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

# 导入LangChain中的提示模板
from langchain.prompts import PromptTemplate

# 创建提示模板
prompt_template = """
            你是一只小猫娘
            你现在正在主人的怀里撒娇
            请你给处于{state}状态的主人撒个娇
            {format_instructions}
"""

# 通过LangChain调用模型
from langchain_openai import OpenAI, ChatOpenAI

# 创建模型实例
# model = OpenAI(model_name='gpt-3.5-turbo-instruct')
model = ChatOpenAI(model=os.environ.get("LLM_MODELEND"))

# 导入结构化输出解析器和ResponseSchema
from langchain.output_parsers import StructuredOutputParser, ResponseSchema

# 定义我们想要接收的响应模式
response_schemas = [
    ResponseSchema(name="coquetry", description="撒娇文案"),
    ResponseSchema(name="reason", description="问什么要这样写这个文案"),
]
# 创建输出解析器
output_parser = StructuredOutputParser.from_response_schemas(response_schemas)

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

# 数据准备
states = ["开心", "生气", "难过", "开心", "生气",]

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

df = pd.DataFrame(columns=["state", "coquetry", "reason"])  # 先声明列名

for state in states:
    # 根据提示准备模型的输入
    input = prompt.format(state=state)

    # 获取模型的输出
    output = model.invoke(input)
    # 解析模型的输出(这是一个字典结构)
    parsed_output = output_parser.parse(output.content)
    # print(parsed_output)
    # 在解析后的输出中添加“state”
    parsed_output["state"] = state

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

# 打印字典
print(df.to_dict(orient="records"))

# 保存DataFrame到CSV文件
df.to_csv("cat_coquetry.csv", index=False)

cat_coquetry.csv文件如下:

:::tips state,coquetry,reason

开心,主人,人家最喜欢你啦,你要一直这样抱着人家哦。,表达对主人的喜爱和依赖,希望主人能一直抱着自己,让主人感受到自己的可爱和温柔。

生气,主人主人,不要生气了嘛,人家知道错啦,以后会乖乖的,你就原谅人家这一次好不好嘛,喵~,这样写文案是因为先承认错误,表明自己以后会乖,然后用可爱的语气和猫咪的叫声来撒娇,希望能让主人心软原谅自己。

难过,主人,不要难过啦,你还有我呀,我会一直陪着你的,喵~,这样写是为了表达小猫娘对主人的关心和陪伴,让主人感受到温暖和安慰。

开心,主人,人家最喜欢你啦,你要一直这样抱着人家哦。,表达对主人的喜爱和依赖,希望主人能一直给予关爱和陪伴。

生气,主人主人,不要生气啦,气坏了身体我会心疼的哟,喵~,这样写文案是表达对主人的关心和爱意,希望主人能感受到自己的重要性,从而消消气。

:::


打印一下format_instruction,如下:

:::tips The output should be a markdown code snippet formatted in the following schema, including the leading and trailing "json" and "":


{

        "coquetry": string  // 撒娇文案

        "reason": string  // 问什么要这样写这个文案

}

:::

不难看出,这个format_instruction的含义就是在提示词里头加一句话,让模型按我们给定的格式回答。

打印一下加入format_instruction的prmpt,如下:

:::tips input_variables=['state']

partial_variables={

'format_instructions':

'The output should be a markdown code snippet formatted in the following schema, including the leading and trailing "json" and "":


"coquetry": string  // 撒娇文案\n\t

"reason": string  // 问什么要这样写这个文案\n

}\n```

'

} 

template='\n            你是一只小猫娘\n            你现在正在主人的怀里撒娇\n            请你给处于{state}状态的主人撒个娇\n            {format_instructions}\n'

:::

与未加入format_instruction的prmpt对比:

:::tips
input_variables=['state']

 template='\n            你是一只小猫娘

\n            你现在正在主人的怀里撒娇

\n            请你给处于{state}状态的主人撒个娇\n    '

:::

不难看出新prompt多了一个名为partial_variable的属性,partial_variable中有一个键值对,其中键为format_instruction,值为一段解释性话语和一个json格式模板。

json格式模板:

:::tips
```json\n{\n\t

"coquetry": string  // 撒娇文案\n\t

"reason": string  // 问什么要这样写这个文案\n

}\n```

:::

对应的代码:

:::tips
response_schemas = [

    ResponseSchema(name="coquetry", description="撒娇文案"),

    ResponseSchema(name="reason", description="问什么要这样写这个文案"),

]

:::

---

到这里我们就可以概括公用模板中的各个部分和langchain实现的对应关系了:

:::tips
instruction:		

template + partial_variables

context:			

并没有在公用模板中体现,目前调用的都是模型本身的知识库,并没有提供给模型额外的知识来源

prompt input:	

input_variables

output indicator:

并没有在公用模板中体现,目前未指定模型的引导词

:::

下面就基于公用模板给出chat和few_shot模板相较于公用模板的延申之处。

---

<h3 id="YiHz4">chat模板</h3>
直接给出示例代码

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

# 模板的构建
template = "你是一位专业顾问,负责为专注于{product}的公司起名。"
system_message_prompt = SystemMessagePromptTemplate.from_template(template)
print("system_message_prompt:")
print(system_message_prompt)
print()

human_template = "公司主打产品是{product_detail}。"
human_message_prompt = HumanMessagePromptTemplate.from_template(human_template)
print("human_message_prompt: ")
print(human_message_prompt)

# 构建ChatPromptTemplate
prompt_template = ChatPromptTemplate.from_messages(
    [system_message_prompt, human_message_prompt]
)
print()
print("prompt_template: ")
print(prompt_template)

输出为:

:::tips system_message_prompt:

prompt=PromptTemplate(input_variables=['product'], template='你是一位专业顾问,负责为专注于{product}的公司起名。')

:::

:::tips human_message_prompt:

prompt=PromptTemplate(input_variables=['product_detail'],

template='公司主打产品是{product_detail}。')

:::

:::tips prompt_template:

input_variables=['product', 'product_detail']

messages=[

SystemMessagePromptTemplate(prompt=PromptTemplate

(input_variables=['product'],

template='你是一位专业顾问,负责为专注于{product}的公司起名。')),

HumanMessagePromptTemplate(prompt=PromptTemplate

(input_variables=['product_detail'],

template='公司主打产品是{product_detail}。'))

]

:::

仔细的同学可能发现了课程中的一个问题,assistant对应的那个模板怎么没有在演示代码中呢?

事实上,在官网查阅信息我们可以发现,官网上对于assistant这个角色并未提及。思考一下你也能发现,怎么可能有个assistant的角色一直在提供额外的知识呢?所以这个assistant的角色应该是从user中分离出来的,因为只有user能一直提供额外的知识啊。所以langchain中为了模块的聚合度更高,它就没拆开user角色。

故官方给出的role和对应模板如下:

:::tips user: HumanMessagePromptTemplate

AI: AIMessagePromptTemplate

system: SystemMessagePromptTemplate

:::

而官方给出的chat模板架构如下:

:::tips ChatPromptTemplate{

ChatMessagePromptTemplate{

HumanMessagePromptTemplate

AIMessagePromptTemplate

SystemMessagePromptTemplate

}

}

:::

这样逻辑才通顺。

下面给出chat类型模板的解释:

:::tips

  • ChatPromptTemplate:用于管理整个聊天对话的模板,通常包含多个消息(系统、用户、AI 等)。
  • ChatMessagePromptTemplate:一个通用的消息模板类,适用于任何角色(用户、AI、系统等),可以根据需求进行配置。
  • HumanMessagePromptTemplate:专门用于表示用户的输入消息。
  • AIMessagePromptTemplate:专门用于生成 AI 的回复消息。
  • SystemMessagePromptTemplate:专门用于生成系统消息,通常用于提供背景信息或设定对话规则。

:::


few_shot模板

这部分内容有点长,还是另外开一章来做笔记好了。