0%

面向开发者的 LLM 入门课程(2)

1
2
from ChatModel import OllamaChat
chat_model = OllamaChat(url="http://localhost:6006/api/chat", model_name='qwen:32b')

文本扩展

文本扩展是大语言模型的一个重要应用方向,它可以输入简短文本,生成更加丰富的长文。这为创作提供了强大支持,但也可能被滥用。因此开发者在使用时,必须谨记社会责任,避免生成有害内容。

在本章中,我们将学习基于LLM实现一个客户邮件自动生成的示例,用于根据客户反馈优化客服邮件。这里还会介绍“温度”(temperature)这一超参数,它可以控制文本生成的多样性

需要注意,扩展功能只应用来辅助人类创作,而非大规模自动生成内容。开发者应审慎使用,避免产生负面影响。只有以负责任和有益的方式应用语言模型,才能发挥其最大价值。相信践行社会责任的开发者可以利用语言模型的扩展功能,开发出真正造福人类的创新应用。

定制客户邮件

在这个客户邮件自动生成的示例中,我们将根据客户的评价和其中的情感倾向,使用大语言模型针对性地生成回复邮件。

具体来说,我们先输入客户的评论文本和对应的情感分析结果(正面或者负面)。然后构造一个 Prompt,要求大语言模型基于这些信息来生成一封定制的回复电子邮件。

下面先给出一个实例,包括一条客户评价和这个评价表达的情感。这为后续的语言模型生成回复邮件提供了关键输入信息。通过输入客户反馈的具体内容和情感态度,语言模型可以生成针对这个特定客户、考虑其具体情感因素的个性化回复。这种针对个体客户特点的邮件生成方式,将大大提升客户满意度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 我们可以在推理那章学习到如何对一个评论判断其情感倾向
sentiment = "消极的"
# 一个产品的评价
review = f"""
他们在11月份的季节性销售期间以约49美元的价格出售17件套装,折扣约为一半。\
但由于某些原因(可能是价格欺诈),到了12月第二周,同样的套装价格全都涨到了70美元到89美元不等。\
11件套装的价格也上涨了大约10美元左右。\
虽然外观看起来还可以,但基座上锁定刀片的部分看起来不如几年前的早期版本那么好。\
不过我打算非常温柔地使用它,例如,\
我会先在搅拌机中将像豆子、冰、米饭等硬物研磨,然后再制成所需的份量,\
切换到打蛋器制作更细的面粉,或者在制作冰沙时先使用交叉切割刀片,然后使用平面刀片制作更细/不粘的效
果。\
制作冰沙时,特别提示:\
将水果和蔬菜切碎并冷冻(如果使用菠菜,则轻轻煮软菠菜,然后冷冻直到使用;\
如果制作果酱,则使用小到中号的食品处理器),这样可以避免在制作冰沙时添加太多冰块。\
大约一年后,电机发出奇怪的噪音,我打电话给客服,但保修已经过期了,所以我不得不再买一个。\
总的来说,这些产品的总体质量已经下降,因此它们依靠品牌认可和消费者忠诚度来维持销售。\
货物在两天内到达。
"""

在这个例子中,我们已经利用前面章节学到的方法,从客户评价中提取出其表达的情感倾向。这里是一条关于搅拌机的评论。现在我们要基于这条评论中的情感倾向,使用大语言模型自动生成一封回复邮件。

以下述 Prompt 为例:首先明确大语言模型的身份是客户服务 AI 助手;它任务是为客户发送电子邮件回复;然后在三个反引号间给出具体的客户评论;最后要求语言模型根据这条反馈邮件生成一封回复,以感谢客户的评价。

1
2
3
4
5
6
7
8
9
10
11
prompt = f"""
你是一位客户服务的AI助手。
你的任务是给一位重要客户发送邮件回复。
根据客户通过“```”分隔的评价,生成回复以感谢客户的评价。提醒模型使用评价中的具体细节
用简明而专业的语气写信。
作为“AI客户代理”签署电子邮件。
客户评论:
​```{review}```
评论情感:{sentiment}
"""
response = chat_model.generate(prompt, stream=True)
主题:感谢您的反馈及产品使用建议,我们重视您的声音

尊敬的客户,

我代表AI客户代理团队,对您花时间分享关于您购买的产品体验表示衷心的感谢。我们非常重视每一条客户评价,尤其是像您这样详细且有建设性的意见。

