auto-sync: 2026-05-29 18:33:32
This commit is contained in:
@@ -0,0 +1,364 @@
|
||||
# 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 个 session(16~484 每个),其中 56 个 `gateway-fallback-*` | 文件系统堆积、磁盘浪费 |
|
||||
| **空 payloads** | 绝大部分 spawn 返回 `status: None, payloads: []` | Daemon 以为任务完成了但实际没有产出 |
|
||||
| **上下文丢失** | 每次 spawn 新 session,Agent 无历史、无 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 session,Agent 自己决定执行方式 |
|
||||
| **Mention** | `spawn_full_agent(session_id=UUID)` 新 session | 投递到 main session,Agent 自主处理 |
|
||||
| **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 session(`agent:<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 Delegation(subagent-delegation skill)
|
||||
|
||||
Agent 收到任务后,用 `sessions_spawn` 创建 subagent 执行具体工作:
|
||||
|
||||
```javascript
|
||||
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 key(`agent:main`)
|
||||
- `max_per_session=1` 保证同一时间只有一个 active turn
|
||||
- `max_concurrent_sessions=3` 限制同一 Agent 最多 3 个不同 session(main + 可能的 sub)
|
||||
- `max_global=5` 限制全局并发
|
||||
|
||||
**注意**:subagent 是 Agent 自己 spawn 的,不经过 Daemon counter。Daemon 只控制 main session 的投递。
|
||||
|
||||
---
|
||||
|
||||
## 四、Prompt 设计
|
||||
|
||||
### 4.1 投递消息格式
|
||||
|
||||
Daemon 投递到 main session 的消息需要包含足够的上下文,让 Agent 自主决策:
|
||||
|
||||
```markdown
|
||||
📋 新任务到达
|
||||
|
||||
**任务**: {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 消息格式
|
||||
|
||||
```markdown
|
||||
🔍 一轮结束,需要你的 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 新增方法
|
||||
|
||||
```python
|
||||
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_agent` 的 `new_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 来知道怎么 delegation;subagent-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 session(`use_main_session=is_mail`),无需改动 |
|
||||
| 5 | **E2E 测试适配** | 现有 E2E 测试依赖 spawner 的 `--session-id UUID` 机制,需要适配 |
|
||||
|
||||
---
|
||||
|
||||
## 九、实施计划
|
||||
|
||||
### Phase 1:spawner 新增 `deliver_to_main`
|
||||
|
||||
- 新增方法,不改动现有 `spawn_full_agent`
|
||||
- 单元测试覆盖
|
||||
|
||||
### Phase 2:Ticker 三条路径切换
|
||||
|
||||
- `_broadcast_and_dispatch` → `deliver_to_main`
|
||||
- `_process_mentions` → `deliver_to_main`
|
||||
- `_spawn_pangtong_review` → `deliver_to_main`
|
||||
|
||||
### Phase 3:Daemon 结果收集改造
|
||||
|
||||
- 移除 `--json` 同步等结果
|
||||
- 改为 ticker 轮询黑板状态变化
|
||||
- on_complete 改为基于黑板事件触发
|
||||
|
||||
### Phase 4:E2E 测试适配 + 验证
|
||||
|
||||
- 适配新的投递机制
|
||||
- 验证 session 不爆炸
|
||||
- 验证 Agent 自主决策行为
|
||||
|
||||
---
|
||||
|
||||
## 十、参考文件
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `01-four-phase-loop.md` | 四相循环设计(现行方案) |
|
||||
| `v2.8-direction-notes.md` | v2.8/v2.9 方向(Prompt 进化 + Daemon 退化) |
|
||||
| `subagent-delegation/SKILL.md` | Delegation skill(Agent 侧执行指南) |
|
||||
| `counter.py` | 并发控制(per session 粒度) |
|
||||
| `spawner.py` `_do_retry` | 续杯机制 |
|
||||
Reference in New Issue
Block a user