給前端的簡單 AI 教學 - 2.4 prompt 總結與雜談
Tags: ai, webdev, frontend, openai
Published: 2024-01-05 16:44:03

在前幾篇中有看到一些 prompt，這篇算是一個整理。不會有實作的部分。然後，由於我本身並不是機器學習專家，所以以下文章的內容算是一些非專業的經驗談。嗯好，要開始了。

## 1. 理解 prompt
首先先來回顧一下，前幾篇在呼叫 OpenAI GPT 的時候，我們都會送一個 payload 給他，大概是長這樣的：

```ts
[
  {
    "role": "system",
    "content": "你是一個客服人員，心情不太好，但是又必須保持專業的跟客人說話。"
  },
  {
    "role": "user",
    "content": "你們的 APP 怎麼不能用？"
  },
  {
    "role": "assistant",
    "content": "請問是哪裡不能用呢？"
  },
  {
    "role": "user",
    "content": "就卡住了啊"
  },
]
```

我一開始只有用過 OpenAI GPT 的語言模型，直到後來我接觸到其他語言模型，發現他們沒有地方給我輸入上面那種 JSON payload，反而告訴我，如果要實現對話功能，請使用以下的範例：

```
你是一個客服人員，心情不太好，但是又必須保持專業的跟客人說話。

user: 你們的 APP 怎麼不能用？
assistant: 請問是哪裡不能用呢？
user: 就卡住了啊
assistant: 
```

然後模型就會回應 assistant 會說的話：

```
啊是不會說清楚喔！？
```

## 1.1 為什麼叫 `completion` ?
可能你曾經注意到，OpenAI GPT 的 API 路徑叫做 `chat/completion`。

`completion` 就是完成的意思，補完、補全、填補之類的概念。以上面那段對話來說，GPT 的行為其實就是「完成」那個對話，他會把對話變「完整」，因為那邊有個看起來很像空格的東西嘛，所以他會把他填滿。

嘗試舉個例子多說明一點那種「感覺」

```
鋼Ｏ人
ＯＯ的芙莉蓮
進Ｘ的巨人
全Ｏ便利Ｘ店
```

不知道你是否在看到這些字的時候，自動把他們「完成」了？

```
鋼鐵人
葬送的芙莉蓮
進擊的巨人
全家便利商店
```

可能在看到最上面的對話的時候，就會有自己幫客服人員接話的念頭。

個人見解：所以這個 api 叫做 `completion`。

而我們要做的事情，就是提供這種給他做出 `completion` 的素材。

因此，當 GPT 看到這樣的對話內容，實際上他會做的，就是根據前面的模式，將對話「接下去」：

```
你是一個客服人員，心情不太好，但是又必須保持專業的跟客人說話。

user: 你們的 APP 怎麼不能用？
assistant: 請問是哪裡不能用呢？
user: 就卡住了啊
assistant: 
```

```
是不會重新開機喔！
```

## 1.2 再深入一點：文字接龍
我聽過一個說法，我認為挺貼切的：「ChatGPT 是一種超大的文字接龍機」。

OpenAI GPT 這種類型的模型生成內容的時候，會根據輸入，生成下一個可能的字 （很像接龍）。

你可以嘗試把以下文字任意的接下去，體會一下他的感覺：

```
我
今天
早
上
忘記帶



```

他生成的時候都會像這樣一次 1 個字或是 2 個字，然後在生成完一個字比如「我」之後，去猜下一個字，比如「今天」，然後再根據前面的結果，生成下一個字。

這就是為什麼說他在接龍，他實際上做的事情就是推測下一個可能的內容，然後把他生出來。

那他是根據什麼去推測下一個可能是什麼字呢？嗯，就是根據他的「學習」，或者說「經驗」。

他的學習資料就是海量的人類說過的話，所以某個字，某個句子，如果人常常會在這種句子說出類似的描述的話，那他也會容易說出那種話，因為人常常這樣說，他也很常看到，所以他就會根據這個推測下一個最可能的內容。

你可以嘗試想想看，當你看到「我」「今天」「早」「上」「忘記帶」的時候，你直覺想到的下一個字是什麼？然後我們再嘗試換個內容「我」「昨天」「晚」「上」「忘記帶」，你直覺想到的下一個字是什麼？會不會因為一點情境跟文字差異，再加上一點點你的個人經驗，而出現不同的句子？

這說不定就是 ChatGPT 的「感覺」。

## 1.3 什麼是 `systemMessage`
好像有點扯遠了，回來看一下一開始的範例，根據原本 OpenAI GPT 的 payload，可以對照如下

