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

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

在这里插入图片描述

🔥博客主页西瓜WiFi

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

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

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

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


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

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

持续更新中…


接上一篇【大语言模型LLM】-基于ChatGPT搭建客服助手(1),本篇我们将不局限于使用ChatGPT的API,还将调用使用Ollama+LLM部署在本地的大语言模型,实现机器人助手的私有化搭建。这部分内容融合了我们前面几篇的全部内容。并且加入了评估步骤。以下是该系统的核心操作流程:

  1. 根据用户输入,获取相关产品信息。
  2. 若产品搜索成功,我们将继续寻找相关的详细产品信息。
  3. 我们使用模型针对用户的问题进行回答。
  4. 最后,让模型自己评估回答是否符合用户需求。

最终,将审核通过的答案呈现给用户。

第一部分 根据用户的输入获取产品列表

import json
from collections import defaultdict


# 获取商品和目录
def get_products_and_category(products_file):
    try:
        with open(products_file, 'r') as file:
            products = json.load(file)
    except FileNotFoundError:
        print('产品文件不存在!')
        return None
    except json.JSONDecodeError:
        print('产品文件格式错误!')
        return None
    # 利用 defaultdict 创建一个空列表,用于存放不同类别的产品信息
    products_by_category = defaultdict(list)
    # 循环遍历产品信息
    for product_name, product_info in products.items():
        # 获取产品类别的信息
        category = product_info.get('category')
        # 如果产品类别存在,则将产品名称添加到对应的列表中
        if category:
            products_by_category[category].append(product_info.get('name'))
    # 返回产品按类别分组的信息
    return dict(products_by_category)


# 将字符串转换为列表
def read_string_to_list(input_string):
    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(input_string)
        print("错误:非法的 Json 格式")
        return None


