关于 blaxel-sdk
Blaxel SDK 指南面向构建 AI Agent 和云端开发环境的开发者,介绍 TypeScript 与 Python SDK 及 `bl` 命令行。内容重点是创建沙箱、运行进程、生成预览 URL,并部署可复用模板和服务。
主要功能
- 01TypeScript 和 Python SDK
- 02创建并管理云端沙箱
- 03写入文件并运行进程
- 04生成公开或私有预览 URL
- 05部署沙箱模板、Agent 和任务
适用场景
- 01在隔离沙箱中运行开发服务器
- 02为应用创建实时预览链接
- 03复用预装依赖的沙箱环境
- 04部署 AI Agent、MCP 服务和批处理任务
MCP 相关
Blaxel 可将 MCP 服务部署为无服务器端点。指南也说明了通过 CLI 和 SDK 创建、部署及管理相关资源。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ blaxel-ai/ agent-skills --skill blaxel-sdk安装后在 Claude Code 中输入/blaxel-sdk确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
metadata.jsonassets/blaxel-logo.svgreferences/sdk-python.mdsdk-typescript.md详细介绍
Blaxel 技能参考
什么是 Blaxel
Blaxel(https://blaxel.ai)是一个云平台,为 AI Agent 提供专属计算环境。其旗舰产品是永久沙箱:可即时启动的微型虚拟机,可在 25ms 内从待机状态恢复,并在闲置几秒后缩减至零。
你主要可以使用 Blaxel 来:
- 启动沙箱、安装依赖项、运行开发服务器,并公开实时预览 URL
- 构建并部署沙箱模板(自定义 Docker 镜像),以复用环境
- 将 AI Agent、MCP 服务和批处理作业部署为无服务器端点
SDK:TypeScript(@blaxel/core)和 Python(blaxel)
CLI:bl(从 https://docs.blaxel.ai/cli-reference/introduction 安装)
文档:https://docs.blaxel.ai
身份验证
SDK 按以下优先顺序使用这些来源进行身份验证:
- 已登录的 Blaxel CLI
.env文件中的环境变量(BL_WORKSPACE、BL_API_KEY)- 系统环境变量
- Blaxel 配置文件(
~/.blaxel/config.yaml)
在本地登录(推荐用于开发):
bl login YOUR-WORKSPACE
也可以设置环境变量(用于远程/CI 环境):
export BL_WORKSPACE=your-workspace
export BL_API_KEY=your-api-key
在 Blaxel 上运行时,会自动进行身份验证。
沙箱工作流(主要用例)
这是最常见的工作流:创建沙箱,在其中运行命令,并获取预览 URL。
第 1 步:创建沙箱
使用 Blaxel Hub 中的公共镜像(https://github.com/blaxel-ai/sandbox/tree/main/hub):
blaxel/base-image:latest— 精简版 Linuxblaxel/node:latest— Node.jsblaxel/nextjs:latest— Next.jsblaxel/vite:latest— Viteblaxel/expo:latest— Expo(React Native)blaxel/py-app:latest— Python
也可以使用你自行部署的自定义模板镜像。
创建时声明所需的端口。创建后无法添加端口。端口 80、443 和 8080 为保留端口。
import { SandboxInstance } from "@blaxel/core";
const sandbox = await SandboxInstance.createIfNotExists({
name: "my-sandbox",
image: "blaxel/base-image:latest",
memory: 4096,
ports: [{ target: 3000, protocol: "HTTP" }],
});
from blaxel.core import SandboxInstance
sandbox = await SandboxInstance.create_if_not_exists({
"name": "my-sandbox",
"image": "blaxel/base-image:latest",
"memory": 4096,
"ports": [{"target": 3000, "protocol": "HTTP"}],
})
使用 createIfNotExists / create_if_not_exists 按名称复用现有沙箱,或创建新沙箱。
始终优先使用此方法,而不是先获取再创建的备用方案。已删除的沙箱会以 TERMINATED 状态保留几分钟(以便继续访问其日志),因此普通的 get 可能返回一个已失效的沙箱,其网关和预览 URL 会因找不到工作负载而失败。createIfNotExists 会为你检查状态,并在现有记录处于 FAILED、TERMINATED、TERMINATING 或 DELETING 状态时重新创建沙箱。从 @blaxel/core 0.2.88 / blaxel 0.2.55 起,该方法还会等待删除后短暂的 DELETING 窗口结束,因此删除后重新创建可以直接完成。在较旧的 SDK 版本中,该窗口可能会导致“Unable to create sandbox after 3 attempts”错误——请稍等片刻后重试,或升级。如果必须使用 get,请在复用实例前检查 sandbox.status。
第 2 步:写入文件并运行命令
// Write files
await sandbox.fs.write("/app/package.json", JSON.stringify({
name: "my-app",
scripts: { dev: "astro dev --host 0.0.0.0 --port 3000" },
dependencies: { "astro": "latest" }
}));
// Or write multiple files at once
await sandbox.fs.writeTree([
{ path: "src/pages/index.astro", content: "<h1>Hello</h1>" },
{ path: "astro.config.mjs", content: "import { defineConfig } from 'astro/config';\nexport default defineConfig({});" },
], "/app");
// Execute a command and wait for it to finish
const install = await sandbox.process.exec({
name: "install",
command: "npm install",
workingDir: "/app",
waitForCompletion: true,
timeout: 60, // seconds (default 600; 0 = no auto-kill with keepAlive)
});
// Start a long-running dev server (don't wait for completion)
const devServer = await sandbox.process.exec({
name: "dev-server",
command: "npm run dev",
workingDir: "/app",
waitForPorts: [3000], // returns once port 3000 is open
});
await sandbox.fs.write("/app/package.json", '{"name":"my-app","scripts":{"dev":"astro dev --host 0.0.0.0 --port 3000"},"dependencies":{"astro":"latest"}}')
await sandbox.fs.write_tree([
{"path": "src/pages/index.astro", "content": "<h1>Hello</h1>"},
{"path": "astro.config.mjs", "content": "import { defineConfig } from 'astro/config';\nexport default defineConfig({});"},
], "/app")
install = await sandbox.process.exec({
"name": "install",
"command": "npm install",
"working_dir": "/app",
"wait_for_completion": True,
"timeout": 60, # seconds (default 600; 0 = no auto-kill with keep_alive)
})
dev_server = await sandbox.process.exec({
"name": "dev-server",
"command": "npm run dev",
"working_dir": "/app",
"wait_for_ports": [3000],
})
重要:开发服务器必须绑定到 0.0.0.0(而非 localhost),才能通过预览 URL 访问。使用 --host 0.0.0.0 或 HOST 环境变量。
第 3 步:创建预览 URL
const preview = await sandbox.previews.createIfNotExists({
metadata: { name: "app-preview" },
spec: { port: 3000, public: true },
});
const url = preview.spec?.url;
// url => https://xxxx.us-pdx-1.preview.bl.run
preview = await sandbox.previews.create_if_not_exists({
"metadata": {"name": "app-preview"},
"spec": {"port": 3000, "public": True},
})
url = preview.spec.url
如需私有预览,请设置 public: false 并创建令牌:
const preview = await sandbox.previews.createIfNotExists({
metadata: { name: "private-preview" },
spec: { port: 3000, public: false },
});
const token = await preview.tokens.create(new Date(Date.now() + 10 * 60 * 1000));
// Access: preview.spec?.url + "?bl_preview_token=" + token.value
第 4 步:管理沙箱
// Reconnect to an existing sandbox. Careful: get can return a recently
// deleted sandbox still in TERMINATED state — check status before reuse,
// or use createIfNotExists which handles this for you.
const sandbox = await SandboxInstance.get("my-sandbox");
// List files
const { subdirectories, files } = await sandbox.fs.ls("/app");
// Read a file
const content = await sandbox.fs.read("/app/src/pages/index.astro");
// Get process info / logs
const proc = await sandbox.process.get("dev-server");
const logs = proc.logs; // available if waitForCompletion was true
// Kill a process
await sandbox.process.kill("dev-server");
// Delete the sandbox (all data is erased)
await sandbox.delete();
sandbox = await SandboxInstance.get("my-sandbox")
result = await sandbox.fs.ls("/app")
content = await sandbox.fs.read("/app/src/pages/index.astro")
proc = await sandbox.process.get("dev-server")
# proc.logs available if wait_for_completion was True
await sandbox.process.kill("dev-server")
await sandbox.delete()
沙箱模板(自定义镜像)
如果需要可复用的环境(例如,预装了所有依赖项的 Astro 项目),请创建模板:
bl new sandbox my-astro-template
cd my-astro-template
这会创建:blaxel.toml、Dockerfile、entrypoint.sh、Makefile。
自定义 Dockerfile。务必包含 sandbox-api 二进制文件:
FROM node:22-alpine
WORKDIR /app
COPY --from=ghcr.io/blaxel-ai/sandbox:latest /sandbox-api /usr/local/bin/sandbox-api
RUN npm install -g astro
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
entrypoint.sh 必须启动 sandbox-api:
#!/bin/sh
/usr/local/bin/sandbox-api &
while ! nc -z 127.0.0.1 8080; do sleep 0.1; done
echo "Sandbox API ready"
# Optionally start a process via the sandbox API:
# curl http://127.0.0.1:8080/process -X POST -d '{"workingDir":"/app","command":"npm run dev","waitForCompletion":false}' -H "Content-Type: application/json"
wait
部署模板:
bl deploy
然后获取 IMAGE_ID,并用它创建沙箱:
bl get sandboxes my-astro-template -ojson | jq -r '.[0].spec.runtime.image'
const sandbox = await SandboxInstance.createIfNotExists({
name: "project-sandbox",
image: "IMAGE_ID",
memory: 4096,
ports: [{ target: 3000, protocol: "HTTP" }],
});
教程和示例
沙箱
Astro:https://docs.blaxel.ai/Tutorials/Astro Expo:https://docs.blaxel.ai/Tutorials/Expo Next.js:https://docs.blaxel.ai/Tutorials/Nextjs
Agent
概览:https://docs.blaxel.ai/Tutorials/Agents-Overview
核心 CLI 命令
对于可能提示输入(如确认)的 CLI 命令,在非交互式/无 TTY 环境(例如脚本、CI、Agent)中运行时,请添加 -y 以自动确认。
| 命令 | 用途 |
|---|---|
bl login | 验证工作区身份 |
bl new sandbox|agent|job|mcp NAME | 基于模板初始化新资源 |
bl deploy | 构建资源并部署到 Blaxel |
bl deploy -d DIR | 从指定目录部署 |
bl serve | 在本地运行资源以进行测试 |
bl serve --hotreload | 在本地运行并启用热重载 |
bl get sandboxes|agents|jobs|functions | 列出资源 |
bl get sandbox NAME --watch | 监视沙盒部署状态 |
bl delete sandbox|agent|job|function NAME | 删除资源 |
bl connect sandbox NAME | 在沙盒中打开交互式终端 |
bl chat AGENT-NAME | 与已部署的 Agent 进行交互式聊天 |
bl run job NAME --data JSON | 执行已部署的批处理作业 |
blaxel.toml 结构
name = "my-resource"
type = "sandbox" # sandbox, agent, function, job, volume-template
[env]
NODE_ENV = "development" # NOT for secrets — use Variables-and-secrets
[runtime]
memory = 4096 # MB
generation = "mk3"
# timeout = 900 # seconds (agents max 900, jobs max 86400)
# Ports (sandbox only)
[[runtime.ports]]
name = "dev-server"
target = 3000
protocol = "tcp"
重要注意事项
- 必须在创建沙盒时声明端口,之后无法添加
- 端口 80、443、8080 已被 Blaxel 保留
- 开发服务器必须绑定到
0.0.0.0,而非localhost,预览 URL 才能正常工作 - 沙盒约 50% 的内存会预留给内存文件系统(tmpfs)。如需额外存储,请使用卷
- 沙盒在闲置约 5 秒后会自动缩容至零。状态会在待机期间保留,并在 <25ms 内恢复
- 已删除的沙盒会在
TERMINATED状态下继续显示几分钟(以便访问日志)。普通的get可能会返回它;重新使用前,请使用createIfNotExists或检查sandbox.status - 进程
timeout的单位为秒(默认值为 600)。它限制waitForCompletion:超时后,调用会抛出超时错误(HTTP 422),但进程仍会继续运行——请使用process.get/process.wait重新连接,而不要假设进程已失败。使用keepAlive时,进程会在超时后自动终止(0 = 不自动终止)。普通的长时间运行进程(例如使用waitForPorts启动的开发服务器)不会因此自动终止 waitForCompletion会在进程运行期间保持 HTTP 请求打开(通过沙盒 MCP 服务时最长约为 58 秒)。对于长时间运行的进程,请在启动时不要使用它,而是通过process.wait(name, { maxWait })等待process.exec还接受env(每个进程的环境变量)、restartOnFailure和maxRestarts- 默认情况下,每一行进程 stdout/stderr 都会作为遥测数据导出——大量日志会造成实际的 CPU 争用。请参阅下文的“禁用进程日志导出”
- 切勿将机密放入
[env]——请使用 Console 中的 Variables-and-secrets 页面
沙盒最佳实践
完整参考:https://docs.blaxel.ai/Sandboxes/best-practices
批量上传文件:zip + writeBinary + unzip
切勿对多个文件逐个调用 fs.write 来写入——每次调用都需要一次网络往返(约 100ms),因此几百个文件就需要数分钟。请将它们打包成 zip,使用一次 writeBinary 调用上传,再在沙盒内解压:只需 2 次调用,而非 N 次。
import AdmZip from "adm-zip";
const zip = new AdmZip();
for (const f of files) zip.addFile(f.path, Buffer.from(f.content));
await sandbox.fs.writeBinary("/tmp/project.zip", zip.toBuffer());
await sandbox.process.exec({
name: "unzip",
command: "unzip -o /tmp/project.zip -d /app/project && rm /tmp/project.zip",
waitForCompletion: true,
});
import io, zipfile
buf = io.BytesIO()
with zipfile.ZipFile(buf, "w", zipfile.ZIP_DEFLATED) as zf:
for f in files:
zf.writestr(f["path"], f["content"])
await sandbox.fs.write_binary("/tmp/project.zip", buf.getvalue())
await sandbox.process.exec({
"name": "unzip",
"command": "unzip -o /tmp/project.zip -d /app/project && rm /tmp/project.zip",
"wait_for_completion": True,
})
少量文件可使用 fs.writeTree(一次批量调用)。如果沙盒镜像中没有 unzip,可改用 python3 -m zipfile -e /tmp/project.zip /app/project 解压,或在模板镜像中安装 unzip。
在沙盒重新创建后保留项目状态
不要在每次冷启动时都重新克隆代码仓库并运行 npm install。删除沙盒时(TTL 到期或显式删除),沙盒文件系统会被清除,但你有以下持久化选项:
- 卷:在创建沙盒时附加的持久性块存储(
volumes: [{ name, mountPath }])。将项目工作区和依赖缓存放在卷上;重新创建时会重新挂载,无需重新拉取。完整示例请参阅 ./references。 - Agent Drive:可挂载到运行中沙盒的共享文件系统(私有预览版)。适用于在多个沙盒之间共享状态。
- 模板镜像:将大型依赖打包到自定义沙盒镜像中,这样冷启动时就能直接使用预装环境。
另外请记住:空闲沙盒会进入待机状态(免费,恢复时间 <25ms)——与其在每个会话结束时删除并重建,不如让沙盒在设置 TTL 的情况下保持空闲。
禁用进程日志导出
默认情况下,沙盒进程的每一行 stdout/stderr 都会作为结构化遥测数据导出。输出频繁的进程(例如详细日志模式的开发服务器、跟踪级别的轮询器)每分钟可能产生数千行日志,而这种导出负载已知会导致沙盒内 CPU 争用。
有两种关闭方式:
- 针对单个沙盒:在沙盒上设置环境变量
SANDBOX_DISABLE_PROCESS_LOGGING=true(例如,在模板 Dockerfile 中设置ENV SANDBOX_DISABLE_PROCESS_LOGGING=true,或在创建沙盒时设置其环境变量)。 - 针对整个工作区:管理员可以在 Console > Workspace settings 中为所有沙盒禁用进程日志记录。
你将无法在 Blaxel Console 中查看进程日志;但仍可通过 SDK/API(process.get、日志流)读取日志。
审查集成时应注意的常见反模式
| 反模式 | 症状 | 修复方法 |
|---|---|---|
使用 get-then-create,而不是 createIfNotExists | 删除后快速重新创建(沙盒处于 TERMINATED 状态)时,网关/预览 URL 返回 workload-not-found | 使用 createIfNotExists,或在 get 后检查 sandbox.status |
| 每次冷启动都重新拉取代码仓库并重新安装依赖 | 每次会话启动都要花费数分钟 | 使用卷或 Agent Drive 存放工作区;使用模板镜像存放依赖 |
对多个文件逐一调用 fs.write | 恢复需要数分钟(N 次网络往返) | 使用 zip + writeBinary + unzip(2 次调用);文件数量较少时使用 writeTree |
| 保持日志导出开启,同时输出大量 stdout | 编译/大量输出期间 CPU 飙升 | 设置 SANDBOX_DISABLE_PROCESS_LOGGING=true 或使用工作区开关;同时降低应用日志详细程度 |
| 沙盒空闲时将其删除 | 每次重新访问都要冷启动重建 | 让待机状态处理空闲情况;使用 ttl 清理 |
Agent Drive(共享文件系统)
Agent Drive 是由 SeaweedFS 提供支持的分布式文件系统,可随时挂载到多个沙盒或 Agent,包括已在运行的沙盒。与仅能在创建沙盒时附加的卷(块存储)不同,Drive 支持多个沙盒并发读写访问,并且可以动态挂载或卸载。
此功能目前处于私有预览阶段。预览期间,Agent Drive 仅在
us-was-1区域可用。Drive 和沙盒都必须位于此区域。
使用场景:
- 在沙盒之间传递数据,无需中间服务
- 存储其他 Agent 使用的工具输出和上下文历史
- 在多个 Agent 之间共享数据集
- 创建共享的依赖包缓存文件系统
创建 Drive
import { DriveInstance } from "@blaxel/core";
const drive = await DriveInstance.createIfNotExists({
name: "my-drive",
region: "us-was-1",
displayName: "My Project Drive", // optional; defaults to name
labels: { env: "dev", project: "x" }, // optional
});
from blaxel.core.drive import DriveInstance
drive = await DriveInstance.create_if_not_exists(
{
"name": "my-drive",
"region": "us-was-1",
"display_name": "My Project Drive",
"labels": {"env": "dev", "project": "x"},
}
)
将 Drive 挂载到沙盒
import { SandboxInstance } from "@blaxel/core";
const sandbox = await SandboxInstance.get("my-sandbox");
await sandbox.drives.mount({
driveName: "my-drive",
mountPath: "/mnt/data",
drivePath: "/", // optional; defaults to root of the drive
});
from blaxel.core import SandboxInstance
sandbox = await SandboxInstance.get("my-sandbox")
await sandbox.drives.mount(
drive_name="my-drive",
mount_path="/mnt/data",
drive_path="/",
)
挂载后,写入沙盒内挂载路径的任何文件都会存储在 Drive 上,即使删除沙盒,这些文件也会保留。
挂载子目录
await sandbox.drives.mount({
driveName: "my-drive",
mountPath: "/app/project",
drivePath: "/projects/alpha",
});
await sandbox.drives.mount(
drive_name="my-drive",
mount_path="/app/project",
drive_path="/projects/alpha",
)
列出、卸载和删除 Drive
// List mounted drives on a sandbox
const mounts = await sandbox.drives.list();
// List all drives
const drives = await DriveInstance.list();
// Unmount
await sandbox.drives.unmount("/mnt/data");
// Delete a drive
await DriveInstance.delete("my-drive");
// or instance-level:
const drive = await DriveInstance.get("my-drive");
await drive.delete();
mounts = await sandbox.drives.list()
drives = await DriveInstance.list()
await sandbox.drives.unmount("/mnt/data")
await DriveInstance.delete("my-drive")
# or instance-level:
drive = await DriveInstance.get("my-drive")
await drive.delete()
CLI:bl get drives
Agent Drive 完整示例
import { SandboxInstance, DriveInstance } from "@blaxel/core";
// 1. Create a drive
const drive = await DriveInstance.createIfNotExists({
name: "agent-storage",
region: "us-was-1",
});
// 2. Create a sandbox (use image ID from custom template)
const sandbox = await SandboxInstance.createIfNotExists({
name: "my-agent-sandbox",
image: "my-sandbox-image-id",
memory: 2048,
region: "us-was-1",
});
// 3. Mount the drive
await sandbox.drives.mount({
driveName: "agent-storage",
mountPath: "/mnt/storage",
drivePath: "/",
});
// 4. Write a file to the mounted drive
await sandbox.fs.write("/mnt/storage/hello.txt", "Hello from the drive!");
// 5. Read it back
const content = await sandbox.fs.read("/mnt/storage/hello.txt");
console.log(content); // "Hello from the drive!"
// 6. List mounted drives
const mounts = await sandbox.drives.list();
console.log(mounts);
import asyncio
from blaxel.core.drive import DriveInstance
from blaxel.core import SandboxInstance
async def main():
drive = await DriveInstance.create_if_not_exists(
{"name": "agent-storage", "region": "us-was-1"}
)
sandbox = await SandboxInstance.create_if_not_exists(
{
"name": "my-agent-sandbox",
"image": "my-sandbox-image-id",
"memory": 2048,
"region": "us-was-1",
}
)
await sandbox.drives.mount(
drive_name="agent-storage",
mount_path="/mnt/storage",
drive_path="/",
)
await sandbox.fs.write("/mnt/storage/hello.txt", "Hello from the drive!")
content = await sandbox.fs.read("/mnt/storage/hello.txt")
print(content)
mounts = await sandbox.drives.list()
print(mounts)
asyncio.run(main())
文档:https://docs.blaxel.ai/Agent-drive/Overview
其他 Blaxel 资源
Agent 托管
将 AI Agent 部署为可无服务器自动扩缩的 HTTP 端点。与框架无关(LangChain、CrewAI、Claude SDK 等)。
bl new agent
# develop in src/agent.ts or src/agent.py
bl serve # test locally
bl deploy # deploy
bl chat AGENT-NAME # query
同步端点处理请求的最长时间为 100 秒,异步端点则最长为 10 分钟。 文档:https://docs.blaxel.ai/Agents/Overview
MCP 服务托管
部署遵循 MCP 协议的自定义工具服务。
bl new mcp
# implement in src/server.ts or src/server.py
bl serve --hotreload # test locally
bl deploy
Agent 通过 SDK 连接到已部署的 MCP 服务:
const tools = await blTools(["functions/my-mcp-server"]);
每个沙盒还会通过 https://<SANDBOX_URL>/mcp 暴露自己的内置 MCP 服务,其中包含用于进程管理、文件系统和代码生成的工具。
文档:https://docs.blaxel.ai/Functions/Overview
批处理任务
用于并行后台任务(耗时数分钟到数小时)的可扩展计算。
bl new job
# implement in src/index.ts or src/index.py
bl deploy
bl run job NAME --data '{"tasks": [...]}'
每项任务最长为 24 小时。在 blaxel.toml 中设置 maxConcurrentTasks。
文档:https://docs.blaxel.ai/Jobs/Overview
资源
- 部署配置参考:https://docs.blaxel.ai/deployment-reference
- CLI 命令参考:https://docs.blaxel.ai/cli-reference/introduction
请阅读各 SDK 文件,了解详细说明和代码示例:
- ./references/sdk-python.md
- ./references/sdk-typescript.md
每个 SDK README 都包含:
- SDK 概览
- 要求
- 使用沙盒、卷、Agent、批处理任务和 MCP 的代码示例
- 其他实用信息
如需更多文档,请参阅:https://docs.blaxel.ai/llms.txt