Prompt Engineering vs. Blind Prompting

Mitchell Hashimoto

提示工程 vs. 盲目提示

「Prompt Engineering(提示工程)」是隨著語言模型的發展而出現的詞彙,用來描述有效運用提示從語言模型中提取資訊的過程,通常是為了在實際應用中使用。

許多自稱正在進行 Prompt Engineering 的人,實際上只是在進行 Blind Prompting1。我用「Blind Prompting(盲目提示)」一詞來描述一種以粗糙的嘗試錯誤法來製作提示、幾乎沒有或完全沒有經過測試,且對提示只有非常淺層理解的方法。Blind Prompting 不是 Prompt Engineering。

也有許多人懷疑,Prompt Engineering 是否真的能被稱為「工程」,抑或只是追逐炒作的人所鼓吹的「巫術」。我認為在大多數情況下,這種懷疑源於我所看到的許多自稱與 Prompt Engineering 相關的推文和部落格文章,實際上充其量只是比 Blind Prompting 稍微好一點的層次。

在這篇部落格文章中,我將論證 Prompt Engineering 是一項可以透過真實的實驗方法來培養的真實技能。我將以一個貼近現實的範例,逐步說明如何透過 Prompt Engineering 為某個問題打造出能為應用程式帶來實用價值的解決方案。

整篇文章將偏重於以文字輸出為前提,因為文字輸出是我使用語言模型時最主要的使用情境。某些技術——例如測試技術——並無法一對一地套用到其他類型的輸出,例如圖像。不過,本文中的所有內容對於多模態輸入則都能良好適用。


什麼是提示?

如果你已經非常熟悉「prompting」或了解什麼是「prompt」,可以跳過本節。

對於語言模型(如果你不熟悉這個詞,可以想成 ChatGPT),「prompt」就是使用者提供給模型的輸入。在 ChatGPT 中,可以有效地將其理解為你輸入文字的文字框。接著語言模型會對你的 prompt「推論」出一個「completion」(補完結果)。舉例來說,如果我在 ChatGPT 中輸入「4 + 3 = 」,它(大概)會回覆「7」。在這個例子中,「4 + 3 = 」就是 prompt,而「7」就是 completion。

「提示」是指利用 prompt 作為從模型中提取所需資訊的手段。這是一種吸引人的資訊提取方式,因為你不需要龐大的離線訓練資料集,不需要離線存取模型,而且即使對非工程師而言也感覺直觀。提示只是調整模型的一種方法而已。

最後,「Prompt Engineering」所描述的是一門更嚴謹的學科(如本文稍後將展示的),其目標是將提示作為一種手段,為實際應用建構可靠的功能。它與 ChatGPT 式的提示不同,因為透過 Prompt Engineering 產生的 prompt,通常是為了在大量且多樣的情境中重複使用,以便為應用程式可靠地解決特定問題。


問題背景

首先,你必須有一個想要建構解決方案的問題。這個問題可以用來評估提示是否為最佳解決方案,或是否存在其他更適合的替代方案。工程的起點不是為了使用某種方法而使用該方法,而是基於相信它是正確的方法而驅動。

在這個範例中,我們假設自己是一家打造行事曆用戶端的公司。我們希望使用者能夠使用自然語言來輸入活動。例如:

Dinner with Alice next Tuesday at Taco Bell

CorpConf on 11/4

1:1 with Bob tomorrow at 10 AM

語言模型可能是將這種自然語言輸入轉換為結構化輸出、以描述可在應用程式中使用的活動的良好解決方案。

顯然還有其他可能的解決方案。我們可以利用一組regular expression和字串搜尋來尋找常見片語(on <Day of Week>tomorrowtodaynext week等)。

語言模型也有其自身的優勢:它們或許能提供一種能更好地處理其他語言、或更能處理錯字或文法錯誤的方法,或至少在 regular expression 失效時作為一個良好的備援。無論如何,仍有足夠的潛力值得繼續將提示作為潛在解決方案來探索。


示範集

接下來,我們必須整理一個示範集。示範集包含預期的輸入以及預期的輸出。此集合將達成多個目標:

  1. 它將用於衡量我們 prompt 的準確率。透過使用單一示範的輸入,我們可以斷言是否收到預期的輸出。

  2. 它明確規範了我們期望的 prompt 輸入與輸出樣貌,讓我們作為工程師能夠判斷其是否符合我們問題所需的形態。

  3. 如果我們選擇使用 few-shot prompt,可以將此示範集的子集作為 few-shot 方法的範例。對於不熟悉「few-shot」一詞的人來說,few-shot 是一種除了 prompt 之外還會提供範例的提示風格。請參閱此處關於 Few-Shot vs Zero-Shot prompting 的良好總覽

