跳到主要内容

看板工作车道(Worker Lanes)

工作车道(Worker Lane) 是看板调度器(dispatcher)可将任务路由到的一类进程。每条车道拥有一个标识(assignee 字符串)、一种生成机制(spawn mechanism),以及一份关于任务生成后必须执行什么工作的约定(contract)。

本页面描述的就是这份约定。它的目标读者有两类:

  • 运维人员(Operators):负责选择将哪些车道接入看板(需要创建哪些配置文件、使用哪些 assignee)。
  • 插件/集成开发者:希望新增一种车道形态(例如封装 Codex / Claude Code / OpenCode 的 CLI 工作器、容器化的评审工作器、通过 API 拉取任务的非 Hermes 外部服务)。

如果你要编写的是运行在车道内部的代理(agent)代码,请参考 kanban-worker 技能文档,其中有更详细的流程说明。

层级关系

Hermes 看板 = 标准任务生命周期 + 审计追踪
工作车道 = 单张已分配卡片的执行器
评审者 = 把关「完成」状态的人类或人类代理
GitHub PR = 可上游合并的产物(可选,仅代码车道涉及)

Hermes 看板拥有生命周期的唯一事实来源 —— readyrunningblocked / done / archived。工作车道负责执行工作,但绝不拥有该事实来源;它们产生的一切都通过 kanban_* 工具(或非 Hermes 外部工作器通过 API)回流至看板内核。评审者把关从「代码变更已写入」到「任务完成」的状态转换。

车道需要提供什么

要成为一个看板工作车道,一个集成必须提供三样东西:

1. 一个 assignee 字符串

调度器将 task.assignee 与 Hermes 配置文件名(默认车道形态)或已注册的非生成型标识符(插件车道形态 —— 见下方「添加外部 CLI 工作车道」)进行匹配。无法解析 assignee 的任务将停留在 ready 状态,并附带一条 skipped_nonspawnable 事件,以便看板运维人员修复;它们不会被静默丢弃或由任意回退车道执行。

2. 一种生成机制(Spawn Mechanism)

对于 Hermes 配置文件车道,调度器的 _default_spawn 在任务绑定的工作区内运行 hermes -p <assignee> chat -q <prompt>(若 $PATH 中不存在 hermes 入口脚本,则使用等效的模块调用形式),并传入以下环境变量:

变量携带内容
HERMES_KANBAN_TASK工作器正在处理的任务 ID
HERMES_KANBAN_DB每块看板对应的 SQLite 文件的绝对路径
HERMES_KANBAN_BOARD看板 slug
HERMES_KANBAN_WORKSPACES_ROOT看板工作区目录树的 root
HERMES_KANBAN_WORKSPACE 任务工作区的绝对路径
HERMES_KANBAN_RUN_ID当前运行的 ID(用于生命周期门控)
HERMES_KANBAN_CLAIM_LOCK声明锁字符串(<host>:<pid>:<uuid>
HERMES_PROFILE工作器自身的配置文件名(用于 kanban_comment 的作者归属)
HERMES_TENANT租户命名空间(如果任务有设置的话)

对于非 Hermes 车道(通过插件注册),插件需提供自己的 spawn_fn 可调用对象,该函数接收 taskworkspaceboard,并返回一个可选的 PID 用于崩溃检测。

3. 一个生命周期终止器(Lifecycle Terminator)

每次声明(claim)必须恰好以以下结果之一结束:

  • kanban_complete(summary=..., metadata=...) — 任务成功,状态翻转为 done
  • kanban_block(reason=...) — 任务等待人工输入,状态翻转为 blocked。当 kanban_unblock 被调用时,调度器会重新生成(respawn)工作器。
  • 工作器进程退出时未调用任何工具。内核会回收它并发出 crashed(PID 已死)、gave_up(连续失败断路器触发)或 timed_out(超过 max_runtime)。这是失败路径;健康的工作器不会在此结束。

看板内核强制规定每次运行必须恰好以上述结果之一终止。一个既未调用 kanban_complete 也未调用 kanban_block 就正常退出的工作器,会被视为崩溃(crashed)。

输出与「需要评审」约定

对于大多数涉及代码变更的任务,工作完成的时刻并不是工作器结束的那一刻 —— 它需要一位人类评审者。「需要评审(review-required)」的区分由看板内核强制执行(「代码变更任务」的边界很模糊,如果强制要求每个代码工作器都改为调用 block 而不是 complete,会破坏那些不需要评审的流程)。这是一层建立在之上的约定:

  • 调用 block 而非 complete,并在 reason 前加上 review-required: 前缀,以便仪表盘 / hermes kanban show 将该行标记为「等待评审」。
  • 先将结构化元数据写入一条 kanban_comment,因为 kanban_block 只携带人类可读的 reason。评论是持久的注解通道 —— 每个与审计相关的字段(changed_files、tests_run、diff_path 或 PR URL、decisions)都应放在那里。
  • 评审者要么批准并解除阻塞(unblock),这会带着评论线程重新生成工作器以进行跟进;要么通过另一条评论要求修改,下一次工作器运行会将这条评论视为 kanban_show 上下文的一部分。

kanban-worker 技能中包含 kanban_complete(真正终端型的任务 —— 错别字修复、文档变更、研究综述)和 review-required 阻塞模式的具体示例。

日志与审计追踪

调度器将每个任务的 worker stdout/stderr 写入 <board-root>/logs/<task_id>.log。日志可通过看板元数据审计:

  • task_runs 表行携带 log_path、退出码(如可用)、summary 和 metadata。
  • task_events 表行携带每一次状态转换(promotedclaimedheartbeatcompletedblockedgave_upcrashedtimed_outreclaimedclaim_extended)。
  • kanban_show 会返回以上两者,因此评审者(或后续工作器)读取任务时,无需访问仪表盘即可获得完整历史。

仪表盘会渲染运行历史,包含摘要、元数据块和退出状态徽章。CLI 用户可以运行 hermes kanban tail <task_id> 实时跟踪,或运行 hermes kanban runs <task_id> 查看历史尝试列表。

现有的车道形态

Hermes 配置文件车道(默认)

这是当前所有看板工作器采用的形态:assignee 是一个配置文件名,调度器生成 hermes -p <profile>,工作器自动加载 kanban-worker 技能和 KANBAN_GUIDANCE 系统提示块,并使用 kanban_* 工具终止运行。除了定义配置文件外,无需其他设置。

为你的工作器集群(fleet)创建配置文件时,请选择与你希望编排器(orchestrator)路由到的角色相匹配的名称。编排器(当存在时)通过 hermes profile list 发现你的配置文件名 —— 系统不假定任何固定的名册(参见 kanban-orchestrator 技能了解合约的编排器侧)。

编排器配置文件车道

配置文件车道的一种特化:编排器是一个 Hermes 配置文件,其工具集包含 kanban 但排除 terminal / file / code / web 等实现类工具。它的工作是通过 kanban_create + kanban_link 将高层级目标分解为子任务,然后退出。编排器技能中编码了「反诱惑(anti-temptation)」规则。

添加外部 CLI 工作车道

将非 Hermes 的 CLI 工具(Codex CLI、Claude Code CLI、OpenCode CLI、本地编码模型运行器等)接驳为看板工作车道,目前还不是一条铺设好的路径。调度器的生成函数是可插拔的(spawn_fndispatch_once 的一个参数),插件可以为非 Hermes 的 assignee 注册自己的 spawn_fn,但围绕它的集成工作 —— 将 CLI 的退出码包装为 kanban_complete / kanban_block 调用、将 CLI 的工作区/沙箱约定映射到调度器的 HERMES_KANBAN_WORKSPACE 环境变量、处理认证和每个 CLI 的策略 —— 仍然是每个集成各自的设计工作。

如果你正在考虑添加一条 CLI 车道,请开一个 issue 描述具体的 CLI 和你想要启用的流程。上述约定是任何此类车道都必须满足的约束;实现形态(每个 CLI 一个插件 vs 一个由配置参数化的通用 CLI 运行器插件)尚待确定。

相关的历史 issue 是 #19931,以及已关闭但未合并的 Codex 专用 PR #19924 —— 这些描述了最初的架构提案,但并未落地一个运行器。

调度器处理的失败模式

车道开发者无需自行重新实现以下内容:

  • 陈旧声明 TTL —— 一个声明了任务但从不发送心跳 / 完成 / 阻塞的工作器,会在 DEFAULT_CLAIM_TTL_SECONDS(默认 15 分钟)后被回收 —— 但前提是工作器进程确实已经死亡。一个活跃的工作器(慢速模型在一次无工具调用的 LLM 调用中花费 20+ 分钟)会导致声明被延长而非终止;只有已死的 PID 才会被回收。
  • 崩溃的工作器 —— 宿主本地 PID 已消失的工作器由 detect_crashed_workers 检测并回收;任务递增 consecutive_failures,断路器触发时可能自动阻塞。
  • 运行级别重试 —— 当任务被重试时(阻塞后、崩溃后、回收后),工作器可以使用终止工具上的 expected_run_id 参数来快速失败(fail fast),如果它自己的运行已被取代的话。
  • 单任务最大运行时间 —— task.max_runtime_seconds 硬性限制每次运行的实际经过时间(wall-clock time),不论 PID 是否存活。这能捕获那些「真正死锁」的工作器 —— 否则存活 PID 延期机制会让它们一直运行下去。
  • 滞留任务检测 —— 一个 ready 任务在其 assignee 在 kanban.stranded_threshold_seconds(默认 30 分钟)内从未产生声明时,hermes kanban diagnostics 会将其显示为 stranded_in_ready 警告。严重性在阈值的 2 倍时升级为 error,6 倍时升级为 critical。这能用一个信号同时捕获打错的 assignee、已删除的配置文件和宕机的外部工作器池 —— 与身份无关,无需维护每个看板的允许列表。

相关文档