OpenAI Prompt Engineering-8. 聊天机器人

OpenAI的Prompt Engineering 课程

8. 聊天机器人

ChatGPT 的 Web 界面能让你通过一个大语言模型来进行对话。一个很酷的事情是你也可以使用大语言模型来构建自己的 AI 智能聊天机器人,也许可以扮演一个 AI 客户服务代理或者 餐馆的 AI 接单员,在这个课程中,你将学习如何做这件事情。

我们先来回顾一下这节课程之前我们都是怎么向大模型接口透传的数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def get_completion(api_key, prompt, model="deepseek-chat", temperature=1.0):
if not api_key:
raise ValueError("API Key is missing.")
if not prompt:
raise ValueError("Prompt is missing.")

messages = [{"role": "user", "content": prompt}]

client = OpenAI(api_key=api_key, base_url="https://api.deepseek.com")
response = client.chat.completions.create(
model=model,
messages=messages,
stream=False,
temperature=temperature,
)

return response.choices[0].message.content

我们将prompt 数据作为参数传入到函数中,然后函数中将prompt包装到用户角色的数组中,传给 DeepSeek,最后拿到结果。

下面我们将构造一个新的函数,这个函数中不再使用单一的用户输入作为提示词,而是组合多种不同的来源数据一起构造大语言模型上下文。新的函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def get_completion_multiple_role(api_key, messages, model="deepseek-chat", temperature=1.0):
if not api_key:
raise ValueError("API Key is missing.")
if not messages:
raise ValueError("messages is missing.")

client = OpenAI(api_key=api_key, base_url="https://api.deepseek.com")
response = client.chat.completions.create(
model=model,
messages=messages,
stream=False,
temperature=temperature,
)

return response.choices[0].message.content

我们日常使用的 ChatGPT 的 Web 产品,传入的信息是分为几个类型的,用户自己输入的被称之为use,还有一种是系统消息,被称之为system,它的主要作用是约束和设置聊天机器人的行为和角色。所以可以把系统消息看作是在聊天机器人耳边说的话,引导聊天机器人的反应。用户是不知道系统消息的内容的。系统消息的好处就是给开发者提供了一种方法来框定对话,并且可以在用户没有察觉的情况下引导聊天机器,并指导它回复。

好,我们来构造一个新的messages对象传入新的get_completion_multiple_role函数中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="Run DeepSeek prompt test")

parser.add_argument('--api_key', required=True, help='Your DeepSeek API Key')
args = parser.parse_args()

open_api_key = args.api_key
model = "deepseek-chat"
messages = [{'role': 'system', 'content': 'You are an assistant that speaks like Shakespeare.'},
{'role': 'user', 'content': 'tell me a joke.'},
{'role': 'assistant', 'content': 'why did the chicken cross the road?'},
{'role': 'user', 'content': 'I do not know.'}]
response = get_completion_multiple_role(open_api_key, messages, model)
print(response)

然后来看看 DeepSeek 的回答:

1
2
3
4
5
Ah, verily, the answer doth bring mirth and merriment!

**"To get to the other side!" **

A jest most simple, yet timeless as the stars—much like mine own sonnets. Prithee, did it tickle thy fancy, or shall I conjure another quip to lift thy spirits? 🎭🐔

在这个例子中我们先使用了{'role': 'system', 'content': 'You are an assistant that speaks.'} 限定聊天机器人的角色是模仿莎士比亚的风格。然后用户说:“讲个笑话”,聊天机器人说:“鸡为什么要过马路?”,用户:“我不知道”,最终聊天机器人模仿莎士比亚的风格讲了一个笑话。

好,我们接着来看一个跟上下文有关的案例:

1
2
messages_1 = [{'role': 'system', 'content': 'You are friendly chatbot.'},
{'role': 'user', 'content': 'Hi,my name is Isa.'},]

我们向大模型介绍了自己的名字,它回复:

1
Hi, Isa! 😊 It's so nice to meet you. How can I help you today?

接下来我们换一个新的问题:

1
2
messages_2 = [{'role': 'system', 'content': 'You are friendly chatbot.'},
{'role': 'user', 'content': 'You can remind me, what is my name?'}, ]

它已经不知道我们的名字是什么了:

1
I don’t have access to personal information about you unless you share it with me. So, I don’t know your name—but I’d love to! What should I call you? 😊

