资讯动态

Cloudflare边缘运行时如何支撑AI Agent:架构、状态管理与工程实践

发布时间:2026/10/28 3:21:02 来源:尧图企业网站定制
1. 从cloudflare-os这个标题说起它到底在指什么第一次看到cloudflare-os这个词我下意识以为是某个操作系统的发行版代号。翻了一圈资料才反应过来它并不是一个真正意义上的操作系统内核而是社区里对把 Cloudflare 全家桶Workers、Durable Objects、KV、R2、D1、Queues、AI 等当成一套分布式运行时来用这种思路的戏称。换句话说它描述的是一种以边缘节点为底座、以 Worker 为进程、以各类绑定binding为系统调用的编程范式。这个视角一旦建立起来很多原本零散的概念就串起来了。传统后端里你有进程、有文件系统、有数据库连接池、有消息队列在cloudflare-os这套心智模型里Worker 就是你的进程Durable Object 是有状态的单例进程KV 是最终一致的键值存储R2 是对象存储D1 是 SQLite 形态的关系库Queues 是消息总线Workers AI 则是内置的推理能力。你写的不是部署到服务器上的应用而是跑在全球边缘网络里的函数集合。为什么这个标题值得单独拿出来聊因为它背后对应的是 AI Agent 开发里一个非常现实的痛点Agent 需要低延迟、需要状态、需要工具调用、需要并发编排而传统一台服务器 一个数据库的架构在全球化场景下会迅速变成瓶颈。Cloudflare 这套东西恰好把边缘 状态 存储 推理打包在了一起于是就有了cloudflare-os这种说法——把它当成一个分布式的、无服务器的、面向 Agent 的操作系统来用。这篇文章适合谁看如果你正在做 AI Agent、正在纠结 Agent 的状态放哪、工具怎么编排、并发怎么控、成本怎么压或者你只是听说过 Workers 但不知道它和 Agent 能怎么结合那这篇内容会对你有用。我会从架构心智、核心组件、Agent 落地、踩坑经验几个角度把cloudflare-os这套玩法拆开讲清楚。需要说明的是下面涉及具体实现的部分有些是基于官方文档的确定信息有些是基于常见工程实践的合理补充我会尽量标注清楚。2. 把边缘运行时当成操作系统核心组件的心智模型2.1 Worker 是进程但不是你想的那种进程在传统操作系统里进程有独立内存空间、有生命周期、可以被 kill。Worker 更像是一个事件驱动的、无状态的、随时可能被回收的函数实例。它没有常驻内存没有全局变量可以跨请求可靠保留同一 isolate 内可能短暂复用但你不能依赖它。这一点是理解整套架构的地基。我见过太多人第一次写 Worker 时习惯性地在模块顶层写一个let counter 0然后期望它跨请求累加。结果本地测试没问题一上线数据就乱。原因很简单请求可能被路由到全球任意一个边缘节点每个节点又有多个 isolate你的全局变量根本不在同一个世界里。所以正确的姿势是Worker 只负责无状态的请求处理逻辑所有需要跨请求保留的东西全部外置到绑定资源里。这就像写一个纯函数式的服务状态永远通过参数传入、通过返回值传出而不是藏在闭包里。2.2 Durable Object 才是真正的有状态进程如果说 Worker 是无状态进程那 Durable ObjectDO就是这套系统里最接近传统进程的东西。每个 DO 有一个全局唯一的 IDCloudflare 保证同一个 ID 的请求会被路由到同一个实例并且这个实例是单线程串行处理请求的。这意味着你终于可以放心地做累加、做锁、做会话状态了。DO 的串行特性是把双刃剑。好处是你不用再操心并发写冲突坏处是如果你的 DO 里有一个慢操作后面所有请求都得排队。我在一个 Agent 项目里就踩过这个坑把一次外部 API 调用平均 800ms直接放在 DO 的请求处理路径里结果高峰期这个 DO 的吞吐直接掉到个位数 QPS。后来改成DO 只做状态协调慢操作丢给 Worker 异步执行问题才解决。提示Durable Object 适合做协调者而不是执行者。把需要串行化的状态变更放进去把耗时的 IO 和计算放到外面。2.3 KV、R2、D1、Queues四种存储各司其职很多人一上来就问我该用 KV 还是 D1其实这个问题本身就问错了。它们不是互相替代的关系而是对应不同的访问模式。我整理了一张对照表这是我实际选型时用的判断依据组件一致性模型典型延迟适用场景不适用场景KV最终一致写后读可能读到旧值边缘读取极快配置、feature flag、缓存、会话 token需要强一致的计数、库存R2强一致对象级中等大文件、模型权重、日志归档、图片高频小对象读写D1强一致单区域主库中等偏高关系型数据、需要 JOIN 的业务表超高频写入、全球低延迟读Durable Object强一致单实例串行低同区域计数器、锁、会话、实时协作大规模并行读这张表里最关键的一行是 KV 的最终一致。KV 的写入要经过一段时间才能在全球所有边缘节点生效官方给的预期是通常几秒最长可达 60 秒。如果你用 KV 存用户余额然后做扣减那基本等于给自己埋雷。这种场景必须上 DO 或者 D1。2.4 Workers AI 与 Vectorize把推理和检索也放进内核cloudflare-os这个说法之所以成立很大程度是因为它把 AI 推理也做成了系统调用。Workers AI 让你在 Worker 里直接env.AI.run()调用托管模型不用自己搭 GPU 集群Vectorize 则是向量数据库专门给 RAG 场景用。这两个东西的价值在于它们和计算在同一张网络里。传统 RAG 架构里你的应用服务器要跨网络去调向量库、再去调模型 API每一跳都是延迟。而在边缘运行时里检索和推理可以就近完成这对 Agent 的响应速度是实打实的提升。当然Workers AI 的模型选择相对有限复杂推理任务还是得外接大模型 API这点要有心理预期。3. 用这套架构搭一个 AI Agent从需求到落地3.1 先想清楚 Agent 的状态该放哪Agent 和普通 API 最大的区别是它有记忆、有工具、有多轮循环。所以搭 Agent 的第一步不是写代码而是回答一个问题这个 Agent 的状态生命周期有多长我把常见状态分成三类对应不同的存储选择单次对话内的临时状态当前轮的工具调用结果、中间推理步骤放在单次 Worker 请求的内存里就行请求结束就丢。跨轮次的会话状态对话历史、用户偏好用 Durable Object每个会话一个 DO 实例天然串行、天然隔离。长期知识文档、向量、用户档案向量走 Vectorize结构化数据走 D1大文件走 R2。这个划分看起来简单但它决定了你后面所有的架构决策。我见过一个项目把所有状态都塞进 KV结果对话历史经常丢帧——因为 KV 的最终一致性导致刚写入的消息下一轮读不到。换成 DO 之后立刻就好了。3.2 一个最小可用的 Agent 骨架下面这个骨架是我实际项目里简化出来的展示了 Worker DO AI 的典型协作方式。注意看 DO 是怎么做会话隔离的// Worker 入口负责路由和鉴权 export default { async fetch(request, env) { const url new URL(request.url); const sessionId url.searchParams.get(session); if (!sessionId) return new Response(missing session, { status: 400 }); // 用 sessionId 定位到唯一的 DO 实例 const id env.AGENT_SESSION.idFromName(sessionId); const stub env.AGENT_SESSION.get(id); return stub.fetch(request); } }; // Durable Object负责会话状态和 Agent 循环 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 }); // Agent 循环推理 - 工具调用 - 再推理 let reply await this.runAgentLoop(); this.history.push({ role: assistant, content: reply }); // 持久化DO 的 storage 是强一致的 await this.state.storage.put(history, this.history); return Response.json({ reply }); } async runAgentLoop() { // 这里调用 Workers AI 或外部模型 const result await this.env.AI.run(cf/meta/llama-3-8b-instruct, { messages: this.history }); return result.response; } }这段代码里有两个关键点值得展开。第一idFromName(sessionId)保证了同一个会话永远路由到同一个 DO这是会话一致性的基础。第二this.state.storage是 DO 内置的持久化存储它是强一致的写进去立刻能读到这点和 KV 完全不同。3.3 工具调用怎么编排才不乱Agent 的工具调用是最容易写乱的地方。我的经验是把工具定义成纯函数把编排逻辑和工具实现分离。工具本身只关心输入什么、输出什么不关心谁调用它、什么时候调用。const tools { async searchDocs(query, env) { const embedding await env.AI.run(cf/baai/bge-base-en-v1.5, { text: [query] }); const matches await env.VECTORIZE.query(embedding.data[0], { topK: 5 }); return matches.matches.map(m m.metadata.text); }, async getWeather(city) { // 外部 API 调用 const res await fetch(https://api.example.com/weather?city${city}); return res.json(); } };然后在 Agent 循环里根据模型的输出决定调哪个工具。这里有个坑不要让模型自由发挥工具名一定要用枚举约束。我早期版本让模型自己拼工具名结果它经常拼出search_docs、searchDocuments、SearchDocs各种变体导致调用失败。后来改成在 prompt 里明确列出可用工具名并在代码里做白名单校验稳定性立刻上来了。3.4 并发和限流边缘架构下反而更要小心很多人以为上了边缘就自动解决了并发问题其实不然。边缘解决的是请求分发的问题但你的下游资源D1、外部 API、DO依然有容量上限。我在一个项目里遇到过这样的情况Agent 需要调用一个第三方 API那个 API 有每分钟 60 次的限制。上线第一天流量不大没事第二天做推广瞬间几百个并发请求打过去第三方直接返回 429整个 Agent 全挂。解决办法是用 DO 做一个全局令牌桶。因为 DO 是单实例串行的天然适合做限流器export class RateLimiter { constructor(state) { this.state state; this.tokens 60; this.lastRefill Date.now(); } async fetch() { const now Date.now(); // 每分钟补充令牌 const elapsed now - this.lastRefill; if (elapsed 60000) { this.tokens 60; this.lastRefill now; } if (this.tokens 0) { return new Response(rate limited, { status: 429 }); } this.tokens--; return new Response(ok); } }这个模式我在多个项目里复用非常稳。核心思路就是凡是需要全局串行化的东西都往 DO 里塞。4. 那些文档里不会写的坑我的踩坑实录4.1 CPU 时间限制不是墙钟时间但一样会咬人Workers 有一个CPU 时间限制免费版是 10ms付费版是 30s可配置。注意这是CPU 时间不是墙钟时间。也就是说你在等网络 IO 的时候不计入但你在做 JSON 解析、加密、循环计算的时候会计入。我踩过的坑是一个 Agent 需要对一段长文本做分块和 embedding 预处理纯 JS 的字符串处理在大文本上直接爆了 CPU 限制。报错信息是Error: Worker exceeded CPU time limit但日志里看不出是哪一步超的。后来我一段一段加计时才定位到是分块逻辑。解决办法有两个一是把重计算拆成多个 Worker 调用每个调用只做一小块二是把重计算挪到 DO 里DO 的 CPU 限制更宽松。我最后选了方案二因为 DO 天然适合做这种后台处理。4.2 冷启动和 isolate 复用别指望内存缓存前面提过全局变量不可靠这里再强调一个相关的坑不要用内存做缓存。我见过有人写了个const cache new Map()放在模块顶层期望它跨请求命中。本地开发时确实能命中因为本地是单进程一上线命中率接近零。正确的缓存姿势是用 Cache API 或者 KV。Cache API 适合缓存 HTTP 响应KV 适合缓存计算结果。如果你需要读多写少、允许短暂不一致的缓存KV 是最省心的选择。4.3 D1 的写入瓶颈它不是为高频写设计的D1 底层是 SQLite单区域主库。这意味着所有写入都要打到主库写入吞吐有上限。我在一个项目里用 D1 存 Agent 的调用日志每条请求写一行结果高峰期写入延迟飙升甚至出现超时。后来改成日志先写 KV最终一致无所谓再异步批量同步到 D1问题解决。这个思路可以推广高频写入用 KV 或 Queues 缓冲低频查询用 D1。D1 的定位是关系型查询不是高吞吐写入。4.4 本地开发和线上行为的差异wrangler dev本地跑起来很爽但它和线上有几个关键差异不注意就会本地好好的线上一堆问题本地是单进程全局变量、内存缓存都会看起来能用线上不行。本地 KV 是内存模拟没有最终一致的延迟线上有。本地 DO 是单实例线上会分布到多个节点。本地没有真实的边缘路由请求延迟和线上完全不同。我的建议是任何涉及状态、并发、一致性的逻辑都要在 staging 环境真实边缘验证一遍不能只信本地。4.5 成本陷阱请求数不是唯一计费维度Cloudflare 的计费维度比想象中多请求数、CPU 时间、DO 的持续时间、KV 的读写次数、D1 的行读写、R2 的存储和操作、AI 的 neuron 消耗。我见过一个项目请求数不多但因为 DO 长时间持有连接比如做 WebSocket 长连接DO 的 duration 费用反而成了大头。所以做成本预估时一定要把所有维度都算进去。特别是 Agent 场景一次对话可能触发多次模型调用、多次工具调用、多次存储读写单次成本比普通 API 高一个数量级。建议上线前先用小流量跑一周看清楚真实的成本结构再放量。5. 从能跑到好用Agent 场景的进阶优化5.1 流式输出用户体验的分水岭Agent 的响应往往要几秒甚至十几秒如果等全部生成完再返回用户会以为卡死了。流式输出SSE 或 WebSocket几乎是 Agent 的标配。在 Workers 里做流式输出核心是用TransformStream把模型的流式响应透传出去const stream await env.AI.run(cf/meta/llama-3-8b-instruct, { messages: this.history, stream: true }); return new Response(stream, { headers: { Content-Type: text/event-stream } });这里有个细节如果你在 DO 里做流式要注意 DO 的请求处理是串行的一个长连接会占住这个 DO。所以流式输出最好放在 Worker 层做DO 只负责状态管理。这个分工我在多个项目里验证过是最稳的。5.2 多 Agent 协作用 Queues 做消息总线当你的系统从单 Agent 进化到多 Agent 协作时同步调用会迅速变成一团乱麻。这时候 Queues 就派上用场了。一个 Agent 完成任务后把消息丢进队列另一个 Agent 消费队列继续处理彼此解耦。我做过一个研究 写作的双 Agent 系统研究 Agent 负责检索和整理资料写作 Agent 负责成文。两者通过 Queue 通信研究 Agent 把结构化资料丢进队列写作 Agent 消费后生成文章。这样即使写作 Agent 临时故障消息也不会丢恢复后继续处理。5.3 可观测性没有日志的 Agent 等于黑盒Agent 的决策链路很长出问题时如果只有最终结果不对这一条信息根本没法排查。我的做法是在每个关键节点打结构化日志模型输入、模型输出、工具调用参数、工具返回、状态变更。这些日志先写 KV再批量同步到 R2 做长期归档。排查问题时我通常按这个顺序看先看用户输入和最终输出确认问题现象再看模型输出确认是模型决策错了还是工具执行错了最后看工具调用参数确认是参数传错了还是工具本身有问题。这个链路走下来90% 的问题都能定位。5.4 安全边界Agent 能调用的工具必须白名单Agent 安全是个大话题但在cloudflare-os这套架构下最实际的一条是工具调用必须白名单 参数校验。不要让模型自由决定调用什么、传什么参数。我见过有人让 Agent 直接执行模型生成的 SQL结果模型生成了一个DROP TABLE虽然是在测试环境但也够吓人的。正确的做法是工具是预定义的函数参数经过 schema 校验危险操作删除、修改、外部调用需要额外的权限检查。这套机制在边缘运行时里实现起来并不复杂但能挡掉绝大多数意外。6. 我对cloudflare-os这套玩法的真实看法用了大半年下来我的整体判断是它非常适合做 Agent 的骨架但不适合做 Agent 的全部。适合的部分边缘分发、会话状态、工具编排、轻量推理、存储分层这些它都做得不错而且开发体验确实比传统服务器 数据库轻快很多。一个 Agent 从想法到上线用这套东西可能只要几天。不适合的部分重计算、复杂模型推理、大规模关系型查询、需要精细控制的并发场景。这些还是得外接专门的服务。把它当成操作系统来理解是对的但别忘了操作系统也需要外设。最后分享一个我踩过的最大的坑不要因为边缘架构看起来简单就跳过架构设计。我早期有个项目觉得 Workers 这么轻量直接上手写结果状态散落在 KV、DO、内存三处后期想加个功能发现到处都要改。后来痛定思痛先把状态放哪、谁负责什么画清楚再动手写代码效率反而高了一倍。如果你正准备用这套东西做 Agent我的建议是先花半天时间把状态模型想清楚再花半天把工具白名单和限流做好剩下的就是水到渠成的编码工作。这套架构的上手门槛不高但用好它需要对无状态和最终一致这两个概念有真正的体感这个只能靠实际踩坑来建立。

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

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

免费获取报价 →
↑