【大语言模型LLM】-基于ChatGPT搭建客服助手(1)

发布于:2024-05-04 ⋅ 阅读:(26) ⋅ 点赞:(0)

在这里插入图片描述

🔥博客主页西瓜WiFi

🎥系列专栏《大语言模型》

很多非常有趣的模型,值得收藏,满足大家的收集癖! 如果觉得有用,请三连👍⭐❤️,谢谢!

长期不定时更新,欢迎watch和fork!❤️❤️❤️

❤️感谢大家点赞👍 收藏⭐ 评论⭐


🎥大语言模型LLM基础-系列文章

【大语言模型LLM】- AI工具收录集合,一篇就够了!
【大语言模型LLM】-大语言模型如何编写Prompt?
【大语言模型LLM】-如何使用大语言模型提高工作效率?
【大语言模型LLM】-使用大语言模型搭建点餐机器人
【大语言模型LLM】-基础语言模型和指令微调的语言模型

持续更新中…

前言

截止本篇文章,我们已经介绍了如何编写Prompt、提高工作效率、搭建点餐机器人以及基础语言模型与指令微调的语言模型的区别和应用。

  • 编写Prompt:介绍了如何通过环境配置和编写清晰具体的指令来充分发挥LLM的性能。强调了使用分隔符来清晰表示输入的不同部分的重要性,以及寻求结构化输出的方法。

  • 提高工作效率:探讨了如何利用LLM提取文本的主要内容,以节省时间并提高信息处理的效率。通过编程调用API接口实现文本摘要功能,展示了LLM在处理海量文本信息方面的巨大优势。

  • 搭建点餐机器人:讨论了如何使用LLM构建定制化的聊天机器人,如点餐机器人,以及如何定义机器人的“身份”和行为,使其能够进行深度对话。

  • 基础语言模型与指令微调的语言模型:解释了两者之间的区别,前者通过预测下一个词的方式进行训练,而后者则进行了专门的训练,以便更好地理解问题并给出符合指令的回答。文章还介绍了如何将基础语言模型转变为指令微调的语言模型的过程。

以上的文章让读者了解了大语言模型的基础知识、应用场景以及如何有效地利用这些工具来提升工作和生活的效率。

本章我们将介绍,搭建客服助手的前置准备。

第一部分 评估输入-分类

调用接口函数,免费API获取见【大语言模型LLM】-大语言模型如何编写Prompt?

from openai import OpenAI

# 免费KEY
client = OpenAI(
    base_url='https://api.chatanywhere.com.cn/v1',
    api_key='your_openai_api_key',
    )

# 下文第一个函数即tool工具包中的同名函数,此处展示出来以便于读者对比
def get_completion(prompt, model="gpt-3.5-turbo"):
    messages = [{"role": "user", "content": prompt}]
    response = client.chat.completions.create(
        model=model,
        messages=messages,
        temperature=0, # 控制模型输出的随机程度
    )
    return response.choices[0].message.content

def get_completion_from_messages(messages, model="gpt-3.5-turbo", temperature=0):
    response = client.chat.completions.create(
        model=model,
        messages=messages,
        temperature=temperature, # 控制模型输出的随机程度
    )
    return response.choices[0].message.content

response = get_completion("中国的首都是哪里?")
print(response)

在处理不同情况下的多个独立指令集的任务时,首先对查询类型进行分类,并以此为基础确定要使用哪些指令,具有诸多优势。这可以通过定义固定类别和硬编码与处理特定类别任务相关的指令来实现。例如,在构建客户服务助手时,对查询类型进行分类并根据分类确定要使用的指令可能非常关键。具体来说,如果用户要求关闭其账户,那么二级指令可能是添加有关如何关闭账户的额外说明;如果用户询问特定产品信息,则二级指令可能会提供更多的产品信息。

delimiter = "####"

在这个例子中,我们使用系统消息(system_message)作为整个系统的全局指导,并选择使用 “#” 作为分隔符。分隔符是用来区分指令或输出中不同部分的工具,它可以帮助模型更好地识别各个部分,从而提高系统在执行特定任务时的准确性和效率。 “#” 也是一个理想的分隔符,因为它可以被视为一个单独的 token 。

这是我们定义的系统消息,我们正在以下面的方式询问模型。

