ByteNoteByteNote

字节笔记本

2026年6月21日

hermes教程-看板(多智能体看板)

API中转
¥120

Kanban — 多智能体看板协作

想要一个演练? 阅读 看板教程 —— 四个用户故事(独立开发者、批量任务、带重试的角色流水线、断路器),每个都配有仪表盘截图。本页是参考文档;教程是叙述性内容。

Hermes 看板是一个持久化的任务板,在所有 Hermes 配置文件中共享,允许多个命名智能体协作处理工作,而无需使用脆弱的进程内子智能体集群。每个任务都是 ~/.hermes/kanban.db 中的一行;每次交接都是任何人都可以读取和写入的一行;每个工作进程都是一个完整的操作系统进程,拥有自己的身份。

两个界面:模型通过工具交互,你通过 CLI 交互

看板有两个前端入口,都基于同一个 ~/.hermes/kanban.db

  • 智能体通过专用的 kanban_* 工具集驱动看板 —— kanban_showkanban_listkanban_completekanban_blockkanban_heartbeatkanban_commentkanban_createkanban_linkkanban_unblock。调度器在生成每个工作进程时,这些工具已经包含在其模式中;编排器配置文件也可以显式启用 kanban 工具集。模型通过直接调用工具来读取和路由任务,而不是通过 shell 执行 hermes kanban。请参阅下面的 工作进程如何与看板交互
  • 你(以及脚本和 cron)通过 CLI 上的 hermes kanban …、作为斜杠命令的 /kanban … 或仪表盘来驱动看板。 这些是为人类和自动化准备的 —— 那些没有工具调用模型支持的地方。

两个界面都通过同一个 kanban_db 层路由,因此读取看到一致的视图,写入不会漂移。本页其余部分展示 CLI 示例,因为它们易于复制粘贴,但每个 CLI 动词都有一个模型使用的工具调用等效项。

这是 delegate_task 无法处理的工作负载的形状:

  • 研究分类 —— 并行研究人员 + 分析师 + 写作者,人机协同。
  • 定时操作 —— 每日简报,持续数周构建日志。
  • 数字孪生 —— 持久的命名助手(inbox-triageops-review),随时间积累记忆。
  • 工程流水线 —— 分解 → 在并行工作树中实现 → 审查 → 迭代 → PR。
  • 批量任务 —— 一个专家管理 N 个主题(50 个社交账号、12 个监控服务)。

有关完整的设计原理、与 Cline Kanban / Paperclip / NanoClaw / Google Gemini Enterprise 的比较分析,以及八种规范协作模式,请参阅仓库中的 docs/hermes-kanban-v1-spec.pdf

Kanban 与 delegate_task

它们看起来很相似,但并非同一原语。

delegate_taskKanban
形状RPC 调用(分叉 → 合并)持久化消息队列 + 状态机
父进程阻塞直到子进程返回创建后即忘
子进程身份匿名子智能体具有持久内存的命名配置文件
可恢复性无 —— 失败即失败阻塞 → 解除阻塞 → 重新运行;崩溃 → 回收
人机协同不支持随时评论/解除阻塞
每个任务智能体数一次调用 = 一个子智能体任务生命周期内的 N 个智能体(重试、审查、后续)
审计轨迹上下文压缩后丢失SQLite 中的持久行,永久保存
协调层级(调用者 → 被调用者)对等 —— 任何配置文件都可以读取/写入任何任务

一句话区分: delegate_task 是一个函数调用;Kanban 是一个工作队列,其中每次交接都是一行,任何配置文件(或人类)都可以看到和编辑。

父智能体需要在继续之前获得一个简短推理答案,不涉及人类,结果返回父智能体上下文时,使用 delegate_task

工作跨越智能体边界、需要在重启后存活、可能需要人类输入、可能被不同角色接手、或需要在事后可发现时,使用 Kanban

它们可以共存:一个看板工作进程可以在其运行期间内部调用 delegate_task

核心概念

  • 看板 —— 一个独立的任务队列,拥有自己的 SQLite 数据库、工作空间目录和调度器循环。单个安装可以有多个看板(例如,每个项目、仓库或域一个);请参阅下面的 看板(多项目)。单项目用户保留在 default 看板上,除了本文档部分外,永远不会看到“看板”这个词。
  • 任务 —— 一行,包含标题、可选正文、一个负责人(配置文件名称)、状态(triage | todo | ready | running | blocked | done | archived)、可选租户命名空间、可选幂等键(用于重试自动化的去重)。
  • 链接 —— task_links 行,记录父 → 子依赖关系。当所有父任务都完成时,调度器将 todo → ready 提升。
  • 评论 —— 智能体间协议。智能体和人类追加评论;当工作进程被(重新)生成时,它会读取完整的评论线程作为其上下文的一部分。
  • 工作空间 —— 工作进程操作的目录。三种类型:
    • scratch(默认)—— 在 ~/.hermes/kanban/workspaces/<id>/ 下的临时目录(非默认看板在 ~/.hermes/kanban/boards/<slug>/workspaces/<id>/)。任务完成时删除 —— scratch 是设计为临时的,因此一旦工作进程(或 hermes kanban complete <id>)将任务标记为完成,该目录就会被清除。如果你想保留工作进程的输出,请改用 worktree:dir:<path>。当第一次在安装中创建 scratch 工作空间时,调度器会记录一条警告,并在任务上发出 tip_scratch_workspace 事件(可通过 hermes kanban show <id> 查看)。
    • dir:<path> —— 一个现有的共享目录(Obsidian 仓库、邮件操作目录、每个账户的文件夹)。必须是绝对路径。dir:../tenants/foo/ 这样的相对路径在调度时会被拒绝,因为它们会相对于调度器所在的 CWD 解析,这是模糊的,并且是一个混淆代理的逃逸向量。路径本身是受信任的 —— 这是你的机器,你的文件系统,工作进程以你的 UID 运行。这是受信任的本地用户威胁模型;看板设计为单主机。完成时保留。
    • worktree —— 一个 git 工作树,位于 .worktrees/<id>/ 下,用于编码任务。使用 worktree:<path> 来固定确切的目标路径。工作进程端 git worktree add 创建它,如果提供了 --branch 则使用。完成时保留。
  • 调度器 —— 一个长时间运行的循环,每 N 秒(默认 60)执行:回收过期的声明、回收崩溃的工作进程(PID 消失但 TTL 尚未过期)、提升就绪任务、原子声明、生成分配的配置文件。默认在网关内部运行(kanban.dispatch_in_gateway: true)。一个调度器每次滴答扫描所有看板;工作进程生成时带有 HERMES_KANBAN_BOARD 环境变量,因此它们无法看到其他看板。在同一个任务上连续 kanban.failure_limit 次生成失败后(默认:2),调度器会自动阻塞该任务,并将最后一个错误作为原因 —— 防止在配置文件不存在、工作空间无法挂载等任务上反复尝试。
  • 租户 —— 看板内部的可选字符串命名空间。一个专家集群可以通过工作空间路径和内存键前缀为多个业务提供服务(--tenant business-a),实现数据隔离。租户是一个软过滤器;看板是硬隔离边界。

看板(多项目)

看板允许你将不相关的工作流分离 —— 每个项目、仓库或域一个 —— 放入隔离的队列。新安装只有一个名为 default 的看板(为向后兼容,数据库位于 ~/.hermes/kanban.db)。只需要一个工作流的用户永远不需要知道看板的存在;该功能是可选加入的。

每个看板的隔离是绝对的:

  • 每个看板有独立的 SQLite 数据库(~/.hermes/kanban/boards/<slug>/kanban.db)。
  • 独立的 workspaces/logs/ 目录。
  • 为任务生成的工作进程只能看到其看板的任务 —— 调度器在子进程环境中设置 HERMES_KANBAN_BOARD,并且工作进程有权访问的每个 kanban_* 工具都会读取它。
  • 不允许跨看板链接任务(保持模式简单;如果你确实需要跨项目引用,请使用自由文本提及并手动按 ID 查找)。

从 CLI 管理看板

bash
## 查看磁盘上的看板。新安装只显示 "default"。
hermes kanban boards list
## 创建一个新看板。
hermes kanban boards create atm10-server \
    --name "ATM10 Server" \
    --description "Minecraft modded server ops" \
    --icon 🎮 \
    --switch                   # 可选:将其设为活动看板
