资讯动态

Cloudflare Workers 边缘运行时构建 AI Agent 后端实战指南

发布时间:2026/10/28 4:23:31 来源:尧图企业网站定制
1. 从“cloudflare-os”这个标题说起它到底在指什么第一次看到“cloudflare-os”这个词我下意识以为是某个操作系统的发行版代号。翻了一圈资料才反应过来它并不是一个真正意义上的操作系统内核而是社区对 Cloudflare 边缘计算能力的一种形象化称呼——把 Workers、Durable Objects、KV、R2、D1、Queues、AI 这一整套能力组合起来形成一个“跑在边缘上的运行时环境”。你可以把它理解成Cloudflare 把自己的全球网络变成了一台巨大的分布式计算机而 Workers 就是这台计算机上的“进程”。这个标题之所以值得单独拿出来聊是因为它背后对应的是一个非常具体的开发范式转变。传统后端开发你要先买服务器、装系统、配 Nginx、部署应用、做负载均衡、搞容灾一整套下来没个几天搞不定。而“cloudflare-os”这套思路下你写一个export default { async fetch(request, env) { ... } }推上去全球几百个节点同时生效。没有服务器要管没有系统要打补丁没有容量要预估。关键词里出现了 Cloudflare Workers、AI、agent、open source这几个词组合在一起指向的其实是一个很明确的场景用边缘运行时来承载 AI agent 的后端逻辑。Agent 这类应用有个特点——它需要低延迟响应、需要状态管理、需要调用外部模型 API、需要处理流式输出。这些需求恰好和 Workers 的能力边界高度重合。这篇文章适合谁看如果你正在考虑用 Workers 搭一个 agent 服务或者你已经在用但总觉得哪里别扭又或者你只是好奇“边缘计算到底能干什么”那接下来的内容应该能给你一些可以直接抄作业的东西。我会从架构选型、核心组件、实操步骤、踩坑经验几个角度展开尽量把每个“为什么”讲清楚。2. 为什么 agent 场景和边缘运行时是天然一对2.1 Agent 的延迟敏感特性决定了它不适合传统架构Agent 和普通 Web 应用最大的区别在于交互模式。普通应用是“请求-响应”一次就结束agent 往往是多轮对话、工具调用、流式输出。用户发一句话后端可能要调三次模型 API、查两次数据库、执行一次外部工具最后把结果流式吐回来。这个链路里任何一环慢了用户体验就崩了。传统架构下你的服务器可能在美国东部用户在中国光网络往返就 200ms 起步。再加上模型 API 的调用延迟一轮对话下来两三秒是常态。而边缘运行时的逻辑是用户的请求打到离他最近的节点这个节点直接处理逻辑、调用外部 API、返回结果。网络跳数少了首字节时间能压到几十毫秒级别。我实测过一个简单的 agent 对话接口部署在传统 VPS 上国内用户平均首字节时间在 400ms 左右换成 Workers 之后同样的逻辑首字节时间降到了 80ms 上下。这个差距在流式输出场景下感知特别明显——用户打完字几乎立刻就能看到第一个 token 冒出来。2.2 Workers 的运行时限制反而倒逼出更干净的架构很多人第一次看 Workers 的文档会皱眉没有 Node.js 的完整 API、不能用原生 TCP、执行时间有限制、内存有上限。这些限制看起来是缺点但实际用下来它们逼着你把架构做干净。举个例子。传统 Node.js 项目里你可能随手就require(fs)读个文件或者起个后台定时任务。但在 Workers 里这些都不行你必须把状态外置到 KV、D1 或 Durable Objects把定时逻辑交给 Cron Triggers。这个约束看起来麻烦但它带来的好处是你的应用天然就是无状态的、可水平扩展的、不依赖本地磁盘的。部署的时候没有任何“服务器环境差异”的问题本地跑通就是线上跑通。Agent 场景尤其受益于这一点。Agent 的状态对话历史、工具调用记录、用户偏好本来就该外置因为你需要它在多个边缘节点之间共享。Workers 的限制恰好推着你往正确的方向走。2.3 成本模型从“养服务器”到“按请求付费”传统架构下哪怕没人用你的服务器也在烧钱。Agent 应用往往有明显的波峰波谷——白天用的人多凌晨几乎没人。用 VPS 的话凌晨那台机器照样在计费。Workers 的计费是按请求数和 CPU 时间来的。免费额度每天 10 万次请求对于个人项目和小型 agent 完全够用。付费版 5 美元起步包含 1000 万次请求。这个成本结构对独立开发者特别友好——你可以先跑起来等真有流量了再考虑优化。不过这里有个坑要注意Workers 的 CPU 时间限制是 10ms免费版到 30s付费版可配置。Agent 如果涉及复杂的本地计算比如大文本处理很容易撞到这个限制。解决办法是把重计算拆出去或者用 Durable Objects 来承载长任务。3. 拆解 cloudflare-os 的核心组件每个部件解决什么问题3.1 Workers无状态的计算层Workers 是整个体系的入口。你写的每个请求处理逻辑都跑在这里。它支持 JavaScript 和 TypeScript也支持 Rust、Python 等通过 WASM 接入。对于 agent 场景Workers 主要承担三件事接收用户请求、编排工具调用、流式返回结果。一个典型的 agent Worker 长这样export default { async fetch(request, env, ctx) { const { messages } await request.json(); // 调用模型 API const response await fetch(https://api.example.com/v1/chat, { method: POST, headers: { Authorization: Bearer ${env.MODEL_API_KEY}, Content-Type: application/json }, body: JSON.stringify({ messages, stream: true }) }); // 流式转发 return new Response(response.body, { headers: { Content-Type: text/event-stream } }); } };这段代码看起来简单但它背后有几个关键设计env对象承载了环境变量和绑定资源ctx提供了waitUntil用于后台任务流式转发直接透传 ReadableStream 不需要缓冲。这些细节决定了 agent 的响应速度和资源消耗。3.2 Durable Objects给 agent 一个“记忆体”Agent 需要记忆。对话历史、用户状态、工具调用中间结果这些都需要一个地方存。KV 适合读多写少的场景D1 适合关系型查询但如果你需要强一致性、需要每个会话有独立的计算实例那就得用 Durable Objects。Durable Objects 的概念是每个对象有一个全局唯一的 ID所有对这个 ID 的请求都会路由到同一个实例上。这个实例有内存状态、有事务性存储、可以串行处理请求。对于 agent 来说你可以给每个对话会话分配一个 Durable Object它负责维护这个会话的完整上下文。export class AgentSession { constructor(state, env) { this.state state; this.env env; this.history []; } async fetch(request) { const { message } await request.json(); this.history.push({ role: user, content: message }); // 调用模型带上历史 const reply await this.callModel(this.history); this.history.push({ role: assistant, content: reply }); // 持久化 await this.state.storage.put(history, this.history); return new Response(JSON.stringify({ reply })); } }这个模式的好处是会话状态天然隔离不需要担心并发冲突Durable Object 可以保持长连接适合 WebSocket 场景存储是事务性的不会出现写一半失败的情况。3.3 AI Gateway模型调用的统一入口Agent 通常要调多个模型——可能主力用 GPT备用 Claude某些任务用本地小模型。如果每个模型都直连密钥管理、限流、缓存、日志都得自己搞。Cloudflare 的 AI Gateway 就是来解决这个问题的。它做的事情很直接你把它当成一个代理层所有模型请求都发给它它负责转发、缓存、限流、记录。配置方式是在 Workers 里把请求地址换成 Gateway 的地址带上你的 Gateway ID。const response await fetch( https://gateway.ai.cloudflare.com/v1/${env.ACCOUNT_ID}/${env.GATEWAY_ID}/openai/chat/completions, { method: POST, headers: { Authorization: Bearer ${env.OPENAI_API_KEY}, Content-Type: application/json }, body: JSON.stringify({ model: gpt-4, messages }) } );用 Gateway 之后你能在面板上看到每个模型的调用量、延迟分布、错误率。缓存功能对 agent 特别有用——相同的系统提示词和工具定义第二次调用可以直接命中缓存省 token 也省时间。3.4 向量数据库与 RAG 支持Agent 要做知识问答就离不开 RAG。Cloudflare 的 Vectorize 是专门为 Workers 设计的向量数据库。你把文档 embedding 之后存进去查询的时候用相似度检索。整个流程是这样的先用 Workers AI 的 embedding 模型把文档切片向量化存到 Vectorize用户提问时把问题向量化去 Vectorize 查最相似的片段拼到 prompt 里发给模型。// 存入 const embeddings await env.AI.run(cf/baai/bge-base-en-v1.5, { text: chunks }); await env.VECTORIZE.insert(chunks.map((chunk, i) ({ id: chunk-${i}, values: embeddings.data[i], metadata: { text: chunk } }))); // 查询 const queryEmbedding await env.AI.run(cf/baai/bge-base-en-v1.5, { text: [userQuestion] }); const matches await env.VECTORIZE.query(queryEmbedding.data[0], { topK: 5, returnMetadata: true });这套组合的好处是全在 Cloudflare 内部没有跨服务调用的网络开销也不需要额外管理一个向量数据库实例。4. 从零搭一个 agent 后端完整实操路径4.1 环境准备与项目初始化第一步是装 Wrangler这是 Cloudflare 的官方 CLI 工具。npm install -g wrangler wrangler login登录之后创建一个新项目npm create cloudflarelatest my-agent -- --templatehello-world cd my-agent生成的目录结构里src/index.js是入口wrangler.toml是配置文件。你需要在这个配置文件里声明各种绑定name my-agent main src/index.js compatibility_date 2024-01-01 [[kv_namespaces]] binding AGENT_KV id your-kv-id [[d1_databases]] binding AGENT_DB database_name agent-db database_id your-db-id [ai] binding AI [[vectorize]] binding VECTORIZE index_name agent-index [[durable_objects.bindings]] name AGENT_SESSION class_name AgentSession [[migrations]] tag v1 new_classes [AgentSession]这里有个容易忽略的点compatibility_date决定了你能用哪些运行时特性。设得太早新 API 用不了设得太晚可能有行为变更。我的经验是设成当前日期往前推一个月既稳定又能用到大部分新功能。4.2 对话循环的实现细节Agent 的核心是一个循环接收输入 → 判断是否需要调工具 → 执行工具 → 把结果喂回模型 → 生成最终回复。这个循环在 Workers 里实现有几个关键决策点。第一个决策流式还是非流式。流式体验好但实现复杂。我的建议是首版先做非流式跑通逻辑之后再改成流式。流式的核心是把模型的 SSE 流透传给前端中间不要做缓冲。第二个决策工具调用怎么编排。简单场景可以用 if-else 硬编码复杂场景建议用状态机。Workers 里没有现成的 agent 框架但你可以自己写一个轻量的调度器async function runAgent(messages, tools, env) { let maxIterations 5; while (maxIterations-- 0) { const response await callModel(messages, tools, env); if (response.finish_reason tool_calls) { for (const call of response.tool_calls) { const result await executeTool(call, env); messages.push({ role: tool, tool_call_id: call.id, content: JSON.stringify(result) }); } continue; } return response.content; } throw new Error(Agent loop exceeded max iterations); }这个循环里maxIterations是必须的。我踩过一次坑模型陷入死循环反复调用同一个工具请求一直不返回最后撞到 Workers 的 CPU 时间限制被强制终止。加上迭代上限之后至少能保证请求会结束。4.3 状态管理的三种方案对比Agent 的状态存哪里是个需要提前想清楚的问题。我把三种方案列出来对比方案适用场景一致性延迟成本KV读多写少如配置、用户偏好最终一致低低D1关系型查询如对话列表、统计强一致中中Durable Objects会话状态、实时协作强一致低较高我的实际做法是组合使用用户的基本信息放 KV对话的元数据标题、时间、token 数放 D1每个会话的完整上下文放 Durable Object。这样查询列表走 D1 很快进入具体会话时从 DO 加载上下文各取所需。注意Durable Objects 的存储是按读写次数和存储量计费的如果会话很多但每个会话的消息很少用 DO 可能不划算。这种情况下可以把多个会话合并到一个 DO 里管理。4.4 部署与灰度发布Workers 的部署命令很简单wrangler deploy但它支持灰度发布这个功能很多人不知道。你可以在wrangler.toml里配置[env.production] routes [ { pattern api.example.com/*, zone_name example.com } ] [env.staging] routes [ { pattern staging-api.example.com/*, zone_name example.com } ]然后分别部署wrangler deploy --env staging wrangler deploy --env production更细粒度的灰度可以用 Workers 的版本管理功能把 10% 的流量导到新版本观察错误率和延迟没问题再全量。这个在 agent 场景下特别重要因为模型行为有时候会有微妙变化全量发布风险太大。5. 那些文档里不会写的踩坑记录5.1 CPU 时间限制最容易被低估的约束Workers 的 CPU 时间限制和“墙钟时间”是两回事。墙钟时间可以很长比如等模型 API 返回但 CPU 时间只算你实际执行代码的时间。免费版 10ms付费版默认 30s。问题在于JSON 解析、字符串拼接、循环处理这些操作都算 CPU 时间。我有一次处理一个 500KB 的对话历史光是JSON.parse和JSON.stringify就花了 8ms再加上遍历消息做 token 估算直接撞到免费版的限制。解决办法有两个一是升级到付费版30s 的 CPU 时间基本够用二是把重计算拆到 Durable Object 里DO 的 CPU 限制更宽松。我现在的做法是Workers 只做请求转发和轻量编排所有涉及大量数据处理的操作都丢给 DO。5.2 流式响应的缓冲陷阱流式输出最怕的就是中间有缓冲。我一开始的实现是这样的const response await fetch(modelApi); const text await response.text(); // 这里会等全部返回 return new Response(text);这样写完全失去了流式的意义。正确的做法是直接透传 ReadableStreamconst response await fetch(modelApi); return new Response(response.body, { headers: { Content-Type: text/event-stream, Cache-Control: no-cache, Connection: keep-alive } });但这里还有个坑某些中间层比如某些 CDN 或代理会自动缓冲响应。你需要确保Content-Type是text/event-stream并且加上X-Accel-Buffering: no头。Cloudflare 自己的网络不会缓冲但如果你的域名前面还有别的代理就要注意这一点。5.3 环境变量的作用域问题Wrangler 支持在wrangler.toml里定义环境变量也支持用wrangler secret put设置加密变量。我踩过的坑是在本地开发时用.dev.vars文件部署时忘了把变量同步到线上结果线上报错说 API key 未定义。现在的做法是所有敏感变量都用wrangler secret put设置本地开发用.dev.vars并且在 CI 流程里加一步检查确保线上环境的 secret 列表和本地一致。另外wrangler.toml里的[vars]是明文存储的千万不要把密钥写在那里。5.4 模型 API 的超时与重试Agent 调用模型 API 时网络抖动是常态。如果不做超时和重试用户会看到莫名其妙的失败。我的做法是封装一个带重试的 fetchasync function fetchWithRetry(url, options, maxRetries 3) { for (let i 0; i maxRetries; i) { try { const controller new AbortController(); const timeout setTimeout(() controller.abort(), 30000); const response await fetch(url, { ...options, signal: controller.signal }); clearTimeout(timeout); if (response.ok) return response; if (response.status 500) return response; // 客户端错误不重试 } catch (e) { if (i maxRetries - 1) throw e; } await new Promise(r setTimeout(r, Math.pow(2, i) * 1000)); } }指数退避是关键。第一次重试等 1 秒第二次 2 秒第三次 4 秒。这样既能应对临时故障又不会在服务真正宕机时疯狂重试。6. 把 agent 做得更“聪明”的几个进阶思路6.1 用 Workers AI 做本地推理兜底Cloudflare 自己的 Workers AI 提供了不少开源模型比如 Llama 系列、Mistral 系列。这些模型的优势是跑在 Cloudflare 的 GPU 上调用延迟低而且计费是按神经元操作数算的比调外部 API 便宜。我的用法是简单任务意图分类、实体提取、格式转换直接用 Workers AI 的轻量模型复杂任务才调外部的大模型。这样能省不少 token 成本而且响应更快。const intent await env.AI.run(cf/meta/llama-3-8b-instruct, { messages: [ { role: system, content: Classify the user intent into: question, command, chitchat }, { role: user, content: userMessage } ] });6.2 多 agent 协作的编排模式单个 agent 能力有限复杂任务需要多个 agent 协作。在 Workers 里实现这个可以用 Durable Objects 做协调器每个子 agent 是一个独立的 Worker 或者 DO 方法。协调器负责拆解任务、分配子任务、汇总结果。子 agent 之间不直接通信都通过协调器中转。这样做的好处是逻辑清晰每个子 agent 可以独立测试和部署。export class Orchestrator { async fetch(request) { const { task } await request.json(); // 拆解任务 const subtasks await this.plan(task); // 并行执行 const results await Promise.all( subtasks.map(st this.executeSubtask(st)) ); // 汇总 return new Response(JSON.stringify({ result: await this.synthesize(results) })); } }6.3 可观测性日志、指标、追踪Agent 出问题的时候最难的是定位是哪一步出了问题。Cloudflare 提供了 Workers Logs 和 Analytics Engine但默认的日志信息不够细。我的做法是在关键节点打结构化日志console.log(JSON.stringify({ event: model_call, model: gpt-4, input_tokens: countTokens(messages), latency_ms: Date.now() - start, session_id: sessionId }));然后在 Analytics Engine 里建一个数据集把这些日志写进去用 SQL 查询分析。比如查“过去一小时平均延迟超过 5 秒的会话”或者“token 消耗最多的前 10 个用户”。这套东西搭起来要花点时间但一旦 agent 上线有真实用户没有可观测性就是睁眼瞎。7. 关于开源与社区生态的一些观察“cloudflare-os”这个概念能火起来很大程度上是因为开源社区在推动。Workers 本身是闭源服务但围绕它的工具链、模板、框架基本都是开源的。比如 Hono 这个 Web 框架就是专门为 Workers 设计的路由性能比 Express 快一个数量级。Agent 相关的开源项目也在涌现。有做 agent 编排的、有做 RAG 管道的、有做工具集成的。我的建议是核心业务逻辑自己写通用能力尽量用社区方案。比如向量检索、文本分块、token 计数这些没必要重复造轮子。但要注意版本锁定。Workers 的运行时更新比较频繁某些依赖可能在某个版本之后就不兼容了。我的做法是在package.json里锁定精确版本升级的时候先在 staging 环境跑一遍完整测试。另外Workers 的compatibility_date和compatibility_flags要配合使用。有些新特性需要显式开启 flag比如nodejs_compat让你能用部分 Node.js API。这个 flag 开了之后包体积会变大冷启动可能变慢需要权衡。8. 我个人的一些实操体会用 Workers 搭 agent 后端这段时间最大的感受是约束不是敌人是朋友。Workers 的各种限制逼着我把架构想清楚把状态外置把逻辑拆干净。最后写出来的代码比传统 Node.js 项目更简洁部署更省心运维成本几乎为零。另一个体会是不要过早优化。我一开始就想做流式、做多 agent、做 RAG结果每个都做了一半哪个都不稳定。后来退回来先把最简单的“请求-模型-响应”跑通再逐步加功能反而快很多。还有一个很实际的建议本地开发一定要用wrangler dev。它启动一个本地运行时模拟线上的环境包括各种绑定。虽然有些行为比如 Durable Objects 的分布式特性本地模拟不了但大部分逻辑都能在本地验证。配合--remote参数还能连到线上的真实资源调试。最后说一个容易被忽略的点Workers 的冷启动。虽然官方说冷启动在 5ms 以内但实际测下来如果包体积大、依赖多冷启动可能到几十毫秒。对于 agent 这种延迟敏感的场景建议把包体积控制在 1MB 以内依赖能少则少。用 esbuild 做 tree-shaking把没用到的代码都摇掉。这套东西还在快速演进新的绑定、新的模型、新的工具每个月都在出。保持关注官方 changelog 和社区讨论比闷头写代码更重要。

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

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

免费获取报价 →
↑