上述第 (2) 點極為重要。我們需要對預期的輸入與預期的輸出有大致的理解,因為在這兩端的[通常]是需要確保資料符合特定格式、並期望以特定格式回傳的軟體。這與典型的軟體工程並無不同,在典型的軟體工程中,我們會將問題拆解為具有某種輸入/輸出期望的一組函式。

我們可以利用先前的範例並將其擴展為完整的示範:

Q: Dinner with Alice next Tuesday at Taco Bell
A: next Tuesday

Q: CorpConf on 11/4
A: 11/4

Q: 1:1 with Bob tomorrow at 10 AM
A: tomorrow

關於示範集大小的說明:在這篇部落格文章中,我們只有三個示範。實際上,你可能至少會想要十幾個。示範越多,你能做的測試就越好,但同時也會因為 token 使用量而變得越昂貴。在達到一定規模時,微調語言模型通常會更經濟實惠。

上述示範中有兩個重要的決策。對於任何提示問題,你都必須做出類似的決策。

第一,我們只擷取一項資訊。你可能會想嘗試讓模型擷取整個活動,例如活動名稱、參與者、時間、地點等,並以某種漂亮、可直接使用的 JSON 或其他格式輸出。模型或許能夠做到這一點。但在處理新問題時,我建議先將其拆解為單一問題。這會讓問題更容易處理,最終也能為你提供一個基準準確率,可用來衡量多重輸出的方法是否真的值得。

第二,我們的輸出並未經過轉換。我們並非試圖將所有內容轉換為日期,或確保所有內容的大小寫都正確等等。我們所做的是字面上的文字擷取。市面上已有非常出色的確定性函式庫,能以極高的準確率將像「next Tuesday」這樣的字串轉換為時間戳記。這不是我們需要語言模型幫我們做的事。因此,我們只需取得粗略的日期格式(「next Tuesday」、「11/4」、「tomorrow」),並使用傳統的程式設計方法來解決將其轉換為時間戳記的問題,因為這是一個微不足道的問題。輸出越簡單,就越容易獲得更高的準確率。

最後,關於輸出解碼的簡要說明:LLM 會以各種方式補完你的 prompt:它可能是一個完整的句子、可能加上句號、可能被大寫等等。在驗證示範集之前,你應該決定你希望 LLM 的輸出要有多完美,以及你願意在多大程度上進行正規化。

舉例來說,如果我正在進行文字擷取,我通常認為修剪空白與句號並將整個輸出轉為小寫是合理的。如果我在做更進階的事情,例如產生 JSON,我可能會以某種確定性的順序與風格來解析並重新編碼 JSON,使比較結果具有確定性。依此類推。

我的建議是:讓來自 LLM 的輸出盡可能保持簡單與彈性,並在你的應用程式中執行一些正規化操作。一開始不要試圖強迫 LLM 輸出完全完美的格式。過早在 LLM 中進行過多的「輸出塑形」會讓人難以區分 LLM 執行核心任務(在此案例中為資訊擷取)的能力與其建構輸出結構的能力。


候選提示

接下來我們要提出一些候選提示。候選提示是指我們認為可能引發語言模型產生我們想要的預期行為的 prompt。我們會提出多個候選,因為我們不太可能一開始就選到最好的 prompt。

為了作為入門程度的文章,我們將手動設計 prompt。為了有效,prompt engineers 在建構 prompt 時應該運用一些基本知識。例如,肯定式的語氣比防禦式的語氣更好。清晰簡潔通常比重複冗長更好。在建構 few-shot prompt 時,標籤的平均分布很重要,展示完整的標籤集合也很重要,等等。在選擇範例時,LLM 可能答錯的範例通常表現最好,研究顯示範例按由短到長的順序排列時通常表現最佳,等等。

需要引用資料!很抱歉,我沒有為這些建議引用實驗研究來佐證。老實說,我只是懶得去查找我讀過的相關論文(通常每個觀點都有多篇)。如果你選擇不相信我,沒關係,更重要的重點是,關於提示技術及其有效性的實驗研究確實存在。但是,我保證這些不是我憑空捏造的,儘管其中一些在現代模型上可能已經過時。

