mirror of
https://github.com/labring/FastGPT.git
synced 2026-04-26 02:07:28 +08:00
V4.14.9 dev (#6555)
* feat: encapsulate logger (#6535) * feat: encapsulate logger * update engines --------- Co-authored-by: archer <545436317@qq.com> * next config * dev shell * Agent sandbox (#6532) * docs: switch to docs layout and apply black theme (#6533) * feat: add Gemini 3.1 models - Add gemini-3.1-pro-preview (released February 19, 2026) - Add gemini-3.1-flash-lite-preview (released March 3, 2026) Both models support: - 1M context window - 64k max response - Vision - Tool choice * docs: switch to docs layout and apply black theme - Change layout from notebook to docs - Update logo to icon + text format - Apply fumadocs black theme - Simplify global.css (keep only navbar and TOC styles) - Fix icon components to properly accept className props - Add mobile text overflow handling - Update Node engine requirement to >=20.x * doc * doc * lock * fix: ts * doc * doc --------- Co-authored-by: archer <archer@archerdeMac-mini.local> Co-authored-by: archer <545436317@qq.com> * Doc (#6493) * cloud doc * doc refactor * doc move * seo * remove doc * yml * doc * fix: tsconfig * fix: tsconfig * sandbox version (#6497) * sandbox version * add sandbox log * update lock * fix * fix: sandbox * doc * add console * i18n * sandbxo in agent * feat: agent sandbox * lock * feat: sandbox ui * sandbox check exists * env tempalte * doc * lock * sandbox in chat window * sandbox entry * fix: test * rename var * sandbox config tip * update sandbox lifecircle * update prompt * rename provider test * sandbox logger * yml --------- Co-authored-by: Archer <archer@fastgpt.io> Co-authored-by: archer <archer@archerdeMac-mini.local> * perf: sandbox error tip * Add sandbox limit and fix some issue (#6550) * sandbox in plan * fix: some issue * fix: test * editor default path * fix: comment * perf: sandbox worksapce * doc * perf: del sandbox * sandbox build * fix: test * fix: pr comment --------- Co-authored-by: Ryo <whoeverimf5@gmail.com> Co-authored-by: Archer <archer@fastgpt.io> Co-authored-by: archer <archer@archerdeMac-mini.local>
This commit is contained in:
@@ -5,6 +5,10 @@ description: 当用户需要设计 FastGPT 的代码时,可调用此 Skill。
|
||||
|
||||
## 目录
|
||||
|
||||
### AI
|
||||
|
||||
* [AI 虚拟机设计文档](./core/ai/sandbox/prd.md)
|
||||
|
||||
### 工作流
|
||||
|
||||
* [工作流设计文档](./core/workflow/index.md)
|
||||
|
||||
@@ -0,0 +1,356 @@
|
||||
# FastGPT AI Sandbox 集成方案
|
||||
|
||||
## 一、背景与目标
|
||||
|
||||
当 Agent 拥有一个独立虚拟机时,可以执行代码、管理文件、调用系统命令,能力大幅增强。本方案通过接入外部沙盒服务,为每个会话提供一个隔离、持久的容器环境,让 Agent 拥有完整的 root 权限操作空间。
|
||||
|
||||
**核心目标**:
|
||||
- 每会话独立隔离,互不干扰
|
||||
- Agent 无感知沙盒状态,调用接口简单
|
||||
- 沙盒自动生命周期管理,节省资源
|
||||
- 支持用户通过 SSH/Web IDE 直接进入沙盒
|
||||
|
||||
---
|
||||
|
||||
## 二、整体架构
|
||||
|
||||
FastGPT 作为**纯业务层**,只负责在合适时机调用 SDK;沙盒的生命周期管理、配额、清理、审计等全部由下游(SDK / SSS)负责。
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph FastGPT["FastGPT 业务层"]
|
||||
Agent["Agent / 工具调用节点\n(useAgentSandbox=true)"]
|
||||
SandboxMgr["execShell()\n(薄封装)"]
|
||||
end
|
||||
|
||||
subgraph SDK["@fastgpt-sdk/sandbox-adapter"]
|
||||
Adapter["统一适配器\ncreate() / exec()"]
|
||||
end
|
||||
|
||||
subgraph Downstream["下游服务(FastGPT 不关心)"]
|
||||
SSS["Sealos Sandbox Server\n(生命周期 / 配额 / 审计)"]
|
||||
Devbox["Sealos Devbox\n(容器实例)"]
|
||||
end
|
||||
|
||||
Agent -->|"首次调用 shell 工具时\n(懒加载)"| SandboxMgr
|
||||
SandboxMgr -->|"create() + exec()"| Adapter
|
||||
Adapter -->|"API 调用"| SSS
|
||||
SSS -->|"管理容器"| Devbox
|
||||
```
|
||||
|
||||
### 组件职责
|
||||
|
||||
| 组件 | 职责 | 归属 |
|
||||
|------|------|------|
|
||||
| **FastGPT execShell()** | 薄封装:组装 sandboxId,调用 SDK | FastGPT |
|
||||
| **@fastgpt-sdk/sandbox-adapter** | 统一适配层;`create()` 保证返回可用沙盒 | SDK |
|
||||
| **Sealos Sandbox Server** | 容器 CRUD、生命周期管理、配额、审计 | 下游 |
|
||||
| **Sealos Devbox** | 实际的隔离容器实例 | 下游 |
|
||||
|
||||
---
|
||||
|
||||
## 三、沙盒管理设计
|
||||
|
||||
### 3.1 沙盒粒度
|
||||
|
||||
沙盒以 **会话维度** 分配,唯一标识由三元组生成:
|
||||
|
||||
```
|
||||
sandboxId = hash(appId + userId + chatId)
|
||||
```
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
AppId["appId"]
|
||||
UserId["userId"]
|
||||
ChatId["chatId"]
|
||||
Hash["Hash 函数\n(SHA256)"]
|
||||
SandboxId["sandboxId\n(唯一 ID)"]
|
||||
|
||||
AppId --> Hash
|
||||
UserId --> Hash
|
||||
ChatId --> Hash
|
||||
Hash --> SandboxId
|
||||
```
|
||||
|
||||
> 不同会话之间完全隔离;同一会话内多轮对话共享同一个沙盒,保留执行上下文(变量、文件等)。
|
||||
|
||||
### 3.2 沙盒生命周期
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Running : Agent 首次调用 shell / 打开 Web IDE\ncreate()(懒加载)
|
||||
|
||||
Running --> Stoped : FastGPT 定时任务\n(5 分钟无活动)
|
||||
Stoped --> Running : Agent 再次调用 shell / 打开 Web IDE\ncreate() 自动恢复
|
||||
|
||||
Running --> Deleted : 会话被删除\n(异步触发)
|
||||
Stoped --> Deleted : 会话被删除\n(异步触发)
|
||||
|
||||
Deleted --> [*]
|
||||
```
|
||||
|
||||
**FastGPT 侧规则**:
|
||||
- **懒加载**:会话开始时不创建沙盒,Agent 首次调用 `shell` 工具或用户打开 Web IDE 时才触发 `create()`
|
||||
- **停止**:由 FastGPT 定时任务驱动,扫描 `lastActiveAt` 超过 5 分钟的 Running 沙盒,调用 SDK 停止
|
||||
- **销毁**:会话被删除时,**异步**触发 SDK 删除并清理 DB 记录(不阻塞会话删除主流程)
|
||||
|
||||
### 3.3 数据库设计
|
||||
|
||||
**集合名**:`sandbox_instances`
|
||||
|
||||
```typescript
|
||||
type SandboxInstanceSchema = {
|
||||
_id: ObjectId;
|
||||
provider: 'sealosdevbox'; // 沙盒提供商
|
||||
sandboxId: string; // hash(appId+userId+chatId)
|
||||
|
||||
appId?: ObjectId; // 可选,Chat 模式下关联应用
|
||||
userId?: string; // 可选,Chat 模式下关联用户
|
||||
chatId?: string; // 可选,Chat 模式下关联会话
|
||||
|
||||
status: 'running' | 'stoped';
|
||||
lastActiveAt: Date; // 最后活跃时间,驱动停止定时任务
|
||||
createdAt: Date;
|
||||
|
||||
limit?: { // 可选,资源限制
|
||||
cpuCount: number;
|
||||
memoryMiB: number;
|
||||
diskGiB: number;
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
**索引**:
|
||||
- `{ provider, sandboxId }`:唯一索引(快速查找)
|
||||
- `{ appId, chatId }`:部分唯一索引(仅当两者都存在时)
|
||||
- `{ status, lastActiveAt }`:暂停定时任务扫描
|
||||
|
||||
### 3.4 定时任务 & 触发时机
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph StopJob["停止任务(每 5 分钟)"]
|
||||
S1["查询 status=running\n且 lastActiveAt < now-5min"] --> S2["SDK.stop(sandboxId)"]
|
||||
S2 --> S3["更新 status=stoped"]
|
||||
end
|
||||
|
||||
subgraph DeleteTrigger["会话删除(事件触发)"]
|
||||
D1["单个会话删除\ndelete by chatId"] --> D2["查询 chatId 对应沙盒"]
|
||||
D2 --> D3["异步:SDK.delete(sandboxId)"]
|
||||
D3 --> D4["删除 DB 记录"]
|
||||
|
||||
D5["整个应用删除\ndelete by appId"] --> D6["查询 appId 下所有沙盒"]
|
||||
D6 --> D7["批量异步:SDK.delete(sandboxId)"]
|
||||
D7 --> D8["批量删除 DB 记录"]
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、执行流程
|
||||
|
||||
Agent 调用 shell 工具时序:
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Agent as Agent 节点
|
||||
participant Exec as execShell()
|
||||
participant DB as MongoDB
|
||||
participant SDK as sandbox-adapter
|
||||
participant SSS as Sealos SSS
|
||||
|
||||
Agent->>Exec: execShell({ appId, userId, chatId, command })
|
||||
Note over Exec: sandboxId = hash(appId+userId+chatId)
|
||||
|
||||
Exec->>SDK: create(sandboxId)
|
||||
Note over SDK,SSS: 幂等:不存在则创建,Stoped 则唤醒,Running 则直接返回
|
||||
SDK-->>Exec: SandboxClient
|
||||
|
||||
Exec->>DB: upsert { sandboxId, status=running, lastActiveAt=now }
|
||||
|
||||
Exec->>SDK: exec(sandboxId, command, timeout)
|
||||
SDK->>SSS: 执行命令
|
||||
SSS-->>SDK: { stdout, stderr, exitCode }
|
||||
SDK-->>Exec: ExecResult
|
||||
Exec-->>Agent: { stdout, stderr, exitCode }
|
||||
```
|
||||
|
||||
**FastGPT 侧代码逻辑(伪代码)**:
|
||||
|
||||
```typescript
|
||||
async function execShell(params: {
|
||||
appId: string;
|
||||
userId: string;
|
||||
chatId: string;
|
||||
command: string;
|
||||
timeout?: number;
|
||||
}) {
|
||||
const { appId, userId, chatId, command, timeout } = params;
|
||||
const sandboxId = sha256(`${appId}-${userId}-${chatId}`).slice(0, 16);
|
||||
const sandbox = await sandboxAdapter.create(sandboxId); // 幂等,保证可用
|
||||
await SandboxInstanceModel.upsert({ sandboxId, status: 'running', lastActiveAt: new Date() });
|
||||
return sandboxAdapter.exec(sandbox.id, command, { timeout });
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
## 五、Agent 工具设计
|
||||
|
||||
### 5.1 节点改造方案
|
||||
|
||||
**不新增节点类型**,在现有的**工具调用节点**上增加一个 input:
|
||||
|
||||
```typescript
|
||||
{
|
||||
key: 'useAgentSandbox',
|
||||
type: 'switch', // 开关类型
|
||||
label: '启用沙盒(Computer Use)',
|
||||
defaultValue: false,
|
||||
description: '开启后,Agent 将获得一个独立 Linux 环境,可执行命令、操作文件'
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 启用后的行为
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
NodeExec["工具调用节点执行"] --> Check{useAgentSandbox\n= true?}
|
||||
Check -->|否| Normal["正常执行,不注入任何沙盒能力"]
|
||||
Check -->|是| Inject["自动注入内置 sandbox_shell 工具\n到 Agent 的 tools 列表"]
|
||||
Inject --> Prompt["在 System Prompt 末尾\n追加沙盒环境说明"]
|
||||
Prompt --> Run["Agent 正常运行\n(可自主决定是否调用 sandbox_shell)"]
|
||||
Run --> CallShell{Agent 调用\nsandbox_shell 工具?}
|
||||
CallShell -->|否| End["正常返回"]
|
||||
CallShell -->|是| Sandbox["SandboxClient\n执行命令并返回结果"]
|
||||
Sandbox --> End
|
||||
```
|
||||
|
||||
**自动注入的内置 sandbox_shell 工具定义**:
|
||||
|
||||
```typescript
|
||||
// 由系统内置,不需要用户配置,useAgentSandbox=true 时自动追加到 tools
|
||||
export const SANDBOX_SHELL_TOOL: ChatCompletionTool = {
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'sandbox_shell',
|
||||
description: '在独立 Linux 环境中执行 shell 命令,支持文件操作、代码运行、包安装等',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
command: { type: 'string', description: '要执行的 shell 命令' },
|
||||
timeout: {
|
||||
type: 'number',
|
||||
description: '超时秒数',
|
||||
max: 300,
|
||||
min: 1
|
||||
}
|
||||
},
|
||||
required: ['command']
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### 5.3 自动注入的系统提示词
|
||||
|
||||
`useAgentSandbox=true` 时,在节点原有 System Prompt **末尾追加**:
|
||||
|
||||
```
|
||||
你拥有一个独立的 Linux 沙盒环境(Ubuntu 22.04),可通过 sandbox_shell 工具执行命令:
|
||||
- 预装:bash / python3 / node / bun / git / curl
|
||||
- 工作目录:/workspace(文件在本次会话内持久保留)
|
||||
- 可自行安装软件包(apt / pip / npm)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 六、错误处理
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Exec["执行命令"] --> E1{沙盒服务\n不可用?}
|
||||
E1 -->|是| Err1["返回错误:\n'沙盒服务暂时不可用,请稍后重试'\nexitCode=-1"]
|
||||
E1 -->|否| E3{exitCode != 0?}
|
||||
E3 -->|是| Warn["返回 stderr 内容\n(非致命错误,Agent 可继续)"]
|
||||
E3 -->|否| OK["返回 stdout\n正常执行完成"]
|
||||
```
|
||||
|
||||
| 错误类型 | exitCode | 处理策略 |
|
||||
|----------|----------|----------|
|
||||
| 沙盒服务不可用 | -1 | 返回错误,终止当前节点,不中断整个工作流 |
|
||||
| 命令执行失败 | ≠0 | 将 stderr 作为输出返回,由 Agent 自行判断 |
|
||||
| 命令超时 | 由上游处理 | 上游沙盒服务自动断开,FastGPT 透传结果即可 |
|
||||
|
||||
---
|
||||
|
||||
## 七、安全与资源限制
|
||||
|
||||
FastGPT 业务层只控制命令超时,其余由下游负责:
|
||||
|
||||
| 限制项 | FastGPT 侧 | 说明 |
|
||||
|--------|-----------|------|
|
||||
| **命令超时** | 支持传递 timeout 参数 | 由上游沙盒服务控制,FastGPT 透传 timeout 参数(秒)转换为毫秒 |
|
||||
| **CPU / 内存 / 磁盘** | 不关心 | 下游(SSS/Devbox)控制 |
|
||||
| **配额** | 不关心 | 下游控制 |
|
||||
| **网络隔离** | 不关心 | 下游控制 |
|
||||
| **审计日志** | 不关心 | 下游控制 |
|
||||
|
||||
---
|
||||
|
||||
## 八、前端功能
|
||||
|
||||
### 8.1 文件操作 API
|
||||
|
||||
提供文件读写和下载接口,替代 Web IDE 方案:
|
||||
|
||||
**文件操作 API**:`POST /api/core/ai/sandbox/file`
|
||||
|
||||
```typescript
|
||||
// 支持三种操作
|
||||
type Action = 'list' | 'read' | 'write';
|
||||
|
||||
// 列出目录
|
||||
{ action: 'list', appId, chatId, path: '/workspace' }
|
||||
→ { action: 'list', files: [{ name, path, type, size }] }
|
||||
|
||||
// 读取文件
|
||||
{ action: 'read', appId, chatId, path: '/workspace/test.txt' }
|
||||
→ { action: 'read', content: 'file content' }
|
||||
|
||||
// 写入文件
|
||||
{ action: 'write', appId, chatId, path: '/workspace/test.txt', content: 'new content' }
|
||||
→ { action: 'write', success: true }
|
||||
```
|
||||
|
||||
**文件下载 API**:`POST /api/core/ai/sandbox/download`
|
||||
|
||||
```typescript
|
||||
// 下载单个文件或整个目录(ZIP)
|
||||
{ appId, chatId, path: '/workspace' }
|
||||
→ 返回文件流或 ZIP 压缩包
|
||||
```
|
||||
|
||||
### 8.2 沙盒状态展示
|
||||
|
||||
在对话页面的工具调用结果中,展示:
|
||||
- 命令内容(折叠显示)
|
||||
- 执行状态(成功/失败/超时)
|
||||
- stdout/stderr 输出(Markdown 代码块)
|
||||
- 执行耗时
|
||||
- 文件操作入口(列表、读取、下载)
|
||||
|
||||
---
|
||||
|
||||
## 九、设计决策记录
|
||||
|
||||
| 问题 | 决策 |
|
||||
|------|------|
|
||||
| 沙盒配额管理 | 不关心,由下游处理 |
|
||||
| 沙盒何时创建 | 懒加载,Agent 首次调用 sandbox_shell 时才创建 |
|
||||
| 停止由谁驱动 | **FastGPT 定时任务**,5 分钟无活动自动停止,不可配置 |
|
||||
| 销毁由谁驱动 | **会话删除时异步触发**,不依赖定时任务 |
|
||||
| 多厂商适配 | 由 SDK 适配层处理,FastGPT 不感知 |
|
||||
| 审计日志 | 下游处理,FastGPT 不记录 |
|
||||
| 事务一致性 | 使用 mongoSessionRun 保证 DB 操作和 SDK 调用的一致性 |
|
||||
| Web IDE 方案 | 改为文件操作 API(list/read/write/download),不使用 Web IDE |
|
||||
@@ -0,0 +1,489 @@
|
||||
# FastGPT AI Sandbox 技术方案
|
||||
|
||||
> 基于 [PRD](./prd.md),本文档详细到每个需要改造/新增的文件。
|
||||
|
||||
---
|
||||
|
||||
## 一、改造总览
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph 新增文件["🆕 新增文件"]
|
||||
A1["packages/service/core/ai/sandbox/schema.ts"]
|
||||
A2["packages/service/core/ai/sandbox/controller.ts\n(含 SandboxClient 类 + cronJob)"]
|
||||
A4["packages/global/core/ai/sandbox/constants.ts"]
|
||||
A5["projects/app/src/pages/api/core/ai/sandbox/webideUrl.ts"]
|
||||
A6["packages/service/.../agent/sub/sandbox/utils.ts"]
|
||||
end
|
||||
|
||||
subgraph 改造文件["✏️ 改造文件"]
|
||||
B1["packages/global/core/workflow/constants.ts"]
|
||||
B1b["packages/global/.../agent/constants.ts\n(SubAppIds + systemSubInfo)"]
|
||||
B3["packages/service/.../agent/utils.ts\n(getSubapps)"]
|
||||
B4["packages/service/.../agent/master/call.ts"]
|
||||
B5["packages/service/.../agent/master/prompt.ts"]
|
||||
B6["packages/service/.../ai/tool/index.ts"]
|
||||
B7["packages/service/.../ai/tool/toolCall.ts"]
|
||||
B9["projects/app/.../system/cron.ts"]
|
||||
B10["projects/app/.../chat/history/batchDelete.ts"]
|
||||
B11["packages/service/core/app/controller.ts"]
|
||||
end
|
||||
|
||||
A6 -.-> B3
|
||||
A2 -.-> B4
|
||||
A2 -.-> B9
|
||||
A2 -.-> B10
|
||||
A2 -.-> B11
|
||||
A4 -.-> B5
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 二、新增文件
|
||||
|
||||
### 2.1 `packages/global/core/ai/sandbox/constants.ts`
|
||||
|
||||
沙盒相关的全局常量和类型定义。
|
||||
|
||||
```typescript
|
||||
// 沙盒系统提示词(useComputer=true 时追加到 System Prompt)
|
||||
export const SANDBOX_SYSTEM_PROMPT = `你拥有一个独立的 Linux 沙盒环境(Ubuntu 22.04),可通过 shell 工具执行命令:
|
||||
- 预装:bash / python3 / node / git / curl / wget
|
||||
- 工作目录:/workspace(文件在本次会话内持久保留)
|
||||
- 可自行安装软件包(apt / pip / npm)
|
||||
- 可通过 timeout 参数指定命令超时时间`;
|
||||
|
||||
// 内置 shell 工具的 function calling schema
|
||||
export const SANDBOX_SHELL_TOOL_SCHEMA = {
|
||||
type: 'function' as const,
|
||||
function: {
|
||||
name: 'sandbox_shell',
|
||||
description: '在独立 Linux 环境中执行 shell 命令,支持文件操作、代码运行、包安装等',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
command: { type: 'string', description: '要执行的 shell 命令' },
|
||||
timeout: { type: 'number', description: '超时秒数(可选,由上游沙盒服务控制)' }
|
||||
},
|
||||
required: ['command']
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 沙盒状态枚举
|
||||
export const SandboxStatusEnum = {
|
||||
running: 'running',
|
||||
stoped: 'stoped'
|
||||
} as const;
|
||||
|
||||
// 沙盒默认配置
|
||||
export const SANDBOX_SUSPEND_MINUTES = 5;
|
||||
|
||||
export const AGENT_SANDBOX_PROVIDER = process.env.AGENT_SANDBOX_PROVIDER
|
||||
export const AGENT_SANDBOX_SEALOS_BASEURL = process.env.AGENT_SANDBOX_SEALOS_BASEURL
|
||||
export const AGENT_SANDBOX_SEALOS_TOKEN = process.env.AGENT_SANDBOX_SEALOS_TOKEN
|
||||
```
|
||||
|
||||
### 2.2 `packages/service/core/ai/sandbox/schema.ts`
|
||||
|
||||
MongoDB Model 定义。
|
||||
|
||||
```typescript
|
||||
// 集合名: sandbox_instances
|
||||
// 字段: sandboxId(唯一), appId, userId, chatId, status('running'|'stoped'), lastActiveAt, createdAt
|
||||
// 索引: sandboxId(unique), chatId, appId, { status, lastActiveAt }
|
||||
```
|
||||
|
||||
### 2.3 `packages/service/core/ai/sandbox/controller.ts`
|
||||
|
||||
沙盒业务逻辑层,核心类和函数:
|
||||
|
||||
**SandboxClient 类**:
|
||||
| 方法 | 职责 |
|
||||
|------|------|
|
||||
| `constructor({ appId, userId, chatId })` | 初始化实例,生成 sandboxId,创建 SDK adapter |
|
||||
| `exec(command, timeout?)` | SDK.create() → upsert DB (status=running) → SDK.execute() → 返回结果 |
|
||||
| `delete()` | 使用事务:删除 DB 记录 + SDK.delete() |
|
||||
| `stop()` | 使用事务:更新 DB status=stoped + SDK.stop() |
|
||||
|
||||
**导出函数**:
|
||||
| 函数 | 职责 |
|
||||
|------|------|
|
||||
| `deleteSandboxesByChatIds(appId, chatIds)` | 查询 DB → 批量创建实例 → 调用 delete() |
|
||||
| `deleteSandboxesByAppId(appId)` | 查询 DB → 批量创建实例 → 调用 delete() |
|
||||
| `cronJob()` | 定时任务:查询 lastActiveAt 超时的 running 记录 → 批量调用 stop() |
|
||||
|
||||
**实现细节**:
|
||||
- 使用 `mongoSessionRun` 保证 DB 操作和 SDK 调用的事务一致性
|
||||
- 定时任务直接在 controller.ts 中实现,使用 `setCron('*/5 * * * *', ...)`
|
||||
- 错误处理:SDK.create() 失败时返回 exitCode=-1 的错误结果
|
||||
|
||||
### 2.4 `projects/app/src/pages/api/core/ai/sandbox/file.ts`
|
||||
|
||||
文件操作 API(列表、读取、写入)。
|
||||
|
||||
```typescript
|
||||
POST /api/core/ai/sandbox/file
|
||||
Body: {
|
||||
appId: string;
|
||||
chatId: string;
|
||||
action: 'list' | 'read' | 'write';
|
||||
path: string;
|
||||
content?: string; // write 时必需
|
||||
outLinkAuthData?: object;
|
||||
}
|
||||
Auth: authChatCrud
|
||||
Response:
|
||||
- list: { action: 'list', files: Array<{ name, path, type, size }> }
|
||||
- read: { action: 'read', content: string }
|
||||
- write: { action: 'write', success: boolean }
|
||||
```
|
||||
|
||||
### 2.5 `projects/app/src/pages/api/core/ai/sandbox/download.ts`
|
||||
|
||||
文件下载 API(单文件或目录 ZIP)。
|
||||
|
||||
```typescript
|
||||
POST /api/core/ai/sandbox/download
|
||||
Body: {
|
||||
appId: string;
|
||||
chatId: string;
|
||||
path: string; // 文件或目录路径
|
||||
outLinkAuthData?: object;
|
||||
}
|
||||
Auth: authChatCrud
|
||||
Response: 文件流或 ZIP 压缩包
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、改造文件
|
||||
|
||||
### 3.2 `packages/global/core/ai/sandbox/constants.ts`
|
||||
|
||||
**改动**:沙盒相关的全局常量和类型定义。
|
||||
|
||||
```typescript
|
||||
// 沙盒状态枚举
|
||||
export const SandboxStatusEnum = {
|
||||
running: 'running',
|
||||
stoped: 'stoped'
|
||||
} as const;
|
||||
|
||||
// 沙盒默认配置
|
||||
export const SANDBOX_SUSPEND_MINUTES = 5;
|
||||
|
||||
// sandboxId 生成函数
|
||||
export const generateSandboxId = (appId: string, userId: string, chatId: string): string => {
|
||||
return hashStr(`${appId}-${userId}-${chatId}`).slice(0, 16);
|
||||
};
|
||||
|
||||
// 工具名称和图标
|
||||
export const SANDBOX_NAME: I18nStringType = {
|
||||
'zh-CN': '虚拟机',
|
||||
'zh-Hant': '虛擬機',
|
||||
en: 'Sandbox'
|
||||
};
|
||||
export const SANDBOX_ICON = 'core/app/sandbox/sandbox';
|
||||
export const SANDBOX_TOOL_NAME = 'sandbox_shell';
|
||||
|
||||
// 系统提示词
|
||||
export const SANDBOX_SYSTEM_PROMPT = `你拥有一个独立的 Linux 沙盒环境(Ubuntu 22.04),可通过 ${SANDBOX_TOOL_NAME} 工具执行命令:
|
||||
- 预装:bash / python3 / node / bun / git / curl
|
||||
- 工作目录:/workspace(文件在本次会话内持久保留)
|
||||
- 可自行安装软件包(apt / pip / npm)`;
|
||||
|
||||
// 工具定义
|
||||
export const SANDBOX_SHELL_TOOL: ChatCompletionTool = {
|
||||
type: 'function',
|
||||
function: {
|
||||
name: SANDBOX_TOOL_NAME,
|
||||
description: '在独立 Linux 环境中执行 shell 命令,支持文件操作、代码运行、包安装等',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
command: { type: 'string', description: '要执行的 shell 命令' },
|
||||
timeout: { type: 'number', description: '超时秒数', max: 300, min: 1 }
|
||||
},
|
||||
required: ['command']
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Zod Schema 用于参数验证
|
||||
export const SandboxShellToolSchema = z.object({
|
||||
command: z.string(),
|
||||
timeout: z.number().optional()
|
||||
});
|
||||
```
|
||||
|
||||
**影响范围**:新增文件,提供全局常量和类型定义。
|
||||
|
||||
### 3.3 `packages/global/core/workflow/constants.ts`
|
||||
|
||||
**改动**:在 `NodeInputKeyEnum` 中新增 key。
|
||||
|
||||
```typescript
|
||||
// 新增
|
||||
useAgentSandbox = 'useAgentSandbox', // 启用沙盒(Computer Use)
|
||||
```
|
||||
|
||||
**影响范围**:枚举新增,不影响现有逻辑。
|
||||
|
||||
---
|
||||
|
||||
### 3.4 `packages/service/env.ts`
|
||||
|
||||
**改动**:新增沙盒相关环境变量定义。
|
||||
|
||||
```typescript
|
||||
export const env = createEnv({
|
||||
server: {
|
||||
AGENT_SANDBOX_PROVIDER: z.enum(['sealosdevbox']).optional(),
|
||||
AGENT_SANDBOX_SEALOS_BASEURL: z.string().optional(),
|
||||
AGENT_SANDBOX_SEALOS_TOKEN: z.string().optional(),
|
||||
// ...其他环境变量
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
**影响范围**:环境变量验证和类型定义。
|
||||
|
||||
---
|
||||
|
||||
### 3.3 `packages/service/core/workflow/dispatch/ai/agent/sub/sandbox/utils.ts` 🆕
|
||||
|
||||
**新增文件**:沙盒工具定义,与 `sub/dataset/utils.ts`、`sub/file/utils.ts` 同级。
|
||||
|
||||
```typescript
|
||||
import type { ChatCompletionTool } from '@fastgpt/global/core/ai/type';
|
||||
import { SubAppIds } from '@fastgpt/global/core/workflow/node/agent/constants';
|
||||
import z from 'zod';
|
||||
|
||||
// Agent 调用时传递的参数
|
||||
export const SandboxShellToolSchema = z.object({
|
||||
command: z.string(),
|
||||
timeout: z.number().optional()
|
||||
});
|
||||
|
||||
// ChatCompletionTool 定义
|
||||
export const sandboxShellTool: ChatCompletionTool = {
|
||||
type: 'function',
|
||||
function: {
|
||||
name: SubAppIds.sandboxShell,
|
||||
description: '在独立 Linux 环境中执行 shell 命令,支持文件操作、代码运行、包安装等',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
command: { type: 'string', description: '要执行的 shell 命令' },
|
||||
timeout: { type: 'number', description: '超时秒数(可选,由上游沙盒服务控制)' }
|
||||
},
|
||||
required: ['command']
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.4 `packages/service/core/workflow/dispatch/ai/agent/utils.ts`
|
||||
|
||||
**改动**:在 `getSubapps()` 中新增 `useAgentSandbox` 参数,与 `hasDataset`、`hasFiles` 同级注入。
|
||||
|
||||
```typescript
|
||||
// 参数新增
|
||||
export const getSubapps = async ({
|
||||
// ...现有参数...
|
||||
useAgentSandbox // 新增
|
||||
}: {
|
||||
// ...现有类型...
|
||||
useAgentSandbox?: boolean; // 新增
|
||||
}) => {
|
||||
// ...现有逻辑...
|
||||
|
||||
/* Sandbox Shell */ // 新增,与 Dataset Search 同级
|
||||
if (useAgentSandbox) {
|
||||
completionTools.push(sandboxShellTool);
|
||||
}
|
||||
|
||||
// ...后续不变...
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.5 `packages/service/core/workflow/dispatch/ai/agent/master/call.ts`
|
||||
|
||||
**改动**:在工具调用分发逻辑中,处理 `sandbox_shell` 的调用结果。
|
||||
|
||||
```
|
||||
位置:约第 440 行附近,工具调用分发逻辑
|
||||
当前:已有 plan / dataset / file / model / tool 等分支
|
||||
新增:if (toolName === SubAppIds.sandboxShell) { 调用 execShell() 并返回结果 }
|
||||
```
|
||||
|
||||
与 `datasetSearch` 的处理方式一致:拦截内置工具名 → 调用对应 controller → 格式化结果返回给 Agent。
|
||||
|
||||
---
|
||||
|
||||
### 3.6 `packages/service/core/workflow/dispatch/ai/agent/master/prompt.ts`
|
||||
|
||||
**改动**:`getMasterSystemPrompt()` 函数中,当 `useAgentSandbox=true` 时,在 System Prompt 末尾追加沙盒环境说明。
|
||||
|
||||
```typescript
|
||||
// 新增参数 useAgentSandbox?: boolean
|
||||
// 当 useAgentSandbox=true 时,追加 SANDBOX_SYSTEM_PROMPT
|
||||
export const getMasterSystemPrompt = (
|
||||
systemPrompt?: string,
|
||||
hasUserTools: boolean = true,
|
||||
useAgentSandbox?: boolean // 新增
|
||||
) => {
|
||||
let prompt = `...现有逻辑...`;
|
||||
if (useAgentSandbox) {
|
||||
prompt += `\n\n${SANDBOX_SYSTEM_PROMPT}`;
|
||||
}
|
||||
return prompt;
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.7 `packages/service/core/workflow/dispatch/ai/tool/index.ts`
|
||||
|
||||
**改动**:`dispatchRunTools`(toolCall 模式)中,读取 `useAgentSandbox` 输入值,传递给下游。
|
||||
|
||||
```
|
||||
位置:函数入口处,从 inputs 中读取 useAgentSandbox
|
||||
传递给 runToolCall() 调用
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.8 `packages/service/core/workflow/dispatch/ai/tool/toolCall.ts`
|
||||
|
||||
**改动**:`runToolCall` 中:
|
||||
|
||||
1. 当 `useAgentSandbox=true` 时,在 `tools` 数组中追加 `sandboxShellTool`
|
||||
2. 在 System Prompt 末尾追加 `SANDBOX_SYSTEM_PROMPT`
|
||||
3. 处理 AI 返回的 `sandbox_shell` 工具调用:拦截 → 调用 `execShell()` → 将结果作为 tool response 返回
|
||||
|
||||
```
|
||||
位置:约第 58-109 行(构建 tools 参数处)和第 205-267 行(处理工具响应处)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.9 `projects/app/src/service/common/system/cron.ts`
|
||||
|
||||
**改动**:在 `startCron()` 中注册沙盒停止定时任务。
|
||||
|
||||
```typescript
|
||||
import { cronJob } from '@fastgpt/service/core/ai/sandbox/controller';
|
||||
|
||||
export const startCron = () => {
|
||||
// ...现有定时任务...
|
||||
cronJob(); // 新增:注册沙盒停止定时任务
|
||||
};
|
||||
```
|
||||
|
||||
**说明**:定时任务逻辑直接在 controller.ts 中实现,不需要单独的 cron.ts 文件。
|
||||
|
||||
---
|
||||
|
||||
### 3.10 `projects/app/src/pages/api/core/chat/history/batchDelete.ts`
|
||||
|
||||
**改动**:在会话批量删除逻辑中,追加异步沙盒清理。
|
||||
|
||||
```typescript
|
||||
import { deleteSandboxesByChatIds } from '@fastgpt/service/core/ai/sandbox/controller';
|
||||
|
||||
// 在现有删除逻辑之后,异步触发(不 await,不阻塞主流程)
|
||||
deleteSandboxesByChatIds(appId, chatIds).catch(console.error);
|
||||
```
|
||||
|
||||
**同样需要改造**:`delHistory.ts`(单个会话软删除时不触发,因为是软删除)和 `clearHistories.ts`(软删除,不触发)。只有硬删除(batchDelete)才触发沙盒清理。
|
||||
|
||||
---
|
||||
|
||||
### 3.11 `packages/service/core/app/controller.ts`
|
||||
|
||||
**改动**:在 `deleteAppDataProcessor()` 中追加沙盒清理。
|
||||
|
||||
```typescript
|
||||
import { deleteSandboxesByAppId } from '../ai/sandbox/controller';
|
||||
|
||||
export const deleteAppDataProcessor = async ({ app, teamId }) => {
|
||||
const appId = String(app._id);
|
||||
|
||||
// ...现有删除逻辑...
|
||||
|
||||
// 新增:删除该应用下所有沙盒
|
||||
await deleteSandboxesByAppId(appId);
|
||||
|
||||
await MongoApp.deleteOne({ _id: appId });
|
||||
};
|
||||
```
|
||||
|
||||
### 环境变量模板调整
|
||||
|
||||
需要调整对应的 env 文件,参考 `@fastgpt-sdk/sandbox-adapter` 需要的变量。
|
||||
|
||||
```bash
|
||||
# Sealos devbox
|
||||
AGENT_SANDBOX_PROVIDER=sealos-devbox
|
||||
AGENT_SANDBOX_SEALOS_BASEURL=
|
||||
AGENT_SANDBOX_SEALOS_TOKEN=
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、文件改动汇总
|
||||
|
||||
| 文件 | 操作 | 改动量 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| `packages/global/core/ai/sandbox/constants.ts` | 🆕 新增 | ~40 行 | 常量、类型、系统提示词 |
|
||||
| `packages/service/core/ai/sandbox/schema.ts` | 🆕 新增 | ~50 行 | MongoDB Model + 索引 |
|
||||
| `packages/service/core/ai/sandbox/controller.ts` | 🆕 新增 | ~156 行 | SandboxClient 类 + delete/stop 函数 + cronJob |
|
||||
| `packages/service/.../agent/sub/sandbox/utils.ts` | 🆕 新增 | ~35 行 | sandboxShellTool 定义(同 datasetSearchTool 模式) |
|
||||
| `projects/app/src/pages/api/core/ai/sandbox/webideUrl.ts` | 🆕 新增 | ~30 行 | Web IDE URL API |
|
||||
| `packages/global/core/workflow/constants.ts` | ✏️ 改造 | +1 行 | NodeInputKeyEnum 新增 useAgentSandbox |
|
||||
| `packages/global/.../agent/constants.ts` | ✏️ 改造 | +12 行 | SubAppIds 新增 sandboxShell + systemSubInfo 注册 |
|
||||
| `packages/service/.../agent/utils.ts` | ✏️ 改造 | +5 行 | getSubapps() 新增 useAgentSandbox 参数,注入 sandboxShellTool |
|
||||
| `packages/service/.../agent/master/call.ts` | ✏️ 改造 | +20 行 | 拦截 sandbox_shell 调用,路由到 SandboxClient.exec() |
|
||||
| `packages/service/.../agent/master/prompt.ts` | ✏️ 改造 | +5 行 | 追加沙盒 System Prompt |
|
||||
| `packages/service/.../ai/tool/index.ts` | ✏️ 改造 | +5 行 | 读取 useAgentSandbox 传递下游 |
|
||||
| `packages/service/.../ai/tool/toolCall.ts` | ✏️ 改造 | +30 行 | 注入 shell tool + 拦截调用 |
|
||||
| `projects/app/.../system/cron.ts` | ✏️ 改造 | +2 行 | 注册沙盒 cronJob |
|
||||
| `projects/app/.../chat/history/batchDelete.ts` | ✏️ 改造 | +3 行 | 异步删除沙盒 |
|
||||
| `packages/service/core/app/controller.ts` | ✏️ 改造 | +3 行 | 应用删除时清理沙盒 |
|
||||
|
||||
---
|
||||
|
||||
## 五、实现顺序
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
P1["Phase 1\n基础设施"] --> P2["Phase 2\n核心调度"] --> P3["Phase 3\n生命周期"] --> P4["Phase 4\n前端/API"]
|
||||
|
||||
P1 --- P1a["constants.ts\nschema.ts\ncontroller.ts\n(含 cronJob)"]
|
||||
P2 --- P2a["NodeInputKeyEnum\nAgent 模板\ncall.ts / prompt.ts\ntoolCall.ts"]
|
||||
P3 --- P3a["注册 cronJob\nbatchDelete.ts\napp/controller.ts"]
|
||||
P4 --- P4a["webideUrl API\n前端入口(后续)"]
|
||||
```
|
||||
|
||||
| 阶段 | 内容 | 可独立测试 |
|
||||
|------|------|-----------|
|
||||
| Phase 1(完成)| 新增 constants + schema + controller (含 cronJob) | 可集成测试 SandboxClient.exec() / stop() / delete() |
|
||||
| Phase 2 (完成)| ToolCall 节点注入 useAgentSandbox + 简易模式支持 useComputer(一个开关即可) + shell tool + 拦截调用 | 需手动运行验证 |
|
||||
| Phase 3(完成) | 注册 cronJob + 会话/应用删除时清理 | 可通过 cron 日志 + 手动删除会话验证 |
|
||||
| Phase 4 | Web IDE URL API + 前端入口 | 需要前端配合 |
|
||||
| Phase 5 | Agent 模式支持 computer | 需手动运行验证 |
|
||||
|
||||
---
|
||||
|
||||
## 六、依赖项
|
||||
|
||||
| 依赖 | 说明 | 状态 |
|
||||
|------|------|------|
|
||||
| `@fastgpt-sdk/sandbox-adapter` | SDK 包,提供 create/exec/suspend/delete/getWebIdeUrl | 需确认 API 是否就绪 |
|
||||
| i18n key | `workflow:template.use_agent_sandbox` / `workflow:template.use_computer_desc` | 需新增中英繁体翻译 |
|
||||
Reference in New Issue
Block a user