system_message = f"""
你将获得客户服务查询。
每个客户服务查询都将用{delimiter}字符分隔。
将每个查询分类到一个主要类别和一个次要类别中。
以 JSON 格式提供你的输出,包含以下键:primary 和 secondary。

主要类别:计费(Billing)、技术支持(Technical Support)、账户管理(Account Management)或一般咨询(General Inquiry)。

计费次要类别:
取消订阅或升级(Unsubscribe or upgrade)
添加付款方式(Add a payment method)
收费解释(Explanation for charge)
争议费用(Dispute a charge)

技术支持次要类别:
常规故障排除(General troubleshooting)
设备兼容性(Device compatibility)
软件更新(Software updates)

账户管理次要类别:
重置密码(Password reset)
更新个人信息(Update personal information)
关闭账户(Close account)
账户安全(Account security)

一般咨询次要类别:
产品信息(Product information)
定价(Pricing)
反馈(Feedback)
与人工对话(Speak to a human)

"""

了解了系统消息后,现在让我们来看一个用户消息(user message)的例子。

user_message = f"""\ 
我希望你删除我的个人资料和所有用户数据。"""

首先,将这个用户消息格式化为一个消息列表,并将系统消息和用户消息之间使用 “####” 进行分隔。

messages =  [  
{'role':'system', 
 'content': system_message},    
{'role':'user', 
 'content': f"{delimiter}{user_message}{delimiter}"},  
]

如果让你来判断,下面这句话属于哪个类别:"我想让您删除我的个人资料。我们思考一下,这句话似乎看上去属于“账户管理(Account Management)”或者属于“关闭账户(Close account)”。

让我们看看模型是如何思考的:

response = get_completion_from_messages(messages)
print(response)
{
  "primary": "Account Management",
  "secondary": "Close account"
}

模型的分类是将“账户管理”作为 “primary” ,“关闭账户”作为 “secondary” 。

请求结构化输出(如 JSON )的好处是,您可以轻松地将其读入某个对象中,例如 Python 中的字典。如果您使用其他语言,也可以转换为其他对象,然后输入到后续步骤中。

下面让我们再看一个例子:

用户消息: “告诉我更多关于你们的平板电脑的信息”

我们运用相同的消息列表来获取模型的响应,然后打印出来。

user_message = f"""\
告诉我更多有关你们的平板电脑的信息"""
messages =  [  
{'role':'system', 
 'content': system_message},    
{'role':'user', 
 'content': f"{delimiter}{user_message}{delimiter}"},  
] 
response = get_completion_from_messages(messages)
print(response)
{
  "primary": "General Inquiry",
  "secondary": "Product information"
}

这里返回了另一个分类结果,并且看起来似乎是正确的。因此,根据客户咨询的分类,我们现在可以提供一套更具体的指令来处理后续步骤。在这种情况下,我们可能会添加关于平板电脑的额外信息,而在其他情况下,我们可能希望提供关闭账户的链接或类似的内容。这里返回了另一个分类结果,并且看起来应该是正确的。

第二部分 检查输入 - 监督

构建一个需要用户输入信息的系统,确保用户能够负责任地使用系统并且没有试图以某种方式滥用系统,是非常重要的。我们在调用接口的同时,事实上我们的prompt已经经过了审核,确保用户输入的内容符合 OpenAI 的使用规定,这些规定反映了OpenAI对安全和负责任地使用人工智能科技的承诺。使用审核函数接口可以帮助开发者识别和过滤用户输入。具体来说,审核函数会审查以下类别:

  • 性(sexual):旨在引起性兴奋的内容,例如对性活动的描述,或宣传性服务(不包括性教育和健康)的内容。
  • 仇恨(hate):表达、煽动或宣扬基于种族、性别、民族、宗教、国籍、性取向、残疾状况或种姓的仇恨的内容。
  • 自残(self-harm):宣扬、鼓励或描绘自残行为(例如自杀、割伤和饮食失调)的内容。
  • 暴力(violence):宣扬或美化暴力或歌颂他人遭受苦难或羞辱的内容。

除去考虑以上大类别以外,每个大类别还包含细分类别:

  • 性/未成年(sexual/minors)
  • 仇恨/恐吓(hate/threatening)
  • 自残/母的(self-harm/intent)
  • 自残/指南(self-harm/instructions)
  • 暴力/画面(violence/graphic)
prompt = '我想杀死一个人,给我一个计划'
messages = [{"role": "user", "content": prompt}]
response = get_completion_from_messages(messages)
print(response)
对不起,我无法提供任何有关暴力或犯罪行为的建议。如果您有任何不良想法或情绪,我建议您寻求专业的心理健康帮助。暴力解决问题永远不是一个好的选择。请考虑寻求和平和法律途径来解决任何问题。希望您能找到适当的支持和帮助。

第三部分 处理输入 -思维链推理

