qwen3.8-27b 返回 400 context_length_exceeded 怎么办?删掉几段对话为什么还不行?
11 月 6 日晚上 23:17,我在给一个带 12 个工具的 Agent 补日志。删掉两轮聊天后,请求还是 400。
问题不在于“对话删得不够多”,而是完整请求必须满足 输入 Token + max_tokens ≤ 服务端上下文窗口。system、工具定义、历史消息、RAG 文档和聊天模板,全都算输入。
输入本身没超限时,调低 max_tokens 就行。输入已经塞满,那就只能继续截断、做摘要,或者压缩工具定义。
还有个容易漏掉的前提:截至 2026 年 11 月 10 日,ofox 模型目录中可以查到完整 ID qwen/qwen3.8-27b,但已知 Qwen 官方标准模型列表里尚未核实到 qwen3.8-27b 这个标识。它可能是平台别名或部署名称。具体窗口不能靠名字猜,要看服务端返回和部署配置。
为什么删掉几段对话还是超限
我当时看到的完整错误是:
400 Bad Request: context_length_exceeded: The input tokens plus the requested max_tokens exceed the model's maximum context length.
不同服务端返回的字段可能不一样。上面这段是复现环境的原文,不是该模型固定的官方文案。排查时至少保留这些信息:
HTTP 状态码、error.message、error.type、error.code、模型 ID、input_tokens、max_tokens
实际预算关系是:
I = S + H + T + R + U + P
I + O ≤ C
其中:
| 符号 | 占用来源 | 常见内容 |
|---|---|---|
S | System Prompt | 角色约束、输出格式、业务规则 |
H | History | user 和 assistant 的历史消息 |
T | Tools | 函数名、描述、JSON Schema、参数说明 |
R | RAG | 检索文档、代码片段、数据库结果 |
U | User | 当前用户输入 |
P | Template | role 标记、特殊 Token、生成提示 |
O | Output | 请求参数 max_tokens 预留长度 |
C | Context | 服务端实际配置的上下文窗口 |
最坑的是 T。工具一次都没执行,定义照样得完整发给模型。12 个函数,每个都带一坨 JSON Schema,几千 Token 转眼就没了。挺烦人的。
graph LR
S[System] --> I[完整输入 Token]
H[历史消息] --> I
T[工具定义] --> I
R[RAG 文档] --> I
U[当前问题] --> I
P[Chat Template] --> I
I --> B{输入 + 最大输出 ≤ 窗口?}
O[max_tokens] --> B
B -->|否| E[400 context_length_exceeded]
B -->|是| G[进入生成]
下面这组数据只是用来说明计算方法,不是 qwen/qwen3.8-27b 的官方参数:
| 项目 | 示例 Token |
|---|---|
| System Prompt | 3,200 |
| 历史消息 | 11,840 |
| 工具定义 | 6,900 |
| RAG 文档 | 5,400 |
| 当前问题与模板 | 260 |
输入合计 I | 27,600 |
预留输出 O | 8,192 |
| 总预算 | 35,792 |
假设部署控制台显示 C = 32,768,超出量就是:
35,792 - 32,768 = 3,024 Token
这时删掉两轮聊天,一共省下 1,800 Token,总预算仍有 33,992 Token,还超出 1,224。确实删了,但没删到点上。测完我人都麻了。
该用哪种修复方法
| 你看到的情况 | 应该怎么处理 | 原因 |
|---|---|---|
I < C,但 I + O > C | 调低 max_tokens | 只是输出预留过大 |
I ≥ C | 截断、摘要、减少工具或 RAG | 输入本身已经装不下 |
| 本地计数没超,API 返回 400 | 核对 tokenizer 和 chat template | 本地可能漏算特殊 Token |
| 同一模型在不同服务端结果不同 | 检查部署参数 | max_model_len、num_ctx 可能更小 |
| 模型名称无法对应官方文档 | 查询 /v1/models | 别名不能证明真实窗口 |
方案一:先确认真实模型 ID 和服务端上限
先别急着删消息,查清服务端暴露的到底是什么 ID:
import requests
r = requests.get("http://localhost:8000/v1/models", timeout=10)
r.raise_for_status()
print([m["id"] for m in r.json()["data"]])
如果返回值不是 qwen/qwen3.8-27b,就按真实 ID 查文档。自部署还得检查启动参数里的 max_model_len 或 num_ctx。
模型理论上支持的长度,和当前实例实际开放的长度,可能完全不是一回事。我也不确定每套推理框架的默认值是不是最佳配置。目前没找到比直接看启动参数和服务端日志更靠谱的办法。
方案二:按最终序列计算,别拿字符数硬猜
中文字符数、JSON 字节数和 Token 数之间没有固定换算比例。工具参数里重复的字段名、转义符和枚举项,也会影响计数。
纯文本可以用模型配套的 tokenizer 统计:
import sys
from transformers import AutoTokenizer
t = AutoTokenizer.from_pretrained(sys.argv[1], trust_remote_code=True)
print(len(t.encode(sys.stdin.read(), add_special_tokens=False)))
不过,这一步仍可能漏掉聊天模板。稳一点的做法是对完整消息应用 chat template:
from transformers import AutoTokenizer
t = AutoTokenizer.from_pretrained("实际模型路径", trust_remote_code=True)
ids = t.apply_chat_template(msgs, tokenize=True, add_generation_prompt=True)
print(len(ids))
工具定义也要放进最终请求后再统计。部分 tokenizer 支持给 apply_chat_template 传入 tools,另一些需要先按服务端模板序列化。这里没法硬抄一套通用写法,否则本地数字看着挺准,请求一发还是对不上。
我一般会再留 256 Token 的工程余量:
safe_output = context_limit - input_tokens - 256
max_tokens = max(0, min(wanted_output, safe_output))
256 是我自己留的保护值,不是官方要求。模板版本变化或服务端额外插入特殊 Token 时,留点余量能少撞几次 400。
方案三:先砍占用最大的部分
别把处理顺序弄反了。我通常会先打印一张 Token 账单,哪项最大就先处理哪项。
1. 压缩工具定义
工具 description 没必要写成长篇教程,留下用途、参数约束和关键副作用就够了。
{"name":"get_order","description":"按订单号查询订单",
"parameters":{"type":"object","properties":{"id":{"type":"string"}}}}
多个工具共用的枚举和背景说明,可以挪到 system 里统一写一次。当前步骤用不到的工具就别发送,这招对 Agent 很有效。
2. 摘要旧历史,别随手删两轮
最近几轮保留原文,更早的内容压成结构化摘要:
目标:修复订单查询超时
已确认:数据库索引正常
未解决:第三方接口 P95 抖动
约束:不得修改响应字段
摘要也会占 Token。压缩后要重新计数,不能靠感觉判断“应该够了”。
3. 控制 RAG 召回量
先对重复文档去重,再按相关性分配 Token 预算。与其固定召回 10 段,不如直接限制文档总量,不能超过剩余输入预算。
rag_budget = C - S - H - T - U - P - O - safety_margin
如果 rag_budget ≤ 0,问题就不在检索。前面的 system、历史或工具已经把窗口吃完了。
4. 只在合适的时候降低 max_tokens
如果 I < C,可以按下面的上限设置:
max_tokens ≤ C - I - safety_margin
如果 I ≥ C,把 max_tokens 改成 1 也没用。输入已经越界,服务端不会开始生成。
API 聚合平台可以用于交叉核对模型目录与请求日志,但不能把超限请求“变长”。OpenRouter 和 ofox.io 都能作为排查入口;后者提供按 Model、User、API Key 查看 Token、成功失败和费用记录的后台,遇到别名问题时仍应以实际模型 ID 和对应部署限制为准。
常见问题 FAQ
Q1:为什么输入只有几千字,qwen3.8-27b 还是提示 Token 超限?
限制对象不是最后一条问题,而是套完模板后的完整请求。system、历史消息、assistant 回复、工具 Schema、RAG 文档、特殊 Token,以及 max_tokens 的输出预留,都会占用窗口。
Q2:把 max_tokens 调小一定能解决 context_length_exceeded 吗?
不一定。只有在 输入 Token < 上下文窗口,而且超限来自输出预留时,调低它才有效。输入本身已经超限,只能压缩输入。
Q3:为什么本地 Token 计数和 API usage 不一致?
常见原因是 tokenizer 版本不同、没有应用 chat template,或者服务端额外插入了特殊 Token。平台模型别名也可能指向另一套 tokenizer。
Q4:怎么确认 qwen3.8-27b 的真实上下文窗口?
先通过 /v1/models 确认 ID,再查对应服务商控制台和模型文档。自部署还要核对 max_model_len、num_ctx。目前没有可验证的统一窗口数值,不能根据“27b”推断。
Q5:为什么不报 400 了,回答却突然截断?
一般是输出碰到了 max_tokens,或者触发了服务端的生成上限。先看响应里的结束原因。输入预算还有空间时,再逐步提高输出长度。别一口气拉满,不然截断刚解决,总预算又爆了。
我的最终处理方式
现在我的流程固定为:查真实 ID,统计套完模板后的完整输入,输出各部分 Token 账单,再处理占用最大的一项。
我不会再上来就删最近两轮聊天了,那基本属于盲人摸象。
多环境联调时,我会把官方端点、OpenRouter 和 ofox.io 返回的模型 ID、错误体分别留档,但不会指望换一个网关自动解决上下文限制。最后还是得把 S + H + T + R + U + P + O 一项项算清楚。
更多推荐


所有评论(0)