# 获取目录和产品
def find_category_and_product_only(user_input,products_and_category):
    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

    只输出对象列表,不包含其他内容。
    """
    messages =  [  
    {'role':'system', 'content': system_message},    
    {'role':'user', 'content': f"{delimiter}{user_input}{delimiter}"},  
    ] 
    return get_gemma_response(messages)


# 商品和目录的数据文件
products_file = 'products.json'
categories_file = 'categories.json'

# 获取目录和产品
category_and_product_response = find_category_and_product_only('你好,有电视机嘛?',products_and_category)

# 将字符串转换为列表
category_and_product_list = read_string_to_list(category_and_product_response)

print("第一步:抽取出商品列表")
print('- '*65)
print(category_and_product_list)

输出结果:

第一步:抽取出商品列表
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
[{'category': 'Televisions and Home Theater Systems', 'products': ['CineView 4K TV', 'CineView 8K TV', 'CineView OLED TV']}]

第二部分 根据商品列表抽取商品信息

# 商品信息的搜索
def get_product_by_name(name):
    products = get_products()
    return products.get(name, None)


# 根据商品列表抽取商品信息
def generate_output_string(data_list):
    """
    根据商品列表抽取商品信息并生成输出字符串。

    参数:
        data_list:包含商品信息的列表。

    返回:
        输出字符串。
    """

    output_string = ""

    # 如果输入列表为 None,则返回空字符串。
    if data_list is None:
        return output_string

    # 循环遍历数据列表中的每个项目。
    for data in data_list:
        try:
            # 如果项目包含 "products" 字段,则获取产品列表并循环遍历产品名称。
            if "products" in data:
                products_list = data["products"]
                for product_name in products_list:
                    # 获取产品名称对应的产品对象。
                    product = get_product_by_name(product_name)
                    # 如果产品存在,则将其转换为 JSON 字符串并将其追加到输出字符串中。
                    if product:
                        output_string += json.dumps(product, indent=4) + "\n"
                    # 如果产品不存在,则打印错误信息。
                    else:
                        print(f"错误: 商品 '{product_name}' 没有找到")
            # 如果项目包含 "category" 字段,则获取该类别的产品列表并循环遍历产品。
            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) + "\n"
            # 如果数据格式不正确,则打印错误信息。
            else:
                print("错误:非法的商品格式")
        # 捕获所有异常并打印错误信息。
        except Exception as e:
            print(f"Error: {e}")

    return output_string

product_information = generate_output_string(category_and_product_list)

print("第二步:抽取商品信息")
print('- '*65)
print(product_information)

输出结果:

第二步:抽取商品信息
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
{
    "name": "CineView 4K TV",
    "category": "Televisions and Home Theater Systems",
    "brand": "CineView",
    "model_number": "CV-4K55",
    "warranty": "2 years",
    "rating": 4.8,
    "features": [
        "55-inch display",
        "4K resolution",
        "HDR",
        "Smart TV"
    ],
    "description": "A stunning 4K TV with vibrant colors and smart features.",
    "price": 599.99
}
{
    "name": "CineView 8K TV",
    "category": "Televisions and Home Theater Systems",
    "brand": "CineView",
    "model_number": "CV-8K65",
    "warranty": "2 years",
    "rating": 4.9,
    "features": [
        "65-inch display",
        "8K resolution",
        "HDR",
        "Smart TV"
    ],
    "description": "Experience the future of television with this stunning 8K TV.",
    "price": 2999.99
}
{
    "name": "CineView OLED TV",
    "category": "Televisions and Home Theater Systems",
    "brand": "CineView",
    "model_number": "CV-OLED55",
    "warranty": "2 years",
    "rating": 4.7,
    "features": [
        "55-inch display",
        "4K resolution",
        "HDR",
        "Smart TV"
    ],
    "description": "Experience true blacks and vibrant colors with this OLED TV.",
    "price": 1499.99
}

第三部分 根据信息生成回答

def get_assistant_response(user_input,all_messages):
    # 分隔符
    delimiter = '''```'''
    system_message = f"""
            您是一家大型电子商店的客户服务助理。\
            请以友好和乐于助人的语气回答问题,并提供详细的答案。\
            请确保向用户提出相关的后续问题。
            """
    # 插入 message
    messages = [
        {'role': 'system', 'content': system_message},
        {'role': 'user', 'content': f"{delimiter}{user_input}{delimiter}"},
        {'role': 'assistant', 'content': f"相关商品信息:\n{product_information}"}
    ]
#     print(messages)
    # 获取 LLM 的回答
    # 通过附加 all_messages 实现多轮对话
    final_response = get_gemma_response(all_messages + messages)
    # 将该轮信息加入到历史信息中
    all_messages = all_messages + messages[1:]
    return final_response

final_response = get_assistant_response('你好,有电视机嘛?',[])
print("第三步:获取助手回答")
print('- '*65)
print(final_response)

第四部分 检查模型是否很好的回答了问题

def check_assistant_reponse(final_response,all_messages,debug = True):
    # 分隔符
    delimiter = '''```'''
    system_message = f"""
            您是一家大型电子商店的客户服务助理。\
            请以友好和乐于助人的语气回答问题,并提供简洁明了的答案。\
            请确保向用户提出相关的后续问题。
        """
    user_message = f"""
    用户信息: {delimiter}{user_input}{delimiter}
    代理回复: {delimiter}{final_response}{delimiter}

    回复是否足够回答问题
    如果足够,回答 Y
    如果不足够,回答 N
    仅回答上述字母即可
    """
    print(user_message)
    messages = [
        {'role': 'system', 'content': system_message},
        {'role': 'user', 'content': user_message}
    ]
    # 要求模型评估回答
    evaluation_response = get_gemma_response(messages)
    print(evaluation_response)
    if debug: print("第四步:模型评估该回答")

    # 第七步:如果评估为 Y,输出回答;如果评估为 N,反馈将由人工修正答案
    # 使用 in 来避免模型可能生成 Yes
    if "Y" in evaluation_response:  
        if debug: print("第五步:模型赞同了该回答.")
        return final_response, all_messages
    else:
        if debug: print("第六步:模型不赞成该回答.")
        neg_str = "很抱歉,我无法提供您所需的信息。我将为您转接到一位人工客服代表以获取进一步帮助。"
        return neg_str, all_messages
        
check_response_result = check_assistant_reponse(final_response,[])

print(check_response_result )

输出结果:

用户信息: ```请告诉我关于 smartx pro phone 和 the fotosnap camera 的信息。另外,请告诉我关于你们的tvs的情况。```
    代理回复: ```...

请问您对哪种电视机感兴趣?```

    回复是否足够回答问题
    如果足够,回答 Y
    如果不足够,回答 N
    仅回答上述字母即可
    
N
第四步:模型评估该回答
第六步:模型不赞成该回答.
('很抱歉,我无法提供您所需的信息。我将为您转接到一位人工客服代表以获取进一步帮助。', [])

这里模型的回答没有通过自己的检查,我们需要注意到,在中文的理解和处理方面,由于模型的特性,我们可能会偶尔遇到不理想的结果。在这种情况下,可以多尝试几次,或者进行深入的研究,以找到更稳定的方法。

第五部分 客服助手全部代码

import json
import requests
from collections import defaultdict


# 商品和目录的数据文件
products_file = 'products.json'
categories_file = 'categories.json'

# 分隔符
delimiter = "####"
# 第二步(抽取商品)系统信息文本
step_2_system_message_content = f"""
您将获得一次客户服务对话。最近的用户查询将使用{delimiter}字符进行分隔。

