第六章 处理输入-链式
链式提示是将复杂任务分解为多个简单Prompt的策略。在本章中,我们将学习如何通过使用链式 Prompt 将复杂任务拆分为一系列简单的子任务。你可能会想,如果我们可以通过思维链推理一次性完成,那为什么要将任务拆分为多个 Prompt 呢?
主要是因为链式提示它具有以下优点:
-
分解复杂度,每个 Prompt 仅处理一个具体子任务,避免过于宽泛的要求,提高成功率。这类似于分阶段烹饪,而不是试图一次完成全部。
-
降低计算成本。过长的 Prompt 使用更多 tokens ,增加成本。拆分 Prompt 可以避免不必要的计算。
-
更容易测试和调试。可以逐步分析每个环节的性能。
-
融入外部工具。不同 Prompt 可以调用 API 、数据库等外部资源。
-
更灵活的工作流程。根据不同情况可以进行不同操作。
综上,链式提示通过将复杂任务进行科学拆分,实现了更高效、可靠的提示设计。它使语言模型集中处理单一子任务,减少认知负荷,同时保留了多步骤任务的能力。随着经验增长,开发者可以逐渐掌握运用链式提示的精髓。
一、 提取产品和类别
我们所拆解的第一个子任务是,要求 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', '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']}]
可以看到,输出是一个对象列表,每个对象都有一个类别和一些产品。如 “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)
[]
二、检索详细信息
我们提供大量的产品信息作为示例,要求模型提取产品和对应的详细信息。限于篇幅,我们产品信息存储在 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}]
三、生成查询答案
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']}]
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 }
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)来获取信息。嵌入可以用于实现对大型语料库的高效知识检索,以查找与给定查询相关的信息。使用文本嵌入的一个关键优势是它们可以实现模糊或语义搜索,这使您能够在不使用精确关键字的情况下找到相关信息。因此,在此例子中,我们不一定需要产品的确切名称,而可以使用更一般的查询如 “手机” 进行搜索。
四、总结
在设计提示链时,我们并不需要也不建议将所有可能相关信息一次性全加载到模型中,而是采取动态、按需提供信息的策略,原因如下:
-
过多无关信息会使模型处理上下文时更加困惑。尤其是低级模型,处理大量数据会表现衰减。
-
模型本身对上下文长度有限制,无法一次加载过多信息。
-
包含过多信息容易导致模型过拟合,处理新查询时效果较差。
-
动态加载信息可以降低计算成本。
-
允许模型主动决定何时需要更多信息,可以增强其推理能力。
-
我们可以使用更智能的检索机制,而不仅是精确匹配,例如文本 Embedding 实现语义搜索。
因此,合理设计提示链的信息提供策略,既考虑模型的能力限制,也兼顾提升其主动学习能力,是提示工程中需要着重考虑的点。希望这些经验可以帮助大家设计出运行高效且智能的提示链系统。
在下一章中我们将讨论如何评估语言模型的输出。