2026-05-20 21:39:12 +08:00

132 lines
7.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: Agent 运行时
created: 2026-04-13
updated: 2026-04-23
type: entity
tags: [runtime, agent, llm, backend]
sources: [packages/backend/src/modules/netaclaw/service/chat_orchestrator.ts, packages/backend/src/modules/netaclaw/session-tree/, packages/backend/src/modules/netaclaw/runtime/, packages/backend/src/modules/netaclaw/service/tool_resolver.ts, packages/backend/src/modules/netaclaw/subagent/]
---
# Agent 运行时
Agent 运行时是 NetaClaw 对话执行链路的核心。当前实现已经从“线性消息 + 单次 runAgent 调用”升级为“Session Tree 快照 + ChatOrchestrator 编排 + ToolResolver/Manifest 治理 + Subagent Worker”的组合架构。
它同时负责:
- 维护会话树、活动路径和运行时上下文,详见 [[session-tree-runtime]]。
- 按 Agent 配置、模型能力、治理策略解析最终可用工具,详见 [[tool-governance]] 和 [[tool-runtime-policy]]。
- 构建 Prompt、执行 ReAct 循环、流式返回 token/thinking/tool 事件。
- 在主 Agent 与子 Agent 之间聚合委派结果,详见 [[subagent-session]]。
- 将快照和增量事件推送给前端,详见 [[websocket-gateway]] 和 [[frontend-architecture]]。
## 关键文件
| 文件 | 职责 |
| --- | --- |
| `service/chat_orchestrator.ts` | 对话主编排:创建会话、追加用户/助手节点、驱动运行、写回结果 |
| `session-tree/provider.ts` | Session Tree provider 抽象 |
| `session-tree/mysql_provider.ts` | MySQL 持久化 provider |
| `session-tree/file_provider.ts` | 文件持久化 provider |
| `session-tree/context_builder.ts` | 从 active path 构造模型上下文 |
| `runtime/agent.ts` | ReAct 循环执行入口 |
| `runtime/prompt_builder.ts` | Prompt Builder 分层注入 |
| `service/tool_resolver.ts` | 工具治理、Agent 覆盖、运行时诊断投影 |
| `tools/manifest.ts` | 工具 manifest 和 worker routing 基础画像 |
| `tools/runtime_policy.ts` | Subprocess worker policy 与工具路由状态推导 |
| `subagent/process_runner.ts` | 子 Agent 独立进程 runner |
| `subagent/process_protocol.ts` | 子进程 JSONL 事件协议 |
## 当前运行链路
```text
用户输入
-> ChatOrchestrator 接收
-> SessionTreeProvider 创建或恢复 session
-> appendMessage(user) 写入会话树
-> SessionTreeContextBuilder 读取 active path
-> ToolResolver 生成 toolNames / disabledReasons / runtimeDiagnostic
-> Prompt Builder 注入工具、记忆、技能、压缩摘要
-> runAgent 执行 ReAct 循环
-> 工具调用经 ToolResolver 提供的工具实例执行
-> 如触发 delegate_task/delegate_parallel进入 SubagentService
-> appendEntry 写入 message、compaction、subagent_batch、subagent_result 等节点
-> WebSocket 推送 snapshot/patch/event
-> 前端 chat store 按 active path 渲染
```
## 与旧实现的关键区别
旧实现偏向按 `netaclaw_message` 的线性历史恢复对话。现在的运行时以会话树为主轴:
- `activePath` 决定当前模型上下文,而不是简单取全量消息列表。
- `leafEntryId` 表示当前分支叶子,支持分支、重置 leaf、派生 session。
- `compaction``branch_summary``label``session_info``subagent_batch``subagent_result` 都是树节点,而不是散落在消息 metadata 里的附属状态。
- 前端刷新后通过 session tree snapshot 恢复历史,不依赖浏览器 localStorage 存完整消息。
## 工具运行时
Agent 运行时不直接相信静态工具列表。最终工具集由 [[tool-governance]] 统一裁决:
- 全局工具状态来自 `netaclaw_tool` 和 catalog 同步。
- Agent 编辑页可以保存 `tools.enabled``tools.disabled``toolOverrides``subagentConfig.allowedToolNames` 等局部配置。
- 后端输出 `runtimeDiagnostic`前端工具管理页、Agent 编辑页、对话页应使用同一份投影。
- 子 Agent 的工具还会经过 [[tool-runtime-policy]] 推导,区分 `worker-local``main-process-proxy``disabled`
## 子 Agent 运行时
主 Agent 通过 `delegate_task``delegate_parallel` 发起委派后,`SubagentService` 会根据配置构造受限 Agent并优先走独立 worker 进程执行。子进程通过 JSONL 协议向父进程上报:
- `run_start`
- `token`
- `thinking`
- `tool_call`
- `tool_result`
- `proxy_tool_call`
- `run_end`
- `run_error`
需要主进程能力的工具会通过 `proxy_tool_call` 回到父进程执行,避免子进程直接持有所有写入、记忆、技能管理权限。详见 [[subagent-session]] 和 [[tool-runtime-policy]]。
## 前端消费方式
前端 `agent/store/chat.ts` 现在围绕 session tree snapshot 工作:
- 保存最近 session/agent 的轻量指针到 localStorage。
- 通过 snapshot 恢复 `sessionMeta``entries``childrenByParentId``activePathIds`
- `visibleEntries` 渲染 message、branch summary、compaction、custom message、subagent batch/result。
- 工具渲染统一通过 `projectToolRender` 和工具 renderer registry。
这意味着“刷新后历史看不到”一类问题应优先检查后端 session tree provider、session id 恢复、snapshot 加载,而不是把完整对话塞进浏览器 localStorage。
## 相关页面
- [[session-tree-runtime]]
- [[tool-runtime-policy]]
- [[tool-governance]]
- [[tool-system]]
- [[subagent-session]]
- [[context-compaction]]
- [[prompt-builder]]
- [[frontend-architecture]]
- [[websocket-gateway]]
- [[netaclaw-module]]
## 2026-04-22 Subagent Replay
The Agent runtime persists subagent evidence and process replay through session tree entries instead of transient chat state.
- `SubagentService` collects worker process events and writes projected events into `resultPayload.processEvents`.
- `ChatOrchestrator` aggregates completed task data into `subagent_result.metadata.processEvents` and `subagent_result.metadata.evidenceSummaries`.
- `SessionTreeProvider.getSnapshot()` is the refresh boundary: after page reload, the frontend should recover subagent evidence cards and replay timeline from `subagent_result` entries in the snapshot.
- These metadata fields are intentionally excluded from `runtimeContext.messages` so refresh/replay UI data does not contaminate the next LLM prompt.
## 2026-04-23 Canonical Projection Boundary
这一轮更新后Agent runtime 对子 Agent 回放数据的口径进一步收敛:
- `gateway/session.ts` 在返回 `getSnapshot()``switchLeaf()``appendSubagentResultEntry()` 等结果前,会统一调用 `projectSubagentSnapshot()` / `projectSubagentEntry()`
- `session-tree/subagent_projection.ts` 负责把 `subagent_result`、历史 tool message 或旧 metadata 中的委派结果整理为 `metadata.subagentProjection`,这才是前端首选的 canonical UI projection。
- projection 会生成 `taskPanels``toolExecutions``evidenceSummaries``processEvents``diagnostics`,并做数量裁剪,避免把整份原始 payload 直接暴露给 UI。
- 前端保留对旧 payload / metadata 的解析能力只是为了兼容历史会话;当前代码路径应优先依赖后端生成的 `subagentProjection`,而不是在页面里重复拼装结果。
- 这意味着“子 Agent 结果怎么展示”已经不再是纯前端约定,而是 Agent runtime 在 session 边界稳定输出的一部分契约。