# Kanban 教程 Hermes Kanban 系统所设计的四个使用场景的完整演示,需在浏览器中打开 dashboard。如果你还没有阅读 [Kanban 概述](./kanban),请先从那里开始——本文假设你已了解 task(任务)、run(运行)、assignee(负责人)和 dispatcher(调度器)的概念。 ## 准备工作 ```bash hermes kanban init # 可选;首次执行 `hermes kanban <任何命令>` 会自动初始化 hermes dashboard # 在浏览器中打开 http://127.0.0.1:9119 # 点击左侧导航栏中的 Kanban ``` dashboard 是**你**观察系统最便捷的地方。dispatcher 生成的 agent worker 不会看到 dashboard 或 CLI——它们通过专用的 `kanban_*` [工具集](./kanban#how-workers-interact-with-the-board)(`kanban_show`、`kanban_list`、`kanban_complete`、`kanban_block`、`kanban_heartbeat`、`kanban_comment`、`kanban_create`、`kanban_link`、`kanban_unblock`)来操作看板。三个界面——dashboard、CLI、worker 工具——都通过同一个每看板独立的 SQLite 数据库(默认看板为 `~/.hermes/kanban.db`,后续创建的任意看板为 `~/.hermes/kanban/boards//kanban.db`)进行路由,因此无论变更来自哪一侧,每个看板的数据始终一致。 本教程全程使用 `default` 看板。如果你需要多个隔离队列(每个项目/仓库/领域一个),请参阅概述中的[看板(多项目)](./kanban#boards-multi-project)——相同的 CLI/dashboard/worker 流程适用于每个看板,且 worker 在物理上无法看到其他看板上的任务。 在本教程中,**标注为 `bash` 的代码块是*你*运行的命令。** 标注为 `# worker tool calls` 的代码块是生成的 worker 模型发出的工具调用——展示在这里是为了让你能端到端地了解整个循环,而不是让你自己去运行它们。 ## 看板概览 ![Kanban board overview](/img/kanban-tutorial/01-board-overview.png) 从左到右共六列: - **Triage(分类)** — 原始想法。默认情况下,dispatcher 会对此处的任务自动运行**分解器**(orchestrator 驱动的扇出):它读取你的 profile 名册和描述,生成一张子任务图,将任务路由给最合适的专家,同时保持原始任务作为父任务存活,以便在所有子任务完成后 orchestrator 重新唤醒来判断完成情况。点击 kanban 页面顶部的 **Orchestration: Auto/Manual** 切换按钮来切换模式。在 Manual 模式下(或没有 orchestrator profile 的配置中),点击卡片上的 **⚗ Decompose**,或运行 `hermes kanban decompose ` / `/kanban decompose `。对于不需要扇出的单个任务,**✨ Specify** 会进行一次性规格重写(目标、方法、验收标准)并将任务提升到 `todo`。在 `config.yaml` 的 `auxiliary.kanban_decomposer` 和 `auxiliary.triage_specifier` 下配置相关模型。参见主 Kanban 指南中的[自动与手动编排](./kanban#auto-vs-manual-orchestration)。 - **Todo(待办)** — 已创建但等待依赖项,或尚未分配。 - **Ready(就绪)** — 已分配,等待 dispatcher 认领。 - **In progress(进行中)** — worker 正在主动执行任务。开启"Lanes by profile"(默认开启)时,此列按负责人分组,让你一眼看出每个 worker 正在做什么。 - **Blocked(阻塞)** — worker 请求人工输入,或熔断器触发。 - **Done(完成)** — 已完成。 顶部栏提供搜索、租户和负责人的筛选器,以及 `Lanes by profile` 切换按钮和 `Nudge dispatcher` 按钮——后者会立即执行一次调度 tick,而无需等待守护进程的下一个间隔。点击任意卡片会在右侧打开其详情抽屉。 ### 平铺视图 如果 profile 泳道显示过于嘈杂,关闭"Lanes by profile",In Progress 列会折叠为按认领时间排序的单一平铺列表: ![Board with lanes by profile off](/img/kanban-tutorial/02-board-flat.png) ## 场景一 — 独立开发者交付功能 你正在开发一个功能。经典流程:设计 schema、实现 API、编写测试。三个任务,具有父→子依赖关系。 ```bash SCHEMA=$(hermes kanban create "Design auth schema" \ --assignee backend-dev --tenant auth-project --priority 2 \ --body "Design the user/session/token schema for the auth module." \ --json | jq -r .id) API=$(hermes kanban create "Implement auth API endpoints" \ --assignee backend-dev --tenant auth-project --priority 2 \ --parent $SCHEMA \ --body "POST /register, POST /login, POST /refresh, POST /logout." \ --json | jq -r .id) hermes kanban create "Write auth integration tests" \ --assignee qa-dev --tenant auth-project --priority 2 \ --parent $API \ --body "Cover happy path, wrong password, expired token, concurrent refresh." ``` 由于 `API` 以 `SCHEMA` 为父任务,`tests` 以 `API` 为父任务,只有 `SCHEMA` 从 `ready` 状态开始。其他两个任务在 `todo` 中等待,直到其父任务完成。这正是依赖提升引擎在发挥作用——在有 API 可测试之前,不会有其他 worker 去接手测试编写工作。 在下一次 dispatcher tick 时(默认 60 秒,或点击 **Nudge dispatcher** 立即触发),`backend-dev` profile 会以 `HERMES_KANBAN_TASK=$SCHEMA` 作为环境变量生成一个 worker。以下是该 worker 在 agent 内部的工具调用循环: ```python # worker tool calls — NOT commands you run kanban_show() # → 返回 title、body、worker_context、parents、prior attempts、comments # (worker 读取 worker_context,使用终端/文件工具设计 schema, # 编写迁移脚本,运行自身检查,提交——真正的工作在这里发生) kanban_heartbeat(note="schema drafted, writing migrations now") kanban_complete( summary="users(id, email, pw_hash), sessions(id, user_id, jti, expires_at); " "refresh tokens stored as sessions with type='refresh'", metadata={ "changed_files": ["migrations/001_users.sql", "migrations/002_sessions.sql"], "decisions": ["bcrypt for hashing", "JWT for session tokens", "7-day refresh, 15-min access"], }, ) ``` `kanban_show` 默认将 `task_id` 设为 `$HERMES_KANBAN_TASK`,因此 worker 无需知道自己的 id。`kanban_complete` 将 summary 和 metadata 写入当前 `task_runs` 行,关闭该 run,并将任务转换为 `done`——全部通过 `kanban_db` 以原子方式完成。 当 `SCHEMA` 进入 `done` 状态时,依赖引擎会自动将 `API` 提升为 `ready`。API worker 认领任务后,调用 `kanban_show()` 时会看到 `SCHEMA` 的 summary 和 metadata 附加在父任务交接信息中——因此它无需重新阅读冗长的设计文档就能了解 schema 的决策。 在看板上点击已完成的 schema 任务,抽屉会显示所有信息: ![Solo dev — completed schema task drawer](/img/kanban-tutorial/03-drawer-schema-task.png) 底部的 Run History 部分是关键新增内容。一次尝试:结果 `completed`,worker `@backend-dev`,耗时、时间戳,以及完整的交接 summary。metadata 块(`changed_files`、`decisions`)也存储在 run 上,并会呈现给读取该父任务的任何下游 worker。 你可以随时在终端检查相同的数据——以下命令是**你**查看看板,而非 worker 执行: ```bash hermes kanban show $SCHEMA hermes kanban runs $SCHEMA # # OUTCOME PROFILE ELAPSED STARTED # 1 completed backend-dev 0s 2026-04-27 19:34 # → users(id, email, pw_hash), sessions(id, user_id, jti, expires_at); refresh tokens ... ``` ## 场景二 — 集群并行处理 你有三个 worker(翻译员、转录员、文案撰写员)和一批相互独立的任务。你希望三者并行拉取任务并产生可见进展。这是最简单的 kanban 使用场景,也是最初设计所优化的场景。 创建工作任务: ```bash for lang in Spanish French German; do hermes kanban create "Translate homepage to $lang" \ --assignee translator --tenant content-ops done for i in 1 2 3 4 5; do hermes kanban create "Transcribe Q3 customer call #$i" \ --assignee transcriber --tenant content-ops done for sku in 1001 1002 1003 1004; do hermes kanban create "Generate product description: SKU-$sku" \ --assignee copywriter --tenant content-ops done ``` 启动 gateway 然后离开——它托管内嵌的 dispatcher, 在同一个 kanban.db 上处理三个专家 profile 的任务: ```bash hermes gateway start ``` 现在将看板筛选到 `content-ops`(或直接搜索"Transcribe"),你会看到: ![Fleet view filtered to transcribe tasks](/img/kanban-tutorial/07-fleet-transcribes.png) 两个转录任务已完成,一个正在运行,两个就绪等待下一次 dispatcher tick。In Progress 列按 profile 分组("Lanes by profile"默认开启),让你无需扫描混合列表即可看到每个 worker 的当前任务。dispatcher 会在当前任务完成后立即将下一个就绪任务提升为运行中。三个守护进程并行处理三个负责人池,整个内容队列无需进一步人工干预即可清空。 **场景一中关于结构化交接的所有内容在这里同样适用。** 完成一次通话的翻译 worker 会发出 `kanban_complete(summary="translated 4 pages, style matched existing marketing voice", metadata={"duration_seconds": 720, "tokens_used": 2100})`——对分析以及依赖此任务的任何下游任务都很有价值。 ## 场景三 — 角色流水线与重试 这正是 Kanban 相比普通 TODO 列表的价值所在。PM 编写规格说明,工程师实现,审查者拒绝第一次尝试,工程师修改后再次尝试,审查者批准。 dashboard 视图,按 `auth-project` 筛选: ![Pipeline view for a multi-role feature](/img/kanban-tutorial/08-pipeline-auth.png) 三个阶段的链条一目了然:`Spec: password reset flow`(DONE,pm)、`Implement password reset flow`(DONE,backend-dev)、`Review password reset PR`(READY,reviewer)。每个任务底部都有绿色的父任务,以及作为依赖项的子任务。 最有趣的是实现任务,因为它经历了阻塞和重试。以下是完整的三 agent 协作流程,以每个 worker 模型发出的工具调用形式展示: ```python # --- PM worker 在 $SPEC 上生成并编写验收标准 --- # worker tool calls kanban_show() kanban_complete( summary="spec approved; POST /forgot-password sends email, " "GET /reset/:token renders form, POST /reset applies new password", metadata={"acceptance": [ "expired token returns 410", "reused last-3 password returns 400 with message", "successful reset invalidates all active sessions", ]}, ) # → $SPEC 完成;$IMPL 自动从 todo 提升为 ready # --- 工程师 worker 在 $IMPL 上生成(第一次尝试)--- # worker tool calls kanban_show() # 在 worker_context 中读取 $SPEC 的 summary 和 acceptance metadata # (工程师编写代码,运行测试,开启 PR) # 审查者反馈到来——工程师认为问题有效并阻塞任务 kanban_block( reason="Review: password strength check missing, reset link isn't " "single-use (can be replayed within 30min)", ) # → $IMPL 转换为 blocked;run 1 以 outcome='blocked' 关闭 ``` 现在你(人类,或单独的 reviewer profile)读取阻塞原因,判断修复方向明确,从 dashboard 的"Unblock"按钮解除阻塞——或通过 CLI/斜杠命令: ```bash hermes kanban unblock $IMPL # 或在聊天中:/kanban unblock $IMPL ``` dispatcher 将 `$IMPL` 提升回 `ready`,并在下一次 tick 时重新生成 `backend-dev` worker。这第二次生成是同一任务上的**新 run**: ```python # --- 工程师 worker 在 $IMPL 上生成(第二次尝试)--- # worker tool calls kanban_show() # → worker_context 现在包含 run 1 的阻塞原因,因此该 worker 知道 # 需要修复哪两个问题,而无需重新阅读整个规格说明 # (工程师添加 zxcvbn 检查,使重置令牌变为一次性,重新运行测试) kanban_complete( summary="added zxcvbn strength check, reset tokens are now single-use " "(stored + deleted on success)", metadata={ "changed_files": [ "auth/reset.py", "auth/tests/test_reset.py", "migrations/003_single_use_reset_tokens.sql", ], "tests_run": 11, "review_iteration": 2, }, ) ``` 点击实现任务,抽屉显示**两次尝试**: ![Implementation task with two runs — blocked then completed](/img/kanban-tutorial/04b-drawer-retry-history-scrolled.png) - **Run 1** — `@backend-dev` 标记为 `blocked`。审查反馈紧跟在结果下方:"password strength check missing, reset link isn't single-use (can be replayed within 30min)"。 - **Run 2** — `@backend-dev` 标记为 `completed`。全新的 summary,全新的 metadata。 每个 run 在 `task_runs` 中都是独立的一行,有自己的 outcome、summary 和 metadata。重试历史不是叠加在"最新状态"任务之上的概念性附加物——它是主要的数据表示形式。当重试的 worker 打开任务时,`build_worker_context` 会向其展示之前的尝试,因此第二次 worker 能看到第一次被阻塞的原因,并针对性地解决那些具体问题,而不是从头重来。 审查者接下来认领任务。当他们打开 `Review password reset PR` 时,会看到: ![Reviewer's drawer view of the pipeline](/img/kanban-tutorial/09-drawer-pipeline-review.png) 父任务链接指向已完成的实现任务。当审查者的 worker 在 `Review password reset PR` 上生成并调用 `kanban_show()` 时,返回的 `worker_context` 包含父任务最近一次已完成 run 的 summary 和 metadata——因此审查者在查看 diff 之前就已读到"added zxcvbn strength check, reset tokens are now single-use",并掌握了变更文件列表。 ## 场景四 — 熔断器与崩溃恢复 真实的 worker 会失败。缺少凭证、OOM 终止、瞬时网络错误。dispatcher 有两道防线:**熔断器**(circuit breaker)在连续 N 次失败后自动阻塞任务,防止看板无限抖动;**崩溃检测**(crash detection)在 worker PID 于 TTL 到期前消失时回收任务。 ### 熔断器 — 持续性失败 一个因 profile 环境中未设置 `AWS_ACCESS_KEY_ID` 而无法生成 worker 的部署任务: ```bash hermes kanban create "Deploy to staging (missing creds)" \ --assignee deploy-bot --tenant ops \ --max-retries 3 ``` dispatcher 尝试生成 worker。生成失败(`RuntimeError: AWS_ACCESS_KEY_ID not set`)。dispatcher 释放认领,递增失败计数器,并在下一次 tick 重试。由于本示例设置了 `--max-retries 3`,在三次连续失败后熔断器触发:任务进入 `blocked` 状态,outcome 为 `gave_up`。如果省略该标志,Hermes 使用 `kanban.failure_limit`(默认值:2)。在人工解除阻塞之前不再重试。 点击被阻塞的任务: ![Circuit breaker — 2 spawn_failed + 1 gave_up](/img/kanban-tutorial/11-drawer-gave-up.png) 三个 run,`error` 字段均为相同错误。前两个为 `spawn_failed`(可重试),第三个为 `gave_up`(终止)。上方的事件日志显示完整序列:`created → claimed → spawn_failed → claimed → spawn_failed → claimed → gave_up`。 在终端: ```bash hermes kanban runs t_ef5d # # OUTCOME PROFILE ELAPSED STARTED # 1 spawn_failed deploy-bot 0s 2026-04-27 19:34 # ! AWS_ACCESS_KEY_ID not set in deploy-bot env # 2 spawn_failed deploy-bot 0s 2026-04-27 19:34 # ! AWS_ACCESS_KEY_ID not set in deploy-bot env # 3 gave_up deploy-bot 0s 2026-04-27 19:34 # ! AWS_ACCESS_KEY_ID not set in deploy-bot env ``` 如果接入了 Telegram/Discord/Slack,gateway 会在 `gave_up` 事件时发送通知,让你无需主动检查看板就能得知故障。 ### 崩溃恢复 — worker 在运行中途死亡 有时生成成功,但 worker 进程在之后死亡——段错误、OOM、`systemctl stop`。dispatcher 轮询 `kill(pid, 0)` 检测到死亡的 pid;认领释放,任务回到 `ready`,下一次 tick 将其分配给新的 worker。 种子数据中的示例是一个因内存不足而运行失败的迁移任务: ```bash # Worker 认领,开始扫描 240 万行,在约 230 万行时被 OOM 终止 # Dispatcher 检测到死亡的 pid,释放认领,递增尝试计数器 # 使用分块策略重试成功 ``` 抽屉显示完整的两次尝试历史: ![Crash and recovery — 1 crashed + 1 completed](/img/kanban-tutorial/06-drawer-crash-recovery.png) Run 1 — `crashed`,错误为 `OOM kill at row 2.3M (process 99999 gone)`。Run 2 — `completed`,metadata 中包含 `"strategy": "chunked with LIMIT + WHERE id > last_id"`。重试的 worker 在其上下文中看到了 run 1 的崩溃信息,并选择了更安全的策略;metadata 让未来的观察者(或事后分析撰写者)能清楚地看到发生了什么变化。 ## 结构化交接 — `summary` 和 `metadata` 的重要性 在上述每个场景中,worker 在结束时都调用了 `kanban_complete(summary=..., metadata=...)`。这不是装饰性的——它是工作流各阶段之间的主要交接通道。 当任务 B 上的 worker 被生成并调用 `kanban_show()` 时,返回的 `worker_context` 包含: - B 的**先前尝试**(之前的 run:outcome、summary、error、metadata),让重试的 worker 不会重蹈失败的路径。 - **父任务结果** — 对于每个父任务,最近一次已完成 run 的 summary 和 metadata——让下游 worker 能看到上游工作的原因和方式。 这取代了平面 kanban 系统中"翻查评论和工作输出"的繁琐流程。PM 在规格说明的 metadata 中编写验收标准,工程师的 worker 在父任务交接中以结构化形式看到它们。工程师记录运行了哪些测试以及通过了多少,审查者的 worker 在打开 diff 之前就已掌握该列表。 批量关闭保护的存在正是因为这些数据是按 run 存储的。`hermes kanban complete a b c --summary X`(你,从 CLI 执行)会被拒绝——将相同的 summary 复制粘贴到三个任务几乎总是错误的。不带交接标志的批量关闭仍然适用于常见的"我完成了一堆行政任务"场景。工具界面根本不提供批量变体;`kanban_complete` 始终是单任务操作,原因相同。 ## 检查当前正在运行的任务 作为补充——以下是一个仍在执行中的任务的抽屉视图(场景一中的 API 实现,已被 `backend-dev` 认领但尚未完成): ![Claimed, in-flight task](/img/kanban-tutorial/10-drawer-in-flight.png) 状态为 `Running`。活跃的 run 出现在 Run History 部分,outcome 为 `active`,没有 `ended_at`。如果该 worker 死亡或超时,dispatcher 会以相应的 outcome 关闭此 run,并在下一次认领时开启新的 run——尝试记录永远不会消失。 ## 后续步骤 - [Kanban 概述](./kanban) — 完整的数据模型、事件词汇表和 CLI 参考。 - `hermes kanban --help` — 所有子命令,所有标志。 - `hermes kanban watch --kinds completed,gave_up,timed_out` — 在整个看板上实时流式输出终端事件。 - `hermes kanban notify-subscribe --platform telegram --chat-id ` — 当特定任务完成时通过 gateway 接收推送通知。