## 在不切换的情况下操作特定看板。
hermes kanban --board atm10-server list
hermes kanban --board atm10-server create "Restart ATM server" --assignee ops
## 更改后续调用的“当前”看板。
hermes kanban boards switch atm10-server
hermes kanban boards show             # 当前哪个看板是活动的?
## 重命名显示名称(slug 是不可变的 —— 它是目录名)。
hermes kanban boards rename atm10-server "ATM10 (Prod)"
## 归档(默认)—— 将看板目录移动到 boards/_archived/<slug>-<ts>/。
## 可以通过将目录移回来恢复。
hermes kanban boards rm atm10-server
## 硬删除 —— `rm -rf` 看板目录。无法恢复。
hermes kanban boards rm atm10-server --delete

看板解析顺序(优先级从高到低):

  1. CLI 调用中的显式 --board <slug>
  2. HERMES_KANBAN_BOARD 环境变量(调度器在生成工作进程时设置,因此工作进程无法看到其他看板)。
  3. ~/.hermes/kanban/current —— 由 hermes kanban boards switch 持久化的 slug。
  4. default

Slug 经过验证:小写字母数字 + 连字符 + 下划线,1-64 个字符,必须以字母数字开头。大写输入会自动转换为小写。其他任何内容(斜杠、空格、点、..)都会在 CLI 层被拒绝,因此路径遍历技巧无法命名看板。

从仪表盘管理看板

hermes dashboard → Kanban 标签页在存在多个看板(或任何看板有任务)时,顶部会显示一个看板切换器。单看板用户只看到一个小的 + New board 按钮;切换器在需要时才会显示。

  • 看板下拉菜单 —— 选择活动看板。你的选择会保存到浏览器的 localStorage,因此在重新加载时保持不变,而不会在你打开的终端下移动 CLI 的 current 指针。
  • + New board —— 打开一个模态框,要求输入 slug、显示名称、描述和图标。可以选择自动切换到新看板。
  • Archive —— 仅在非 default 看板上显示。确认后,将看板目录移动到 boards/_archived/

所有仪表盘 API 端点都接受 ?board=<slug> 用于看板范围限定。事件 WebSocket 在连接时固定到一个看板;在 UI 中切换会打开一个针对新看板的新 WS。

文件附件

任务可以携带文件附件 —— PDF、图像、源文档 —— 这样工作进程就拥有所需的源材料,而无需你将路径粘贴到正文中并希望它能找到它们。

  • 上传 —— 在仪表盘抽屉中打开一个任务,使用 Attachments 部分的 Upload file 按钮(可以一次上传多个文件)。每个上传限制为 25 MB。
  • 存储 —— 文件存放在 <hermes-home>/kanban/attachments/<task_id>/(默认看板)或 <hermes-home>/kanban/boards/<slug>/attachments/<task_id>/(命名看板)。设置 HERMES_KANBAN_ATTACHMENTS_ROOT 来指定自定义位置。
  • 工作进程看到的内容 —— 当调度器将任务交给工作进程时,工作进程的上下文包含一个 Attachments 部分,列出每个文件的名称及其绝对路径。工作进程拥有完整的文件/终端工具访问权限,因此它可以直接读取附件(read_file,或像 pdftotext 这样的 shell 工具)。
  • 下载/删除 —— 抽屉列出每个附件,带有下载链接和删除(×)控件。删除附件会同时删除元数据行和磁盘上的文件。

注意 —— 远程终端后端

附件路径直接在本地终端后端解析,这是 Kanban 工作进程的默认设置。如果你在远程后端(Docker、Modal)上运行工作进程,请将看板的 attachments/ 目录挂载到沙箱中,以便工作进程上下文中的绝对路径是可访问的。

快速开始

下面的命令是(人类)设置看板并创建任务。一旦任务被分配,调度器会生成分配的配置文件作为工作进程,然后模型通过 kanban_* 工具调用驱动任务,而不是 CLI 命令 —— 请参阅 工作进程如何与看板交互

bash
## 1. 创建看板(你)
hermes kanban init
## 2. 启动网关(托管嵌入式调度器)
hermes gateway start
## 3. 创建任务(你 —— 或通过 kanban_create 的编排器智能体)
hermes kanban create "research AI funding landscape" --assignee researcher
## 4. 实时查看活动(你)
hermes kanban watch
## 5. 查看看板(你)
hermes kanban list
hermes kanban stats

当调度器拾取 t_abcd 并生成 researcher 配置文件时,该工作进程模型做的第一件事就是调用 kanban_show() 来读取其任务。它不会运行 hermes kanban show t_abcd

网关嵌入式调度器(默认)

调度器在网关进程内运行。无需安装,无需管理单独的服务 —— 如果网关启动,就绪任务会在下一个滴答(默认 60 秒)被拾取。

yaml
## config.yaml
kanban:
  dispatch_in_gateway: true        # 默认
  dispatch_interval_seconds: 60    # 默认

在运行时通过 HERMES_KANBAN_DISPATCH_IN_GATEWAY=0 覆盖配置标志以进行调试。标准网关监督适用:直接运行 hermes gateway start,或将网关作为 systemd 用户单元连接(请参阅网关文档)。如果没有运行中的网关,ready 任务将保持原样,直到网关启动 —— hermes kanban create 在创建时会发出警告。

hermes kanban daemon 作为独立进程运行是已弃用的;请使用网关。如果你确实无法运行网关(无头主机策略禁止长时间运行的服务等),--force 逃生口会在一个发布周期内保持旧的独立守护进程存活,但同时运行网关嵌入式调度器和独立守护进程针对同一个 kanban.db 会导致声明竞争,不受支持。

幂等创建(用于自动化/Webhook)

bash
## 第一次调用创建任务。任何后续使用相同键的调用
## 返回现有任务 ID,而不是重复创建。
hermes kanban create "nightly ops review" \
    --assignee ops \
    --idempotency-key "nightly-ops-$(date -u +%Y-%m-%d)" \
    --json

批量 CLI 动词

所有生命周期动词都接受多个 ID,因此你可以一次清理一批:

bash
hermes kanban complete t_abc t_def t_hij --result "batch wrap"
hermes kanban archive  t_abc t_def t_hij
hermes kanban unblock  t_abc t_def
hermes kanban block    t_abc "need input" --ids t_def t_hij

工作进程如何与看板交互

工作进程不会通过 shell 执行 hermes kanban 当调度器生成工作进程时,它会在子进程环境中设置 HERMES_KANBAN_TASK=t_abcd,并且该环境变量会在模型的模式中启用一个专用的 kanban 工具集。同样的工具集也可用于在其工具集配置中启用 kanban 的编排器配置文件。这些工具通过 Python kanban_db 层直接读取和修改看板,与 CLI 相同。运行中的工作进程像调用任何其他工具一样调用这些工具;它永远不会看到或需要 hermes kanban CLI。

工具用途必需参数
kanban_show读取当前任务(标题、正文、先前尝试、父任务交接、评论、完整的预格式化 worker_context)。默认为环境中的任务 ID。
kanban_list列出任务摘要,支持按 assigneestatustenant、归档可见性和数量过滤。用于编排器发现看板工作。
kanban_complete完成,附带 summary + metadata 结构化交接。至少 summary / result 之一
kanban_block升级为需要人类输入,附带 reasonreason
kanban_heartbeat在长时间操作期间发送存活信号。纯副作用。
kanban_comment向任务线程追加持久化注释。task_idbody
kanban_create(编排器)扇出到子任务,附带 assignee、可选的 parentsskills 等。titleassignee
kanban_link(编排器)事后添加 parent_id → child_id 依赖边。parent_idchild_id
kanban_unblock(编排器)将阻塞的任务移回 readytask_id

一个典型的工作进程轮次如下:

text
## 模型的工具调用,按顺序:
kanban_show()                                     # 无参数 —— 使用 HERMES_KANBAN_TASK
## (模型读取返回的 worker_context,通过终端/文件工具完成工作)
kanban_heartbeat(note="halfway through — 4 of 8 files transformed")
## (更多工作)
kanban_complete(
    summary="migrated limiter.py to token-bucket; added 14 tests, all pass",
    metadata={"changed_files": ["limiter.py", "tests/test_limiter.py"], "tests_run": 14},
)

一个编排器工作进程则进行扇出:

text
kanban_show()
kanban_create(
    title="research ICP funding 2024-2026",
    assignee="researcher-a",
    body="focus on seed + series A, North America, AI-adjacent",
)
## → returns {"task_id": "t_r1", ...}
kanban_create(title="research ICP funding — EU angle", assignee="researcher-b", body="…")
## → returns {"task_id": "t_r2", ...}
kanban_create(
    title="synthesize findings into launch brief",
    assignee="writer",
    parents=["t_r1", "t_r2"],                     # 当两者都完成时提升为 ready
    body="one-pager, 300 words, neutral tone",
)
kanban_complete(summary="decomposed into 2 research tasks + 1 writer; linked dependencies")

“(编排器)”工具 —— kanban_listkanban_createkanban_linkkanban_unblock 和针对外部任务的 kanban_comment —— 通过同一工具集可用;约定(由 kanban-orchestrator 技能强制执行)是工作进程配置文件不扇出或路由不相关的工作,编排器配置文件不执行实现工作。调度器生成的工作进程在破坏性生命周期操作上仍受任务范围限制,不能修改不相关的任务。

为什么使用工具而不是 shell 执行 hermes kanban

三个原因:

  1. 后端可移植性。 终端工具指向远程后端(Docker / Modal / Singularity / SSH)的工作进程会在容器内部运行 hermes kanban complete,而容器中可能没有安装 hermes,也没有挂载 ~/.hermes/kanban.db。Kanban 工具在智能体自己的 Python 进程中运行,无论终端后端如何,始终能访问 ~/.hermes/kanban.db
  2. 没有 shell 引号脆弱性。 通过 shlex + argparse 传递 --metadata '{"files": [...]}' 是一个潜在的陷阱。结构化工具参数完全避免了这一点。
  3. 更好的错误信息。 工具结果是结构化的 JSON,模型可以推理,而不是需要解析的 stderr 字符串。

在普通会话中零模式占用。 一个常规的 hermes chat 会话在其模式中没有任何 kanban_* 工具,除非活动配置文件显式启用了 kanban 工具集用于编排工作。调度器生成的任务工作进程会获得任务范围工具,因为设置了 HERMES_KANBAN_TASK;编排器配置文件通过配置获得更广泛的路由表面。对于从不接触看板的用户,没有工具膨胀。

kanban-workerkanban-orchestrator 技能教会模型在何时以及按什么顺序调用哪个工具。

推荐的交接证据

kanban_complete(summary=..., metadata={...}) 有意保持灵活:summary 是人类可读的结案报告,而 metadata 是机器可读的交接信息,下游智能体、审查者或仪表盘可以重用,而无需从文本中提取。

对于工程和审查任务,推荐使用以下可选的 metadata 结构:

json
{
  "changed_files": ["path/to/file.py"],
  "verification": ["pytest tests/hermes_cli/test_kanban_db.py -q"],
  "dependencies": ["parent task id or external issue, if any"],
  "blocked_reason": null,
  "retry_notes": "what failed before, if this was a retry",
  "residual_risk": ["what was not tested or still needs human review"]
}

这些键是约定,而不是模式要求。有用的属性是每个工作进程都留下足够的证据,让下一个读者能快速回答四个问题:

  1. 什么改变了?
  2. 如何验证的?
  3. 如果失败,什么可以解除阻塞或重试?
  4. 还有哪些风险故意保持开放?

不要在 metadata 中包含机密、原始日志、令牌、OAuth 材料和不相关的记录。请存储指针和摘要。如果任务没有文件或测试,请在 summary 中明确说明,并使用 metadata 记录确实存在的证据,例如源 URL、问题 ID 或手动审查步骤。

工作进程技能

任何应该能够处理看板任务的配置文件都必须加载 kanban-worker 技能。它教会工作进程完整的生命周期,使用工具调用,而不是 CLI 命令:

  1. 生成时,调用 kanban_show() 读取标题 + 正文 + 父任务交接 + 先前尝试 + 完整评论线程。
  2. cd $HERMES_KANBAN_WORKSPACE(通过终端工具)并在那里工作。
  3. 在长时间操作期间,每隔几分钟调用 kanban_heartbeat(note="...")如果你的工作可能运行超过 1 小时,请至少每小时调用一次 kanban_heartbeat —— 调度器会回收那些运行时间超过 kanban.dispatch_stale_timeout_seconds(默认 4 小时)且在过去一小时内没有心跳的任务,假设工作进程崩溃且未清理。回收是无害的(任务回到 ready 状态重新调度,不会增加失败计数器),但你会丢失当前运行的进度。
  4. 使用 kanban_complete(summary="...", metadata={...}) 完成,或如果卡住则使用 kanban_block(reason="...")

最后的 kanban_complete / kanban_block 调用是工作进程协议的一部分。如果工作进程以状态 0 退出,而任务仍处于 running 状态,调度器会将其视为协议违规,发出 protocol_violation 事件,并在下一个滴答时自动阻塞任务,而不是将其重新生成到同一循环中。这通常意味着模型编写了纯文本答案并在未使用 Kanban 工具表面的情况下退出。

kanban-worker 是一个捆绑技能,在安装和更新时同步到每个配置文件中 —— 没有单独的技能中心安装步骤。请验证它在你用于看板工作进程的配置文件中存在(researcherwriterops 等):

bash
hermes -p <your-worker-profile> skills list | grep kanban-worker

如果捆绑副本丢失,请为该配置文件恢复:

bash
hermes -p <your-worker-profile> skills reset kanban-worker --restore

调度器在生成每个工作进程时也会自动传递 --skills kanban-worker,因此即使配置文件的默认技能配置不包含它,工作进程也始终拥有模式库。

为特定任务固定额外技能

有时单个任务需要专家上下文,而负责人配置文件默认不携带 —— 需要 translation 技能的翻译工作、需要 github-code-review 的审查任务、需要 security-pr-audit 的安全审计。与其每次编辑负责人的配置文件,不如将技能直接附加到任务上。

从编排器智能体(常见情况 —— 一个智能体将工作路由给另一个),使用 kanban_create 工具的 skills 数组:

text
kanban_create(
    title="translate README to Japanese",
    assignee="linguist",
    skills=["translation"],
)

kanban_create(
    title="audit auth flow",
    assignee="reviewer",
    skills=["security-pr-audit", "github-code-review"],
)

从人类(CLI / 斜杠命令),每个技能重复 --skill

bash
hermes kanban create "translate README to Japanese" \
    --assignee linguist \
    --skill translation

hermes kanban create "audit auth flow" \
    --assignee reviewer \
    --skill security-pr-audit \
    --skill github-code-review

从仪表盘,在行内创建表单的 skills 字段中以逗号分隔输入技能。

这些技能是附加到内置的 kanban-worker 上的 —— 调度器为每个技能(以及内置技能)发出一个 --skills <name> 标志,因此工作进程生成时加载了所有这些技能。技能名称必须与负责人配置文件上实际安装的技能匹配(运行 hermes skills list 查看可用技能);没有运行时安装。

目标模式卡片(--goal

默认情况下,每个工作进程有一次机会处理其卡片 —— 完成工作,调用 kanban_complete/kanban_block,退出。传递 --goal(CLI)或 goal_mode=Truekanban_create 工具 / 仪表盘)可以改为在目标循环中运行该工作进程,这是与 /goal 斜杠命令相同的 Ralph 风格引擎:每轮之后,一个辅助裁判检查工作进程的输出是否与卡片的标题 + 正文(视为验收标准)匹配,如果工作未完成 —— 并且轮次预算还有剩余 —— 工作进程会在同一会话中继续,直到裁判同意、工作进程自行终止任务或预算耗尽(此时卡片会阻塞等待人工审查,而不是静默退出)。

bash
hermes kanban create "Translate the docs site to French" \
    --body "Acceptance: every page translated, no English left, links intact." \
    --assignee linguist \
    --goal \
    --goal-max-turns 15      # 可选;默认 20

将其用于开放式、多步骤或“继续直到 X 为真”的卡片。对于廉价的一次性工作,跳过它 —— 每轮裁判的开销不值得,并且调度器现有的重试/断路器已经处理了临时工作进程故障。裁判的质量取决于你的目标文本,因此请将正文写为明确的验收标准

编排器技能

