资讯动态

Cloudflare Workers 边缘运行时构建 AI Agent 开源架构实战

发布时间:2026/10/28 4:07:02 来源:尧图企业网站定制
1. 从“cloudflare-os”这个标题说起它到底想解决什么问题第一次看到“cloudflare-os”这个标题很多人会误以为Cloudflare要发布一款桌面操作系统。实际上结合Cloudflare Workers、AI Agent、开源这几个关键词来看它指向的是一个更务实的方向把Cloudflare的边缘网络当作一个“操作系统”来用在上面跑AI Agent和各类自动化任务。这个思路并不新鲜但真正把它做成一套可复用的开源方案价值就出来了。我最早接触Cloudflare Workers是在做静态站点加速的时候当时只觉得它是个CDN的增强版。后来Workers支持了Durable Objects、KV、R2、Queues、AI Gateway我才意识到这套东西已经具备了一个“运行时环境”的雏形。所谓“cloudflare-os”本质上就是用Workers作为内核用各类绑定服务作为系统调用用AI Agent作为上层应用的一套架构范式。它解决的问题很具体传统AI Agent部署要么依赖一台常驻服务器要么依赖某个云厂商的容器服务成本和运维复杂度都不低。而Cloudflare的免费额度和按需计费模式让个人开发者和小团队也能跑起一个7x24小时在线的Agent。这篇文章适合三类人看一是想入门AI Agent开发但不想折腾服务器的开发者二是已经在用Cloudflare Workers、想进一步挖掘其AI能力的工程师三是对“边缘计算Agent”这个组合感兴趣、想找一个可复现参考架构的技术爱好者。我会从整体设计思路讲到具体实操包括Durable Objects怎么管状态、Workers AI怎么调模型、Agent的记忆和工具调用怎么落地以及我在实际搭建过程中踩过的坑。2. 整体架构设计为什么把Workers当操作系统用2.1 核心思路边缘运行时 状态层 AI能力层把Cloudflare Workers类比成操作系统这个说法乍听有点夸张但拆开看其实很贴切。Workers本身是计算单元相当于CPU调度KV和R2是持久化存储相当于文件系统Durable Objects是有状态的协调者相当于进程管理Queues是消息总线相当于IPCWorkers AI和AI Gateway是模型推理入口相当于设备驱动。这套东西组合起来确实能撑起一个Agent运行时的骨架。我选择这个架构而不是传统的“一台VPS跑Python脚本”核心理由有三个。第一是冷启动足够快Workers的冷启动在毫秒级Agent的每次请求不需要等容器拉起。第二是全球分布用户的请求会被路由到最近的边缘节点Agent的响应延迟天然就低。第三是成本可控免费额度每天10万次请求对于个人项目和小规模Agent完全够用超出后按量计费也比常驻服务器便宜。当然这个架构也有明显的取舍。Workers的运行时间有限制免费版CPU时间10ms付费版30秒可配置到更长这意味着长时任务必须拆解或异步化。另外Workers不支持原生Node.js的全部API某些依赖需要找替代方案。这些限制在后面实操部分会详细讲怎么绕过。2.2 为什么选Durable Objects做Agent的状态管理Agent和普通API最大的区别在于它是有状态的。一次对话需要记住上下文一个任务需要跟踪执行进度这些都不能靠无状态的Workers请求来完成。Cloudflare提供的方案里KV是最终一致的不适合做实时状态R2是对象存储延迟偏高Durable Objects是唯一强一致、单点协调的选择。Durable Objects的工作方式是这样的每个Object有一个唯一ID所有对该ID的请求都会被路由到同一个实例上这个实例在内存中维护状态同时可以把状态持久化到内置的存储里。对于Agent来说我可以给每个用户会话分配一个Durable Object这样对话历史、工具调用记录、临时变量都放在这个Object里天然就是隔离的。注意Durable Objects的计费方式和Workers不同它按“请求数持续时间存储”三项计费。如果你的Agent需要长时间保持连接比如WebSocket费用会明显上升。建议在会话结束后主动释放Object或者设置合理的超时。2.3 AI能力层Workers AI与外部模型的混合调用Cloudflare自己提供了Workers AI跑的是开源模型比如Llama系列、Mistral系列。优点是无需API Key、按神经元计费、延迟低缺点是模型能力相比GPT-4级别还有差距。我的做法是混合调用简单任务意图识别、文本分类、格式化输出走Workers AI复杂任务长文推理、代码生成通过AI Gateway转发到外部模型。AI Gateway在这里的角色是统一入口缓存限流。它支持多家模型提供商的API格式可以在不改代码的情况下切换后端。更重要的是它有缓存功能相同的Prompt在短时间内重复请求会直接返回缓存结果这对Agent场景很实用——很多系统提示词是固定的缓存能省下不少token。3. 核心细节解析Agent在Workers上怎么跑起来3.1 Agent的循环结构在边缘环境下的改造标准Agent的ReAct循环是“思考-行动-观察”三步反复执行直到任务完成。在传统服务器上这个循环可以用一个while循环搞定但在Workers上不行因为单次请求有时间限制。我的改造方案是把循环拆成多次请求每次请求只执行一轮“思考行动”然后把状态存回Durable Object通过Queues或者递归调用的方式触发下一轮。具体实现上我会在Durable Object里维护一个step计数器和一个status字段。每次Worker收到请求先检查status如果是running就继续执行下一步如果是done就返回结果。这样即使单次请求超时整个任务也不会丢失下次请求会从断点继续。// Durable Object中的状态结构示例 class AgentSession { constructor(state, env) { this.state state; this.env env; this.session { status: idle, step: 0, history: [], pendingAction: null }; } async fetch(request) { const input await request.json(); if (this.session.status done) { return new Response(JSON.stringify({ result: this.session.result })); } // 执行一轮Agent循环 const next await this.runOneStep(input); this.session.step; await this.state.storage.put(session, this.session); return new Response(JSON.stringify(next)); } }这个结构的好处是天然支持断点续传和人工介入。如果某一轮Agent需要用户确认才能继续我可以把status设为waiting等用户下次请求时再恢复。3.2 工具调用的注册与安全边界Agent要干活就得调工具比如查数据库、发HTTP请求、读写文件。在Workers环境里工具的实现方式就是一个个独立的函数注册在一个工具表里Agent根据任务需要选择调用。我一般会把工具分成三类只读工具查询类、写入工具修改类、危险工具删除、发送类。只读工具可以直接执行写入工具需要记录日志危险工具必须加确认机制。const tools { get_weather: { description: 查询指定城市的天气, parameters: { city: string }, handler: async ({ city }) { const resp await fetch(https://api.weather.com/v1/${city}); return resp.json(); }, level: read }, send_email: { description: 发送邮件, parameters: { to: string, subject: string, body: string }, handler: async (params) { // 实际发送逻辑 return { sent: true }; }, level: dangerous, requireConfirm: true } };提示工具的参数校验一定要做不能信任模型输出的JSON。我遇到过模型把city字段写成数组的情况直接导致下游API报错。建议用zod或者手写校验函数在handler入口处拦截非法参数。3.3 记忆系统的分层设计Agent的记忆不能全塞进上下文窗口那样token消耗太快。我的做法是三层记忆短期记忆放在Durable Object的内存里就是最近几轮对话中期记忆存在KV里按会话ID索引保存摘要和关键事实长期记忆存在R2里保存完整的对话日志和工具调用记录需要时通过向量检索召回。向量检索这块Cloudflare有Vectorize服务可以配合Workers AI的embedding模型使用。我把历史对话切片后生成向量存入Vectorize每次新请求来时先检索最相关的几条历史记录拼接到上下文里。这样既控制了token用量又保留了长期记忆的能力。4. 实操过程从零搭建一个最小可用的Agent4.1 环境准备与项目初始化先确保你有一个Cloudflare账号然后安装wrangler CLI。我用的版本是3.x命令如下npm install -g wrangler wrangler login登录后会跳转浏览器授权。接着创建项目npm create cloudflarelatest my-agent cd my-agent选择“Hello World Worker”模板即可。然后修改wrangler.toml添加Durable Objects和KV的绑定name my-agent main src/index.js compatibility_date 2024-01-01 [durable_objects] bindings [ { name AGENT_SESSION, class_name AgentSession } ] [[kv_namespaces]] binding AGENT_MEMORY id your-kv-id [[migrations]] tag v1 new_classes [AgentSession]KV的id需要先在Cloudflare控制台创建一个命名空间然后把id填进来。4.2 编写Agent核心逻辑核心逻辑分三块请求入口、Agent循环、工具执行。请求入口负责解析用户输入并路由到对应的Durable ObjectAgent循环负责调用模型并解析输出工具执行负责实际调用函数。// src/index.js export default { async fetch(request, env) { const url new URL(request.url); const sessionId url.searchParams.get(session) || default; const id env.AGENT_SESSION.idFromName(sessionId); const stub env.AGENT_SESSION.get(id); return stub.fetch(request); } }; export class AgentSession { constructor(state, env) { this.state state; this.env env; } async fetch(request) { const { message } await request.json(); let session await this.state.storage.get(session) || { history: [], step: 0 }; // 构建Prompt const prompt this.buildPrompt(session.history, message); // 调用Workers AI const response await this.env.AI.run(cf/meta/llama-3-8b-instruct, { messages: prompt }); // 解析模型输出判断是工具调用还是最终回答 const parsed this.parseResponse(response.response); if (parsed.type tool) { const result await this.executeTool(parsed.tool, parsed.args); session.history.push({ role: assistant, content: response.response }); session.history.push({ role: tool, content: JSON.stringify(result) }); await this.state.storage.put(session, session); // 递归触发下一轮 return this.fetch(new Request(request.url, { method: POST, body: JSON.stringify({ message: continue }) })); } session.history.push({ role: assistant, content: parsed.answer }); await this.state.storage.put(session, session); return new Response(JSON.stringify({ answer: parsed.answer })); } buildPrompt(history, message) { const system 你是一个助手可以调用以下工具${Object.keys(tools).join(, )}。 输出格式要求如果是工具调用输出JSON {type:tool,tool:名称,args:{...}} 如果是最终回答输出JSON {type:answer,answer:内容}。; return [ { role: system, content: system }, ...history.slice(-10), { role: user, content: message } ]; } parseResponse(text) { try { const json JSON.parse(text.match(/\{[\s\S]*\}/)[0]); return json; } catch { return { type: answer, answer: text }; } } async executeTool(name, args) { const tool tools[name]; if (!tool) return { error: 工具不存在 }; if (tool.requireConfirm) { // 危险工具需要额外确认逻辑 return { error: 需要用户确认 }; } return tool.handler(args); } }这段代码跑起来后你就有了一个能调用工具的最小Agent。实测下来Llama-3-8B在意图识别和简单工具调用上表现还行但复杂推理容易出错建议复杂场景切换到更大的模型。4.3 部署与调试部署命令很简单wrangler deploy部署后可以用wrangler tail实时查看日志。我调试Agent时最常用的手段是在关键节点打日志比如模型原始输出、解析后的JSON、工具执行结果。因为Agent的行为不确定性高没有日志根本没法排查。注意Workers的日志有采样不是每条都会显示。调试阶段可以在代码里加console.log然后用wrangler tail --format pretty查看。生产环境记得把日志级别调高避免泄露敏感信息。5. 常见问题与排查技巧实录5.1 模型输出格式不稳定怎么办这是最常见的问题。Llama-3-8B有时候不按JSON格式输出直接返回一段自然语言。我的应对策略是三层兜底第一层用正则提取JSON第二层如果提取失败就用few-shot示例引导第三层如果还失败就降级为纯文本回答。另外在system prompt里明确写“只输出JSON不要有其他内容”能显著提升格式合规率。5.2 Durable Object的并发问题Durable Objects是单线程的同一时刻只能处理一个请求。如果Agent的某一轮执行时间较长后续请求会排队。我遇到过用户快速连发消息导致响应延迟的情况。解决方案是在客户端做防抖或者把长任务拆成异步步骤用Queues解耦。5.3 工具调用超时Workers的fetch有30秒超时限制付费版可调如果工具调用的外部API响应慢整个Agent循环就会失败。我的做法是给每个工具设置独立的超时用Promise.race包装const result await Promise.race([ tool.handler(args), new Promise((_, reject) setTimeout(() reject(new Error(工具超时)), 10000)) ]);这样即使某个工具卡住也不会拖垮整个Agent。5.4 常见问题速查表问题现象可能原因排查方法解决方案模型输出非JSONPrompt不够明确打印原始输出加few-shot示例强化格式指令Durable Object响应慢并发请求排队查看wrangler tail日志客户端防抖任务异步化工具调用失败参数格式错误打印args加参数校验用zod部署后报错绑定配置缺失检查wrangler.toml补全KV/DO绑定费用超预期请求量过大查看Dashboard用量加缓存限制单用户频率5.5 几个我踩过的坑第一个坑是KV的最终一致性。我一开始把会话状态存在KV里结果发现刚写入的数据有时候读不到导致Agent重复执行。后来换成Durable Object的storage才解决。第二个坑是Workers AI的模型名称。不同区域的模型名称可能不一样我照着文档写的cf/meta/llama-3-8b-instruct在某些区域报错后来改成cf/meta/llama-3-8b-instruct-awq才跑通。第三个坑是Durable Object的迁移。如果你修改了DO的类名或绑定需要在wrangler.toml里加migration否则部署会失败。6. 这个架构还能怎么扩展跑通最小版本后我陆续加了几个扩展。一个是多Agent协作用Queues做消息传递一个Agent负责规划另一个负责执行第三个负责审核。这个模式在复杂任务上效果不错但调试难度也上去了。另一个是定时任务用Cron Triggers每天触发Agent做数据汇总结果写到R2里。还有一个是WebSocket支持让Agent能主动推送消息这个需要Durable Objects的WebSocket Hibernation API能省不少费用。如果你也想试试这个方向我的建议是先从单Agent单工具开始跑通了再加复杂度。Agent开发最怕一上来就搞大而全的架构最后卡在某个细节上动弹不得。先把“模型调用-工具执行-状态保存”这个最小闭环跑通后面加什么都是在这个基础上叠。最后分享一个实用技巧把Agent的system prompt存在KV里这样不改代码就能调整Agent的行为。我经常在调试时直接改KV里的prompt刷新页面就生效比重新部署快得多。这个技巧在快速迭代阶段特别管用。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