Learning Objectives
- 用 tiktoken 查看文字切成哪些 token,說明中文、英文、數字、emoji 的切法差異
- 對照 tiktoken 的估算與 API 回報的 usage
- 說明上下文長度包含什麼,以及超過時的處理方式
- 依輸入、輸出 token 與呼叫次數估算費用
模型處理的單位是 token
模型不是一個字一個字讀文字。文字送進模型之前,會先被切成 token,每個 token 對應一個整數編號,模型處理的是這串編號。
自己寫 AI 應用時,有三件事都用 token 計算:費用依 token 數計價;一次請求能放多少內容,上限也是 token 數;回答被截斷,同樣是因為 token 用完了。所以你要能回答:這段文字大約幾個 token?這個功能一個月大概要花多少?
這一課用 OpenAI 的 tiktoken 函式庫實際切給你看,編碼使用 o200k_base。範例一樣是預錄的,tiktoken 的結果在這裡只查得到課文用到的文字,因為頁面上沒有放完整的詞表;在自己的電腦 pip install tiktoken 就可以切任何文字。
逐步看:文字怎麼切成 token
同一個函數切八段文字。每段請注意兩件事:切成幾個,以及切的位置是不是你以為的「一個詞」。
Token 是位元組片段,不是字也不是詞
tiktoken 使用的方法叫 BPE(byte pair encoding)。它處理的對象是 UTF-8 位元組,把常見的位元組組合併成一個 token,這個過程可逆,token 接回去就是原本的文字。所以:
- 一個 token 不一定是一個字,也不一定是一個詞(
今、天天、氣)。 - 一個字可能要好幾個 token(
鬱),單一 token 也可能不是完整的字元。 - 標點常和旁邊的字併在一起,例如
,我。
字數不能直接換算成 token 數。 下面是同樣意思的繁中和英文,數字都是實際切出來的:
| 文字 | 繁中 token(位元組) | 英文 token(位元組) |
|---|---|---|
| 一般句子 | 13(48) | 14(58) |
| 專有名詞 | 7(30) | 6(50) |
| 不常見的字 | 13(27) | 8(47) |
中文看起來字少,token 不一定比較少。tiktoken 的說明文件提到平均每個 token 約 4 個位元組,這只是概略值;要知道確切數字,直接用 tiktoken 算最省事。
逐步看:tiktoken 的估算與真實 usage
程式送出一次請求(已預錄),再用 tiktoken 數一次,對照 API 回報的數字。
事前估算與事後對帳
這次的輸入多 10 個、輸出多 3 個。我們比對這一軌其他課錄到的回答,正常結束的輸出也都剛好比 tiktoken 多 3 個;官方文件沒有說明這 3 個的內容,所以這裡只當作觀察到的現象,不把它寫成規則。
實務上兩者各有用途:
- 呼叫之前: 用 tiktoken 估算,決定要不要裁剪內容、要不要拒絕太長的輸入。不用呼叫 API,也不花錢。
- 呼叫之後: 記錄回應裡的
usage,這才是實際用量,統計費用時用它。
模型不同,切法可能不同;換模型時要查文件,並用 usage 對照一次。
上下文長度:一次請求的總額度
上下文長度(context window)是一次請求能使用的 token 上限,輸入與輸出都算在內,有些模型的推理 token 也算。你可以想成同一個額度:輸入放得越多,留給輸出的越少。各模型的上限不同,要查模型的文件,而且會隨版本改變。
輸入太長時,常見的處理方式有這些:
| 方式 | 做法 | 代價 | 這一軌在哪裡 |
|---|---|---|---|
| 裁剪 | 只留最近的對話或文件開頭 | 較早的內容會遺失 | 第 7 課,本課實作題 |
| 摘要 | 先把舊內容摘要成短文字 | 多一次呼叫,細節可能遺漏 | 第 7 課 |
| 只送相關段落 | 先檢索,再把相關的幾段送給模型 | 要先建立檢索,可能找錯 | 第 10~13 課 |
| 切塊分批處理 | 切成幾段各自處理,再合併結果 | 跨段落的關聯會斷掉 | 第 12 課 |
另一個上限是 max_completion_tokens:你自己設定的輸出上限。下面看它實際的效果。
逐步看:輸出被上限截斷
同一個問題,輸出上限分別設成 10 和 200。請看結束原因,還有回答停在哪裡。
逐步看:估算一個功能的費用
上一個範例用 10 或 200 都是講到一半就斷掉:上限只負責切斷,模型不會因此把內容寫得比較短。要短回答時,同時在提示詞裡要求長度(第 5 課),並檢查 finish_reason;看到 length 就不要把結果當成完整的(第 9 課)。
接著看費用。情境是把課程回饋分類,單價是寫在程式裡的示意值,不是任何模型的實際價格;看的重點是計算方式,以及哪個數字影響最大。
費用怎麼估
一次費用 =(輸入 token × 輸入單價 + 輸出 token × 輸出單價)÷ 1,000,000
總費用 = 一次費用 × 呼叫次數
價格與模型清單會變動,寫報告或做預算時請到 OpenAI 官方定價頁查當時的數字;頁面上也列出快取輸入、批次處理等分開計價的項目。
估算時最容易漏掉的是:
- 對話要重送歷史: 第 7 課會看到,每一輪的輸入都包含之前的所有內容。
- 系統提示詞與參考資料: 每次呼叫都算一次。第 13 課的 RAG 會把檢索到的段落放進輸入。
- 重試與失敗: 第 8 課會處理。
上線後請依 usage 記錄實際用量,和估算對照。
實作:把對話裁剪到預算之內
完成 fit_budget(history, budget):回傳新的串列,第一則 system 一定保留;其他訊息從最新的往回加,再加一則就會超過 budget 時停止,順序要維持原本的先後。
budget 是內容的 token 總數,包含 system 那一則。如果連 system 都已經超過預算,就只留 system。
這題只用 tiktoken,文字已預先算好,所以請不要修改 history 裡的內容,否則會查不到 token。
小測驗:token、上下文與費用
「今天天氣很好」切出來是 今、天天、氣、很好。這說明什麼?
要在呼叫 API 之前判斷輸入會不會太長,應該怎麼做?
這段程式會印出什麼?(單價是示意值)
上下文長度的額度包含什麼?
max_completion_tokens 設成 200 後,回答的 finish_reason 是 length。下面哪個說法正確?
重點整理
- 文字會先切成 token;token 是位元組片段,不等於字或詞,常見片段併在一起,不常見的字可能被拆開。
- 字數不能換算成 token 數,要用 tiktoken 算;實際用量以回應的
usage為準。 - 上下文長度同時包含輸入與輸出;太長時可以裁剪、摘要、只送相關段落或切塊處理。
max_completion_tokens只會切斷,不會讓回答變短;要檢查finish_reason。- 費用=(輸入×單價+輸出×單價)÷ 100 萬 × 次數,輸入與輸出分開計價;價格以官方定價頁為準。
下一課: 切好的 token 進到模型之後,模型怎麼決定每個位置該參考句子裡的哪些部分?下一課用 numpy 手算一次注意力機制。