﻿# 如何使用 SharpToken 計算不同 OpenAI 模型在送出提示詞時的 Token 數量

由於大多數 LLM 模型都有 Token 數量的限制，因此我們在開發 Generative AI (GAI) 應用程式時，都會對 Token 的用量斤斤計較，因此我們需要一個可以計算 Token 數量的函式庫，而在 .NET 世界裡 [SharpToken](https://github.com/dmitry-brazhenko/SharpToken) 套件不但可以對文字進行編碼(Tokenize)與解碼，還可以用來計算文字的 Token 數量。今天這篇文章我就來介紹這個好用的 NuGet 套件。

![generative-ai](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/729f36176b823ae26d39-ed072b2a-5eaa-4ade-9a06-63c3084d6dcc.webp)

### 瞭解基本背景知識

我一開始看到 [SharpToken](https://github.com/dmitry-brazhenko/SharpToken) 的時候，其實不太理解他的用法，因為透過 [SharpToken](https://github.com/dmitry-brazhenko/SharpToken) 計算出來的 Token 數量，好像跟 OpenAI 的 [Tokenizer](https://platform.openai.com/tokenizer) 計算結果不太一樣，這讓我十分困惑。

我們先來說說目前 GPT 相關的 API 大致可區分兩大類：

1.  **Chat completions API**
    
    Chat completions API 是一種透過一系列的往返訊息對話當作輸入，並由模型產生回應訊息的一種 AI 完成模式，是新一代的對話式 AI 模型。
    
    常見的模型包括：
    
    -   GPT-4
        -   `gpt-4` (上限 8,192 tokens)
        -   `gpt-4-32k` (上限 32,768 tokens)
    -   GPT-3.5
        -   `gpt-3.5-turbo` (上限 4,097 tokens)
        -   `gpt-3.5-turbo-16k` (上限 16,385 tokens)
2.  **Completions API** (Legacy)
    
    Completions API 的最後更新時間為 2023 年的 7 月，算是早期的 AI 模型，與 Chat completions API 有著截然不同的呼叫方式，這種呼叫方式並非提供一系列的對話，而是透過自由輸入的文字(freeform text)來進行提問(Prompt)，市面上許多 Prompt engineering (提示工程) 的技巧，大多是針對 Completions API 為主。不過大多 Prompt engineering (提示工程) 的技巧，通常也適用在 Chat completions API 模式上。
    
    常見的模型包括：
    
    -   GPT-3.5
        -   `text-davinci-003` (上限 4,097 tokens)
        -   `text-davinci-002` (上限 4,097 tokens)

> 關於 OpenAI 的完整模型清單可以參見 [OpenAI API Models](https://platform.openai.com/docs/models)

### 安裝 SharpToken 套件 (NuGet)

-   透過 NuGet package manager 安裝：
    
    ```powershell
    Install-Package SharpToken
    ```
    
-   透過 .NET CLI 安裝：
    
    ```bash
    dotnet add package SharpToken
    ```
    

> 套件網址 ▶ [https://www.nuget.org/packages/SharpToken](https://www.nuget.org/packages/SharpToken)

### 使用方式

1.  先引用命名空間
    
    ```csharp
    using SharpToken;
    ```
    
2.  取得 Tokenizer 編碼器
    
    ```csharp
    // 透過編碼名稱取得 GptEncoding 編碼器物件
    var encoding = GptEncoding.GetEncoding("cl100k_base");
    
    // 透過模型名稱取得 GptEncoding 編碼器物件
    var encoding = GptEncoding.GetEncodingForModel("gpt-4");
    ```
    
3.  對文字進行編碼，將字串轉成 Token 型態 (`List<int>`)
    
    ```csharp
    var encoded = encoding.Encode("Hello, world!"); // Output: [9906, 11, 1917, 0]
    ```
    
    > 中文也都可以正常支援！
    
4.  對 Token 進行解碼，將 Token 轉回字串
    
    ```csharp
    var decoded = encoding.Decode(encoded); // Output: "Hello, world!"
    ```
    

用法非常簡單對吧，就單純的「編碼」與「解碼」而已，使用方式就跟 Base64 一樣簡單！

### 解決疑惑之處

目前 `SharpToken` 支援以下編碼器：

-   `r50k_base`
-   `p50k_base`
-   `p50k_edit`
-   `cl100k_base`

老實說，[SharpToken](https://github.com/dmitry-brazhenko/SharpToken) 官網並沒有特別說明這幾個編碼要在何時使用，範例也只有寫到 `gpt-4`, `gpt-3.5-turbo` 而已，且這兩個模型剛好都是使用 `Chat Completions API`，支援的 Encoding 為 `cl100k_base` 編碼器！

所以我一開始拿 `cl100k_base` 編碼器來對任意文字作編碼，轉成 Token 的形式後 (`List<int>`)，理論上直接看他產生幾個 `int` 元素，就知道有幾個 Token 才對，但我怎樣算都跟 OpenAI 的 [Tokenizer](https://platform.openai.com/tokenizer) 計算出來的 Token 數量不一樣，這讓我十分困惑。

舉個例子來說，我用 `多奇數位創意有限公司` 這段中文來進行編碼：

-   SharpToken 透過 `cl100k_base` 編碼器編碼後，會產生 12 個 Tokens
-   OpenAI 的 Tokenizer 則會產生 21 個 Tokens

這兩邊差異蠻大的！

經研究後發現，原來 OpenAI 的 [Tokenizer](https://platform.openai.com/tokenizer) 網頁所採用的編碼器，其實是 **Completions API** 的版本，也就是 `p50k_base` 這個編碼器，難怪我怎樣算都不太對！

> 想想其實也覺得合理，因為 ChatGPT 剛出來的時候就推出了 [Tokenizer](https://platform.openai.com/tokenizer) 網頁，之後推出了 ChatGPT 就沒有再推出新版的 Tokenizer 線上工具了。

另外，你可以發現 `cl100k_base` 編碼器編碼出來的 Token 數量少很多，這同時也意味著採用 `cl100k_base` 編碼器其實比較省錢，成本比較低！

我之前寫的翻譯工具，選用的模型是 `text-davinci-003`，所以我把程式改寫一下，不要寫死 `encodingName`，直接拿 `modelName` 來取得 `encodingName` 即可萬無一失，計算過程也就正確無誤了！

```csharp
// cl100k_base
//var encodingName = SharpToken.Model.GetEncodingNameForModel("gpt-35-turbo");
//var encodingName = SharpToken.Model.GetEncodingNameForModel("gpt-4");

// p50k_base
var encodingName = SharpToken.Model.GetEncodingNameForModel("text-davinci-003");

// Get encoding by encoding name
var encoding = GptEncoding.GetEncoding(encodingName);

var str = "多奇數位創意有限公司";

var encoded = encoding.Encode(str);

var decoded = encoding.Decode(encoded);

// 取得編碼後的 Token 總數
var token_count = encoded.Count
```

### 相關連結

-   [dmitry-brazhenko/SharpToken](https://github.com/dmitry-brazhenko/SharpToken): SharpToken is a C# library for tokenizing natural language text. It's based on the tiktoken Python library and designed to be fast and accurate.
    -   [Incorrect token count with Cyrillic · Issue #7 · dmitry-brazhenko/SharpToken](https://github.com/dmitry-brazhenko/SharpToken/issues/7)
-   OpenAI API
    -   [GPT](https://platform.openai.com/docs/guides/gpt)
    -   [GPT best practices](https://platform.openai.com/docs/guides/gpt-best-practices)
