news 2026/10/28 4:06:54

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Cloudflare Workers 边缘运行时构建 AI Agent 开源架构实战

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是消息总线,相当于IPC;Workers 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. 实操过程:从零搭建一个最小可用的Agent

4.1 环境准备与项目初始化

先确保你有一个Cloudflare账号,然后安装wrangler CLI。我用的版本是3.x,命令如下:

npm install -g wrangler wrangler login

登录后会跳转浏览器授权。接着创建项目:

npm create cloudflare@latest 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 Object;Agent循环负责调用模型并解析输出;工具执行负责实际调用函数。

// 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,刷新页面就生效,比重新部署快得多。这个技巧在快速迭代阶段特别管用。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/28 4:06:13

ECC椭圆曲线密码学从原理到落地:P-256/P-384曲线选型与OpenSSL实操

最近在排查一个线上服务的证书问题时,顺手用openssl x509 -text解析了一张HTTPS证书,发现签发机构是Trustwave,公钥算法一栏写着id-ecPublicKey,椭圆曲线参数是secp384r1(也就是常说的P-384)。当时我愣了一…

作者头像 李华
网站建设 2026/10/28 4:05:56

用Canvas实现浪漫星空告白墙的技术实践

1. 项目背景与核心思路那天晚上和女友视频时,她半开玩笑地说:"你们程序员整天对着电脑敲代码,一点都不浪漫。"这句话像一记重拳打在我这个前端工程师的自尊心上。挂断电话后,我盯着桌面上闪烁的代码编辑器,突…

作者头像 李华
网站建设 2026/10/28 4:05:03

PVSYST工程化设计:从软件操作到可答辩的光伏系统验证链

简介:本资源是一份面向光伏工程师、新能源专业学生及系统设计初学者的PVSYST软件入门教程PPT,聚焦并网型光伏系统的设计全流程,解决软件安装避坑、界面功能认知、初步与工程两级设计实操、遮挡建模、损失参数设置及模拟报告生成等核心问题。文…

作者头像 李华
网站建设 2026/10/28 4:03:49

impeccable怎么用?从词源、搭配到场景的英语高级表达指南

最近“impeccable”这个词在网络上被频繁使用——穿搭博主用它夸一套 look,职场人士用它形容一场汇报,字幕组用它翻译原著里那种“无可挑剔”的出场。很多人看到它的第一反应是:这不就是 perfect 的意思吗?那为什么还要单独学它&a…

作者头像 李华
网站建设 2026/10/28 4:03:49

能力调用:结构智能从静态能力到动态执行的机制研究

能力调用:结构智能从静态能力到动态执行的机制研究——WSaiOS-SI 能力调用理论的系统阐释摘要:能力匹配理论解决了“调用什么能力”的问题,但匹配成功并不意味着任务完成。本文基于 WSaiOS-SI 理论框架,系统阐述能力调用&#xff…

作者头像 李华
网站建设 2026/10/28 4:03:15

Adaboost在时间序列预测中的优势与实战

1. 为什么Adaboost能成为时间序列预测的"最强算法"?在金融、气象、工业设备监控等领域,时间序列预测一直是个让人又爱又恨的难题。传统方法像ARIMA虽然经典,但面对非线性特征就力不从心;LSTM这类深度学习模型虽然强大&a…

作者头像 李华