乐于分享
好东西不私藏

openclaw会话树型依赖-上篇(架构与Spawn)

openclaw会话树型依赖-上篇(架构与Spawn)

一、总体架构

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)

核心特征

  1. 会话之间是树型(parent → multiple children)
  2. 每个会话内部的消息是线性的(JSONL 顺序追加)
  3. 每个子会话有且仅有一个父会话(通过 ‘spawnedBy’ 字段指向)
  4. 树的深度和宽度均可配置,有硬性上限

二、数据模型

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

父会话key(发起 spawn 的会话)

controllerSessionKey

string

控制者会话(默认等于 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 消息存储 — 单会话内线性

  1. 每个会话的transcript是JSONL格式的线性文件。
  2. 通过‘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 + 1 9. 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 + 1  3. 兜底:解析 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