输出一个Python对象列表,其中每个对象具有以下格式:
'category': <包括以下几个类别:Computers and Laptops、martphones and Accessories、elevisions and Home Theater Systems、elevisions and Home Theater Systems、elevisions and Home Theater Systems、'category': <包括以下几个类别:Computers and Laptops、martphones and Accessories、elevisions and Home Theater Systems、elevisions and Home Theater Systems、elevisions and Home Theater Systems、相机和摄像机>,
或者
'products': <必须是下面的允许产品列表中找到的产品>

类别和产品必须在客户服务查询中找到。
如果提到了产品,它必须与下面的允许产品列表中的正确类别相关联。
如果未找到任何产品或类别,请输出一个空列表。
只列出之前对话的早期部分未提及和讨论的产品和类别。

允许的产品:

Computers and Laptops类别:

TechPro Ultrabook
BlueWave Gaming Laptop
PowerLite Convertible
TechPro Desktop
BlueWave Chromebook

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

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

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

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

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

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

step_2_system_message = {'role':'system', 'content': step_2_system_message_content}    

# 第四步(生成用户回答)的系统信息
step_4_system_message_content = f"""
    你是一家大型电子商店的客户服务助理。
    以友好和乐于助人的语气回答,回答保持简洁明了。
    确保让用户提出相关的后续问题。
"""

step_4_system_message = {'role':'system', 'content': step_4_system_message_content}    

# 第六步(验证模型回答)的系统信息
step_6_system_message_content = f"""
    你是一个助手,评估客户服务代理的回答是否足够回答客户的问题,并验证助手从产品信息中引用的所有事实是否正确。
    对话历史、产品信息、用户和客户服务代理的消息将用```进行分隔。
    请用一个字母回答,不带标点符号:
    Y - 如果输出足够回答问题,并且回答正确使用了产品信息
    N - 输出不足够回答问题,或者没有正确使用产品信息

    只输出一个字母。
"""

step_6_system_message = {'role':'system', 'content': step_6_system_message_content}    


def get_gemma_response(messages,model='llama3'): 
    '''
    prompt: 对应的提示词
    
    Data 参数:
        model:(必需)模型名称
        messages:聊天消息,这可用于保持聊天记忆
        消息对象具有以下字段:

        role:消息的角色,可以是system、user或assistant
        content:消息的内容
        images(可选):要包含在消息中的图像列表(用于多模态模型,如llava)
        高级参数(可选):

        format:返回响应的格式。当前唯一接受的值是json
        options:在Modelfile文档中列出的额外模型参数,如temperature
        stream:如果为false,则响应将作为单个响应对象返回,而不是对象流
        keep_alive:控制模型在请求后保持加载到内存中的时间(默认值:5m)
    '''
    # Ollama 本地 API 接口
    url = 'http://localhost:11434/api/chat'
    # 定义请求的数据
    data = {
        "model": model,
        "messages": messages,
        "stream": False
    }
    # 发送 POST 请求
    response = requests.post(url, json=data)
    response_data = response.json()
    return response_data['message']['content']


# 创建目录(如果没有本地目录文件,需要创建一份)
def create_categories():
    categories_dict = {
      'Billing': [
                'Unsubscribe or upgrade',
                'Add a payment method',
                'Explanation for charge',
                'Dispute a charge'],
      'Technical Support':[
                'General troubleshooting',
                'Device compatibility',
                'Software updates'],
      'Account Management':[
                'Password reset',
                'Update personal information',
                'Close account',
                'Account security'],
      'General Inquiry':[
                'Product information',
                'Pricing',
                'Feedback',
                'Speak to a human']
    }

    with open(categories_file, 'w') as file:
        json.dump(categories_dict, file)
        
    return categories_dict

# 获取目录数据
def get_categories():
    with open(categories_file, 'r') as file:
            categories = json.load(file)
    return categories

# 获取商品列表
def get_product_list():
    products = get_products()
    product_list = []
    for product in products.keys():
        product_list.append(product)
    
    return product_list

# 获取商品和目录
def get_products_and_category():
    # 获取所有商品
    products = get_products()
    # 使用 defaultdict初始化一个空字典,用于将商品分组到目录中
    products_by_category = defaultdict(list)
    # 遍历所有商品
    for product_name, product_info in products.items():
        # 获取商品的目录
        category = product_info.get('category')
        # 如果目录存在,则将商品名称添加到该目录的列表中
        if category:
            products_by_category[category].append(product_info.get('name'))
    
    # 返回商品按目录分组的字典
    return dict(products_by_category)

# 从商品数据中获取
def get_products():
    with open(products_file, 'r') as file:
        products = json.load(file)
    return products

