Files
sanguo_moziplus_v2/docs/design/02-main-session-delegation.md
T
2026-05-29 18:33:32 +08:00

14 KiB
Raw Blame History

02-main-session-delegation.md — Main Session + Delegation 架构

日期: 2026-05-29 作者: 庞统 状态: 设计中 前置: 01-four-phase-loop.md(四相循环 E2E 验证暴露 session 爆炸问题)


一、问题:现行 spawn 方案的 session 爆炸

现状

spawner 通过 openclaw agent --session-id UUID --message "..." --json spawn Agent

spawner → 新 UUID session → Agent 执行 → 进程退出 → session 残留

问题

问题 证据 影响
Session 爆炸 今天 E2E 测试 6 个 Agent 产生 68 个 session16~484 每个),其中 56 个 gateway-fallback-* 文件系统堆积、磁盘浪费
空 payloads 绝大部分 spawn 返回 status: None, payloads: [] Daemon 以为任务完成了但实际没有产出
上下文丢失 每次 spawn 新 sessionAgent 无历史、无 workspace bootstrap Agent 不知道自己是谁、不知道之前的决策
续杯浪费 续杯时 reuse_session_id 复用 UUID,但 Gateway 可能又创建 fallback round_count 空转、Agent busy 但不产出

根因

openclaw agent --session-id UUID 的语义是"在指定 session 里执行 turn",但 Gateway 的实际行为是 find-or-create

  • session 不存在 → 创建 gateway-fallback-<UUID> session
  • 每次都是全新上下文
  • Agent 没有历史,没有 SOUL.md 等身份注入

这是结构性问题,不是 bug。 在这个方案上继续修 bug 没有意义。


二、方案:Main Session + Delegation

核心思路

现行:Daemon 硬编码步骤 → spawner 直接 spawn 新 session → Agent 被动执行

改为:Daemon 投递任务 → Agent main session 收到 → Agent 自主分析 → sessions_spawn(subagent) 执行 → sub 完成 → Agent review → 写回黑板

架构对比

┌─ 现行 ──────────────────────────────────────────┐
│                                                  │
│  Daemon/Ticker                                   │
│  ├── broadcast → spawn_full_agent(UUID session)  │
│  │   → Agent 执行固定步骤 prompt                  │
│  │   → 空 payloads / session 残留                 │
│  ├── mention  → spawn_full_agent(UUID session)   │
│  └── review   → spawn_full_agent(UUID session)   │
│                                                  │
└──────────────────────────────────────────────────┘

┌─ 改为 ───────────────────────────────────────────┐
│                                                   │
│  Daemon/Ticker                                    │
│  ├── broadcast → 投递到 Agent main session        │
│  ├── mention  → 投递到 Agent main session         │
│  └── review   → 投递到庞统 main session           │
│                                                   │
│  Agent Main Session(有完整身份+上下文)            │
│  ├── 收到任务消息                                  │
│  ├── 分析黑板(API 读任务详情/上下文/依赖)          │
│  ├── sessions_spawn(subagent, cleanup: "delete")  │
│  │   └── sub 执行具体工作 → 完成后自动清理          │
│  ├── sessions_yield → 等待 sub 完成               │
│  ├── review sub 结果                               │
│  └── 写回黑板(产出/状态/评论)                      │
│                                                   │
└───────────────────────────────────────────────────┘

三条路径的变化

路径 现行 改为
Broadcast dispatch spawn_full_agent(session_id=UUID) 新 session 投递到 main sessionAgent 自己决定执行方式
Mention spawn_full_agent(session_id=UUID) 新 session 投递到 main sessionAgent 自主处理
Review(庞统) spawn_full_agent(session_id=UUID) 新 session 投递到庞统 main session,庞统自主 review

三、关键机制

3.1 投递到 Main Session