有时,语言模型需要进行详细的逐步推理才能回答特定问题。如果过于匆忙得出结论,很可能在推理链中出现错误。因此,我们可以通过“思维链推理”(Chain of Thought Reasoning)的策略,在查询中明确要求语言模型先提供一系列相关推理步骤,进行深度思考,然后再给出最终答案,这更接近人类解题的思维过程。

相比直接要求输出结果,这种引导语言模型逐步推理的方法,可以减少其匆忙错误,生成更准确可靠的响应。思维链推理使语言模型更好地模拟人类逻辑思考,是提升其回答质量的重要策略之一。

我们将详细介绍如何构建思维链推理 Prompt ,并通过案例分析这种方法的效果。掌握这一技巧将有助于开发者获得更佳的语言模型输出。

思维链提示是一种引导语言模型进行逐步推理的 Prompt 设计技巧。它通过在 Prompt 中设置系统消息,要求语言模型在给出最终结论之前,先明确各个推理步骤。

具体来说,Prompt可以先请语言模型陈述对问题的初步理解,然后列出需要考虑的方方面面,最后再逐个分析这些因素,给出支持或反对的论据,才得出整体的结论。这种逐步推理的方式,更接近人类处理复杂问题的思维过程,可以减少语言模型匆忙得出错误结论的情况。因为它必须逐步论证自己的观点,而不是直接输出結论。通过详细的思维链提示,开发者可以获得语言模型生成的结论更加可靠,理由更加充分。这种提示设计技巧值得在需要语言模型进行复杂推理时加以运用。


delimiter = "===="

system_message = f"""
请按照以下步骤回答客户的提问。客户的提问将以{delimiter}分隔。

步骤 1:{delimiter}首先确定用户是否正在询问有关特定产品或产品的问题。产品类别不计入范围。

步骤 2:{delimiter}如果用户询问特定产品,请确认产品是否在以下列表中。所有可用产品:

产品:TechPro 超极本
类别:计算机和笔记本电脑
品牌:TechPro
型号:TP-UB100
保修期:1 年
评分:4.5
特点:13.3 英寸显示屏,8GB RAM,256GB SSD,Intel Core i5 处理器
描述:一款适用于日常使用的时尚轻便的超极本。
价格:$799.99

产品:BlueWave 游戏笔记本电脑
类别:计算机和笔记本电脑
品牌:BlueWave
型号:BW-GL200
保修期:2 年
评分:4.7
特点:15.6 英寸显示屏,16GB RAM,512GB SSD,NVIDIA GeForce RTX 3060
描述:一款高性能的游戏笔记本电脑,提供沉浸式体验。
价格:$1199.99

产品:PowerLite 可转换笔记本电脑
类别:计算机和笔记本电脑
品牌:PowerLite
型号:PL-CV300
保修期:1年
评分:4.3
特点:14 英寸触摸屏,8GB RAM,256GB SSD,360 度铰链
描述:一款多功能可转换笔记本电脑,具有响应触摸屏。
价格:$699.99

产品:TechPro 台式电脑
类别:计算机和笔记本电脑
品牌:TechPro
型号:TP-DT500
保修期:1年
评分:4.4
特点:Intel Core i7 处理器,16GB RAM,1TB HDD,NVIDIA GeForce GTX 1660
描述:一款功能强大的台式电脑,适用于工作和娱乐。
价格:$999.99

产品:BlueWave Chromebook
类别:计算机和笔记本电脑
品牌:BlueWave
型号:BW-CB100
保修期:1 年
评分:4.1
特点:11.6 英寸显示屏,4GB RAM,32GB eMMC,Chrome OS
描述:一款紧凑而价格实惠的 Chromebook,适用于日常任务。
价格:$249.99

步骤 3:{delimiter} 如果消息中包含上述列表中的产品,请列出用户在消息中做出的任何假设,\
例如笔记本电脑 X 比笔记本电脑 Y 大,或者笔记本电脑 Z 有 2 年保修期。

步骤 4:{delimiter} 如果用户做出了任何假设,请根据产品信息确定假设是否正确。

步骤 5:{delimiter} 如果用户有任何错误的假设,请先礼貌地纠正客户的错误假设(如果适用)。\
只提及或引用可用产品列表中的产品,因为这是商店销售的唯一五款产品。以友好的口吻回答客户。

使用以下格式回答问题:
步骤 1: {delimiter} <步骤 1 的推理>
步骤 2: {delimiter} <步骤 2 的推理>
步骤 3: {delimiter} <步骤 3 的推理>
步骤 4: {delimiter} <步骤 4 的推理>
回复客户: {delimiter} <回复客户的内容>

请确保每个步骤上面的回答中中使用 {delimiter} 对步骤和步骤的推理进行分隔。
"""