# 从用户问题中抽取商品和类别
def find_category_and_product(user_input,products_and_category):
    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': <必须是下面的允许产品列表中找到的产品列表>

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

    允许的产品以JSON格式提供。
    每个项的键表示类别。
    每个项的值是该类别中的产品列表。
    允许的产品:{products_and_category}
    
    """
    messages =  [  
    {'role':'system', 'content': system_message},    
    {'role':'user', 'content': f"{delimiter}{user_input}{delimiter}"},  
    ] 
    return get_gemma_response(messages)

# 相比上一个函数,可获取的商品直接在 template 中限定
def find_category_and_product_only(user_input,products_and_category):
    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

    只输出对象列表,不包含其他内容。
    """
    messages =  [  
    {'role':'system', 'content': system_message},    
    {'role':'user', 'content': f"{delimiter}{user_input}{delimiter}"},  
    ] 
    return get_gemma_response(messages)

# 从问题中抽取商品
def get_products_from_query(user_msg):
    products_and_category = get_products_and_category()
    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': <必须是下面的允许产品列表中找到的产品列表>

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

    允许的产品以JSON格式提供。
    每个项目的键表示类别。
    每个项目的值是该类别中的产品列表。

    以下是允许的产品:{products_and_category}

    """
    
    messages =  [  
    {'role':'system', 'content': system_message},    
    {'role':'user', 'content': f"{delimiter}{user_msg}{delimiter}"},  
    ] 
    category_and_product_response = get_gemma_response(messages)
    
    return category_and_product_response


# 商品信息的搜索
def get_product_by_name(name):
    products = get_products()
    return products.get(name, None)

def get_products_by_category(category):
    products = get_products()
    return [product for product in products.values() if product["category"] == category]

# 获取提及的商品信息
def get_mentioned_product_info(data_list):
    # 初始化一个空列表来保存商品信息
    product_info_l = []

    # 如果输入列表为空,则返回空列表
    if data_list is None:
        return product_info_l

    # 遍历数据列表
    for data in data_list:
        try:
            # 检查数据中是否存在 "products" 字段,如果是则获取商品列表
            if "products" in data:
                products_list = data["products"]
                # 遍历商品列表
                for product_name in products_list:
                    # 获取商品信息,并将其添加到商品信息列表中
                    product = get_product_by_name(product_name)
                    if product:
                        product_info_l.append(product)
                    # 如果商品未找到,则打印错误信息
                    else:
                        print(f"错误: 商品 '{product_name}' 未找到")
            # 检查数据中是否存在 "category" 字段,如果是则获取该类别的商品信息
            elif "category" in data:
                category_name = data["category"]
                # 获取该类别的商品信息
                category_products = get_products_by_category(category_name)
                # 遍历商品信息列表并将其添加到商品信息列表中
                for product in category_products:
                    product_info_l.append(product)
            # 如果数据格式非法,则打印错误信息
            else:
                print("错误:非法的商品格式")
        # 捕获任何异常并打印错误信息
        except Exception as e:
            print(f"Error: {e}")

    # 返回商品信息列表
    return product_info_l


def read_string_to_list(input_string):
    if input_string is None:
        return None

    try:
        input_string = input_string.replace("'", "\"")  # Replace single quotes with double quotes for valid JSON
        data = json.loads(input_string)
        return data
    except json.JSONDecodeError:
        print(input_string)
        print("错误:非法的 Json 格式")
        return None

# 生成输出字符串
def generate_output_string(data_list):
    """
    从数据列表中生成输出字符串。

    参数:
        data_list:包含商品或目录信息的列表。

    返回:
        输出字符串。
    """

    output_string = ""

    # 如果数据列表为空,则返回空字符串。
    if data_list is None:
        return output_string

    # 遍历数据列表。
    for data in data_list:
        try:
            # 检查数据是否包含 "products" 或 "category" 字段。
            if "products" in data:
                # 获取产品列表。
                products_list = data["products"]

                # 遍历产品列表。
                for product_name in products_list:
                    # 获取商品信息。
                    product = get_product_by_name(product_name)

                    # 如果商品存在,则将其转换为 JSON 字符串并添加到输出字符串中。
                    if product:
                        output_string += json.dumps(product, indent=4) + "\n"
                    else:
                        # 如果商品不存在,则打印错误信息。
                        print(f"错误: 商品 '{product_name}' 没有找到")
            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) + "\n"
            else:
                # 打印错误信息。
                print("错误:非法的商品格式")
        except Exception as e:
            # 打印错误信息。
            print(f"Error: {e}")

    # 返回输出字符串。
    return output_string

# Example usage:
#product_information_for_user_message_1 = generate_output_string(category_and_product_list)
#print(product_information_for_user_message_1)
# 回答用户问题
def answer_user_msg(user_msg,product_info):
    """
    代码参见第五节课
    """
    delimiter = "####"
    system_message = f"""
    您是一家大型电子商店的客户服务助理。\
    请用友好和乐于助人的口吻回答问题,提供简洁明了的答案。\
    确保向用户提出相关的后续问题。
    """
    # user_msg = f"""
    # tell me about the smartx pro phone and the fotosnap camera, the dslr one. Also what tell me about your tvs"""
    messages =  [  
    {'role':'system', 'content': system_message},   
    {'role':'user', 'content': f"{delimiter}{user_msg}{delimiter}"},  
    {'role':'assistant', 'content': f"相关产品信息:\n{product_info}"},   
    ] 
    response = get_gemma_response(messages)
    return response

