很多人第一次用 Hermes Agent,最容易破防的瞬间不是它答错问题,而是它明明已经想好了,结果临门一脚自己把命令刹住了。你让它清理目录,它不跑。你让它删文件,它犹豫。你让它执行危险命令,它跳审批。你再加一层自定义 Hook,结果 Hook 自己都不生效。然后不少人得出一个非常人类的结论:这东西废了。其实多数时候,它不是废了,而是安全机制正在按设计工作。Hermes 官方 FAQ 直接写明,像 rm -rf、DROP TABLE 这类潜在破坏性命令,会被识别为危险命令并触发审批;这不是 bug,而是安全特性。
先把最关键的一层说透。Hermes 里至少有两套容易被混淆的"拦截"机制。第一套是 Agent 自己对危险终端命令的审批,也就是 FAQ 里说的那类 destructive command 检测。第二套是 你自己配置的 Shell Hooks / Plugin Hooks 对工具调用的拦截,比如在 pre_tool_call 里把某些 terminal 命令拦下来。这两套机制看起来都像"命令被拦",但根因和排法完全不是一回事。你要是把系统自带安全审批和你自己写的 Hook 混成一锅,排障就会从一开始跑偏。
一、危险命令被拦,很多时候不是故障,而是 Hermes 根本就不允许它悄悄跑
Hermes 官方 FAQ 对 "Command blocked as dangerous" 这条写得很明白。它举的例子就是 rm -rf、DROP TABLE 这种潜在破坏性命令,并明确说这是一个 safety feature。官方给出的处理方式也很直接:当提示出现时,用户需要审查命令,再输入 y 批准;如果不想放行,就让 Agent 改用更安全的替代方案。更关键的一句是:Hermes never silently runs destructive commands. 也就是说,这种拦截不是它不会做,而是它故意不替你偷偷做。
这条逻辑非常重要。很多用户看到命令被拦,下意识会认为"Agent 不够智能"。实际上,真正危险的系统从来不是会停下来征求确认的系统,而是那个一声不吭就把你的目录、数据库或者配置文件狠狠干没了的系统。Hermes 在这里的设计取向其实挺清楚的:它宁可让你嫌麻烦,也不愿替你制造不可逆事故。人类平时嘴上都说要安全,真到按一下 y 的时候又嫌烦,挺一致的。
二、在消息网关里,危险命令不是靠终端里的 y,而是靠 /approve 和 /deny
如果你是在 Telegram、Discord 这类消息渠道里用 Hermes,审批机制又是另一套交互。官方 Messaging Gateway 文档明确列出了两个命令:/approve 用来批准一个待执行的危险命令,/deny 用来拒绝它。也就是说,网关场景里的审批不是你在 shell 里敲 y,而是通过聊天命令完成。很多"明明 Hermes 想执行,但最后没跑"的情况,根本不是它卡住了,而是它在消息会话里等你发 /approve。
如果你是在 Telegram 这类消息渠道里遇到审批异常,而不只是终端里被拦,那可以回看 Hermes Gateway 连上 Telegram 还是不回消息,先确认消息网关本身有没有正常工作。
这里还要注意一个现实限制。Hermes 官方 FAQ 直接写到,sudo 在 messaging gateway 里通常不工作,因为消息网关没有交互式终端,无法弹出密码提示。所以如果你在 Telegram 里要求它跑需要密码的 sudo,它不是单纯"被拦",而是根本没法完成这类交互。官方给的办法只有三个:尽量避免在消息渠道里用 sudo,让 Agent 找替代方案;如果必须用,就为特定命令配置 passwordless sudo;或者直接切回终端里的 hermes chat 做管理员操作。换句话说,很多"危险命令总被拦"的抱怨,实际上掺杂了"运行场景根本不支持交互式提权"的问题。
三、真正容易把人绕晕的,是你自己加的 Shell Hooks 也会拦,而且第一次还要单独授权
Hermes 的 Hooks 文档把这事讲得非常清楚。你可以在 pre_tool_call 里注册 Shell Hook,专门拦截工具调用,比如拦 terminal、write_file、patch。官方甚至给了现成例子,演示如何用一个 shell 脚本阻止类似 rm -rf / 这样的命令。换句话说,除了 Hermes 默认的危险命令审批外,你完全可以再叠一层自己的企业策略、团队策略或个人策略。这很强,但也很容易把人自己坑进去。
为什么会坑自己?因为 Shell Hooks 不是"写完就自动生效"。官方 Hooks 文档明确说,每一个唯一的 (event, command) 组合,在 Hermes 第一次看到时都会弹一次授权提示,并把决定持久化到 ~/.hermes/shell-hooks-allowlist.json。后续 CLI 或 Gateway 再遇到同一个 Hook,就不会重复问了。也就是说,你如果新加了 Hook,但第一次授权没过,或者压根没在交互场景里批准过,它就可能一直处于"静默未注册"状态。然后用户就会一脸茫然地问:我 Hook 明明写了,为什么没生效?答案通常是:因为你从来没真正授权它。
四、hooks_auto_accept 是把第一次 Hook 授权自动跳过,不是把危险命令审批全部关掉
这一步是很多人最容易误解的。官方 Hooks 文档写得很清楚,绕过首次交互式 Hook 授权有三种方式。第一,CLI 启动时带 --accept-hooks。第二,设置环境变量 HERMES_ACCEPT_HOOKS=1。第三,在配置里写 hooks_auto_accept: true。这三种方式的作用,都是让新的 Shell Hook 在非交互或首次运行时不用再人工点头。
但这里千万别理解错。hooks_auto_accept: true 处理的是 Shell Hook 自己的授权模型,不是把 Hermes 对危险终端命令的默认安全审批整个废掉。一个是"我信任这段 Hook 脚本以后可以运行",另一个是"Agent 现在要跑一条潜在破坏性命令,我是否批准"。这两件事不是一个开关。很多用户把 hooks_auto_accept 当成"全局放行",结果发现危险命令还是会弹审批,就开始怀疑配置没生效。其实不是没生效,而是你理解错了对象。
五、为什么 Hook 在 Gateway、cron、CI 里经常"像没装一样"?
这个问题官方文档也已经提前替你踩过坑了。Hooks 文档明确写到,非 TTY 场景,比如 Gateway、cron、CI,如果你新加了 Hook,又没有用 --accept-hooks、HERMES_ACCEPT_HOOKS=1 或 hooks_auto_accept: true 其中任一种方式提前授权,那么这个新 Hook 会静默不注册,同时在日志里打 warning。注意,是"静默不注册",不是大喊一声报错。所以很多人会觉得最诡异的地方就在这:CLI 里似乎好好的,一挂到 Gateway 就像没效果。根因不是 Hook 代码突然失忆,而是网关没法弹交互授权,于是默认没让它上岗。
这一条对长期运行的 Bot 特别重要。你在本地终端里测试 Hook 没问题,不代表挂到 Telegram 网关里它就天然可用。因为 Gateway 是长期后台进程,不适合临时弹交互批准。所以只要是准备给 Gateway、cron、CI 用的 Hook,最稳的做法就是提前选一种官方认可的自动接受方式,并在上线前跑一遍 hermes hooks doctor。官方对这个工具的说明也很实在,它会检查执行权限、allowlist 状态、mtime 漂移、JSON 输出有效性和大致执行时间。比起你肉眼猜它到底哪里没生效,这玩意靠谱得多。
六、shell-hooks-allowlist.json 不是摆设,它决定你到底批准过哪些 Hook
如果你经常改 Hook,却总觉得 Hermes 反应很怪,那有必要知道一个核心文件:~/.hermes/shell-hooks-allowlist.json。官方 Hooks 文档明确说,首次授权决定就是持久化在这里。后续 CLI 和 Gateway 都会用这份 allowlist 来判断某个 (event, command) 组合是否已经被批准。也就是说,你要排查 Hook 到底有没有被接受,这个文件不是装饰品,是证据。
还有一个很容易踩坑的细节,官方也特别提醒了:允许列表是按命令字符串匹配,不按脚本哈希匹配。 这意味着你就算改了脚本内容,只要 command 字符串没变,之前的同意仍然有效。反过来,如果你改了命令路径或命令字符串,即便脚本本身没变,也会被当成一个新 Hook,需要重新批准。这就是为什么官方才建议跑 hermes hooks doctor 去看 mtime drift,因为脚本编辑本身不会自动触发重审。安全边界在这里是偏实用主义的,不是完美法庭。
七、真正实用的排障顺序,不是情绪化地关权限,而是分清"谁在拦你"
这类问题最怕一上来就走极端。有人一烦就想把所有审批关掉,结果把网关做成裸奔;也有人什么都不敢放行,最后连自己写的 Hook 都没法跑。更靠谱的做法是先分层:
先判断是 Hermes 默认危险命令审批,还是 你自定义 Hook 的拦截。前者看终端提示或消息里的 /approve、/deny;后者看 pre_tool_call 的 hook 配置和日志。
如果是消息渠道里执行失败,再补看 是不是 sudo 场景。消息网关没有交互终端,sudo 密码输不进去,这和"命令危险被拦"不是同一个故障。
如果是 Hook 没生效,再看 是否首次授权没过,或者 Gateway / cron / CI 场景下因为没有 TTY 而静默未注册。这时优先检查 hooks_auto_accept、HERMES_ACCEPT_HOOKS=1、--accept-hooks,以及 shell-hooks-allowlist.json。
如果你已经放开了 Hook,却还是莫名其妙被拦,再回头看是不是 Hermes 自带的危险命令审批在起作用。别把两个系统的锅互相乱甩。
八、最稳的做法,不是全开,而是只对你信得过的 Hook 自动批准
官方 Hooks 文档对安全边界写得很清楚:Shell Hooks 以你的用户权限运行,信任边界跟 cron、shell alias 差不多。所以官方建议只引用你自己写过或完整审过的脚本,最好放在 ~/.hermes/agent-hooks/ 这类容易审计的路径下;如果配置是团队共享的,任何改动 hooks: 段的 PR 都应该像改 CI 一样认真审核。这个建议听起来像老生常谈,但真到 Bot 能碰终端和文件系统的时候,这就不是形式主义了。
所以,hooks_auto_accept: true 不是不能用,但更适合你已经非常清楚 Hook 来源和用途的场景。你自己的 VPS、你自己的脚本、你自己的 Bot,可以这么搞;团队共用、来源复杂、脚本经常变化的环境,就别太奔放。安全和效率本来就是跷跷板,别幻想一个配置把两头都拿满。世界没那么善良。
结语
Hermes Agent 里的"命令总被拦",绝大多数时候都不是同一个问题。有的是默认的危险命令审批,有的是消息网关里的 /approve / /deny 没处理,有的是 sudo 天生不适合在消息里跑,有的是你自己写的 Shell Hook 根本没完成首次授权,还有的是 Gateway、cron、CI 没有 TTY,导致新 Hook 静默未注册。你要是把这些全都归纳成一句"它又不让我执行",那跟把发烧、骨折和低血糖都统称为"不舒服"差不多,听着没错,修起来没用。
真正靠谱的排法就一句话:
先分清是谁在拦你,再决定是批准、改命令、换终端,还是给 Hook 正确授权。
如果你连基础 CLI 会话都还没跑稳定,就先别急着怀疑审批机制,建议先看 Hermes 装好了却不工作怎么办,把安装、模型和网关链路先理顺。
另外,如果你遇到的不是命令被拦,而是长任务做到一半突然上下文超限,可以继续看 Hermes Context Length exceeded 怎么修。
问题求助
没能解决你的问题?直接问我
如果你遇到任何技术问题无法解决,可以在这里提交求助。我会尽快查看并回复你。
支持作者
如果这篇文章帮到了你,可以支持我
扫码打赏,支持我持续更新原创排障文章。