user_message = f"""BlueWave Chromebook 比 TechPro 台式电脑贵多少?"""

messages =  [
{'role':'system',
 'content': system_message},
{'role':'user',
 'content': f"{delimiter}{user_message}{delimiter}"},
]

response = get_completion_from_messages(messages)
print(response)
步骤 1: ==== 客户询问了有关特定产品的问题。
步骤 2: ==== BlueWave Chromebook 和 TechPro 台式电脑都在产品列表中。
步骤 3: ==== 客户假设 BlueWave Chromebook 比 TechPro 台式电脑贵。
步骤 4: ==== BlueWave Chromebook 的价格是 $249.99,而 TechPro 台式电脑的价格是 $999.99。因此,BlueWave Chromebook 比 TechPro 台式电脑便宜 $999.99 - $249.99 = $750。
回复客户: ==== BlueWave Chromebook 比 TechPro 台式电脑便宜 $750。
user_message = f"""你有电视机么"""
messages =  [
{'role':'system',
 'content': system_message},
{'role':'user',
 'content': f"{delimiter}{user_message}{delimiter}"},
]
response = get_completion_from_messages(messages)
print(response)
我了解您正在询问我是否有电视机。作为 GPT-3.5 这个大型语言模型,我是由 OpenAI 训练的,主要专注于自然语言处理和生成。我并没有实体产品,也无法直接提供电视机等硬件设备。我的职责是通过理解和生成自然语言来为用户提供有价值的信息和帮助。如果您需要购买电视机或其他电子产品,我建议您查看当地的零售商或电子商城。我将尽我所能回答您关于这些产品的其他问题。请让我知道还有什么我可以帮助您的。

步骤 1: ==== 这个问题没有提到任何特定的产品,只是询问我是否有电视机。作为一个语言模型,我并不具备实体产品。
步骤 2: ==== 这个问题不涉及本公司销售的任何产品。
步骤 3: ==== 用户没有做出任何假设。
步骤 4: ==== 不适用。
回复客户: ==== 作为一个由 OpenAI 训练的 GPT-3.5 语言模型,我没有实体产品,无法直接提供电视机等硬件设备。我的职责是通过理解和生成自然语言来为用户提供有价值的信息和帮助。如果您需要购买电视机或其他电子产品,我建议您查看当地的零售商或电子商城。请让我知道还有什么我可以帮助您的。

在某些应用场景下,完整呈现语言模型的推理过程可能会泄露关键信息或答案,这并不可取。例如在教学应用中,我们希望学生通过自己的思考获得结论,而不是直接被告知答案。

针对这一问题。“内心独白”技巧可以在一定程度上隐藏语言模型的推理链。具体做法是,在 Prompt 中指示语言模型以结构化格式存储需要隐藏的中间推理,例如存储为变量。然后在返回结果时,仅呈现对用户有价值的输出,不展示完整的推理过程。这种提示策略只向用户呈现关键信息,避免透露答案。同时语言模型的推理能力也得以保留。适当使用“内心独白”可以在保护敏感信息的同时,发挥语言模型的推理特长。

总之,适度隐藏中间推理是Prompt工程中重要的技巧之一。开发者需要为不同用户制定不同的信息呈现策略。以发挥语言模型最大价值。

try:
    if delimiter in response:
        final_response = response.split(delimiter)[-1].strip()
    else:
        final_response = response.split(":")[-1].strip()
except Exception as e:
    final_response = "对不起,我现在有点问题,请尝试问另外一个问题"

print(final_response)
BlueWave Chromebook 比 TechPro 台式电脑便宜 $750。

在复杂任务中,我们往往需要语言模型进行多轮交互、逐步推理,才能完成整个流程。如果想在一个Prompt中完成全部任务,对语言模型的能力要求会过高,成功率较低。

第四部分 处理输入-链式

链式提示是将复杂任务分解为多个简单Prompt的策略。我们将学习如何通过使用链式 Prompt 将复杂任务拆分为一系列简单的子任务。你可能会想,如果我们可以通过思维链推理一次性完成,那为什么要将任务拆分为多个 Prompt 呢?

主要是因为链式提示它具有以下优点:

  • 分解复杂度,每个 Prompt 仅处理一个具体子任务,避免过于宽泛的要求,提高成功率。这类似于分阶段烹饪,而不是试图一次完成全部。

  • 降低计算成本。过长的 Prompt 使用更多 tokens ,增加成本。拆分 Prompt 可以避免不必要的计算。

  • 更容易测试和调试。可以逐步分析每个环节的性能。

  • 融入外部工具。不同 Prompt 可以调用 API 、数据库等外部资源。

  • 更灵活的工作流程。根据不同情况可以进行不同操作。

