# Lifecycle(生命周期管理) **模块:** `gateway/core/lifecycle/` ## 文档维护规范 0. **先改文档,再动代码** - 新功能或重大修改需先完成文档更新、并完成审阅后,再进行代码实现 1. **文档分层,链接代码** - 关键实现需标注代码文件路径;格式:`module/file.py:function_name` 2. **简洁快照,日志分离** - 只记录已确认的设计 --- ## 模块职责 Agent 生命周期管理,包括: - **Trace 注册**:调用 Agent 框架创建 Trace - **Trace 查询**:调用 Agent 框架查询 Trace 信息 - **Workspace 管理**:确保 Workspace 目录存在和清理 - **配置热重载**:监听 Agent 技能配置变化并热重载 **说明:** Trace 元数据由 Agent 框架管理,Gateway 不维护副本 --- ## 核心概念 ### Trace 基于 Agent Core 架构(详见 `../../../agent/docs/architecture.md`): - **Trace = Agent 的执行记录** - 每个 Trace 有独立的 Workspace - Trace 数据结构由 Agent Core 定义 ### Workspace - 多个 Trace 可以共享一个 Workspace(主 Agent 和子 Agent) - 包含 Agent 的配置、记忆、技能等 - Gateway 进程内目录:``{GATEWAY_WORKSPACES_ROOT}/{sha256(workspace_id)}/``(默认 ``/root/.gateway/workspaces/<64位hex>/``),**不是**用明文 ``workspace_id`` 作目录名;详见下文「目录与 meta」。 - 需要引用计数机制,确保清理时无活跃 Trace --- ## 模块结构 ``` gateway/core/lifecycle/ ├── __init__.py # 聚合导出(TraceManager、WorkspaceManager 等) ├── errors.py # LifecycleError、WorkspaceDockerError ├── config_watcher.py # 配置热重载 ├── workspace/ │ ├── manager.py # WorkspaceManager(目录、引用计数) │ └── docker_runner.py # WorkspaceDockerRunner(沙箱容器) └── trace/ ├── manager.py # TraceManager(Agent API 代理与本地登记) └── backend.py # LifecycleTraceBackend(channels.TraceBackend) ``` --- ## 关键功能 ### TraceManager **实现位置:** `gateway/core/lifecycle/trace/manager.py` **职责:** - 调用 Agent 框架创建 Trace - 查询 Trace 信息 - 管理 Trace 和 Workspace 的关联关系 **核心接口:** **说明:** Trace 的创建由 **Agent API**(HTTP)完成;Gateway 在拿到 `trace_id` 后通过 `bind_agent_trace` 与 `WorkspaceManager` 建立关联。详见 `gateway/core/lifecycle/trace/manager.py`。 ```python class TraceManager: async def prepare_workspace_session(self, workspace_id: str) -> None: """调用 WorkspaceManager.ensure_session(目录 + 沙箱)""" pass async def bind_agent_trace( self, workspace_id: str, agent_trace_id: str, agent_type: str, metadata: dict | None = None, ) -> None: """Agent 返回 trace_id 后登记本地 meta 与 workspace 引用""" pass async def release_agent_trace(self, workspace_id: str, agent_trace_id: str) -> None: """解除绑定""" pass async def get_trace(self, trace_id: str) -> dict: """优先请求 Agent API,失败时返回 Gateway 本地登记""" pass async def list_traces( self, workspace_id: str | None = None, agent_type: str | None = None, *, limit: int = 50, ) -> list[dict]: """查询 Trace 列表(带 Agent API 查询参数约定)""" pass def get_workspace_id(self, trace_id: str) -> str: """同步:解析 workspace_id(Executor、gateway_exec 使用)""" pass ``` ### WorkspaceManager **实现位置:** `gateway/core/lifecycle/workspace/manager.py`(Docker 编排见同目录 `docker_runner.py`) #### 目录与 meta 磁盘布局与 `manager.py` 文档字符串一致: | 根路径(环境变量) | 默认(容器内) | 用途 | |-------------------|----------------|------| | `GATEWAY_WORKSPACES_ROOT` | `/root/.gateway/workspaces` | 每个 workspace 一个子目录(名为 `sha256(workspace_id)`),其下含 `.gateway/meta.json` | | `GATEWAY_SHARED_ROOT` | `/root/.gateway/shared` | 多会话/多 workspace 共享文件;沙箱内挂载为 `/home/agent/shared` | **meta.json** 路径:`{GATEWAY_WORKSPACES_ROOT}//.gateway/meta.json`。由 `WorkspaceManager._save_meta` 写入,字段包含 `workspace_id`、`trace_refs`、`workspace_container_id`(沙箱容器 ID)等。在 **`volume_subpath` 模式**下,该目录与沙箱内 `/home/agent/workspace` 为同一块存储(命名卷 + Subpath),故 Agent 在沙箱里也能看到 `workspace/.gateway/meta.json`。 #### Docker 沙箱(WorkspaceDockerRunner) **实现位置:** `gateway/core/lifecycle/workspace/docker_runner.py` - 为每个 workspace 保证一个运行中的沙箱容器(镜像默认 `agent/workspace:latest`,容器名前缀 `gws-`)。 - **挂载模式** `GATEWAY_WORKSPACE_MOUNT_MODE`: - **`bind`(默认)**:`workspace_host_path`、`shared_host_path` 解析后的路径直接 bind 到沙箱(适合 Gateway 与本机 Docker 守护进程同环境、路径对 Docker 可见)。 - **`volume_subpath`**:使用命名卷 + `VolumeOptions.Subpath`,将 **workspace 卷** 的子目录 `` 挂到 `/home/agent/workspace`,**shared 卷** 整卷挂到 `/home/agent/shared`。要求同时设置 `GATEWAY_WORKSPACE_DOCKER_VOLUME` 与 `GATEWAY_SHARED_DOCKER_VOLUME`,且 Gateway 通过 **宿主机 `docker.sock`** 创建的容器与 Compose 使用同一 Docker(卷名一致)。 - 镜像与网络:`GATEWAY_WORKSPACE_IMAGE`(默认 `agent/workspace:latest`);`GATEWAY_WORKSPACE_DOCKER_NETWORK` 指定沙箱加入的网络名(如 `agent`)。 - 关闭沙箱编排:`GATEWAY_WORKSPACE_DOCKER_ENABLED=false`;若失败需中断会话则设 `GATEWAY_WORKSPACE_DOCKER_REQUIRED=true`。 #### Gateway 环境变量(Workspace / Docker) | 变量 | 说明 | |------|------| | `GATEWAY_WORKSPACES_ROOT` | Workspace 根目录(容器内常为 `/root/.gateway/workspaces`) | | `GATEWAY_SHARED_ROOT` | 共享目录(容器内常为 `/root/.gateway/shared`) | | `GATEWAY_WORKSPACE_MOUNT_MODE` | `bind` 或 `volume_subpath` | | `GATEWAY_WORKSPACE_DOCKER_VOLUME` | `volume_subpath` 时:workspace 数据 **Docker 卷名**(与 Compose 中 `name:` 一致,如 `agent_workspace_root`) | | `GATEWAY_SHARED_DOCKER_VOLUME` | `volume_subpath` 时:shared 数据 **Docker 卷名**(如 `agent_workspace_shared`) | | `GATEWAY_WORKSPACE_DOCKER_NETWORK` | 沙箱容器加入的网络名 | | `GATEWAY_WORKSPACE_IMAGE` | 沙箱镜像,默认 `agent/workspace:latest` | | `GATEWAY_WORKSPACE_DOCKER_ENABLED` | 是否启用沙箱容器 | | `GATEWAY_WORKSPACE_DOCKER_REQUIRED` | Docker 失败时是否视为致命错误 | **TraceManager(HTTP)**:`GATEWAY_AGENT_API_BASE_URL`、`GATEWAY_AGENT_API_TIMEOUT`(与 Executor 共用 Agent 基址)。 #### 仓库 `docker-compose.yml`(Gateway 服务) - 命名卷 **`workspace_root`**(固定名 **`agent_workspace_root`**)→ `/root/.gateway/workspaces`。 - 命名卷 **`workspace_shared`**(固定名 **`agent_workspace_shared`**)→ `/root/.gateway/shared`;使用 **`driver: local` + bind**,将卷绑定到宿主机目录,便于直接查看文件。 - **`GATEWAY_SHARED_HOST_BIND`**:**仅用于 Compose 解析**,写在项目根 `.env` 或 shell 环境中;作为 `driver_opts.device` 的宿主机路径。未设置时默认 `${PWD}/.gateway/shared`(请在项目根执行 `docker compose`,并保证目录存在)。**Gateway 进程不读取该变量。** - 与上述卷名对应的环境变量:`GATEWAY_WORKSPACE_DOCKER_VOLUME=agent_workspace_root`、`GATEWAY_SHARED_DOCKER_VOLUME=agent_workspace_shared`,并通常配合 `GATEWAY_WORKSPACE_MOUNT_MODE=volume_subpath`。 **职责:** - 创建和初始化 Workspace 目录 - 管理 Workspace 引用计数 - 清理无活跃 Trace 的 Workspace **核心接口:** ```python class WorkspaceManager: async def create_workspace(self, workspace_id: str) -> str: """创建 Workspace 目录(含 meta),返回绝对路径""" pass async def ensure_session(self, workspace_id: str) -> str: """会话启动:目录 + 共享目录 + 按需启动沙箱;返回 Workspace 绝对路径""" pass async def get_workspace_path(self, workspace_id: str) -> str: """获取 Workspace 路径(不存在则抛错)""" pass def get_workspace_container_id(self, workspace_id: str) -> str | None: """读 meta 中沙箱容器 ID(供 Executor 注入 gateway_exec)""" pass async def add_trace_ref(self, workspace_id: str, trace_id: str) -> None: """增加 Trace 引用""" pass async def remove_trace_ref(self, workspace_id: str, trace_id: str) -> None: """移除 Trace 引用""" pass async def cleanup_workspace(self, workspace_id: str, force: bool = False) -> None: """清理 Workspace(检查引用计数,force=True 强制清理)""" pass async def list_workspaces(self) -> list[dict]: """列出所有 Workspace 及其引用计数、容器 ID 等""" pass ``` ### ConfigWatcher **实现位置:** `gateway/core/lifecycle/config_watcher.py` **职责:** - 监听 Agent 技能配置文件变化 - 触发热重载回调 - 不影响正在运行的 Trace **核心接口:** ```python class ConfigWatcher: def watch(self, workspace_id: str, callback: callable): """监听指定 Workspace 的配置变化""" pass def stop_watch(self, workspace_id: str): """停止监听""" pass ``` --- ## 典型流程 ### 个人助理型 Agent 首次对话(与当前实现一致) 1. 用户通过飞书发送消息;渠道层解析 `workspace_id`(如 `feishu:`)。 2. `TraceManager.prepare_workspace_session(workspace_id)` → 内部 `WorkspaceManager.ensure_session`(目录、shared、沙箱容器)。 3. 调用 **Agent HTTP API** 创建/续跑 Trace,得到 `trace_id`。 4. `TraceManager.bind_agent_trace(workspace_id, trace_id, agent_type, ...)` 登记引用与本地 meta。 5. **Executor** 侧通过 `TaskManager.submit_task(trace_id, ...)` 驱动 `POST /api/traces/{id}/run`(可带 `gateway_exec` 指向沙箱)。 ### 数字员工型 Agent 串行处理 1. 用户 A 发送消息 2. Routing 模块查询数字员工的 Trace(共享 workspace_id) 3. 如果无 Trace → 仍由 Agent API 创建 trace,再 `bind_agent_trace`(同上) 4. 如果有 Trace 且正在处理 → 消息进入队列 5. 当前对话结束 → 从队列取下一条消息 ### 主 Agent 调用子 Agent 1. 主 Agent 执行过程中需要子 Trace 时,由 Agent 侧创建子 trace(HTTP) 2. Gateway 对子 trace 调用 `bind_agent_trace(同一 workspace_id, 子 trace_id, ...)` 3. `WorkspaceManager.add_trace_ref` 维护引用;子 Agent 结束后 `remove_trace_ref`(或由渠道策略 `release_agent_trace`) ### Workspace 清理 1. Trace 结束时调用 `WorkspaceManager.remove_trace_ref(...)` 2. WorkspaceManager 检查引用计数 3. 引用计数为 0 → 可以清理(可选延迟清理策略) 4. 删除 Workspace 目录 --- ## 错误处理 ### Trace 创建 / 绑定失败 - Agent HTTP 创建或续跑失败 → 渠道/Executor 向用户返回友好错误;**勿**在未拿到 `trace_id` 时调用 `bind_agent_trace` - `ensure_session`(Docker 卷、沙箱)失败 → 若 `GATEWAY_WORKSPACE_DOCKER_REQUIRED=true` 则中断;否则可降级为无沙箱(见日志) ### Workspace 引用计数不一致 - 定期检查 Workspace 引用计数和实际 Trace 状态 - 发现不一致 → 记录日志,修正引用计数 ### 配置热重载失败 - 配置文件格式错误 → 记录日志,保持旧配置 - 不影响正在运行的 Trace ### 并发安全 - TraceManager 和 WorkspaceManager 的关键操作需要加锁 - 避免多个请求同时创建相同 workspace_id 的 Workspace --- ## 相关文档 - [需求规划](../requirements.md):生命周期管理需求 - [架构设计](../architecture.md):模块在整体架构中的位置 - [Agent Core 架构](../../../agent/docs/architecture.md):Trace 数据结构定义 - 仓库根目录 `docker-compose.yml`:`gateway` 服务的卷与环境变量与上文「Compose」小节一致,部署时以此为准。