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:
Archer
2026-03-16 17:09:25 +08:00
committed by GitHub
parent 21b3f8549a
commit aaa7d17ef1
258 changed files with 6844 additions and 6162 deletions
+4
View File
@@ -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 方案 | 改为文件操作 APIlist/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` | 需新增中英繁体翻译 |