GPU_GUARD_MONOREPO/docs/code-wiki/concepts/session-tree-runtime.md

155 lines
6.9 KiB
Markdown
Raw Normal View History

2026-05-20 21:39:12 +08:00
---
title: Session Tree 运行时
created: 2026-04-21
updated: 2026-04-26
type: concept
tags: [runtime, agent, backend, database]
sources: [packages/backend/src/modules/netaclaw/session-tree/provider.ts, packages/backend/src/modules/netaclaw/session-tree/types.ts, packages/backend/src/modules/netaclaw/session-tree/snapshot.ts, packages/backend/src/modules/netaclaw/session-tree/context_builder.ts, packages/backend/src/modules/netaclaw/session-tree/mysql_provider.ts, packages/backend/src/modules/netaclaw/session-tree/file_provider.ts, packages/backend/src/modules/netaclaw/gateway/session.ts, packages/frontend/src/modules/agent/store/chat.ts]
---
# Session Tree 运行时
Session Tree 是 NetaClaw 新 Agent runtime 的会话状态模型。它把对话从线性消息列表升级为“节点树 + 当前叶子 + 活动路径 + 运行时上下文”的结构,使刷新恢复、分支、压缩、子 Agent 批次、标签、模型切换都能进入同一套持久化和投影机制。
它是 [[agent-runtime]]、[[context-compaction]]、[[subagent-session]] 和 [[frontend-architecture]] 的共同底座。
## Provider 抽象
`SessionTreeProvider` 定义统一接口,当前有 file 和 mysql 两种 provider。
主要能力包括:
- `createSession`
- `getSession`
- `updateSession`
- `deleteSession`
- `listEntries`
- `appendEntry`
- `appendMessage`
- `appendThinkingLevelChange`
- `appendModelChange`
- `appendCompaction`
- `appendBranchSummary`
- `appendLabelChange`
- `appendSessionInfo`
- `updateEntry`
- `switchLeaf`
- `resetLeaf`
- `createBranchedSession`
- `getActivePath`
- `getSnapshot`
这层抽象让运行时不直接绑定 MySQL 或本地文件,也为未来多 workspace、本地优先存储、导入导出保留空间。
## Snapshot 结构
`SessionTreeSnapshot` 是前后端对齐的核心投影:
| 字段 | 含义 |
| --- | --- |
| `session` | 会话元信息,包括 provider、rootEntryId、leafEntryId、cwd、agentId 等 |
| `entries` | 当前会话所有树节点 |
| `activePath` | 从 root 到 leaf 的当前上下文路径 |
| `childrenByParentId` | 前端重建树结构所需的父子索引 |
| `labelsByEntryId` | 节点标签状态 |
| `runtimeContext` | 供模型调用使用的消息、thinking level、模型引用 |
前端刷新恢复应优先加载 snapshot而不是依赖 localStorage 保存完整聊天记录。
## Entry 类型
当前树节点类型包括:
| 类型 | 用途 |
| --- | --- |
| `message` | system/user/assistant/tool 模型消息 |
| `thinking_level_change` | 会话内 thinking level 切换 |
| `model_change` | 会话内模型切换 |
| `compaction` | 压缩摘要节点,详见 [[context-compaction]] |
| `branch_summary` | 分支摘要 |
| `custom` | 不直接展示的自定义数据 |
| `custom_message` | 可展示的自定义消息 |
| `label` | 对目标节点设置或清除标签 |
| `session_info` | 会话信息变更 |
| `subagent_batch` | 子 Agent 批次节点,详见 [[subagent-session]] |
| `subagent_result` | 子 Agent 批次结果节点 |
## Active Path 与上下文构建
模型上下文不再等于“所有历史消息”。运行时会从当前 `leafEntryId` 回溯得到 `activePath`,再由 `context_builder.ts` 转成 `runtimeContext.messages`
这带来几个结果:
- 分支切换只需要切换 leaf。
- 压缩摘要可以替代被压缩的历史段。
- 子 Agent、分支摘要、custom message 可以参与展示,但不一定进入模型上下文。
- thinking/model 变更可以沿路径生效。
## 持久化位置
Session Tree 支持两类持久化:
- MySQL provider适合生产和多端共享。
- File provider适合本地优先、单机 workspace、调试和导入导出。
浏览器 localStorage 只保存最近 session/agent 之类的轻量指针,不是对话历史主存储。
## 删除语义
Session Tree 删除必须按会话实际所属的 provider 执行:
- 前端 `chat.ts` 删除请求优先携带会话列表中该 session 的 `agentId`
- 后端 `gateway/session.ts` 在删除前先从 `netaclaw_agent_session` 反查 `sessionId` 所属 `agentId`,再解析该 Agent 的 session backend。
- MySQL provider 删除 `netaclaw_agent_session_entry``netaclaw_agent_session`file provider 删除对应 JSONL 文件。
- 随后后端再清理 legacy session/message、subagent_session 和 session-tree 兼容表记录。
这避免了 MySQL 会话在当前选中 Agent 不一致、或前端缺少 `agentId` 时走默认 file provider导致“点击删除没有效果”。
## 前端消费
`packages/frontend/src/modules/agent/store/chat.ts` 负责消费 snapshot
- `sessionMeta` 保存会话元信息。
- `entries``entryById` 保存节点。
- `childrenByParentId` 支持树结构恢复。
- `activePathIds` 控制当前可见路径。
- `visibleEntries` 将树节点投影成对话 UI。
- `subagentRuntimeByBatchId``toolRuntimeRoutesByBatchId` 记录子 Agent 工具路由展示数据。
对话页滚动、刷新恢复、历史批次展示都应该围绕这些状态实现。
## 相关页面
- [[agent-runtime]]
- [[subagent-session]]
- [[context-compaction]]
- [[frontend-architecture]]
- [[websocket-gateway]]
- [[netaclaw-module]]
- [[frontend-architecture]]
## 2026-04-22 Subagent Result Metadata
`subagent_result` carries durable metadata needed to replay subagent execution after refresh.
- `metadata.processEvents`: normalized worker event timeline for UI replay.
- `metadata.evidenceSummaries`: structured evidence projection built from subagent tool results.
- `metadata.toolRuntimeRoutes`: tool routing diagnostics for the delegated batch.
The frontend store should project these fields into dedicated maps keyed by entry id, rather than letting view components parse raw metadata directly.
## 2026-04-23 Continue-From-Entry And Projection Contract
Session Tree 现在不仅负责“存什么”,还决定“从哪里继续对话”:
- 前端 `chat.ts` 维护 `selectedEntryId``switchingLeafEntryId``pendingLeafConfirmation`,允许用户选中任意节点后继续发送,而不是只能沿当前 leaf 末尾追加。
- 如果选中的是普通 user 节点,继续发送会基于它的父节点重放该条 user message形成新的分支。
- 如果选中的是 `branch_summary``compaction` 或非当前 leaf 节点,前端会先切换 leaf再沿该路径继续。
- 这些交互都以 `session.leafEntryId``activePath` 和 snapshot 中的树结构为依据,不依赖前端自行缓存一份线性历史。
对子 Agent 相关节点,当前 snapshot 契约也更明确:
- `subagent_result.metadata.subagentProjection` 是会话树边界输出的 canonical projection。
- projection 内的 `diagnostics.selectedSource``inputSources``fallbackUsed` 用来说明当前展示究竟来自 `subagent_result`、旧 tool message还是历史 metadata fallback。
- 这让 Session Tree 不再只是原始数据容器,而是把“兼容历史记录”和“为当前 UI 供给稳定形态”一起纳入运行时模型。