這一系列文章其實就是 Andrew Ng 大大與 OpenAI 合作的一門免費課程【Building Systems with the ChatGPT API】的筆記。很建議大家直接看原本的課程影片,內容蠻淺顯易懂的。
整合提示
在上一篇文章中,我們實際展示了「思考鏈」概念在實作過程中的運用。接著,我們將更進一步地示範如何將大型語言模型(LLM)與我們的其他程式碼進行深度整合。我們將以一個3C賣場的客服助理為例,展示如何在收到使用者訊息後提取出有助於我們進一步處理的提示(prompt),再將提取出的產品資訊與後端的產品資料庫結合,最後將整合後的完整資訊回傳給LLM,由LLM產生出最終的回應訊息。
使用者訊息的解析
接下來的範例中,我們將進一步分析客戶的詢問,並提供一份簡化的產品清單來限縮語言模型回答的範疇,最後會要求語言模型產生一份我們可以直接使用的Python物件列表。
delimiter = "####"
system_message = """
1. 你將會收到一些客戶服務查詢。客戶服務查詢將由 {delimiter} 字元作為分隔符。
2. 如果客戶訊息中的產品在下方允許產品清單中,你就輸出一個Python物件列表,
每個物件同時具有以下格式:
'category': <以下其中一項:電腦和筆記本,手機和配件,電視和家庭劇院系統,
遊戲機和配件,音響設備,相機和攝影機>
或
'products': <必須在以下列出的允許產品中找到的產品列表>
3. 客戶提到下方允許產品清單中的產品時,也同時在在物件屬性列出它對應的類別。
4. 如果客戶沒有特別提到什麼產品,你只要列出類別就好。
5. 如果客戶訊息中的產品不在下方允許產品清單中,你就輸出一個空列表。
6. 如果產品屬性是空的,也不要輸出類別屬性,直接輸出空列表。
允許的產品:
電腦和筆記型電腦類別:
TechPro Ultrabook
BlueWave 電競筆電
PowerLite 二合一電腦
TechPro 桌機
BlueWave Chromebook
... 中間省略 ...
相機和攝影機類別:
FotoSnap 單眼相機
ActionCam 4K 攝影機
FotoSnap 無反光鏡相機
ZoomMaster 攝影機
FotoSnap 拍立得相機
你只可以輸出Python物件列表,不能輸出其他任何資訊。
"""
使用者訊息的處理範例如下:
user_message_1 = f"""
請跟我介紹 SmartX ProPhone 和 FotoSnap 單眼相機, \
也順便告訴我你們是否有電視。
"""
# 中文版本的 prompt 如果沒有加上下方的 in-context learning 範例,
#GPT 的回應會莫名自己加上【回應: 】字樣。
# 課程中的英文版本則不會。
in_context_user1 = "你們有賣 ProGamer 方向盤 嗎?"
in_context_assistant1 = [
{'category': '遊戲機和配件', 'products': 'ProGamer 方向盤'}]
in_context_user2 = "我正在找某個音響。"
in_context_assistant2 = [{'category': '音響設備'}]
messages = [
{'role':'system',
'content': system_message},
{'role':'user',
'content': f"{delimiter}{in_context_user1}{delimiter}"},
{'role':'assistant',
'content': f"{in_context_assistant1}"},
{'role':'user',
'content': f"{delimiter}{in_context_user2}{delimiter}"},
{'role':'assistant',
'content': f"{in_context_assistant2}"},
{'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)
--- 以下是 LLm 的回覆 ---
[{'category': '手機和配件', 'products': ['SmartX ProPhone']},
{'category': '相機和攝影機', 'products': ['FotoSnap 單眼相機']},
{'category': '電視和家庭劇院系統', 'products': ['CineView 4K 液晶電視',
'CineView 8K 液晶電視', 'CineView OLED 電視', 'SoundMax 家庭劇院']}]
在以上提示作業英翻中的過程中,我們遇到了一些小插曲。在我們將提示中文化後發現,ChatGPT對於中文的理解和處理相對於英文存在明顯的落差,這使得ChatGPT對中文提示的理解不如預期。因此,我們最後不僅重新修改了提示,還在傳入LLM的訊息中加入了in-context learning,這才讓LLM能夠按照我們的期待來處理訊息。
對於這部分,當大家在設計中文提示時,可能需要特別留意。
詳細資訊的取得
接下來我們將使用Python的dictionary物件來模擬一個小型資料庫,並從中提取詳細的資訊。具體的程式碼如下:
# 產品資訊
products = {
"TechPro Ultrabook": {
"name": "TechPro Ultrabook",
"category": "電腦與筆記型電腦",
"brand": "TechPro",
"model_number": "TP-UB100",
"warranty": "一年",
"rating": 4.5,
"features": ["13.3 吋顯示器", "8GB 記憶體", "256GB SSD硬碟", \
"Intel Core i5 處理器"],
"description": "一款適合日常使用的輕薄 Ultrabook。",
"price": 799.99 },
... 中間省略 ...
"FotoSnap 拍立得相機": {
"name": "FotoSnap 拍立得相機",
"category": "相機和攝影機",
"brand": "FotoSnap",
"model_number": "FS-IC10",
"warranty": "一年",
"rating": 4.1,
"features": ["立即列印", "內建閃光燈", "自拍鏡", "可攜式電池"],
"description": "用這款有趣和便攜的拍立得相機創造即時回憶。",
"price": 69.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]
print(get_product_by_name("TechPro Ultrabook"))
>>> {'name': 'TechPro Ultrabook', 'category': '電腦與筆記型電腦',
'brand': 'TechPro', 'model_number': 'TP-UB100', 'warranty': '一年',
'rating': 4.5, 'features': ['13.3 吋顯示器', '8GB 記憶體',
'256GB SSD硬碟', 'Intel Core i5 處理器'],
'description': '一款適合日常使用的輕薄 Ultrabook。',
'price': 799.99}
print(get_products_by_category("電腦與筆記型電腦"))
>>> [{'name': 'TechPro Ultrabook', 'category': '電腦與筆記型電腦',
'brand': 'TechPro', 'model_number': 'TP-UB100', 'warranty': '一年',
'rating': 4.5, 'features': ['13.3 吋顯示器', '8GB 記憶體',
'256GB SSD硬碟', 'Intel Core i5 處理器'],
'description': '一款適合日常使用的輕薄 Ultrabook。', 'price': 799.99},
... 中間省略 ...
{'name': 'BlueWave Chromebook', 'category': '電腦與筆記型電腦',
'brand': 'BlueWave', 'model_number': 'BW-CB100', 'warranty': '一年',
'rating': 4.1, 'features': ['11.6 吋顯示器', '4GB 記憶體',
'32GB eMMC 硬碟', 'Chrome 作業系統'],
'description': '一款便捷的Chromebook,適合在網路上瀏覽和處理基本工作。.',
'price': 249.99}]
接下來的工具函式則是用來將LLM回傳的訊息轉換成JSON物件:
import json
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("Error: Invalid JSON string")
return None
轉換成JSON物件的實際結果如下
category_and_product_list = read_string_to_list(category_and_product_response_1)
print(category_and_product_list)
>> [{'category': '手機和配件', 'products': ['SmartX ProPhone']},
{'category': '相機和攝影機', 'products': ['FotoSnap 單眼相機']},
{'category': '電視和家庭劇院系統', 'products': [ 'CineView 4K 液晶電視',
'CineView 8K 液晶電視', 'CineView OLED 電視', 'SoundMax 家庭劇院',
'SoundMax 音箱'] }]
接下來,我們要展示的是將從訊息中提取的產品/產品類別資訊與外部資料結合的核心函式:
def generate_output_string(data_list):
output_string = ""
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)
if product:
# 將產品資訊轉換為 JSON 格式,並加入換行符號
output_string += json.dumps(product, indent=4, ensure_ascii=False) + "\n"
else:
print(f"Error: Product '{product_name}' not found")
# 如果有 "category" 屬性,就代表是要查詢類別
elif "category" in data:
category_name = data["category"]
# 透過類別名稱取得產品資訊
category_products = get_products_by_category(category_name)
for product in category_products:
# 將產品資訊轉換為 JSON 格式,並加入換行符號
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)
>> {
"name": "SmartX ProPhone",
"category": "手機和配件",
"brand": "SmartX",
"model_number": "SX-PP10",
"warranty": "一年",
"rating": 4.6,
"features": [
"6.1 吋顯示器",
"128GB 儲存空間",
"12MP 雙鏡頭",
"5G 網路"
],
"description": "一款功能強大並具有先進照相功能的智慧手機",
"price": 899.99 }
{
"name": "FotoSnap 單眼相機",
"category": "相機和攝影機",
"brand": "FotoSnap",
"model_number": "FS-DSLR200",
"warranty": "一年",
"rating": 4.7,
"features": [
"24.2MP 感光元件",
... 中間省略 ...
"description": "使用這款時尚且音質出色的音響升級您的電視聲光效果。",
"price": 199.99
}
整合資訊並生成回應訊息
有了以上的各種工具函式,我們現在可以將使用者的訊息從提取訊息到獲取詳細資訊,最後產生回應訊息的完整流程串連起來,具體的使用方法如下:
ystem_message = f"""
您是一家大型3C賣場的客服助理。 \
請以親切且樂於助人的態度回答問題,\
並確保您的答案簡潔明瞭。\
也要記得向用戶提出相關的追問。
"""
user_message_1 = f"""
請告訴我關於SmartX Pro手機以及 \
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)
--- 以下是語言模型的回覆訊息 ---
>> 以上是我們店裡的 SmartX Pro 手機和 FotoSnap 單眼相機的資訊。
SmartX Pro 手機擁有6.1吋顯示器、128GB 儲存空間、12MP 雙鏡頭和5G 網路等功能。
FotoSnap 單眼相機則擁有24.2MP 感光元件、1080p 影片、
3 吋 LCD 和可更換鏡頭等功能。
此外,我們店裡還有多款電視產品可供選擇。
CineView 4K 液晶電視擁有55吋顯示器、4K 解析度、HDR 和智慧電視等功能。
CineView 8K 液晶電視則擁有65吋顯示器、8K 解析度、HDR 和智慧電視等功能。
CineView OLED 電視則擁有55吋顯示器、4K 解析度、HDR 和智慧電視等功能。
此外,我們還有 SoundMax 家庭劇院和 SoundMax 音箱等產品,
可以升級您的電視聲光效果。 如果您對這些產品有任何問題或需要更多資訊
,請隨時向我們詢問。