进阶 mastra.ai 2026-10-12 10:15:56 · 26 阅读
第14章 沙箱(Sandboxes)
沙箱与文件系统
沙箱为你的 agent 提供一个隔离环境,可以在其中运行命令、执行代码、安装依赖、管理进程。有了它,agent 就能完成那些直接在应用内运行会有风险、消耗资源或不太现实的任务。
沙箱通常是临时性的,环境停止后,其中创建的文件可能就没了。文件系统则给 agent 提供了一个可读、可写、可搜索的地方,文件的生命周期可以超过沙箱。你可以用它跨多次运行保留输出,用已有文件初始化新沙箱,或者让 agent 在工作时能够检索文档。没有沙箱也能使用文件系统,比如 agent 只需要一个知识库,或访问 Google Drive 这类服务中的文件。一个实际案例是 Mastra Factory,它用沙箱为 coding-agent 会话提供仓库检出、依赖安装和命令执行的环境。
何时使用沙箱
当 agent 需要以下操作时,就使用沙箱:
克隆仓库,并在独立环境中运行 shell、Git、构建或测试等流程。
借助专门的库处理 PDF、演示文稿等文件,并生成新的产物。
在相互隔离的文件和进程环境中运行长时间或并行的任务。
如果 agent 只需要读取、写入或搜索文件,直接配置文件系统访问即可。
快速上手
给 agent 配一个本地沙箱:
src/mastra/agents/coding-agent.tsimport { Agent } from '@mastra/core/agent'
import { LocalSandbox, Workspace } from '@mastra/core/workspace'
export const codingAgent = new Agent({
id: 'coding-agent',
name: 'Coding agent',
instructions: 'Use the sandbox to complete coding tasks.',
model: 'openai/gpt-5.6-sol',
workspace: new Workspace({
sandbox: new LocalSandbox({
workingDirectory: './workspace',
}),
}),
})
await codingAgent.generate('List the files in the sandbox directory')
throw new Error('This tool requires a workspace execution context')
}
const sandbox = await ctx.workspace.resolveSandbox({
requestContext: ctx.requestContext,
})
if (!sandbox?.processes) {
throw new Error('The configured sandbox does not support background processes')
}
const server = await sandbox.processes.spawn(command)
return { pid: server.pid }
},
})
Sandboxes(沙箱)直接链接到 Sandboxes LocalSandbox直接链接到 localsandbox LocalSandbox 会在与 Mastra 应用相同的机器上执行命令。默认情况下,命令直接在宿主机上以应用进程的权限运行。 const sandbox = new LocalSandbox({
workingDirectory: './workspace',
isolation: 'seatbelt', // Use 'bwrap' on Linux
nativeSandbox: {
allowNetwork: false,
readOnlyPaths: ['./reference-data'],
},
})
启用原生隔离,在操作系统层面限制文件系统和网络访问。macOS 上使用 Seatbelt(sandbox-exec),Linux 上使用 Bubblewrap(bwrap)。可以通过 LocalSandbox.detectIsolation() 检查当前系统是否支持 Seatbelt 或 Bubblewrap。 nativeSandbox 选项用于控制网络访问、只读或可写路径、工作目录的写入权限以及系统二进制文件。你还可以提供自定义的 Seatbelt profile 或 Bubblewrap 参数。完整配置参见 LocalSandbox。 远程沙箱(Remote sandboxes) 当命令需要与宿主应用之间有更强的隔离边界,或工作负载需要扩展到应用服务器资源之外时,可以使用远程或容器沙箱。每种沙箱都有各自的隔离、持久化、网络和挂载行为: AgentCoreApple ContainerBlaxelCloudflare SandboxDaytonaDockerE2BE2B DesktopMastraModalRailwayFly.io SpritesVercel 如果 Mastra 不支持你的沙箱提供商,可以实现沙箱提供商接口来接入。 文件系统(Filesystem) 每个沙箱都有供命令使用的文件系统。LocalSandbox 使用宿主机文件系统,而远程沙箱的文件系统是隔离的,且通常是临时的。需要长期保留的文件应使用外部存储。 Mastra 文件系统将 agent 和沙箱连接到基于提供商的存储服务,例如 Amazon S3、Google Cloud Storage 或 Google Drive。配置后,agent 将获得读写和搜索文件的工具。当远程沙箱和提供商都支持挂载时,Mastra 会通过 Filesystem in Userspace(FUSE)自动挂载存储。命令使用普通文件路径,而由提供商在沙箱之外保存变更。 提供商、文件工具和挂载的详情参见 Filesystem。 计算机操作(Computer use) 受支持的桌面沙箱允许 agent 控制截屏、鼠标和键盘。支持的沙箱及配置参见 Computer。 按用户或线程划分沙箱 当每个用户、租户或线程需要独立的沙箱时,可以使用 resolver。将 sandboxCacheKey 设为沙箱所有者的身份标识,这样后续请求就能复用同一个运行中的环境。 下面的示例为每个用户创建并缓存一个 Daytona 沙箱: src/mastra/workspaces.tsimport { Workspace } from '@mastra/core/workspace'
import { DaytonaSandbox } from '@mastra/daytona'
const workspace = new Workspace({
sandbox: async ({ requestContext }) => {
const userId = requestContext.get('user-id') as string
const sandbox = new DaytonaSandbox({ id: `user-${userId}` })
await sandbox.start()
return sandbox
},
sandboxCacheKey: ({ requestContext }) => requestContext.get('user-id') as string,
})
用户发起的第一个请求会创建 sandbox,之后相同用户 ID 的请求会复用同一个实例。 如果 sandbox 需要持久化存储,可以创建一个 filesystem 并在 resolver 里挂载。下面的例子为每条 memory thread 创建一个 Daytona sandbox 和一个 S3 存储前缀,然后把存储挂载到 /workspace: src/mastra/workspaces.tsimport { MASTRA_THREAD_ID_KEY, type RequestContext } from '@mastra/core/request-context'
import { Workspace } from '@mastra/core/workspace'
import { DaytonaSandbox } from '@mastra/daytona'
import { S3Filesystem } from '@mastra/s3'
const getThreadId = (requestContext: RequestContext) =>
requestContext.get(MASTRA_THREAD_ID_KEY) as string
const createThreadFilesystem = (threadId: string) =>
new S3Filesystem({
bucket: process.env.S3_BUCKET!,
region: process.env.S3_REGION!,
prefix: `threads/${threadId}`,
})
const workspace = new Workspace({
filesystem: ({ requestContext }) => createThreadFilesystem(getThreadId(requestContext)),
sandbox: async ({ requestContext }) => {
const threadId = getThreadId(requestContext)
const sandbox = new DaytonaSandbox({
id: `thread-${threadId}`,
language: 'typescript',
})
await sandbox.start()
await sandbox.mount(createThreadFilesystem(threadId), '/workspace')
return sandbox
},
sandboxCacheKey: ({ requestContext }) => getThreadId(requestContext),
})
同一线程中的第一个请求会运行解析器并启动 sandbox。后续使用相同 thread ID 的请求会复用缓存的 sandbox。不同的 thread ID 则会创建不同的 sandbox 和存储前缀。 示例中在解析器内部挂载了文件系统,因为静态挂载无法与 sandbox 解析器组合使用。生成的工具会自动从请求上下文中解析文件系统和 sandbox。 warning 解析器返回的 sandbox 由你的应用负责管理。当用户、线程或会话结束时,请销毁它们并调用 workspace.clearSandboxCache(cacheKey)。workspace.destroy() 不会销毁解析器返回的 sandbox。 工具可用性 使用静态 sandbox 时,Mastra 知道后端支持哪些能力,只会给 agent 提供对应的工具。应用代码在使用可选能力之前,也应先检查其是否可用。 由于基于解析器的 sandbox 要到请求时才能确定,Mastra 最初会暴露所有 sandbox 工具。如果调用了已解析后端不支持的工具,会抛出 SandboxFeatureNotSupportedError。 后台进程 当 agent 通过 execute_command 启动后台进程时,你可以收到 stdout、stderr 和进程退出的生命周期回调。通过其 WORKSPACE_TOOLS.SANDBOX.EXECUTE_COMMAND 条目中的 backgroundProcesses 选项进行配置: src/mastra/workspaces.tsimport { Workspace, LocalSandbox, WORKSPACE_TOOLS } from '@mastra/core/workspace'
const workspace = new Workspace({
sandbox: new LocalSandbox({ workingDirectory: './workspace' }),
tools: {
[WORKSPACE_TOOLS.SANDBOX.EXECUTE_COMMAND]: {
backgroundProcesses: {
onStdout: (data, { pid }) => console.log(`[${pid}] ${data}`),
onStderr: (data, { pid }) => console.error(`[${pid}] ${data}`),
onExit: ({ pid, exitCode }) => console.log(`Process ${pid} exited: ${exitCode}`),
},
},
},
})
这些回调会在 agent 通过 execute_command 启动的所有后台进程上触发。 默认情况下,后台进程继承 agent 的中断信号,agent 断开连接时随之停止。可以给 abortSignal 传入自定义信号;如果希望进程在请求结束后继续运行,则传 null 或 false。 完整的 SandboxProcessManager API(包括以编程方式启动进程、读取输出和写入标准输入)参见 SandboxProcessManager 参考文档。 生命周期与持久化 谁共享同一个沙箱,取决于你在哪一层配置,以及是否使用了 resolver: | 配置方式 | 沙箱共享范围 | | --- | --- | | Mastra 层配置 | 从 Mastra 实例继承配置的 agent 共用同一个沙箱 | | Agent 层配置 | 该 agent 实例处理的所有请求共用同一个沙箱 | | 按资源隔离的 resolver | resolver 为每个资源 ID 缓存一个沙箱 | | 按线程隔离的 resolver | 同一 memory thread 中的多个请求持续复用同一个沙箱 | 静态沙箱不会自动绑定到当前资源或 memory thread。如果需要按资源或线程隔离,请使用 resolver,并把 sandboxCacheKey 设置为对应的 ID,详见“按用户或线程划分沙箱”。 启动 静态沙箱环境通常在第一条命令执行时才启动,因此大多数应用无需显式初始化。只有在希望静态沙箱在应用启动阶段就启动并挂载好 mounts 时,才需要在启动时调用 workspace.init()。这样凭据、网络或 provider 相关的错误会在启动时暴露出来,而不是等到第一条命令执行时才报错。 workspace.init() 不会启动 resolver 管理的沙箱,因为在请求真正运行之前并不存在选定的沙箱。此时 resolver 必须返回一个已经启动、或者能在首次使用时自行启动的沙箱。 Hooks 可以通过 onStart、onStop 和 onDestroy 在生命周期切换时执行应用代码。这些 hooks 会拿到当前沙箱实例: const sandbox = new LocalSandbox({
workingDirectory: './workspace',
onStart: ({ sandbox }) => console.log(`Started ${sandbox.id}`),
onStop: ({ sandbox }) => console.log(`Stopping ${sandbox.id}`),
onDestroy: ({ sandbox }) => console.log(`Destroying ${sandbox.id}`),
})
onStart 在沙箱启动后运行。onStop 和 onDestroy 在对应操作执行前运行。 清理 直接配置沙箱(而不是通过 resolver)时,Mastra 拥有它的所有权。mastra dev 和生成的 Mastra server 会处理关闭信号并自动销毁已注册的资源。如果你把 Mastra 嵌入到自定义 server 或进程中,需要在它的关闭钩子里调用 mastra.shutdown()。独立使用时,直接调用 workspace.destroy() 即可。 stop() 和 destroy() 的具体行为由后端决定。比如 stop 可能只关闭计算资源、保留可重启的环境,而 destroy 可能会彻底删除环境及其临时文件。依赖这些行为前,先查阅所选后端的文档。 resolver 返回的沙箱归你的应用所有。workspace.destroy() 和 mastra.shutdown() 只会清除缓存引用,不会销毁已解析的沙箱。应用的生命周期代码需要自行跟踪这些沙箱,在其用户、线程或会话结束时调用 destroy(),然后再调用 workspace.clearSandboxCache(cacheKey)。这样可以避免闲置的计算资源持续运行,也避免后续请求复用过期的沙箱。 持久化 持久化行为因后端而异。沙箱停止或应用重启后,文件和进程不保证仍然存在,空闲超时也可能将它们清除。有些后端支持通过沙箱 ID 重连,或保留快照和卷;有些则会创建全新的环境。 每个沙箱都有 id,但这个 ID 并不保证跨后端的持久化。依赖重连或持久化状态之前,先阅读所选后端的参考文档。 网络与凭证安全 Mastra 没有为所有沙箱后端定义统一的网络策略。各后端的默认行为和支持的控制方式不同,因此在运行不受信任的代码前,先查阅所选后端的文档。对于敏感或生产环境的工作负载,建议阻止出站访问,或只放行任务所需的目标地址。应在创建沙箱时就应用该策略,确保在命令执行前生效。 如果 agent 在远程沙箱里启动了一个 web server,这个 server 最初只监听沙箱内部的端口。支持端口暴露的后端会实现 sandbox.networking.getPortUrl(),返回一个 URL,让你可以在沙箱外部预览或测试该 server: const url = await sandbox.networking?.getPortUrl(8000)
根据后端的访问控制策略,URL 可能会让其他人也能访问到该服务。这种入站暴露与出站的沙箱访问是两回事。带原生隔离的 LocalSandbox 默认禁止网络访问,除非启用 allowNetwork;没有原生隔离时,本地命令直接使用宿主机网络。 以 DaytonaSandbox 为例,默认允许出站访问,除非你主动配置限制。如果任务只需要访问特定服务,可以设置 networkBlockAll 并配一个域名白名单: import { DaytonaSandbox } from '@mastra/daytona'
const sandbox = new DaytonaSandbox({
networkBlockAll: true,
domainAllowList: 'registry.npmjs.org,api.github.com',
})
Daytona 在创建沙箱时会应用这些设置,还支持通过 networkAllowList 配置 CIDR 规则。完整配置细节见 Daytona 集成页面。 传入沙箱或命令的环境变量,对其中运行的代码都是可见的。Mastra 没有提供一个后端无关的凭证代理,无法让密钥留在沙箱之外、同时为沙箱内的请求授权。因此只给命令它真正需要的凭证,并按任务范围收窄权限,遵循所选后端的密钥管理最佳实践。 比如,只把只读 token 传给需要它的那条命令,而不是为沙箱里的所有命令都配置: await sandbox.executeCommand('node', ['scripts/download-reports.js'], {
env: {
REPORTS_READ_TOKEN: process.env.REPORTS_READ_TOKEN,
},
})
相关内容Direct link to 相关内容 SandboxProcessManager 参考 Sandbox provider 接口 文件系统 Mastra Factory
import { LocalSandbox, Workspace } from '@mastra/core/workspace'
export const codingAgent = new Agent({
id: 'coding-agent',
name: 'Coding agent',
instructions: 'Use the sandbox to complete coding tasks.',
model: 'openai/gpt-5.6-sol',
workspace: new Workspace({
sandbox: new LocalSandbox({
workingDirectory: './workspace',
}),
}),
})
await codingAgent.generate('List the files in the sandbox directory')
第14章 Sandboxes
默认情况下,LocalSandbox 直接在应用主机上运行命令,既没有隔离也不安全。运行不可信代码时,请启用原生隔离,或改用远程或容器沙箱。
静态沙箱会被所有请求和内存线程(使用该 agent 的)共享。如果每个用户或线程都需要独立的环境,请使用 resolver。共享与清理的细节参见 Lifecycle and persistence。
使用沙箱
agent 会获得与沙箱后端所支持能力对应的工具:
| 工具 | 说明 |
|---|---|
| execute_command | 运行 shell 命令,返回 stdout、stderr 和退出码。当后端支持启动长时间运行的进程时,支持 background: true。 |
| get_process_output | 获取后台进程的 stdout、stderr 和状态。支持 tail 限制输出、wait: true 等待进程退出。该工具和 kill_process 只在后端支持后台进程时才会添加。 |
| kill_process | 停止后台进程并返回其最近的输出。 |
配置的能力决定了哪些工具可用。随后可以通过工具配置来禁用、重命名这些工具,或为其添加策略:
import { LocalSandbox, Workspace, WORKSPACE_TOOLS } from '@mastra/core/workspace'
const workspace = new Workspace({
sandbox: new LocalSandbox({ workingDirectory: './workspace' }),
tools: {
requireApproval: true,
[WORKSPACE_TOOLS.SANDBOX.EXECUTE_COMMAND]: {
enabled: false,
},
},
})
给单个工具设置 { enabled: false } 可以移除该工具;设置顶层的 enabled: false 则默认禁用所有生成的工具,单个工具再设置 { enabled: true } 可覆盖该全局配置。顶层 requireApproval 策略默认对所有生成的工具生效,除非某个工具单独覆盖。
所有生成的工具参见 sandbox tools reference,审批、输出限制和 hooks 参见 tool configuration reference。
工具、工作流步骤等自定义运行时函数,可以从执行上下文中获取当前的沙箱实例,用它来执行命令、安装依赖、处理文件或启动长时间运行的进程。
src/mastra/tools/start-server.tsimport { createTool } from '@mastra/core/tools'
import { z } from 'zod'
export const startServerTool = createTool({
id: 'start-server',
description: 'Starts a server in the sandbox',
inputSchema: z.object({
command: z.string(),
}),
execute: async ({ command }, ctx) => {
if (!ctx.workspace || !ctx.requestContext) {throw new Error('This tool requires a workspace execution context')
}
const sandbox = await ctx.workspace.resolveSandbox({
requestContext: ctx.requestContext,
})
if (!sandbox?.processes) {
throw new Error('The configured sandbox does not support background processes')
}
const server = await sandbox.processes.spawn(command)
return { pid: server.pid }
},
})
Sandboxes(沙箱)直接链接到 Sandboxes LocalSandbox直接链接到 localsandbox LocalSandbox 会在与 Mastra 应用相同的机器上执行命令。默认情况下,命令直接在宿主机上以应用进程的权限运行。 const sandbox = new LocalSandbox({
workingDirectory: './workspace',
isolation: 'seatbelt', // Use 'bwrap' on Linux
nativeSandbox: {
allowNetwork: false,
readOnlyPaths: ['./reference-data'],
},
})
启用原生隔离,在操作系统层面限制文件系统和网络访问。macOS 上使用 Seatbelt(sandbox-exec),Linux 上使用 Bubblewrap(bwrap)。可以通过 LocalSandbox.detectIsolation() 检查当前系统是否支持 Seatbelt 或 Bubblewrap。 nativeSandbox 选项用于控制网络访问、只读或可写路径、工作目录的写入权限以及系统二进制文件。你还可以提供自定义的 Seatbelt profile 或 Bubblewrap 参数。完整配置参见 LocalSandbox。 远程沙箱(Remote sandboxes) 当命令需要与宿主应用之间有更强的隔离边界,或工作负载需要扩展到应用服务器资源之外时,可以使用远程或容器沙箱。每种沙箱都有各自的隔离、持久化、网络和挂载行为: AgentCoreApple ContainerBlaxelCloudflare SandboxDaytonaDockerE2BE2B DesktopMastraModalRailwayFly.io SpritesVercel 如果 Mastra 不支持你的沙箱提供商,可以实现沙箱提供商接口来接入。 文件系统(Filesystem) 每个沙箱都有供命令使用的文件系统。LocalSandbox 使用宿主机文件系统,而远程沙箱的文件系统是隔离的,且通常是临时的。需要长期保留的文件应使用外部存储。 Mastra 文件系统将 agent 和沙箱连接到基于提供商的存储服务,例如 Amazon S3、Google Cloud Storage 或 Google Drive。配置后,agent 将获得读写和搜索文件的工具。当远程沙箱和提供商都支持挂载时,Mastra 会通过 Filesystem in Userspace(FUSE)自动挂载存储。命令使用普通文件路径,而由提供商在沙箱之外保存变更。 提供商、文件工具和挂载的详情参见 Filesystem。 计算机操作(Computer use) 受支持的桌面沙箱允许 agent 控制截屏、鼠标和键盘。支持的沙箱及配置参见 Computer。 按用户或线程划分沙箱 当每个用户、租户或线程需要独立的沙箱时,可以使用 resolver。将 sandboxCacheKey 设为沙箱所有者的身份标识,这样后续请求就能复用同一个运行中的环境。 下面的示例为每个用户创建并缓存一个 Daytona 沙箱: src/mastra/workspaces.tsimport { Workspace } from '@mastra/core/workspace'
import { DaytonaSandbox } from '@mastra/daytona'
const workspace = new Workspace({
sandbox: async ({ requestContext }) => {
const userId = requestContext.get('user-id') as string
const sandbox = new DaytonaSandbox({ id: `user-${userId}` })
await sandbox.start()
return sandbox
},
sandboxCacheKey: ({ requestContext }) => requestContext.get('user-id') as string,
})
用户发起的第一个请求会创建 sandbox,之后相同用户 ID 的请求会复用同一个实例。 如果 sandbox 需要持久化存储,可以创建一个 filesystem 并在 resolver 里挂载。下面的例子为每条 memory thread 创建一个 Daytona sandbox 和一个 S3 存储前缀,然后把存储挂载到 /workspace: src/mastra/workspaces.tsimport { MASTRA_THREAD_ID_KEY, type RequestContext } from '@mastra/core/request-context'
import { Workspace } from '@mastra/core/workspace'
import { DaytonaSandbox } from '@mastra/daytona'
import { S3Filesystem } from '@mastra/s3'
const getThreadId = (requestContext: RequestContext) =>
requestContext.get(MASTRA_THREAD_ID_KEY) as string
const createThreadFilesystem = (threadId: string) =>
new S3Filesystem({
bucket: process.env.S3_BUCKET!,
region: process.env.S3_REGION!,
prefix: `threads/${threadId}`,
})
const workspace = new Workspace({
filesystem: ({ requestContext }) => createThreadFilesystem(getThreadId(requestContext)),
sandbox: async ({ requestContext }) => {
const threadId = getThreadId(requestContext)
const sandbox = new DaytonaSandbox({
id: `thread-${threadId}`,
language: 'typescript',
})
await sandbox.start()
await sandbox.mount(createThreadFilesystem(threadId), '/workspace')
return sandbox
},
sandboxCacheKey: ({ requestContext }) => getThreadId(requestContext),
})
同一线程中的第一个请求会运行解析器并启动 sandbox。后续使用相同 thread ID 的请求会复用缓存的 sandbox。不同的 thread ID 则会创建不同的 sandbox 和存储前缀。 示例中在解析器内部挂载了文件系统,因为静态挂载无法与 sandbox 解析器组合使用。生成的工具会自动从请求上下文中解析文件系统和 sandbox。 warning 解析器返回的 sandbox 由你的应用负责管理。当用户、线程或会话结束时,请销毁它们并调用 workspace.clearSandboxCache(cacheKey)。workspace.destroy() 不会销毁解析器返回的 sandbox。 工具可用性 使用静态 sandbox 时,Mastra 知道后端支持哪些能力,只会给 agent 提供对应的工具。应用代码在使用可选能力之前,也应先检查其是否可用。 由于基于解析器的 sandbox 要到请求时才能确定,Mastra 最初会暴露所有 sandbox 工具。如果调用了已解析后端不支持的工具,会抛出 SandboxFeatureNotSupportedError。 后台进程 当 agent 通过 execute_command 启动后台进程时,你可以收到 stdout、stderr 和进程退出的生命周期回调。通过其 WORKSPACE_TOOLS.SANDBOX.EXECUTE_COMMAND 条目中的 backgroundProcesses 选项进行配置: src/mastra/workspaces.tsimport { Workspace, LocalSandbox, WORKSPACE_TOOLS } from '@mastra/core/workspace'
const workspace = new Workspace({
sandbox: new LocalSandbox({ workingDirectory: './workspace' }),
tools: {
[WORKSPACE_TOOLS.SANDBOX.EXECUTE_COMMAND]: {
backgroundProcesses: {
onStdout: (data, { pid }) => console.log(`[${pid}] ${data}`),
onStderr: (data, { pid }) => console.error(`[${pid}] ${data}`),
onExit: ({ pid, exitCode }) => console.log(`Process ${pid} exited: ${exitCode}`),
},
},
},
})
这些回调会在 agent 通过 execute_command 启动的所有后台进程上触发。 默认情况下,后台进程继承 agent 的中断信号,agent 断开连接时随之停止。可以给 abortSignal 传入自定义信号;如果希望进程在请求结束后继续运行,则传 null 或 false。 完整的 SandboxProcessManager API(包括以编程方式启动进程、读取输出和写入标准输入)参见 SandboxProcessManager 参考文档。 生命周期与持久化 谁共享同一个沙箱,取决于你在哪一层配置,以及是否使用了 resolver: | 配置方式 | 沙箱共享范围 | | --- | --- | | Mastra 层配置 | 从 Mastra 实例继承配置的 agent 共用同一个沙箱 | | Agent 层配置 | 该 agent 实例处理的所有请求共用同一个沙箱 | | 按资源隔离的 resolver | resolver 为每个资源 ID 缓存一个沙箱 | | 按线程隔离的 resolver | 同一 memory thread 中的多个请求持续复用同一个沙箱 | 静态沙箱不会自动绑定到当前资源或 memory thread。如果需要按资源或线程隔离,请使用 resolver,并把 sandboxCacheKey 设置为对应的 ID,详见“按用户或线程划分沙箱”。 启动 静态沙箱环境通常在第一条命令执行时才启动,因此大多数应用无需显式初始化。只有在希望静态沙箱在应用启动阶段就启动并挂载好 mounts 时,才需要在启动时调用 workspace.init()。这样凭据、网络或 provider 相关的错误会在启动时暴露出来,而不是等到第一条命令执行时才报错。 workspace.init() 不会启动 resolver 管理的沙箱,因为在请求真正运行之前并不存在选定的沙箱。此时 resolver 必须返回一个已经启动、或者能在首次使用时自行启动的沙箱。 Hooks 可以通过 onStart、onStop 和 onDestroy 在生命周期切换时执行应用代码。这些 hooks 会拿到当前沙箱实例: const sandbox = new LocalSandbox({
workingDirectory: './workspace',
onStart: ({ sandbox }) => console.log(`Started ${sandbox.id}`),
onStop: ({ sandbox }) => console.log(`Stopping ${sandbox.id}`),
onDestroy: ({ sandbox }) => console.log(`Destroying ${sandbox.id}`),
})
onStart 在沙箱启动后运行。onStop 和 onDestroy 在对应操作执行前运行。 清理 直接配置沙箱(而不是通过 resolver)时,Mastra 拥有它的所有权。mastra dev 和生成的 Mastra server 会处理关闭信号并自动销毁已注册的资源。如果你把 Mastra 嵌入到自定义 server 或进程中,需要在它的关闭钩子里调用 mastra.shutdown()。独立使用时,直接调用 workspace.destroy() 即可。 stop() 和 destroy() 的具体行为由后端决定。比如 stop 可能只关闭计算资源、保留可重启的环境,而 destroy 可能会彻底删除环境及其临时文件。依赖这些行为前,先查阅所选后端的文档。 resolver 返回的沙箱归你的应用所有。workspace.destroy() 和 mastra.shutdown() 只会清除缓存引用,不会销毁已解析的沙箱。应用的生命周期代码需要自行跟踪这些沙箱,在其用户、线程或会话结束时调用 destroy(),然后再调用 workspace.clearSandboxCache(cacheKey)。这样可以避免闲置的计算资源持续运行,也避免后续请求复用过期的沙箱。 持久化 持久化行为因后端而异。沙箱停止或应用重启后,文件和进程不保证仍然存在,空闲超时也可能将它们清除。有些后端支持通过沙箱 ID 重连,或保留快照和卷;有些则会创建全新的环境。 每个沙箱都有 id,但这个 ID 并不保证跨后端的持久化。依赖重连或持久化状态之前,先阅读所选后端的参考文档。 网络与凭证安全 Mastra 没有为所有沙箱后端定义统一的网络策略。各后端的默认行为和支持的控制方式不同,因此在运行不受信任的代码前,先查阅所选后端的文档。对于敏感或生产环境的工作负载,建议阻止出站访问,或只放行任务所需的目标地址。应在创建沙箱时就应用该策略,确保在命令执行前生效。 如果 agent 在远程沙箱里启动了一个 web server,这个 server 最初只监听沙箱内部的端口。支持端口暴露的后端会实现 sandbox.networking.getPortUrl(),返回一个 URL,让你可以在沙箱外部预览或测试该 server: const url = await sandbox.networking?.getPortUrl(8000)
根据后端的访问控制策略,URL 可能会让其他人也能访问到该服务。这种入站暴露与出站的沙箱访问是两回事。带原生隔离的 LocalSandbox 默认禁止网络访问,除非启用 allowNetwork;没有原生隔离时,本地命令直接使用宿主机网络。 以 DaytonaSandbox 为例,默认允许出站访问,除非你主动配置限制。如果任务只需要访问特定服务,可以设置 networkBlockAll 并配一个域名白名单: import { DaytonaSandbox } from '@mastra/daytona'
const sandbox = new DaytonaSandbox({
networkBlockAll: true,
domainAllowList: 'registry.npmjs.org,api.github.com',
})
Daytona 在创建沙箱时会应用这些设置,还支持通过 networkAllowList 配置 CIDR 规则。完整配置细节见 Daytona 集成页面。 传入沙箱或命令的环境变量,对其中运行的代码都是可见的。Mastra 没有提供一个后端无关的凭证代理,无法让密钥留在沙箱之外、同时为沙箱内的请求授权。因此只给命令它真正需要的凭证,并按任务范围收窄权限,遵循所选后端的密钥管理最佳实践。 比如,只把只读 token 传给需要它的那条命令,而不是为沙箱里的所有命令都配置: await sandbox.executeCommand('node', ['scripts/download-reports.js'], {
env: {
REPORTS_READ_TOKEN: process.env.REPORTS_READ_TOKEN,
},
})
相关内容Direct link to 相关内容 SandboxProcessManager 参考 Sandbox provider 接口 文件系统 Mastra Factory