# 创建并存入商品数据
def create_products():
    # product information
    # fun fact: all these products are fake and were generated by a language model
    products = {
        "TechPro Ultrabook": {
            "name": "TechPro Ultrabook",
            "category": "Computers and Laptops",
            "brand": "TechPro",
            "model_number": "TP-UB100",
            "warranty": "1 year",
            "rating": 4.5,
            "features": ["13.3-inch display", "8GB RAM", "256GB SSD", "Intel Core i5 processor"],
            "description": "A sleek and lightweight ultrabook for everyday use.",
            "price": 799.99
        },
        "BlueWave Gaming Laptop": {
            "name": "BlueWave Gaming Laptop",
            "category": "Computers and Laptops",
            "brand": "BlueWave",
            "model_number": "BW-GL200",
            "warranty": "2 years",
            "rating": 4.7,
            "features": ["15.6-inch display", "16GB RAM", "512GB SSD", "NVIDIA GeForce RTX 3060"],
            "description": "A high-performance gaming laptop for an immersive experience.",
            "price": 1199.99
        },
        "PowerLite Convertible": {
            "name": "PowerLite Convertible",
            "category": "Computers and Laptops",
            "brand": "PowerLite",
            "model_number": "PL-CV300",
            "warranty": "1 year",
            "rating": 4.3,
            "features": ["14-inch touchscreen", "8GB RAM", "256GB SSD", "360-degree hinge"],
            "description": "A versatile convertible laptop with a responsive touchscreen.",
            "price": 699.99
        },
        "TechPro Desktop": {
            "name": "TechPro Desktop",
            "category": "Computers and Laptops",
            "brand": "TechPro",
            "model_number": "TP-DT500",
            "warranty": "1 year",
            "rating": 4.4,
            "features": ["Intel Core i7 processor", "16GB RAM", "1TB HDD", "NVIDIA GeForce GTX 1660"],
            "description": "A powerful desktop computer for work and play.",
            "price": 999.99
        },
        "BlueWave Chromebook": {
            "name": "BlueWave Chromebook",
            "category": "Computers and Laptops",
            "brand": "BlueWave",
            "model_number": "BW-CB100",
            "warranty": "1 year",
            "rating": 4.1,
            "features": ["11.6-inch display", "4GB RAM", "32GB eMMC", "Chrome OS"],
            "description": "A compact and affordable Chromebook for everyday tasks.",
            "price": 249.99
        },
        "SmartX ProPhone": {
            "name": "SmartX ProPhone",
            "category": "Smartphones and Accessories",
            "brand": "SmartX",
            "model_number": "SX-PP10",
            "warranty": "1 year",
            "rating": 4.6,
            "features": ["6.1-inch display", "128GB storage", "12MP dual camera", "5G"],
            "description": "A powerful smartphone with advanced camera features.",
            "price": 899.99
        },
        "MobiTech PowerCase": {
            "name": "MobiTech PowerCase",
            "category": "Smartphones and Accessories",
            "brand": "MobiTech",
            "model_number": "MT-PC20",
            "warranty": "1 year",
            "rating": 4.3,
            "features": ["5000mAh battery", "Wireless charging", "Compatible with SmartX ProPhone"],
            "description": "A protective case with built-in battery for extended usage.",
            "price": 59.99
        },
        "SmartX MiniPhone": {
            "name": "SmartX MiniPhone",
            "category": "Smartphones and Accessories",
            "brand": "SmartX",
            "model_number": "SX-MP5",
            "warranty": "1 year",
            "rating": 4.2,
            "features": ["4.7-inch display", "64GB storage", "8MP camera", "4G"],
            "description": "A compact and affordable smartphone for basic tasks.",
            "price": 399.99
        },
        "MobiTech Wireless Charger": {
            "name": "MobiTech Wireless Charger",
            "category": "Smartphones and Accessories",
            "brand": "MobiTech",
            "model_number": "MT-WC10",
            "warranty": "1 year",
            "rating": 4.5,
            "features": ["10W fast charging", "Qi-compatible", "LED indicator", "Compact design"],
            "description": "A convenient wireless charger for a clutter-free workspace.",
            "price": 29.99
        },
        "SmartX EarBuds": {
            "name": "SmartX EarBuds",
            "category": "Smartphones and Accessories",
            "brand": "SmartX",
            "model_number": "SX-EB20",
            "warranty": "1 year",
            "rating": 4.4,
            "features": ["True wireless", "Bluetooth 5.0", "Touch controls", "24-hour battery life"],
            "description": "Experience true wireless freedom with these comfortable earbuds.",
            "price": 99.99
        },

        "CineView 4K TV": {
            "name": "CineView 4K TV",
            "category": "Televisions and Home Theater Systems",
            "brand": "CineView",
            "model_number": "CV-4K55",
            "warranty": "2 years",
            "rating": 4.8,
            "features": ["55-inch display", "4K resolution", "HDR", "Smart TV"],
            "description": "A stunning 4K TV with vibrant colors and smart features.",
            "price": 599.99
        },
        "SoundMax Home Theater": {
            "name": "SoundMax Home Theater",
            "category": "Televisions and Home Theater Systems",
            "brand": "SoundMax",
            "model_number": "SM-HT100",
            "warranty": "1 year",
            "rating": 4.4,
            "features": ["5.1 channel", "1000W output", "Wireless subwoofer", "Bluetooth"],
            "description": "A powerful home theater system for an immersive audio experience.",
            "price": 399.99
        },
        "CineView 8K TV": {
            "name": "CineView 8K TV",
            "category": "Televisions and Home Theater Systems",
            "brand": "CineView",
            "model_number": "CV-8K65",
            "warranty": "2 years",
            "rating": 4.9,
            "features": ["65-inch display", "8K resolution", "HDR", "Smart TV"],
            "description": "Experience the future of television with this stunning 8K TV.",
            "price": 2999.99
        },
        "SoundMax Soundbar": {
            "name": "SoundMax Soundbar",
            "category": "Televisions and Home Theater Systems",
            "brand": "SoundMax",
            "model_number": "SM-SB50",
            "warranty": "1 year",
            "rating": 4.3,
            "features": ["2.1 channel", "300W output", "Wireless subwoofer", "Bluetooth"],
            "description": "Upgrade your TV's audio with this sleek and powerful soundbar.",
            "price": 199.99
        },
        "CineView OLED TV": {
            "name": "CineView OLED TV",
            "category": "Televisions and Home Theater Systems",
            "brand": "CineView",
            "model_number": "CV-OLED55",
            "warranty": "2 years",
            "rating": 4.7,
            "features": ["55-inch display", "4K resolution", "HDR", "Smart TV"],
            "description": "Experience true blacks and vibrant colors with this OLED TV.",
            "price": 1499.99
        },

        "GameSphere X": {
            "name": "GameSphere X",
            "category": "Gaming Consoles and Accessories",
            "brand": "GameSphere",
            "model_number": "GS-X",
            "warranty": "1 year",
            "rating": 4.9,
            "features": ["4K gaming", "1TB storage", "Backward compatibility", "Online multiplayer"],
            "description": "A next-generation gaming console for the ultimate gaming experience.",
            "price": 499.99
        },
        "ProGamer Controller": {
            "name": "ProGamer Controller",
            "category": "Gaming Consoles and Accessories",
            "brand": "ProGamer",
            "model_number": "PG-C100",
            "warranty": "1 year",
            "rating": 4.2,
            "features": ["Ergonomic design", "Customizable buttons", "Wireless", "Rechargeable battery"],
            "description": "A high-quality gaming controller for precision and comfort.",
            "price": 59.99
        },
        "GameSphere Y": {
            "name": "GameSphere Y",
            "category": "Gaming Consoles and Accessories",
            "brand": "GameSphere",
            "model_number": "GS-Y",
            "warranty": "1 year",
            "rating": 4.8,
            "features": ["4K gaming", "500GB storage", "Backward compatibility", "Online multiplayer"],
            "description": "A compact gaming console with powerful performance.",
            "price": 399.99
        },
        "ProGamer Racing Wheel": {
            "name": "ProGamer Racing Wheel",
            "category": "Gaming Consoles and Accessories",
            "brand": "ProGamer",
            "model_number": "PG-RW200",
            "warranty": "1 year",
            "rating": 4.5,
            "features": ["Force feedback", "Adjustable pedals", "Paddle shifters", "Compatible with GameSphere X"],
            "description": "Enhance your racing games with this realistic racing wheel.",
            "price": 249.99
        },
        "GameSphere VR Headset": {
            "name": "GameSphere VR Headset",
            "category": "Gaming Consoles and Accessories",
            "brand": "GameSphere",
            "model_number": "GS-VR",
            "warranty": "1 year",
            "rating": 4.6,
            "features": ["Immersive VR experience", "Built-in headphones", "Adjustable headband", "Compatible with GameSphere X"],
            "description": "Step into the world of virtual reality with this comfortable VR headset.",
            "price": 299.99
        },

        "AudioPhonic Noise-Canceling Headphones": {
            "name": "AudioPhonic Noise-Canceling Headphones",
            "category": "Audio Equipment",
            "brand": "AudioPhonic",
            "model_number": "AP-NC100",
            "warranty": "1 year",
            "rating": 4.6,
            "features": ["Active noise-canceling", "Bluetooth", "20-hour battery life", "Comfortable fit"],
            "description": "Experience immersive sound with these noise-canceling headphones.",
            "price": 199.99
        },
        "WaveSound Bluetooth Speaker": {
            "name": "WaveSound Bluetooth Speaker",
            "category": "Audio Equipment",
            "brand": "WaveSound",
            "model_number": "WS-BS50",
            "warranty": "1 year",
            "rating": 4.5,
            "features": ["Portable", "10-hour battery life", "Water-resistant", "Built-in microphone"],
            "description": "A compact and versatile Bluetooth speaker for music on the go.",
            "price": 49.99
        },
        "AudioPhonic True Wireless Earbuds": {
            "name": "AudioPhonic True Wireless Earbuds",
            "category": "Audio Equipment",
            "brand": "AudioPhonic",
            "model_number": "AP-TW20",
            "warranty": "1 year",
            "rating": 4.4,
            "features": ["True wireless", "Bluetooth 5.0", "Touch controls", "18-hour battery life"],
            "description": "Enjoy music without wires with these comfortable true wireless earbuds.",
            "price": 79.99
        },
        "WaveSound Soundbar": {
            "name": "WaveSound Soundbar",
            "category": "Audio Equipment",
            "brand": "WaveSound",
            "model_number": "WS-SB40",
            "warranty": "1 year",
            "rating": 4.3,
            "features": ["2.0 channel", "80W output", "Bluetooth", "Wall-mountable"],
            "description": "Upgrade your TV's audio with this slim and powerful soundbar.",
            "price": 99.99
        },
        "AudioPhonic Turntable": {
            "name": "AudioPhonic Turntable",
            "category": "Audio Equipment",
            "brand": "AudioPhonic",
            "model_number": "AP-TT10",
            "warranty": "1 year",
            "rating": 4.2,
            "features": ["3-speed", "Built-in speakers", "Bluetooth", "USB recording"],
            "description": "Rediscover your vinyl collection with this modern turntable.",
            "price": 149.99
        },

        "FotoSnap DSLR Camera": {
            "name": "FotoSnap DSLR Camera",
            "category": "Cameras and Camcorders",
            "brand": "FotoSnap",
            "model_number": "FS-DSLR200",
            "warranty": "1 year",
            "rating": 4.7,
            "features": ["24.2MP sensor", "1080p video", "3-inch LCD", "Interchangeable lenses"],
            "description": "Capture stunning photos and videos with this versatile DSLR camera.",
            "price": 599.99
        },
        "ActionCam 4K": {
            "name": "ActionCam 4K",
            "category": "Cameras and Camcorders",
            "brand": "ActionCam",
            "model_number": "AC-4K",
            "warranty": "1 year",
            "rating": 4.4,
            "features": ["4K video", "Waterproof", "Image stabilization", "Wi-Fi"],
            "description": "Record your adventures with this rugged and compact 4K action camera.",
            "price": 299.99
        },
        "FotoSnap Mirrorless Camera": {
            "name": "FotoSnap Mirrorless Camera",
            "category": "Cameras and Camcorders",
            "brand": "FotoSnap",
            "model_number": "FS-ML100",
            "warranty": "1 year",
            "rating": 4.6,
            "features": ["20.1MP sensor", "4K video", "3-inch touchscreen", "Interchangeable lenses"],
            "description": "A compact and lightweight mirrorless camera with advanced features.",
            "price": 799.99
        },
        "ZoomMaster Camcorder": {
            "name": "ZoomMaster Camcorder",
            "category": "Cameras and Camcorders",
            "brand": "ZoomMaster",
            "model_number": "ZM-CM50",
            "warranty": "1 year",
            "rating": 4.3,
            "features": ["1080p video", "30x optical zoom", "3-inch LCD", "Image stabilization"],
            "description": "Capture life's moments with this easy-to-use camcorder.",
            "price": 249.99
        },
        "FotoSnap Instant Camera": {
            "name": "FotoSnap Instant Camera",
            "category": "Cameras and Camcorders",
            "brand": "FotoSnap",
            "model_number": "FS-IC10",
            "warranty": "1 year",
            "rating": 4.1,
            "features": ["Instant prints", "Built-in flash", "Selfie mirror", "Battery-powered"],
            "description": "Create instant memories with this fun and portable instant camera.",
            "price": 69.99
        }
    }

    products_file = 'products.json'
    with open(products_file, 'w') as file:
        json.dump(products, file)
        
    return products




