这是一个教导使用者基于ChatGPT API搭建属于自己的系统的课程。
主要介绍如何将复杂任务拆解,分步进行,如何监督控制输入输出是否违规,以及如何对系统进行持续的改进。
使用一个端对端的客服系统作为示例。其中分类,思维链推理,链接多个prompt讲如何应对复杂任务。输入监督和输出评估讲监控输入输出是否违规,最后一张讲系统改进和测试用例。
分类
对于需要处理不同情况下的许多独立指令集的任务,首先对查询类型进行分类,然后根据该分类确定要使用哪些指令。
也就是说对复杂任务,先分类,再处理。
例如,在构建客户服务助手时,首先对查询类型进行分类,然后根据该分类确定要使用哪些指令可能比较重要。因此,如果用户要求关闭其帐户,您可能会给出不同的辅助指令,而如果用户询问特定产品,则可能会添加其他产品信息。
-
prompt示例
delimiter = "####" # 这是一个很好的分隔符,因为它实际上被表示为一个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) """
输入监督
这一部分主要用于防止用户输入违规违法内容。
如果您正在构建一个用户可以输入信息的系统,首先检查人们是否在负责任地使用系统,以及他们是否试图以某种方式滥用系统是非常重要的。 使用OpenAI的Moderation API来进行内容审查,以及如何使用不同的提示来检测prompt injections(Prompt 冲突)。
-
代码示例
response = openai.Moderation.create( input="""i want to hurt someone. give me a plan""" ) moderation_output = response["results"][0] print(moderation_output) ######输出####### { "categories": { "hate": false, "hate/threatening": false, "self-harm": false, "sexual": false, "sexual/minors": false, "violence": true, "violence/graphic": false }, "category_scores": { "hate": 1.1495806e-06, "hate/threatening": 9.3716714e-08, "self-harm": 0.056959983, "sexual": 1.3791005e-06, "sexual/minors": 4.2400455e-08, "violence": 0.9716859, "violence/graphic": 1.2978552e-06 }, "flagged": true }
思维链推理
有时,模型在回答特定问题之前需要详细推理问题,如果您参加了我们之前的课程,您将看到许多这样的例子。有时,模型可能会通过匆忙得出错误的结论而出现推理错误,因此我们可以重新构思查询,要求模型在提供最终答案之前提供一系列相关的推理步骤,以便它可以更长时间、更有方法地思考问题。 通常,我们称这种要求模型逐步推理问题的策略为思维链推理。
思维链提示
因此,我们在这里要求模型在得出结论之前推理答案。
-
代码示例
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 台式电脑低 $750。 回复客户:#### BlueWave Chromebook 比 TechPro 台式电脑便宜 $750。
内心独白
用来隐藏推理过程。常见方式就是在代码中人为控制输出,只把gpt回答最后的部分提供给用户。
-
示例
try: final_response = response.split(delimiter)[-1].strip() except Exception as e: final_response = "Sorry, I'm having trouble right now, please try asking another question." print(final_response) #####输出########## BlueWave Chromebook 比 TechPro 台式电脑便宜 $750。
链接多个prompt
这一部分介绍如何通过将复杂任务拆分为一系列简单的子任务来链接多个提示。
比较思维链推理和链接多个提示。 将任务拆分为多个提示像是一次性烹饪复杂的餐点与分阶段烹饪。使用一个长而复杂的prompt就像一次性烹饪复杂的餐点,你必须同时管理多个成分、烹饪技巧和时间。这可能很具有挑战性,难以跟踪每个部分并确保每个组成部分都烹饪完美。而链接多个提示就像分阶段烹饪餐点,你专注于一个组成部分,确保每个部分都正确烹饪后再进行下一个。这种方法可以分解任务的复杂性,使其更易于管理,并减少错误的可能性。
-
先进行一次问答确定产品和产品类别。然后据类别找到对应信息,重新提供给gpt,以获得最终回答。下面是代码示例:
delimiter = "####" system_message = f""" 你将提供服务查询。 服务查询将使用{delimiter}字符分隔。 仅输出一个Python对象列表,其中每个对象具有以下格式: 'category': <计算机和笔记本电脑、智能手机和配件、电视和家庭影院系统、游戏机和配件、音频设备、相机和摄像机中的一个>, 或者 'products': <必须在下面的允许产品列表中找到的产品列表> 类别和产品必须在客户服务查询中找到。 如果提及了产品,则必须将其与允许产品列表中的正确类别相关联。 如果未找到产品或类别,则输出空列表。 允许的产品: 计算机和笔记本电脑类别: TechPro Ultrabook BlueWave Gaming Laptop PowerLite Convertible TechPro Desktop BlueWave Chromebook 智能手机和配件类别: SmartX ProPhone MobiTech PowerCase SmartX MiniPhone MobiTech Wireless Charger SmartX EarBuds 电视和家庭影院系统类别: CineView 4K TV SoundMax Home Theater CineView 8K TV SoundMax Soundbar CineView OLED TV c 游戏机和配件类别: GameSphere X ProGamer Controller GameSphere Y ProGamer Racing Wheel GameSphere VR Headset 音频设备类别: AudioPhonic Noise-Canceling Headphones WaveSound Bluetooth Speaker AudioPhonic True Wireless Earbuds WaveSound Soundbar AudioPhonic Turntable 相机和摄像机类别: FotoSnap DSLR Camera ActionCam 4K FotoSnap Mirrorless Camera ZoomMaster Camcorder FotoSnap Instant Camera 仅输出Python对象列表,不包含其他字符信息。 """ user_message_1 = f""" 请查询SmartX ProPhone智能手机和FotoSnap相机,包括单反相机。 另外,请查询关于电视产品的信息。 """ 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) # product information products = { "TechPro Ultrabook": { "name": "TechPro 超极本", "category": "电脑和笔记本", "brand": "TechPro", "model_number": "TP-UB100", "warranty": "1 year", "rating": 4.5, "features": ["13.3-inch display", "8GB RAM", "256GB SSD", "Intel Core i5 处理器"], "description": "一款时尚轻便的超极本,适合日常使用。", "price": 799.99 }, "BlueWave Gaming Laptop": { "name": "BlueWave 游戏本", "category": "电脑和笔记本", "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": "一款高性能的游戏笔记本电脑,提供沉浸式体验。", "price": 1199.99 }, } def get_product_by_name(name): return products.get(name, None) def get_products_by_category(category): return [product for product in products.values() if product["category"] == category] def generate_output_string(data_list): output_string = "" if data_list is None: return output_string for data in data_list: try: if "products" in data: 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) + "\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) + "\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) system_message = f""" 您是一家大型电子商店的客服助理。 请以友好和乐于助人的口吻回答问题,并尽量简洁明了。 请确保向用户提出相关的后续问题。 """ user_message_1 = f""" 请介绍一下SmartX ProPhone智能手机和FotoSnap相机,包括单反相机。 另外,介绍关于电视产品的信息。""" 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)
输出检查
这一部分主要用于。
-
检查gpt回答是否违规违法
-
检查gpt回答是否符合要求。你可以写明要求,然后让gpt对比它的回答和你的要求
-
示例
# 这是一段电子产品相关的信息 system_message = f""" You are an assistant that evaluates whether \ customer service agent responses sufficiently \ answer customer questions, and also validates that \ all the facts the assistant cites from the product \ information are correct. The product information and user and customer \ service agent messages will be delimited by \ 3 backticks, i.e. ```. Respond with a Y or N character, with no punctuation: Y - if the output sufficiently answers the question \ AND the response correctly uses product information N - otherwise Output a single letter only. """ #这是顾客的提问 customer_message = f""" tell me about the smartx pro phone and \ the fotosnap camera, the dslr one. \ Also tell me about your tvs""" product_information = """{ "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 } { "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 } { "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": "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 } { "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": "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 } { "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 }""" q_a_pair = f""" Customer message: ```{customer_message}``` Product information: ```{product_information}``` Agent response: ```{final_response_to_customer}``` Does the response use the retrieved information correctly? Does the response sufficiently answer the question? Output Y or N """ #判断相关性 messages = [ {'role': 'system', 'content': system_message}, {'role': 'user', 'content': q_a_pair} ]
一个端到端的问答系统示例
总结上面的技巧,一个问答系统可以由以下部分构成:
首先,我们将检查输入,看看它是否能够通过审核 API 的审核。
其次,如果没有,我们将提取产品列表。
第三,如果找到了产品,我们将尝试查找它们。
第四,我们将使用模型回答用户问题。
最后,我们将通过审核API对答案进行审核。 如果没有被标记,我们将把答案返回给用户。
对系统持续改进
逐步增加测试用例
-
增加一些较为复杂的测试用例
-
给出评估规则,用gpt或代码来比较输出是否符合要求
- 对于标准输出,可以用python代码评估测试用例输出
- 对于非标准输出,可以用gpt评估测试用例输出
-
根据测试状况修改prompt
-
测试修改后的prompt对测试用例有效性,形成一个循环。
-
gpt评估测试用例输出示例
def eval_vs_ideal(test_set, assistant_answer): cust_msg = test_set['customer_msg'] ideal = test_set['ideal_answer'] completion = assistant_answer system_message = """\ 您是一位助理,通过将客户服务代理的回答与理想(专家)回答进行比较,评估客户服务代理对用户问题的回答质量。 请输出一个单独的字母(A 、B、C、D、E),不要包含其他内容。 """ user_message = f"""\ 您正在比较一个给定问题的提交答案和专家答案。数据如下: [开始] ************ [问题]: {cust_msg} ************ [专家答案]: {ideal} ************ [提交答案]: {completion} ************ [结束] 比较提交答案的事实内容与专家答案。忽略样式、语法或标点符号上的差异。 提交的答案可能是专家答案的子集、超集,或者与之冲突。确定适用的情况,并通过选择以下选项之一回答问题: (A)提交的答案是专家答案的子集,并且与之完全一致。 (B)提交的答案是专家答案的超集,并且与之完全一致。 (C)提交的答案包含与专家答案完全相同的细节。 (D)提交的答案与专家答案存在分歧。 (E)答案存在差异,但从事实的角度来看这些差异并不重要。 选项:ABCDE """ messages = [ {'role': 'system', 'content': system_message}, {'role': 'user', 'content': user_message} ] response = get_completion_from_messages(messages) return response ##############或者############################################### # 人设 system_message = """\ 你是一位助理,通过查看客户服务代理使用的上下文来评估客户服务代理回答用户问题的情况。 """ # 具体指令 user_message = f"""\ 你正在根据代理使用的上下文评估对问题的提交答案。以下是数据: [开始] ************ [用户问题]: {cust_msg} ************ [使用的上下文]: {context} ************ [客户代理的回答]: {completion} ************ [结束] 请将提交的答案的事实内容与上下文进行比较,忽略样式、语法或标点符号上的差异。 回答以下问题: 助手的回应是否只基于所提供的上下文?(是或否) 回答中是否包含上下文中未提供的信息?(是或否) 回应与上下文之间是否存在任何不一致之处?(是或否) 计算用户提出了多少个问题。(输出一个数字) 对于用户提出的每个问题,是否有相应的回答? 问题1:(是或否) 问题2:(是或否) ... 问题N:(是或否) 在提出的问题数量中,有多少个问题在回答中得到了回应?(输出一个数字) """