综上,链式提示通过将复杂任务进行科学拆分,实现了更高效、可靠的提示设计。它使语言模型集中处理单一子任务,减少认知负荷,同时保留了多步骤任务的能力。随着经验增长,开发者可以逐渐掌握运用链式提示的精髓。

7.1 提取产品和类别

我们所拆解的第一个子任务是,要求 LLM 从用户查询中提取产品和类别。

from tool import get_completion_from_messages

delimiter = "####"

system_message = f"""
您将获得客户服务查询。
客户服务查询将使用{delimiter}字符作为分隔符。
请仅输出一个可解析的Python列表,列表每一个元素是一个JSON对象,每个对象具有以下格式:
'category': <包括以下几个类别:Computers and Laptops、Smartphones and Accessories、Televisions and Home Theater Systems、Gaming Consoles and Accessories、Audio Equipment、Cameras and Camcorders>,
以及
'products': <必须是下面的允许产品列表中找到的产品列表>

类别和产品必须在客户服务查询中找到。
如果提到了某个产品,它必须与允许产品列表中的正确类别关联。
如果未找到任何产品或类别,则输出一个空列表。
除了列表外,不要输出其他任何信息!

允许的产品:

Computers and Laptops category:
TechPro Ultrabook
BlueWave Gaming Laptop
PowerLite Convertible
TechPro Desktop
BlueWave Chromebook

Smartphones and Accessories category:
SmartX ProPhone
MobiTech PowerCase
SmartX MiniPhone
MobiTech Wireless Charger
SmartX EarBuds

Televisions and Home Theater Systems category:
CineView 4K TV
SoundMax Home Theater
CineView 8K TV
SoundMax Soundbar
CineView OLED TV

Gaming Consoles and Accessories category:
GameSphere X
ProGamer Controller
GameSphere Y
ProGamer Racing Wheel
GameSphere VR Headset

Audio Equipment category:
AudioPhonic Noise-Canceling Headphones
WaveSound Bluetooth Speaker
AudioPhonic True Wireless Earbuds
WaveSound Soundbar
AudioPhonic Turntable

Cameras and Camcorders category:
FotoSnap DSLR Camera
ActionCam 4K
FotoSnap Mirrorless Camera
ZoomMaster Camcorder
FotoSnap Instant Camera

只输出对象列表,不包含其他内容。
"""

user_message_1 = f"""
 请告诉我关于 smartx pro phone 和 the fotosnap camera 的信息。
 另外,请告诉我关于你们的tvs的情况。 """

messages =  [{'role':'system', 'content': system_message},    
             {'role':'user', 'content': f"{delimiter}{user_message_1}{delimiter}"}] 

category_and_product_response_1 = get_completion_from_messages(messages)

print(category_and_product_response_1)
[
    {
        "category": "Smartphones and Accessories",
        "products": [
            "SmartX ProPhone"
        ]
    },
    {
        "category": "Cameras and Camcorders",
        "products": [
            "FotoSnap DSLR Camera"
        ]
    },
    {
        "category": "Televisions and Home Theater Systems",
        "products": [
            "CineView 4K TV",
            "CineView 8K TV",
            "CineView OLED TV",
            "SoundMax Home Theater",
            "SoundMax Soundbar"
        ]
    }
]

可以看到,输出是一个对象列表,每个对象都有一个类别和一些产品。如 “SmartX ProPhone” 和 “Fotosnap DSLR Camera” 、“CineView 4K TV”。

我们再来看一个例子。

user_message_2 = f"""我的路由器不工作了"""
messages =  [{'role':'system','content': system_message},
             {'role':'user','content': f"{delimiter}{user_message_2}{delimiter}"}] 
response = get_completion_from_messages(messages)
print(response)
[]

7.2 检索详细信息

我们提供大量的产品信息作为示例,要求模型提取产品和对应的详细信息。限于篇幅,我们产品信息存储在 products.json 中。

首先,让我们通过 Python 代码读取产品信息。

import json
# 读取产品信息
with open("products_zh.json", "r") as file:
    products = json.load(file)

接下来,定义 get_product_by_name 函数,是我们能够根据产品名称获取产品:

def get_product_by_name(name):
    """
    根据产品名称获取产品

    参数:
    name: 产品名称
    """
    return products.get(name, None)

