You need to sign in before continuing.
Commit 677fec23869170d60906c03922a371d382552322
1 parent
e9bb6feb
docs: Boss Agent requirements -- autonomous arbiter for blocked_pending_human
Requirements doc (BRD/PRD level) for a new org-level role that monitors blocked_pending_human tasks, does real diagnosis (not blind retry), and acts within an explicit authorization boundary instead of always waiting on a literal human -- generalizing the planner_reviewer precedent (an autonomous judgment agent already replacing a human review gate) to this surface. Motivated by today's real incident: 4 tasks blocked with the same generic execution_error reason but three genuinely different root causes (stale process, legacy narrative-output format, a real concurrency bug), all diagnosed by hand. Also documents watch_resume.py's real concurrency-race flaw as a requirement input (FR-6) so the eventual implementation doesn't repeat it. Deliberately does not pick the autonomy boundary per blocked_reason -- left as an open question for explicit user sign-off, not something Planner should infer silently. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Showing
1 changed file
with
133 additions
and
0 deletions
docs/todo/boss-agent-autonomous-blocked-task-arbiter-requirements.md
0 → 100644
| 1 | +# Boss Agent——自主处理 blocked_pending_human 的仲裁角色——需求文档 | ||
| 2 | + | ||
| 3 | +**日期**: 2026-08-13 | ||
| 4 | +**性质**: 需求文档(BRD/PRD 层面),不含详细技术设计。按本仓库既有约定,这份文档确认后本身就 | ||
| 5 | +是喂给 graphlemon 自己(Planner→planner_reviewer→Coder→Validator)的一个真实 Goal,由它自举 | ||
| 6 | +实现,不是本次会话直接写代码。 | ||
| 7 | +**状态**: 草案,待确认后作为 Goal 提交。 | ||
| 8 | + | ||
| 9 | +## 1. 背景 | ||
| 10 | + | ||
| 11 | +`task.status = 'blocked_pending_human'` 目前的语义是"系统遇到自己解决不了的情况,字面意义上 | ||
| 12 | +等一个真人来决定下一步"。触发它的 `blocked_reason` 目前有一个明确的枚举(散落在 | ||
| 13 | +`src/scheduler.py::_BLOCKED_REASON_FOR_EXCEPTION`、`src/budget_guardrail.py`、 | ||
| 14 | +`src/org_loop_route.py`、`src/backend_server.py` 里):`budget_exceeded`、`loop_limit_reached`、 | ||
| 15 | +`classifier_blocked`、`corrupt_persisted_state`、`usage_limit_wait_timeout`、 | ||
| 16 | +`prd_consistency_check_failed`、`planner_review_loop_limit_reached`,以及一个兜底的 | ||
| 17 | +`execution_error`(任何未被前面几类捕获的异常都落进这里)。 | ||
| 18 | + | ||
| 19 | +今天(2026-08-13)自举验证 5 个真实任务时的实况,是这个问题的真实成本:4 个任务先后卡在 | ||
| 20 | +`blocked_pending_human`,`blocked_reason` 全部显示同一个词 `execution_error`,但背后其实是三种 | ||
| 21 | +完全不同的真实原因——(a) 两个任务是 `backend_server` 进程跑的是过期代码(一次 schema 迁移改动 | ||
| 22 | +还没重启生效);(b) 一个任务是旧版 Dynamic Workflow 机制遗留的叙述性输出格式,被新的 | ||
| 23 | +`root_cause_category` 枚举校验拒绝;(c) 两个任务是本次会话新发现的真实并发 bug(`planner_reviewer` | ||
| 24 | +打回 INCONCLUSIVE/FAIL 后,重新规划的路径会在同一个 `task_id` 已有未终态 `work_graph_run` 时再插 | ||
| 25 | +一行,撞 `ux_work_graph_run_active` 唯一索引)。这四种情况需要的应对完全不同(重启生效、判断是否 | ||
| 26 | +真的是叙述性输出、修一个真实的并发 bug),但从任务列表上看,全部只显示同一个不可区分的 | ||
| 27 | +`execution_error`——过去几个小时里,区分这四种情况、决定怎么处理,全靠人(这次是用户 + Claude | ||
| 28 | +Code 协作)手动读 `execution_event`/日志/代码逐个诊断。 | ||
| 29 | + | ||
| 30 | +这个诊断过程本身没有用到任何人类独有的判断力——读 DB 里的 `execution_event`/`task`/ | ||
| 31 | +`work_graph_run`,读日志,读代码,判断真正卡在哪一类,能不能自主处理——这正是 | ||
| 32 | +`planner_reviewer`(Org Graph unification Phase 1,commit `ea19c7d`)已经验证过的模式:"把原本 | ||
| 33 | +需要人工审核的一道关卡换成自主判断的 agent",只是 `planner_reviewer` 把关的是 Planner 产出,这次 | ||
| 34 | +要把关的是 `blocked_pending_human`。 | ||
| 35 | + | ||
| 36 | +今天临时手搭的 `watch_resume.py`(本次会话内一次性脚本,轮询任务列表、对首次进入 | ||
| 37 | +`blocked_pending_human` 的目标任务调用 `manual_resume`)是这个需求最原始的雏形,但它只会"不看 | ||
| 38 | +原因、直接再试一次",不做真实诊断——而且这次会话已经实测出它的一个真实缺陷:一次性对多个任务 | ||
| 39 | +并发调用 `manual_resume`,会在全局并发上限(`max_concurrent_goals`)检查上出现竞态,导致本该排队 | ||
| 40 | +的任务反而立刻被判超限、又打回 `blocked_pending_human`。Boss Agent 需要真正吸取这个教训,不能只 | ||
| 41 | +是"更勤快地重试"。 | ||
| 42 | + | ||
| 43 | +## 2. 目标 | ||
| 44 | + | ||
| 45 | +一个新的、常驻的 org 级角色(暂定名 **Boss Agent**,`role_id` 暂定 `boss_agent`——**不要**叫 | ||
| 46 | +"Supervisor",`CLAUDE.md` 已经记录过两个同名 Supervisor 概念互相混淆的坑,这会是第三个),职责是: | ||
| 47 | + | ||
| 48 | +1. 常驻轮询 `task.status = 'blocked_pending_human'` 的任务(轮询模式跟 FR-1 | ||
| 49 | + `docs/done/scheduler-new-goal-dispatch-requirements.md` 已经实现的常驻轮询是同一个载体形式的 | ||
| 50 | + 姊妹机制——那个负责"新 Goal 怎么自动开始",这个负责"已经卡住的任务怎么自动接回去",两者是独 | ||
| 51 | + 立循环,不合并)。 | ||
| 52 | +2. 对每个卡住的任务做真实诊断:读 `blocked_reason` + 相关 `execution_event`/日志/代码,判断这次 | ||
| 53 | + 卡住的真实原因属于哪一类(复用今天实际观察到的分类作为起点:过期进程/环境问题、真实代码 | ||
| 54 | + bug、内容/规划类问题、资源类限制如 `budget_exceeded`)。 | ||
| 55 | +3. 在**明确的自主权边界**内(边界怎么画见 §7 开放问题)自主决定并执行下一步——可以是直接 | ||
| 56 | + `manual_resume`、也可以是需要先动代码修一个真实 bug(本次会话的实际先例:直接改代码修 | ||
| 57 | + `src/schema.py` 的迁移函数、改 `src/org_loop_route.py` 的并发问题),修完再重跑受影响的任务。 | ||
| 58 | + 超出边界的,才真正升级给人,而不是像现在这样"只要卡住就一律等人"。 | ||
| 59 | +4. 不论是自主处理还是升级,都要留下**可事后追溯的决策记录**(谁/什么时候/诊断出什么原因/做了 | ||
| 60 | + 什么决定/为什么)——匹配本仓库既有的"记忆必须留 git 痕迹,不能是静默可变的本地状态"的原则 | ||
| 61 | + (`.claude/agent-memory/` 的既有约定),不是决策了就完事、无迹可查。 | ||
| 62 | + | ||
| 63 | +## 3. 非目标 | ||
| 64 | + | ||
| 65 | +1. **不在本文档里裁定"哪些 `blocked_reason` 可以完全自主处理、哪些必须升级给人"这个具体边界**。 | ||
| 66 | + 这是一个改变系统自主权边界的真实决策(跟 FR-1 当初"要不要自动派发新 Goal"是同一量级的决策), | ||
| 67 | + 需要用户在设计阶段之前先明确拍板,不能让 Planner 自己悄悄选一种理解——本文档 §7 只列出这个 | ||
| 68 | + 问题本身和已知的参考信息(比如 `budget_exceeded` 涉及真实花费、动生产 DB 这类操作需要先 | ||
| 69 | + dry-run),不代为决定。 | ||
| 70 | +2. **不要求把这次会话里的 4 个具体任务和相关代码 bug 也一并算进本次实现范围**——那几个 bug 已经 | ||
| 71 | + 由本次会话直接修复(不经过 graphlemon 自己的 Coder),Boss Agent 是面向未来的通用机制,不是 | ||
| 72 | + "顺便把今天的坑填了"。 | ||
| 73 | +3. **不重新设计 `blocked_pending_human` 状态机本身**(`task.status`/`blocked_reason` 的既有取值、 | ||
| 74 | + `org_loop_route.py` 的路由逻辑)。Boss Agent 是在这个既有状态机之上新增一个"自动响应者",不是 | ||
| 75 | + 替换它。 | ||
| 76 | +4. **不等同于、也不合并** FR-1 的新 Goal 自动派发(面向 `queued` 状态的新任务)——两者是完全独立 | ||
| 77 | + 的两类工作,动机相似但触发条件、风险量级都不同,具体理由见 §1 最后一段。 | ||
| 78 | + | ||
| 79 | +## 4. 功能需求 | ||
| 80 | + | ||
| 81 | +| # | 需求描述 | 优先级 | | ||
| 82 | +|---|---|---| | ||
| 83 | +| FR-1 | 需要一个机制,定期扫描 `task.status = 'blocked_pending_human'` 的任务(轮询周期留待设计阶段)。 | 必须 | | ||
| 84 | +| FR-2 | 对每个扫到的任务,基于 `blocked_reason` + 相关 `execution_event`/日志/代码做真实诊断,区分"环境/进程问题"“真实代码 bug”“内容/规划问题”“资源限制”等类别,不能只看 `blocked_reason` 这个粗粒度字符串就下结论(今天 `execution_error` 这一个值背后有三种完全不同真实原因的教训)。 | 必须 | | ||
| 85 | +| FR-3 | 在授权边界内(§7 待拍板)自主执行下一步:可能是直接触发 resume(`POST /api/tasks/:id/route`,`decided_route=manual_resume`),也可能是需要先定位并修复一个真实 bug 后再 resume。 | 必须 | | ||
| 86 | +| FR-4 | 超出授权边界的情况,明确升级给人,并说明升级原因(不能是"我不确定所以升级"这种不可操作的理由,要具体到"诊断出的原因是 X,属于需要人工判断的 Y 类别")。 | 必须 | | ||
| 87 | +| FR-5 | 每一次处理(自主执行或升级)都要留下可事后追溯的决策记录,包含:task_id、诊断出的真实原因、采取的动作、时间。 | 必须 | | ||
| 88 | +| FR-6 | 并发安全:同一时刻可能有多个任务同时卡住,Boss Agent 对它们的处理不能重蹈今天 `watch_resume.py` 的覆辙——批量触发 resume 前必须正确读取当前并发占用(`max_concurrent_goals`),不能对全局并发上限的检查引入竞态。 | 必须 | | ||
| 89 | + | ||
| 90 | +## 5. 验收标准 | ||
| 91 | + | ||
| 92 | +1. 人为制造一个已知、可自主处理类别的 `blocked_pending_human` 任务(比如模拟一次可安全重试的 | ||
| 93 | + `execution_error`),验证 Boss Agent 能在轮询周期内自主诊断、自主 resume,不需要人工点击。 | ||
| 94 | +2. 人为制造一个应当升级的场景(比如 `budget_exceeded`,或诊断置信度不足的情况),验证 Boss | ||
| 95 | + Agent 不会自作主张处理,而是正确升级并留下可读的升级原因。 | ||
| 96 | +3. 同时制造 3+ 个卡住任务、且当前并发已接近上限,验证 Boss Agent 的批量处理不会出现竞态导致 | ||
| 97 | + 任务被错误地重复打回。 | ||
| 98 | +4. 查询决策记录(不论是自主处理还是升级的),能还原出"这个任务什么时候卡住、诊断出什么原因、 | ||
| 99 | + Boss Agent 做了什么"的完整链路,不需要再回头翻 `execution_event`/日志。 | ||
| 100 | + | ||
| 101 | +## 6. 风险 | ||
| 102 | + | ||
| 103 | +**这是本仓库第二次改变系统自主权边界的决策**(第一次是 FR-1 的新 Goal 自动派发,已经拍板"完整 | ||
| 104 | +开放")。跟 FR-1 不同,这次涉及的场景**本质更不均质**——FR-1 的每一次自动派发都是同一件事(开始 | ||
| 105 | +执行一个新 Goal),风险边界清楚;而 `blocked_pending_human` 背后的原因五花八门,有的自主处理零 | ||
| 106 | +风险(比如今天的场景 a:进程重启后重跑),有的自主处理风险很高(`budget_exceeded` 意味着自主决 | ||
| 107 | +定继续花真实的 API 调用费用;涉及生产 DB/schema 的修复如果不先 dry-run 就直接自主执行,本次会话 | ||
| 108 | +已经真实示范过"不 dry-run 直接迁移生产库"是会出真事故的操作)。**"哪些能自主、哪些必须升级"这条 | ||
| 109 | +边界线画错的代价,在两侧是不对称的**——画得太保守,Boss Agent 就退化成"更勤快但没有真正自主权 | ||
| 110 | +的通知器";画得太激进,可能出现自主决策造成真实数据/费用损失后才被发现。这条边界线必须在设计 | ||
| 111 | +阶段之前由用户明确拍板,不能让实现过程中的 Planner/Coder 自己悄悄决定。 | ||
| 112 | + | ||
| 113 | +## 7. 开放问题 | ||
| 114 | + | ||
| 115 | +- [ ] **自主权边界怎么画**:按 `blocked_reason` 分类,哪些默认允许 Boss Agent 完全自主处理 | ||
| 116 | + (比如 `classifier_blocked`/`corrupt_persisted_state` 这类看起来是纯技术性、可安全重试的), | ||
| 117 | + 哪些必须默认升级给人(比如 `budget_exceeded` 涉及真实花费;任何需要修改生产数据库 | ||
| 118 | + schema/迁移的场景)?还是应该反过来,默认全部升级、只有显式加入白名单的类别才允许自主? | ||
| 119 | +- [ ] **决策记录存在哪**:新建一张表(类似 `bug_record` 但记录的是"Boss Agent 的处理决策"而不是 | ||
| 120 | + "缺陷"),还是复用 `execution_event` 追加一种新 `event_type`,还是两者都要? | ||
| 121 | +- [ ] **是否需要同时补上 `bug_record.source` 的枚举缺口**:`bug_record.source` 目前只允许 | ||
| 122 | + `'validator'`/`'human'` 两个值(`src/schema.py:217`)。如果希望 Boss Agent 诊断出的真实代码 | ||
| 123 | + bug 也能走正式的 `bug_record`/`skill_iteration` 闭环(而不是像本次会话一样绕开这条正式路径 | ||
| 124 | + 直接改代码),这个枚举需要新增一个值代表 Boss Agent 自己——这个决策会牵动 | ||
| 125 | + `roles/planner_reviewer/role.json` 之外,是否也要给 Boss Agent 建一份独立的 | ||
| 126 | + `roles/boss_agent/role.json`(用哪些工具权限:只读诊断 vs 允许直接改代码修 bug,是两种完全 | ||
| 127 | + 不同的权限量级,需要先决定 Boss Agent 到底是"诊断+建议"还是"诊断+亲自动手修")。 | ||
| 128 | +- [ ] **轮询周期**:跟 FR-1 的 45 秒、还是"在途任务复原"的 300 秒更接近?`blocked_pending_human` | ||
| 129 | + 的紧迫性介于两者之间,需要拍板一个具体数字。 | ||
| 130 | +- [ ] **载体形式**:跟 FR-1/在途任务复原同款、`backend_server.py` 内置后台线程,还是独立进程? | ||
| 131 | + 如果 Boss Agent 被授权直接改代码修 bug(而不只是 resume),它可能需要调用 `claude -p` | ||
| 132 | + 本身(跟 Planner/Coder/Validator 一样是一次真实 agent 调用),这种情况下用简单的后台线程 | ||
| 133 | + 是否够用,还是需要更接近 Deploy Supervisor 那种独立进程的隔离级别? |