'''
注意:限于模型对中文理解能力较弱,中文 Prompt 可能会随机出现不成功,可以多次运行;也非常欢迎同学探究更稳定的中文 Prompt
'''
def process_user_message_ch(user_input, all_messages, debug=True):
    """
    对用户信息进行预处理
    
    参数:
    user_input : 用户输入
    all_messages : 历史信息
    debug : 是否开启 DEBUG 模式,默认开启
    """
    # 分隔符
    delimiter = "```"
    
    # 第一步:抽取出商品和对应的目录,类似于之前课程中的方法,做了一个封装
    category_and_product_response = find_category_and_product_only(user_input, get_products_and_category())
    # 将抽取出来的字符串转化为列表
    category_and_product_list = read_string_to_list(category_and_product_response)
    # print(category_and_product_list)

    if debug: print("第一步:抽取出商品列表")

    # 第三步:查找商品对应信息
    product_information = generate_output_string(category_and_product_list)
    if debug: print("第二步:查找抽取出的商品信息")
#     print('商品信息:',product_information)
    # 第四步:根据信息生成回答
    system_message = f"""
        您是一家西瓜电器的客户服务助理。\
        请以友好和乐于助人的语气回答问题,并提供简洁明了的答案。\
        当用户向你咨询时,请友好的告诉用户你所知道的产品信息。\
        引导用户购买,我们的产品。\
        检查你的回答,如果你的回答不是中文简体,请把它翻译为中文简体。\
        请确保向用户提出相关的后续问题。
    """
    # 插入 message
    messages = [
        {'role': 'system', 'content': system_message},
        {'role': 'user', 'content': f"{delimiter}{user_input}{delimiter}"},
        {'role': 'assistant', 'content': f"相关商品信息:\n{product_information}"}
    ]
    # 获取 LLM 的回答
    # 通过附加 all_messages 实现多轮对话
    final_response = get_gemma_response(all_messages + messages)