一个行为良好的编排器不会自己完成工作。 它将用户的目标分解为任务,链接它们,将每个任务分配给你设置的一个配置文件,然后退后一步。kanban-orchestrator 技能将其编码为工具调用模式:反诱惑规则、第 0 步配置文件发现提示(调度器在未知负责人名称上静默失败,因此编排器必须将每个卡片基于你机器上实际存在的配置文件)、以及以 kanban_create / kanban_link / kanban_comment 为键的分解剧本。

一个典型的编排器轮次(两个并行研究人员交给写作者):

text
## 用户目标:"draft a launch post on the ICP funding landscape"
kanban_create(title="research ICP funding, NA angle",  assignee="researcher-a", body="…")  # → t_r1
kanban_create(title="research ICP funding, EU angle",  assignee="researcher-b", body="…")  # → t_r2
kanban_create(
    title="synthesize ICP funding research into launch post draft",
    assignee="writer",
    parents=["t_r1", "t_r2"],        # 当两个研究人员都完成时提升为 'ready'
    body="one-pager, neutral tone, cite sources inline",
)                                     # → t_w1
## 可选:事后添加跨领域依赖,无需重新创建任务
kanban_link(parent_id="t_r1", child_id="t_followup")
kanban_complete(
    summary="decomposed into 2 parallel research tasks → 1 synthesis task; writer starts when both researchers finish",
)

kanban-orchestrator 是一个捆绑技能。它在安装和更新时同步到每个配置文件中,因此没有单独的技能中心安装步骤。请验证它在你编排器配置文件中存在:

bash
hermes -p orchestrator skills list | grep kanban-orchestrator

如果捆绑副本丢失,请为该配置文件恢复:

bash
hermes -p orchestrator skills reset kanban-orchestrator --restore

为获得最佳效果,请将其与一个工具集仅限于看板操作(kanbangatewaymemory)的配置文件配对,这样编排器即使尝试也无法执行实现任务。

仪表盘(GUI)

/kanban CLI 和斜杠命令足以无头运行看板,但可视化看板通常是人类参与循环的正确界面:分类、跨配置文件监督、阅读评论线程以及在列之间拖拽卡片。Hermes 将其作为捆绑的仪表盘插件提供,位于 plugins/kanban/ —— 不是核心功能,也不是单独的服务 —— 遵循 扩展仪表盘 中列出的模型。

使用以下命令打开:

bash
hermes kanban init      # 一次性:如果 kanban.db 不存在则创建
hermes dashboard        # 导航中出现 "Kanban" 标签页,在 "Skills" 之后

插件提供的内容

  • 一个 Kanban 标签页,每个状态一列:triagetodoreadyrunningblockeddone(以及 archived,当切换打开时)。
    • triage 是粗略想法的停放列。默认情况下(kanban.auto_decompose: true),调度器会自动对落在此处的任务运行分解器。内置分解器使用 auxiliary.kanban_decomposer 模型路径,读取你的配置文件列表(带描述),并将任务扇出到一个小的子任务图中,路由到最合适的专家。原始任务作为所有子任务的父任务保持存活,因此其负责人(kanban.orchestrator_profile,或未设置时的活动默认配置文件)会在所有子任务完成时重新唤醒以判断完成情况。翻转页面顶部的 Orchestration: Auto/Manual 药丸(翠绿色 = 自动,柔灰色 = 手动),或直接编辑 config.yaml。两种模式都与 hermes kanban specify 共存 —— 当你不需要扇出时,它仍然可以作为单任务规范重写使用。
  • 卡片显示任务 ID、标题、优先级徽章、租户标签、分配的配置文件、评论/链接计数、一个进度药丸(当任务有依赖项时,显示 N/M 子任务完成)以及“创建于 N 前”。每个卡片有一个复选框,支持多选。
  • Running 列内的按配置文件分栏 —— 工具栏复选框切换 Running 列按负责人分组的子分组。
  • 通过 WebSocket 实时更新 —— 插件以短轮询间隔跟踪仅追加的 task_events 表;看板在任何配置文件(CLI、网关或其他仪表盘标签页)操作时立即反映变化。重新加载是去抖的,因此事件突发只会触发一次重新获取。
  • 拖拽卡片到列之间以更改状态。拖放会发送 PATCH /api/plugins/kanban/tasks/:id,该请求通过 CLI 使用的相同 kanban_db 代码路由 —— 三个表面永远不会漂移。移动到破坏性状态(donearchivedblocked)会提示确认。触摸设备使用基于指针的回退,因此看板可以在平板电脑上使用。
  • 行内创建 —— 点击任何列标题上的 + 以输入标题、负责人、优先级,以及(可选)从下拉列表中选择一个父任务(覆盖所有现有任务)。按 Enter 创建任务,Shift+Enter 在标题字段中插入换行,或按 Escape 取消。从 Triage 列创建会自动将新任务停放在 triage 中。
  • 多选与批量操作 —— Shift/Ctrl 点击卡片或勾选其复选框以添加到选择。顶部出现一个批量操作栏,包含批量状态转换、归档和重新分配(通过配置文件下拉列表,或“(unassign)”)。破坏性批量操作会先确认。每个 ID 的部分失败会报告,而不会中止其余操作。
  • 点击卡片(不带 Shift/Ctrl)打开一个侧边抽屉(Escape 或点击外部关闭),包含:
    • 可编辑标题 —— 点击标题重命名。
    • 可编辑负责人/优先级 —— 点击元数据行重写。
    • 可编辑描述 —— 默认以 Markdown 渲染(标题、粗体、斜体、行内代码、围栏代码、http(s) / mailto: 链接、项目符号列表),带有一个“编辑”按钮,可切换为文本区域。Markdown 渲染是一个小型、XSS 安全的渲染器 —— 每个替换都在 HTML 转义的输入上运行,只有 http(s) / mailto: 链接通过,并且始终设置 target="_blank" + rel="noopener noreferrer"
    • 依赖编辑器 —— 父任务和子任务的芯片列表,每个带有 × 取消链接,以及覆盖所有其他任务的下拉列表以添加新的父任务或子任务。循环尝试会在服务器端被拒绝,并带有清晰的消息。
    • 状态操作行(→ triage / → ready / → running / block / unblock / complete / archive),对破坏性转换有确认提示。对于 Triage 列中的卡片,该行还公开了两个 LLM 驱动的操作:⚗ Decompose 将任务扇出到子任务图中,路由到按描述匹配的专家配置文件;✨ Specify 执行单任务规范重写。当 LLM 决定任务不需要扇出时,Decompose 会回退到 specify 风格的提升,因此它是严格的超集。两者都可以从 CLI(hermes kanban decompose <id> / specify <id> / --all)、从任何网关平台(/kanban decompose <id>)以及通过编程方式(POST /api/plugins/kanban/tasks/:id/decompose…/specify)访问。在 config.yaml 中的 auxiliary.kanban_decomposerauxiliary.triage_specifier 下配置模型。
    • 结果部分(也以 Markdown 渲染)、评论线程(Enter 提交)、最后 20 个事件。
  • 工具栏过滤器 —— 自由文本搜索、租户下拉列表(默认为 config.yaml 中的 dashboard.kanban.default_tenant)、负责人下拉列表、“显示归档”切换、“按配置文件分栏”切换,以及一个 Nudge dispatcher 按钮,这样你就不必等待下一个 60 秒滴答。

视觉目标是熟悉的 Linear / Fusion 布局:深色主题、带计数的列标题、彩色状态点、优先级和租户的药丸芯片。插件只读取主题 CSS 变量(--color-*--radius--font-mono、...),因此它会根据当前活动的仪表盘主题自动重新皮肤。

自动与手动编排

看板有两种方式处理你放入 Triage 列的任务:

自动(默认) —— kanban.auto_decompose: true。网关嵌入式调度器在每个滴答上运行分解器,上限为 kanban.auto_decompose_per_tick(默认每个滴答 3 个任务),这样批量加载 triage 任务不会突发消耗辅助 LLM。分解器使用内置分解提示加上 auxiliary.kanban_decomposer 模型路径,读取你安装的配置文件及其描述,并要求 LLM 生成一个 JSON 任务图:要生成哪些任务、分配给谁、以及哪些依赖哪些。原始 triage 任务成为图中每个叶节点的父任务,因此它保持存活直到整个图完成 —— 然后提升回 ready,以便其负责人(kanban.orchestrator_profile,或未设置时的活动默认配置文件)可以判断完成情况,并在工作未完成时添加更多任务。这是“丢下一行,走开”的流程。