理解您在11月季节性销售中享受到了优惠的价格,但随后价格波动确实带来了困惑和不满。这种情况可能由于库存管理或市场策略调整引起,我们会将您的反馈传达给相关部门,确保此类问题不会再次出现,并维护公平的定价策略。

关于产品品质的变化,我们深感抱歉给您带来的不便。您对于刀片基座的观察已记录在案,我们会进一步审查我们的生产流程以确保产品质量始终如一。同时,您分享的使用技巧非常实用,对于其他用户来说是一笔宝贵的财富。

电机噪音的问题及保修期外的情况,确实说明我们在售后服务方面有待提高。我们理解产品的耐用性和客户服务的重要性,并承诺会持续优化这些环节,以满足客户的期望。

货物快速送达是我们的目标,我很高兴这部分体验符合您的期待。请知道,您的满意度是我们不断改进的动力。如您在未来有任何疑问或需要任何帮助,请随时联系我们的客户服务团队。

再次感谢您的反馈和对品牌的理解。我们期待有机会通过提升产品和服务质量赢回您的信任。

顺祝商祺,

AI客户代理
[Your Company Name]

通过这个Prompt,我们将具体的客户评论内容和需要表达的客服助手语气与要生成的回复邮件链接起来。

语言模型可以在充分理解客户反馈的基础上,自动撰写恰当的回复。这种依据具体客户评价个性化回复的方法,将大大提升客户体验和满意度。

引入温度系数

大语言模型中的 “温度”(temperature) 参数可以控制生成文本的随机性和多样性。

temperature 的值越大,语言模型输出的多样性越大;temperature 的值越小,输出越倾向高概率的文本。

举个例子,在某一上下文中,语言模型可能认为“比萨”是接下来最可能的词,其次是“寿司”和“塔可”。若temperature 为0,则每次都会生成“比萨”;而当 temperature 越接近 1 时,生成结果是“寿司”或“塔可”的可能性越大,使文本更加多样。

1
2
3
4
5
6
7
8
9
10
11
prompt = f"""
你是一位客户服务的AI助手。
你的任务是给一位重要客户发送邮件回复。
根据客户通过“```”分隔的评价,生成回复以感谢客户的评价。提醒模型使用评价中的具体细节
用简明而专业的语气写信。
作为“AI客户代理”签署电子邮件。
客户评论:
​```{review}```
评论情感:{sentiment}
"""
response = chat_model.generate(prompt, stream=True, temperature=0.7)
主题:感谢您的反馈及产品使用建议 - [客户姓名]

尊敬的 [客户姓名],

AI 客户代理在此向您表示衷心的感谢,感谢您花时间分享对我们的产品的详细评价。我们非常重视每一位客户的反馈,因为它帮助我们理解如何更好地改进和满足您的期望。

首先,请允许我为12月产品价格波动的情况道歉。我们了解到这种体验可能会令人困扰,这并非我们希望带给任何顾客的购物体验。我们会将您的反馈转达给相关部门,以确保我们的定价政策更为透明并保持一致性。如您遇到任何进一步的价格问题,或者需要关于当前销售的信息,请随时联系我们的客户服务团队。

关于产品细节和质量方面的关切,我理解您对基座刀片部分的比较,并且感谢您分享了细致的使用建议。这不仅有助于我们了解产品的实际表现,也可能对其他用户有所帮助。我们将把您的观察反馈给我们的产品开发团队,以便他们在未来的产品改进中考虑。

关于电机噪音及保修期的问题,我深感遗憾听到您的经历。显然,我们未能在耐用性上达到您期待的标准。关于过期的保修问题,我们明白这可能会给您带来额外的不便。如有可能,我们将探讨是否能提供超出常规保修范围的支持,请您稍等,我们的客户服务团队将尽快与您联系以探讨可能的解决方案。

最后,尽管这次体验可能并未完全符合您的期望,但我们诚挚地希望未来的产品和服务能够重新赢得您的信任。您的忠诚度对我们至关重要,我们承诺会持续改进,为您提供更优质的产品和服务。

再次感谢您的反馈和宝贵的建议。如果您有任何其他疑问或需要进一步的帮助,请随时通过回复此邮件或联系我们的客户服务团队。我们将竭诚为您服务。

顺祝商祺,

AI 客户代理
[公司名称]
客户服务部

温度(temperature)参数可以控制语言模型生成文本的随机性。温度为0时,每次使用同样的Prompt,得到的结果总是一致的。而在上面的样例中,当温度设为0.7时,则每次执行都会生成不同的文本。