要詳細介紹這些技術中的每一項,就足以另寫一篇專文,這並非本文的目標。本文的目標是展示高層次的端到端流程,並說明從 LLM 中提取價值確實存在一種工程方法

在這個階段,目標是提出一些不錯的 zero-shot prompt。zero-shot prompt 可以轉換為 few-shot,而 few-shot 又可以進一步轉換為 chain of thought。而這些又都可以進一步轉變為 batched prompt 等等。因此,由於 zero-shot 是必須嘗試的基礎要求,我們將聚焦於此。

以下是我想出的三個候選提示:

Identify the date or day mentioned in the given text and provide it as the output.

Identify the date or day mentioned in the given event description.

Determine the date or day from each input and provide the output accordingly as a single word or date.

這些都是合理的 prompt。以任何受過教育的普通人來說,每個 prompt 很可能都會產生非常高的準確率。但語言模型並非人類,因此我們不能自動預期會有同等的表現。我之前已經展示過非常合理的 prompt 也可能表現得一塌糊塗。因此,我們的下一步是透過進行一些測試與量測來為我們的決策提供依據。


提示測試

有了一組候選提示以及示範集,我們現在可以衡量準確率了。我目前發現最好的方法是使用像 LangChain 這樣的函式庫來建立一個簡單的 Python 腳本。在我的測試中,我通常會遍歷每一個示範並執行以下 prompt 範本:

{{prompt}}. Q: {{input}} A:

我總是先測試 zero-shot。我想要取得一個基準準確率指標。從那裡,你可以接著測試 few-shot,並比較不同候選提示以及不同提示類型之間的差異。依此類推。

這必須針對每個模型分別進行。即使是在更強大的模型上,相同的 prompt 也不保證會有相同或更好的準確率;準確率可能會下降2

提示測試最基本的結果應該是如下表所示的表格。你也可能有其他維度,例如模型(例如 GPT-3.5 與 GPT-4)。

           | Zero-Shot | Few-Shot | ... |
-----------------------------------------
Prompt 1   | 64        | 68       | ... |
-----------------------------------------
Prompt ... | 44        | 52       | ... |
-----------------------------------------
Prompt N   | 23        | 22       | ... |

此表格在 Y 軸上顯示候選提示,在 X 軸上顯示使用這些提示的提示類型。數值為正確答案所占的準確率百分比。延續行事曆應用程式的範例,以 zero-shot prompt 形式呈現的「prompt 1」搭配一個示範,可能會如下所示:

Identify the date or day mentioned in the given text and provide it as the output. Q: CorpConf on 11/4 A:

而我們預期「11/4」作為答案。few-shot 版本可能如下所示:

Identify the date or day mentioned in the given text and provide it as the output.

Q: Dinner with Alice next Tuesday at Taco Bell. A: next Tuesday

Q: 1:1 with Bob tomorrow at 10 AM. A: tomorrow

Q: CorpConf on 11/4. A:

給有經驗的提示者的說明:few-shot 範例中並沒有寫類似「請模仿下列範例」這樣的話。實驗研究顯示這樣做並不能可靠地提高準確率,因此我喜歡先在不加這句話的情況下進行測試,以節省 token。第二,few-shot 範例中的範例從未展示「MM/DD」這種擷取形式,這是不好的做法。在真正的 few-shot 情境中,展示所有擷取樣式可能很重要(Zhao(趙)等人 2021)。

對於某些類型的問題,例如分類問題,你可以使用confusion matrix(Strobelt(史卓貝爾特)等人 2022)來視覺化其他標籤的機率,並利用它來判斷你的標籤集合是否可能被調整得更好。

除了準確率之外,你還會想要衡量所使用的 token 數量、請求次數等。所有這些在選擇最終 prompt 時都必須納入考量。


選擇提示

最後,你會選擇其中一個候選提示整合到你的應用程式中。這不一定是準確率最高的 prompt。這是一項基於所使用模型、所需 token 數量與所呈現準確率的成本與準確率分析。

舉例來說,你可能會發現 few-shot 變體表現最好,但在測試集上僅高出 4% 的準確率,卻需要多 200% 的 token(對於目前以 API 驅動的模型而言,實際上等於成本翻倍)。你可能會為你的業務判斷,以一半的成本換取 4% 的準確率下降是值得的。

