来源:一次 Claude Code × 第三方网关的实测排查(2026-10-15)· 客户端版本 2.1.300
交叉验证:issue #77247 · issue #88345 · 结论绑版本,升级后须复验
网关明明支持 1M,长会话却在 200K 附近就显示 100% context used。根因在客户端:provider 被误判,未识别模型一律按 200K 兜底。解法是给实际发送的模型名加 [1m] 后缀——一行配置,窗口从 200K 到 1M。
TL;DR · 四条结论
症状与判别
长会话在 200K 附近就显示 100% context used,而模型的真实上限远高于此。实测中一次会话峰值 190,240 tokens 即满,59 条 API 请求的 betas 里一条 context-1m 都没有。
最快判别法:不跑 A/B,看 /context 两点——窗口值是不是 200k;超限后还能不能跑。
显示 297.7k/200k (149%)、Context exceeds the 200k-token limit…,会话却照常工作。这是铁证:200K 是客户端误判,网关真实上限更高。
处理 → 加 [1m] 后缀(本文方案)
报 prompt is too long / 413 / 网关包装的同类错误——那才是网关真的卡在 200K。
处理 → 调低 CLAUDE_CODE_AUTO_COMPACT_WINDOW=200000 让压缩提前,不要加 [1m]
两种情形表象像、处理相反,先分清再动手。
根因 · 客户端侧,非网关
provider 解析函数只看内部 gateway 认证状态,不看 ANTHROPIC_BASE_URL 的 host(以下摘自 2.1.300 二进制内嵌 JS,符号名已按原文保留):
// provider 解析:只认内部 gatewayAuth 状态,不看 base URL host
function En(){ if (By()) return "gateway"; // By() = 内部 gatewayAuth 状态
return USE_BEDROCK ? "bedrock" : … : "firstParty"; }
function kd(){ let e = process.env.ANTHROPIC_BASE_URL;
return !e || new URL(e).host === "api.anthropic.com"; }
只设 ANTHROPIC_BASE_URL 时:By() 为假 → provider 落 firstParty,而 kd() 又为 false → 原生 1M 分支一个都不命中 → 窗口兜底 200000。
声明窗口的函数从「当前生效的模型目录」取值,而目录开关同样卡在 provider:
function wkt(...){ if(ys(a.CLAUDE_CODE_MODEL_CATALOG)) return "env_off";
… if(!Aa()) return "not_first_party"; // Aa() 要求 firstParty && Si()
… }
not_first_party → 目录判为 off → 窗口声明函数一路返回 null/undefined → 回落到兜底常量 200000。注意:内置目录里 claude-sonnet-4-5 声明的那条 200K根本没被读到,真正生效的是兜底值。
1M 的唯一入口是对模型名本身打标:
function iNt(e){ return /\[1m\]/i.test(e) } // [1m] 检测
function QVn(e,n){ return (iNt(e) || …) && !jr(e) } // 命中即视作 1M
模型名不带标记 → 检测为假 → 200K。
解法
在 ~/.claude/settings.json 里,给每个要跑 1M 的模型在 modelPicker 行里改 model 字段:
"model": "your-model[1m]",
"modelPicker": {
"options": [
{ "model": "your-model[1m]", "label": "your-model",
"behavesAs": "claude-sonnet-4-5" }
]
}
也可以临时用命令行验证:claude --model 'your-model[1m]'。
settings.json 是启动时读取的,改完要重开 Claude Code 才生效(当前会话不受影响)。重启后:
踩过的坑:改完配置没重启就去看 /context,看到仍是旧模型名 / 200k / 149%,差点误判成「方案无效」。实际只是配置尚未加载。
验证
同一网关、同一模型,只差后缀,端到端实测(客户端 2.1.300,2026-10-15):
| 组 | --model | 实际发出的 model | betas 含 context-1m | contextWindow | 结果 |
|---|---|---|---|---|---|
| A | your-model | your-model | ✗ | 200000 | OK |
| B | your-model[1m] | your-model(后缀已剥离) | ✓ | 1000000 | OK |
B 组请求成功返回,说明网关接受了带 1M beta 的请求——端到端闭环,不是只看客户端标记。
claude --model 'your-model[1m]' --print --output-format json 'Reply with exactly: OK' \
| python3 -c "import sys,json; m=json.load(sys.stdin)['modelUsage']; \
print({k:(v['contextWindow'],v['canonicalModel'],v['provider']) for k,v in m.items()})"
# 期望:('…[1m]': (1000000, 'claude-sonnet-4-5', 'firstParty'))
再确认请求头真的带了 beta(取上一步的 session_id):
grep -o '"context-1m-2025-08-07"' \
~/.claude/projects/<你的项目路径>/<session_id>.jsonl | head -1
注:输出里 provider 仍显示 firstParty——说明 [1m] 走的是「模型名打标」这条独立路径,不依赖 provider 被正确解析成 gateway。别拿 provider 字段判断方案是否生效,看 contextWindow。
备选方案
| 方案 | 说明 | 为何没选 |
|---|---|---|
| CLAUDE_CODE_USE_GATEWAY=1 + ANTHROPIC_AUTH_TOKEN |
强制 provider 解析为 gateway;社区实测 200k → 967k,662K 请求网关收下 | 它走「原生 1M 模型」分支,只认 Sonnet 5+ / Opus 4.7+,旧一代模型不在列;且要求 base URL 为 https |
| CLAUDE_CODE_MAX_CONTEXT_TOKENS | 官方文档:未识别的 provider / custom 拼写且不带 [1m] 时直接生效 | 实测不需要——只加 [1m] 就够了;在此场景是冗余变量 |
| 覆盖模型目录 (CLAUDE_CODE_MODEL_CATALOG_URL 等) |
让目录开关返回 env_off 走自建目录 | 未验证加载路径,风险高 |
时效性检查清单
排查方法论
本坑最初用了错方法:直接反编译二进制、自行推导窗口逻辑,连续三次给出错误结论(先说「1M 生效」→ 再说「配置正确不用改」→ 又自己推翻)。绕了两小时才想到搜社区,而 GitHub issue #77247 早已给出验证过的根因 + 一行解法。