Caduceus — 给 Hermes Agent 写一柄"赫尔墨斯杖"
最近给自己用的 Hermes Agent 写了一个本地审计 dashboard,叫 Caduceus。两条相交的蛇 = skill 的 lineage(创建 / 补丁 / 覆写两条线交织展开),它做的事就是把那些被 agent 层层覆写的笔迹翻出来给你看。
GitHub · InfiniteZh/CaduceusCaduceus /kəˈdjuːsɪəs/ — Hermes 那柄两条蛇缠绕的赫尔墨斯杖。这个工具就是 Hermes Agent 的赫尔墨斯杖:你拿着它,就能看见 agent 在地下做了什么。

为什么要做这个
Hermes Agent 在使用过程中会自己做以下事情:
- 写新 skill、patch 旧 skill
- 把”用户偏好”和”环境约定”塞进 memory
- 自动排队 task 在你不看的时候执行
- 装载 plugin 提供新工具
这些都是长期影响 agent 行为的状态。但它们以裸 markdown / JSON / SQLite 的形式分散在 ~/.hermes/ 下,没有统一的视图。我经常想问:
- agent 创建了哪个 skill —— 在哪次 session 创建的?
- 一个 skill 被哪些 session 引用过 —— 它真的有用吗?
- session 的 tool_calls 里到底跑了什么?花了多少 token?
- agent 在我不看的时候排了什么 task?哪些跑崩了?错在哪里?
- 装了哪些 plugin?暴露了什么工具?env 配齐了吗?
- Soul 改了没?Memory 里都记了我什么?
Caduceus 把以上问题全部摊到桌面上。
启动
1 | cd hermes-dashboard |
环境变量:
| 变量 | 默认 | 说明 |
|---|---|---|
HERMES_HOME |
~/.hermes |
数据根目录 |
PORT |
5273 |
dashboard 端口 |
技术栈极简:
- 后端:Node.js + Express + better-sqlite3,零编译
- 前端:单页 HTML + Alpine.js(CDN)+ 纯手写 CSS 变量驱动主题 + ECharts + marked
- 构建:无。
node server.js即跑
视图是怎么排的
整个 dashboard 一共 8 个视图,通过左侧导航切换,每个视图都能反向跳到关联的其他视图。
graph LR
Overview[00 · Overview]
Soul[01 · Soul]
Memory[02 · Memory]
Skills[03 · Skills]
Tasks[04 · Tasks]
Plugins[05 · Plugins]
Sessions[06 · Sessions]
Timeline[07 · Timeline]
Skills <--> Sessions
Plugins <--> Sessions
Tasks --> Sessions
Timeline --> Skills
Timeline --> Sessions
Timeline --> Tasks
Timeline --> Plugins
Overview --> Skills
Overview --> Sessions
下面挑几个核心视图展开讲讲。

DOMAIN MAP + AGENT EDITS strip + SKILLS GRID 三层结构。
- DOMAIN MAP:treemap 视图,19 个分类矩形按 skill 数量大小排列,点 tile 联动下方 grid filter
- AGENT EDITS strip:两类信号合并的药丸列表
- patched 通过
skill_manage(action=edit)改的,有完整 rationale + 内容历史 - drifted SKILL.md md5 ≠ shipped md5 但没有
skill_manage记录 —— 说明 agent 是用 Write/Edit/sed 等”普通”工具直接改的文件 - patched-and-drifted 两条都中
- patched 通过
- SKILLS GRID:按 mtime 倒序的卡片列表,最近 7 天修改过的 skill 右上角有绿点
每张卡片点开是 skill drawer,里面藏着我最得意的功能 —— LINEAGE 时间轴。
来自 ~/.hermes/kanban.db 的 agent 任务队列。纯审计视角 —— 不创建、不暂停、不重跑,只回答”agent 在我不看的时候做了什么、哪次崩了、错在哪里”。
- TASKS 表:每行一个 task + 当前 status pill + 最近一次 run 的 outcome
- crashed / timed_out / failed 用斜体红色 pill —— 眼睛先抓到出错的
- task drawer:meta 行 + RUN HISTORY 表 + EVENTS 列
来自 ~/.hermes/plugins/<name>/ 的已装插件。manifest、暴露的 tools/hooks、env 状态、调用过它们的 session,全部在一处。
- TOOLS chips(sage 墨绿)+ HOOKS chips(clay 暖棕)
- ENV grid:每个 env 一格 ——
✓ configured/✕ required/○ optional - CALLED IN sessions:扫
messages.tool_calls.function.name,反向匹配本插件提供的 tool name
合并 sessions / skills / soul / skill-create / tasks / plugins 的全部修改事件,按时间倒序。
每行末尾都有一枚 → audit 主按钮,按事件类型路由到对应 drawer,把时间线变成”审计入口”而不是”只能看时间戳的 log”。
skill-create 用绿色脉冲圆点,task-crash 用红色 outline,从一堆 modified 中一眼能挑出来。