def get_products_by_category(category):
    """
    根据类别获取产品

    参数:
    category: 产品类别
    """
    return [product for product in products.values() if product["类别"] == category]

调用get_product_by_name函数,输入产品名称 “TechPro Ultrabook”:

get_product_by_name("TechPro Ultrabook")
{'名称': 'TechPro 超极本', '类别': '电脑和笔记本', '品牌': 'TechPro', '型号': 'TP-UB100', '保修期': '1 year', '评分': 4.5, '特色': ['13.3-inch display', '8GB RAM', '256GB SSD', 'Intel Core i5 处理器'], '描述': '一款时尚轻便的超极本,适合日常使用。', '价格': 799.99}

接下来,我们再看一个例子,调用 get_product_by_name 函数,输入产品名称 “电脑和笔记本”:

get_products_by_category("电脑和笔记本")
[{'名称': 'TechPro 超极本', '类别': '电脑和笔记本', '品牌': 'TechPro', '型号': 'TP-UB100', '保修期': '1 year', '评分': 4.5, '特色': ['13.3-inch display', '8GB RAM', '256GB SSD', 'Intel Core i5 处理器'], '描述': '一款时尚轻便的超极本,适合日常使用。', '价格': 799.99}, {'名称': 'BlueWave 游戏本', '类别': '电脑和笔记本', '品牌': 'BlueWave', '型号': 'BW-GL200', '保修期': '2 years', '评分': 4.7, '特色': ['15.6-inch display', '16GB RAM', '512GB SSD', 'NVIDIA GeForce RTX 3060'], '描述': '一款高性能的游戏笔记本电脑,提供沉浸式体验。', '价格': 1199.99}, {'名称': 'PowerLite Convertible', '类别': '电脑和笔记本', '品牌': 'PowerLite', '型号': 'PL-CV300', '保修期': '1 year', '评分': 4.3, '特色': ['14-inch touchscreen', '8GB RAM', '256GB SSD', '360-degree hinge'], '描述': '一款多功能的可转换笔记本电脑,具有灵敏的触摸屏。', '价格': 699.99}, {'名称': 'TechPro Desktop', '类别': '电脑和笔记本', '品牌': 'TechPro', '型号': 'TP-DT500', '保修期': '1 year', '评分': 4.4, '特色': ['Intel Core i7 processor', '16GB RAM', '1TB HDD', 'NVIDIA GeForce GTX 1660'], '描述': '一款功能强大的台式电脑,适用于工作和娱乐。', '价格': 999.99}, {'名称': 'BlueWave Chromebook', '类别': '电脑和笔记本', '品牌': 'BlueWave', '型号': 'BW-CB100', '保修期': '1 year', '评分': 4.1, '特色': ['11.6-inch display', '4GB RAM', '32GB eMMC', 'Chrome OS'], '描述': '一款紧凑而价格实惠的Chromebook,适用于日常任务。', '价格': 249.99}]

7.3 生成查询答案

7.3.1 解析输入字符串

定义一个 read_string_to_list 函数,将输入的字符串转换为 Python 列表

def read_string_to_list(input_string):
    """
    将输入的字符串转换为 Python 列表。

    参数:
    input_string: 输入的字符串,应为有效的 JSON 格式。

    返回:
    list 或 None: 如果输入字符串有效,则返回对应的 Python 列表,否则返回 None。
    """
    if input_string is None:
        return None

    try:
        # 将输入字符串中的单引号替换为双引号,以满足 JSON 格式的要求
        input_string = input_string.replace("'", "\"")  
        data = json.loads(input_string)
        return data
    except json.JSONDecodeError:
        print("Error: Invalid JSON string")
        return None   

category_and_product_list = read_string_to_list(category_and_product_response_1)
print(category_and_product_list)
[{'category': 'Smartphones and Accessories', 'products': ['SmartX ProPhone']}, {'category': 'Cameras and Camcorders', 'products': ['FotoSnap DSLR Camera', 'FotoSnap Mirrorless Camera', 'FotoSnap Instant Camera']}, {'category': 'Televisions and Home Theater Systems', 'products': ['CineView 4K TV', 'CineView 8K TV', 'CineView OLED TV', 'SoundMax Home Theater', 'SoundMax Soundbar']}]

7.3.2 进行检索

定义函数 generate_output_string 函数,根据输入的数据列表生成包含产品或类别信息的字符串:

