9.3. 技能冲突与工具调用失败需要系统性排查流程
技能冲突与工具调用失败需要系统性排查流程
预计时间:30 分钟
你需要什么:一个运行中的 Hermes Agent 实例(本地或远程均可),已注册至少两个 Skill/工具,有访问终端和日志的权限。
星期三下午,团队刚把两个 Skill 合并上线——一个负责拉取 Jira 工单,另一个负责按优先级推送到 Slack。你盯着 hermes run 的输出,只看到一连串红色的 ToolNotFound 和 InvalidParam。同事说“重启一下试试”,但重启三次之后,错误不仅没消失,反而又多了一条“技能冲突”的警告。这时你意识到:技能问题不等于单点 bug,它需要一个诊断树(decision tree),而不是盲猜。
本章会带你搭建一套可复用的排查框架,将技能冲突与工具调用失败拆成三个主轴:注册与权限、参数契约、版本兼容。读完这一章,你将能根据错误信息快速走到叶子节点,定位根因并修复,而不是在日志海里碰运气。
最终成果:一份可打印的诊断检查表,外加一次完整的排障实战——你会在自己的 Agent 上重现并解决至少一个技能调用失败的问题。
1. 诊断起点:先分清“谁报的错”
第一个也是最容易跳过的步骤,是确认错误发生在哪个阶段。技能调用失败的堆栈通常长这样:
2026-06-15T10:23:01 [ERROR] SkillMgr: tool 'jira_fetch' failed: tool not found
2026-06-15T10:23:01 [WARN] Executor: skill 'jira_fetch' raised ToolNotFoundError
2026-06-15T10:23:02 [ERROR] Memory: failed to write execution result for skill_id=...
看上去有多个报错,但根因往往只有一个。建议在终端里先快速抓取唯一错误码:
hermes run --task "获取Sprint活跃工单" 2>&1 | grep -Eo 'ToolNotFound|InvalidParam|VersionConflict|PermissionDenied' | sort | uniq -c
# 输出示例:
# 3 ToolNotFound
# 1 VersionConflict
统计出现最多的错误码,通常就是你的诊断入口。如果同时出现多种,按频率从高到低处理——上面这个例子应该先排查 ToolNotFound。
踩坑提醒
不要被“Memory: failed to write”这类下游错误带偏。记忆模块写失败往往只是受害方,真正的凶手在上一行的ToolNotFound里。
确定了入口之后,下面我们沿着三种最常见的错误类型,一棵树一棵树地走。
2. 错误类型一:工具未找到或权限不足
2.1 第一步:确认工具是否已注册
最常见的 ToolNotFound 并不是拼写错误,而是你忘记注册或者注册后没重启(或没重新加载)。先用 hermes skills 命令列出当前所有已注册技能:
hermes skills
# 激活的Skill列表(部分示例输出):
# - name: jira_fetch
# version: 1.2.0
# tools: [fetch_issues, assign_to_user]
# - name: slack_notify
# version: 0.9.0
# tools: [send_message]
如果 jira_fetch 根本不在这张表里,那就返回到注册步骤。常见的注册方式是编辑 ~/.hermes/skills/ 目录下的 YAML 文件,然后执行热加载:
curl -X POST http://localhost:8000/api/admin/skills/reload # Hermes 默认本地端口
# 预期输出:{"status":"ok","reloaded":2}
如果你刚把文件放进去,却忘了调 reload,ToolNotFound 就再正常不过了。
2.2 第二步:检查名称拼写和别名
名称匹配是大小写敏感的。假设技能定义中的工具名叫 fetch_issues,而 Agent 的提示里写成了 fetchIssues 或 fetch_issue(单数),也会抛出 ToolNotFound。你可以在调试模式下强制打印工具清单:
hermes run --debug --task "测试" 2>&1 | grep -A5 "Available tools"
# 输出示例:
# Available tools:
# - fetch_issues (description: Get issues from Jira by project key)
# - send_message (description: Send a Slack message to a channel)
对照这个列表,逐字检查 Agent 动作中调用的名字。
踩坑提醒
Hermes 的 Skill 名称和工具名称是两个层级。例如jira_fetch是技能名,其下挂载的工具才是fetch_issues。如果代理直接尝试以技能名jira_fetch作为工具名调用,也会报ToolNotFound。务必在排查时区分这两个概念。
2.3 第三步:权限与执行环境
如果工具已注册且名称无误,但错误仍是 “not found”,可能是执行权限不足。某些 Skill 会内嵌 Shell 脚本或安装系统包,这需要 Agent 进程有相应特权。一个常见的信号是日志中出现 Permission denied:
hermes run --task "更新系统包" 2>&1 | grep "Permission denied"
# 如果看到类似:
# /bin/sh: /home/user/.hermes/skills/update_packages.sh: Permission denied
解决方法是给脚本添加执行权限:
chmod +x ~/.hermes/skills/update_packages.sh
或者,如果 Skill 需要调用受保护的系统服务,确保 Agent 进程运行在具有适当权限的用户下。
诊断树小结
对于 ToolNotFound,依次检查:注册且热加载 → 名称完全匹配 → 工具名而非技能名 → 文件权限。这个顺序可以过滤掉 90% 的情况。
3. 错误类型二:参数校验失败(JSON Schema 报错)
Agent 已经调用了正确的工具,但传参不符合预期,你会在日志里看到 InvalidParam 或 JSON Schema 验证错误。
3.1 读懂 Schema 验证报错
Hermes 的 Skill 允许开发者用 JSON Schema 定义每个工具的参数。调用失败时,日志会打印类似:
ValidationError: 'jira_project_key' is a required property
Failed instance: {'project': 'HMS'}
它明确告诉你缺了什么、错在哪里。先不要急着改代码,而是找到该工具的 Schema 定义,确保与调用方的传参对齐。
3.2 用 Dry-Run 快照前置校验
Hermes 提供了 --dry-run 模式,可以只校验参数而不真正执行工具:
hermes run --task "获取HMS项目的工单" --dry-run
# 预期输出:
# [DryRun] Would call tool 'fetch_issues' with params: {'jira_project_key': 'HMS'}
# [DryRun] Validation passed.
若没通过,会直接抛 ValidationError,根本不会到达真实的 API 调用。
3.3 在 Skill 代码里做兜底
如果你自己就是 Skill 的编写者,可以内置一层 HTTP 异常捕获,把外部 API 返回的 4xx 错误转换成更友好的 Schema 提示,而不是抛一个通用的 InvalidParam。例如:
# skill_handler.py
try:
response = requests.post(url, json=params)
response.raise_for_status()
except requests.exceptions.HTTPError as e:
if e.response.status_code == 400:
# 将 API 返回的详细信息包装为友好的 SchemaViolation
raise ValueError(f"Parameter error from API: {e.response.text}") from e
raise
这样日后排查时,可以直接从 Hermes 日志定位到原始 API 的报错细节。
诊断树小结
遇到 InvalidParam:看 Schema 报错信息 → 用 --dry-run 复现 → 对照工具签名补齐必填字段或修正类型。不要跳过 “缺了哪个字段”、“类型不符” 这两个最基础的问题。
4. 错误类型三:技能版本不兼容
当一个 Skill 被更新到 2.x,而另一个 Skill 还在依赖旧版接口时,版本冲突就会以运行时错误的形式爆发。通常的表现是 VersionConflict 警告,或者某个工具的调用静默失败。
4.1 用语义化版本管理约束
Hermes 支持在 Skill 的 manifest.yaml 里声明依赖:
# jira_fetch/skill.yaml
name: jira_fetch
version: 2.0.0
dependencies:
common_utils: ">=1.5.0, <2.0.0"
如果你更新了 common_utils 到 2.0.0,但没有同步升级 jira_fetch,加载阶段就会抛出:
VersionConflict: skill 'jira_fetch' requires common_utils<2.0.0, but 2.0.0 is installed
修复思路有两个:把 jira_fetch 的依赖范围放宽;或者回滚 common_utils 到兼容版本。
4.2 快速回滚到稳定版本
Hermes 的技能目录通常受 Git 版本控制,回滚也就是一个 git checkout 的事:
cd ~/.hermes/skills/jira_fetch
git log --oneline -5
# 输出:
# d3f2a1b (HEAD) upgrade to API v2
# b9e7c44 stable: v1.2.0
# ...
git checkout b9e7c44 .
curl -X POST http://localhost:8000/api/admin/skills/reload
然后再次运行任务,观察 VersionConflict 是否消失。如果冲突解决,你就可以有余裕去规划升级路线,而不是在线上硬修。
踩坑提醒
如果你的团队多人协作开发 Skill,一定要把manifest.yaml中的版本号和依赖声明纳入 Code Review。很多时候VersionConflict就是因为一个人升级了共享模块,却没有运行hermes skills --check做一致性检查。
4.3 一致性检查命令
Hermes 自带依赖健康检查指令,可以把它集成到 CI 里:
hermes skills --check
# 如果一切正常:
# All skill dependencies are consistent.
# 否则会列出冲突对,例如:
# Conflict: jira_fetch v2.0.0 -> common_utils v2.0.0 (requires <2.0.0)
在生产部署前跑一遍这个命令,能避免大量午夜报警。
5. 回顾:你已经建立了诊断树
我们来复盘一下这个过程。面对技能调用失败,你不再慌乱地翻阅整个日志,而是先抓错误码、再按分支走下去:
- ToolNotFound → 注册与热加载 → 名称/别名核对 → 权限
- InvalidParam → 读 Schema →
--dry-run→ 补齐必填字段 - VersionConflict →
hermes skills --check→ 回滚或升级依赖
你用了大约 30 分钟,修复了一个真实问题,并产出一套可重复执行的排查清单。将来任何技能故障,都可以沿着这三条路径定位。
行动清单
- 在本机运行
hermes skills并检查是否所有技能处于激活状态。 - 用
grep统计最近一次运行日志中的错误码,选择最高频的作为入口。 - 针对
ToolNotFound,检查注册、名称和文件权限。 - 针对
InvalidParam,使用--dry-run校验参数并修正 Schema 定义。 - 针对
VersionConflict,运行hermes skills --check,必要时回滚到 Git 标记的稳定版本。
下一步
当你把技能层的问题排查清楚后,往往会发现一些故障的根源并非 Hermes 本身,而是与外部平台集成或社区生态有关。下一章 《GitHub Issues 高频问题与社区解决方案》 将带你走进真实用户踩过的坑,学习如何利用社区批量诊断那些非核心代码引发的问题。
Hermes Agent 系统设计与工程落地
关于 LearnKu