Skip to content

OpenAI API 请求前令牌计数方法

问题背景

OpenAI 的文本模型有上下文长度限制(例如 GPT-4 模型的上下文窗口为 128,000 令牌。在生成文本时,您需要设置 max_tokens 参数来控制生成文本的长度,确保:

text
max_tokens = 模型最大上下文长度 - 提示内容令牌数

但关键问题在于:如何在发送 API 请求前准确计算提示文本的令牌数量,从而避免超过模型限制或浪费资源。

核心技术方案

1. 使用官方 tiktoken 库

官方推荐方案

OpenAI 官方提供了 tiktoken 库用于精确计数字符串令牌数:

python
# 安装库
pip install tiktoken

基础用法

python
import tiktoken

def count_tokens(prompt: str, model: str) -> int:
    """计算指定模型下提示的令牌数"""
    try:
        # 为模型获取正确的编码器
        encoding = tiktoken.encoding_for_model(model)
    except KeyError:
        # 后备编码方案(新版模型使用)
        encoding = tiktoken.get_encoding("cl100k_base")
    return len(encoding.encode(prompt))

# 示例用法
prompt = "Hello world, 自然语言处理很有趣!"
token_count = count_tokens(prompt, "gpt-3.5-turbo")
print(f"令牌数量: {token_count}")

不同模型的编码方案

编码方案支持的 OpenAI 模型
o200k_baseGPT-4o 系列
cl100k_baseGPT-4/GPT-3.5 系列/Embedding 模型
p50k_base已弃用

重要提示

新模型(例如 GPT-4o)需要使用 o200k_base 编码,确保选择与目标模型匹配的编码器

2. 聊天消息的令牌计算

当使用 Chat Completions API 时,系统消息和消息元数据也需计入令牌数:

python
import tiktoken

def count_chat_tokens(messages: list, model: str) -> int:
    """计算聊天消息的令牌总数"""
    try:
        encoding = tiktoken.encoding_for_model(model)
    except KeyError:
        encoding = tiktoken.get_encoding("cl100k_base")
    
    tokens_per_message = 3  # GPT-3.5/4 的默认值
    tokens_per_name = 1
    
    total_tokens = 0
    for message in messages:
        total_tokens += tokens_per_message
        for key, value in message.items():
            total_tokens += len(encoding.encode(value))
            if key == "name":
                total_tokens += tokens_per_name
    total_tokens += 3  # 结尾标记
    return total_tokens

# 示例聊天消息结构
messages = [
    {"role": "system", "content": "你是有帮助的助手"},
    {"role": "user", "content": "解释量子计算"}
]
token_count = count_chat_tokens(messages, "gpt-4")

模型差异警告

令牌计算规则因模型版本而异:

  • GPT-3.5-turbo-0301: tokens_per_message = 4
  • 新型号模型: tokens_per_message = 3

3. 动态设置 max_tokens

基于令牌计数结果动态计算最大生成长度:

python
def calculate_max_tokens(prompt_tokens: int, model: str) -> int:
    """计算可用的max_tokens值"""
    model_context_size = {
        "gpt-3.5-turbo": 4096,
        "gpt-4-1106-preview": 128000,
        "gpt-4o": 128000
    }
    # 默认为4096如果未找到模型
    context_size = model_context_size.get(model, 4096)
    return context_size - prompt_tokens - 50  # 预留安全空间

# 使用示例
max_output_tokens = calculate_max_tokens(prompt_tokens, "gpt-4o")

多语言实现方案

除 Python 外,其他语言的令牌计数器实现:

JavaScript

javascript
// 使用 npm 包 @dqbd/tiktoken
import { encoding_for_model } from "@dqbd/tiktoken";

const encoder = encoding_for_model("gpt-4");
const tokens = encoder.encode("你的文本");
console.log(`Tokens: ${tokens.length}`);

Java

java
// 使用 jtokkit 库
import com.knuddels.jtokkit.api.EncodingRegistry;

EncodingRegistry registry = Encodings.newDefaultEncodingRegistry();
var encoding = registry.getEncodingForModel("gpt-4");
int tokenCount = encoding.countTokens("您的文本");

常见问题解答

为什么 tiktoken 与 API 报告值有差异?

  • API 返回值包含特殊控制令牌(如 <|start|>
  • 推荐在实际请求后确认 response.usage.prompt_tokens

计算令牌的最佳实践

  1. 始终为您的目标模型指定正确编码器
  2. 在部署前验证本地计数与 API 返回值
  3. 为长文本设置令牌缓存机制
  4. 使用 max_tokens = context_size - prompt_tokens - buffer 公式(建议预留 50 令牌缓冲)

官方更新备注:截至 2024 年 5 月,cl100k_base 编码支持所有 GPT-3.5/4 系列模型,o200k_base 用于 GPT-4o

备选方案比较

方法精度易用性适用场景
tiktoken★★★★★★★★★生产环境首选
API 后验计数★★★★★★★★结果验证
基于空格/字符的分割★★☆★★★★★快速估算/后备方案

注意事项:避免使用过时的 GPT-2 分词器,其与 OpenAI 当前模型的分词方式存在显著差异。

扩展工具资源