手动 —— kanban.auto_decompose: false。Triage 任务保持 triage 状态,直到你操作。点击卡片上的 ⚗ Decompose 按钮,运行 hermes kanban decompose <id>(或 --all),或从聊天中使用 /kanban decompose <id>。这匹配看板在分解器之前的行为,当你希望完全控制何时运行什么时很有用。

在 kanban 页面顶部的 Orchestration: Auto/Manual 药丸之间切换(翠绿色 = 自动,柔灰色 = 手动),或直接编辑 config.yaml。两种模式都与 hermes kanban specify 共存 —— 当你不需要扇出时,它仍然可以作为单任务规范重写使用。

分解器的路由决策取决于配置文件描述,这是一个每个配置文件的标签原语,你可以使用 hermes profile create --description "..."hermes profile describe <name> --text "..."hermes profile describe <name> --auto(从配置文件安装的技能 + 模型 LLM 生成)或仪表盘扩展的 Orchestration settings 面板中的每个配置文件编辑器来设置。没有描述的配置文件仍然出现在列表中 —— 它们可以按名称路由,只是不够精确。分解器永远不会将子任务分配给 assignee=None:当 LLM 选择未知配置文件时,子任务会路由到 kanban.default_assignee(如果未设置,则回退到活动默认配置文件)。

kanban.orchestrator_profile 不会将该配置文件的提示、技能或自定义逻辑加载到分解调用中。它控制扇出后根/编排任务的所有者。要更改分解器的模型/提供商,请配置 auxiliary.kanban_decomposer。要使用配置文件的自定义任务拆分逻辑而不是内置分解器,请切换到手动模式,并让该配置文件显式创建或分解任务。

配置旋钮(全部位于 ~/.hermes/config.yamlkanban: 下):

默认值用途
auto_decomposetrue调度器在每个滴答上自动运行分解器。
auto_decompose_per_tick3每个调度器滴答的分解次数上限。超出部分推迟到下一个滴答。
orchestrator_profile""分解后分配给根/编排任务的配置文件。空 = 回退到活动默认配置文件。
default_assignee""当 LLM 选择未知配置文件时,子任务落地的位置。空 = 回退到活动默认。

以及两个辅助 LLM 插槽:

用途
auxiliary.kanban_decomposer生成任务图的模型(由 Decompose 调用)。设置 provider/model 以覆盖主聊天模型。
auxiliary.profile_describer自动生成配置文件描述的模型(由 hermes profile describe --auto 调用)。

架构

GUI 严格是一个通过 DB 读取 + 通过 kanban_db 写入的层,没有自己的领域逻辑:

text
┌────────────────────────┐      WebSocket (跟踪 task_events)
│   React SPA (插件)     │ ◀──────────────────────────────────┐
│   HTML5 拖放           │                                    │
└──────────┬─────────────┘                                    │
           │ REST over fetchJSON                              │
           ▼                                                  │
┌────────────────────────┐     写入调用 kanban_db.*          │
│  FastAPI 路由器         │     直接 —— 与 CLI /kanban 动词  │
│  plugins/kanban/        │     使用的相同代码路径            │
│  dashboard/plugin_api.py                                    │
└──────────┬─────────────┘                                    │
           │                                                  │
           ▼                                                  │
┌────────────────────────┐                                    │
│  ~/.hermes/kanban.db   │ ───── 追加 task_events ──────────┘
│  (WAL, 共享)           │
└────────────────────────┘

REST 接口

所有路由都挂载在 /api/plugins/kanban/ 下,并由仪表盘的临时会话令牌保护:

方法路径用途
GET/board?tenant=<name>&include_archived=…按状态列分组的完整看板,加上用于过滤器下拉列表的租户和负责人
GET/tasks/:id任务 + 评论 + 事件 + 链接
POST/tasks创建(包装 kanban_db.create_task,接受 triage: boolparents: [id, …]
PATCH/tasks/:id状态 / 负责人 / 优先级 / 标题 / 正文 / 结果
POST/tasks/bulkids 中的每个 ID 应用相同的补丁(状态 / 归档 / 负责人 / 优先级)。每个 ID 的失败会报告,不会中止兄弟任务
POST/tasks/:id/comments追加评论
POST/tasks/:id/specify运行 triage 说明器 —— 辅助 LLM 充实任务正文并将其从 triage 提升到 todo。返回 {ok, task_id, reason, new_title}ok=false 并带有人类可读的原因(“不在 triage” / 无辅助客户端 / LLM 错误)时返回 200,而不是 4xx
POST/tasks/:id/decompose运行看板分解器 —— 辅助 LLM 生成任务图,助手原子地创建子任务 + 链接根任务 + 翻转 triage → todo。返回 {ok, task_id, reason, fanout, child_ids, new_title}。与 /specify 相同的 200-on-LLM-error 约定。
GET/profiles列出已安装的配置文件及其描述(由仪表盘的配置文件描述编辑器和编排器选择器使用)。
PATCH/profiles/:name设置或清除配置文件的描述(用户编写 —— description_auto: false)。返回 {ok, profile, description}
POST/profiles/:name/describe-auto通过 auxiliary.profile_describer 为配置文件生成描述。以 description_auto: true 持久化,以便仪表盘可以显示“审查”徽章。
GET/orchestration读取看板编排设置(orchestrator_profiledefault_assigneeauto_decompose)以及回退后的解析后有效值。
PUT/orchestration更新 config.yaml 中三个编排键中的一个或多个。验证非空配置文件名称确实存在。
POST/links添加依赖关系(parent_idchild_id
DELETE/links?parent_id=…&child_id=…移除依赖关系
POST/dispatch?max=…&dry_run=…推动调度器 —— 跳过 60 秒等待
GET/configconfig.yaml 读取 dashboard.kanban 偏好 —— default_tenantlane_by_profileinclude_archived_by_defaultrender_markdown
WS/events?since=<event_id>task_events 行的实时流

每个处理器都是一个薄包装 —— 插件大约 700 行 Python(路由器 + WebSocket 跟踪 + 批量处理器 + 配置读取器),不添加新的业务逻辑。一个小的 _conn() 助手在每次读取和写入时自动初始化 kanban.db,因此新安装无论用户是先打开仪表盘、直接访问 REST API 还是运行 hermes kanban init 都能工作。

仪表盘配置

~/.hermes/config.yamldashboard.kanban 下的任何这些键都会更改标签页的默认值 —— 插件在加载时通过 GET /config 读取它们:

yaml
dashboard:
  kanban:
    default_tenant: acme              # 预选租户过滤器
    lane_by_profile: true             # “按配置文件分栏”切换的默认值
    include_archived_by_default: false
    render_markdown: true             # 设置为 false 以使用纯 <pre> 渲染

每个键都是可选的,并回退到显示的默认值。

安全模型

仪表盘的 HTTP 认证中间件显式跳过 /api/plugins/ —— 插件路由设计为未经认证,因为仪表盘默认绑定到 localhost。这意味着看板 REST 接口可以从主机上的任何进程访问。

WebSocket 多了一个步骤:它要求仪表盘的临时会话令牌作为 ?token=… 查询参数(浏览器无法在升级请求上设置 Authorization),与浏览器内 PTY 桥使用的模式匹配。

如果你运行 hermes dashboard --host 0.0.0.0,每个插件路由 —— 包括看板 —— 都会从网络可达。不要在共享主机上这样做。 看板包含任务正文、评论和工作空间路径;攻击者访问这些路由可以获得对整个协作表面的读取访问权限,并且还可以创建/重新分配/归档任务。

~/.hermes/kanban.db 中的任务有意与配置文件无关(这是协调原语)。如果你使用 hermes -p <profile> dashboard 打开仪表盘,看板仍然显示主机上任何其他配置文件创建的任务。同一用户拥有所有配置文件,但如果你有多个角色共存,这一点值得注意。

实时更新

task_events 是一个仅追加的 SQLite 表,具有单调递增的 id。WebSocket 端点保存每个客户端最后看到的事件 ID,并在新行到达时推送它们。当事件突发到达时,前端重新加载(非常便宜的)看板端点 —— 比尝试从每种事件类型修补本地状态更简单、更正确。WAL 模式意味着读取循环永远不会阻塞调度器的 BEGIN IMMEDIATE 声明事务。

扩展它

该插件使用标准的 Hermes 仪表盘插件合约 —— 请参阅 扩展仪表盘 了解完整的清单参考、shell 插槽、页面范围插槽和插件 SDK。额外的列、自定义卡片外观、按租户过滤的布局或完整的 tab.override 替换都可以在不分叉此插件的情况下实现。

要禁用而不删除:在 config.yaml 中添加 dashboard.plugins.kanban.enabled: false(或删除 plugins/kanban/dashboard/manifest.json)。

范围边界

GUI 故意保持精简。插件能做的所有事情都可以从 CLI 访问;插件只是让人类使用起来更舒适。自动分配、预算、治理门和组织视图仍然是用户空间 —— 一个路由器配置文件、另一个插件或重用 tools/approval.py —— 正如设计规范的范围外部分所列。

CLI 命令参考

这是(或脚本、cron、仪表盘)用来驱动看板的界面。在调度器内部运行的工作进程使用 kanban_* 工具表面 进行相同的操作 —— 这里的 CLI 和那里的工具都通过 kanban_db 路由,因此两个表面在构造上是一致的。

text
hermes kanban init                                     # 创建 kanban.db + 打印守护进程提示
hermes kanban create "<title>" [--body ...] [--assignee <profile>]
                                [--parent <id>]... [--tenant <name>]
                                [--workspace scratch|worktree|worktree:<path>|dir:<path>]
                                [--branch <name>]
                                [--priority N] [--triage] [--idempotency-key KEY]
                                [--max-runtime 30m|2h|1d|<seconds>]
                                [--max-retries N]
                                [--goal] [--goal-max-turns N]
                                [--skill <name>]...
                                [--json]
hermes kanban list [--mine] [--assignee P] [--status S] [--tenant T] [--archived]
        [--workflow-template-id <id>] [--current-step-key <key>]
        [--sort created|created-desc|priority|priority-desc|status|assignee|title|updated]
        [--json]
hermes kanban show <id> [--json]
hermes kanban assign <id> <profile>                    # 或 'none' 取消分配
hermes kanban reassign <id>... <profile>               # 批量将任务重新分配给一个配置文件
hermes kanban edit <id> [--title ...] [--body ...]     # 原地编辑任务标题/正文/优先级
        [--priority N]
hermes kanban promote <id>...                          # 将 todo/blocked 任务移动到 ready(恢复)
hermes kanban schedule <id> --at <ISO8601>             # 设置/清除任务的 scheduled_at 开始时间
hermes kanban diagnostics [--json]                     # 看板健康快照(别名:diag)
hermes kanban link <parent_id> <child_id>
hermes kanban unlink <parent_id> <child_id>
hermes kanban claim <id> [--ttl SECONDS]
hermes kanban comment <id> "<text>" [--author NAME]
## 批量动词 —— 接受多个 ID:
hermes kanban complete <id>... [--result "..."]
hermes kanban block <id> "<reason>" [--ids <id>...]
hermes kanban unblock <id>...
hermes kanban archive <id>...

hermes kanban tail <id>                                # 跟踪单个任务的事件流
hermes kanban watch [--assignee P] [--tenant T]        # 将所有事件实时流式传输到终端
        [--kinds completed,blocked,…] [--interval SECS]
hermes kanban heartbeat <id> [--note "..."]            # 工作进程存活信号,用于长时间操作
hermes kanban runs <id> [--json]                       # 尝试历史(每次运行一行)
hermes kanban assignees [--json]                       # 磁盘上的配置文件 + 每个负责人的任务计数
hermes kanban dispatch [--dry-run] [--max N]           # 单次传递
        [--failure-limit N] [--json]
hermes kanban daemon --force                           # 已弃用 —— 独立调度器(改用 `hermes gateway start`)
        [--failure-limit N] [--pidfile PATH] [-v]
hermes kanban stats [--json]                           # 按状态 + 按负责人计数
hermes kanban log <id> [--tail BYTES]                  # 来自 ~/.hermes/kanban/logs/ 的工作进程日志
hermes kanban notify-subscribe <id>                    # 网关桥接钩子(由网关中的 /kanban 使用)
        --platform <name> --chat-id <id> [--thread-id <id>] [--user-id <id>]
hermes kanban notify-list [<id>] [--json]
hermes kanban notify-unsubscribe <id>
        --platform <name> --chat-id <id> [--thread-id <id>]
hermes kanban context <id>                             # 工作进程看到的内容
hermes kanban specify [<id> | --all] [--tenant T]      # 充实 triage 列的想法
        [--author NAME] [--json]                       #   成为完整规范并提升为 todo
hermes kanban gc [--event-retention-days N]            # 工作空间 + 旧事件 + 旧日志
        [--log-retention-days N]

所有命令也可作为交互式 CLI 和消息网关中的斜杠命令使用(请参阅下面的 /kanban 斜杠命令)。

--max-retries 是每个任务的断路器覆盖,用于调度器。--max-retries 1 在第一次非成功尝试时阻塞任务,而 --max-retries 3 允许两次重试,并在第三次失败时阻塞。省略它则使用 config.yaml 中的 kanban.failure_limit,然后是内置默认值。

并发、调度和子任务提升配置

配置键默认值作用
kanban.max_in_progress未设置(无限制)限制同时运行的任务数量。当看板已有 N 个运行中任务时,调度器跳过生成更多任务 —— 对于慢速工作进程(本地 LLM、资源受限的主机)很有用,这样它们能完成已有任务,而不是堆积更多并超时。无效或低于 1 的值会记录警告并表现为无限制。
kanban.max_in_progress_per_profile未设置(无限制)max_in_progress 的每个配置文件变体 —— 限制任何单个负责人配置文件可以同时运行的任务数量。当一个配置文件较慢或受速率限制,但其他配置文件应继续流动时很有用。与看板范围的 max_in_progress 一起应用;两者都必须允许生成才能继续。
kanban.auto_promote_childrentruedecompose_triage_task() 生成没有父阻塞依赖的子任务后,它们会自动提升为 ready,以便调度器可以拾取它们。设置为 false 以要求手动审查 —— 子任务保持 todo 状态,直到你提升它们。
kanban.default_workdir未设置看板级别的默认工作目录,应用于新任务,当 --workspace 或任务本身未覆盖时。每个任务的 workspace: 仍然优先。
yaml
kanban:
  max_in_progress: 2
  auto_promote_children: false
  default_workdir: ~/work/active-project

计划任务开始(scheduled_at

在任务上设置 scheduled_at 以将调度延迟到特定时间。调度器跳过 scheduled_at 在未来时间的就绪任务,并在该时间戳之后的第一个滴答时拾取它们。

bash
hermes kanban create "nightly backup audit" \
  --assignee ops --scheduled-at "2026-06-01T03:00:00Z"

重生保护

当就绪任务在上一次运行时遇到配额/认证/429 错误(blocker_auth),或在保护窗口内成功完成一次运行(recent_success),或最近的评论链接到 GitHub PR(active_pr)时,调度器拒绝重新生成该任务。这可以防止在人类赶上之前,针对同一个错误或任务重复出现工作进程风暴。请参阅 事件参考 中的 respawn_guarded 行。

拖拽删除和批量删除(仪表盘)

仪表盘在看板页面上暴露一个垃圾桶拖放区域 —— 将任何卡片拖入其中以删除任务(级联删除 task_events、子链接和订阅)。确认提示可防止意外。批量删除也可以通过 DELETE /api/plugins/kanban/tasks 实现,请求体为 JSON {"ids": ["t_abc", "t_def", ...]}

工作进程可见性端点

仪表盘插件 API 现在为外部监视器暴露以下只读端点(加上一个运行控制动词):

端点返回
GET /api/plugins/kanban/workers/active当前生成的工作进程,包含 PID、配置文件、任务 ID、开始时间、最后心跳
GET /api/plugins/kanban/runs/{id}单次运行详情 —— 任务 ID、状态、开始/结束、退出代码、日志路径
POST /api/plugins/kanban/runs/{run_id}/terminate终止一个可回收的运行 —— 停止工作进程并释放任务以供重新调度
GET /api/plugins/kanban/inspect组合的调度器快照 —— 积压、进行中计数 vs. max_in_progress、最近事件

所有这些都受与看板插件 API 其余部分相同的仪表盘插件认证保护。

Kanban Swarm 拓扑助手

hermes kanban swarm 一次性创建一个持久的 Kanban Swarm v1 图:一个已完成的根/黑板卡片、N 个并行工作卡片、一个在所有工作卡片上门控的验证卡片,以及在验证卡片上门控的综合卡片。共享的 swarm 上下文(“黑板”)作为结构化 JSON 评论存储在根卡片上,因此任何工作进程都可以读取它。

bash
hermes kanban swarm "Design a multi-region failover plan" \
  --workers researcher,architect,sre \
  --verifier reviewer --synthesizer writer

生成的图正常调度 —— 工作进程并行运行,验证者在它们全部完成后唤醒,综合者在验证者标记工作干净后唤醒。

/kanban 斜杠命令 {#kanban-斜杠命令}

每个 hermes kanban <action> 动词也可以通过 /kanban <action> 访问 —— 从交互式 hermes chat 会话内部以及从任何网关平台(Telegram、Discord、Slack、WhatsApp、Signal、Matrix、Mattermost、电子邮件、短信)。两个表面都调用完全相同的 hermes_cli.kanban.run_slash() 入口点,该入口点重用 hermes kanban argparse 树,因此参数表面、标志和输出格式在 CLI、/kanbanhermes kanban 之间是相同的。你无需离开聊天即可驱动看板。

text
/kanban list
/kanban show t_abcd
/kanban create "write launch post" --assignee writer --parent t_research
/kanban comment t_abcd "looks good, ship it"
/kanban unblock t_abcd
/kanban dispatch --max 3
/kanban specify t_abcd                  # 将 triage 一行充实为真实规范
/kanban specify --all --tenant engineering  # 扫描一个租户中的所有 triage 任务

引用多词参数的方式与在 shell 中相同 —— run_slash 使用 shlex.split 解析行的其余部分,因此 "..."'...' 都有效。

运行中使用:/kanban 绕过运行中智能体保护

网关通常会在智能体仍在思考时对斜杠命令和用户消息进行排队 —— 这可以防止你在第一个轮次进行中时意外开始第二个轮次。/kanban 明确豁免于此保护。 看板存在于 ~/.hermes/kanban.db 中,而不是运行中智能体的状态中,因此读取(listshowcontexttailwatchstatsruns)和写入(commentunblockblockassignarchivecreatelink、…)都会立即通过,即使在轮次中间也是如此。

这就是分离的全部意义:

  • 工作进程阻塞等待同伴 → 你从手机发送 /kanban unblock t_abcd,调度器在下一个滴答时拾取同伴。被阻塞的工作进程不会被中断 —— 它只是不再被阻塞。
  • 你发现一个需要人类上下文的卡片 → /kanban comment t_xyz "use the 2026 schema, not 2025" 落在任务线程上,该任务的下一次运行将在 kanban_show() 中读取它。
  • 你想知道你的集群在做什么而不停止编排器 → /kanban list --mine/kanban stats 检查看板,而不影响你的主对话。

/kanban create 时自动订阅(仅限网关)

当你从网关使用 /kanban create "…" 创建任务时,原始聊天(平台 + 聊天 ID + 线程 ID)会自动订阅该任务的终端事件(completedblockedgave_upcrashedtimed_out)。每个终端事件你会收到一条消息 —— 包括 completed 时工作进程结果摘要的第一行 —— 无需轮询或记住任务 ID。

text
you> /kanban create "transcribe today's podcast" --assignee transcriber
bot> Created t_9fc1a3  (ready, assignee=transcriber)
     (subscribed — you'll be notified when t_9fc1a3 completes or blocks)

… ~8 分钟后 …

bot> ✓ t_9fc1a3 completed by transcriber
     transcribed 42 minutes, saved to podcast/2026-05-04.md

一旦任务达到 donearchived,订阅会自动移除。如果你使用 --json(机器输出)脚本化创建,则跳过自动订阅 —— 假设脚本化调用者希望显式管理订阅,通过 /kanban notify-subscribe

消息中的输出截断

网关平台有实际的消息长度限制。如果 /kanban list/kanban show/kanban tail 产生超过约 3800 个字符的输出,响应会被截断,并带有 … (truncated; use \hermes kanban …` in your terminal for full output)` 页脚。CLI 表面没有此类限制。

自动补全

在交互式 CLI 中,输入 /kanban 并按 Tab 键会循环显示内置子命令列表(listlsshowcreateassignlinkunlinkclaimcommentcompleteblockunblockarchivetaildispatchcontextinitgc)。上面 CLI 参考中列出的其余动词(watchstatsrunslogassigneesheartbeatnotify-subscribenotify-listnotify-unsubscribedaemon)也有效 —— 只是尚未在自动补全提示列表中。

协作模式

看板支持以下八种模式,无需任何新原语:

模式形状示例
P1 扇出N 个同级,相同角色“并行研究 5 个角度”
P2 流水线角色链:侦察 → 编辑 → 写作者每日简报汇编
P3 投票/法定人数N 个同级 + 1 个聚合器3 个研究人员 → 1 个审查者选择
P4 长期日志相同配置文件 + 共享目录 + cronObsidian 仓库
P5 人机协同工作进程阻塞 → 用户评论 → 解除阻塞模糊决策
P6 @提及从散文中内联路由@reviewer look at this
P7 线程范围工作空间线程中的 /kanban here每个项目的网关线程
P8 批量任务一个配置文件,N 个主题50 个社交账号
P9 分类说明器粗略想法 → triagehermes kanban specify 扩展正文 → todo“将这一行变成规范任务”

有关每个模式的详细示例,请参阅 docs/hermes-kanban-v1-spec.pdf

多租户使用

当一个专家集群为多个业务服务时,为每个任务标记一个租户:

bash
hermes kanban create "monthly report" \
    --assignee researcher \
    --tenant business-a \
    --workspace dir:~/tenants/business-a/data/

工作进程接收 $HERMES_TENANT 并通过前缀命名空间其内存写入。看板、调度器和配置文件定义都是共享的;只有数据是范围限定的。

网关通知

当你从网关(Telegram、Discord、Slack 等)运行 /kanban create … 时,原始聊天会自动订阅新任务。网关的后台通知器每隔几秒轮询 task_events,并为每个终端事件(completedblockedgave_upcrashedtimed_out)向该聊天发送一条消息。已完成的任务还会发送工作进程 --result 的第一行,这样你无需 /kanban show 就能看到结果。

你可以从 CLI 显式管理订阅 —— 当脚本/cron 作业想要通知一个它并非来自的聊天时很有用:

bash
hermes kanban notify-subscribe t_abcd \
    --platform telegram --chat-id 12345678 --thread-id 7
hermes kanban notify-list
hermes kanban notify-unsubscribe t_abcd \
    --platform telegram --chat-id 12345678 --thread-id 7

一旦任务达到 donearchived,订阅会自动移除;无需清理。

运行 —— 每次尝试一行

任务是一个逻辑工作单元;运行是执行它的一次尝试。当调度器声明一个就绪任务时,它会在 task_runs 中创建一行,并将 tasks.current_run_id 指向它。当该尝试结束时 —— 完成、阻塞、崩溃、超时、生成失败、回收 —— 运行行会以 outcome 关闭,任务的指针会清除。一个被尝试了三次的任务会有三行 task_runs

为什么需要两个表而不是仅仅修改任务:你需要完整的尝试历史用于真实世界的事后分析(“第二次审查尝试批准了,第三次合并了”),并且你需要一个干净的地方来挂载每次尝试的元数据 —— 哪些文件更改了、哪些测试运行了、审查者注意到了哪些发现。这些是运行事实,而不是任务事实。

运行也是结构化交接所在。当工作进程完成任务时(通过 kanban_complete(...)),它可以传递:

  • summary(工具参数)/ --summary(CLI)—— 人类交接;放在运行上;下游子任务在它们的 build_worker_context 中看到它。
  • metadata(工具参数)/ --metadata(CLI)—— 运行上的自由格式 JSON 字典;子任务在摘要旁边看到它序列化。
  • result(工具参数)/ --result(CLI)—— 放在任务行上的短日志行(遗留字段,为向后兼容保留)。

下游子任务读取每个父任务最近完成的运行的摘要 + 元数据。重试的工作进程读取自己任务上的先前尝试(结果、摘要、错误),这样它们就不会重复已经失败的路径。

text
## 工作进程实际做的事情 —— 从智能体循环内部的一个工具调用:
kanban_complete(
    summary="implemented token bucket, keys on user_id with IP fallback, all tests pass",
    metadata={"changed_files": ["limiter.py", "tests/test_limiter.py"], "tests_run": 14},
    result="rate limiter shipped",
)

(人类)需要关闭工作进程无法完成的任务时,也可以从 CLI 进行相同的交接 —— 例如,一个被放弃的任务,或者你从仪表盘手动标记为完成的任务:

bash
hermes kanban complete t_abcd \
    --result "rate limiter shipped" \
    --summary "implemented token bucket, keys on user_id with IP fallback, all tests pass" \
    --metadata '{"changed_files": ["limiter.py", "tests/test_limiter.py"], "tests_run": 14}'
## 查看重试任务的尝试历史:
hermes kanban runs t_abcd
## #  OUTCOME       PROFILE           ELAPSED  STARTED
## 1  blocked       worker               12s  2026-04-27 14:02
## → BLOCKED: need decision on rate-limit key
## 2  completed     worker                8m   2026-04-27 15:18
## → implemented token bucket, keys on user_id with IP fallback

运行在仪表盘上暴露(抽屉中的运行历史部分,每次尝试一行彩色行),也在 REST API 上暴露(GET /api/plugins/kanban/tasks/:id 返回一个 runs[] 数组)。PATCH /api/plugins/kanban/tasks/:id 带有 {status: "done", summary, metadata} 会将两者转发到内核,因此仪表盘的“标记完成”按钮与 CLI 等效。task_events 行携带它们所属的 run_id,以便 UI 可以按尝试分组事件,并且 completed 事件在其负载中嵌入第一行摘要(限制为 400 个字符),以便网关通知器无需第二次 SQL 往返即可渲染结构化交接。

批量关闭注意事项。 hermes kanban complete a b c --summary X 会被拒绝 —— 结构化交接是按运行进行的,因此将相同的摘要复制粘贴到 N 个任务几乎总是错误的。不带 --summary / --metadata 的批量关闭仍然适用于常见的“我完成了一堆管理任务”的情况。

状态更改导致的回收运行。 如果你在仪表盘中将一个运行中的任务拖离 running(回到 ready,或直接到 todo),或者归档一个仍在运行的任务,正在进行的运行会以 outcome='reclaimed' 关闭,而不是成为孤儿。当 tasks.current_run_idNULL 时,task_runs 行始终处于终端状态,反之亦然 —— 该不变性在 CLI、仪表盘、调度器和通知器之间保持一致。

从未声明完成的合成运行。 完成或阻塞一个从未被声明的任务(例如,人类从仪表盘关闭一个 ready 任务并带有摘要,或 CLI 用户运行 hermes kanban complete <ready-task> --summary X)否则会丢失交接。相反,内核会插入一个零持续时间的运行行(started_at == ended_at),携带摘要/元数据/原因,以便尝试历史保持完整。completed / blocked 事件的 run_id 指向该行。

实时抽屉刷新。 当仪表盘的 WebSocket 事件流报告用户当前正在查看的任务有新事件时,抽屉会重新加载自身(通过一个每个任务的事件计数器,该计数器被线程化到其 useEffect 依赖列表中)。不再需要关闭并重新打开来查看运行的新行或更新的结果。

向前兼容

tasks 上的两个可空列保留用于 v2 工作流路由:workflow_template_id(此任务所属的模板)和 current_step_key(该模板中当前活动的步骤)。v1 内核在路由时忽略它们,但允许客户端写入它们,因此 v2 版本可以添加路由机制而无需另一次模式迁移。

事件参考

每个转换都会向 task_events 追加一行。每行携带一个可选的 run_id,以便 UI 可以按尝试分组事件。种类分为三个集群,以便于过滤(hermes kanban watch --kinds completed,gave_up,timed_out):

生命周期(关于任务作为逻辑单元的变化):

种类负载何时
created{assignee, status, parents, tenant}任务插入。run_idNULL
promotedtodo → ready,因为所有父任务都达到 donerun_idNULL
claimed{lock, expires, run_id}调度器原子声明了一个 ready 任务以进行生成。
completed{result_len, summary?}工作进程写了 --result / --summary 并且任务达到 donesummary 是第一行交接(400 字符限制);完整版本位于运行行上。如果在从未声明的任务上调用 complete_task 并带有交接字段,则会合成一个零持续时间的运行,因此 run_id 仍然指向某个东西。
blocked{reason}工作进程或人类将任务翻转为 blocked。当在从未声明的任务上调用并带有 --reason 时,会合成一个零持续时间的运行。
unblockedblocked → ready,手动或通过 /unblockrun_idNULL
archived从默认看板隐藏。如果任务仍在运行,则携带作为副作用被回收的运行的 run_id

编辑(人类驱动的非转换更改):

种类负载何时
assigned{assignee}负责人更改(包括取消分配)。
edited{fields}标题或正文更新。
reprioritized{priority}优先级更改。
status{status}仪表盘拖放直接写入状态(例如 todo → ready)。当从 running 拖离时,携带被回收的运行的 run_id;否则 run_id 为 NULL。

工作进程遥测(关于执行过程,而不是逻辑任务):

种类负载何时
spawned{pid}调度器成功启动了一个工作进程。
heartbeat{note?}工作进程调用 hermes kanban heartbeat $TASK 以在长时间操作期间发送存活信号。
reclaimed{stale_lock}声明 TTL 过期而未完成;任务回到 ready
crashed{pid, claimer}工作进程 PID 不再存活,但 TTL 尚未过期。
timed_out{pid, elapsed_seconds, limit_seconds, sigkill}max_runtime_seconds 超出;调度器发送 SIGTERM(然后在 5 秒宽限期后发送 SIGKILL)并重新排队。
stale{elapsed_seconds, last_heartbeat_at, heartbeat_age_seconds, timeout_seconds, pid, terminated}任务运行时间超过 kanban.dispatch_stale_timeout_seconds(默认 4 小时)并且在过去一小时内没有 kanban_heartbeat 到达。调度器向主机本地工作进程(如果有)发送 SIGTERM,将任务重置为 ready 以重新调度。不会增加失败计数器(stale 是调度器端缺席检测,不是工作进程故障)。运行长时间操作的工作进程应至少每小时调用一次 kanban_heartbeat 以避免此情况。
respawn_guarded{reason}调度器拒绝在此滴答重新生成此就绪任务。原因:blocker_auth(上次失败是配额/认证/429 错误 —— 等待速率窗口重置)、recent_success(过去一小时内发生了完成的运行 —— 等待审查后再重新运行)、active_pr(最近评论中出现 GitHub PR URL —— 先前的工作进程已经打开了 PR)。任务保持在 ready;下一个滴答有另一次生成机会。如果底层条件持续存在,正常的 consecutive_failures 断路器会在 failure_limit 次失败后通过 gave_up 自动阻塞。
spawn_failed{error, failures}一次生成尝试失败(PATH 缺失、工作空间无法挂载等)。计数器递增;任务返回 ready 以重试。
protocol_violation{pid, claimer, exit_code}工作进程在任务仍处于 running 时成功退出,通常是因为它回答了但没有调用 kanban_completekanban_block。调度器还会发出 gave_up 并立即自动阻塞,而不是重试。
gave_up{failures, effective_limit, limit_source, error}断路器在 N 次连续非成功尝试后触发。任务自动阻塞,并带有最后一个错误。有效限制解析为任务 max_retries,然后是调度器 failure_limit / kanban.failure_limit,然后是内置默认值。

hermes kanban tail <id> 显示单个任务的这些事件。hermes kanban watch 流式传输整个看板的事件。

范围外

Kanban 有意设计为单主机。~/.hermes/kanban.db 是一个本地 SQLite 文件,调度器在同一台机器上生成工作进程。不支持在两台主机之间运行共享看板 —— 没有用于“主机 A 上的工作进程 X,主机 B 上的工作进程 Y”的协调原语,并且崩溃检测路径假设 PID 是主机本地的。如果你需要多主机,请为每个主机运行一个独立的看板,并使用 delegate_task / 消息队列来桥接它们。

设计规范

完整的设计 —— 架构、并发正确性、与其他系统的比较、实现计划、风险、未解决问题 —— 位于 docs/hermes-kanban-v1-spec.pdf。在提交任何行为更改 PR 之前,请先阅读该文档。


分享: