Skip to content

OpenAI API 令牌上下文长度限制

问题描述

当使用 OpenAI API 时,用户尝试使用 text-davinci-003 模型生成文本完成。提示(prompt)包含 1360个令牌,同时将 max_tokens 参数设置为 4000,此时API返回以下错误:

json
{
  "message": "This model's maximum context length is 4097 tokens, however you requested 5360 tokens (1360 in your prompt; 4000 for the completion). Please reduce your prompt or completion length.",
  "type": "invalid_request_error",
  "param": null,
  "code": null
}

这个错误的核心在于对 令牌分配机制模型上下文限制 的误解:

  • OpenAI模型有固定的最大上下文长度
  • max_tokens 参数定义了生成完成文本的最大令牌数
  • 提示+完成的总令牌数不能超过模型限制
  • 在示例中:1360(提示)+ 4000(完成)= 5360 > 4097(模型上限)

令牌计数误区

即使在OpenAI Playground中测试显示总令牌约1374个,仍需要保证请求时 prompt令牌数 + max_tokens <= 模型上限,否则API直接拒绝请求。

解决方案

1. 调整max_tokens参数值

最直接的解决方法是动态计算可用的 max_tokens

js
// Node.js 示例
const prompt = "你的长提示内容";
const promptTokens = 1360; // 应实际计算令牌数

// 计算可用令牌空间(保留50令牌作为缓冲)
const availableTokens = 4097 - promptTokens - 50;
const maxTokens = Math.max(availableTokens, 1); // 确保至少为1

const response = await openai.createCompletion({
  model: 'text-davinci-003',
  prompt: prompt,
  max_tokens: maxTokens, // 动态设置
  temperature: 0.2
});

计算令牌的最佳实践

使用 tiktoken 库精确计算令牌:

bash
npm install tiktoken
js
import { encoding_for_model } from 'tiktoken';
const enc = encoding_for_model('text-davinci-003');
const promptTokens = enc.encode(prompt).length;

2. 升级到支持更大上下文的模型

如应用场景需要更长上下文,可切换到支持更多令牌的更新模型:

模型名称最大上下文长度说明
gpt-4-1106-preview128,000 tokensGPT-4 Turbo (推荐)
gpt-4-06138,192 tokensGPT-4标准
gpt-4-32k-061332,768 tokensGPT-4长上下文版
gpt-3.5-turbo-110616,385 tokensGPT-3.5 Turbo最新版
gpt-3.5-turbo-16k16,385 tokensGPT-3.5长上下文版
text-davinci-0034,097 tokens旧模型(不推荐用于长文本)

使用新模型参考代码:

js
// 使用GPT-4 Turbo
const response = await openai.chat.completions.create({
  model: "gpt-4-1106-preview",
  messages: [{ role: "user", content: prompt }],
  max_tokens: 4096, // 最高可设4096令牌完成
});

3. 优化提示策略

当无法升级模型时,可采用以下文本优化技巧:

  1. 压缩提示内容

    • 删除冗余描述
    • 使用缩写和简写
    • 移除无关上下文
  2. 分块处理技术

    python
    # Python伪代码
    chunks = split_text(prompt, chunk_size=2000)
    results = []
    for chunk in chunks:
        response = openai.Completion.create(
            model="text-davinci-003",
            prompt=chunk,
            max_tokens=2000
        )
        results.append(response.choices[0].text)
    final_result = combine_results(results)
  3. 摘要提炼法

    js
    // 首先生成摘要
    const summaryPrompt = `生成以下文本的摘要:\n${longText}`;
    const summary = await generateCompletion(summaryPrompt, 500);
    
    // 使用摘要继续后续任务
    const finalPrompt = `基于摘要:${summary}\n${question}`;
    const answer = await generateCompletion(finalPrompt, 1000);

关键原理说明

令牌共享机制

OpenAI模型使用共享令牌池:

┌───────────────┬───────────────┐
│   提示令牌    │   完成令牌    │
├───────────────┼───────────────┤
│   Prompt      │ max_tokens    │
└───────┬───────┴───────┬───────┘
        │               │
        └───── 模型最大上下文长度 ───┘

如官方文档所述:

不同模型的最大上下文长度不同。如果提示占用4000个令牌,那么完成部分最多只能使用97个令牌。

不同模型的特殊考量

  1. 聊天模型(gpt-3.5-turbo/gpt-4)

    特殊格式影响

    消息格式包含系统/用户/助手角色标记,会使实际令牌计数难以预测:

    js
    // 角色标记增加额外令牌
    messages: [
      { role: "system", content: "你是有帮助的助手" },
      { role: "user", content: prompt }
    ]

    使用官方推荐方法精确计数。

  2. GPT-4 Vision多模态模型

    • 图像输入需特殊编码占用显著令牌
    • 优先压缩文本提示部分

最佳实践总结

  1. 始终前置令牌计算:在请求前使用tiktoken精确计算
  2. 使用模型映射表:根据需求选择合适上下文长度的模型
  3. 动态设置max_tokens可用令牌 = 模型上限 - 提示令牌 - 缓冲(50~100)
  4. 长文本优化:优先考虑模型升级 > 分块处理 > 提示压缩
  5. 实时监控令牌:在响应中包含使用指标
    js
    console.log(`使用令牌: ${response.usage.total_tokens}`);

未来发展趋势

OpenAI正持续扩大模型上下文窗口(GPT-4 Turbo已支持128K令牌),关注模型更新日志获取最新能力。