【Claude】Prompt too long 错误:上下文 Token 超出上限的截断与压缩 bug报错已解决

关键词: Claude Code、Prompt too long、上下文窗口、Token 超限、context window exceeded、截断、压缩、Token 计算、消息长度、max_tokens、输入长度、模型限制


一、问题描述:当上下文"塞满"了

"Prompt too long" 是 Claude API 最直接的输入长度错误之一。当请求中的消息内容(包括当前消息和历史对话)的总 Token 数超过模型支持的最大上下文窗口时,API 会直接拒绝处理并返回此错误。与 "Error during compaction"(系统尝试压缩但失败)不同,"Prompt too long" 意味着在压缩之前,请求就已经超过了硬性限制——系统甚至不会尝试压缩,直接拒绝。

1.1 典型报错场景与错误信息

场景一:单次消息超长
import anthropic
client = anthropic.Anthropic(api_key="your-key")

long_text = "A" * 500000  # 50 万字符
response = client.messages.create(
    model="claude-sonnet-4-20250514",
    messages=[{"role": "user", "content": long_text}]
)
# 错误:prompt is too long: 200000 tokens > 200000 limit
场景二:累积历史超限
# 经过多轮对话后,历史消息累积超过 200K tokens
messages = [...]  # 包含大量历史对话
response = client.messages.create(
    model="claude-sonnet-4-20250514",
    messages=messages + [{"role": "user", "content": "new message"}]
)
# 错误:prompt is too long
场景三:图片 Base64 导致超长
import base64

with open("large_image.png", "rb") as f:
    image_data = base64.b64encode(f.read()).decode()

response = client.messages.create(
    model="claude-sonnet-4-20250514",
    messages=[{
        "role": "user",
        "content": [
            {"type": "text", "text": "分析图片"},
            {"type": "image", "source": {"type": "base64", "media_type": "image/png", "data": image_data}}
        ]
    }]
)
# 错误:prompt is too long(base64 编码非常长)

13

二、根因分析:Token 限制与超限原因

2.1 Token 限制的硬性边界

模型 最大上下文 描述
Claude 4 Sonnet 200,000 tokens 输入 + 输出总和
Claude 4 Opus 200,000 tokens 同上
Claude 3 Haiku 200,000 tokens 同上

关键公式

总 Token 数 = 系统提示 + 历史消息 + 当前消息 + max_tokens(输出预留)

必须满足:总 Token 数 <= 200,000

2.2 常见超限原因

原因 描述 占比
大量历史对话 多轮对话累积 40%
长文本粘贴 粘贴整本书/长文档 30%
代码文件过多 粘贴多个代码文件 20%
图片 Base64 图片编码非常长 10%

三、实际操练:截断与压缩策略

3.1 策略一:精确计算 Token 数

#!/usr/bin/env python3
# token_calculation.py

import tiktoken

def estimate_tokens(text):
    """估算 Token 数"""
    enc = tiktoken.get_encoding("cl100k_base")
    return len(enc.encode(text))

def check_message_length(messages, max_tokens=200000, output_reserve=4096):
    """
    检查消息是否超过限制
    """
    total = 0
    for msg in messages:
        content = msg.get("content", "")
        if isinstance(content, list):
            for item in content:
                if item.get("type") == "text":
                    total += estimate_tokens(item.get("text", ""))
                elif item.get("type") == "image":
                    data = item.get("source", {}).get("data", "")
                    total += int(len(data) * 0.75)
        else:
            total += estimate_tokens(str(content))
        total += 4  # 消息格式开销
    
    available = max_tokens - total - output_reserve
    return {
        "total_input": total,
        "available_output": available,
        "is_over_limit": available < 0,
        "usage_percent": (total / max_tokens) * 100
    }

# 使用
messages = [{"role": "user", "content": "Hello " * 10000}]
result = check_message_length(messages)
print(f"Token usage: {result['usage_percent']:.1f}%")
print(f"Over limit: {result['is_over_limit']}")

3.2 策略二:截断历史对话

def truncate_messages(messages, max_tokens=180000):
    """
    截断消息列表,保留最新的消息
    """
    enc = tiktoken.get_encoding("cl100k_base")
    total = 0
    truncated = []
    
    # 从最新消息开始倒序处理
    for msg in reversed(messages):
        tokens = estimate_tokens(str(msg.get("content", ""))) + 4
        if total + tokens > max_tokens:
            break
        total += tokens
        truncated.insert(0, msg)
    
    return truncated

