OpenClaw 中 Claude 模型工具调用频繁失败的解决办法

问题描述

最近在使用 OpenClaw(开源 AI Agent 框架)时,发现了一个令人头疼的问题:Claude 模型(无论是 Sonnet 还是 Opus)在调用系统工具时频繁失败,报错 Tool XXX not found

具体表现:

  • 让 AI 搜索信息时,它会调用 WebSearch 而不是正确的 web_search
  • 抓取网页时,它会调用 WebFetch 而不是 web_fetch
  • 同样的错误在 MemorySearchSessionsList 等工具上反复出现
  • 模型会连续重试多次,每次都用错误的名字,白白消耗大量 token
  • 最终只能 fallback 到 exec + curl 曲线救国

这不是偶发问题——Sonnet 和 Opus 都会犯同样的错,几乎每个新 session 都会出现。

初步排查:不是缓存问题

一开始我怀疑是 OpenClaw 的 system prompt 缓存导致模型"记忆"了错误的工具名。

但检查后发现:AGENTS.md、TOOLS.md 等配置文件是作为 system prompt 的一部分每次完整注入的,不存在缓存错乱的问题。而且 system prompt 里已经明确列出了所有正确的工具名(全小写 + 下划线),甚至加了 ⚠️ 警告。

模型就是不听。

根因分析

Claude 模型的训练数据中,函数/工具名通常使用 PascalCase(如 WebSearchReadFile),这是 AI 工具调用的一个常见约定。

但 OpenClaw 的工具注册名使用的是 snake_case(如 web_searchweb_fetch)。

OpenClaw 内部有一个 normalizeToolName 函数,会对工具名做处理:

// dist/agents/tool-policy.js
export function normalizeToolName(name) {
    const normalized = name.trim().toLowerCase();
    return TOOL_NAME_ALIASES[normalized] ?? normalized;
}

它做了两件事:

  1. trim() + toLowerCase() — 所以 WebSearch 会变成 websearch
  2. TOOL_NAME_ALIASES 别名表

问题出在别名表:

const TOOL_NAME_ALIASES = {
    bash: "exec",
    "apply-patch": "apply_patch",
};

只有两条规则!websearch 没有映射到 web_search,所以 toLowerCase() 之后的 websearch 直接被当作最终工具名——然而并不存在这个工具。

关键差异:这不是单纯的大小写问题,而是下划线丢失的问题。

WebSearchtoLowerCase()websearchweb_search

解决方案

TOOL_NAME_ALIASES 中补全所有带下划线的工具名的无下划线变体:

const TOOL_NAME_ALIASES = {
    bash: "exec",
    "apply-patch": "apply_patch",
    // camelCase / no-underscore aliases
    // (normalizeToolName lowercases first, so WebSearch → websearch → web_search)
    agentslist: "agents_list",
    applypatch: "apply_patch",
    memoryget: "memory_get",
    memorysearch: "memory_search",
    sessionshistory: "sessions_history",
    sessionslist: "sessions_list",
    sessionssend: "sessions_send",
    sessionsspawn: "sessions_spawn",
    sessionstatus: "session_status",
    webfetch: "web_fetch",
    websearch: "web_search",
};

修改文件路径:

<openclaw_install_dir>/dist/agents/tool-policy.js

修改后重启 Gateway 即可生效:

openclaw gateway restart

原理图解

模型输出: WebSearch
    ↓ normalizeToolName()
    ↓ trim().toLowerCase()
    → "websearch"
    ↓ TOOL_NAME_ALIASES["websearch"]
    → "web_search" ✅ 找到工具,正常执行

注意事项

  1. 这个修改会在 openclaw update 后被覆盖,因为改的是 dist/ 下的编译产物。建议向 OpenClaw 官方提 PR 将其合入主线。
  1. 如果你自定义了额外的带下划线的工具,也需要在别名表里补上对应的无下划线版本。
  1. 这个问题不仅影响 Claude,理论上任何倾向于 PascalCase 命名的模型都可能遇到。

写在最后

这个 bug 的表面现象是"AI 工具调用失败",但本质是 AI 模型的命名习惯与框架的命名约定不匹配。框架已经做了 toLowerCase() 的兼容,但漏掉了下划线分隔符的差异。

一个 15 行的别名表,解决了每次对话都可能浪费几千 token 的问题。

希望这篇文章能帮到同样遇到这个问题的 OpenClaw 用户。如果你觉得有用,也欢迎去 OpenClaw GitHub 给这个改动点个 👍。


环境:OpenClaw latest · Claude Opus 4.5 / Sonnet 4.5 · macOS (Apple Silicon)