昨天先整理了一個很重要的觀念:AI 看起來記得前面的內容,不代表 LLM 本身真的擁有長期記憶。很多時候,是應用程式把前面的資訊再次放進這次的輸入裡,所以模型才能接著回答。
平常我們在 ChatGPT 或其他 AI 工具裡輸入一句:
幫我用簡單的方式解釋 present perfect。
按下送出之後,畫面看起來很簡單,就是「我打一段文字,AI 回我一段文字」。但站在 LLM 的角度,事情其實不是單純的「看到一句話 → 理解一句話 → 回答一句話」。
在真正開始寫 Chatbot 之前,我覺得有必要先把一次最基本的 LLM 對話拆開來看,因為之後不管是 Conversation History、Short-term Memory,甚至更後面的 Long-term Memory,本質上都和同一件事情有關:
我要怎麼把資訊整理成模型這次可以使用的內容?
今天就先從三個最重要的概念開始:Token、Message、Context。
假設我們輸入:
I love learning English.
對人類來說,這就是一句完整的英文。但模型不會把整句話當成一個單位直接處理。文字在進入模型之前,會先經過 Tokenization,也就是把文字切成 Token(Token可以先理解成模型處理文字時使用的基本單位)
例如:
I love learning English.
可能會被切成類似:
I
love
learning
English
.
不過這裡只是示意。真正怎麼切,會依不同模型使用的 tokenizer 而有所不同。
有些常見英文單字可能會是一個 Token,有些比較長或比較少見的單字則可能被拆成數個 Token。中文也一樣,並不能直接理解成「一個中文字就是一個 Token」。
所以比起背「一個 Token 等於幾個字」,更重要的是先記住這件事:
LLM 實際處理的是 Token,而不是我們眼睛看到的完整句子。
整個流程可以先簡化成:
文字
↓
Tokenizer
↓
Tokens
↓
LLM
而 Token 為什麼重要?因為後面我們會碰到的 Context Window、輸入長度、輸出長度,甚至 API 使用量,很多時候都是用 Token 來計算。
不過 Context Window 和 Token Limit 會留到後面再深入,今天只要先建立這個概念就夠了。
接下來再看另一個問題。如果我跟 AI 有這樣一段對話:
我:我的英文程度是 B1。
AI:了解。
我:那幫我出五題適合我的題目。
模型不能只拿到一大串文字,還需要知道哪些內容是我說的,哪些內容是它之前回答的。
因此在聊天型 LLM 應用裡,通常會用 Message 來表示每一段對話。最常看到的角色就是:
System
User
Assistant
其中 User Message 就是使用者輸入的內容。例如:
User:
請用簡單的方式解釋 present perfect。
Assistant Message 則是模型產生的回答:
Assistant:
Present perfect is used to talk about...
而 System Message 比較像是在一開始設定模型的行為方式,例如:
System:
你是一位英文學習助理,
請使用適合 B1 學習者的方式回答。
把它們放在一起,就會像:
System:
你是一位英文學習助理。
User:
我的英文程度是 B1。
Assistant:
了解。
User:
請幫我出五題適合我的英文題目。
從聊天介面的角度來看,我看到的是一來一往的訊息;但從系統的角度來看,真正存在的是一連串有角色、有順序的 Message。這也是為什麼後面做到 Conversation History 時,我們不會只是把整段聊天全部黏成一個大字串,而是會保存:
User Message
Assistant Message
User Message
Assistant Message
...
因為每一段話是誰說的,本身就是對話的一部分。
這三個角色可以先簡單整理成:
| Role | 主要作用 | 例子 |
|---|---|---|
| System | 設定模型的整體行為與規則 | 你是一位英文老師 |
| User | 使用者目前提出的需求 | 幫我解釋現在完成式 |
| Assistant | 模型先前產生的回答 | 現在完成式常用來…… |
例如我現在要做的是一個 Personal Learning Agent,那最前面的 System Message 可能會寫:
System:
你是一位個人化英文學習助理。
回答時請考慮使用者程度,
並優先使用簡單、清楚的例子。
接著 User 說:
User:
Can you explain "have been"?
模型回答:
Assistant:
"Have been" can be used when...
接著 User 再問:
User:
Can you give me another example?
這時候「another example」到底指的是什麼,就需要前面的對話才能理解。
如果只把最後一句:
Can you give me another example?
單獨丟給模型,它並不知道我要它再舉什麼例子。
所以 Message 不只是把畫面變成聊天形式而已,它同時也保存了對話之間的關係。
這裡就可以接回昨天講的 Stateless。假設第一輪我說:
User:
My English level is B1.
AI 回:
Assistant:
Got it.
接著我再說:
User:
Please give me a suitable reading exercise.
如果希望模型知道「suitable」是指適合 B1,那第二次呼叫模型時,就不能只送最後一句話。更完整的內容會像:
System:
You are an English learning assistant.
User:
My English level is B1.
Assistant:
Got it.
User:
Please give me a suitable reading exercise.
也就是說,從使用者的角度,我只是多打了一句話;但從應用程式的角度,它可能需要把前面的 Message 一起整理,再交給 LLM。
可以想成:
第一輪
User Message
↓
LLM
↓
Assistant Message
第二輪
Previous Messages
+
Current User Message
↓
LLM
↓
New Assistant Message
這也再次說明,AI 能接著聊天,不是因為模型「自動保留了整個聊天室」,而是應用程式把需要的 Message 帶進了下一次請求。
現在就可以把 Day 1 提到的 Context 講得更完整一點。
Context 可以理解成:
模型這一次產生回答時,可以使用的全部資訊。
最簡單的 Chatbot,Context 可能只包含:
System Message
+
Previous Messages
+
Current User Message
可以畫成:
┌─────────────────────────────┐
│ Context │
│ │
│ System Message │
│ │
│ Previous User Message │
│ Previous Assistant Message │
│ │
│ Current User Message │
└──────────────┬──────────────┘
↓
LLM
↓
Response
所以 Context 不是某一則 Message,而是這次模型可以使用的整體資訊。
而這件事之後會越來越重要。例如現在的 Chatbot,Context 可能只有:
System
+
Conversation
後面做到 Long-term Memory 時,就可能變成:
System
+
Conversation
+
Relevant Memories
+
Current User Message
再往後走到 Agent,甚至還可能加入:
Tool Results
User Profile
Retrieved Knowledge
Task State
所以整個 30 天雖然會一直加入新的技術,但其中一個核心問題其實一直沒有改變:
這一次到底要讓模型看到哪些資訊?
這也是為什麼 Context 在 AI Application 裡非常重要。
前面都在講輸入,那模型拿到 Context 之後,回答又是怎麼產生的?
這裡可以先抓住一個最重要的概念:
LLM 是逐步產生下一個 Token。
例如輸入:
The capital of France is
模型會根據目前的 Context,計算下一個 Token 的可能性。可能會像:
Paris 很高
London 很低
Tokyo 更低
...
接著模型產生:
Paris
這個新產生的 Token 又會成為接下來生成的一部分,模型再繼續往下預測。
可以簡化成:
Context
↓
預測下一個 Token
↓
加入生成結果
↓
再預測下一個 Token
↓
繼續生成
↓
完成 Response
例如:
The capital of France is
↓
Paris
↓
The capital of France is Paris
↓
.
實際模型內部的運算當然複雜很多,但從 AI 應用開發的角度,先掌握「根據 Context 逐步產生 Token(類似文字接龍)」這件事就夠了。這也代表 Context 裡放了什麼,會直接影響後面生成的內容。
到這裡,可以把今天的流程全部放在一起。假設我輸入:
請用簡單的方式解釋 present perfect。
從應用程式到模型,大致可以理解成:
使用者輸入文字
↓
建立 User Message
↓
加上 System Message
↓
加上 Previous Messages
↓
形成 Context
↓
Tokenizer
↓
Tokens
↓
LLM
↓
逐步產生 Tokens
↓
組成 Assistant Message
↓
顯示給使用者
如果再縮短一點,就是:
Text
↓
Message
↓
Context
↓
Token
↓
LLM
↓
Response
平常使用聊天工具時,這整個過程都被藏在介面後面,所以我們只會看到:
我問一句
↓
AI 回一句
但一旦要自己做 AI Application,這些事情就會開始變成我們要處理的部分。
尤其從明天開始,Message 怎麼建立、Request 怎麼送出去、Response 怎麼拿回來,都會出現在程式碼裡。
雖然今天主要在講 LLM 的基本對話流程,但其實這些概念跟後面的 Memory 直接相關。假設未來 Memora 已經記住:
英文程度:B1
偏好:短篇文章
每天學習時間:15 分鐘
就算這些資訊全部存在 Database 裡,也不代表 LLM 自動知道。真正要使用這些 Memory 時,還是需要經過:
Memory Store
↓
找到這次真正相關的 Memory
↓
整理成模型能使用的內容
↓
放進 Context
↓
交給 LLM
也就是說:
Memory 最後還是要回到 Context,模型才能真正使用它。
這也是為什麼我在進入 Conversation History 和 Long-term Memory 之前,會先把 Message 和 Context 講清楚。如果不知道「資訊最後怎麼進到模型」,後面就很容易只會呼叫:
retrieve_memory()
卻不知道取回來的資訊真正應該放在哪裡、又為什麼能影響模型的回答。
今天先把一次 LLM 對話濃縮成三個核心概念:
Token、Message、Context。
Token 是模型實際處理文字時使用的基本單位;Message 用來區分 System、User、Assistant 各自說了什麼;Context 則是模型這一次產生回答時,可以使用的整體資訊。最後再由 LLM 根據 Context,逐步產生新的 Token,形成我們看到的 Response。
LLM 並不是在「看一個聊天室」,而是在處理這一次被交給它的 Context。
理解這件事情之後,下一步就來把這整套流程寫成程式吧!
前兩天都還在建立觀念,明天就要正式開始動手。
下一篇我們來用 Python 串接 LLM API,第一次實際處理 API Key、Environment Variable、Request、Response,以及今天提到的 Message。
不過我們先做最簡單的版本開始。先來做可以回答問題但不會保存 Conversation History 的就好。下一篇來正式做出這 30 天的第一個Stateless Chatbot吧~~