FastGPT 用户级 Sandbox 架构全解:实例身份、生命周期状态机与 Legacy Workspace 迁移实践
FastGPT 用户级 Sandbox 架构全解实例身份、生命周期状态机与 Legacy Workspace 迁移实践【免费下载链接】FastGPTFastGPT is a knowledge-based platform built on the LLMs, offers a comprehensive suite of out-of-the-box capabilities such as data processing, RAG retrieval, and visual AI workflow orchestration, letting you easily develop and deploy complex question-answering systems without the need for extensive setup or configuration.项目地址: https://gitcode.com/GitHub_Trending/fa/FastGPT本文是 FastGPT 开源仓库中 用户级 Sandbox 最终方案状态已实现作为当前分支唯一技术方案的深度解读与技术展开。它解决的是普通 App Chat 的 Sandbox 隔离边界收敛问题将原先由appId effectiveUid chatId三参数构成的沙盒归属收敛为appId effectiveUid用户级共享实例并同步定义了 v2 实例数据模型、生命周期状态机、Legacy Workspace 两阶段迁移、Provider/镜像变化时的运行时收敛、App Chat 降级以及 Workspace 直连预览等完整契约。读完本文你将掌握用户级 Sandbox 的实例寻址规则、状态转换与 Lease 并发控制原理、迁移屏障的设计思路并能把文中的关键结论对应到仓库源码入口具备阅读与二次开发该模块的能力。1. 目标与范围从 Chat 级隔离到用户级隔离普通 App Chat 的 Sandbox 隔离边界由appId effectiveUid chatId收敛为appId effectiveUid。这意味着同一 App、同一有效用户的多个 Chat 共享一个物理 Sandbox 和 WorkspacechatId只用于区分sessions/chatId下的默认工作目录不再参与实例身份。该方案同时定义用户级 Sandbox 必须依赖的最终契约v2 实例身份、数据模型和生命周期状态机App session、Skill runtime 和 Skill Edit 的路径边界Legacy Workspace 向用户级 Sandbox 的迁移与发布屏障Provider 或镜像变化时的运行时收敛Sandbox 不可用时的 App Chat 降级Workspace 文件直连预览OpenSandbox 与 Sealos Devbox 的最终生命周期差异。需要特别澄清职责边界Sandbox 不负责 Agent 模型循环、Workflow 调度或 Skill 版本创建。Agent 和 ToolCall 只在确认本轮需要且允许使用 Sandbox 后获取已经准备好的SandboxClient参见 Agent Sandbox 当前设计 的分层说明。2. 核心不变量身份、路径与生命周期2.1 实例身份业务归属统一使用sourceType/sourceId/userId三元组物理资源使用稳定sandboxId场景逻辑身份sandboxIdApp Chatapp appId effectiveUidapp-hash(appId-effectiveUid)Skill EditskillEdit skillId skillEditskilledit-hash(skillId-skillEdit)Chat Agent Helper不支持 Sandbox调用时显式报错其中 hash 取16 位小写十六进制。App 和 Skill Edit 都不把chatId放入实例 ID同时不保留旧三参数 ID、无前缀 ID 或空userId的运行时兼容分支。源码实现印证packages/global/core/ai/sandbox/constants.tsexport const generateSandboxId ({ sourceType, sourceId, userId }: { sourceType: ChatSourceTypeEnum; sourceId: string; userId: string; }): string ${sourceType.toLowerCase()}-${hashStr(${sourceId}-${userId}).slice(0, 16)};而getSandboxUserId负责把调用用户收敛为 v2 逻辑身份App 场景返回有效用户 IDSkill Edit 场景固定返回ChatSourceTypeEnum.skillEditChat Agent Helper 场景直接抛出ChatAgentHelper source does not support sandbox identitypackages/service/core/ai/sandbox/utils/id.ts。2.2 Workspace 路径App Sandbox 的路径固定拆分为三段workspaceRoot provider workDirectory runtimeSkillsRoot workspaceRoot/projects sessionWorkDirectory workspaceRoot/sessions/chatIdSandbox 工具、用户输入文件和 Sandbox Editor 默认使用sessionWorkDirectory已发布 Skill 版本部署到共享runtimeSkillsRootApp entrypoint 在workspaceRoot执行并按物理 Sandbox 记录执行状态Skill Edit 使用 Workspace 根目录不参与 App session 目录模型Session 目录是默认工作目录不是同一 Sandbox 内的硬安全边界Sandbox Editor 本轮只展示当前 session不提供切回 Workspace 根目录的 UI。对应的纯函数实现在 packages/service/core/ai/sandbox/utils/index.ts 的getSandboxRuntimePaths中App 返回workspaceRoot projects sessions/chatId三段Skill Edit 的sessionWorkDirectory直接等于workspaceRoot。其中chatId先经过getSandboxSessionPathSegment处理——常规 NanoID 保持原值异常输入.、..、超过 200 字节则回退为chat-sha256 前 40 位确保目录名稳定且不会构成路径穿越。2.3 生命周期agent_sandbox_instances_v2是新运行时的唯一实例表顶层status是唯一权威生命周期状态operation只记录 operation token、持久阶段、心跳和错误不承担第二套状态判断普通 runtime 不得连接legacyMigrating或其他过渡态实例单个 Chat 删除不删除共享 Sandbox也不单独清理sessions/chatIdApp 或 Skill 删除负责清理所属 v2 与 Legacy 资源。3. 数据模型v2 实例与 Legacy 实例3.1 v2 实例type SandboxInstance { provider: opensandbox | sealosdevbox; sandboxId: string; sourceType: app | skillEdit; sourceId: string; userId: string; status: | provisioning | legacyMigrating | running | stopping | stopped | archiving | archived | restoring | deleting; lastActiveAt: Date; createdAt: Date; limit?: SandboxLimit; storage?: SandboxStorage; teamId?: string; image?: SandboxImage; versionId?: string; operation?: { id: string; type: provision | legacyMigration | stop | archive | restore | delete; phase: string; previousStatus?: running | stopped | archived; startedAt: Date; heartbeatAt: Date; failedAt?: Date; error?: string; }; };关键约束(provider, sandboxId)唯一约束 Provider 侧物理资源记录(sourceType, sourceId, userId)唯一约束业务逻辑实例稳定态只有running/stopped/archived稳定态不得残留 operation每个过渡态必须匹配唯一 operation 类型过渡态接管按status operation.heartbeatAt查询空闲资源按status lastActiveAt查询v2 不包含通用metadata容器也不包含chatId或旧appId/type。Mongo Schema 层直接实现了过渡态必须匹配唯一 operation这一约束在 packages/service/core/ai/sandbox/infrastructure/instance/schema.ts 中expectedOperationByStatus将provisioning → provision、legacyMigrating → legacyMigration、stopping → stop、archiving → archive、restoring → restore、deleting → delete一一绑定稳定态running/stopped/archived不允许存在 operationschema 通过pre(validate)钩子与strict: throw保证非法状态组合在写入前即被拒绝。3.2 Legacy 实例旧agent_sandbox_instances使用独立 Legacy Schema只允许 migration repository、迁移预检和 Source 删除清理读取。普通 runtime、归档 cron、资源 API 和 Skill Edit不得回退查询 Legacy 表。已确认 Legacy 数据不存在 E2B 记录因此当前 Provider 和迁移范围仅包含 OpenSandbox 与 Sealos Devbox不保留 E2B adapter 或数据兼容分支。4. 运行时与文件行为prepareAgentSandboxRuntime根据标准 Chat source 生成稳定 ID并返回sandboxClient、workspaceRoot和当前workDirectorypackages/service/core/ai/sandbox/application/runtime/index.ts。完整路径只通过SandboxClient.getRuntimePaths()暴露给文件 API、IDE 和 migration避免调用方自行拼接 Provider 路径。App runtime 遵循以下 7 条规则shell 和文件工具以sessionWorkDirectory为默认目录相对路径锚定当前 session绝对路径必须位于workspaceRoot内用户输入文件写入sessionWorkDirectory/user_files写文件、运行时文件注入和 HTTP 上传在调用 ProviderwriteFiles前统一创建目标父目录Skill 包和 Skill entrypoint 使用共享runtimeSkillsRoot不进入 session 目录内置 Skill 同步到 Sandbox HOME 下的.fastgpt/skills/name不进入用户 Workspace、编辑树、导出包或发布包App entrypoint 在workspaceRoot执行同一脚本内容按 hash 幂等执行。路径规则的底层实现在resolveSandboxRuntimePathpackages/service/core/ai/sandbox/utils/index.ts相对路径以sessionWorkDirectory为基准拼接任何包含..的路径直接抛Path traversal detected绝对路径默认要求位于workspaceRoot之下allowOutsideWorkspace为 false 时未开启allowAbsolutePath时绝对路径一律拒绝。此外getSafeSandboxInputFilename会对用户上传文件名做清洗去除控制字符、剥离路径、同名加序号因为 URL query、API body 和模型上下文都可能携带不可信文件名。并发初始化控制同一 Sandbox 的 prepare 使用agent-sandbox:init:sandboxIdRedis lease 串行化。锁覆盖 session 目录准备、输入文件注入、镜像源、Skill 同步、entrypoint 和 Skill 扫描锁释放后后续 Chat 可以重新调整共享projects因此/projects不承诺在一次 Agent 执行期间保持不变。entrypoint 幂等依赖buildRuntimeHashpackages/service/core/ai/sandbox/utils/index.ts脚本内容按sha256:hex哈希记录成功状态内容不变时不重复执行Skill entrypoint 则以不可变versionId为键只执行一次未选中的版本会从执行状态中清理。5. 生命周期与并发状态机、Lease 分层、归档删除5.1 状态转换操作起始状态过渡态终态首次创建无记录provisioningrunningLegacy 导入无记录或可接管目标legacyMigratingrunning停止runningstoppingstopped归档running/stoppedarchivingarchived恢复archivedrestoringrunning删除可抢占状态deleting删除记录每次生命周期操作先通过Mongo CAS 抢占 operation再执行 Provider、volume 或 S3 副作用每个副作用完成后持久化 phase最后使用相同 operation ID提交终态。失败保留过渡态、phase 和错误由原操作重试或满足隔离窗口后的 stale recovery 接管不能直接把过渡态改回running。5.2 Lease 分层锁顺序固定为Source Mutation Lease - Sandbox Lifecycle LeaseSource Mutation Lease 串行化同一 App/Skill 的首次创建、Legacy 导入和业务删除Sandbox Lifecycle Lease 以稳定sandboxId为键跨 Provider串行化单个物理身份的生命周期Legacy migration job lease 只防止管理员重复调度不承担单条资源正确性prepare 初始化 lease 只保护运行时文件准备不替代生命周期 lease。长任务在每个远端副作用前后调用 leaseassertValid()。Provider 的 create/start/stop/delete 必须基于稳定 ID 保持幂等重复删除或 404 按成功处理。App/Skill source 在创建、恢复、迁移前必须仍然 active删除任务只处理已经持久标记删除的 source。相关实现位于 packages/service/core/ai/sandbox/application/lease.ts 与 packages/service/core/ai/sandbox/application/lifecycle/runner.ts。5.3 归档与删除v2 归档使用sandbox/archive/sandboxId/package.zipLegacy 归档继续使用agent-sandbox/legacySandboxId/package.zip不能直接改名为 v2 归档restore 发布running后保留 v2 S3 归档后续重复恢复仍以该归档作为持久备份只有业务资源删除流程才清理对应归档App 删除清理全部用户级 v2 与 Legacy Provider 资源、volume、S3 和 Mongo 记录Skill 删除清理 Skill Edit Sandbox普通编辑 Chat 删除不删除共享 Skill Edit Sandboxkeepalive、存在性检查和历史资源 stop/delete 不得通过运行时 client 意外恢复 archived 实例。6. Legacy Workspace 迁移归一化屏障与两阶段迁移6.1 beta6 前置阶段/api/admin/4160/initUserSandbox内置 beta6 Sandbox 归属归一化作为第 0 阶段不再依赖已经删除的/api/admin/4150/init4150-beta6。该阶段补齐 LegacysourceType/sourceId、清理历史appId/type/metadata.skillId字段并删除无法归属的孤儿 Sandbox 资源。随后按 beta6 原规则清理缺失sourceType的旧 Skill Debug Chat 三表记录和私有、公开 Bucket 旧 S3 前缀与 App 同 ID 的 Skill 跳过清理防止误删 App Chat。dryRuntrue时只统计不执行写入或删除。第 0 阶段结束后必须重新统计 Sandbox 待归一化记录和待清理旧 Debug Chat两者合计为pendingCount只要它不为 0整次任务就停在该阶段不得归档 Workspace、删除待迁移物理资源或创建 v2 目标。该总数归零后先执行一次 Legacy 专属整表预检再进入 Workspace 迁移该预检不复用 v2 instance schema。Legacy 预检使用独立的LegacySandboxInstanceZodSchema不得使用 v2 实例 schema 校验 Legacy 输入。Legacy metadata 可以包含providerCreatedAt、旧storage等 Skill 编辑历史字段这些字段由 Legacy schema 读取在映射到 v2 时显式丢弃。toV2SandboxFields只能按 v2 稳定根字段白名单构造结果避免新的 Legacy 字段通过对象展开spread泄漏到 v2。6.2 两阶段迁移通过归一化屏障后Workspace 迁移分为全量预归档和安装两个严格阶段第一阶段为所有未完成 Legacy 记录生成或复用 S3 归档确认归档后删除旧物理 Sandbox 和 OpenSandbox volume并提交archiveReady第一阶段继续收集全部失败只要任一记录未完成归档、校验或资源删除全局屏障就禁止第二阶段创建 migration 目标第二阶段只从 Legacy S3 下载和安装 Workspace不再连接或打包旧物理实例App 按sourceId userId聚合到一个用户级目标Skill Edit 搬到新的稳定 Skill ID目标在安装期间保持legacyMigrating legacyMigration operation普通 runtime 只能返回忙碌所有分组文件至少提交installed后先暂停目标物理 Sandbox再一次性发布目标为stopped暂停失败不得提交迁移完成发布后把 Legacy 阶段提交为completed保留旧 S3 和 Legacy Mongo 记录作为迁移备份。skipError开关管理员入口支持可选skipErrortrue用于兼容业务 source 已不存在或已软删除、但 Legacy Sandbox 记录仍残留的升级场景。该开关只跳过 source fence 返回Sandbox source is missing or deleted的整个 source 分组被跳过的记录不执行归档、资源删除、目标创建或安装并通过skippedCount/skipped单独返回对象存储、Provider、Lease、归档和安装等其他错误仍计入failedCount/failures并维持全局归档屏障。省略该参数或传false时保持原有严格行为。第一阶段释放单个 Source Lease 后正常用户请求可以先创建确定性的 v2 目标第二阶段必须接管或复用该目标并按**目标内容优先**规则合并不能覆盖已经产生的用户文件。6.3 Workspace 安装规则每条 App Legacy Workspace 安装到目标sessions/legacyChatIdstaging 中直接位于projects/下、名称为 24 位十六进制 Version ID 的运行时缓存目录不迁移目标 session 不存在时使用 staging rename原子提交目标已存在时递归合并同名文件或文件/目录类型冲突都保留目标现有内容目录存在不能推断安装成功必须以持久化installed阶段为准失败保留目标、Legacy 记录和归档重试从持久 phase 继续不重复已经确认的副作用completed是 Legacy 迁移终态后续迁移只预检、不重复安装。旧 Skill Debug Chat 清理沿用 beta6 初始化脚本扫描当前全部 Skill排除同 ID 的 App 后逐个统计 Legacy Chat列表包含空 Skill 的检查结果但正式执行只删除chatCount 0的 Skill。matchedSkillCount表示排除冲突后的扫描数量cleanedSkillCount表示实际提交删除的 Skill 数量pendingChatCount用于迁移阻塞判断。并发度控制Skill 分组并发度为20App 分组并发度为5组内按lastActiveAt从新到旧串行安装。7. 运行时配置收敛Provider 与镜像变化App Chat 和 Workflow 只在 Agent 或 ToolCall 节点确定本轮实际使用 Sandbox 后比较目标 Provider 以及目标镜像的repository tag配置一致时直接创建、恢复或连接 runtime镜像变化时通过标准 archive 状态机保存 Workspace再使用目标镜像恢复Provider 变化时先校验目标 adapter再在同一个 Lifecycle Lease 中归档旧资源随后对原记录原子切换 Provider 和镜像最后使用标准 restore 恢复活跃 operation 由当前请求等待或接管不能并发启动第二条迁移链App 迁移在本次 Workflow 内静默完成只发送upgrading - lazyInit粗粒度状态不弹窗、不重放用户请求失败按标准 Workflow 错误终止当前节点Skill Edit 继续由用户显式确认升级并在页面轮询不复用 App Chat 的静默交互。迁移过程不新增数据库状态——它复用 archive、restore 和稳定archived状态。历史记录缺少image时按镜像不一致处理。相关编排位于 packages/service/core/ai/sandbox/application/providerMigration.ts 与 packages/service/core/ai/sandbox/application/runtime/upgrade.ts。8. Sandbox 不可用时的 App Chat 降级普通 App Chat 使用三种稳定不可用原因systemDisabled系统未配置或已下架 SandboxappDisabled当前 App Agent/ToolCall 未开启 SandboxteamPlanUnavailable应用团队套餐不提供 Sandbox套餐查询失败也按该原因降级。不可用时不注入 Sandbox system prompt、Sandbox tools 或依赖 Sandbox 的 Skill不准备 runtime、不执行 entrypoint也不把关闭状态写成 Agent/ToolCall 错误其他模型、工具、知识库和 Workflow 节点继续运行。Skill Edit 和 Skill 调试仍是 Sandbox 强依赖保持结构化错误阻断。checkExist同时返回真实本地实例存在性和可选unavailableReason查询本身不能创建或恢复实例。页面加载与对话期间不主动提示只有用户点击现有虚拟机入口时刷新状态并显示统一 Toast。Ticket、上传、下载和预览 API 仍在服务端重新校验可用性不能依赖前端守卫。降级判断集中在 packages/service/core/ai/sandbox/application/availability.ts。9. Workspace 直连预览短期只读 URL 取代 S3 上传HTML 预览和sandbox_get_file_url不再把文件上传到 S3而是签发短期只读 URLpreviewProxy/preview/sandboxId/sessionId/workspaceRelativePath最终链路为FastGPT 创建 Redis preview session - agent-sandbox-proxy 校验 session 并向 FastGPT 解析 Provider endpoint - fastgpt-ide-agent:1319 在 FASTGPT_WORKDIR 内流式读取文件关键约束sandboxId必须匹配app|skilledit-16 hex随机sessionId为 24 位字母数字字符串session TTL 为2 小时每个 Sandbox 最多500个活动 sessionURL 是对应 Sandbox 整个 Workspace 的临时只读 bearer capability不只授权 URL 中单个文件session 只保存业务寻址上下文不保存 Provider endpoint 或 IDE agent 密码支持GET、HEAD、ETag 和单段 Range禁止目录列表、路径穿越和逃逸 Workspace 的软链接响应使用no-referrer、nosniff和private, no-store公开预览 origin 必须与 FastGPT App origin 隔离HTML 资源必须使用./assets/...等相对路径/assets/...根路径不保留 preview URL 前缀preview 与 Workspace 冷归档是独立能力S3 archive 流程不受影响。这也与系统提示中写给模型的要求一致——SANDBOX_SYSTEM_PROMPT明确要求HTML 等多文件预览产物必须使用相对资源路径例如./assets/app.js不要使用/assets/app.js这类根路径packages/global/core/ai/sandbox/constants.ts。FastGPT、proxy 和包含 1319 preview listener 的 runtime image 必须协调发布不支持新旧版本混合滚动兼容。相关实现见 packages/service/core/ai/sandbox/application/preview.ts 与 projects/agent-sandbox-proxy。10. Provider 与模块边界10.1 Provider 最终契约OpenSandboxstop()删除远端计算实例不调用 pause它不删除 FastGPT 管理的 volume、Mongo 记录或 S3 归档。后续使用相同业务sandboxId创建新远端实例并重新挂载原 volumeSealos Devboxstop()继续调用 pause因此公共stop()只表示执行 Provider 停止策略不承诺复用同一个远端实例OpenSandbox 已绑定 client 时通过Sandbox.kill()删除cron 的未绑定 adapter 通过SandboxManager.killSandbox()删除两条路径都等待远端消失并保持幂等close()只释放本地 transport不改变远端生命周期Provider 默认镜像、工作目录、HOME、环境变量和创建参数统一由 runtime profile 解析业务层不按 Provider 名称自行拼配置。两个 Provider 的 adapter 实现分别在 sdk/sandbox-adapter/src/adapters/opensandbox 与 sdk/sandbox-adapter/src/adapters/sealos-devboxOpenSandbox Kubernetes PVC 生命周期问题与设计的定论见 OpenSandbox Kubernetes PVC 生命周期问题与设计。10.2 模块边界Sandbox 模块保持单向依赖interface - application - infrastructure - sandbox-adapter外部生产代码只从interface/*使用稳定能力入口见 packages/service/core/ai/sandbox/interface分为 runtime、toolCall、resource、preview、admin、config、file、skillEdit、session 等子模块application 负责编排不直接访问 Mongoose Modelv2 与 Legacy Mongo 读写集中在infrastructure/instancerepositorypackages/service/core/ai/sandbox/infrastructure/instance/repository.tsLegacy migration 按service/workspace/cleanup/normalization/debugChatCleanup/types拆分阶段判断留在 application对外聚合只使用目录index.ts不保留非index.ts的兼容转发文件架构测试阻止反向依赖、外部绕过 interface 和 Sandbox 内部循环导入。11. 验证范围用户级 Sandbox 改动至少覆盖以下测试维度对应测试位于 packages/service/test/core/ai/sandbox例如 runtime/index.test.tsApp/Skill ID 稳定性、source 唯一索引和 v2 schema 状态约束Runtime Context、session/Skill 路径、父目录创建和 Editor 路径换算Source/Lifecycle/init lease、operation fencing、stale 接管和 Provider 幂等stop、archive、restore、delete、Provider/镜像迁移的副作用顺序和失败恢复beta6 待处理数屏障、全量预归档屏障、目标内容优先合并、发布屏障和幂等重试App 三种不可用原因的静默降级以及 Skill Edit 强依赖行为preview session、鉴权、TTL/限额、HTTP Range、路径穿越和软链接逃逸App/Chat/Skill 删除边界以及 v2/Legacy S3 key 隔离。12. 收敛落地情况截至文档最后核对2026-07-27收敛 TODO 已全部完成包括用户级 ID、v2 schema、共享 Workspace 与 session 默认目录生命周期状态机、operation runner、Lease 分层与 Source fenceLegacy 两阶段迁移、备份保留与 v2 归档 keyApp Provider/镜像静默迁移与 Skill Edit 显式升级交互App Chat 不可用降级与文件 API 服务端兜底Workspace 直连预览链路OpenSandbox/Sealos stop 契约与 sandbox-adapter 结构收敛模块依赖边界与公共 interface 收敛beta6 Sandbox 归一化移入initUserSandbox并在剩余待处理数归零前阻断归档restore 后保留 v2 S3 归档以及initUserSandbox的可选skipError开关。阅读建议本文是方案契约层配套的 Agent Sandbox 当前设计 是代码入口索引若需深入实现可按interface → application/runtime、application/lifecycle → infrastructure/instance、infrastructure/provider/runtimeProfile → sdk/sandbox-adapter的依赖方向逐层阅读与上述测试目录中的用例对照即可完整掌握 FastGPT 用户级 Sandbox 的运行机理。【免费下载链接】FastGPTFastGPT is a knowledge-based platform built on the LLMs, offers a comprehensive suite of out-of-the-box capabilities such as data processing, RAG retrieval, and visual AI workflow orchestration, letting you easily develop and deploy complex question-answering systems without the need for extensive setup or configuration.项目地址: https://gitcode.com/GitHub_Trending/fa/FastGPT创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考