#     print(messages)
#     print(final_response)
    if debug:print("第三步:生成用户回答")
    # 将该轮信息加入到历史信息中
    all_messages = all_messages + messages[1:]

    # 第四步:模型检查是否很好地回答了用户问题
    user_message = f"""
    用户信息: {delimiter}{user_input}{delimiter}
    代理回复: {delimiter}{final_response}{delimiter}

    回复是否足够回答问题
    如果足够,回答 Y
    如果不足够,回答 N
    仅回答上述字母即可
    """
    # print(final_response)
    messages = [
        {'role': 'system', 'content': system_message},
        {'role': 'user', 'content': user_message}
    ]
    # 要求模型评估回答
    evaluation_response = get_gemma_response(messages)
    # print(evaluation_response)
    if debug: print("第六步:模型评估该回答")

    # 第七步:如果评估为 Y,输出回答;如果评估为 N,反馈将由人工修正答案
    # 使用 in 来避免模型可能生成 Yes
    if "Y" in evaluation_response:  
        if debug: print("第七步:模型赞同了该回答.")
        return final_response, all_messages
    else:
        if debug: print("第七步:模型不赞成该回答.")
        neg_str = "很抱歉,我无法提供您所需的信息。我将为您转接到一位人工客服代表以获取进一步帮助。"
        return neg_str, all_messages

