Caduceus:给会自我改写的 Hermes Agent 加一层审计台
我做 Caduceus 的直接原因很简单:Hermes Agent 越用越顺手之后,我反而开始不放心。
不是不放心它某一次回答,而是不放心它在多轮对话之后留下来的东西。它会记住偏好,会沉淀工作流,会把某些经验写进 skill,也可能把旧 skill 改掉。一次对话错了,关掉重来就行;长期状态错了,它会在后面的很多次任务里继续生效。
官方文档里,Hermes 的 skill 被描述成按需加载的知识文档,存放在 ~/.hermes/skills/,并且 agent 可以修改或删除 skill。文档还单独列出 skill_manage:agent 可以用它创建、更新、删除自己的 skill,用来保存已经跑通的工作流。
参考资料:
长期状态不应只依赖信任
如果一个 agent 只是在当前窗口里回答问题,审计重点大多落在输入、输出、工具调用上。Hermes 的有趣之处在于,它不只是当前会话里的助手。
官方文档把 memory 拆成 MEMORY.md 和 USER.md 两类,前者存环境事实、项目约定、踩坑经验,后者存用户偏好和沟通习惯。这些内容在会话开始时注入系统提示词。文档也明确写到,agent 通过 memory 工具管理自己的记忆,可以 add、replace、remove。
skill 更像程序化记忆——它不是一句偏好,而是一套任务操作手册。文档把”何时该沉淀”拆成四类触发条件,其中只有一条是量化阈值:
- 量化阈值 完成一次 **5+ tool calls** 的复杂任务并成功收尾
- 撞过错误或死路、最终找到可行路径之后
- 用户纠正了它的做法之后
- 发现了一条非平凡的工作流之后
写入与修改都走 skill_manage 工具,结果统一落到 ~/.hermes/skills/:
| action | 用途 |
|---|---|
create |
从零新建 skill,需要完整 content |
patch |
局部修改 SKILL.md,按 old_string / new_string 增量替换,文档列为首选 |
edit |
整篇结构性重写,等价于完整覆盖 SKILL.md |
delete |
删除整个 skill |
write_file / remove_file |
增删 skill 目录下的辅助文件(references、scripts、templates) |
这套机制让 agent 能把经验落到磁盘、在下一次会话里复用。问题在于,能力一旦变成长期状态,就需要回答几个很朴素的问题:
- 它什么时候创建了一个 skill?
- 它为什么改了某个 skill?
- 改完之后和出厂版本差了多少?
- 某个 skill 被哪些 session 用过?
- memory 里到底记了什么,哪些会影响后续行为?
- 后台任务、cron、plugin hook 有没有在我不看的时候做过事?
这些问题靠 ls ~/.hermes 可以查,但很难连续地看。文件散在不同目录,SQLite 里有 session 和 tool call,skill 的 shipped baseline、当前版本、usage 计数又来自不同地方。Caduceus 做的就是把这些痕迹合到一个只读的审计台里。

