尧图精选

基于 Flue 框架在 Cloudflare 上构建沙箱 Agent:R2 与 Git 工作区水合实战

🕒 发布时间:2026/9/17 3:44:34 📁 来源:尧图网络
基于 Flue 框架在 Cloudflare 上构建沙箱 AgentR2 与 Git 工作区水合实战【免费下载链接】flueThe sandbox agent framework.项目地址: https://gitcode.com/GitHub_Trending/flue1/flue本指南以仓库中 examples/cloudflare 示例为核心完整讲解如何用 Flue 在 Cloudflare Workers 上运行沙箱型 Agent通过 Workers AI Binding 免密钥调用模型、使用 Worker Loader 驱动的 cloudflare-computer 沙箱以及如何从 R2 存储桶或 Git 仓库水合hydrate出可写的工作区并让模型调用技能skill。读完你将掌握use agent模块、useModel/useSandbox/defineTool三个核心 Hook 的实战组合以及从本地vite dev到远端wrangler dev --remote、再到pnpm run deploy的完整运行链路。示例概览三个最小化 Agent 各自演示一种能力examples/cloudflare目录下的三个 Agent 被刻意保持最小化——每个只端到端演示一项 Cloudflare 专属能力方便直接复制模式到真实应用Agent 模块演示的能力with-cloudflare-binding.ts通过 Workers AI Binding 路由模型流量无需任何 Provider API Keyskills-from-r2.ts从 R2 存储桶水合 cloudflare-computerWorkspace并使用发现到的技能通过模型可调用的check_spamactionskills-from-git.ts通过内置的workspace.git客户端从 Git 仓库水合 cloudflare-computerWorkspace三个 Agent 共享同一个项目自有沙箱适配器 src/sandboxes/cloudflare-computer.ts该文件顶部标注了flue-blueprint: sandbox/cloudflare-computer1概念上由flue add sandbox cloudflare/computer命令生成。应用使用 Vite 构建vite.config.ts 中同时装配了flue/vite的flue()和官方cloudflare/vite-plugin的cloudflare()且flue()必须放在前面——Flue 先扫描项目、准备生成的 Worker 入口和合并后的 wrangler 配置Cloudflare 插件随后消费这些产物。Agent 模块携带use agent指令路由由 src/app.ts 显式挂载。use agent指令与模块注册每个 Agent 文件的第一行都是use agent;见 with-cloudflare-binding.ts。如 app.ts 顶部注释所述注册 Agent 的是对带use agent指令模块的扫描而非路由挂载本身——因此一个仅用于分发的 Agent 甚至不需要挂载路由。useModel一行指定 Workers AI 模型useModel来自flue/runtime实现在 packages/runtime/src/hooks/use-model.ts。三个示例统一使用useModel(cloudflare/cf/moonshotai/kimi-k2.6);模型 ID 带cloudflare/前缀Flue 会自动路由到 Workers AI Binding。注意 app.ts 中关于 AI Gateway 的说明默认情况下每次cloudflare/...模型调用都会经由 Cloudflare 默认 AI Gateway按需为你的账户创建要自定义指定网关、覆盖缓存、附加元数据或完全退出可自行注册setProvider(cloudflareBindingProvider({ binding: env.AI, gateway: ... }))用户app.ts的导入先于自动注册ESM 提升所以你的注册优先。环境准备与构建安装依赖并预热构建产物pnpm install全新检出时仓库的dist/目录是陈旧的需要先构建工作区包pnpm run build -F flue/runtime -F flue/vite由于本示例的 Agent 全部使用 Workers AI Binding不需要任何 Provider API Key。如果切换到非 Cloudflare 模型请在项目根目录的.env中放入对应的 Provider Key。Worker Loader 前提skills-from-r2 与 skills-from-git两个水合示例都依赖worker_loadersbinding。Worker Loader目前处于 beta 阶段需要你的 Cloudflare 账户开通访问权限binding 已声明在 wrangler.jsonc 中worker_loaders: [{ binding: LOADER }], compatibility_flags: [nodejs_compat, experimental],experimental兼容性标志是 cloudflare-computer 沙箱的 worker-shell 后端所必需的其 Dynamic Worker 依赖它见 wrangler.jsonc。如果账户没有 Worker Loader 访问权限可以注释掉这段配置——只有使用它的两个 Agentskills-from-r2、skills-from-git会在沙箱构建时抛出清晰错误其余示例with-cloudflare-binding不受影响。wrangler.jsonc 中的绑定与迁移wrangler.jsonc 是用户自有的 wrangler 配置Cloudflare Vite 集成读取它并应用 Flue 的贡献生成的 Worker 入口与每个 Agent 的 Durable Object binding经由flueWorkerConfig()定制器最终产出可部署的 Worker{ name: cloudflare, compatibility_date: 2026-06-01, ai: { binding: AI }, r2_buckets: [ { binding: KNOWLEDGE_BASE, bucket_name: flue-example-knowledge-base, preview_bucket_name: flue-example-knowledge-base-dev } ] }迁移历史由用户维护新增一个 Agent 新增导出的函数 挂载 为其生成的类FluePascalNameAgent由 Agent 函数名派生添加一条迁移 tag重命名 Agent 函数属于存储身份变更要用 wrangler 原生的renamed_classes表达。preview_bucket_name让wrangler dev --remote使用独立的 dev 桶避免污染 prod 数据。沙箱适配器cloudflare-computer 的接线方式cloudflare-computer.ts 是理解整个示例的关键文件它展示了三个层次的接线。workspaceHost把 Durable Object 变成工作区宿主Flue 的extendAPI来自flue/runtime/cloudflare被用来包装 Agent 的 Durable Object 基类export const workspaceHost extend({ base: (Base) class extends Base { // 构造函数中记录 ctx/env 到 per-DO-id 的模块级 Map async __getWorkspaceStub() { // 返回 workspace.stub()供 shell 的 env.HOST 拨回 } }, });设计动机值得注意每个 id 只有一个存活的 Durable Object 实例Agent 在它内部渲染所以按 DO id 字符串键控的模块级状态把扩展捕获的宿主与沙箱工厂连接起来。Workspace 骑在宿主条目上它绑定到实例的存储缓存同一 Durable Object 的新构造驱逐、dev 重载不能看到前一代的 Workspace。每个使用该沙箱的 Agent 模块都需要重新导出它让 worker-shell 后端能拨回工作区见 skills-from-r2.ts 与 skills-from-git.tsexport { workspaceHost as cloudflare } from ../sandboxes/cloudflare-computer;getComputerWorkspace一次性构造共享的持久文件系统每个 Durable Object 一个持久文件系统首次调用时创建并与沙箱共享。它要求传入loader: env.LOADER并构造WorkspaceOptionsstorage来自host.ctx.storageSQLite作为WorkspaceOptions的持久层waitUntil绑定host.ctx.waitUntil让分离的工作区工作模块执行、延迟同步能活过发起它的请求git: createGitClient()启用workspace.git与 shell 内置git命令不需要时可删除此行连同platformatic/vfs依赖以把 git 排除出构建backends默认注册WorkerShellBackendjust-bash。这是该适配器的接线不是包的能力上限——Workspace 可以针对同一份持久文件注册更多后端尤其是来自cloudflare/computer/backends/container的完整 LinuxCloudflareContainerBackend通过workspace钩子追加并用runtime.exec(cmd, { backend: id })按调用选择。options.workspace钩子允许在构造前重塑默认选项追加 R2 mount、observer、defaultGitIdentity等。getComputerSandbox把 Workspace 包装成通用 Sandbox返回SandboxFactorycreateSandbox()内部确保/workspace目录存在并用createWorkspaceSandbox(workspace, DEFAULT_CWD)把 Workspace 的文件系统与运行时暴露成 Flue 的通用Sandbox动词exec/readFile/writeFile/stat/readdir/exists/mkdir/rm。因为exec()可用框架的标准工具集bash/grep/glob/read/write/edit直接适用无需tools覆盖。exec还处理了 AbortSignal 的及时拒绝与SIGKILL尽力终止。场景一从 R2 存储桶水合工作区skills-from-r2skills-from-r2.ts 演示从 R2 水合 Workspace并让模型通过check_spam工具使用水合发现的技能。一次性水合放在 SandboxFactory 里水合是环境的一次性设置而非每次渲染的工作因此它住在传给useSandbox的自写SandboxFactory中——符合SandboxFactory契约的惰性要求构造工厂对象是廉价的昂贵的 R2 读取只发生一次在createSandbox()里、初始化时绝不在重渲染时发生useSandbox({ async createSandbox(options) { const hydrated await workspace.fs.stat(HYDRATION_SENTINEL).then( () true, () false, ); if (!hydrated) { await hydrateFromBucket(workspace, KNOWLEDGE_BASE, HYDRATION_ROOT); await workspace.fs.writeFile(HYDRATION_SENTINEL, new Date().toISOString()); } return computer.createSandbox(options); }, });sentinel机制/.hydrated哨兵写入 Durable Object 的 SQLite首次运行后水合即空操作no-op。需要强制重新水合时可以改源码中的哨兵 key 或清空 DO 的存储。hydrateFromBucket流式拷贝 R2 对象async function hydrateFromBucket(workspace, bucket, root) { let cursor; while (true) { const listing await bucket.list({ cursor }); for (const obj of listing.objects) { if (obj.key || obj.key.endsWith(/)) continue; const body await bucket.get(obj.key); if (!body) continue; await workspace.fs.writeFile(${root}/${obj.key}, body.body); } if (!listing.truncated) break; if (!listing.cursor) throw new Error(...); cursor listing.cursor; } }R2 的 body 直接流入持久文件系统无整文件缓冲用游标分页处理截断的 listing。注释还点出替代方案只读且不变的数据可用 R2 mountWorkspaceOptions.mounts而拷贝方式保持整棵树可写。check_spam带子 harness 的模型可调用工具技能调用位于模型可调用的check_spam工具中。defineTool声明结构化输入输出valibot schemaharness: true让它的 run 拥有一个子 harness其 scratch 会话发现同一批水合技能const checkSpam defineTool({ name: check_spam, description: Classify a message as spam or not using the spam-filter skill..., input: v.object({ message: v.string() }), output: v.object({ spam: v.boolean(), confidence: v.picklist([low, medium, high]), reasoning: v.string(), }), harness: true, async run({ harness, data }) { const result await harness.prompt( Use the spam-filter skill to classify the following message:\n\n${data.message}, { result: v.object({ spam: v.boolean(), confidence: v.picklist([...]), reasoning: v.string() }) }, ); return { output: result.data }; }, });Agent 主体用useTool(checkSpam)注册系统提示词指示模型被问及消息是否垃圾邮件时调用该工具并报告判定。场景二从 Git 仓库水合工作区skills-from-gitskills-from-git.ts 演示通过 Workspace 内置 git 客户端克隆仓库再让模型用标准 shell 与文件工具探索const TARGET_REPO https://github.com/FredKSchott/vinext-starter; const CLONE_DIR /workspace/repo; useSandbox( { async createSandbox(options) { const hydrated await workspace.fs.stat(HYDRATION_SENTINEL).then(() true, () false); if (!hydrated) { await workspace.git.clone({ url: TARGET_REPO, dir: CLONE_DIR }); await workspace.fs.writeFile(HYDRATION_SENTINEL, new Date().toISOString()); } return computer.createSandbox(options); }, }, { cwd: CLONE_DIR }, );与 R2 版本同样的惰性水合模式与哨兵检查区别在于useSandbox的第二个参数{ cwd: CLONE_DIR }把沙箱默认工作目录指向克隆目录。系统提示词要求模型被问及仓库时务必实际用 shell 与文件工具检查文件绝不凭假设作答。路由挂载与 HTTP 接口app.ts 用 Hono 构造应用默认导出拥有整个请求管线const app new Hono(); app.get(/api/ping, (c) c.json({ pong: true, at: new Date().toISOString() })); app.route(/agents/with-cloudflare-binding, createAgentRouter(WithCloudflareBinding)); app.route(/agents/skills-from-git, createAgentRouter(SkillsFromGit)); app.route(/agents/skills-from-r2, createAgentRouter(SkillsFromR2)); export default app;createAgentRouter(Fn)是纯路由工厂实现于 packages/runtime/src/routing.ts。/api/ping这类自定义路由运行在 worker isolate 中、不在 Agent 的 Durable Object 内适合存活探针、状态页等不需要 Agent 状态/流式的端点。相对挂载点每个 Agent 的 HTTP 面为POST /:id——发送 prompt202 受理GET | HEAD /:id——读取会话流POST /:id/abort——中止进行中的工作挂载路径由你决定Agent 函数名其持久身份才是会话与 Durable Object 类的键。同构的app.ts在 Node 与 Cloudflare 目标上都能工作flue()内部适配Cloudflare 上每个挂载的 Agent 路由解析生成的 binding经 Agents SDK 转发到对应 Durable Object其余部分就是普通 Hono 应用。此外 cloudflare.ts 导出WorkspaceServiceProxy——它必须是入口模块的导出cloudflare-computer 沙箱的 shell 后端为它铸造 loopback bindingctx.exports.WorkspaceServiceProxy而 Cloudflare 只为 Worker 入口导出的类铸造 loopback。Flue 会把该文件的所有导出从生成的 Worker 入口重新导出。本地开发的注意事项vite dev在本地 workerd 中运行 worker可以暴露本地的worker_loadersbinding但Wrangler 的本地 R2 CLI 存储可能对运行中的 dev server 的 R2 binding 不可见。要做端到端的 R2 水合冒烟测试请使用远端资源两种选择wrangler dev --remote——让 worker 使用你的 dev 桶运行在 Cloudflare 边缘需要账户开通 Worker Loader 访问权限。先执行pnpm run buildwrangler dev/deploy通过 Cloudflare Vite 插件写入的 deploy redirect 读取构建产物。部署到 preview 环境——pnpm run deploy之后通过 HTTP 练习 Agent。还要注意沙箱的执行能力边界cloudflare-computer 沙箱在 just-bash一个 JavaScript shell中、在持久 Workspace 上运行命令——coreutils 与grep/find可用原生二进制与npm不可用。如果你的账户没有 Worker Loader 访问权限或需要 Linux 工具链或可写桶挂载改用cloudflare/sandboxContainers mountBucket。给 R2 播种技能仅 skills-from-r2运行skills-from-r2之前需要把 SKILL.md 放进 dev R2 桶让水合步骤有东西可拷贝# 在本目录下执行需要已安装并认证 wrangler ./seed-r2.shseed-r2.sh 会把.agents/skills/spam-filter/SKILL.md写入flue-example-knowledge-base-dev。环境变量控制目标BUCKETprod ./seed-r2.sh # 播种 prod 桶 REMOTE0 ./seed-r2.sh # 播种 wrangler 本地 R2 存储脚本内容即技能本体一个带 frontmattername: spam-filter、description: Classify a message as spam or not spam...的 SKILL.md描述了结构化判定协议spam/confidence/reasoning与强垃圾信号启发式紧迫感、免费奖品、可疑短链接、敏感信息索求。若想换桶名需要同步修改 wrangler.jsonc 与seed-r2.sh中的BUCKET_NAME表。运行与触发 Agent# Dev server经 Cloudflare Vite 插件的本地 workerd pnpm run dev # 生产构建dist/ 下的可部署 Worker 输出 pnpm run build # 触发 Agent挂载点位于 src/app.ts端口由 vite dev 打印。 # prompt 是 fire-and-forget202 受理用同一 URL 的 GET 读取会话流中的回复。 curl -X POST http://localhost:5173/agents/with-cloudflare-binding/test-1 \ -H Content-Type: application/json -d {kind: user, body: Say hello.} curl http://localhost:5173/agents/with-cloudflare-binding/test-1 curl -X POST http://localhost:5173/agents/skills-from-r2/test-1 \ -H Content-Type: application/json \ -d {kind: user, body: Check this message for spam: CONGRATS! You won a free iPhone: http://bit.ly/xyz} curl -X POST http://localhost:5173/agents/skills-from-git/test-1 \ -H Content-Type: application/json \ -d {kind: user, body: List every top-level file and directory in the repo, then describe the project.}脚本命令对应 package.json 中的dev: vite dev、build: vite build、deploy: vite build wrangler deploy。skills-from-r2与skills-from-git首次运行时会在 Durable Object 的 SQLite 中写入/.hydrated哨兵第二次运行的水合在哨兵检查处即为空操作。演进说明从 Workflow 到 Agent示例末尾的注释记录了一次重要演进以前的skills-from-*workflow 现在是 Agent。Workflow 的run体变成了你发送的消息skills-from-git或一个模型可调用 actionskills-from-r2 的check_spam会话conversation是唯一的持久单元。这意味着本示例展示的一次水合 会话内多轮复用模式正是 Flue 当前推荐的、以会话为唯一持久单位的 Agent 形态。【免费下载链接】flueThe sandbox agent framework.项目地址: https://gitcode.com/GitHub_Trending/flue1/flue创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联 返回资讯列表 →