user_input = "请告诉我关于 smartx pro phone 和 the fotosnap camera 的信息。另外,请告诉我关于你们的tvs的情况。"
response,_ = process_user_message_ch(user_input,[],debug=False)
print(response)

输出结果:

####
Customer Service Query: 
####
category: Smartphones and Accessories
products: ["SmartX ProPhone", "MobiTech PowerCase"]

category: Cameras and Camcorders
products: ["FotoSnap DSLR Camera", "ActionCam 4K"]

category: Televisions and Home Theater Systems
products: ["CineView 4K TV", "SoundMax Home Theater"]
####
错误:非法的 Json 格式
1. SmartX Pro Phone:

SmartX Pro Phone 是我们公司最新推出的智能手机产品。它具有高级的照相摄像技术、高速数据处理和智能化功能等特点。这款手机适用于日常生活和工作,能够满足您的多样需求。

2. FotoSnap Camera:

FotoSnap Camera 是我们的新一代相机产品。它拥有超高像素、高感光度和快速拍摄速度等特点。相机可以拍摄高清照片、视频,且具有智能化功能,可以自动调整拍摄参数、修复照相结果等功能。

3. TV:

关于我们公司的电视产品,我可以告诉您,我们提供了一系列的液晶电视、LED电视和OLED电视等多样化的选择。我们的电视具备高清显示、高对比度和智能化功能等特点,可以满足您的家庭娱乐需求。

额外信息:
您是否想了解更多关于 SmartX Pro Phone 和 FotoSnap Camera 的详细信息?或者您也想了解关于我们公司的电视产品有什么优缺点?

请随时提出您的问题,我们将尽快为您解答!