Kanban(多 profile 协作)¶
Kanban 是 Hermes 自带的任务看板:不是独立应用、不需要额外起服务。它就是 ~/.hermes/kanban.db 一个 SQLite 文件,四个 profile(爱玛士 default / 爱码士 coder / 道元 daoyuan / finance)共用同一块板——这是它们"互相派活"的物理基础。
三扇窗,同一个库¶
| 入口 | 用什么 | 适合 |
|---|---|---|
| 飞书(主入口) | 聊天里发 /kanban … |
日常建卡、看板、催卡 |
| 网页 | http://localhost:9119/kanban(hermes-dashboard 容器,一直在跑) |
看全局、拖拽、读评论线程 |
| CLI | docker compose exec hermes /opt/hermes/.venv/bin/hermes kanban … |
排障、脚本、cron |
kanban toolset 是关闭的(hermes tools list 显示 ✗ disabled)——这不影响使用:/kanban 和 CLI 走的是同一套 kanban_db,不需要模型有工具。要开(会让爱玛士多出 30+ 工具):
概念¶
| 词 | 含义 |
|---|---|
| 板 board | 默认一块 default。一个项目/工作流一块板,板之间互不干扰 |
| 卡 task | 一行数据:标题 + body + 状态 + assignee + 评论线程 + 若干次 run |
| 状态 | triage → todo → ready → running → blocked / review → done(另有 scheduled 表示等时间而非等人) |
| assignee | profile 名(coder、daoyuan、finance),不是模型名。一张卡一次只归一个身份 |
| dispatcher | 常驻在网关里的循环,默认 60 秒扫一次:回收超时 claim → todo 转 ready → 原子抢占 → spawn 一个 OS 进程 hermes -p <assignee> … chat -q work kanban task t_x |
| workspace | scratch(默认,跑完即删)/ dir:<路径> / worktree[:路径](git 分支) |
| run | 一次执行尝试。一张卡可以有多轮 run(被打回、换人重做) |
| 交接 | 靠评论线程:worker 重新被调度时会把整条评论读进上下文 |
两个容易混的邻近机制:delegate_task 是进程内 fork→join(子代理匿名、无记忆),适合"我需要个短答案才能继续";kanban 是跨进程、跨 profile、持久化的队列,适合"这活交给另一个身份慢慢干"。
日常:飞书里三行搞定¶
/kanban list # 看板
/kanban show t_7dbb550e # 一张卡的 body / 评论 / run
/kanban create 标题 --assignee coder --body "…" # 建卡(单行 body,别换行)
/kanban runs t_xxx # 每次尝试的耗时与结论
/kanban block t_xxx --reason "…" # 卡住时人工介入
/kanban unblock t_xxx # 放开重跑
/kanban create 会自动把当前飞书会话订阅到这张卡(notify+wake):卡跑完你会在这个会话里收到结果。用 CLI 建的卡不会自动订阅,要补:
hermes kanban notify-subscribe <task_id> --platform feishu --chat-id <oc_…> \
--notifier-profile default --delivery-mode notify+wake
四条硬约定¶
- 卡里必须写明产物的绝对路径。默认
scratchworkspace 跑完就删,worker 的"临时目录"不会留下来。写/opt/data/tmp/xxx.md这种。 - 报问题的人和修问题的人不是同一身份。
default(爱玛士)握着 server-monitoring / aisecretary 等 MCP,真正干活的coder有另一套。所以卡里必须自带证据(报错原文、容器名、时间戳),worker 无法借用你的上下文。 - 一个容器一个 dispatcher。别为了"让 coder 的网关起来"在
hermes容器里s6-svc -u /run/service/gateway-coder——那会和hermes-coder容器里的同名网关抢同一个 profile、同一条飞书连接,出现重复回复和gateway_state.json双写。(网关隔离本身怎么维持、出现「某 bot 完全没反应」怎么定位,见 Hermes 拓扑。) - worker 继承的是派发容器的挂载与环境变量,不是 assignee 的。这就是下一节那条约定存在的原因。
角色归角色,机制归机制¶
default(爱玛士)是总管:它决定做什么、把活拆成卡、指派给谁、卡住时定夺、最后验收。这个权威跟"哪个进程跑派发循环"无关 —— 谁建卡、谁指派、谁看板,谁就是总管。
派发执行器(工头)是另一回事:它是个 60 秒的机械循环,只做四件事 —— 回收超时 claim、把 todo 抬成 ready、原子抢占、spawn 进程。它不做任何判断。
两者必须分开看,因为它们受不同的物理约束:
总管(default / 爱玛士) |
派发执行器 | |
|---|---|---|
| 干什么 | 拆活、建卡、指派、验收、答疑 | 扫板、抢占、spawn worker |
| 在哪 | hermes 容器(爱玛士自己的网关) |
跟"工人要在哪个车间干活"走 |
| 受什么约束 | 无 | worker 是它的子进程,继承它的挂载与环境变量 |
为什么工头不在总管容器里¶
因为 worker 是 dispatcher 的 subprocess,只可能在 dispatcher 所在的容器里跑。两个容器的能力差集决定了工头该放哪:
| 能力 | hermes(总管) |
hermes-coder |
|---|---|---|
| 真邮箱配置 / token | ✅ 有 | ❌ 空卷遮蔽 |
OneDrive token(/opt/onedrive) |
✅ 有 | ❌ 无 |
gitcode 配置(/opt/gitcode-config) |
❌ 无(token 仍在 env) | ✅ 有 |
| gh / opencode / lark / Zotero / myagentdata / aisecretary skills | ✅ | ✅ |
把工头放在总管容器里,coder 的 worker 就会读到爱玛士的邮箱凭据和 OneDrive token —— 那会破掉仓库刻意维持的"邮箱仅爱玛士可用"隔离(docs/email.md)。放在 hermes-coder,worker 拿得到干活要的工具、拿不到不该拿的钥匙(代价只是 gitcode 配置目录;GITCODE_TOKEN 在两边 env 里都有)。
所以 docker-compose.yml 里除 hermes-coder 外的三个容器都设了:
这不是让 coder 当老板,是让"工头"待在正确的车间。爱玛士照旧建卡派活 —— 它建完卡,最长 60 秒后由 hermes-coder 里的循环接手。
另一条实测记录:改之前是
hermes-finance抢到了派发锁。finance/daoyuan 没有SERVER_MONITORING_MCP_URL等凭据(tests/test_hermes_server_monitoring_wiring.py刻意钉着"其它 profile 不该收到它"),于是给coder的卡会在 finance 容器里跑、MCP 展开成空 URL —— 这就是"同一张卡时好时坏"的来源。
守卫:tests/test_hermes_kanban_wiring.py(断言恰好一个容器开着派发,且是 hermes-coder)。
当前只有一个工人身份¶
工人继承的是 hermes-coder 容器,所以今天只有 coder 适合当 assignee。给 daoyuan 派活会跑在 coder 容器里,缺 PAPER_QUEUE_DAOYUAN_TOKEN / DAILYINFO_MCP_KEY 等 env(zotero 这种无 token 的网络 MCP 反而可用);给 finance 同理。
2026-11-12 决定(业主):暂时保持"单工人 = coder",不扩。 理由是当前 95% 值得排队的活都是 coder 的,为此付出隔离代价不划算。 这不是待修的缺陷,是明确的选择 —— 除非出现"必须由 daoyuan/finance 身份做、且超过 10 分钟、要留痕"的活(累计 ≥3 次),否则不要动它。 真要扩有三条路:① 凭据下沉到
~/.hermes/profiles/<p>/.env(override=True,单派发器即可派多身份;代价是凭据落在共享卷上);② 把四个 gateway 收回一个容器(最接近官方,但容器级隔离全没了);③ 新建只装工人凭据的容器(保留 boss/worker 隔离,工人间凭据互通)。
兜底¶
hermes-coder 停机时不会有自动派发。手动推一轮(注意:从哪个容器跑,worker 就落在哪个容器):
⚠️ 在
hermes-coder容器里用 CLI 建卡时,可能会打一句No gateway is running — the task will sit in 'ready'。这是误报:该探测按HERMES_HOME(这里是/opt/data根 profile)找gateway_state.json,而 coder 的网关写在profiles/coder/下。卡的创建与派发都正常(实测 60s 内被pid 558认领)。
排障¶
| 症状 | 先看什么 |
|---|---|
卡在 ready 一直不动 |
谁在派发:grep -i "dispatcher" ~/.hermes/profiles/*/logs/gateway.log 里的 holding singleton dispatcher lock / will NOT dispatch;再看 docker compose ps hermes-coder |
卡 blocked 且带 Auto-blocked |
连续 2 次 spawn 失败(failure_limit=2)触发熔断。看 hermes kanban log <id> 找真正的报错,修完 unblock |
卡 done 但产物不见了 |
用了 scratch workspace。产物必须写在 db 外面(/opt/data/…) |
| worker 说某个 MCP 连不上 | worker 落在了没有该凭据的容器。确认派发者是 hermes-coder |
| 飞书里同一个 bot 回两遍 | 有重复网关:docker compose exec hermes /command/s6-svstat /run/service/gateway-coder 应显示 down |
| 想看 worker 干了什么 | hermes kanban log <id>(工具流)/ hermes kanban show <id>(评论与 run)/ ~/.hermes/kanban/logs/<id>.log |
常用巡检:
docker compose exec hermes-coder /opt/hermes/.venv/bin/hermes kanban stats # 各状态计数 + 最老 ready 年龄
docker compose exec hermes-coder /opt/hermes/.venv/bin/hermes kanban list
docker compose exec hermes-coder /opt/hermes/.venv/bin/hermes kanban diagnostics
网页看板¶
http://localhost:9119/kanban,用户名 admin,密码是 ~/.hermes/config.yaml 里 dashboard.basic_auth 配的那个。忘了密码就重算一个 hash 填回去:
docker compose exec hermes /opt/hermes/.venv/bin/python -c \
"import sys; sys.path.insert(0,'/opt/hermes'); from plugins.dashboard_auth.basic import hash_password; print(hash_password('新密码'))"
# 把输出的 scrypt$… 写进 config.yaml 的 dashboard.basic_auth.password_hash,重启 hermes-dashboard
进阶(用熟之后)¶
- review 流:
request-review把卡挪到review,reviewer(另一个 profile)可以request-changes打回原实现者,或直接完工。默认kanban.review_dispatch: true。 - 拆解:
--triage建卡停在triage,由 specifier 补全规格后升todo;hermes kanban decompose把一张卡拆成依赖图。 - 并行编排:
hermes kanban swarm生成"并行 worker → verifier → synthesizer"的图。 - 可见性:
hermes kanban watch实时跟事件流;hermes kanban tail <id>跟单卡。
不在本文范围¶
- 事件(GitHub webhook / server-monitoring 告警 / cron)自动转成卡——需要先有一张"授权判定表"(哪些信号该开卡、哪些只播报、哪些丢弃),属于下一阶段。
- A2A(跨进程/跨机器的 Agent-to-Agent 协议):同机多 profile 不需要它,kanban 更省事。