所以,这次的结果与之前得到的邮件就不太一样了。再次执行同样的 Prompt,邮件内容还会有变化。因此。我建议读者朋友们可以自己尝试不同的 temperature ,来观察输出的变化。总体来说,temperature 越高,语言模型的文本生成就越具有随机性。可以想象,高温度下,语言模型就像心绪更加活跃,但也可能更有创造力。

适当调节这个超参数,可以让语言模型的生成更富有多样性,也更能意外惊喜。希望这些经验可以帮助你在不同场景中找到最合适的温度设置。

聊天机器人

大型语言模型带给我们的激动人心的一种可能性是,我们可以通过它构建定制的聊天机器人(Chatbot),而且只需很少的工作量。在这一章节的探索中,我们将带你了解如何利用会话形式,与具有个性化特性(或专门为特定任务或行为设计)的聊天机器人进行深度对话。

像 ChatGPT 这样的聊天模型实际上是组装成以一系列消息作为输入,并返回一个模型生成的消息作为输出的。这种聊天格式原本的设计目标是简便多轮对话,但我们通过之前的学习可以知道,它对于不会涉及任何对话的单轮任务也同样有用。

给定身份

我们将 Prompt 放入某种类似用户消息的对话框中。传入一个消息列表。这些消息可以来自大量不同的角色 (roles) ,我们会描述一下这些角色。

讲笑话

我们通过系统消息来定义:“你是一个说话像莎士比亚的助手。”这是我们向助手描述它应该如何表现的方式。

然后,第一个用户消息:“给我讲个笑话。”

接下来以助手身份给出回复:“为什么鸡会过马路?”

最后发送用户消息是:“我不知道。”

1
2
3
4
5
6
# 中文
messages = [
{'role':'system', 'content': '你是一个像莎士比亚一样说话的助手。'},
{'role':'user', 'content': '给我讲个笑话'},
{'role':'assistant', 'content': '鸡为什么过马路'},
]
1
response = chat_model.chat("我不知道", messages, stream=True, temperature=1)
为了到那一边,我的朋友,这是为何物奔波劳累,只为寻求生活的真理与新的牧场。毕竟,每一枚鸡蛋背后都隐藏着一只渴望探索的鸡魂。

(注:上述例子中由于选定 temperature = 1,模型的回答会比较随机且迥异(不乏很有创意)。

友好的聊天机器人

让我们看另一个例子。系统消息来定义:“ 你是一个友好的聊天机器人 ”,第一个用户消息:“ 嗨,我叫 Isa。”

我们想要得到第一个用户消息的回复。

1
2
3
4
messages = [ 
{'role':'system', 'content':'你是个友好的聊天机器人。'},
]
response = chat_model.chat("Hi, 我是Isa。", messages, stream=True, temperature=1)
你好,Isa!很高兴认识你。有什么可以帮到你的吗?

构建上下文

让我们再试一个例子。系统消息来定义:“你是一个友好的聊天机器人”,第一个用户消息:“是的,你能提醒我我的名字是什么吗?”

1
2
3
4
messages = [ 
{'role':'system', 'content':'你是个友好的聊天机器人。'}
]
response = chat_model.chat("好,你能提醒我,我的名字是什么吗?", messages, stream=True, temperature=1)
当然可以,你的名字是“用户”。在我们的对话中,我用这个称呼来指代您。如果你的名字不是“用户”,请告诉我你的名字,这样我可以更亲切地与你交流。

如上所见,模型实际上并不知道我的名字。

因此,每次与语言模型的交互都互相独立,这意味着我们必须提供所有相关的消息,以便模型在当前对话中进行引用。如果想让模型引用或 “记住” 对话的早期部分,则必须在模型的输入中提供早期的交流。

我们将其称为上下文 (context) 。尝试以下示例。

1
2
3
4
5
6
messages = [
{'role':'system', 'content':'你是个友好的聊天机器人。'},
{'role':'user', 'content':'Hi, 我是Isa'},
{'role':'assistant', 'content': "Hi Isa! 很高兴认识你。今天有什么可以帮到你的吗?"}
]
response = chat_model.chat("是的,你可以提醒我, 我的名字是什么?", messages, stream=True, temperature=1)
当然可以,Isa。你的名字是Isa。如果你需要任何提醒或者有其他问题,随时告诉我。

现在我们已经给模型提供了上下文,也就是之前的对话中提到的我的名字,然后我们会问同样的问题,也就是我的名字是什么。因为模型有了需要的全部上下文,所以它能够做出回应,就像我们在输入的消息列表中看到的一样。

订餐机器人

在这一新的章节中,我们将探索如何构建一个 “点餐助手机器人”。这个机器人将被设计为自动收集用户信息,并接收来自比萨饼店的订单。让我们开始这个有趣的项目,深入理解它如何帮助简化日常的订餐
流程。

构建机器人

下面这个函数将收集我们的用户消息,以便我们可以避免像刚才一样手动输入。这个函数将从我们下面构建的用户界面中收集 Prompt ,然后将其附加到一个名为上下文( context )的列表中,并在每次调用模型时使用该上下文。模型的响应也会添加到上下文中,所以用户消息和模型消息都被添加到上下文中,上下文逐渐变长。这样,模型就有了需要的信息来确定下一步要做什么。

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
context = [{'role':'system', 'content':"""
你是订餐机器人,为披萨餐厅自动收集订单信息。
你要首先问候顾客。然后等待用户回复收集订单信息。收集完信息需确认顾客是否还需要添加其他内容。
最后需要询问是否自取或外送,如果是外送,你要询问地址。
最后告诉顾客订单总金额,并送上祝福。
请确保明确所有选项、附加项和尺寸,以便从菜单中识别出该项唯一的内容。
你的回应应该以简短、非常随意和友好的风格呈现。
菜单包括:
菜品:
意式辣香肠披萨(大、中、小) 12.95、10.00、7.00
芝士披萨(大、中、小) 10.95、9.25、6.50
茄子披萨(大、中、小) 11.95、9.75、6.75
薯条(大、小) 4.50、3.50
希腊沙拉 7.25
配料:
奶酪 2.00
蘑菇 1.50
香肠 3.00
加拿大熏肉 3.50
AI酱 1.50
辣椒 1.00
饮料:
可乐(大、中、小) 3.00、2.00、1.00
雪碧(大、中、小) 3.00、2.00、1.00
瓶装水 5.00
"""}]
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
import panel as pn
import param

pn.extension()

# 假设你有一个名为 chat_function 的函数,它接受用户输入和聊天历史,返回模型的响应

# 创建一个Panel布局
input_box = pn.widgets.TextInput(value='你好', placeholder='请在这里输入你的文字...', width=600)
send_button = pn.widgets.Button(name='聊天', width=60)
chat_history = pn.pane.Markdown(width=600)

# 定义一个发送消息的函数
def send_message(event):
user_input = input_box.value
if user_input:
response = chat_model.chat(user_input, context.copy())
chat_history.object = f"{chat_history.object}\n\nYou: {user_input}\nBot: {response[0]}"
input_box.value = ''

# 将发送消息函数绑定到按钮的点击事件
send_button.on_click(send_message)

# 创建布局并显示
chat_app = pn.Column(input_box, send_button, chat_history)
chat_app.show()


</div>

Launching server at http://localhost:55561





<panel.io.server.Server at 0x22b5036f820>

image-20240527183024084

创建JSON摘要

此处我们另外要求模型创建一个 JSON 摘要,方便我们发送给订单系统。

因此我们需要在上下文的基础上追加另一个系统消息,作为另一条指示 (instruction) 。我们说创建一个刚刚订单的 JSON 摘要,列出每个项目的价格,字段应包括:

  1. 披萨,包括尺寸
  2. 配料列表
  3. 饮料列表
  4. 辅菜列表,包括尺寸,
  5. 总价格。

此处也可以定义为用户消息,不一定是系统消息。

请注意,这里我们使用了一个较低的温度,因为对于这些类型的任务,我们希望输出相对可预测。

1
2
3
4
5
6
7
8
9
10
11
12
messages = context.copy()
messages.append(
{'role':'system', 'content':
'''创建上一个食品订单的 json 摘要。\
逐项列出每件商品的价格,字段应该是
1) 披萨,包括大小
2) 配料列表
3) 饮料列表,包括大小
4) 配菜列表包括大小
你应该给我返回一个可解析的Json对象,包括上述字段'''},
)
response = chat_model.chat("", messages, stream=True, temperature=0)
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
{
"orders": [
{
"item": "披萨",
"size": "大",
"price": 12.95,
"toppings": [
{"name": "奶酪", "price": 2.00},
{"name": "蘑菇", "price": 1.50}
]
},
{
"item": "饮料",
"size": "中",
"price": 2.00,
"drink": "可乐"
},
{
"item": "配菜",
"size": "小",
"price": 3.50,
"side": "薯条"
}
],
"total": 19.45
}
1

-------------本文结束感谢您的阅读-------------