真正解决问题的那一块:Skill History
Hermes 通过
skill_manage(action=edit)自动改写 SKILL.md 时不会在文件系统留 git 痕迹,但每次改写的完整新内容、时间戳、session id、改之前 agent 的推理都被几条独立的信号同时记录下来。Caduceus 把这些信号拼起来还原一条 shipped → patch ×1 → patch ×2 → … → current 的修订链,和 git diff 一样可比对,但还附带 agent 当时的”为什么这样改”。

三栏元数据
| 栏位 | 含义 |
|---|---|
| SHIPPED | V0 基线,hermes-agent 仓库里出厂版的 SKILL.md |
| CURRENT | 当前在盘的 SKILL.md(可能与最后一次 edit 一致也可能继续被改过) |
| PATCHES | .usage.json 里 patch_count 计数 |
每栏都附 md5 前 12 位(同色即一致)和定位到 Finder 的链接。Skill 已不在盘上时显式标注 — no longer on disk。
CHAIN 时间链
纵向节点序列:V0 / #1 / #2 / NOW。每个 patch 节点附:
- timestamp + sid(点击跳到对应 session)
- rationale 折叠块:
skill_manage调用之前最近的一条 assistant 消息内容(≤1500 字符),即 agent 当时为何要做这次改写 diff base/diff target两枚 ghost 按钮,把当前节点设为对比的起点或终点
信号合流
最关键的设计决定,是认识到两条独立通道互补,缺一不可:
点开看完整的信号清单
| 信号 | 作用 | 触发方式 |
|---|---|---|
~/.hermes/hermes-agent/skills/<group>/<name>/SKILL.md |
shipped baseline(V0),出厂版原文 | — |
~/.hermes/skills/.bundled_manifest |
每个 skill 出厂内容的 md5(旁证,但实测会因 hermes-agent 升级而 stale) | — |
~/.hermes/skills/.usage.json |
patch_count / last_patched_at / created_at |
仅 通过 skill_manage API 才会自增 |
state.db.messages.tool_calls(skill_manage action=edit|create) |
完整改后内容 + 时间戳 + session_id;同 message 之前最近的 assistant 文本即 agent 当时的推理 | 仅 通过 skill_manage 调用 |
| md5(live SKILL.md) vs md5(shipped SKILL.md) | drift 检测 | 任何改文件的方式都会触发 |
~/.hermes/skills/<group>/<name>/SKILL.md |
current(在盘版本) | — |
DIFF 视图
纯客户端 LCS 行级 diff,无库依赖(3000 行/侧 上限):
- 新增 sage 绿底
- 删除 clay 红底
- 等行 淡灰前景
- 头部带
+N / −M统计

数据来源一览
dashboard 的所有展示都来自下面这几条线,没有任何外网调用,没有任何遥测。
| 视图 | 数据源 |
|---|---|
| Soul | ~/.hermes/SOUL.md |
| Memory | ~/.hermes/memories/* |
| Skills | ~/.hermes/skills/<group>/<name>/SKILL.md(递归) |
| Sessions 列表 | state.db 的 sessions 表 |
| Session 详情 | state.db.messages + ~/.hermes/sessions/session_*.json 兜底 |
| Skill 使用记录 | 扫描 messages.tool_calls + 所有 session JSON |
| Skill 修订链 | shipped baseline + state.db.messages.tool_calls 中每个 skill_manage(edit) + 紧邻的 assistant 消息 + current |
| Tasks | ~/.hermes/kanban.db(readonly) |
| Plugins | ~/.hermes/plugins/<name>/plugin.yaml + README.md |
| Plugin env status | ~/.hermes/.env 与每个插件 .env.template 的 key 名对照 |
关于主题
右上角 ◐ edition / ◑ console 切换:
- EDITION(默认):cream
#f6f6f3+ 墨黑 + sage#1e9974+ clay#b8714e。白天用,editorial 期刊的范儿 - CONSOLE:近黑
#0c0c0b+ 米白 + 同样的 sage / clay 强调。夜里看终端、做长 session 审计
切换状态写入 localStorage,ECharts 图表会重绘适配新调色板。
写到最后
写这个工具的过程,本身就成了它要解决的问题的镜像:agent 在帮我改 skill 的时候改了什么、为什么要这么改、改完之后下一次 session 又被怎么用 —— 这些是我以前只能通过肉眼比对去回溯的。因此就有了 Caduceus。
如果你也在用任何会自己改自己的 agent,欢迎抄走这个思路 —— 把所有”agent 自改痕迹”的信号通道都列出来,找出它们的互补性,然后把它们合流成一条可比对的修订链。
