
1. 长对话突然报 Prompt is too long问题到底出在哪如果你在用 Claude 做长文档分析、多轮代码审查或者把 Claude Code 挂在项目里连续跑任务大概率见过这个报错Prompt is too long。它不像语法错误那样能一眼定位因为触发它的不是某一行代码写错而是整个请求的输入 token 累计超过了模型上下文窗口。换句话说你这次发出去的内容本身可能没问题但加上之前所有对话历史、系统提示、工具返回结果总量爆了。这个报错在 Claude API 和 Claude Code 里都会出现。API 侧通常返回 HTTP 400错误体里带prompt: too long或input length字样Claude Code 侧则直接提示Prompt is too long有时候你执行了/compact压缩历史结果还是报同样的错。很多人第一反应是“我这条消息又不长啊”但上下文窗口算的是总和不是单条。我先把结论说清楚Prompt is too long的本质是输入 token 数超过了模型配置上限。要解决它得先搞清楚三件事——token 怎么算、窗口怎么构成、哪部分在偷偷吃额度。这篇就按这个顺序拆给出可复制的 token 估算脚本、分段裁剪配置以及一次从报错到恢复的完整验证动作。适合正在用 Claude API 或 Claude Code 做长上下文任务的开发者也适合刚接触 token 概念、被这个报错卡住的小白。先建立一个直觉token 不是字符也不是单词。英文里 1 个单词大约 1.3 个 token1 个汉字大约 1 到 2 个 token一行代码可能 5 到 20 个 token。所以一个 10KB 的英文文本可能只有 3000 token但同样 10KB 的中文文本可能到 6000 token。代码因为符号密集往往比同等长度的散文更吃 token。你如果按字符数去估误差会很大这就是为什么很多人觉得“我没超啊”却依然报错。上下文窗口的构成也要拆开看系统提示 所有历史用户消息 所有历史助手消息 当前消息 max_tokens输出预留。注意最后一项max_tokens是你给输出留的空间它同样占用窗口。比如 200K 窗口的模型你设了max_tokens8192那输入实际可用就是 200K 减去 8192 再减去一些安全余量。很多人只盯着输入忘了输出预留也在窗口里。触发超限的常见原因按占比排对话历史累积最高多轮之后历史消息把窗口占满单条消息过大其次一次塞了长文档或完整代码文件系统提示过长也常见尤其是那种写了几千字角色设定的 promptmax_tokens设置过高会挤压输入空间工具调用返回大量数据也会中招。不同模型窗口大小还不一样用 Haiku 时比 Opus 更容易频繁触发因为窗口配置不同。所以定位思路应该是先算总量再找最大占用项最后决定是截断、滑动窗口还是分块。下面进入实操。2. 用 TaoToken 统一接入 Claude先把 Key 和 Base URL 配好在动手写 token 估算脚本之前得先有一个能稳定调用 Claude 的入口。我这边习惯用 TaoToken 做统一接入原因是它把模型对话、API Key 管理、Coding Plan 这些入口放在一起切换模型和排查请求都方便。官网是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 注意 API 这个地址不加 UTM 参数。如果你只是想在网页里验证一下模型能不能正常回话可以直接用模型对话入口https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite 。这个适合快速确认“是输入太长还是计数偏差”——你先发一条短消息如果正常返回说明接入没问题问题在长输入如果短消息也报错那可能是 Key 或 Base URL 配错了。要拿 API Key去这个地址https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 。拿到 Key 之后Claude Code 用户还需要配 Base URL 和 Model ID这三件套缺一不可。Claude Code 的接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面有具体的环境变量写法。如果你打算长期跑编码任务或者 Agent建议看一下 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 。它更适合那种需要连续多轮、上下文不断累积的场景因为这类场景正是Prompt is too long的高发区提前规划好额度管理比事后救火省事。配好之后先做一次最小验证用 curl 或 Python SDK 发一条你好确认返回正常。这一步别跳过因为后面所有 token 分析都建立在“接入是通的”这个前提上。如果这一步就报 401那先解决鉴权别急着调 token。3. 可复制的 Token 估算与分段裁剪配置这一节是核心给出能直接跑的代码和配置。先讲 token 估算再讲截断最后给 Claude Code 的 settings 片段。3.1 用 API 精确计数别只靠字符估算Anthropic 提供了count_tokens接口能拿到精确的 input token 数。这是最靠谱的方式因为不同语言的 token 密度差异太大纯字符估算误差可能到 30% 以上。下面这段可以直接复制import anthropic client anthropic.Anthropic( api_key你的_TaoToken_Key, base_urlhttps://taotoken.net/api ) def count_tokens_exact(text, modelclaude-3-5-sonnet-20241022): 调用 API 精确计算 token 数 result client.beta.messages.count_tokens( modelmodel, messages[{role: user, content: text}] ) return result.input_tokens def count_tokens_approximate(text): 降级估算混合文本按 1 token ≈ 3 字符 return len(text) // 3注意base_url指向 TaoToken 的 API 地址这样你不需要改其他代码就能切换。精确计数适合在发送前做一次校验尤其是长文档场景多花一次轻量请求换来确定性很值。3.2 分析对话构成找出谁在吃 token光知道总量不够得知道是哪条消息占的。下面这个函数会按消息逐条统计并打印最大的几条def analyze_conversation(messages, modelclaude-3-5-sonnet-20241022, max_tokens4096): total_text breakdown [] for i, msg in enumerate(messages): content msg[content] if isinstance(content, list): content .join([c.get(text, ) for c in content if c.get(type) text]) approx count_tokens_approximate(content) total_text content breakdown.append({ index: i, role: msg[role], chars: len(content), approx_tokens: approx, preview: content[:80] ... if len(content) 80 else content }) total_approx count_tokens_approximate(total_text) max_tokens print(f消息数: {len(messages)} | 总字符: {len(total_text)} | 估算总 tokens(含输出预留): {total_approx}) for item in breakdown: print(f#{item[index]} [{item[role]}] 字符:{item[chars]} 估算:{item[approx_tokens]} | {item[preview]}) return breakdown, total_approx跑一次你就能看到往往不是当前这条消息最大而是某条历史助手回复或者工具返回结果特别长。定位到之后处理就有针对性了。3.3 分段裁剪保留最近消息截断超长单条下面这个TokenManager做两件事从后往前保留最近消息遇到超预算的单条就按比例截断并尽量在句子边界断开class TokenManager: def __init__(self, modelclaude-3-5-sonnet-20241022, max_context200000, max_output4096): self.model model self.max_context max_context self.max_output max_output self.available_input max_context - max_output - 1000 # 预留安全余量 def count_tokens(self, text): try: return count_tokens_exact(text, self.model) except Exception: return count_tokens_approximate(text) def truncate_message(self, content, max_tokens): current self.count_tokens(content) if current max_tokens: return content ratio max_tokens / current target_chars int(len(content) * ratio) truncated content[:target_chars] last_period max(truncated.rfind(。), truncated.rfind(.), truncated.rfind(!)) if last_period len(truncated) * 0.8: truncated truncated[:last_period 1] return truncated \n[内容已截断...] def prepare_messages(self, messages, system_prompt): system_tokens self.count_tokens(system_prompt) if system_prompt else 0 budget self.available_input - system_tokens prepared [] total 0 for msg in reversed(messages): content msg[content] if isinstance(content, list): content .join([c.get(text, ) for c in content if c.get(type) text]) msg_tokens self.count_tokens(content) if total msg_tokens budget: prepared.insert(0, msg) total msg_tokens else: remaining budget - total if remaining 100: prepared.insert(0, {role: msg[role], content: self.truncate_message(content, remaining - 50)}) break return prepared, total用法就是manager TokenManager()然后prepared_msgs, token_count manager.prepare_messages(messages)。这样发送前就自动裁剪好了。3.4 Claude Code 的 settings 配置片段如果你用的是 Claude Code可以在项目根目录的.claude/settings.json里配置环境变量把 Base URL、Key、Model ID 三件套写全{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_API_KEY: 你的_TaoToken_Key, ANTHROPIC_MODEL: claude-3-5-sonnet-20241022 } }注意ANTHROPIC_BASE_URL不要带 UTM 参数保持干净。配好之后重启 Claude Code再跑一次长任务观察是否还报Prompt is too long。如果还报说明是历史累积问题需要配合/compact或者手动清理会话。4. 验证请求从报错到恢复的完整动作这一节演示一次真实的恢复过程。假设你有一个长对话发送后报Prompt is too long按下面步骤走。第一步先确认接入是通的。发一条短消息resp client.messages.create( modelclaude-3-5-sonnet-20241022, max_tokens100, messages[{role: user, content: 你好}] ) print(resp.usage.input_tokens, resp.content[0].text)如果这条正常返回说明 Key、Base URL、Model ID 都没问题问题在长输入。如果这条也报错先回去检查第 2 节的配置。第二步用analyze_conversation跑一遍你的长对话看总量和最大占用项。假设输出显示总估算 210K token其中第 3 条助手消息占了 80K那基本就是它。第三步用TokenManager.prepare_messages裁剪后重新发送manager TokenManager() prepared, total manager.prepare_messages(messages) print(f裁剪后消息数: {len(prepared)}, 估算 tokens: {total}) resp client.messages.create( modelclaude-3-5-sonnet-20241022, max_tokens4096, messagesprepared ) print(成功input tokens:, resp.usage.input_tokens)如果这次返回正常并且resp.usage.input_tokens明显小于窗口上限说明恢复成功。注意看usage.input_tokens这个字段它是 API 实际计数的结果比你的估算更权威。如果它接近 200K说明你裁得还不够需要再降max_output或者进一步截断历史。第四步做一次回归验证。把裁剪后的消息再跑一次analyze_conversation确认总量在预算内。然后连续发 3 到 5 轮新对话观察是否再次触发。如果不再触发说明滑动窗口策略生效了。这里有个细节/compact在 Claude Code 里是压缩历史但它压缩后的结果仍然占 token如果压缩本身没压到位还是会报错。所以/compact之后要再看一次 token 数别以为执行了就万事大吉。5. 常见报错对照排查这一节把真实会遇到的报错列出来对照处理。401 UnauthorizedKey 错了或者没带上。检查ANTHROPIC_API_KEY是否填对Base URL 是否是https://taotoken.net/api。注意 API 地址不带 UTM带了可能被当成非法路径。local proxy failed本地网络层的问题通常是 Base URL 写错或者本地有拦截。确认你填的是 TaoToken 的 API 地址而不是别的地址。这个报错和 token 无关别往上下文方向查。reading choices这个报错通常出现在响应解析阶段说明返回体结构和你预期的不一样。检查 Model ID 是否写对比如claude-3-5-sonnet-20241022这种完整 ID别简写。如果 Model ID 错了返回体可能不是标准格式解析就崩了。OAuth相关报错Claude Code 有时会走 OAuth 流程如果你用的是 API Key 模式确认没有混用。settings.json 里三件套写全别只写 Key 不写 Base URL。Prompt is too long本身按第 3、4 节处理。先算总量再找最大项再裁剪。如果裁剪后还报检查max_tokens是不是设太高把它降到 4096 甚至 2048 试试。还有一个容易忽略的工具调用返回。如果你用了 function calling工具返回的 JSON 可能非常大它同样计入上下文。这种情况要在工具层做截断别把整个返回塞回去。排查顺序建议先确认接入通短消息测试再看报错类型401 还是 too long再算 token最后裁剪。别一上来就改代码先定位。6. 把上下文管理做成习惯而不是救火Prompt is too long这个报错本质上是上下文管理没跟上。200K 窗口听起来很大但在长对话里消耗得比你想象快。我的做法是在消息进入 API 之前加一层TokenManager自动截断和滑动窗口这样不用每次手动救火。几个实用习惯发送前估算总量接近上限就主动裁剪单条消息超过 5000 token 就截断保留最近 10 到 20 轮对话早期内容要么总结要么丢弃长文档分块处理每块控制在 3000 token 以内系统提示精简到 1000 token 内max_tokens别设太高够用就行。如果你经常跑长任务建议把 Coding Plan 用起来它的额度管理更适合这种连续多轮场景。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面有完整的配置说明。需要拿 Key 就去 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 想先验证模型回话就去 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite 。最后提醒一句token 数直接决定费用主动管理上下文不只是为了避免报错也是在省钱。把usage.input_tokens打日志观察它的变化趋势比事后猜要靠谱得多。