或者,你可能會決定回頭測試其他提高準確率的方法。例如,你可能會在成本較低的模型上嘗試self-consistency decoding strategy(Wang(王)等人 2022),看看是否能充分提升準確率。有時,在較便宜的模型上使用更多 token,反而會比在較昂貴的模型上使用較少 token 節省可觀的費用。例如,GPT-4 目前的價格約為 GPT-3.5 的 15 倍。這意味著你實際上擁有 15 倍的 token 預算來提升 GPT-3.5 prompt 的準確率(需注意速率限制等附帶條件)。

以本文的範例來說,我們可能會選擇表格中 Prompt 1 的 zero-shot 版本,因為它擁有 64% 的準確率,且可能使用明顯較少的 token。也許我們認為 64% 的準確率已足以至少為行事曆應用程式填入活動範本。對於這個特定問題,我認為我們可以做得比 64% 好得多,但這只是我在本文中使用的數字。

最重要的是,你擁有數據可以做出明智的決策。


信任但驗證與持續改進

由於生成式 AI 具有機率性本質,你的 prompt 很可能存在一些問題。即使你在測試集上的準確率達到 100%,也可能仍有未知的輸入會產生錯誤的輸出。因此,你應該信任但驗證,並將驗證失敗的案例加入你的示範集,以便開發新的 prompt 並提高準確率。

驗證方式高度取決於問題。以我們的行事曆應用程式為例,我們可能會想明確地詢問使用者:「這個活動正確嗎?」如果他們回答「否」,就將該自然語言輸入記錄下來以供人工審核。或者,我們或許可以做得更好,自動追蹤使用者在我們自動擷取資訊後手動修改的任何活動3

作為另一個範例,如果我們的 prompt 正在產生程式碼(例如 regular expression 或程式語言文字),我們至少可以嘗試對其進行解析。解析不應成為安全疑慮4,而且它至少提供了最基本的驗證,確保至少語法是正確的。同樣地,如果此驗證失敗,我們可以記錄輸入與輸出,擴充我們的示範集,並開發更好的 prompt。

驗證也有助於防範adversarial prompting。Adversarial prompting 本身就是一個完整的議題,我不會在本文中涵蓋。


接下來……

這篇部落格文章展示了如何開發 prompt——依我之見——可以是一種工程實踐。它描述了一種有系統的方法,用於辨識問題、形成解決方案、驗證這些解決方案,並應用持續改進來完善這些解決方案。

將本文中的方法與「Blind Prompting」相比,後者依賴軼事經驗與無所不在的嘗試錯誤來得出某種提議的解決方案,而且通常不會建立適當的有系統基礎架構,以便隨著時間可靠地迭代 prompt。

我想指出,這篇部落格文章非常基礎。本文中有多處可以透過已經廣為人知的更進階技術來改進。此外,我並未涵蓋諸如 adversarial prompting 等重要主題。舉一個具體例子,有更科學的方法來為 few-shot prompt 挑選最佳範例,但我希望盡可能讓本文保持基礎。如果你想學習一些更進階的技術,Prompt Engineering by Lilian Weng(莉莉安·翁)提供了絕佳的總覽。

此外,每個人都在迅速邁向更高階的 LLM 整合:prompt chaining、agents 等。有些人認為,像這樣的未來創新以及更多發展將使人類的提示變得過時。無論這是否屬實,我個人相信從「第一性原理」5來學習事物是有價值的,而且我認為學習像這樣的提示技術只會提升我運用更高階語言模型技術的能力。我也相信,像這樣基礎的提示仍能讓更高階的概念有更好的表現。

註腳

  1. 可能是許多「吵雜」的人。我已經遇見並向許多真正將良好工程實踐應用於此領域的優秀 prompt engineers 學習。遺憾的是,我在 Twitter 及其他平台上看到的許多噪音往往並非如此。

  2. 又需要引用資料。同樣地,我只是在偷懶,但這是基於我在多篇論文中讀到的實驗研究。你可以選擇不相信我,直接自己測試看看。

  3. 這裡顯然有隱私方面的考量。我只是分享一個範例,該技術是否合適取決於實際情況。

  4. YAML。😐

  5. 我了解真正的「第一性原理」會是更底層的層次。我在此處使用「第一性原理」一詞是作為常見的慣用語,用來指稱某個任意的低層次起點,在其之上可以建構更多知識。

原文由 Mitchell Hashimoto 發布

本文章由 muse-spark-1.2-contributor 進行翻譯