PicoBot/docs/SUB_AGENT_ORCHESTRATION_REVIEW.md
xiaoxixi ac201a3949 feat: durable agent orchestration with run persistence, inbox continuation, and signal/steer
- AgentCatalog/definitions with strict Markdown frontmatter, delegation graph,
  fail-closed tool scoping, and signal contracts
- structured cancellation (AgentError::Cancelled/TimedOut) across provider
  streams, tool batches, and sleep; /stop drives the same terminal state
- schema v6 run/group/inbox persistence with execution-ID conditional
  transitions and completion-slot reservations
- ExecutionGate separating run quota from provider/tool step permits
- background completion inbox with hidden-trigger continuation turns,
  fairness scheduling, lease release, dead-lettering, and activation recovery
- typed TurnMailbox with two-phase steer admission and atomic consumption at
  turn commit; /stop releases admitted steer events back to pending
- emit_signal tool with contract-enforced rate/dedupe/severity/size limits
- WS run/event projection (GetAgentRuns, AgentRunUpdated, AgentEventUpdated),
  /api/agent-runs* management endpoints, /api/tasks union, WebUI run tree
  and signal cards
- ChannelContext.durable_private persisted for continuation delivery reuse

Version 1.7.0
2026-08-11 11:51:20 +08:00

16 KiB
Raw Blame History

子 Agent 编排与信号投递设计审核报告

状态审核完成2026-08。审核对象为设计提案 docs/SUB_AGENT_ORCHESTRATION_DESIGN.md,该设计尚未实现;本文所有"现状"描述以当前代码和测试为准。

本文结合现有实现逐条核实设计的现状诊断,评估架构合理性,并按严重程度列出缺陷与落地前必须补齐的定义。行号基于审核时的代码快照,后续实现合并后可能过时。

设计方逐项答复见 SUB_AGENT_ORCHESTRATION_REVIEW_RESPONSE.md;已接受结论同步写入设计文档。

1. 审核范围与依据

1.1 审核对象

  • 设计文档:docs/SUB_AGENT_ORCHESTRATION_DESIGN.md(提案,未实现;rg 确认 src/webui/ 中无任何 AgentCatalog/AgentCoordinator/agent_inbox/emit_signal 相关实现)
  • 对照实现:src/agent/sub_agent.rssrc/agent/agent_loop.rssrc/agent/steering.rssrc/session/session.rssrc/session/turn_input.rssrc/session/persistence.rssrc/tools/delegate.rssrc/tools/sleep.rssrc/tools/send_message.rssrc/tools/traits.rssrc/storage/src/work/mod.rssrc/scheduler/mod.rssrc/task_supervisor.rssrc/config/mod.rssrc/gateway/reload.rs