大模型的每一次对话都是一个独立的互动,这就意味着,你必须提供所有相关的信息提供模型在当前对话中使用。如果你想让大模型记住对话早期的内容你必须在模型的输入中提供。那我们再修改一下提示词,看看输出结果是什么样的:

1
2
3
4
messages_3 = [{'role': 'system', 'content': 'You are friendly chatbot.'},
{'role': 'user', 'content': 'Hi,my name is Isa.'},
{'role': 'assistant', 'content': 'Hi Isa! It''s nice to meet you'},
{'role': 'user', 'content': 'You can remind me, what is my name?'}]

我们将所有的上下文信息这次都附加在了输入里,我们来看看大模型的回复,它就可以正常“记起”名字了:

1
Of course! Your name is **Isa**—I just met you, so I made sure to remember it. 😊 How can I help you today?

最后Isa 以一个披萨店的点单聊天机器人作为案例,我们将自动收集用户的提示和助理的回应,以建立这个机器人,所以我们构建一个拼接函数,不再手动输入用户和系统的输入内容:

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import argparse
from openai import OpenAI
import panel as pn

pn.extension()

panels = []

# 聊天上下文初始化
context = [{
'role': 'system',
'content': """
You are OrderBot, an automated service to collect orders for a pizza restaurant. \
You first greet the customer, then collects the order, \
and then asks if it's a pickup or delivery. \
You wait to collect the entire order, then summarize it and check for a final \
time if the customer wants to add anything else. \
If it's a delivery, you ask for an address. \
Finally you collect the payment.\
Make sure to clarify all options, extras and sizes to uniquely \
identify the item from the menu.\
You respond in a short, very conversational friendly style. \
The menu includes \
pepperoni pizza 12.95, 10.00, 7.00 \
cheese pizza 10.95, 9.25, 6.50 \
eggplant pizza 11.95, 9.75, 6.75 \
fries 4.50, 3.50 \
greek salad 7.25 \
Toppings: \
extra cheese 2.00, \
mushrooms 1.50 \
sausage 3.00 \
canadian bacon 3.50 \
AI sauce 1.50 \
peppers 1.00 \
Drinks: \
coke 3.00, 2.00, 1.00 \
sprite 3.00, 2.00, 1.00 \
bottled water 5.00
"""
}]

def get_completion_multiple_role(api_key, messages, model="deepseek-chat", temperature=1.0):
if not api_key:
raise ValueError("API Key is missing.")
if not messages:
raise ValueError("messages is missing.")

client = OpenAI(api_key=api_key, base_url="https://api.deepseek.com")
response = client.chat.completions.create(
model=model,
messages=messages,
stream=False,
temperature=temperature,
)
return response.choices[0].message.content

def collect_messages(api_key):
prompt = inp.value_input
inp.value = ''
context.append({'role': 'user', 'content': prompt})
response = get_completion_multiple_role(api_key, context)
context.append({'role': 'assistant', 'content': response})

panels.append(
pn.Row('User:', pn.pane.Markdown(prompt, width=600))
)
panels.append(
pn.Row(
'Assistant:',
pn.pane.HTML(
f"<div style='background-color:#F6F6F6;padding:8px;border-radius:6px'>{response}</div>",
width=600
)
)
)

chat_area.objects = pn.Column(*panels).objects

# 组件创建(必须在全局范围)
inp = pn.widgets.TextInput(value="Hi", placeholder='Enter text here…')
button_conversation = pn.widgets.Button(name="Chat!")
chat_area = pn.Column()

# 默认 API Key(你可以在这里写死或从环境变量读取)
open_api_key = "sk-xxxxx"

def on_click_callback(event):
collect_messages(open_api_key)

button_conversation.on_click(on_click_callback)

dashboard = pn.Column(
pn.pane.Markdown("## 🤖 Chatbot App (Panel 1.7)"),
inp,
pn.Row(button_conversation),
chat_area
)

# pn.config.title = "Chatbot App"
dashboard.servable()

原教程中使用的panel 也不知道是什么版本的,太旧以至于现在很多参数都已经不再支持。我这里使用的是最新的1.7版本。在代码中隐去了DeepSeek的key,换上自己的key即可直接使用。还有这里原视频省去了很多对于panel框架的介绍和配置。如果感觉到配置有点困难,可以参考我的代码。

在终端中执行: panel serve main_8_OrderChatBot.py --autoreload,然后访问:http://localhost:5006/main_8_OrderChatBot,就可以在网页上使用点餐机器人了,效果如下: