> ## Documentation Index
> Fetch the complete documentation index at: https://docs.profy.cn/llms.txt
> Use this file to discover all available pages before exploring further.

# Agent 平台：Runtime 与声明式沙箱

> Profy 如何在 Agent Runtime 与声明式沙箱（E2B/Docker）中运行 AI 专家，包含 SSE 流式输出、项目协作机制与按积分计费的能力说明。

# Agent 平台（Expert + Agent Runtime）

**Expert（AI 专家产品）** 的对话与工具执行由 **Agent Runtime**（`services/agent-runtime`）承载，运行在 **声明式 Sandbox（E2B 或 Docker）** 中。每个 Expert 对应隔离的文件系统与运行环境，通过 SSE 协议实现实时流式对话。

## 概述

```mermaid theme={null}
graph TB
    User[用户] --> Web["Web 前端"]
    Web -->|SSE| Proxy["Next.js SSE 代理"]
    Proxy -->|runtimeUrl / 编排结果| Sandbox1["Sandbox: expert-001"]
    Proxy --> Sandbox2["Sandbox: expert-002"]
    Proxy --> Sandbox3["Sandbox: expert-003"]

    subgraph Sandboxes["声明式 Sandbox（E2B / Docker）"]
        Sandbox1
        Sandbox2
        Sandbox3
    end

    Sandbox1 --> Engine["Agent Runtime"]
    Engine --> Tools["工具调用"]
    Engine --> Files["文件操作"]
    Engine --> Preview["Web 预览"]

    style Sandboxes fill:#f0f4ff,stroke:#4a6cf7
    style Engine fill:#e8faf0,stroke:#10b981
```

## 运行时架构

| 层级                | 说明                                                   |
| ----------------- | ---------------------------------------------------- |
| **Core API**      | Expert / Sandbox 生命周期编排、鉴权、计费、`prepare-invoke` 等业务真源 |
| **Agent Runtime** | `services/agent-runtime`，在 Sandbox 内执行模型对话、工具与文件访问   |

Web 经 SSE 代理将对话请求转发到已由 Core 准备好的 Sandbox 端点；编排细节见 [系统架构 — Expert 沙箱与 agent-runtime](/zh/documentation/architecture/overview)。

## Expert / Sandbox 生命周期

```mermaid theme={null}
stateDiagram-v2
    [*] --> Provisioning: 创建 Expert / Sandbox
    Provisioning --> Running: Sandbox 就绪 + 技能加载 + 文件预取
    Provisioning --> Error: Sandbox 创建失败
    Running --> Stopped: 用户停止 / 空闲超时
    Running --> Error: Sandbox 或 Runtime 崩溃
    Stopped --> Provisioning: 用户重启（重新 provision）
    Error --> Running: 编排侧自动恢复（Sandbox 运行中 + Agent Runtime 可达）
    Error --> Provisioning: 用户手动重启
    Stopped --> [*]: 永久删除
```

### 状态

| 状态      | 标识             | 说明                                             |
| ------- | -------------- | ---------------------------------------------- |
| **创建中** | `provisioning` | Sandbox 正在分配和启动                                |
| **运行中** | `running`      | Expert 就绪，可接受对话                                |
| **已停止** | `stopped`      | Sandbox 已释放；元数据与配置保留以便快速恢复                     |
| **异常**  | `error`        | Sandbox 或 Agent Runtime 不可达；恢复后可自动转为 `running` |

### 停止与删除的区别

| 操作     | Sandbox 行为          | DNS / 路由                    | 恢复速度               |
| ------ | ------------------- | --------------------------- | ------------------ |
| **停止** | 暂停或销毁沙箱计算资源，编排元数据保留 | 对外入口由 Core/Web 统一维护时可保持路由稳定 | 通常秒级（重新 provision） |
| **删除** | 元数据与工作区持久化按策略清理     | 关联 URL 失效                   | 按产品策略可能不可恢复        |

> **设计决策**：停止优先于「硬删路由对象」，避免反向代理 DNS 缓存 NXDOMAIN 导致的短时 404。旧 Kubernetes 方案中删除 Service 后 resolver 缓存曾造成唤醒窗口内无法路由，已在 Compose + 声明式 Sandbox 模型下由编排层显式管理。

### Error 状态自动恢复

`error` 不是死胡同。`checkInstanceHealth`（或等价健康任务）在轮询时检测 Sandbox 与 Agent Runtime：

* Sandbox 运行中 + Agent Runtime HTTP 可达 → 自动恢复为 `running`
* Sandbox 仍在重启/崩溃循环 → 保持 `error`，等下次检查
* Sandbox 已终结（失败 / 不存在）→ 保持 `error`，用户需手动重启

Health 路径对 `error` 状态的实例不应误触发「强制停止」，以免打断编排侧正在进行的自动恢复。

### 进入聊天的就绪门禁

前端在进入对话界面前，依次等待 3 个真实信号：

```
Sandbox 就绪 → Agent Runtime 可达 → 技能加载完成 → 文件列表预取 → 进入聊天
              ↓                      ↓                 ↓
         runtimeReady           skillsReady       filesLoaded → setView("chat")
```

| 步骤       | 信号             | 来源         | 含义                                        |
| -------- | -------------- | ---------- | ----------------------------------------- |
| 1. 启动服务  | `runtimeReady` | Health API | Sandbox 运行中 + Agent Runtime HTTP 可达       |
| 2. 装载技能  | `skillsReady`  | Health API | config 中的 URL 技能全部安装到 `workspace/skills/` |
| 3. 准备工作区 | `filesLoaded`  | 前端本地       | `loadInstanceFiles` 完成，文件列表已预取            |

用户在启动界面能看到每一步的真实进度和对勾。3 步全部完成后短暂展示全部对勾，再切入聊天。

**所有入口统一走加载页**：即使是已 `running` 的 Expert（如页面刷新、从列表重新进入），也会先显示加载页并执行 3 步就绪检查，确保文件列表在进入对话前就已就绪。对于已运行的实例，步骤 1-2 会在首次 health poll 时秒过。

### 前端文件状态管理

每个 Expert 通常映射一个主会话工作区。`sandboxFiles`（Sandbox 文件列表）是**实例级**状态，不随 session 生命周期重置：

| 概念               | 级别           | 说明                                      |
| ---------------- | ------------ | --------------------------------------- |
| `sandboxFiles`   | Expert / 实例级 | Sandbox 文件系统列表，由 `loadInstanceFiles` 填充 |
| `trajectoryData` | Session 级    | 对话轨迹（tool calls、todos 等）                |
| `dbEvents`       | Session 级    | 持久化的对话事件，用于刷新后回放                        |

`switchToSession` 在创建新 session 快照时，会保留当前 `sandboxFiles`，避免因 session 初始化而清空已加载的 Sandbox 文件。

> **历史 Bug**：`switchToSession` 曾用 `initialWorkspaceSnapshot`（`sandboxFiles: []`）创建 fresh session，导致 `loadInstanceFiles` 加载的文件在 `setView("chat")` 触发 session 恢复时被清空——表现为文件「闪一下然后消失」。

### Expert / 实例管理接口

以下 HTTP 路径沿用历史兼容前缀 `/api/claw/*`（实现上对应 Expert 与 Sandbox 编排）：

| 方法     | 路径                                | 说明                           |
| ------ | --------------------------------- | ---------------------------- |
| POST   | `/api/claw/instances`             | 创建 Expert / Sandbox          |
| GET    | `/api/claw/instances`             | 列出用户的实例                      |
| GET    | `/api/claw/instances/:id`         | 实例详情                         |
| PATCH  | `/api/claw/instances/:id`         | 重命名实例                        |
| DELETE | `/api/claw/instances/:id`         | 删除实例（`?permanent=true` 永久删除） |
| POST   | `/api/claw/instances/:id/restart` | 重启实例                         |
| POST   | `/api/claw/instances/:id/stop`    | 停止实例                         |

## SSE 实时流式交互

Agent 平台的核心交互基于 Server-Sent Events（SSE）协议，前端通过 `POST /api/claw/chat` 发送消息并接收实时流式响应。

### SSE 事件流

```mermaid theme={null}
sequenceDiagram
    participant U as 用户
    participant W as Web
    participant Proxy as SSE 代理
    participant Engine as "Agent Runtime（Sandbox 内）"

    U->>W: 发送消息
    W->>Proxy: POST /api/claw/chat
    Proxy->>Engine: 转发请求（注入用户身份）

    loop SSE 事件流
        Engine-->>Proxy: event: text (文本片段)
        Proxy-->>W: 转发 SSE 事件
        W-->>U: 实时渲染文本

        Engine-->>Proxy: event: tool_call_chunk (工具调用)
        Proxy-->>W: 转发
        W-->>U: 展示工具执行状态
    end

    Engine-->>Proxy: event: complete
    Proxy-->>W: 流结束
    W-->>U: 对话完成
```

### SSE 事件类型

| 事件                | 说明                |
| ----------------- | ----------------- |
| `text`            | AI 文本输出片段，增量渲染    |
| `tool_call_chunk` | 工具调用信息（函数名、参数、结果） |
| `complete`        | 对话回合完成            |
| `error`           | 错误信息              |

### 消息处理架构

前端通过模块化的 Message Handler 处理不同事件类型：

```
apps/web/src/lib/message-handlers/
├── stream-processor.ts    # SSE 流解析和分发
├── tool-call-handler.ts   # 工具调用渲染
├── text-handler.ts        # 文本片段拼接
├── error-handler.ts       # 错误处理
└── complete-handler.ts    # 完成回调
```

## Sandbox 与 Agent Runtime 访问

### 路由与端口

每个 Expert / Sandbox 分配由 Core 编排解析得到的运行时 URL（历史上常称 `containerUrl`）。Next.js 代理根据编排结果访问 Sandbox 内的 Agent Runtime，无需自建 Kubernetes Ingress。

| 端口    | 用途                            | 访问方式                     |
| ----- | ----------------------------- | ------------------------ |
| 18789 | Agent Runtime HTTP（API + SSE） | 编排返回的默认入口                |
| 3000  | Web 预览（开发服务器）                 | `runtimeUrl:3000`（视模板而定） |
| 3001  | 备用预览端口                        | 视模板而定                    |
| 6080  | noVNC 浏览器访问                   | 视模板而定                    |

### 实例配置

| 接口                                         | 说明                       |
| ------------------------------------------ | ------------------------ |
| `GET /api/claw/instances/:id/config`       | 读取 Agent Runtime / 工作区配置 |
| `PATCH /api/claw/instances/:id/config`     | 更新配置（`?apply=true` 立即生效） |
| `GET /api/claw/instances/:id/config-drift` | 检测配置漂移                   |

### 文件与预览

| 接口                                  | 说明                     |
| ----------------------------------- | ---------------------- |
| `GET /api/claw/files`               | 列出/读取/导出 Sandbox 工作区文件 |
| `POST /api/claw/files`              | 上传文件到 Sandbox          |
| `GET /api/claw/preview/:instanceId` | 代理到 Sandbox 内的 Web 服务  |
| `GET /api/claw/novnc`               | noVNC 桌面预览代理           |

## 技能系统

Expert 支持在 Sandbox 内安装和管理技能（Skill），扩展 Agent 能力：

| 方法    | 路径                               | 说明       |
| ----- | -------------------------------- | -------- |
| GET   | `/api/claw/instances/:id/skills` | 实例已安装的技能 |
| GET   | `/api/claw/skills`               | 技能注册表    |
| PATCH | `/api/claw/skills`               | 启用/禁用技能  |

技能市场通过 `SkillMarketDialog` 组件提供可视化的技能发现与安装体验。

## 积分与服务计划

Expert / Sandbox 的运行消耗积分，计费规则由服务计划（Service Plan）定义。

```mermaid theme={null}
graph LR
    Plan["服务计划"] --> Price["计费规则"]
    Price --> Consume["积分消费"]
    Consume --> Balance["余额扣减"]

    Check["预检余额"] -.-> Consume
```

| 接口                                   | 说明               |
| ------------------------------------ | ---------------- |
| `GET /api/claw/credits`              | 查询余额（代理到 Core）   |
| `GET /api/claw/service-plans`        | 服务计划列表（代理到 Core） |
| `GET /api/claw/credits/transactions` | 消费记录             |

## Auth 桥接

Expert 对话与 Portal 共享用户体系。Web 端相关路由通过以下机制桥接认证：

```mermaid theme={null}
flowchart LR
    Cookie["profy_token Cookie"] --> Extract["getTokenFromHeaders"]
    Extract --> Cache{"内存缓存<br/>TTL 5min"}
    Cache -->|命中| UserId["返回 userId"]
    Cache -->|未命中| Verify["调用 Core<br/>/api/auth/userinfo"]
    Verify --> UserId
    UserId --> ExpertAPI["Expert / Profy 兼容 API 路由"]
```

* `proxy.ts` 保护需登录的页面路由，无 Token 重定向登录
* `getAuthUserId()` 为上述 API 路由提供用户身份
* Token 在代理转发时自动注入到上游请求

## 健康检查与清理

| 接口                             | 说明                                       |
| ------------------------------ | ---------------------------------------- |
| `GET /api/claw/health`         | 实例健康状态（含 `runtimeReady` + `skillsReady`） |
| `POST /api/claw/health`        | 超时实例清理 + error 状态自动恢复检测                  |
| `POST /api/claw/admin/cleanup` | 管理员清理孤立实例                                |

Health API 返回字段（名称可能随版本演进，以 OpenAPI/实现为准）：

| 字段             | 类型      | 说明                                      |
| -------------- | ------- | --------------------------------------- |
| `healthy`      | boolean | Sandbox / 进程是否存活                        |
| `runtimeReady` | boolean | Agent Runtime HTTP 端口可达                 |
| `skillsReady`  | boolean | 所有 URL 技能已安装到 `workspace/skills/`       |
| `podStatus`    | string  | 历史字段名；语义上表示编排状态如 `running` / `starting` |
| `uptime`       | number  | 实例运行秒数                                  |

## 相关链接

<CardGroup cols={2}>
  <Card title="系统架构" icon="sitemap" href="/zh/documentation/architecture/overview">
    Expert 与 Agent Runtime 在整体架构中的位置
  </Card>

  <Card title="金融支付" icon="credit-card" href="/zh/documentation/finance">
    Expert / Sandbox 的积分计费
  </Card>

  <Card title="应用市场" icon="store" href="/zh/documentation/marketplace">
    技能市场与应用发现
  </Card>

  <Card title="部署指南" icon="server" href="/zh/documentation/deployment">
    Docker Compose 与可选 E2B Sandbox
  </Card>
</CardGroup>