# 使用:保留最新的 180K tokens
messages = truncate_messages(long_messages, max_tokens=180000)

3.3 策略三:摘要压缩历史

#!/usr/bin/env python3
# conversation_summarizer.py

import anthropic
client = anthropic.Anthropic(api_key="your-key")

def summarize_conversation(messages, max_summary_tokens=5000):
    """
    将长对话压缩为摘要
    """
    # 将对话转为文本
    conversation_text = "\n\n".join([
        f"{msg['role']}: {msg['content']}"
        for msg in messages
    ])
    
    # 让 Claude 生成摘要
    summary_response = client.messages.create(
        model="claude-haiku-3-20250307",
        max_tokens=max_summary_tokens,
        messages=[{
            "role": "user",
            "content": f"请用 2000 字以内总结以下对话的关键信息:\n\n{conversation_text[:50000]}"
        }]
    )
    
    summary = summary_response.content[0].text
    
    # 返回摘要 + 最近 2 轮对话
    return [
        {"role": "user", "content": f"之前的对话摘要:{summary}"},
    ] + messages[-4:]  # 保留最近 2 轮(user + assistant)

# 使用
compressed = summarize_conversation(long_messages)
response = client.messages.create(
    model="claude-sonnet-4-20250514",
    messages=compressed + [{"role": "user", "content": "继续讨论"}]
)

3.4 策略四:分块处理长文本

def chunk_text(text, max_chunk_tokens=10000):
    """
    将长文本分块,每块不超过指定 Token 数
    """
    enc = tiktoken.get_encoding("cl100k_base")
    tokens = enc.encode(text)
    
    chunks = []
    for i in range(0, len(tokens), max_chunk_tokens):
        chunk_tokens = tokens[i:i + max_chunk_tokens]
        chunks.append(enc.decode(chunk_tokens))
    
    return chunks

# 使用:逐块处理
chunks = chunk_text(long_document, max_chunk_tokens=15000)
for i, chunk in enumerate(chunks):
    response = client.messages.create(
        model="claude-sonnet-4-20250514",
        max_tokens=1000,
        messages=[{
            "role": "user",
            "content": f"分析以下文档片段(第 {i+1}/{len(chunks)} 部分):\n\n{chunk}"
        }]
    )
    print(f"Chunk {i+1} result: {response.content[0].text[:100]}")

3.5 策略五:压缩图片

from PIL import Image
import io, base64

def compress_image_for_claude(image_path, max_size=(800, 800), quality=80):
    """
    压缩图片以减少 Token 占用
    """
    with Image.open(image_path) as img:
        img.thumbnail(max_size)
        if img.mode in ('RGBA', 'LA'):
            img = img.convert('RGB')
        
        buffer = io.BytesIO()
        img.save(buffer, format='JPEG', quality=quality)
        base64_str = base64.b64encode(buffer.getvalue()).decode()
        
    print(f"Compressed base64 length: {len(base64_str)}")
    print(f"Estimated tokens: {int(len(base64_str) * 0.75)}")
    return base64_str

# 使用
base64_image = compress_image_for_claude("photo.png", max_size=(600, 600))

四、验证与回归测试

#!/usr/bin/env python3
# prompt_length_test.py

import anthropic
from anthropic import BadRequestError

client = anthropic.Anthropic(api_key="your-key")

def test_length_limit():
    """测试长度限制"""
    lengths = [10000, 50000, 100000, 150000, 190000, 200000]
    
    for length in lengths:
        text = "A" * length
        try:
            response = client.messages.create(
                model="claude-haiku-3-20250307",
                max_tokens=10,
                messages=[{"role": "user", "content": text}]
            )
            print(f"✅ {length} chars: OK")
        except BadRequestError as e:
            if "too long" in str(e):
                print(f"❌ {length} chars: TOO LONG")
            else:
                print(f"⚠️ {length} chars: {e}")

test_length_limit()

五、总结与最佳实践

5.1 核心要点

  1. 200K 是硬性限制:输入 + 输出 <= 200,000 tokens
  2. 预防优于截断:发送前计算 Token 数
  3. 压缩图片:减少图片的 base64 长度
  4. 分块处理:长文档分块处理
  5. 摘要历史:用摘要替代完整对话历史

5.2 最佳实践

场景 推荐做法
长文档 > 100K 字符 分块处理
多轮对话 > 50 轮 摘要历史 + 保留最近 2 轮
图片 > 1MB 压缩到 800x800 以下
不确定长度 先计算 Token 数再发送
代码库分析 逐文件分析,而非全部粘贴

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