一、总体架构
OpenClaw 的会话(Session)采用有向无环树(DAG-Tree)结构组织:
Root Session (depth=0, role=main)├── Subagent A (depth=1, role=orchestrator)│ ├── Sub-subagent A1 (depth=2, role=leaf)│ └── Sub-subagent A2 (depth=2, role=leaf)├── Subagent B (depth=1, role=leaf)└── Subagent C (depth=1, role=orchestrator)└── Sub-subagent C1 (depth=2, role=leaf)
其核心特征:
会话之间是树型(parent → multiple children) 每个会话内部的消息是线性的(JSONL 顺序追加) 每个子会话有且仅有一个父会话(通过 ‘spawnedBy’ 字段指向) 树的深度和宽度均可配置,有硬性上限
二、数据模型
2.1 SessionEntry — 会话节点
‘src/config/sessions/types.ts’;
与树结构相关的核心字段:
sessionId | string | 会话唯一标识 |
spawnedBy | string | 父会话 key,形成树的边 |
spawnDepth | number | 树的深度:0=主会话1,=子 agent,2=孙 agent |
spawnedWorkspaceDir | string | 从父会话继承的工作目录 |
forkedFromParent | boolean | 是否从父会话 transcript 分叉而来 |
subagentRole | orchestrator | 节点角色:orchestrator 可继续 spawn,leaf 不可 |
subagentControlScope | children | 控制权范围:可控制子会话 or 无控制权 |
子会话 key 的格式为:
agent:{targetAgentId}:subagent:{uuid}2.2 SubagentRunRecord — 运行记录(树的边)
‘src/agents/subagent-registry.types.ts’;
记录每次 spawn 形成的父子连接:
runId | string | 运行唯一标识 |
childSessionKey | string | 子会话key |
requesterSessionKey | string | |
controllerSessionKey | 控制者会话(默认等于 requester) | |
createdAt / startedAt / endedAt | number | 生命周期时间戳 |
outcome | {status: ok\error\timeout\unknown} | 运行结果 |
endedReason | string | 结束原因:complete / error / killed / session-reset / session-delete |
cleanup | delete\keep | 运行结束后是否保留子会话 |
spawnMode | run\session | run是一次性执行后清理,session是持久线程绑定 |
wakeOnDescendantSettle | boolean | 是否等待所有后代完成后再唤醒 |
frozenResultText | string | 冻结的完成输出,用于向父会话 announce |
2.3 消息存储 — 单会话内线性
每个会话的transcript是JSONL格式的线性文件。 通过‘SessionManager.appendMessage()’顺序追加。无会话内分支。
三、树的创建 — Spawn 机制
3.1 完整 Spawn 流程
‘src/agents/subagent-spawn.ts’->‘spawnSubagentDirect()’;
共 19 个阶段:
1. 参数校验(agentId 格式)2. 解析请求上下文(channel, accountId 等)3. 深度检查:callerDepth >= maxSpawnDepth → 拒绝4. 子会话数检查:activeChildren >= maxChildrenPerAgent → 拒绝5. Agent 允许列表检查(跨 agent spawn)6. 生成子会话 key:agent:{id}:subagent:{uuid}7. 沙箱一致性校验8. 计算子深度:childDepth = callerDepth + 19. Patch 子会话:spawnDepth + role + controlScope10. Patch 子会话:model override(可选)11. Patch 子会话:thinking level(可选)12. Thread 绑定(可选,mode="session" 时必须)13. Attachment 物化(base64 解码、文件存储)14. 构建子 agent 系统提示词15. Patch 血缘关系:spawnedBy + spawnedWorkspaceDir16. 调用 Gateway agent 方法启动子会话17. 注册 SubagentRunRecord18. 触发 subagent_spawned 生命周期钩子19. 返回结果 {status, childSessionKey, runId}
3.2 深度计算算法
‘src/agents/subagent-depth.ts’;
采用递归向上遍历父链的方式:
getSubagentDepthFromSessionStore(sessionKey):1. 查 session store 中的 spawnDepth → 有则直接返回2. 查 spawnedBy 字段 → 递归求父深度 → parentDepth + 13. 兜底:解析 key 中 `:subagent:` 出现次数4. 使用 visited Set 防止循环引用
3.3 角色分配
‘src/agents/subagent-capabilities.ts’;
根据深度自动决定:
条件 | role | controlScope |
depth < maxSpawnDepth | orchestrator | children |
depth >= maxSpawnDepth | leaf | none |
orchestrator可以继续 spawn 子会话;leaf不能。
3.4 工作目录继承
同 agent spawn:子会话继承父会话的 workspace
跨 agent spawn:子会话从目标 agent 配置解析自己的 workspace
显式覆盖:spawn 参数可传入‘workspaceDir’
3.5 Spawn 校验清单
校验项 | 条件 | 结果 |
agentId 格式 | 不匹配 `[a-z0-9][a-z0-9_-]{0,63}` | error |
最大深度 | callerDepth>=maxSpawnDepth | forbidden |
最大子会话数 | activeChildren>=maxChildrenPerAgent | forbidden |
Agent 允许列表 | 跨agent spawn且不在allowlist 中 | forbidden |
沙箱一致性 | 沙箱会话 spawn 非沙箱子会话 | forbidden |
Mode-Thread 绑定 | mode="session" 但 `thread=false | error |
Thinking 级别 | 非法 thinking 值 | error |
夜雨聆风