装了一堆但毫无效果——RTK 对 Hermes Agent 为什么不生效

装了一堆但毫无效果——RTK 对 Hermes Agent 为什么不生效

2026-06-05

花了三个月,装了三次,RTK (Rust Token Killer) 对 Hermes Agent 就是没效果。安装成功、插件加载、日志正常——metrics 永远是 0。

不是 RTK 不好,而是它的工作机制和 Hermes 的架构没对上。

RTK 做了什么

RTK 是一个用 Rust 写的命令行输出压缩工具。把 ls -la 替换成 rtk ls -la,输出的 token 量能降 5-10 倍:

# 原生 ls
total 60
drwxrwxr-x  3 liu liu 4096 May 12 21:33 .
-rw-rw-r--  1 liu liu 9276 May 12 21:33 init.py

# rtk ls
__pycache__/
init.py  9.1K

权限、所有者、时间、大小列全精简。在 Claude Code 上实测能节省数百万 token。

为什么对 Hermes 无效

前一个错误的猜测

我第一次安装后,RTK 完全不工作。我的第一反应是 PATH 问题——rtk 二进制不在 Hermes 能找到的路径上。查了环境变量,修正了 ~/.local/bin 路径,重启。没用。

然后怀疑是 exit code 问题——RTK 对未知命令返回 exit code 1,Hermes 可能因此不处理输出。查了文档,RTK 的处理逻辑是正确的,不是这个原因。

还有人提到 RTK 只支持 18 个命令的 rewrite。但 rtk rewrite 实际支持的命令远不止这些。

这些猜测都错了。根因比这些深得多——藏在 Hermes 源码里。

真正的根因:钩子被框架跳过

RTK 为 Hermes 提供了 rtk-hermes 插件(v1.2.0),不走 shell hook,而是通过 Hermes 的 pre_tool_call 钩子来改写命令。

安装后,插件注册成功,日志显示 56 条 registered。但注册成功 ≠ 钩子被调用。

翻 Hermes v0.15.1 源码发现,agent 主循环的所有工具调用入口,全部传了 skip_pre_tool_call_hook=True

入口 1:tool_executor.py(顺序执行)

# tool_executor.py:849、876
function_result = _ra().handle_function_call(
    ..., skip_pre_tool_call_hook=True,   # ← 永远 True
)

入口 2:agent_runtime_helpers.py(并发执行)

# agent_runtime_helpers.py:1689
return _ra().handle_function_call(
    ..., skip_pre_tool_call_hook=True,   # ← 永远 True
)

再看实际调用的地方:

# model_tools.py:928
if not skip_pre_tool_call_hook:    # ← 永远是 False
    block_message = get_pre_tool_call_block_message(...)

3 条路径的 skip_pre_tool_call_hook 全部是 True,钩子永远不会触发。

这不是插件写错了,是框架架构设计的问题。pre_tool_call 钩子在 Hermes 中的设计意图是安全阻断——检查工具是否应该被拦截。每次工具调用都跑一次阻断检查,对自动化场景没必要,所以 Hermes 在 agent 主循环的热路径上跳过了它。

rtk-hermes 把 pre_tool_call重写钩子用(原地修改 args["command"]),但这个钩子机制在 v0.15.1 不是为这个设计的。

唯一会触发钩子的路径

有一条窄路径会让钩子真的触发——agent_runtime_helpers.invoke_tool() 在调用 handle_function_call 之前单独调了一次 get_pre_tool_call_block_message

# agent_runtime_helpers.py:1617
if not pre_tool_block_checked:
    block_message = get_pre_tool_call_block_message(
        function_name, function_args, ...
    )

但只有走 invoke_tool 这个并发路径的请求才会经过这里,不是所有终端命令。

就算钩子触发了

就算 rtk-hermes 的钩子正确触发,它也只拦截 terminal 工具。read_filesearch_filesweb_searchdelegate_taskbrowser_*——全不经过 terminal。

而且 token 大头不在 terminal 输出上:

占比 RTK 能否触及
系统提示词 + memory ~40%
工具 schema 定义 ~20%
LLM 推理输出 ~25%
terminal 输出 ~10% ⚠️ 部分
其他工具输出 ~5%

即使 terminal 被压缩 90%,整体节省 ~9%。比想象中少得多。

验证方法

跑一句就能确认问题:

python3 -c "
from rtk_hermes import _metrics_snapshot
import json
m = _metrics_snapshot()
if m['attempted'] == 0 and m['missing_rtk'] == 0:
    print('❌ RTK 未工作:钩子未触发(agent 主循环跳过了 pre_tool_call)')
elif m['missing_rtk'] > 0:
    print('❌ RTK 二进制不可用:missing_rtk=' + str(m['missing_rtk']))
else:
    print(f'✅ 在工作:attempted={m[\"attempted\"]}, rewritten={m[\"rewritten\"]}')
"

如果 attempted=0missing_rtk=0,说明钩子根本没被调用,不是配置问题。

正确的 Token 节省方向

对自动化 Agent 来说,省 Token 的正确路径是:

  1. 精简 skills 和 memory — 占 40%,压缩率最高
  2. 减少工具 schema 体积 — 占 20%,可以裁剪不需要的工具
  3. 善用子任务隔离 — 子代理只加载它需要的工具,schema 和 skills 大幅减少
  4. 控制历史长度 — Hermes 自带自动上下文压缩

踩坑总结

  • RTK 对 Hermes 无效的根因不是 PATH、不是 exit code、不是命令支持数量
  • 是框架设计层面的不匹配:pre_tool_call 是为阻断设计的,不是为重写设计的
  • Hermes v0.15.1 在所有 agent 工具调用中跳过了这个钩子
  • 唯一触发路径在 invoke_tool 并发路径,但不是所有终端命令走这条路
  • 评估工具时,排查沉默故障:别停留在”看起来装好了”,要验证”钩子跑了吗”