sanitize 的关键词误杀:别用正则在全量回复里"抓错误"

sanitize 的关键词误杀:别用正则在全量回复里"抓错误"

OpenClaw 又被报了一个让人无语的 bug(#12309):用户问 Agent "什么是 context overflow",Agent 在 WebChat 里正常回复了一段技术解释。但同一条回复发到飞书频道时,内容被替换成了:

Context overflow: prompt too large for the model. Try again with less input or a larger-context model.

Agent 没有遇到任何错误。它在正常回答用户的技术问题。但因为回复里包含了 "context overflow" 这个关键词,被 OpenClaw 的消息后处理逻辑当成了真的错误,直接替换掉了。

同一条消息,两个渠道,两种结果

WebChat 的消息直接从 Agent 事件流传递,不经过 sanitizeUserFacingText()。外部渠道(飞书、Telegram、Slack 等)的消息走 createReplyDispatchernormalizeReplyPayloadsanitizeUserFacingText()

sanitizeUserFacingText() 里调了 isContextOverflowError()。这个函数用正则匹配文本内容,只要文本里出现 "context overflow"、"timeout"、"timed out"、"maximum context length"、"prompt is too long" 这些关键词,就判定为错误消息,然后用预设的错误提示替换原文。

问题在于:它匹配的是全文,不区分"这是错误消息"还是"这是在讨论错误消息"。

哪些关键词会被误杀

#12309 列出了容易踩雷的关键词:

关键词 正常对话中出现的概率 场景
context overflow 讨论 LLM 技术细节
timeout 讨论网络、编程
timed out 讨论网络、编程
overloaded 讨论服务器负载
rate limit 讨论 API 使用

如果你的 Agent 是用来做技术问答的——这恰恰是 OpenClaw 用户的主流场景——这些词几乎天天出现在正常对话里。

你让 Agent 解释"怎么处理 API rate limit",它回复了一段技术方案,发到 Telegram 上变成了"Rate limit exceeded, please try again later"。你让 Agent 帮你排查超时问题,回复里有 "timed out" 字样,到了飞书上变成一条错误提示。

根本问题:在文本层面做错误检测

用正则在全量回复文本里扫关键词来判断"这是不是错误消息",本身就是一个有缺陷的方案。

Agent 的输出文本和 Agent 的执行状态是两回事。Agent 可能在正常回复里提到各种"像错误"的词(因为用户问的就是技术问题),也可能在遇到真正的错误时输出看起来很正常的文本("我无法完成这个任务,但这里有一些替代方案")。

正确的做法是通过结构化的元数据判断错误,而不是扫描文本内容。

Agent 运行时已经有 stopReason 字段。如果 Agent 因为错误而停止,stopReason 会是 "error"。真正的错误应该由 formatAssistantErrorText() 处理,这个函数会先检查 stopReason === "error",确认确实出了问题,再用错误模式去匹配具体类型。

sanitizeUserFacingText() 不检查 stopReason。它对所有出站消息一视同仁地扫关键词,包括正常的回复。这就是误杀的根源。

修复方向

#12309 给出的建议很清晰:sanitizeUserFacingText() 应该只做两件事——

  1. 清除内部标签(比如 <thinking> 标签)
  2. 合并重复的文本块

不应该在这个函数里做错误模式匹配。错误检测应该交给专门的错误处理路径,由 stopReason 等结构化信号门控。

这个思路的核心是"数据面和控制面分离"。Agent 输出的文本是数据面。Agent 的执行状态(成功、失败、超时、溢出)是控制面。不要用数据面的内容去推断控制面的状态。

更广的教训:Agent 消息后处理的陷阱

这个 bug 不只是 OpenClaw 的问题。任何 Agent 系统在把模型输出分发到多个渠道时,都可能遇到类似的后处理陷阱。

陷阱一:渠道不一致。WebChat 看到的和 Telegram 看到的不一样。如果你的 Agent 接了多个渠道,一定要验证同一条消息在所有渠道上的表现。

陷阱二:后处理改变语义。后处理逻辑(格式转换、内容过滤、敏感词替换)可能改变消息的含义。一个 Markdown 表格在 Telegram 上变成乱码,一段正常回复被 sanitize 函数替换成错误提示——这些都是后处理引入的问题。

陷阱三:过度保护。出于安全考虑,把各种可能暴露内部状态的关键词都加进过滤列表。结果正常内容被大面积误杀。安全和可用性之间需要平衡。

临时绕过方案

如果你现在被这个 bug 困扰,有几个临时办法。

改用 WebChat 作为主渠道。WebChat 不走 sanitizeUserFacingText(),不会被误杀。但这违背了 OpenClaw"用你习惯的聊天工具"的初衷。

避免在对话中直接提到这些关键词。比如用"上下文超出限制"代替"context overflow",用"请求超时"代替"timed out"。这是一种荒谬的妥协,但现阶段管用。

等待修复。#12309 的修复方案很明确,大概率会在下个版本解决。已有先例——2026.2.6 已经把 isBillingErrorMessage()sanitizeUserFacingText() 里移除了(参考 PR #12226),context overflow 的检测大概率也会被移除。


参考链接

  • OpenClaw Issue #12309(sanitize 误杀):https://github.com/openclaw/openclaw/issues/12309
  • 相关修复先例 PR #12226:移除了 isBillingErrorMessage() 的文本匹配
← 返回博客列表