Files
MyAiDesk/nomifun-tauri/docs/guides/autowork-requirements.zh.md
T
freedak f7a720204a Update: 将子项目从 submodule 转为完整内容
- 移除 GovAI, nomifun-tauri, 算力盒子 的 submodule 引用
- 添加所有子项目的完整源代码
- 保留原始 .git 为 .git.bak 备份
2026-07-04 19:20:46 +08:00

16 KiB
Raw Blame History

AutoWork 与 Requirements

AutoWork 是 NomiFun 的旗舰自动化能力:一块 需求看板requirements board)加上一个 编排器orchestrator),由它驱动 AI 智能体(或运行在终端中的 agent CLI)逐条处理这些需求,无需你全程盯着。

你登记需求,按 tag 分组,把 tag 绑定到一个会话(对话或终端),编排器就会按顺序认领、执行并完结它们。当某条需求进入终态时,可以触发 完成通知Lark/飞书 webhook),让你的团队第一时间知道结果。

这里描述的所有内容都是 后端权威 的:AutoWork 在进程启动时自动恢复,无论你是否打开 UI 都会运行。

AutoWork tag-sessions 总览

概念

术语 含义
Requirement 一个工作单元:标题、内容(实际指令)、tag、order_key(按字典序比较的字符串)以及状态。存储在 SQLite 中。
Tag 任意字符串,用来把需求归入一个队列。绑定关系、看板列以及 webhook 路由都以 tag 为键。
Status pendingin_progressdone(或 failed / cancelled)。看板视图每个状态对应一列。
Claim & lease 编排器原子地把某 tag 中 order_key 最小的 pending 需求转为 in_progress,并写入一份带过期时间的租约(lease)。
Lease sweeper 一个后台任务(每 60 秒一次),会把租约已过期、且持有它的会话已不在的 in_progress 行重置回 pending——这样崩溃永远不会让任务孤立。
Orchestrator 每个目标对应一个的循环:认领 → 注入 → 等待 → 完结 → 重复。每个绑定的会话有一个循环。它是常驻的:队列空了就空闲等待,不会退出。
Target 实际执行工作的对象。两种:会话(一个 AI 智能体),或者 终端(通过 PTY 运行的真实 CLI 智能体)。
回合完成 一轮如何宣告"完成"。对智能体目标来说,是该智能体结束本轮回复(或调用 Nomi 专属工具);对终端目标来说,是终端输出静默下来(干净收尾)。
Completion notifier 当需求进入 done/failed/cancelled 时触发的 Lark/飞书 webhook。按 tag 绑定。
IDMM 智能决策模式(Intelligent Decision-Making Mode)——一个会话级监督器,能在 provider 故障和决策卡顿时让目标继续存活。可与 AutoWork 叠加使用。

单条需求的生命周期

pending  ──claim_next()──▶  in_progress (lease)  ──injection──▶  agent / CLI runs
                                  │                                   │
                                  ▼                                   ▼
                       sweeper re-pends if lease         Finish event / quiescence
                       expires & loop is gone                        │
                                                                     ▼
                                                            done | failed | cancelled
                                                                     │
                                                                     ▼
                                                       CompletionNotifier fires (best-effort)

当 tag 为空时编排器 不会 退出。它会等待唤醒通知(外加一个 10 秒兜底轮询),并永久持续认领,因此向已绑定的 tag 新提交的需求几乎是即时被拾取。

它仅在以下情况退出:

  • 你对该目标关闭了 AutoWork
  • 绑定触达了 max_requirements 上限(此时配置会被持久化为已禁用,使该上限在重启后依然生效);或
  • 某个终端目标对应的行被删除(仅仅是 PTY 退出的终端会进入空闲并等待重新启动——它并不会停止循环)。

三种视图

AutoWork 在每个视图中的数据完全相同,视图只是不同的"镜头"。

需求列表 — /requirements

扁平表格。可按 tag、状态或全文搜索过滤。可批量删除选中行。点击行可以打开详情抽屉;编辑 路径是 /requirements/:id/edit新建需求 通过 /requirements?new=1 打开,旧的 /requirements/new 会重定向到这里。

需求列表

看板 — /requirements?view=board

针对所选 tag,每个状态一列。这里有意 通过拖拽来改状态;请使用详情抽屉。看板会在每次 requirements.* 实时事件触发时重取数据,因此能跟随编排器实时变化。

需求看板

Tag sessions — 需求平台 → 扩展能力 → 自动执行

AutoWork 的管理面板(/requirements/extensions?tab=autowork)。列出所有 tag、所有绑定(哪些会话和终端绑定到哪个 tag)、每条绑定的实时运行状态(Idle,或正在执行某轮时为 Active)。每个 tag 的完成 webhook 现在在旁边的 通知 tab/requirements/extensions?tab=notify)。

这里是你"巡视舰队"的地方。要在某条绑定上 启动 AutoWork,请打开会话本身并在那里切换 AutoWork 开关——那才是绑定 tag、设置 max_requirements 和持久化配置的标准位置。

Tag sessions 管理面板

提交一条需求

在列表页点击 新建需求(或访问 /requirements?new=1)。表单包含:

  • 标题:简短的标签。
  • Tag:选择已有 tag 或键入一个新值。tag 在首次使用时会被创建。
  • 内容:交给智能体 / CLI 的实际指令。当作 ticket 来写:上下文足够让智能体不必反问就能开始,并附上清晰的"完成定义"。
  • Order key:用于队列排序的字符串。按字典序排列,因此常见模式如 1.01.11.2.0 等等。值越小越早。
  • 状态:默认是 pending。你也可以在这里手动把某行标记为 donecancelled

提交后该行进入队列。如果已有会话绑定到该 tag,它会立刻被唤醒并开始处理这条需求(前提是没有别的需求排在它前面)。

绑定会话:智能体 vs 终端

一条绑定形如 (target_kind, target_id, tag, max_requirements?)。target kind 只有两种。

智能体目标(一个会话)

打开任意会话。头部有一个 AutoWork 控件。选择 tag,可选地设置完成上限,然后启用。

每一轮中发生的事:

  1. 编排器认领该 tag 中下一条 pending 需求。
  2. 它构造一段注入 prompt,点名该需求并告知智能体如何上报完成状态。具体协议是 engine-aware 的:
    • 仅在 Nomi-engine 会话上,智能体会注册 requirement_complete / requirement_update_status 工具,并由 prompt 要求模型调用它们。
    • 在所有其他 engineACP / Codex / Gemini / Openclaw / Nanobot / Remote)上,智能体不会注册任何 requirement 工具,因此 prompt 使用 无工具协议:把工作做完,以一段纯文本完成说明结束本轮,平台会在本轮干净结束后自动记为 done。失败通过纯文本上报(prompt 要求模型把最后一行以 Requirement failed: 起头,紧跟原因)。
  3. 注入消息会从用户可见的对话记录中隐藏。
  4. 编排器订阅该智能体的流,等待 Finish(干净)或 Error/超时(重置回 pending 或 fail)。同时它会把智能体的文本输出捕获到一份 tail-bounded 的 completion note,存到该需求上;在无工具协议的 engine 上,这份 note 就是发到下游的报告。
  5. 当本轮干净结束时,finalize_if_needed 把该行记为 done 并触发通知器。

终端目标(运行在 PTY 中的 agent CLI

打开预设为 claudecodex 的终端(普通 shell 不符合条件)。 Gemini 终端可以手动运行,但后端目前不会接受它作为终端 AutoWork 目标: 它的回合生命周期和完成契约还没有接入编排器。符合条件的终端头部会显示同一个 AutoWork 控件;绑定一个 tag 并启用即可。

每一轮中发生的事:

  1. 编排器在注入 之前 订阅终端的实时输出流(这样不会漏任何字节)。
  2. 它向 PTY 写入需求 prompt,外面包了一对 bracketed-paste 标记(ESC [200~ … ESC [201~),后跟 CR,使多行文本作为单次粘贴落入 CLI 的编辑器,并由 Enter 实际提交。
  3. prompt 只要求 agent 把活干完、结束本轮回复——不需要打印任何标记。从交互式 TUI 里抓协议字符串被证明不可靠(光标重绘输出、没有干净的换行、模型抄错 code),所以完成判定改为基于回合本身。
  4. 当输出 静默(在最少 3 秒后≥10 秒无输出,且 PTY 还活着),说明 agent 已干完并回到空闲——本轮记为 done,与无工具的对话 agent 用的是同一套「干净收尾即完成」契约。
  5. 如果 agent 无法完成,会被要求用纯文本明确说明(例如最后一行以 Requirement failed: 开头);这类回合在平台层面仍记为 done,拿不准时请回看对话。
  6. 中途 PTY 死亡 → 重置回 pending。整轮硬超时是 1 小时。

强烈推荐 Full Auto。 一旦本轮撞到交互式批准提示,会一直阻塞到超时。每个 agent CLI 都有一个非交互式开关,终端的 "Full Auto" 模式会替你加上(参见 Terminals → Creating a terminal)。

被绑定但 PTY 已退出的终端会让循环以空闲方式存活:当你重新启动该终端时,AutoWork 会从中断处继续——无需先关闭再开启绑定。

启动恢复——它在你不在场时也会运行

编排器的运行集合存放在内存中,但每条绑定的 enabledtagmax_requirements 都已持久化(在会话的 extra.autowork 或终端的 autowork 列中)。进程启动时后端会列出每个用户、遍历每条 tag 绑定,并 自行启动 这些循环。要让 AutoWork 工作你不必去打开会话页面;UI 只是把已经在跑的状态展示给你看。

这就是为什么"AutoWork 只在我开着标签页时才工作"是一个 bug 而不是 feature。如果你观察到这种现象,去检查编排器日志中是否有该用户/目标的 resume 失败记录。

完成通知(Lark / 飞书)

当需求进入终态时,会调用 CompletionNotifier。今天它做的事:

  1. 查找该需求 tag 的 per-tag 设置——如果该 tag 没有设置或没有绑定 webhook,通知器静默 no-op。
  2. 按 id 查找绑定的 webhook;如果它处于禁用状态,no-op。
  3. 构造一张 Lark 互动卡片,字段如下: 需求id · 需求名 · 需求内容(截断到 500 字符) · 完成状态done/failed/cancelled · 完成记录(报告)(本轮中捕获的 completion note,截断到 500 字符)。
  4. POST 到 webhook URL。如果该 webhook 配置了 secret,请求会按 Lark 自定义机器人的标准方案签名(HMAC-SHA256(key="{ts}\n{secret}", msg="")base64)。
  5. 失败会以 warn 记录并吞掉——一个不稳定的 webhook 永远不会影响需求状态。

配置步骤

  1. 进入 需求平台 → 扩展能力 → 通知/requirements/extensions?tab=notify)并 Create webhook:填写名称、Lark 自定义机器人 URL,以及(可选的)匹配 secret。点 Test 发一张卡片,验证机器人可达。
  2. 在同一个 通知 tab 里找到该 tag,从 per-tag 下拉框中挑选 webhook。设置按 tag 保存。

你可以随时改变某个 tag 指向哪个 webhook,包括清空绑定以静音该 tag 的通知。

Per-tag webhook 路由

IDMM——让卡顿中的本轮继续存活

IDMM 是一个独立、可选的监督器(nomifun-idmm)。它监视会话,并在检测到卡顿时介入:

  • 规则层(无 LLM:provider 报错、反复重试、模型在工具调用上转圈等等——以确定性策略处理。
  • Sidecar 层:调用一个轻量备用模型来下达下一步决策,避免会话挂死。

当 AutoWork 启动一轮时,它会请求 IDMM(如果已对接)在本轮持续期间 保证监督 该目标。两个特性可以组合:AutoWork 推动前进,IDMM 让每一轮不至于卡死,从而真正进入终态而不是超时。IDMM 与 AutoWork 在同一处切换(会话头部)。

每层策略的细节和介入日志 API 见 crates/backend/nomifun-idmm/

想看完整全貌 —— 规则层、旁路模型、会话保活,以及何时开启 —— 参见专门的 智能决策(IDMM指南。

路由与 API

用途 位置
需求列表 /requirements
看板(按 tag /requirements?view=board
Tag sessions 管理 /requirements/extensions?tab=autowork
通知配置 /requirements/extensions?tab=notify
新建 / 编辑 /requirements?new=1/requirements/:id/edit
旧版 /autowork/requirements/tag-sessions 重定向到 /requirements/extensions?tab=autowork
旧版 /requirements/new/requirements/kanban 重定向到当前 query-param 路由
列出 / 创建需求 GET /api/requirementsPOST /api/requirements
Tags GET /api/requirements/tags
Tag 绑定(管理) GET /api/requirements/tag-bindings
Per-tag 看板 GET /api/requirements/board?tag=…
获取 / 更新 / 删除 `GET
状态 / 完成 / 认领 POST /api/requirements/:id/status…/complete…/claim
AutoWork 开关 / 状态 POST /api/requirements/autoworkGET …/autowork/:kind/:tid
Webhooks `GET
Per-tag webhook `GET

实现注记(写给好奇的你)

  • requirements.conversation_id 有意 不带外键 指向 conversations 表。一条需求一旦创建,会随着重置回 pending 在多个会话之间轮转;用引用完整性把它绑死到单个 conversation 会让清理逻辑变得别扭,而且并不会带来真正的安全保障。请把该列视为参考性字段。
  • 编排器的 wake Notify 与 RequirementService 共用;任何会重置回 pending 或创建工作的状态变更都会触发它,循环也会在每次 claim_next() 调用前后用 armed-then-await 的方式包起来,因此在"claim 返回 None"和"await"之间到达的唤醒永远不会丢。
  • 终端注入仍用 bracketed-paste 标记把多行 prompt 作为单次粘贴落入,再在一拍之后单独写一个 CR 提交(与 paste 同批写入的 CR 会被现代 agent TUI 的 paste-burst 检测吞掉)。
  • 无工具 engine 的 completion note 有上限(MAX_NOTE_CHARS = 4000)且 偏向尾部——智能体倾向于在末尾做总结,因此当需要截断时我们保留尾部。