方式openclaw agent --agent <id> --message "..."(不传 --session-id

Gateway 行为(已确认):

  • 不传 --session-id → 投递到 Agent 的 main sessionagent:<id>:main
  • main session 正忙 → 消息排队,Agent 当前 turn 完成后处理
  • Agent 有完整上下文(SOUL.md / MEMORY.md / workspace bootstrap

spawner 改动

  • 新增 deliver_to_main() 方法,用 openclaw agent --agent <id> --message "..." --json 投递
  • 不走 spawn_full_agent 的 session 管理,改用 Gateway 的原生投递机制
  • counter 仍然生效(per main session 粒度,max_per_session=1 保证不并发)

3.2 Delegationsubagent-delegation skill

Agent 收到任务后,用 sessions_spawn 创建 subagent 执行具体工作:

sessions_spawn({
  task: "任务目标\n\n上下文信息",
  cleanup: "delete",    // 完成后自动清理 session
  model: "zhipu/glm-5.1",
  label: "T1-01"
})

关键特性

  • cleanup: "delete" → sub 执行完 session 立即删除(transcript 保留可查)
  • main session 通过 sessions_yield 等待 sub 完成(非阻塞,可被新消息打断)
  • sub 的结果通过完成事件回传给 main session

3.3 续杯机制(已有,无需改动)

Agent 在 main session 中执行,如果超时/中断:

  • 续杯机制(_do_retry)已经实现了重新投递逻辑
  • counter 的 acquire/release 在 spawn_full_agent 内部管理
  • 改用 main session 后,续杯变成"重新投递到 main session",逻辑更简单

3.4 Counter 调整

改用 main session 后:

  • 所有 spawn 走同一个 session keyagent:main
  • max_per_session=1 保证同一时间只有一个 active turn
  • max_concurrent_sessions=3 限制同一 Agent 最多 3 个不同 sessionmain + 可能的 sub
  • max_global=5 限制全局并发

注意subagent 是 Agent 自己 spawn 的,不经过 Daemon counter。Daemon 只控制 main session 的投递。


四、Prompt 设计

4.1 投递消息格式

Daemon 投递到 main session 的消息需要包含足够的上下文,让 Agent 自主决策:

📋 新任务到达

**任务**: {title}
**描述**: {description}
**Project**: {project_id}
**Task ID**: {task_id}
**类型**: {task_type} | **风险**: {risk_level}
**验收标准**: {must_haves}
**依赖产出**: {depends_on_outputs_summary}

**黑板 API**: http://localhost:8083/api/projects/{project_id}
- 读任务详情: GET /api/projects/{project_id}/tasks/{task_id}?expand=all
- 写产出: POST /api/projects/{project_id}/tasks/{task_id}/outputs
- 写评论: POST /api/projects/{project_id}/tasks/{task_id}/comments
- 更新状态: POST /api/projects/{project_id}/tasks/{task_id}/status

**约束**:
- 完成后必须写产出 + 标 review
- 失败了标 failed 并写明原因
- 使用 subagent-delegation skill 进行具体执行
- 禁止使用 sessions_send 直接发消息

4.2 Agent 自主决策

Agent 收到消息后的自主行为(不是硬编码步骤):

  1. 分析上下文:读黑板 API 获取任务详情、依赖、历史讨论
  2. 决定执行策略
    • 简单任务 → 自己直接做
    • 复杂任务 → sessions_spawn 拆分 sub task
    • 需要协作 → @mention 其他 Agent
  3. 执行:通过 subagent 或直接执行
  4. Review:检查 sub 结果质量
  5. 写回:产出 + 状态 + handoff comment

4.3 庞统 Review 消息格式

🔍 一轮结束,需要你的 review

**Parent Task**: {title}ID: {task_id}
**Project**: {project_id}
**Round**: {round_num}/{max_rounds}

**本轮状态**:
- 完成: {done} | 失败: {failed} | 取消: {cancelled}
- 总计: {total}

**三问**:
1. Goal 还清晰吗?(是否有 goal drift)
2. 成果物覆盖 goal 了吗?(逐条检查验收标准)
3. 下一轮需要做什么?(创建新 sub tasks / 标记完成 / 调整方向)

**你可以**:
- 创建新 sub task: curl -X POST http://localhost:8083/api/projects/{project_id}/tasks ...
- 调整 goal
- 标记 GOAL_ACHIEVED

回复 GOAL_ACHIEVED 表示完成,否则系统会等待你创建新 sub tasks。

五、Daemon 侧改动

5.1 Spawner 新增方法

async def deliver_to_main(self, agent_id: str, message: str,
                           task_id: str = None,
                           on_complete: Callable = None) -> str:
    """投递到 Agent main session(不创建新 session

    用 openclaw agent --agent <id> --message "..." 投递,
    Gateway 自动路由到 main session。
    """
    cmd = [
        "openclaw", "agent",
        "--agent", agent_id,
        "--message", message,
        "--json",
        "--timeout", str(int(self.gateway_timeout)),
    ]
    # ... 执行 + 监控,和 spawn_full_agent 类似但不传 --session-id

5.2 Ticker 改动

方法 改动
_broadcast_and_dispatch 调用 deliver_to_main 替代 spawn_full_agent
_process_mentions 调用 deliver_to_main 替代 spawn_full_agent
_spawn_pangtong_review 调用 deliver_to_main 替代 spawn_full_agent
_check_round_complete 保留 reviewing 中间态逻辑
_handle_review_conclusion 保留,但改为从 main session 的回复中解析

5.3 Dispatcher 改动

确定性路由也改用 deliver_to_main

  • 移除 use_main_session 参数(全部走 main session
  • 移除 new_session 参数
  • counter 逻辑简化(per main session 粒度)

5.4 废弃代码

  • spawn_full_agentnew_session / use_main_session 参数标注废弃
  • gateway-fallback-* session 的产生路径消失
  • 续杯逻辑简化(reuse_session_id 不再需要)

六、Session 生命周期对比

现行

每次 spawn → 创建新 session → 执行 → 残留 → 手动清理
                                          ↓
                              gateway-fallback-* 堆积

改为

main session(常驻,有完整上下文)
  ├── 收到任务 A → spawn sub(A1, cleanup: delete) → yield → review → 写回
  ├── 收到任务 B → spawn sub(B1, cleanup: delete) → yield → review → 写回
  └── 收到 mention → 自主处理 → 写回

sub session(临时,完成即删)
  ├── sub(A1) → 执行 → 完成 → session 删除(transcript 保留)
  └── sub(B1) → 执行 → 完成 → session 删除(transcript 保留)

Session 数量6 个 main session(常驻)+ 临时 sub session(用完即删) 对比现行6 个 main + N 个 gateway-fallback-*(无限增长)


七、收益与风险

收益

维度 改善
Session 数量 从无限增长 → 6 个 main + 临时 sub
Agent 上下文 从无历史 → 完整身份 + 记忆 + workspace
代码简化 移除 new_session / use_main_session / reuse_session_id 逻辑
Prompt 灵活度 从硬编码步骤 → Agent 自主决策
空 payloads 消除(main session 有完整生命周期管理)

风险

风险 缓解
main session 上下文膨胀 OpenClaw 自带的 compact 机制处理;sub session 不膨胀(用完删)
main session 被阻塞 sessions_yield 非阻塞,可被新消息打断;Gateway 排队机制保证不丢消息
sub 质量不可控 main session review sub 结果;reviewing 中间态保证 round 不重复触发
Prompt 依赖 Agent 需要正确的 prompt 来知道怎么 delegationsubagent-delegation skill 已调优
Gateway 投递延迟 main session 排队是异步的,Daemon 不同步等结果;改为"投递后等黑板状态变化"

八、待确认

# 问题 说明
1 Daemon 如何知道任务完成了? 现行用 --json 同步等结果。改用 main session 后,需要改为"投递后 ticker 轮询黑板状态变化"
2 投递消息的 prompt 模板 需要和 v2.8 方向的 Prompt 进化对齐(身份+能力+约束 vs 固定步骤)
3 on_complete 回调 改为基于黑板状态变化(status/review/done)触发,不再依赖 spawn 返回值
4 Mail 投递路径 Mail 已经走 main sessionuse_main_session=is_mail),无需改动
5 E2E 测试适配 现有 E2E 测试依赖 spawner 的 --session-id UUID 机制,需要适配

九、实施计划

Phase 1spawner 新增 deliver_to_main

  • 新增方法,不改动现有 spawn_full_agent
  • 单元测试覆盖

Phase 2Ticker 三条路径切换

  • _broadcast_and_dispatchdeliver_to_main
  • _process_mentionsdeliver_to_main
  • _spawn_pangtong_reviewdeliver_to_main

Phase 3Daemon 结果收集改造

  • 移除 --json 同步等结果
  • 改为 ticker 轮询黑板状态变化
  • on_complete 改为基于黑板事件触发

Phase 4E2E 测试适配 + 验证

  • 适配新的投递机制
  • 验证 session 不爆炸
  • 验证 Agent 自主决策行为

十、参考文件

文件 说明
01-four-phase-loop.md 四相循环设计(现行方案)
v2.8-direction-notes.md v2.8/v2.9 方向(Prompt 进化 + Daemon 退化)
subagent-delegation/SKILL.md Delegation skillAgent 侧执行指南)
counter.py 并发控制(per session 粒度)
spawner.py _do_retry 续杯机制