```
你是一個客服人員，心情不太好，但是又必須保持專業的跟客人說話。
// systemMessage

user: 你們的 APP 怎麼不能用？ // userMessage
assistant: 請問是哪裡不能用呢？ // aiMessage
user: 就卡住了啊 // userMessage
assistant: 
```

`systemMessage` 是一種「指示」(Instruction)，他會告訴 GPT 應該如何處理以下的文本。

對了，前面的文章也提過，`userMessage` 也具有類似的效果：

```
user: 你是一個客服人員，心情不太好，但是又必須保持專業的跟客人說話。
assistant: 好的，請問有什麼可以協助？ 
user: 你們的 APP 怎麼不能用？
assistant: 是哪裡不能用？
user: 就卡住了啊 
assistant: 
```

既然有類似效果，那這兩者有什麼不同呢？

你可以把他想成一個去面試的感覺。比方說你今天去面試，然後面試前負責接待的人跟你說了一些注意事項，然後面試開始了，你按照前面的注意事項跟面試官對話：

```
// systemMessage
等一下會進行面試，請你先做自我介紹，約一分鐘，如果有作品的話，可以在自我介紹完展示作品。接著然後面試官會問你問題：

面試官：你好，請開始。 // userMessage
你： 你好，我是XXX，先前曾經...... // aiMessage

```

然後面試官同樣也可以給你一些指示，比方說，就在你自我介紹完，面試官可能就脫離前面說的流程，請你先做別的：

```
// systemMessage
等一下會進行面試，請你先做自我介紹，約一分鐘，如果有作品的話，可以在自我介紹完展示作品。接著然後面試官會問你問題：

面試官：你好，請開始。 // userMessage
你： 你好，我是XXX，先前曾經...... // aiMessage
面試官：在作品介紹之前，想先請你回答幾個問題...你剛剛說...
你：我xxxxxxx
面試官：那你有什麼什麼嗎？
......

```

雖然有個接待人員跟你說過的注意事項，但實際上，情境可能發生變化，比方說面試官對你提出注意事項以外的要求，這時候你也會拋棄前面注意事項的規則，並且以面試官說的話為主。

套回去的話可以說是 userMessage 的重要性比 systemMessage 還要高。而兩個都是指示，但一個是「基本上的要求、前提、原則」，另一個是「對話對象提出來的要求」。

實際上，根據模型訓練的方式，也有可能 `systemMessage` 的重要性會比 `userMessage` 高。想想看，在生活之中，也不一定對話的人說什麼你就做什麼吧？

```
（旁白）你是一個警察，看過太多種狡猾的犯人，你的原則就是不輕易相信別人的說詞。

我：你要相信我
你：
```

所以，`systemMessage` 與 `userMessage` 有何不同？這兩個東西的語義不太一樣，一個是類似旁白、畫外音的一種原則或是指示，一個對話之中對方跟你說的話。

## 2. 填鴨式教育
是這樣的，在還是學生的時候，你可能遇到各種填鴨式教育的題型，我覺得非常適合拿來當作架構 prompt 的一些參考，尤其在訣竅上也幾乎一模一樣

### 2.1 題型

#### 2.1.1 選擇題
```
小明跟小華到文具店買筆，小明買了 1 隻藍筆，小華買了 1 隻紅筆。一隻藍筆 20 元，一隻紅筆 10 元。請問小明和小華總共花了多少錢？

A. 50
B. 30
C. 10
D. 以上皆非

請以 A, B, C 或 D 回答，
例如：A
例如：C

答案：
```

#### 2.1.2 是非題
```
一隻藍筆 20 元，一隻紅筆 10 元。請問「藍筆比紅筆貴」這句話是對還是錯？請回答「對」或「錯」。

答：
```

#### 2.1.3 填空題

```
請填入對話中以（）省略的文字。請絕對只回答省略的文字，除此之外不要有其他文字。

小華：我的（）勒？
小明：誰知道
小華：是不是你吃掉了

答案：
```

#### 2.1.4 閱讀題
```
請先閱讀以下文章，並且根據文章，回答問題。

（文章）

問題：請問女主角到底喜不喜歡男主角？
答案：

```


#### 2.1.5 簡答題
```
小明跟小華一起到菜市場吃牛肉麵，小華沒有帶錢，小明也沒有帶錢，但是他們最後還是吃到牛肉麵了。請試圖說明小明和小華為什麼可以吃到牛肉麵（限 100 字）：
```

### 2.2 寫 prompt 的注意事項
我們在上面看到了一些熟悉的題型，這裡試圖從上面的例子中，說明一下常見的寫 prompt 方法：