上面这张 Overview 截图是入口页。它先不急着展示文件树,而是把总数摆出来:skill 数、session 数、token IO、Soul 大小、tasks、cron、plugins。对我来说,这页的价值不是漂亮,而是先确认这个 agent 已经积累了多少长期状态。
数据来源与读取范围
Caduceus 没有引入新的数据库,也不向 Hermes 写任何东西。它只读 ~/.hermes 下面已经存在的数据,把不同来源拼成可以跳转的视图。
graph LR
A[~/.hermes] --> B[SOUL.md]
A --> C[memories]
A --> D[skills]
A --> E[state.db]
A --> F[kanban.db]
A --> G[cron/jobs.json]
A --> H[plugins]
D --> I[Skill Grid]
E --> J[Sessions]
E --> K[Tool Calls]
K --> L[Skill Lineage]
K --> M[Plugin Usage]
F --> N[Tasks]
G --> O[Cron]
B --> P[Timeline]
C --> P
I --> P
J --> P
N --> P
O --> P
H --> P
几类数据尤其关键:
| 数据 | 作用 |
|---|---|
~/.hermes/skills/<group>/<name>/SKILL.md |
当前正在生效的 skill 内容 |
~/.hermes/hermes-agent/skills/.../SKILL.md |
bundled skill 的 shipped baseline,用来和当前版本做 diff |
~/.hermes/skills/.usage.json |
patch_count、last_patched_at、created_at 等使用痕迹 |
state.db.messages.tool_calls |
找出 skill_view、skill_manage、plugin tool call 和对应 session |
~/.hermes/memories/* |
agent 的 memory 与 user profile |
kanban.db |
后台 task、run history、失败原因 |
plugins/<name>/plugin.yaml |
plugin 暴露的 tool、hook、env 要求 |
这里有一个设计取舍:Caduceus 不试图取代 Hermes 自己的管理命令。它只是把可疑点摊开。看到 drift、patch、某个 session、某个 task crash 之后,真正的修改仍然应该回到编辑器、Hermes CLI 或版本控制里做。
Skill 视图:patch 与 drift 的双线追踪
我最先想看的就是 skill。
Hermes 的 skill 目录越用越多之后,单纯的列表不够用。Caduceus 的 Skills 页先用 domain map 让你看到分类规模,再用卡片展示每个 skill 的描述、mtime、版本、标签、patch 状态和 drift 状态。

这里的两个状态很重要:
patched:有skill_manage记录,可以关联到具体 session,通常能还原 agent 当时为什么改。drifted:当前SKILL.md和 shipped baseline 的 md5 不一致,但没有skill_manage记录。它可能是人手改的,也可能是 agent 用普通文件编辑工具改的。
第二种更值得警惕。只看 patch_count 会漏掉它,因为它没有走 skill_manage。所以 Caduceus 同时走两条线:一条看 usage 和 tool call,一条直接算 shipped/current md5。
skill_manage 留下的是结构化审计线索;普通文件编辑只留下文件差异。两种都应该看,不然会把“没有记录”误读成“没有变化”。
Skill 详情:lineage 与修订链
点开一个 skill 之后,抽屉里会把 lineage、view 次数、agent edits 摘要和完整 SKILL.md 放在一起。下面这张图里,stock-monitor 这个 skill 有 8 次 patch 计数,抽屉里还能看到 10 条 recorded edits、跨 7 个 session,以及进入 edit history 的按钮。

这块的设计目标是把 skill 的变化看成一条修订链,而不是一个孤立文件。一个 skill 被创建、被引用、被 patch、最后变成当前版本,中间每一步都应该能回到 session 里看上下文。
Timeline:跨数据源的统一审计入口
单页看 skill 还不够。很多时候我想知道的是某一天 agent 到底做了什么。
Timeline 页把 sessions、skills、tasks、cron、plugins、soul 合在一起,按时间倒序展示。每条事件后面都有 audit 入口:skill 跳 skill drawer,session 跳消息流,task 跳 run history,plugin 跳 manifest 和调用记录。

这比打开文件更接近审计流程。因为真正的问题通常不是“某个文件是什么内容”,而是“这个内容为什么变成这样”。时间线给了一个入口,drawer 给证据链。
只读约束:审计层与执行层分离
Dashboard 很容易长出管理功能:点一下恢复 shipped 版本、点一下删除 memory、点一下禁用 cron。
我暂时不想做。
审计台最怕两件事:一边看证据,一边改证据。Caduceus 的定位是本地只读,后端对 state.db 和 kanban.db 用 readonly 模式打开,读取 .env 时也只读 key 名,不读 value。/api/open 和 /api/file 会校验路径必须在 HERMES_HOME 里面。
如果要修改 skill,我宁愿在编辑器里改,或者用 Hermes 自己的命令改,再让 Caduceus 重新扫描。这样审计层和执行层分开,出了问题也更容易归因。
我更希望 Caduceus 成为一张“证据桌”,而不是另一个会改状态的控制面。
视图全景
现在的 Caduceus 已经不只是 skill viewer。它覆盖了这些视图:
| 视图 | 看什么 |
|---|---|
| Overview | 总量、分布、最近产物 |
| Soul | SOUL.md 全文 |
| Memory | ~/.hermes/memories/ 下的记忆 |
| Skills | skill 分类、patch、drift、lineage |
| Tasks | kanban 后台任务和 run history |
| Cron | 定时任务和下一次执行 |
| Plugins | manifest、tools、hooks、env 状态、调用 session |
| Sessions | 消息流和 tool calls |
| Timeline | 所有事件的统一入口 |
Plugin 详情:tools、hooks、env 与调用记录
插件不是只列一个名字就够了。点进 plugin drawer 后,Caduceus 会展示这个插件声明的 tools、hooks、env 配置状态,以及被哪些 session 调用过。env 这里刻意只显示 key 是否配置,不读取 secret value。

结语
Agent 会写代码并不稀奇。真正改变工程习惯的是,它开始写自己的操作手册,记自己的偏好,排自己的后台任务,还能通过 plugin 和 hook 接进更多系统。
这时候我们需要的不是更强的信任感,而是更便宜的回看能力。
Caduceus 就是为这个目的做的:把 ~/.hermes 里那些会长期影响 agent 行为的状态摆出来,让每一次自我改写都有地方可查。
