最近在使用 Claude Code + Claude Code Router(CCR)+ AWS Bedrock 模型 做代码开发时,遇到了一连串和上下文长度、模型路由、AWS 端点类型相关的问题。表面上看只是一个 context length exceeded 报错,实际排查下来牵扯到:
- Claude Code 如何组织上下文;
- CCR 如何判断长上下文并切换模型;
- AWS Bedrock 上不同模型的上下文窗口;
- Bedrock Mantle endpoint 与 Bedrock Runtime endpoint 的差异;
- OpenAI 兼容接口、Responses / Chat Completions、Converse / InvokeModel 之间的调用方式差别;
- CCR 日志应该怎么看;
- 如何查询自己能用哪些 Mantle 模型。
这篇文章把这次排查过程整理下来,作为后续使用 Claude Code + CCR + AWS Bedrock 的避坑记录。
一、问题背景
我的调用链路大致是:
text
Claude Code
-> Claude Code Router
-> AWS Bedrock Mantle endpoint
-> 后端模型,例如 GLM-5 / MiniMax / Qwen3 Coder Next
一开始我使用的是 GLM-5,后来尝试 MiniMax,最后临时换成 Qwen3 Coder Next 才稳定解决上下文超限问题。
Claude Code Router 本身的作用就是把 Claude Code 的请求路由到不同模型,并可以通过 transformer 适配不同供应商 API。CCR 官方说明它支持模型路由、多 provider、请求/响应转换、动态模型切换和插件系统。(GitHub)
我的典型 CCR 配置结构类似这样:
json
{
"PORT": 3456,
"LOG": true,
"API_TIMEOUT_MS": 1200000,
"Providers": [
{
"name": "aws-bedrock",
"api_base_url": "https://bedrock-mantle.us-east-1.api.aws/v1/chat/completions",
"api_key": "$BEDROCK_MANTLE_API_KEY",
"max_tokens": 31000,
"models": [
"zai.glm-5",
"minimax.minimax-m2",
"qwen.qwen3-coder-next"
],
"transformer": {
"use": [
["openai"]
]
}
}
],
"Router": {
"default": "aws-bedrock,qwen.qwen3-coder-next",
"background": "aws-bedrock,qwen.qwen3-coder-next",
"think": "aws-bedrock,qwen.qwen3-coder-next",
"longContext": "aws-bedrock,qwen.qwen3-coder-next",
"longContextThreshold": 60000
}
}
注意:实际使用时不要把 API key 明文写进配置文件,建议放环境变量。
二、最开始遇到的错误:上下文超过模型限制
最典型的错误如下:
text
This model's maximum context length is 202752 tokens.
However, you requested 32000 output tokens and your prompt contains at least 170753 input tokens,
for a total of at least 202753 tokens.
Please reduce the length of the input prompt or the number of requested output tokens.
这个错误很直观:
text
模型最大上下文:202752
输入 token:170753
请求输出 token:32000
总计:202753
刚好多了:
text
202753 - 202752 = 1
后来换 MiniMax 时也遇到类似问题:
text
This model's maximum context length is 196608 tokens.
However, you requested 32000 output tokens and your prompt contains at least 164609 input tokens,
for a total of at least 196609 tokens.
同样是刚好多 1:
text
196609 - 196608 = 1
这个现象很容易让人怀疑是 CCR、transformer 或 provider 适配层里的 off-by-one 边界问题。
GLM-5是203K上下文,MINIMAX-2号称是256K上下文。从GLM-5换成MINIMAX-2是试图通过改用更大大上下文的模型来解决本次会话中上下文超大的问题。不幸的是,MINIMAX-2实际只有196K上下文。这里的信息来源有点不太属实--AWS上文档可能写错了,MINIMAX-2.5可能才是256K。
但从服务端角度看,它只是严格按照下面这个规则校验:
text
input_tokens + requested_output_tokens <= maximum_context_length
只要超过 1 token,也会直接返回 400。
所以这类问题不能靠侥幸贴边解决,必须给上下文和输出留安全余量。
三、CCR 日志怎么解读
CCR 开启日志后,可以看到类似下面的日志:
json
{
"level": 30,
"time": 1777363452122,
"pid": 92877,
"hostname": "BILLY",
"reqId": "req-8",
"msg": "Using long context model due to token count: 168448, threshold: 60000"
}
这条日志非常关键,它说明:
text
CCR 估算当前请求 token 数是 168448
longContextThreshold 配置的是 60000
因为 168448 > 60000
所以 CCR 使用 Router.longContext 配置的模型
也就是说,这条日志是 CCR 触发长上下文路由的证据。
CCR 官方文档里有对应配置示例:
json
{
"Router": {
"longContextThreshold": 100000,
"longContext": "gemini,gemini-1.5-pro"
}
}
官方说明这是用于将长上下文请求路由到指定模型。
所以这条日志不是错误,而是在告诉你:
text
CCR 认为当前请求已经是长上下文请求,并且正在切到 longContext 模型。
四、CCR 中表明"切换到另一个模型"的证据
判断 CCR 是否切换模型,主要看两类日志。
第一类是长上下文路由日志:
text
Using long context model due to token count: 168448, threshold: 60000
这说明它使用了:
json
"Router": {
"longContext": "provider,model"
}
第二类是 provider 报错中的模型名。例如:
text
Error from provider(aws-glm5,minimax.minimax-m2: 400)
这说明这次请求最终打到了:
text
provider = aws-glm5
model = minimax.minimax-m2
如果你看到:
text
Error from provider(aws-bedrock,qwen.qwen3-coder-next: 400)
那说明请求最终打到了:
text
provider = aws-bedrock
model = qwen.qwen3-coder-next
所以要判断模型是否切换成功,不要只看 Claude Code 欢迎页显示什么,还要看 CCR 日志里的 provider/model。
CCR 的路由格式是:
text
provider,model
官方文档也明确说明 fallback 和路由模型使用 provider,model 格式,并且 backup model 必须存在于 Providers 配置里。(MusiStudio)
五、longContextThreshold 不是上下文上限
我一开始很容易误解:
json
"longContextThreshold": 60000
这个配置不是说模型最大上下文是 60000,也不是说输入超过 60000 会被自动压缩。
它只是说:
text
当 CCR 估算请求 token 超过 60000 时,切到 Router.longContext 指定的模型。
例如:
json
"Router": {
"default": "aws-bedrock,zai.glm-5",
"longContext": "aws-bedrock,qwen.qwen3-coder-next",
"longContextThreshold": 60000
}
含义是:
text
普通请求走 GLM-5
超过 60000 token 的请求走 Qwen3 Coder Next
如果你这样配置:
json
"Router": {
"default": "aws-bedrock,zai.glm-5",
"longContext": "aws-bedrock,zai.glm-5",
"longContextThreshold": 60000
}
那虽然 CCR 会判断"这是长上下文",但最终还是走同一个模型,实际上不会解决上下文不够的问题。
六、为什么最后换 Qwen3 Coder Next
这次排查中,我先后尝试了 GLM-5、MiniMax,最后发现 Qwen3 Coder Next 的上下文达到256,能够解决本次的问题。
AWS Bedrock 官方模型卡显示,Qwen3 Coder Next 是面向代码生成、调试和软件工程能力优化的模型。官方文档中它的模型 ID 是:
text
qwen.qwen3-coder-next
并且文档写明:
text
Context window: 256K tokens
Max output tokens: 32K
同时它支持 Responses、Chat Completions、Invoke 和 Converse 等接口类型,并支持 bedrock-runtime 与 bedrock-mantle 端点。AWS 文档还特别提示,尽可能使用 bedrock-mantle endpoint。(AWS 文档)
这里有一个有趣的体验:
实测下来,Claude Code + Qwen3 Coder Next 的效果不差。我的感觉是,Claude Code 本身对上下文组织得比较好,它会把文件、工具调用、历史信息组织成模型比较容易理解的形式。也就是说,模型本身重要,但 Claude Code 的上下文组织方式也非常关键。
这次我看到一条请求达到:
text
168448 tokens
一行日志甚至达到:
text
约 690K 字符
这说明 Claude Code + CCR 这种链路里,上下文膨胀是非常真实的问题。尤其是 Claude Code 读取大文件、长文档、多轮工具调用之后,输入 token 很容易暴涨。
七、为什么 max_tokens 配了没生效
我曾经在 CCR Provider 里配置:
json
"max_tokens": 8192
但报错里仍然显示:
text
requested 32000 output tokens
这说明 provider 级别的 max_tokens 并不一定能覆盖 Claude Code 上游传入的输出上限,或者在当前 transformer 链路里被透传了。
比较稳妥的做法是两边都配:
CCR 侧
json
"max_tokens": 31000
Claude Code 侧
bash
export CLAUDE_CODE_MAX_OUTPUT_TOKENS=31000
因为我是在WSL下的相应代码目录里执行code . 拉起claude插件的,所以还要source ~/.bashrc才能让配置生效。不过最后这个方法没能解决上下文控制的问题。与我的聊天历史有关,因为历史已经走到要发送大的上下文了,此时的配置已经控制不到历史消息了,只能对新消息有效果。
如果还看到服务端报:
text
requested 32000 output tokens
说明最终请求体里仍然是 32000,这时需要进一步检查 CCR 的 transformer,甚至需要自定义 transformer,在请求转发前强制 clamp:
js
body.max_tokens = Math.min(body.max_tokens || 31000, 31000)
八、Bedrock Mantle endpoint 和 Runtime endpoint 的坑
这次另一个很大的坑,是 AWS Bedrock 上有不同类型的端点。
我实际踩到的是:
text
一种是 bedrock-mantle endpoint
一种是 bedrock-runtime endpoint
例如 Qwen3 Coder Next 的 AWS 官方文档中就明确列出了两类 programmatic access:
text
bedrock-runtime -> qwen.qwen3-coder-next -> https://bedrock-runtime.{region}.amazonaws.com
bedrock-mantle -> qwen.qwen3-coder-next -> https://bedrock-mantle.{region}.api.aws/v1
并且官方文档写明,如果 region 是 us-east-1,对应 endpoint 分别是:
text
https://bedrock-runtime.us-east-1.amazonaws.com
https://bedrock-mantle.us-east-1.api.aws/v1
AWS 文档还提示:Whenever possible, use the bedrock-mantle endpoint。(AWS 文档)
这两类端点的使用方式不一样:
1. Mantle endpoint
Mantle endpoint 更像 OpenAI 兼容接口,例如:
text
https://bedrock-mantle.us-east-1.api.aws/v1/chat/completions
它适合 CCR 这类 OpenAI-compatible router 使用。
CCR 配置里通常是:
json
{
"api_base_url": "https://bedrock-mantle.us-east-1.api.aws/v1/chat/completions",
"api_key": "$BEDROCK_MANTLE_API_KEY",
"transformer": {
"use": [
["openai"]
]
}
}
这里的认证方式一般是:
http
Authorization: Bearer XXXX
2. Runtime endpoint
Runtime endpoint 是 AWS 原生 Bedrock Runtime API,例如:
text
https://bedrock-runtime.us-east-1.amazonaws.com
它不是简单的 OpenAI-compatible chat/completions 调用方式,而是 AWS Bedrock Runtime 的 API,例如 Converse。AWS Converse API 的请求路径是:
text
POST /model/modelId/converse
请求体中有 messages、system、inferenceConfig.maxTokens、toolConfig 等字段。(AWS 文档)
这类接口通常走 AWS SDK 或 SigV4 签名,也就是使用:
text
aws_access_key_id
aws_secret_access_key
region
这种方式,而不是简单的 HTTP Bearer token。
所以我的理解是:
text
CCR + OpenAI transformer 更适合接 Mantle endpoint
AWS SDK / boto3 / 原生 Bedrock 调用更适合接 Runtime endpoint
把 Runtime endpoint 当成 Mantle/OpenAI endpoint 配进 CCR,很容易出现模型不支持、接口不匹配、认证方式不对等问题。
九、如何列出自己能用哪些 Mantle 模型
这是最实用的一条命令。
如果你使用 Bedrock Mantle endpoint,可以直接调用:
bash
curl -X GET https://bedrock-mantle.us-east-1.api.aws/v1/models \
-H "Authorization: Bearer XXXX"
注意:
text
XXXX 替换成自己的 Mantle API Key
不要把真实 key 发到博客、GitHub、聊天工具里
如果返回成功,会看到当前 API key 能访问的模型列表。然后你就可以从里面挑模型 ID,写进 CCR 的 models 和 Router 里。
例如:
json
"models": [
"qwen.qwen3-coder-next",
"zai.glm-5",
"minimax.minimax-m2"
]
Router 里再写:
json
"Router": {
"default": "aws-bedrock,qwen.qwen3-coder-next",
"longContext": "aws-bedrock,qwen.qwen3-coder-next",
"longContextThreshold": 60000
}
另外,如果你走的是 AWS 原生 Bedrock API,也可以用 AWS 的 ListFoundationModels API。AWS 官方说明 ListFoundationModels 用于列出可使用的 Amazon Bedrock foundation models,并支持按 provider、output modality、inference type 等过滤。(AWS 文档)
AWS CLI 方式通常类似:
bash
aws bedrock list-foundation-models --region us-east-1
但要注意:这列的是 Bedrock 原生模型视图,不一定等同于 Mantle OpenAI-compatible endpoint 下当前 API key 可访问的模型列表。所以如果你是 CCR + Mantle,优先用:
bash
curl -X GET https://bedrock-mantle.us-east-1.api.aws/v1/models \
-H "Authorization: Bearer XXXX"
十、如何查看模型上下文大小
不要靠猜,也不要只看模型名。一定要查官方模型卡。
以 Qwen3 Coder Next 为例,AWS 官方模型卡中明确写了:
text
Context window: 128K tokens
Max output tokens: 16K
并且列出了 programmatic access 的模型 ID 和 endpoint。(AWS 文档)
查看方式:
- 打开 AWS Bedrock 文档;
- 搜索模型名,例如
Qwen3 Coder Next Amazon Bedrock; - 找到模型卡;
- 看以下字段:
text
Context window
Max output tokens
APIs supported
Endpoints supported
Programmatic Access
Regional Availability
尤其要注意:
text
Context window 不等于 max output tokens
input_tokens + output_tokens 不能超过 context window
例如某模型:
text
Context window: 128K
Max output tokens: 16K
并不代表你可以输入 128K 再输出 16K。实际请求仍然要满足:
text
input_tokens + requested_output_tokens <= context_window
十一、一次请求 168K token 意味着什么
我这次日志里看到:
text
Using long context model due to token count: 168448, threshold: 60000
这个 token 数已经非常大了。
如果此时还请求:
text
max_tokens = 32000
那总预算至少要:
text
168448 + 32000 = 200448
所以:
- 196K 上下文的模型会炸;
- 202K 上下文的模型理论上勉强够,但只剩 2000 多 token 的余量,非常危险;
- 更大上下文模型会更稳。
这也是我最后换更大上下文模型的原因。
十二、推荐的 CCR 配置模板
下面是我现在更推荐的写法。
1. .bashrc 环境变量
bash
export BEDROCK_MANTLE_API_KEY="XXXX"
export ANTHROPIC_BASE_URL="http://127.0.0.1:3456"
export ANTHROPIC_API_KEY="dummy"
export CLAUDE_CODE_MAX_OUTPUT_TOKENS=16000
export API_TIMEOUT_MS=1200000
如果用更大上下文模型,可以酌情调整:
bash
export CLAUDE_CODE_AUTO_COMPACT_WINDOW=262000
export CLAUDE_AUTOCOMPACT_PCT_OVERRIDE=80
这里不要盲目设到模型极限。因为实际请求里还有:
text
system prompt
tool schema
工具调用历史
文件内容
CCR transformer 包装
provider tokenizer 误差
2. CCR 配置
json
{
"PORT": 3456,
"LOG": true,
"API_TIMEOUT_MS": 1200000,
"Providers": [
{
"name": "aws-bedrock",
"api_base_url": "https://bedrock-mantle.us-east-1.api.aws/v1/chat/completions",
"api_key": "$BEDROCK_MANTLE_API_KEY",
"max_tokens": 16000,
"models": [
"qwen.qwen3-coder-next",
"zai.glm-5",
"minimax.minimax-m2"
],
"transformer": {
"use": [
["openai"]
]
}
}
],
"Router": {
"default": "aws-bedrock,qwen.qwen3-coder-next",
"background": "aws-bedrock,qwen.qwen3-coder-next",
"think": "aws-bedrock,qwen.qwen3-coder-next",
"longContext": "aws-bedrock,qwen.qwen3-coder-next",
"longContextThreshold": 60000
}
}
如果你希望普通任务走一个模型,长上下文走另一个模型,可以写成:
json
"Router": {
"default": "aws-bedrock,zai.glm-5",
"think": "aws-bedrock,zai.glm-5",
"background": "aws-bedrock,qwen.qwen3-coder-next",
"longContext": "aws-bedrock,qwen.qwen3-coder-next",
"longContextThreshold": 60000
}
十三、调试时的检查清单
以后再遇到类似问题,我建议按这个顺序查。
1. 看 CCR 日志是否触发 long context
搜索:
text
Using long context model due to token count
看到类似:
text
Using long context model due to token count: 168448, threshold: 60000
说明 longContext 路由生效。
2. 看最终打到哪个模型
看错误里:
text
Error from provider(providerName,modelId)
例如:
text
Error from provider(aws-bedrock,qwen.qwen3-coder-next)
这才是实际请求模型。
3. 看上下文计算
把错误里的三项拿出来算:
text
maximum context length
input tokens
requested output tokens
公式:
text
input_tokens + requested_output_tokens <= maximum_context_length
只要不满足,就会 400。
4. 不要贴边
如果模型最大上下文是:
text
196608
不要配置成:
text
输入 164608 + 输出 32000 = 196608
看起来刚好,实际一定容易炸。
建议至少留:
text
2000 - 10000 tokens
安全余量。
5. 确认 endpoint 类型
CCR + OpenAI transformer:
text
bedrock-mantle endpoint
/v1/chat/completions
Authorization: Bearer XXXX
AWS 原生 SDK:
text
bedrock-runtime endpoint
Converse / InvokeModel
aws_access_key_id / aws_secret_access_key / region
不要混用。
十四、这次踩坑后的结论
这次最大的问题不是单个模型不行,而是整个链路里有多个边界:
text
Claude Code 上下文组织
CCR token 估算
CCR longContext 路由
transformer 请求转换
AWS Mantle endpoint
模型真实上下文限制
max output tokens
只要其中任何一层没有留余量,就会出现:
text
只差 1 token 也被拒绝
最终我的处理方式是:
- 不再强行贴 32K 输出极限;
- 用 CCR 日志确认是否触发 longContext;
- 用 Mantle
/v1/models查询当前 key 可用模型; - 查 AWS 模型卡确认上下文大小和 max output;
- 对长上下文任务切到 Qwen3 Coder Next;
- 发现 Claude Code + Qwen3 Coder Next 的实际代码体验并不差。
尤其是最后一点很有意思:
虽然模型很重要,但 Claude Code 对上下文的组织能力也很关键。即使换成非 Claude 原生模型,只要 CCR 适配得好,实际编码体验也可以接受。
十五、保留命令
查看 Mantle 模型
bash
curl -X GET https://bedrock-mantle.us-east-1.api.aws/v1/models \
-H "Authorization: Bearer XXXX"
重启 CCR
bash
ccr restart
查看 CCR 配置
bash
cat ~/.claude-code-router/config.json
搜索 CCR 日志
bash
grep -R "Using long context model" ~/.claude-code-router 2>/dev/null
查看 Claude Code 当前模型
在 Claude Code 里:
text
/model
AWS 原生方式列模型
bash
aws bedrock list-foundation-models --region us-east-1
结语
这次问题看起来只是一个 400 报错,实际上暴露了 Claude Code + CCR + AWS Bedrock 组合使用时最容易踩的几个坑:
text
上下文窗口不是无限的
max output 会占用总上下文
longContextThreshold 只是路由阈值
Mantle endpoint 和 Runtime endpoint 不是一回事
CCR 日志能证明是否切换模型
模型 ID 要以实际 endpoint 可访问列表为准
不要把请求卡在模型极限边界
后续再配置模型时,我会优先做三件事:
text
先 curl /v1/models 看 Mantle 可用模型
再查 AWS 模型卡确认 context window / max output
最后根据 CCR 日志确认真实路由到哪个模型
这能少走很多弯路。