OpenClaw升级后卡死+死循环,用TaoToken给小龙虾配一套RepeatGuard自救配置
1. OpenClaw 升级后卡死与死循环的真实场景OpenClaw 从 4.2 升到 4.22 之后我这边最直观的变化就是飞书侧频繁卡死Agent 工具调用开始出现死循环。表现很典型——同一个run_command连续执行十几次reasoning_content每次都在变但tool_calls.arguments一模一样最后要么把调用次数耗光要么被 API 限流。你如果也在用 OpenClaw 跑 Agent 任务大概率遇到过这种“看着它在动其实原地打转”的情况。这个问题本质上不是 OpenClaw 单方面的锅。升级后提示词变长、任务复杂度上升模型在长上下文里更容易“迷糊”于是反复尝试同一个失败动作。OpenClaw 本身没有对重复工具调用做硬性拦截所以死循环会一直跑到触发外部限制为止。我试过降级、换插件、精简提示词效果都不稳定最后决定给“小龙虾”加一层 RepeatGuard让 Agent 自己识别并打断重复调用。这篇要交付的东西很具体一份可复制的config.toml/settings.json骨架、TaoToken 统一 Key 的接入配置、复现死循环的操作步骤以及验证 RepeatGuard 拦截生效的方法。适合正在用 OpenClaw 跑 Agent、被工具调用死循环卡住、想自己动手加一层守卫的开发者。核心检索词就三个OpenClaw、死循环、RepeatGuard。2. TaoToken 前置统一 Key 与接入准备在动 RepeatGuard 之前先把模型调用这条链路理顺。OpenClaw 升级后卡死有一部分原因是多插件各自持有不同的 Key请求分散、限流策略不统一排查时很难定位到底是模型侧还是工具侧的问题。我的做法是把模型调用统一收口到 TaoToken用一个 Key 管所有模型请求这样死循环复现时能清楚看到是调用次数问题还是逻辑问题。TaoToken 在这里的角色是统一模型接入层官网地址是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 入口是 https://taotoken.net/api 。你需要先去控制台创建 Key控制台地址带 deep linkhttps://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 。创建完在 API Keys 页面复制页面地址https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面写了 OpenAI 兼容格式的请求方式。OpenClaw 的工具调用走的就是 OpenAI 格式的tool_calls所以只要把 base_url 指向 TaoToken 的 API 地址Key 填进去模型侧就统一了。这一步不做后面 RepeatGuard 拦截时你分不清是模型重复调用还是网关重试导致的重复。注意Key 只放在本地配置文件或环境变量里不要写进会提交到仓库的代码。OpenClaw 的配置文件如果纳入版本管理记得把 Key 字段排除。如果你后面要长期跑编码类 Agent 任务可以了解下 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。单纯想先验证模型对话是否正常可以用模型对话页面https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentchatutm_campaignrewrite 。3. 可复制配置config.toml 与 settings.json 骨架先把 OpenClaw 的模型接入配置改好。下面这份config.toml骨架把模型请求指向 TaoToken同时保留了 OpenClaw 的工具调用开关。字段名按你本地 OpenClaw 版本对齐核心是base_url和api_key两项。# config.toml - OpenClaw 模型接入骨架 [model] provider openai-compatible base_url https://taotoken.net/api api_key sk-你的TaoTokenKey model claude-sonnet-4-20250514 max_tokens 8192 temperature 0.3 [agent] # 单轮最大工具调用轮数防止无限循环的最后一道闸 max_tool_rounds 30 # 开启工具调用 enable_tool_calls true [tools] # 失败率高的工具先关掉减少无意义调用 enable_edit_file false enable_web_search false enable_run_command true enable_read_file true enable_write_file true enable_list_dir truemax_tool_rounds是兜底RepeatGuard 是精细拦截两者不冲突。edit_file和web_search我在实测里关掉了因为这两个工具在 OpenClaw 里失败率高关掉之后调用流程顺滑很多这一点和原案例里的观察一致。然后是 RepeatGuard 的配置常量放在settings.json里方便和代码解耦{ repeatGuard: { enabled: true, blockThreshold: 5, thresholds: { run_command: 2, read_file: 2, write_file: 3, edit_file: 2, list_dir: 2, web_fetch: 2, default: 2 }, warnRole: user, blockIdPrefix: call_blocked_ } }blockThreshold设为 5意思是同一个签名重复 5 次就硬拦截跳过执行。write_file给到 3是因为写文件经常分步进行——先写头部、再补内容、最后收尾3 次能覆盖常见场景不会误杀正常的分步修改。warnRole用user因为 user 角色在对话流里权重最高模型会把它当指令而不是数据警示效果最强。签名生成用 djb2 变种把任意长度字符串压成短字符串纯函数、无副作用function simpleHash(str) { if (!str) return 0; let hash 0; for (let i 0; i str.length; i) { const char str.charCodeAt(i); hash ((hash 5) - hash) char; hash hash hash; // 转 32 位整数 } return hash.toString(16); }不同工具按不同维度生成签名避免误杀。read_file带上offset:limit分页读取不会被当成重复write_file只追踪路径允许分步写edit_file追踪oldTextHash:newTextHash不同修改内容签名不同run_command追踪完整命令文本。签名示例read_file:C:\test.txt:1:2000、run_command:echo hello、web_fetch:https://example.com。4. 复现死循环并验证 RepeatGuard 拦截生效配置就位后先主动复现一次死循环确认 RepeatGuard 真的能拦住。构造一个必然失败的命令让 Agent 反复尝试同一个动作。比如让它在不存在的路径下执行读取# 故意指向不存在的文件触发重复调用 node openclaw.js --task 读取 C:\not_exist_dir\missing.txt 并输出内容正常情况下模型会连续多次调用read_file参数完全一致。没有 RepeatGuard 时它会一直重试到max_tool_rounds或 API 限流。加上 RepeatGuard 后你会在日志里看到这样的过程第 1 次正常执行返回真实结果第 2 次触发软警告警告以 user 角色注入对话流到第 5 次触发硬拦截block: true跳过执行并返回拦截提示。核心检查函数checkRepeat的调用位置在assistant.js的工具执行循环里// 1. 先保存工具调用请求 this.memory.push({ role: assistant, content: null, tool_calls: [toolCall] }); // 2. 调用守卫检查 const repeatAlert checkRepeat(guardMap, toolCall); if (repeatAlert) { // 3. 保存警告到 memory this.memory.push({ role: user, content: repeatAlert.warning }); repeatWarnings.push(repeatAlert.warning); if (repeatAlert.block) { // 4. 硬拦截跳过执行 results.push(repeatAlert.blockMessage); this.memory.push({ role: tool, tool_call_id: call_blocked_ Date.now(), content: repeatAlert.blockMessage }); continue; } } // 5. 正常执行 const result await this.executeToolCall(toolCall); results.push(result); this.memory.push({ role: tool, tool_call_id: toolCall.id, content: result });guardMap的生命周期很关键每个 turn 开始时创建新的 Map循环内每次工具调用前调用checkRepeatturn 结束后自动回收。这样跨 turn 的相同调用不会被误判为重复只有同一个 turn 内的重复才计数。验证成功的标志有三个日志里出现软警告文本第 5 次调用被跳过tool结果里是拦截提示而不是真实执行结果整个任务在有限轮数内结束没有触发 API 限流。行为对照表如下重复次数行为tool 结果user 警告1 次正常执行真实结果无2~4 次正常执行真实结果有警告≥5 次硬拦截拦截提示有警告硬拦截后仍然要返回tool结果这是 OpenAI API 规范要求每个tool_call必须有对应的tool结果否则对话流断裂模型会报错。tool_call_id用call_blocked_前缀加时间戳生成因为拦截时没有真实的toolCall.id用时间戳保证唯一避免冲突。5. 本篇常见错排查拦截不生效Agent 还是死循环。先确认checkRepeat是否真的被调用。常见原因是guardMap在循环外被复用或者每个 turn 没有重新创建 Map导致计数被重置。检查assistant.js里guardMap的创建位置必须在 turn 开始时new Map()。正常分步写文件被误拦。说明write_file阈值设低了。分步写头部、内容、尾部是三次不同调用如果阈值是 2 就会误杀。把write_file调到 3 或更高或者确认签名只追踪路径而不是内容。软警告注入了但模型不换策略。检查warnRole是不是user。如果注入成system或assistant权重不够模型可能忽略。user 角色在对话流里权重最高这是设计上的关键点。硬拦截后对话流报错。大概率是拦截时没有返回tool结果。OpenAI 格式要求每个tool_call都有对应的tool消息continue之前必须把拦截提示 push 进 memorytool_call_id用生成的唯一 ID。TaoToken 侧返回 401 或 403。检查config.toml里的api_key是否完整复制有没有多余空格。base_url 必须是https://taotoken.net/api不要带路径后缀。如果还是不通去接入文档对照请求格式https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。调用轮数还是被耗尽。说明max_tool_rounds设太大或者 RepeatGuard 的blockThreshold设太高。两者配合使用RepeatGuard 负责精细拦截重复调用max_tool_rounds负责兜底。建议blockThreshold保持 5max_tool_rounds控制在 30 以内。6. 让小龙虾自己修好自己接入与验证入口RepeatGuard 加完之后我这边最明显的变化是Agent 遇到失败命令时不再无限重试第 5 次被拦下后模型会收到 user 角色的警告开始换策略。之前那种连续十几次相同tool_calls.arguments的情况基本消失调用次数和 API 限流压力都降下来了。这套配置的核心思路是——不指望模型永远不犯错而是给它一个能自己发现“我在重复”的机制。如果你还在调模型接入这一层先去 API Keys 页面把 Key 建好https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。接入格式对照文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。想先单独验证模型对话是否正常用模型对话页面https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentchatutm_campaignrewrite 。长期跑编码类 Agent 任务的话Coding Plan 在 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。最后留一个我踩过的坑guardMap千万别做成全局单例。我一开始图省事把它挂在模块顶层结果跨 turn 的相同调用被累计计数正常任务跑几轮就被误拦。改成每个 turn 新建 Map 之后拦截精准度立刻正常了。这个细节比阈值调参更影响实际效果。