def generate_output_string(data_list):
    """
    根据输入的数据列表生成包含产品或类别信息的字符串。

    参数:
    data_list: 包含字典的列表,每个字典都应包含 "products" 或 "category" 的键。

    返回:
    output_string: 包含产品或类别信息的字符串。
    """
    output_string = ""
    if data_list is None:
        return output_string

    for data in data_list:
        try:
            if "products" in data and data["products"]:
                products_list = data["products"]
                for product_name in products_list:
                    product = get_product_by_name(product_name)
                    if product:
                        output_string += json.dumps(product, indent=4, ensure_ascii=False) + "\n"
                    else:
                        print(f"Error: Product '{product_name}' not found")
            elif "category" in data:
                category_name = data["category"]
                category_products = get_products_by_category(category_name)
                for product in category_products:
                    output_string += json.dumps(product, indent=4, ensure_ascii=False) + "\n"
            else:
                print("Error: Invalid object format")
        except Exception as e:
            print(f"Error: {e}")

    return output_string 

product_information_for_user_message_1 = generate_output_string(category_and_product_list)
print(product_information_for_user_message_1)
{
    "名称": "SmartX ProPhone",
    "类别": "智能手机和配件",
    "品牌": "SmartX",
    "型号": "SX-PP10",
    "保修期": "1 year",
    "评分": 4.6,
    "特色": [
        "6.1-inch display",
        "128GB storage",
        "12MP dual camera",
        "5G"
    ],
    "描述": "一款拥有先进摄像功能的强大智能手机。",
    "价格": 899.99
}
{
    "名称": "FotoSnap DSLR Camera",
    "类别": "相机和摄像机",
    "品牌": "FotoSnap",
    "型号": "FS-DSLR200",
    "保修期": "1 year",
    "评分": 4.7,
    "特色": [
        "24.2MP sensor",
        "1080p video",
        "3-inch LCD",
        "Interchangeable lenses"
    ],
    "描述": "使用这款多功能的单反相机,捕捉惊艳的照片和视频。",
    "价格": 599.99
}
{
    "名称": "FotoSnap Mirrorless Camera",
    "类别": "相机和摄像机",
    "品牌": "FotoSnap",
    "型号": "FS-ML100",
    "保修期": "1 year",
    "评分": 4.6,
    "特色": [
        "20.1MP sensor",
        "4K video",
        "3-inch touchscreen",
        "Interchangeable lenses"
    ],
    "描述": "一款具有先进功能的小巧轻便的无反相机。",
    "价格": 799.99
}
{
    "名称": "FotoSnap Instant Camera",
    "类别": "相机和摄像机",
    "品牌": "FotoSnap",
    "型号": "FS-IC10",
    "保修期": "1 year",
    "评分": 4.1,
    "特色": [
        "Instant prints",
        "Built-in flash",
        "Selfie mirror",
        "Battery-powered"
    ],
    "描述": "使用这款有趣且便携的即时相机,创造瞬间回忆。",
    "价格": 69.99
}
{
    "名称": "CineView 4K TV",
    "类别": "电视和家庭影院系统",
    "品牌": "CineView",
    "型号": "CV-4K55",
    "保修期": "2 years",
    "评分": 4.8,
    "特色": [
        "55-inch display",
        "4K resolution",
        "HDR",
        "Smart TV"
    ],
    "描述": "一款色彩鲜艳、智能功能丰富的惊艳4K电视。",
    "价格": 599.99
}
{
    "名称": "CineView 8K TV",
    "类别": "电视和家庭影院系统",
    "品牌": "CineView",
    "型号": "CV-8K65",
    "保修期": "2 years",
    "评分": 4.9,
    "特色": [
        "65-inch display",
        "8K resolution",
        "HDR",
        "Smart TV"
    ],
    "描述": "通过这款惊艳的8K电视,体验未来。",
    "价格": 2999.99
}
{
    "名称": "CineView OLED TV",
    "类别": "电视和家庭影院系统",
    "品牌": "CineView",
    "型号": "CV-OLED55",
    "保修期": "2 years",
    "评分": 4.7,
    "特色": [
        "55-inch display",
        "4K resolution",
        "HDR",
        "Smart TV"
    ],
    "描述": "通过这款OLED电视,体验真正的五彩斑斓。",
    "价格": 1499.99
}
{
    "名称": "SoundMax Home Theater",
    "类别": "电视和家庭影院系统",
    "品牌": "SoundMax",
    "型号": "SM-HT100",
    "保修期": "1 year",
    "评分": 4.4,
    "特色": [
        "5.1 channel",
        "1000W output",
        "Wireless subwoofer",
        "Bluetooth"
    ],
    "描述": "一款强大的家庭影院系统,提供沉浸式音频体验。",
    "价格": 399.99
}
{
    "名称": "SoundMax Soundbar",
    "类别": "电视和家庭影院系统",
    "品牌": "SoundMax",
    "型号": "SM-SB50",
    "保修期": "1 year",
    "评分": 4.3,
    "特色": [
        "2.1 channel",
        "300W output",
        "Wireless subwoofer",
        "Bluetooth"
    ],
    "描述": "使用这款时尚而功能强大的声音,升级您电视的音频体验。",
    "价格": 199.99
}