1.2 审核依据

  • docs/ARCHITECTURE.md 与 AGENTS.md 中的架构边界和并发不变量
  • 现有相似机制Scheduler durable leasesrc/storage/scheduler.rs:310-348、WorkManager 乐观并发(src/work/mod.rs:320-342、TurnController"持久化后才 Completed"src/session/persistence.rs:147-167、RuntimeAdmissionsrc/gateway/reload.rs

2. 总体结论

设计方向合理,可以按分期推进;但存在 5 处与现有代码强耦合的接缝缺口A1A5落地前必须先补齐定义否则 Phase 2/3 会被迫返工。

设计的核心决策——foreground/backgroundqueue/steer 两个正交维度、先持久化后唤醒、SQLite inbox 为权威来源、lease/consumed 事务提交、ancestry 环检查、AgentCatalog 绑定运行代——与 PicoBot 既有不变量一致,且现状诊断(设计 §1 的 9 条)逐条属实(见第 3 节)。主要问题不在方向,而在设计与现有 session worker、/stop、AgentLoop 取消机制的衔接处留白过多。

3. 现状诊断核实

设计 §1 的 9 条诊断全部与代码一致:

# 设计诊断 代码证据 结论
1 所有子 Agent 复用同一 LLMProviderConfig SubAgentManager.provider_config 单实例(src/agent/sub_agent.rs:119inline/background 均用它创建 Provider:204、:477 属实
2 无具名角色文件,工具权限由 allowed_tools 临时决定 delegate schema 的 allowed_tools 数组(src/tools/delegate.rs:50-54);未填时用默认只读集(sub_agent.rs:34-41 属实
3 子 Agent 被统一移除 delegate filter_tools 硬编码排除 delegate/todo/reload_configsub_agent.rs:177-181 属实
4 DelegateContext 只有 session/channel/chat sub_agent.rs:90-95;无 caller/parent/depth/ancestry 属实
5 parallel 混淆"委托方是否等待"与"是否并发" run_parallel 就是 join_all(run_inline)sub_agent.rs:332-346 属实
6 后台完成通知直发 MessageBus.outbound,不成为主 Agent 输入 background-task-notifications 任务格式化后 publish_outbound fire-and-forgetsrc/session/session.rs:1757-1776),不写会话历史、不触发 Turn 属实
7 Steering mailbox 只建模用户输入,且继承 /stop 丢弃语义 admission 只推 SourceKind::UserInputsession.rs:2910-2938AgentLoop 防御性归一 role=usersrc/agent/agent_loop.rs:624-628/stopclose_and_take_pending 主动丢弃(session.rs:2120-2128src/agent/steering.rs:215-229 属实
8 SleepTool 只等定时器 tokio::time::sleep 单一路径(src/tools/sleep.rs:72),无 wakeup/cancel 分支 属实
9 send_message 同时覆盖跨 Channel、目标会话写入和同 Turn 附件暂存 src/tools/send_message.rs 的 target/content/origin/files 参数;OutboundDelivery::AttachedToCurrentTurn 同 Turn 分支(src/tools/traits.rs:127-131 属实

补充:设计隐含覆盖了一个现存 bug。 inline 结果截断时提示"完整结果请使用 check_task 查看"sub_agent.rs:809),但 run_inline 从不写 background_tasks 表(只有 run_background 写,sub_agent.rs:373-398check_tasksub_agent.rs:707-713)查不到 inline 结果。设计 Phase 2"Foreground 结果也持久化"§23修复此问题分期安排正确。

4. 设计合理性评估

以下决策予以肯定:

设计点 评估
执行生命周期与投递方式正交化§4.1 干净消除 parallel 的语义混淆;批量 foreground 并发等价旧 parallel 但不作为第三种模式
先持久化后唤醒、内存 wakeup 仅为加速器§6.4、§14.4 与"持久化后才 Completed"既有不变量(persistence.rs:147-167)同构
lease/consumed 与条件更新§14、§16.4 复用 Scheduler durable lease 与 WorkManager 乐观并发的成熟模式
target not in ancestry 拒绝 A→B→A§7.1 以显式 iteration workflow 替代隐式递归,边界正确
授权不依赖 task-local§8 正确诊断现状 DELEGATE_CONTEXT task-localsub_agent.rs:19-28)不是授权事实来源;tokio::spawn 不传播 task-local
AgentCatalog 以 Arc 固定运行代§5.4、§18.4 与 RuntimeAdmission 现有集成一致(SubAgentManager 已接 admissionsub_agent.rs:157-163、353-358
llm_profile 引用现有 config.agents§5.2 Config.agentsget_provider_config(agent_name) 已存在(src/config/mod.rs:45、712-745),无需配置重构
Completion 由运行时自动生成、不依赖模型记得调工具§12.1 正确;emit_signal 无任意目标参数,收敛了权限面
可唤醒 sleep 用 watch revision 而非裸 Notify§17.2 正确规避"输入先于订阅到达"的丢失唤醒竞态
§26 不变量清单 与 ARCHITECTURE.md 一致,可作为实现验收标准
分期顺序§23 Phase 1 纯增量Phase 2 顺带修复 inline 截断 bug依赖方向正确

5. 缺陷清单

严重程度A=主要落地前必须补齐定义B=中等(实现对应 Phase 前补齐C=次要(修订文档即可)。

5.1 A 级:主要缺陷

A1 — Session 队列饱和语义与现状冲突(设计 §15.3

现状session 队列容量 32session.rs:27),满时 try_send 失败直接丢弃输入并回复"队列已满"session.rs:3001-3007)。设计要求 durable event 在队列饱和时"保持 durable pending由 Router 有界重试,不能丢弃",但未定义:

  • agent 事件与用户输入是否共用同一 mpsc共用则用户流量可长期占满队列事件重试无收敛界
  • Router 重试的退避、deadline 与最终处置;
  • §15.4 只拆分了 TurnMailbox 的 laneuser 32/64KiB、agent 8/32KiBsession 级队列的 agent lane 容量与优先级未定义。

A2 — /stop 与 worker 退出时 durable event 的恢复机制缺失(设计 §18.3

现状 /stopcurrent_cancel.take()session.rs:2117)→ close_and_take_pending 丢弃 steering:2120-2128agent_tx.take() 丢弃全部排队任务(:2136→ bump generation/state_version:2139-2140→ 取消后台子任务(:2144-2148。mpsc 被 drop 时没有逐项回调。设计未定义:

  • 被丢弃的内部 AgentTask(含 BackgroundAgentResults)如何触发 inbox lease 释放——只能靠 lease 超时被动收敛(应明说延迟界),或为 AgentTask 增加 Drop guard未提
  • /stop 后 worker 退出(task_rx.recv() 返回 None 即 breaksession.rs:3150-3152),被取消 run 异步生成的 cancelled completion 由谁、何时消费,完整链路未写。

A3 — waiting_children permit 释放在现有结构中无落点(设计 §19.2

AgentLoop 目前没有任何 permit/cancellation 原语(agent_loop.rs 无 CancellationToken/select取消靠 worker 整体 drop futuresession.rs:3778-3804TaskSupervisor 也没有并发上限,只在 stopping 时拒绝 spawntask_supervisor.rs:60-89)。设计只给出原则"permit 限制活跃 Provider/工具步骤",未定义:

  • permit 由谁持有与获取/释放AgentLoopAgentRunnerCoordinator
  • delegate 在父 run 的 tool batch 内执行(agent_loop.rs:1187-1242),父 run 进入等待时释放 permit 的钩子如何嵌入现有批处理流程。

这是 Phase 2 复杂度最高的部分,只给原则不够。

A4 — 结构化取消是前置条件,但 AgentLoop 当前零支持(设计 §18.1

新架构中子 run 是 Coordinator spawn 的独立任务,父 future 被 drop 不再传播取消,必须用显式 CancellationToken 树贯穿 AgentLoop——这是横切重构设计只在 Phase 2 列了一行"实现结构化取消"。另有一处表述需要澄清§3 非目标"不默认硬中断正在进行的 Provider 请求"只约束 steer;现有 /stop 恰是硬 dropdrop process_future 连带中断 provider 流,session.rs:3778-3804)。文档应显式声明 /stop 保持硬语义,避免实现时误读为 /stop 也要走安全边界。

A5 — 内部 continuation Turn 的消息/持久化/渲染模型未定义(设计 §16.3

现有 Turn 以用户消息为起点:先持久化用户消息(session.rs:3191prepare_turn_input 把 runtime context 附加到最后一条 user messagesrc/session/turn_input.rs:19-25WebUI/TUI 按 user/assistant 交替渲染。设计说"内部输入不显示用户气泡",但未定义:

  • continuation Turn 写什么消息行(无 user 行?系统行?)、历史 replay 给 provider 时的形态;
  • 客户端如何渲染无用户消息的 Turn§15.1 的 SourceKind 扩展只解决标记问题);
  • continuation 输出的投递目标:AgentTask 的 channel/chat_id/channel_context 来自 InboundMessagesession.rs:631-641),内部任务没有 channel 上下文,应显式规定投递到 session 最近的 channel/chat。

5.2 B 级:中等缺陷

B1 — browser/resource scope 隔离是行为破坏,且无共享出口(设计 §10 现状子 Agent 复用父对话的 browser sessionbrowser_session_id 回退到 delegate context 的 session_idsub_agent.rs:264-279background 路径 :505-508 同)。改为 root_session_id + run_id 隔离会破坏依赖父会话登录态/cookie 的场景。"确需共享必须由工具定义显式支持"没有给出机制,应指明 browser_profiles persistent ID 为官方共享路径。

B2 — dead_letter 与重试上限策略空缺(设计 §14.3、§21 Phase 3 移除直发通知后continuation 反复失败转 dead_letter 时结果对用户彻底不可见,文档未定义 dead-letter 后的用户可见行为(如回退一条系统通知)。max_pending_inbox_events_per_session=128 打满后新事件的行为同样未定义。

B3 — completion_policy=each 只有字段没有语义(设计 §14.2)。 正文只描述了 all:一个 run 超时会把全组结果交付拖到 group deadline缺少 per-run 提前交付或分组拆分策略。

B4 — "UI 未读状态"是防饥饿的关键依赖,但不存在且未立项(设计 §16.2)。 调度优先级把 queue completion 排在用户输入之后,持续用户流量下后台结果会被无限推迟,设计靠"UI 未读状态"兜底;该 WebUI 功能当前不存在Phase 3 只写了"WebUI 投影",未列为明确工作项。

B5 — cost 字段假设了不存在的定价配置(设计 §6.5、§14.1)。 agent_runs.cost 与 ProviderFactory"复用价格信息"的前提不成立config 从不填 price_input/output_per_millionsrc/config/mod.rs:742-743 硬编码 None无配置键解析。要么补定价配置要么注明 cost 暂为 NULL。

B6 — 子 Agent run 内 sleep 的唤醒语义未定义(设计 §17 全章隐含 root session 的 Turnsub-run 没有"当前 session 用户输入"概念。应显式规定 sub-run 内 sleep 只响应自身 cancellation/timeout否则 TurnWakeupHandle 的来源不明。

5.3 C 级:次要问题

C1 — SQLite UNIQUE 与 NULL 语义(设计 §14.3、§9.6)。 UNIQUE(run_id, event_type, event_key)emit_signal 未提供 dedupe_keyevent_key 的生成规则未定义。idempotency_key 的唯一范围 (root_session_id, caller_run_id, key) 在 ROOT 调用时 caller_run_id 为 NULLSQLite 中 NULL≠NULL 会导致去重失效,需要哨兵值(如 root)。

C2 — 授权与上下文的细节留白(设计 §7.1、§9.1、§10 definition max_depth 与全局 max_tree_depth 是否取 min 未明说;agent_task 工具能否操作本 root session 任务树之外的 run跨 session 越权未明说skills/memory 是否进入子 Agent 上下文未提(现状子 Agent 可带 skills promptsub_agent.rs:188-197memory recall 只在 session Turnturn_input.rs:47)。

C3 — async 别名是多余假设(设计 §22.1)。 现代码从未接受 asyncdelegate.rs:151-165 只解析 inline/background/parallel该迁移条目可删。

C4 — 客户端协议变更未枚举(设计 §20.2、Phase 4 AgentSignal 卡片、"已接纳"状态、continuation Turn 都需要新的 WsOutbound 消息类型(src/protocol.rsPhase 4 只写"添加 AgentSignal UI 和任务树",未列协议变更清单。

6. 修订建议

实现启动前,建议在设计文档中补充五个专项定义(对应 A 级缺陷):

  1. Durable event 与 session 队列的 lane 划分agent 事件是否独立队列、饱和时的重试退避/deadline/dead-letter 策略、与用户输入的优先级关系A1
  2. /stop、worker 退出与 lease 释放的衔接:被丢弃内部任务的 lease 释放路径Drop guard 或明确依赖 lease 超时及延迟界)、/stop 后生成的 cancelled completion 的消费链路A2
  3. Permit 归属:执行 permit 在 AgentLoop/AgentRunner/Coordinator 之间的获取与释放点,特别是 waiting_children 前后的钩子位置A3
  4. Continuation Turn 模型消息行写入形态、provider replay 形态、客户端渲染契约、输出投递目标A5
  5. 取消横切方案CancellationToken 贯穿 AgentLoop 的接口设计,并显式声明 /stop 保持硬 drop 语义、安全边界注入只约束 steerA4

B 级问题建议在对应 Phase 实现前补齐B1/B6 在 Phase 1B5 在 Phase 2B2/B3/B4 在 Phase 3。

7. 分期实施意见

Phase 风险 意见
1 具名 Agent 与 Foreground 纯增量。llm_profile 直接复用 Config::get_provider_configconfig/mod.rs:712-745),无配置重构。注意 B1browser scope 隔离会改变现有子 Agent 共享父会话 browser 的行为,需要迁移说明
2 统一 Run 持久化 改动面最大AgentLoop 取消与 permit 均为横切变更。建议先独立原型"CancellationToken 贯穿 AgentLoop",用现有 sleep 取消测试(sleep.rs:194-237)与 steering 恢复测试锁定回归基线,再叠加 permit
3 Inbox 与 Queue Completion UX 拐点:完成通知从直发 Channel 改为主 Agent continuation。建议保留配置开关回退直发通知覆盖 B2 的 dead-letter 空窗§25.4 测试矩阵是本 Phase 验收关键
4 Emit Signal 与 Steer TurnMailbox 泛化触及 handle_message 核心 admission 路径(session.rs:2815-2953),与 /stop 的原子性必须沿用现有同锁判定模式;先补 C4 协议清单
5 可唤醒 Sleep 相对独立。watch revision 方案正确;先补 B6 的 sub-run 语义

8. 结论

设计的现状诊断准确、核心决策与既有架构不变量兼容、分期依赖方向正确,审核结论为"方向通过,需修订后实现"。A1A5 五个接缝缺口不是方向错误,而是设计与 session worker//stop/AgentLoop 取消机制的衔接定义不足;按第 6 节补齐专项定义后,可按第 7 节顺序分期实施。