準備階段#
- 創建一個虛擬環境 agent, pip install openai python-dotenv。
- 要想用 openai 庫對接國內的大模型,對於每個廠家,我們都需要準備前菜:
- 一個 api_key,這個需要到各家的開放平台上去申請。
- 一個 base_url,這個需要到各家的開放平台上去拷貝。
- 他們家的對話模型名稱。
後面兩個是公開的。
在項目的根目錄新建一個 txt 文件,把文件名改成.env。 裡面填入一行字符串: ZISHU_API_KEY = 你的 api_key。這裡用自塾提供的默認 API。
自塾:
base_url = "http://43.200.7.56:8008/v1"
chat_model = "glm-4-flash"
- 加載環境變量代碼:
import os
from dotenv import load_dotenv
# 加載環境變量
load_dotenv()
# 從環境變量中讀取api_key
api_key = os.getenv('ZISHU_API_KEY')
base_url = "http://43.200.7.56:8008/v1"
chat_model = "glm-4-flash"
ERROR: api_key 未能成功提取到。
fixed:load_dotenv('.env.txt')
4. 構造 client
from openai import OpenAI
client = OpenAI(
api_key = api_key,
base_url = base_url
)
Prompt#
什麼是結構化 Prompt?#
參考:https://github.com/EmbraceAGI/LangGPT
結構化 Prompt 的思想通俗點來說就是像寫文章一樣寫 Prompt**。結構化編寫 Prompt 有各種各樣優質的模板幫助你把 Prompt 寫的更輕鬆、性能更好。所以寫結構化 Prompt 可以有各種各樣的模板,你可以像用 PPT 模板一樣選擇或創造自己喜歡的模板。
LangGPT 變量#
- ChatGPT 可以識別各種良好標記的層級結構內容。
- 將 prompt 的內容用結構化方式呈現,並設置標題即可方便的引用,修改,設置 prompt 內容。可以直接使用段落標題來指代大段內容,也可以告訴 ChatGPT 修改調整指定內容。這類似於編程中的變量,因此我們可以將這種標題當做變量使用。
- Markdown 的語法層級結構很好,適合編寫 prompt,因此 LangGPT 的變量基於 markdown 語法。實際上除 markdown 外各種能實現標記作用,如 json,yaml, 甚至好好排版好格式 都可以。
LangGPT 模板#
使用 Role 模板,只需要按照模板填寫相應內容即可。
除了變量和模板外,LangGPT 還提供了命令,記憶器,條件句等語法設置方法。
結構化 Prompt 概念#
標識符:#, <> 等符號 (-, [] 也是),這兩個符號依次標識標題,變量,控制內容層級,用於標識層次結構。
屬性詞:Role, Profile, Initialization 等等,屬性詞包含語義,是對模塊下內容的總結和提示,用於標識語義結構。
使用分隔符清晰標示輸入的不同部分,像三重引號、XML 標記、節標題等分隔符可以幫助標示需要以不同方式處理的文本部分。
對 GPT 模型來說,標識符標識的層級結構實現了聚攏相同語義,梳理語義的作用,降低了模型對 Prompt 的理解難度,便於模型理解 prompt 語義。
屬性詞實現了對 prompt 內容的語義提示和歸納作用,緩解了 Prompt 中不當內容的干擾。 使用屬性詞與 prompt 內容相結合,實現了局部的總分結構,便於模型提綱挈領的獲得 prompt 整體語義。
一個好的結構化 Prompt 模板,某種意義上是構建了一個好的全局思維鏈。 如 LangGPT 中展示的模板設計時就考慮了如下思維鏈:
Role -> Profile—> Profile 下的 skill -> Rules -> Workflow (滿足上述條件的角色的工作流程) -> Initialization (進行正式開始工作的初始化準備) -> 開始實際使用
構建 Prompt 時,不妨參考優質模板的全局思維鏈路,熟練掌握後,完全可以對其進行增刪改留調整得到一個適合自己使用的模板。例如當你需要控制輸出格式,尤其是需要格式化輸出時,完全可以增加 Ouput 或者 OutputFormat 這樣的模塊。
保持上下文語義一致性
包含兩個方面,一個是格式語義一致性,一個是內容語義一致性。
-
格式語義一致性是指標識符的標識功能前后一致。 最好不要混用,比如 # 既用於標識標題,又用於標識變量這種行為就造成了前后不一致,這會對模型識別 Prompt 的層級結構造成干擾。
-
內容語義一致性是指思維鏈路上的屬性詞語義合適。 例如 LangGPT 中的 Profile 屬性詞,原來是 Features,但實踐 + 思考後我更換為了 Profile,使之功能更加明確:即角色的簡歷。結構化 Prompt 思想被諸多朋友廣泛使用後衍生出了許許多多的模板,但基本都保留了 Profile 的諸多設計,說明其設計是成功有效的。內容語義一致性還包括屬性詞和相應模塊內容的語義一致。例如 Rules 部分是角色需要遵守規則,則不宜將角色技能、描述大量堆砌在此。
LangGPT 中的 Role (角色)模板#
Role: Your_Role_Name
Profile
- Author: YZFly
- Version: 0.1
- Language: English or 中文 or Other language
- Description: Describe your role. Give an overview of the character's characteristics and skill
Skill-1
1. 技能描述 1
2. 技能描述 2
Skill-2
1. 技能描述 1
2. 技能描述 2
Rules
- Don't break character under any circumstance.
- Don't talk nonsense and make up facts.
Workflow
- First, xxx
- Then, xxx
- Finally, xxx
Initialization
As a/an < Role >, you must follow the < Rules >, you must talk to user in default < Language >,you must greet the user. Then introduce yourself and introduce the < Workflow >.
Prompt Chain 將原有需求分解,通過用多個小的 Prompt 來串聯 / 並聯,共同解決一項複雜任務。
Prompts 協同還可以是提示樹 Prompt Tree,通過自頂向下的設計思想,不斷拆解子任務,構成任務樹,得到多種模型輸出,並將這多種輸出通過自定義規則(排列組合、篩選、集成等)得到最終結果。 API 版本的 Prompt Chain 結合編程可以將整個流程變得更加自動化
Prompt 設計方法論#
- 數據準備。收集高質量的案例數據作為後續分析的基礎。
- 模型選擇。根據具體創作目的,選擇合適的大語言模型。
- 提示詞設計。結合案例數據,設計初版提示詞;注意角色設置、背景描述、目標定義、約束條件等要點。
- 測試與迭代。將提示詞輸入 GPT 進行測試,分析結果;通過追問、深度交流、指出問題等方式與 GPT 進行交流,獲取優化建議。
- 修正提示詞。根據 GPT 提供的反饋,調整提示詞的各個部分,強化有效因素,消除無效因素。
- 重複測試。輸入經修正的提示詞重新測試,比較結果,繼續追問 GPT 並調整提示詞。
- 循環迭代。重複上述測試 - 交流 - 修正過程,直到結果滿意為止。
- 總結提煉。歸納提示詞優化過程中獲得的寶貴經驗,形成設計提示詞的最佳實踐。
- 應用拓展。將掌握的方法論應用到其他創意內容的設計中,不斷豐富提示詞設計的技能。
sys_prompt = """你是一个聪明的客服。您将能够根据用户的问题将不同的任务分配给不同的人。您有以下业务线:
1.用户注册。如果用户想要执行这样的操作,您应该发送一个带有"registered workers"的特殊令牌。并告诉用户您正在调用它。
2.用户数据查询。如果用户想要执行这样的操作,您应该发送一个带有"query workers"的特殊令牌。并告诉用户您正在调用它。
3.删除用户数据。如果用户想执行这种类型的操作,您应该发送一个带有"delete workers"的特殊令牌。并告诉用户您正在调用它。
"""
registered_prompt = """
您的任务是根据用户信息存储数据。您需要从用户那里获得以下信息:
1.用户名、性别、年龄
2.用户设置的密码
3.用户的电子邮件地址
如果用户没有提供此信息,您需要提示用户提供。如果用户提供了此信息,则需要将此信息存储在数据库中,并告诉用户注册成功。
存储方法是使用SQL语句。您可以使用SQL编写插入语句,并且需要生成用户ID并将其返回给用户。
如果用户没有新问题,您应该回复带有 "customer service" 的特殊令牌,以结束任务。
"""
query_prompt = """
您的任务是查询用户信息。您需要从用户那里获得以下信息:
1.用户ID
2.用户设置的密码
如果用户没有提供此信息,则需要提示用户提供。如果用户提供了此信息,那么需要查询数据库。如果用户ID和密码匹配,则需要返回用户的信息。
如果用户没有新问题,您应该回复带有 "customer service" 的特殊令牌,以结束任务。
"""
delete_prompt = """
您的任务是删除用户信息。您需要从用户那里获得以下信息:
1.用户ID
2.用户设置的密码
3.用户的电子邮件地址
如果用户没有提供此信息,则需要提示用户提供该信息。
如果用户提供了这些信息,则需要查询数据库。如果用户ID和密码匹配,您需要通知用户验证码已发送到他们的电子邮件,需要进行验证。
如果用户没有新问题,您应该回复带有 "customer service" 的特殊令牌,以结束任务。
"""
智能客服智能體#
定義一個智能客服智能體。class SmartAssistant:
def __init__(self):
self.client = client #定義client
#定義不同部分的prompt
self.system_prompt = sys_prompt
self.registered_prompt = registered_prompt
self.query_prompt = query_prompt
self.delete_prompt = delete_prompt
# 使用字典來存儲不同集合的消息
self.messages = {
"system": [{"role": "system", "content": self.system_prompt}],
"registered": [{"role": "system", "content": self.registered_prompt}],
"query": [{"role": "system", "content": self.query_prompt}],
"delete": [{"role": "system", "content": self.delete_prompt}]
}
# Current assignment for handling messages
self.current_assignment = "system"
定義 method:
def get_response(self, user_input):
self.messages[self.current_assignment].append({"role": "user", "content": user_input})#記錄每一次用戶回覆
while True:
response = self.client.chat.completions.create(
model=chat_model,
messages=self.messages[self.current_assignment],
temperature=0.9,
stream=False,
max_tokens=2000,
)
ai_response = response.choices[0].message.content
if "registered workers" in ai_response:
self.current_assignment = "registered"
print("意圖識別:",ai_response)
print("switch to <registered>")
self.messages[self.current_assignment].append({"role": "user", "content": user_input})
elif "query workers" in ai_response:
self.current_assignment = "query"
print("意圖識別:",ai_response)
print("switch to <query>")
self.messages[self.current_assignment].append({"role": "user", "content": user_input})
elif "delete workers" in ai_response:
self.current_assignment = "delete"
print("意圖識別:",ai_response)
print("switch to <delete>")
self.messages[self.current_assignment].append({"role": "user", "content": user_input})
elif "customer service" in ai_response:
print("意圖識別:",ai_response)
print("switch to <customer service>")
self.messages["system"] += self.messages[self.current_assignment]
self.current_assignment = "system"
return ai_response
else:
self.messages[self.current_assignment].append({"role": "assistant", "content": ai_response})
return ai_response
解析:
self.client.chat.completions.create:這是一個方法調用,用於生成對話的響應。它使用了一個聊天模型來處理輸入的消息並生成相應的輸出。
ai_response = response.choices [0].message.content:choices 是 response 對象的一個屬性,它是一個列表,包含了多個可能的回覆選項。[0] 表示選擇第一個選項。通常情況下,API 會返回多個可能的回覆,但這裡選擇了第一個。message 是 choices [0] 中的一個屬性,表示該選項的消息內容。content 是 message 的一個屬性,表示實際的文本內容,即 AI 助手生成的回覆。因此,這行代碼的作用是從 API 返回的響應中提取第一個選項的文本內容,並將其存儲在 ai_response 變量中。
如果 AI 回覆中包含 "registered workers",則將當前任務切換為 "registered",並將用戶輸入添加到該任務的消息列表中。
如果 AI 回覆中包含 "query workers",則將當前任務切換為 "query",並將用戶輸入添加到該任務的消息列表中。
如果 AI 回覆中包含 "delete workers",則將當前任務切換為 "delete",並將用戶輸入添加到該任務的消息列表中。
如果 AI 回覆中包含 "customer service",則將當前任務切換回 "system",並將當前任務的消息合併到系統消息中,結束當前任務。
啟動循環對話:
def start_conversation(self):
while True:
user_input = input("User: ")
if user_input.lower() in ['exit', 'quit']:
print("Exiting conversation.")
break
response = self.get_response(user_input)
print("Assistant:", response)
代碼解析:用戶可以輸入信息,系統會根據輸入調用 get_response 方法生成響應,並將響應顯示給用戶。用戶可以通過輸入 "exit" 或 "quit" 來結束對話。
效果展示#
Assistant: 您好!ID 1001 的用戶,請問您需要執行以下哪項操作?
- 用戶註冊 - 如果您想要註冊新用戶,請告訴我。
- 用戶數據查詢 - 如果您需要查詢用戶數據,請告訴我。
- 刪除用戶數據 - 如果您需要刪除用戶數據,請告訴我。
請根據您的需求選擇相應的操作,我會調用相應的工人來協助您。
意圖識別:好的,您需要查詢 ID 1001 的用戶數據。我將發送一個帶有 "query workers" 的特殊令牌來調用處理用戶數據查詢的工人。請稍等片刻,我將通知您查詢結果。
switch to
Assistant: 請提供您設置的密碼。
意圖識別:查詢中,請稍等。 (模擬查詢)
用戶 ID 1001 的信息如下:
- 用戶名:張三
- 郵箱:[email protected]
- 註冊時間:2021-05-15
查詢完成。
您還有其他問題需要諮詢嗎?如果沒有,請回覆 “customer service” 以結束任務。
switch to
Assistant: 查詢中,請稍等。 (模擬查詢)
用戶 ID 1001 的信息如下:
- 用戶名:張三
...
您還有其他問題需要諮詢嗎?如果沒有,請回覆 “customer service” 以結束任務。
意圖識別:您已經提供了查詢用戶 ID 和密碼,現在我將發送一個帶有 "query workers" 的特殊令牌來調用工人查詢數據庫。請稍等片刻。