1. openclaw 报错现场:为什么模型突然“吃不下”了
你正在用 openclaw 跑一个本地 Agent 任务,前面几轮对话都正常,突然终端里蹦出一行红字:Unhandled stop reason: model_context_window_exceeded。进程没崩,但任务卡住了,后续输入像石沉大海。这个报错的意思很直白:本次请求的 token 总量超过了模型允许的上下文窗口,openclaw 没有为这种 stop reason 写兜底逻辑,于是直接抛给了你。
它和“模型返回空”“API 超时”不是一类问题。超时是网络或服务端排队,空返回可能是权限或参数错误,而model_context_window_exceeded是实打实的“装不下”。openclaw 会把系统提示、历史消息、工具调用结果、当前用户输入全部拼成一个请求体,只要这个请求体的 token 数超过模型上限,服务端就会以这个 stop reason 结束,openclaw 收到后无法识别,就打印了 Unhandled。
适合谁看:已经在本地把 openclaw 接上某个模型通道、能正常跑通第一轮对话,但在长任务或多轮工具调用后撞上这个报错的开发者。如果你还没跑通第一轮,先解决鉴权和 base_url 的问题,这篇解决的是“跑着跑着撑爆”的问题。
我试过最没用的做法就是重装 openclaw。重装不会改变模型窗口大小,也不会改变你塞进去的历史长度,报错会原样复现。真正要动的是三处:config.toml 里的窗口参数、openclaw 的上下文管理策略、以及你走的 API 通道是否对上下文做了额外限制。
2. 先分清三种“超窗”:别把通道限制当成模型限制
很多人一看到model_context_window_exceeded就以为是模型本身窗口太小,其实要分三层来看,排查顺序也应该是从外到内。
第一层是模型真实窗口。比如你选的模型标称 128k,那它的硬上限就是 128k token,任何通道都改不了。第二层是通道侧限制。你通过统一 Key/API 通道访问模型时,通道可能对单次请求体大小、max_tokens、甚至历史消息条数做了约束,超过就提前拒绝。第三层是 openclaw 自己的组装策略。它默认可能把全部历史原样带上,不做裁剪,也不做摘要,于是历史越长,请求越大,最终撞墙。
| 层级 | 典型表现 | 排查手段 |
|---|---|---|
| 模型窗口 | 接近标称上限才报错 | 看模型文档的 context 长度 |
| 通道限制 | 远未到模型上限就报错 | 换通道对比、看返回头 |
| openclaw 组装 | 多轮后必现,清空历史就好 | 用 /compact 或调 config |
这里有个容易踩的坑:你以为把max_tokens调小就能解决,其实max_tokens控制的是“输出预留”,不控制“输入历史”。输入历史太大,照样超窗。真正要压的是输入侧。
3. TaoToken 前置:把 Key 和通道先理顺
在改 config.toml 之前,建议先把访问通道固定下来,否则你调完参数发现还是报错,分不清是参数没生效还是通道在拦。我习惯用 TaoToken 做统一入口,原因是它把模型对话、Coding Plan、API Keys 分开管理,排查时能快速确认“是通道问题还是本地配置问题”。
你需要先拿到一个可用的 Key。进入控制台创建 API Key,地址是 https://taotoken.net/api-keys ,创建后复制保存,它只显示一次。如果你要跑的是长期编码或 Agent 类任务,可以看 Coding Plan 页面 https://taotoken.net/coding-plan ,它面向的就是这种多轮、长上下文的场景。只是想先验证模型能不能正常回话,用模型对话页 https://taotoken.net/model-chat 最快。
接入文档在 https://taotoken.net/doc ,里面写了 base_url 和鉴权头的标准写法。API 根地址是 https://taotoken.net/api ,注意这个地址不带任何查询参数,直接作为 base_url 使用。官网入口是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,从官网进控制台、文档、模型对话都能找到。
注意:不要把 Key 写进会提交到 Git 的文件里。用环境变量或本地未跟踪的配置文件,这是排查阶段也要守住的习惯。
4. 可复制配置:config.toml 骨架与窗口参数
openclaw 的配置通常在项目根目录或用户配置目录下的config.toml。下面给一份可直接改的骨架,重点看[model]和[context]两段。字段名以你本地 openclaw 版本为准,如果某个键不识别,先注释掉再逐项加回。
# config.toml [model] # 统一通道的 API 根地址,不带查询参数 base_url = "https://taotoken.net/api" # 从控制台创建的 Key,建议用环境变量注入 api_key = "${TAOTOKEN_API_KEY}" # 模型名按你实际开通的填写 model = "your-model-name" # 输出预留,不要设得过大,否则挤压输入空间 max_tokens = 4096 # 请求超时,长任务适当放大 timeout_seconds = 120 [context] # 模型真实窗口,按你选的模型填,别虚报 model_context_window = 128000 # 触发压缩的阈值,留出安全余量 compact_threshold = 0.75 # 保留最近多少轮原始消息不参与压缩 keep_recent_turns = 6 # 单条工具结果的最大字符数,超长结果先截断 max_tool_result_chars = 8000 # 是否在超窗前主动压缩 auto_compact = true [agent] # 工具调用结果是否回灌历史 include_tool_results = true # 历史消息总条数上限,防止无限增长 max_history_messages = 200几个参数的实际作用要讲清楚。model_context_window是你告诉 openclaw“这个模型能吃多少”,它据此计算何时压缩。如果你填得比模型真实窗口大,openclaw 会以为还有空间,结果请求发出去被服务端拒绝,报错依旧是model_context_window_exceeded。所以这个值宁可填小一点,比如模型标称 128k,你填 120000,留出安全垫。
compact_threshold = 0.75表示当估算 token 达到窗口的 75% 时触发压缩。压缩会把较早的历史总结成一段短文本,保留最近keep_recent_turns轮原文。这样既保住近期上下文,又不让请求无限膨胀。max_tool_result_chars很关键,很多超窗不是聊天撑爆的,而是某次工具返回了一大段 JSON 或日志,直接灌进历史。截断到 8000 字符能挡掉大部分意外。
如果你不想改配置文件,openclaw 交互里通常有内置命令。/status用来看当前会话的 token 估算和窗口占用,/compact用来手动触发一次压缩。遇到报错先敲/status,看占用是不是已经贴着上限;再敲/compact,然后重试刚才的输入。这两个命令能救急,但治本还是靠上面的配置。
5. 验证请求:从 /status 到一次成功回话
改完配置后不要直接上长任务,先用最小请求验证通道和参数都生效。第一步,确认环境变量已注入:
export TAOTOKEN_API_KEY="你的Key" echo ${TAOTOKEN_API_KEY:0:6}输出前 6 位说明变量存在。第二步,用 curl 直接打一次对话接口,确认通道本身没问题。具体路径和请求体以接入文档为准,下面给的是结构示例:
curl -sS https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer ${TAOTOKEN_API_KEY}" \ -H "Content-Type: application/json" \ -d '{ "model": "your-model-name", "messages": [ {"role": "user", "content": "只回复两个字:收到"} ], "max_tokens": 32 }'如果返回里能看到正常的choices和内容,说明 Key、base_url、模型名三者都对。如果这里就报鉴权失败或模型不存在,先别碰 openclaw,把通道问题解决掉。
第三步,回到 openclaw,启动后先敲/status,确认它读到的model_context_window是你配置里的值,而不是默认值。然后发一句短消息,确认能正常回。第四步,故意构造一段长输入,比如粘贴几千字文本,再敲/status看占用变化,确认auto_compact在接近阈值时被触发。如果占用到 75% 后历史被压缩、请求继续成功,说明配置生效了。
成功的结果是:长任务跑到之前会报错的轮次,不再出现Unhandled stop reason: model_context_window_exceeded,而是自动压缩后继续。你可以在日志里看到压缩发生的记录,历史条数下降,但任务上下文没丢关键信息。
6. 本篇常见错排查:报错还在的几种可能
配置改了但没重启。openclaw 多数情况下在启动时读取 config.toml,改完不重启不生效。先重启再测。
model_context_window 填得比模型真实窗口大。这是最常见的自欺欺人。你以为填大点能用满,实际是让 openclaw 误判,压缩触发太晚,请求发出去照样被拒。填小不填大。
工具结果没截断。某次工具返回了超大结果,max_tool_result_chars没生效或设得太大,单条就把窗口吃掉。把值降到 8000 甚至 4000 试。
通道侧另有请求体上限。如果 curl 小请求正常、稍大就失败,可能是通道对单次请求体有约束。这时要控制单次输入规模,配合压缩策略,而不是硬怼。
max_tokens 设得过大。输出预留占的是同一块窗口预算,设成 32000 会挤压输入空间。按实际需要设,4096 对多数对话够用。
历史消息条数无上限。max_history_messages没设或设得过大,历史无限增长,压缩也救不回来。设一个合理上限,比如 200。
Key 或 base_url 写错导致回退到默认通道。有些人配置里 Key 写错,openclaw 回退到某个默认端点,那个端点窗口更小,于是报错。用 curl 单独验证 Key 和 base_url,排除这个可能。
排查顺序建议固定:先 curl 验证通道,再/status看窗口读数,再/compact手动压缩,最后才动 config.toml。这样每一步都有明确结论,不会来回改配置却不知道哪步起了作用。
如果你在接入或排障过程中卡住,接入文档 https://taotoken.net/doc 里有 base_url 和鉴权头的标准写法;需要新建或更换 Key 去 https://taotoken.net/api-keys ;想先确认模型本身能正常回话,用模型对话 https://taotoken.net/model-chat 最快;长期跑编码和 Agent 任务,Coding Plan https://taotoken.net/coding-plan 更合适。把通道固定下来,再回头调 openclaw 的窗口参数,model_context_window_exceeded这类报错基本就能从“必现”变成“可控”。