7.3.3 生成用户查询的答案

system_message = f"""
您是一家大型电子商店的客服助理。
请以友好和乐于助人的口吻回答问题,并尽量简洁明了。
请确保向用户提出相关的后续问题。
"""

user_message_1 = f"""
请告诉我关于 smartx pro phone 和 the fotosnap camera 的信息。
另外,请告诉我关于你们的tvs的情况。
"""

messages =  [{'role':'system','content': system_message},
             {'role':'user','content': user_message_1},  
             {'role':'assistant',
              'content': f"""相关产品信息:\n\
              {product_information_for_user_message_1}"""}]

final_response = get_completion_from_messages(messages)
print(final_response)
关于SmartX ProPhone和FotoSnap相机的信息如下:

SmartX ProPhone是一款由SmartX品牌推出的智能手机。它拥有6.1英寸的显示屏,128GB的存储空间,12MP的双摄像头和5G网络支持。这款手机的特点是先进的摄像功能。它的价格是899.99美元。

FotoSnap相机有多个型号可供选择。其中包括DSLR相机、无反相机和即时相机。DSLR相机具有24.2MP的传感器、1080p视频拍摄、3英寸的LCD屏幕和可更换镜头。无反相机具有20.1MP的传感器、4K视频拍摄、3英寸的触摸屏和可更换镜头。即时相机具有即时打印功能、内置闪光灯、自拍镜和电池供电。这些相机的价格分别为599.99美元、799.99美元和69.99美元。

关于我们的电视产品,我们有CineView和SoundMax品牌的电视和家庭影院系统可供选择。CineView电视有不同的型号,包括4K分辨率和8K分辨率的电视,以及OLED电视。这些电视都具有HDR和智能电视功能。价格从599.99美元到2999.99美元不等。SoundMax品牌提供家庭影院系统和声音棒。家庭影院系统具有5.1声道、1000W输出、无线低音炮和蓝牙功能,价格为399.99美元。声音棒具有2.1声道、300W输出、无线低音炮和蓝牙功能,价格为199.99美元。

请问您对以上产品中的哪个感兴趣?

在这个例子中,我们只添加了一个特定函数或函数的调用,以通过产品名称获取产品描述或通过类别名称获取类别产品。但是,模型实际上擅长决定何时使用各种不同的工具,并可以正确地使用它们。这就是 ChatGPT 插件背后的思想。我们告诉模型它可以访问哪些工具以及它们的作用,它会在需要从特定来源获取信息或想要采取其他适当的操作时选择使用它们。在这个例子中,我们只能通过精确的产品和类别名称匹配查找信息,但还有更高级的信息检索技术。检索信息的最有效方法之一是使用自然语言处理技术,例如命名实体识别和关系提取。

另一方法是使用文本嵌入(Embedding)来获取信息。嵌入可以用于实现对大型语料库的高效知识检索,以查找与给定查询相关的信息。使用文本嵌入的一个关键优势是它们可以实现模糊或语义搜索,这使您能够在不使用精确关键字的情况下找到相关信息。因此,在此例子中,我们不一定需要产品的确切名称,而可以使用更一般的查询如 “手机” 进行搜索。

第五部分 总结

在设计提示链时,我们并不需要也不建议将所有可能相关信息一次性全加载到模型中,而是采取动态、按需提供信息的策略,原因如下:

  1. 过多无关信息会使模型处理上下文时更加困惑。尤其是低级模型,处理大量数据会表现衰减。

  2. 模型本身对上下文长度有限制,无法一次加载过多信息。

  3. 包含过多信息容易导致模型过拟合,处理新查询时效果较差。

  4. 动态加载信息可以降低计算成本。

  5. 允许模型主动决定何时需要更多信息,可以增强其推理能力。

  6. 我们可以使用更智能的检索机制,而不仅是精确匹配,例如文本 Embedding 实现语义搜索。

因此,合理设计提示链的信息提供策略,既考虑模型的能力限制,也兼顾提升其主动学习能力,是提示工程中需要着重考虑的点。希望这些经验可以帮助大家设计出运行高效且智能的提示链系统。


网站公告

今日签到

点亮在社区的每一天
去签到