危险操作防护体系 (Danger Ops Guard)
Overview
解决"记忆/技能存在但未被应用"的根本方案——不依赖 LLM 记得规则,而是结构性拦截/检测(确定性脚本,非概率决策)。错误按"损失发生时点"分三层,用不同机制防护。
When to Use
- 准备执行高危命令(rm/删除/覆盖/密钥写入/git push)
- 排查错误日志(WinError/锁/Cloudflare/依赖降级等已知特征)
- 删除记忆/SQLite/配置/技能等受保护资产
- 用户问"这个操作安全吗/会不会删掉重要的东西"
Don't use for:
- 记忆价值判断 →
memory-storage-management - 会话知识沉淀 →
session-knowledge-capture - 变更决策(改还是重建)→
change-decision-framework
错误分层与机制选择
L2 不可逆损失(发生即重大)→ 事前屏障(danger-check BLOCK + safe-rm 转回收)
删 SQLite 主库 / 覆盖记忆 / 泄漏密钥 / 误推 git
L1 可恢复但代价高 → 事后检测 + 回滚点(.bak/git)
L0 可轻松重跑 → 事后检测(日志特征暴露)
原则:对 L2 用事前拦截(检测到时损失已发生=无效);对 L0/L1 用事后检测(暴露即够)。
工具一:danger-check.py(命令前置检查 + 日志扫描)
位置:~/AppData/Local/hermes/scripts/danger-check.py
PY="$HOME/AppData/Local/hermes/hermes-agent/venv/Scripts/python.exe"
DANGER="$HERMES_HOME/scripts/danger-check.py"
# 检查命令(执行前调用)
"$PY" "$DANGER" rm -rf ~/AppData/Local/hermes/memories # → BLOCK exit=2
"$PY" "$DANGER" git push origin main # → BLOCK exit=2
"$PY" "$DANGER" uv pip install torch # → WARN exit=3
"$PY" "$DANGER" ls -la /tmp # → 安全 exit=0
# 扫描日志错误特征(排查时调用)
"$PY" "$DANGER" --scan-log "$HERMES_HOME/logs/errors.log"
退出码:0=安全 / 2=BLOCK(阻止)/ 3=WARN(确认后执行)
现有模式库(BLOCK/WARN)
| 模式 | 级别 | 提示 |
|---|---|---|
| rm -rf 指向受保护路径 | BLOCK | 记忆/SQLite/配置/技能/venv |
| git push | BLOCK | hermes-state-git 私有边界 |
| 密钥写入命令 | BLOCK | api_key/token/secret 值进命令 |
| rm 记忆/技能文件 | BLOCK | 不可逆 |
| chmod 777 敏感文件 | BLOCK | 凭证泄露 |
| cp/mv 覆盖关键文件 | WARN | 确认备份 |
| SQL 删记忆/日志 | WARN | 确认意图 |
| uv pip install torch | WARN | CUDA 降级风险 |
| uv pip install 非 --no-deps | WARN | pyarrow/torch 降级 |
| hermes-scope-recall upgrade | WARN | 需 doctor 全绿+备份 |
| rm *.sqlite3 | BLOCK | 运行中库禁止 |
日志特征库(B1a)
| 特征 | 根因提示 |
|---|---|
F:\f\ 或 F:\c\ |
MSYS 跨盘拼接(native 程序收 /f/) |
WinError 126 + torch |
Python ABI 不匹配(外部 Py 进程经 PYTHONPATH 加载 venv torch);先用 venv 自身 python 验证 torch 是否真坏 |
| database is locked | SQLite 锁 |
| error code: 1010 | Cloudflare UA 拦截(nightly-llm-ua.patch) |
| ModuleNotFoundError: dotenv | uv sync 进错 .venv |
| trusted non-CLI user principal | scope-desktop-principal.patch |
| did not emit a valid JSON object | doctor GBK(installer-utf8.patch) |
| 2.13.0+cpu | CUDA torch 降级 |
| lancedb unavailable fallback | ML 依赖被清(restore-ml-deps.ps1) |
| exclude-newer 过滤 | uv 时间门(--exclude-newer 绕过) |
工具二:safe-rm.py(删除转回收)
位置:~/AppData/Local/hermes/scripts/safe-rm.py
PY="$HOME/AppData/Local/hermes/hermes-agent/venv/Scripts/python.exe"
SAFERM="$HERMES_HOME/scripts/safe-rm.py"
"$PY" "$SAFERM" /tmp/temp-file.txt # 普通删除
"$PY" "$SAFERM" ~/AppData/Local/hermes/memories/x.txt # 受保护→转回收站
"$PY" "$SAFERM" --list-protected # 查看保护列表
"$PY" "$SAFERM" --force <path> # 强制永久删除(确认过)
- 回收站:
F:/AI/Hermes/trash/<日期>/(可恢复) - 受保护路径与 danger-check 一致(memories/SQLite/lancedb/config/auth/.env/skills/venv/state-git)
- MSYS 路径已处理:/tmp/x → C:/Users/.../Temp/x,/f/x → F:/x(2026-08-07 实证修复)
工具三:backup-gate.py(修改前强制备份门)
位置:~/AppData/Local/hermes/scripts/backup-gate.py
PY="$HOME/AppData/Local/hermes/hermes-agent/venv/Scripts/python.exe"
GATE="$HERMES_HOME/scripts/backup-gate.py"
"$PY" "$GATE" --check <path> # 检查是否受保护(2=受保护需先备份)
"$PY" "$GATE" <path> # 为受保护路径生成 .bak.<ts>
"$PY" "$GATE" --list-protected
- 修改 memories/config/auth/.env/skills/SQLite 前调用,自动快照
- 与 danger-check 互补:拦删除(check)、管修改(gate)
- MSYS 路径已处理(/f/x → F:/x,2026-08-07 实证)
工具四:post-check.py(高频命令后置验证)
位置:~/AppData/Local/hermes/scripts/post-check.py
PY="$HOME/AppData/Local/hermes/hermes-agent/venv/Scripts/python.exe"
PC="$HERMES_HOME/scripts/post-check.py"
"$PY" "$PC" # torch CUDA + ML 依赖 + 插件版本 + doctor 全绿
"$PY" "$PC" --quick # 跳过 doctor(快速)
"$PY" "$PC" --torch-only
- upgrade/install/装包后运行,验证:torch 未降级(2.6.0+cu124 True)、ST/lancedb 存在、插件版本、doctor 全绿
- 退出码:0=通过 / 2=有失败项
工具五:trash-clean.py(回收站定期清理)
位置:~/AppData/Local/hermes/scripts/trash-clean.py + trash-clean-wrapper.py(cron 用)
"$PY" trash-clean.py --days 30 --dry-run # 预览
"$PY" trash-clean.py --days 30 # 清理 30 天前
- cron:
trash-cleanup(每周日 4:00,no_agent)
看门狗与统计
- cron
danger-watchdog(每小时):danger-watch.py→ danger-check --watch --hours=24,命中才报 - 统计:
~/AppData/Local/hermes/logs/danger-watchdog-stats.jsonl(每次运行一行:ts/hits/exit/elapsed_s) - 查看统计:
tail -20 ~/AppData/Local/hermes/logs/danger-watchdog-stats.jsonl
工具六:Shell Hook(官方推荐,主防线)——block-hermes-assets.py
这是官方推荐的扩展方式(非源码补丁):config.yaml 的 hooks.pre_tool_call 在每次 terminal 工具调用前触发脚本,返回 {"decision":"block"} 即拦截——独立于 approval 系统,覆盖所有命令。
位置:~/AppData/Local/hermes/agent-hooks/block-hermes-assets.py
# config.yaml(已配置)
hooks:
pre_tool_call:
- matcher: terminal
command: $HERMES_HOME/hermes-agent/venv/Scripts/python.exe $HERMES_HOME/agent-hooks/block-hermes-assets.py
timeout: 5
hooks_auto_accept: true
拦截(与 danger-check 一致):
rm指向 memories/ → block(不可逆记忆丢失)rm *.sqlite3→ block(不可逆)git push→ block(本地私有边界)- 放行:trash/备份/.bak 路径(不误拦回收站清理);
safe-rm.py(转回收,可恢复)
⚠️ 正则边界(2026-08-07 修复):rm 匹配必须用 (?:^|[;&|\s])\s*rm\b 前缀限定命令位置——裸 \brm\b 会误拦 safe-rm.py(rm 前是 - 也构成词边界)。已修复并 10 项回归测试通过。
探针日志:~/AppData/Local/hermes/logs/block-hermes-assets.probe.log 记录每次 hook 调用(时间戳+命令),保留作审计。
验证命令:
hermes hooks list # 应显示 ✓ allowed
hermes hooks test pre_tool_call --payload-file <json> # payload 用 args 键(非 tool_input!)
⚠️ 关键坑(2026-08-07 实证):
- Windows 下 command 必须带 python 解释器(裸 .py 报 WinError 193)
- allowlist 按精确 command 串匹配——改 command 后必须同步更新
shell-hooks-allowlist.json - hooks test 的 payload 用
args键(_serialize_payload把 args→tool_input);--for-tool合成 payload 无 command - hermes config set 不支持 dict/list(只转 bool/int/float)——hooks 块需直接编辑 config.yaml(agent 工具层保护,需用户手动或终端脚本插入)
- consent:非 TTY 需
hooks_auto_accept: true或预写shell-hooks-allowlist.json(approvals 数组,精确 command 串)
为什么不用源码补丁(DANGEROUS_PATTERNS)?官方文档明确:
pre_tool_call是"block a tool before it reaches the approval system"的指定拦截点(hooks.md 1103 行);源码补丁升级被覆盖、且 Hermes 有官方 shell hooks 机制。2026-08-07 曾先做源码补丁(approval.py +3 正则),后按官方文档改用 Shell Hook 并回滚源码补丁。
⚠️ 已知缺口:hermes serve(桌面端)不注册 shell hooks(2026-08-07 实证 + 已补丁)
现象:桌面 app 会话(hermes_cli.main serve 进程)中执行 terminal 命令,pre_tool_call hook 从不触发(探针日志为空)。
根因:register_from_config 只在 cli.py:1047(CLI)和 gateway/run.py:10965(gateway)调用。hermes serve(桌面后端)不经过 _prepare_agent_startup 的 agent-command 守卫(_AGENT_COMMANDS = {None,"chat","acp","rl"}),且 cmd_dashboard 无显式注册——桌面会话的 hook 从未注册。
修复(补丁 serve-hooks-registration.patch):
_AGENT_COMMANDS加入"serve","dashboard"(让守卫通过)cmd_dashboard中显式调用register_from_config(serve 入口)
验证:启动 hermes_cli.main serve 测试实例 → agent.log 出现 "shell hook registered: pre_tool_call -> block-hermes-assets.py"(21:48:20 实证)。
补丁后需重启桌面 app(其 serve 进程是补丁前启动的旧进程)。升级 Hermes 会覆盖 main.py——需重打此补丁。
Common Pitfalls
- 对 L2 用事后检测 — 检测到时损失已发生;L2 必须事前拦截(BLOCK/转回收)
- 模式库不更新 — 每次发现新错误特征(如新错误模式),立即补进 danger-check.py 的 LOG_FEATURES/BLOCK_PATTERNS
- 忘记调用 — danger-check 是手动触发的;高频命令(upgrade/install)可固化进流程(如 upgrade 前先 doctor 全绿已有)
- safe-rm --force 滥用 — 仅确认过才用;受保护路径的 --force 会永久删除
- 路径判断靠 resolve() — junction(HERMES_HOME)下相对/绝对路径都能正确识别
Verification Checklist
- 高危命令执行前跑了 danger-check(BLOCK 则未执行)
- 删除受保护资产用了 safe-rm(而非裸 rm)
- 排查错误时跑了 --scan-log 检查已知特征
- 新错误特征已补入模式库
- trash 定期清理(回收站不自动过期)