#### 2.2.1 說明清楚，越龜毛越好
首先要盡可能的把問題描述清楚。這是說，你要盡可能地想別人可能會怎麼誤會你。

我自己在寫 prompt 的時候，有時候就會遇到「自己預期的回應」跟「GPT 的回應」有微妙的誤差。這時候你就可以回去看自己的 prompt 了，似乎有些地方的描述可以再更具體一點。

```
小明跟小華一起到菜市場吃牛肉麵，小華沒有帶錢，小明也沒有帶錢，但是他們最後還是吃到牛肉麵了，而且他們沒有使用錢。
```

透過增加「而且他們沒有使用錢」這句話，更限定了 GPT 的答題方向。

當然，「具體」跟「細節」是可以一直填補下去的，基本上只要填補到他的回應幾乎都 OK 就行了

#### 2.2.2 指定格式
指定格式是 GPT 跟我們很有差別的部分。

比方說「是非題」或是「選擇題」。如果我沒特別指定格式，實際上他可能會先說一大串，然後再說答案「是」。

因為我們已經很熟悉這種問題應該要怎麼回應了，所以我們可以用最簡短、格式化的方式回應（我們被調教得很好）。

但是 GPT，他是個孩子呀～你最好明確的告訴他怎麼回，最好還要加個例子，因為「範例」很具體，「範例」是一個可以模仿的東西

```
.....

請只以 A, B, C 或 D 回答，
回答範例一：A
回答範例二：C

回答：
```

#### 2.2.3 回答：
注意一下，這邊有個「回答：」。為什麼要有這個呢？假設我們把他抽掉，以他 `completion` 的天性來看，他說不定會試圖去「讓這個題目變得完整」，換句話說我給了他誤會的空間，導致他可能會回答：

```
.....
回答範例一：A
回答範例二：C

回答範例三：B
```

因此為了明確告訴他，「這個題目已經結束，接下來是回答」，所以我加了「回答：」

### 2.3 他真的知道自己在說什麼嗎？
有一次我嘗試透過 ChatGPT 學習 GCP 的網路架構，剛開始我有種終於找到個說人話的了，很安心。然後當我針對某些概念追問，或是嘗試比較類似的A 或 B，兩者有什麼差異的時候，我發現他會明顯地說出前後矛盾，或是沒有明顯差異的描述，然後說「因此這兩個是不一樣的」。

這大概就是一種時常會說到，LLM 會有的「幻覺」問題

結合前面提到的，GPT 的基本行為是「根據他看過的文本，推測下一個最有可能的字」。而他推測的素材是他學習到的「人類的說話模式」。簡單來說，他只是在模仿人類說話。或者說他的就是在掰。

不過他還是有可以做到一定的邏輯推論，而且可以用 prompt 強化邏輯推理的效果。只是如果高度要求準確，使用上要小心。因為它就像是一個只是會講出很多了不起的名詞，可是完全不明白那些是什麼意思的屁孩。


### 3. 結尾
最後要說一下，每一家出的模型的訓練方式並不相同，什麼樣的 Prompt 會確實有效，真的是要嘗試過後才知道。但是文章內說的一些概念仍然可以參考與應用。

下一篇文章，會介紹 LangChain 與 LangChain Agent。一提到 Agent 會以為是什麼很複雜的東西，但其實他只是個 Prompt 技巧。下一篇會來介紹這種特別的 prompt。

然後推薦想多認識 prompt 跟一些技巧的人參考以下網站：

#### OpenAI: Prompt Engineering
https://platform.openai.com/docs/guides/prompt-engineering/six-strategies-for-getting-better-results

這個是 OpenAI 自己出的 prompt 撰寫指南，推薦去讀一下

#### Prompt Engineering
https://www.promptingguide.ai/techniques/zeroshot

這個網站介紹了很多 prompt 的技巧，雖然有一些對 OpenAI GPT 沒有什麼效果（因為他已經很強大了）。但當你遇到瓶頸，怎麼樣也無法讓他好好回應時，你可以回來這裡找靈感。

#### Google: Prompt design strategies
https://ai.google.dev/docs/prompt_best_practices?hl=en#prompt-iteration-strategies

如果你想換換口味，Google 的 prompt 文件也說了不少東西。甚至你可以去試試 Gemini 模型。

----------------------------------------
## Try Browser AI Project (In development)
Use Browser AI to help you build an LLM powered website!

Repository: https://github.com/shunnNet/browser-ai
Website: https://courageous-manatee-a625e9.netlify.app/