资讯详情

Cloudflare Workers 上跑 AI Agent:cloudflare-os 架构与实战

📅 2026/10/28 4:12:57 | 华诺云谱 👁 阅读
Cloudflare Workers 上跑 AI Agent:cloudflare-os 架构与实战
1. 从cloudflare-os这个名字说起它到底想解决什么问题第一次看到cloudflare-os这个仓库名我脑子里冒出来的第一个念头是Cloudflare 什么时候开始做操作系统了点进去翻了一圈才反应过来这里的 OS 不是传统意义上管进程、管内存、管文件系统的操作系统而是一个面向 Cloudflare Workers 生态的 AI Agent 运行时骨架。你可以把它理解成给 AI 代理准备的一套工作台——它不负责调度 CPU 时间片它负责的是让一个 AI Agent 能在边缘节点上跑起来、能调用工具、能维护会话状态、能按需扩展。这个定位其实挺有意思。过去一年我接触过不少 AI Agent 项目绝大多数都是跑在本地或者一台常驻服务器上的用 Python 写个循环接上 OpenAI 或者别的模型 API再挂几个工具函数就完事了。这种方案做 demo 很快但一旦要上线给真实用户用问题就来了你得考虑并发、考虑冷启动、考虑会话状态存哪、考虑成本。而 Cloudflare Workers 这套东西恰好在这几个维度上有天然优势——全球边缘节点、毫秒级冷启动、按请求计费、自带 KV 和 D1 这些存储原语。cloudflare-os本质上就是把这些能力打包成一个 Agent 框架让你不用从零搭脚手架。那它适合谁我的判断是三类人第一类是已经熟悉 Cloudflare Workers 开发、想往 AI 方向延伸的前端或全栈工程师第二类是想做轻量级 AI 应用、但不想维护服务器的独立开发者第三类是想研究 Agent 架构、找一个可读性强的开源实现来拆解学习的人。如果你连wrangler是什么都还没搞明白那建议先把 Workers 的基础过一遍再来看这个项目不然会有点吃力。关键词里出现了pnpm、wrangler、Cloudflare Workers、AI、ai agent这几个词基本勾勒出了这个项目的技术栈轮廓用 pnpm 做包管理用 wrangler 做本地开发和部署跑在 Workers 运行时上核心功能是 AI Agent。下面我就按这个脉络把整个项目从环境搭建到核心机制再到实战踩坑完整地捋一遍。2. 环境准备pnpm 和 wrangler 这两个坎必须先过2.1 为什么这个项目偏偏要用 pnpm热词里pnpm出现的频率高得离谱甚至还有pnpm下载失败pnpm离线安装删除pnpm这种词说明很多人在这一步就卡住了。我先说说为什么现代前端/边缘计算项目越来越倾向于用 pnpm 而不是 npm 或 yarn。pnpm 的核心优势在于它的内容寻址存储机制。npm 和 yarn 的做法是每个项目都把依赖复制一份到自己的node_modules里十个项目用同一个 lodash硬盘上就存十份。pnpm 不一样它在全局维护一个存储池所有项目的依赖都通过硬链接指向这个池子同一个版本的包在硬盘上只存一份。对于cloudflare-os这种依赖树可能比较深的项目来说pnpm 能省下大量磁盘空间安装速度也明显更快。另一个关键点是 pnpm 对幽灵依赖的处理更严格。npm 的扁平化node_modules结构会导致你可以引用那些没有写在package.json里的包这在本地开发时可能没事但部署到 Workers 这种严格环境里就会炸。pnpm 用符号链接构建的嵌套结构从根源上杜绝了这个问题让你在本地就能发现潜在的依赖缺失。安装 pnpm 本身不复杂但坑往往出在细节上。Windows 用户如果用npm install -g pnpm装完之后在 PowerShell 里敲pnpm -v报无法将pnpm项识别为 cmdlet八成是 npm 全局 bin 目录没加到 PATH 里。你可以先跑npm config get prefix看看全局目录在哪然后手动把这个路径加进系统环境变量。macOS 和 Linux 用户如果用corepack enable来启用 pnpm 会更省心corepack 是 Node.js 自带的包管理器版本管理工具能自动帮你锁定项目指定的 pnpm 版本。提示如果你所在的环境网络受限pnpm 官方提供了独立的安装脚本和离线包。离线安装的思路是先在能联网的机器上把 pnpm 的二进制和依赖缓存导出再拷贝到目标机器上。具体做法可以参考 pnpm 官方文档的 standalone 安装章节这里不展开。2.2 wrangler 的定位和初始化流程wrangler是 Cloudflare 官方的 CLI 工具你可以把它类比成create-react-app加vercel的结合体——既能帮你初始化项目脚手架又能帮你本地调试和部署上线。对于cloudflare-os这类项目wrangler 基本是绕不开的。初始化一个 Workers 项目的标准流程是这样的# 用 pnpm 创建一个新的 Workers 项目 pnpm create cloudflarelatest my-agent # 进入项目目录 cd my-agent # 本地开发调试 pnpm wrangler dev # 部署到线上 pnpm wrangler deploywrangler dev会在本地起一个模拟 Workers 运行时的环境默认监听localhost:8787。这个本地模拟环境很重要因为 Workers 的运行时和 Node.js 有本质区别——它基于 V8 的 isolate 机制没有完整的 Node.js API很多你在 Node 里习以为常的模块比如fs、path在这里是用不了的。所以本地用 wrangler 调试能帮你提前发现这类兼容性问题而不是等部署上去才报错。wrangler.toml是这个项目的核心配置文件里面定义了 Worker 的名称、入口文件、兼容性日期、环境变量、KV 命名空间绑定、D1 数据库绑定等等。我见过不少人部署失败就是因为compatibility_date设得太老导致一些新的运行时 API 用不了。建议直接设成当前日期除非你有明确的兼容性需求。2.3 依赖安装阶段最容易翻车的几个点热词里pnpm下载失败pnpm安装这些词扎堆出现说明依赖安装是新手的第一道坎。我总结几个高频问题和对应的排查思路。第一个是镜像源问题。默认的 npm registry 在国内访问可能不稳定导致安装超时。解决办法是切换到国内镜像源比如pnpm config set registry https://registry.npmmirror.com。但要注意有些包在镜像源上同步不及时如果遇到某个包死活装不上可以临时切回官方源试试。第二个是Node.js 版本不匹配。cloudflare-os这类项目通常会在package.json里声明engines字段要求特定的 Node 版本。如果你本地是 Node 16 而项目要求 Node 18pnpm 会直接报错。用nvm或fnm这类版本管理工具切换一下就行。第三个是lockfile 冲突。如果项目里同时存在package-lock.json和pnpm-lock.yamlpnpm 可能会警告甚至报错。这时候应该删掉package-lock.json和node_modules重新跑pnpm install生成干净的 lockfile。3. 拆开看核心Agent 在 Workers 上是怎么跑起来的3.1 Workers 运行时对 Agent 架构的约束要理解cloudflare-os的设计必须先理解 Workers 运行时的几个硬约束因为这些约束直接决定了 Agent 架构能怎么设计。约束一没有常驻进程。Workers 是请求驱动的一个请求进来运行时分配一个 isolate 处理处理完这个 isolate 可能就被回收了。这意味着你不能像在服务器上那样起一个常驻的 Agent 循环一直挂着等消息。Agent 的每一次思考都必须绑定到一次请求上。约束二执行时间有限制。免费版 Workers 的 CPU 时间限制是 10 毫秒付费版是 30 秒可配置到更高。这个限制对 Agent 来说是个挑战因为一次 LLM 调用加上工具执行可能就要好几秒。所以 Agent 的设计必须考虑分步执行——把一个大任务拆成多个请求每个请求只做一小步用外部存储来保存中间状态。约束三没有本地文件系统。你不能往磁盘上写文件所有持久化都得走 KV、D1、R2 这些云存储服务。这对 Agent 的记忆管理提出了新要求——会话历史、工具调用记录、中间结果全都得序列化后存到外部。理解了这三条约束你就能明白为什么cloudflare-os的架构看起来和本地 Agent 框架不太一样。它不是简单地把 Python Agent 翻译成 JS而是针对边缘运行时的特性重新设计了执行模型。3.2 会话状态管理KV 和 D1 怎么选Agent 要能记住上下文就必须有地方存会话状态。cloudflare-os里通常会用到两种存储KV 和 D1。KV 是键值存储特点是读快写慢、最终一致。适合存那种读多写少的数据比如用户的偏好设置、Agent 的配置信息。它的读写延迟在全球边缘节点上都很低但写入后需要一段时间才能在全球同步。D1 是基于 SQLite 的关系型数据库支持 SQL 查询适合存结构化的会话历史、工具调用日志这类需要复杂查询的数据。它的写入是一致性的但读延迟比 KV 高一些。我的选型经验是这样的如果只是存一个会话的最近 N 条消息用 KV 就够了key 用session:${sessionId}value 存 JSON 序列化后的消息数组。如果需要按时间范围查询、按用户聚合统计、或者做全文搜索那就上 D1。实际项目里两者经常混用——KV 存热数据做快速读取D1 存冷数据做分析和回溯。这里有个容易踩的坑KV 的写入有频率限制。同一个 key 每秒最多写一次如果你在 Agent 循环里每步都往同一个 key 写状态很容易触发限流。解决办法是把状态按步骤分 key 存比如session:${id}:step:1、session:${id}:step:2或者用 D1 来存高频写入的数据。3.3 工具调用机制的设计取舍Agent 的核心能力之一是调用工具。在cloudflare-os里工具通常被定义成一个个独立的函数每个函数有名称、描述、参数 schemaAgent 根据 LLM 的输出决定调用哪个工具、传什么参数。工具定义的结构大概长这样const tools { getWeather: { description: 查询指定城市的天气, parameters: { type: object, properties: { city: { type: string, description: 城市名称 } }, required: [city] }, execute: async ({ city }) { // 实际调用天气 API const res await fetch(https://api.example.com/weather?city${city}); return await res.json(); } } };这个结构看起来简单但实际设计时有几个关键决策点。决策点一工具执行是同步还是异步在 Workers 环境下工具执行必须是异步的因为任何网络请求都是异步的。但更重要的是你要考虑工具执行超时怎么办。如果一个工具调用卡住了整个 Agent 循环就会挂起。我的做法是给每个工具调用加一个超时包装超过阈值就返回错误信息让 LLM 决定下一步。决策点二工具执行结果怎么回传给 LLM有两种模式一种是把结果直接拼进下一轮的 prompt 里另一种是用 function calling 的标准格式回传。前者兼容性好但 token 消耗大后者更规范但要求模型支持 function calling。cloudflare-os通常两种都支持根据模型能力自动选择。决策点三工具能不能并行调用如果 LLM 一次返回了多个工具调用请求你是串行执行还是并行执行并行能省时间但要注意 Workers 的并发限制和工具之间的依赖关系。我的经验是无依赖的工具调用可以并行有依赖的必须串行这个判断逻辑最好在框架层面处理好不要让每个工具自己操心。4. 从零跑通一个最小 Agent完整实操链路4.1 项目初始化的具体命令和配置假设你现在要从零开始基于cloudflare-os的思路搭一个最小可用的 Agent。我把完整流程拆成几步每步都说明为什么这么做。第一步创建项目。用pnpm create cloudflarelatest会引导你选择模板选 Hello World 类型的 Worker 模板就行后面我们自己加 Agent 逻辑。第二步安装依赖。核心依赖通常包括aiVercel 的 AI SDK提供了统一的模型调用接口、zod做参数校验、hono轻量级 Web 框架比原生 Workers 的 fetch handler 好用。安装命令pnpm add ai zod hono第三步配置wrangler.toml。关键配置项包括name my-agent main src/index.ts compatibility_date 2024-09-23 [[kv_namespaces]] binding AGENT_KV id 你的KV命名空间ID [vars] MODEL_NAME your-model-name这里的compatibility_date建议设成比较新的日期这样能用上最新的运行时特性。KV 命名空间需要先在 Cloudflare 控制台创建然后把 ID 填进来。第四步写入口逻辑。一个最小的 Agent 入口大概是这样import { Hono } from hono; type Env { AGENT_KV: KVNamespace; MODEL_NAME: string; }; const app new Hono{ Bindings: Env }(); app.post(/chat, async (c) { const { sessionId, message } await c.req.json(); // 读取历史 const history await c.env.AGENT_KV.get(session:${sessionId}, json) || []; // 调用模型这里简化处理 const reply await callModel(message, history, c.env.MODEL_NAME); // 更新历史 history.push({ role: user, content: message }); history.push({ role: assistant, content: reply }); await c.env.AGENT_KV.put(session:${sessionId}, JSON.stringify(history.slice(-20))); return c.json({ reply }); }); export default app;这段代码虽然简单但已经包含了 Agent 的核心循环读状态、调模型、写状态。后面所有的复杂功能都是在这个骨架上叠加的。4.2 本地调试时怎么模拟边缘环境wrangler dev默认起的是本地模拟环境但有些行为和生产环境还是有差异。我建议在本地调试时注意几点。第一KV 在本地是存在内存或本地文件里的重启wrangler dev后数据可能丢失。如果你需要持久化的本地 KV可以用wrangler dev --persist参数它会把数据存到.wrangler/state目录下。第二本地环境的网络请求走的是你本机的网络而生产环境走的是 Cloudflare 的边缘网络。这意味着某些依赖特定地理位置的 API 在本地测试时可能行为不一致。如果遇到这种情况可以在wrangler.toml里配置[dev]段落来模拟不同的区域。第三本地调试时日志直接打在终端上但生产环境的日志需要用wrangler tail来实时查看。建议在开发阶段就把日志格式统一好方便上线后排查问题。4.3 部署上线的完整流程和验证方法部署本身就一条命令pnpm wrangler deploy但部署前后的验证工作才是重点。部署前先跑一遍pnpm wrangler deploy --dry-run它会模拟部署过程但不实际上传能帮你提前发现配置错误。然后检查wrangler.toml里的所有绑定是否都正确——KV 的 ID 对不对、环境变量有没有漏、路由规则是否符合预期。部署后第一件事是用wrangler tail看实时日志确认没有报错。第二件事是发一个测试请求验证核心功能是否正常。第三件事是检查响应时间如果明显比本地慢可能是某个外部 API 调用拖了后腿。我踩过的一个坑是部署后忘记更新 KV 的命名空间 ID结果本地测试一切正常线上却读不到任何数据。原因是本地用的模拟 KV 和生产 KV 是两套东西。所以每次部署前我都会对照wrangler.toml和控制台里的资源列表逐项核对一遍。5. 那些文档里不会写的踩坑记录5.1 pnpm 在 CI/CD 环境里的诡异行为本地跑得好好的 pnpm一到 CI 环境就各种报错这是很多人遇到过的场景。我遇到最多的问题是lockfile 版本不匹配。pnpm 的 lockfile 格式在不同大版本之间有变化如果 CI 环境装的 pnpm 版本和本地不一致就可能出现 lockfile 解析失败。解决办法是在package.json里加一个packageManager字段明确指定 pnpm 的版本{ packageManager: pnpm9.0.0 }这样 corepack 会自动使用指定版本的 pnpm避免版本漂移。另外CI 环境里建议用pnpm install --frozen-lockfile这个参数会强制要求 lockfile 和package.json完全一致不一致就直接报错避免出现本地能跑、CI 跑不了的情况。还有一个坑是pnpm 的 store 目录权限问题。在某些 CI 容器里pnpm 的全局 store 目录可能没有写权限导致安装失败。可以通过pnpm config set store-dir把 store 目录指到一个有写权限的位置来解决。5.2 Workers 冷启动和 LLM 调用的时间博弈Workers 的冷启动通常在几毫秒级别这个很快不是问题。真正的问题是LLM 调用的延迟。一次 LLM 调用动辄几秒如果 Agent 需要多轮调用总时间很容易超过 Workers 的执行时间限制。我的应对策略是把长任务拆成多个短请求。具体做法是Agent 每完成一步就把状态存到 KV 或 D1然后返回一个进行中的响应给客户端客户端轮询或者用 WebSocket 接收后续结果。这样每个请求的执行时间都控制在几秒以内不会触发超时。另一个技巧是用流式响应。Workers 支持流式返回你可以在 LLM 生成第一个 token 时就开始往客户端推数据而不是等全部生成完再返回。这样用户感知的延迟会低很多即使总生成时间较长也不会觉得卡。5.3 会话数据膨胀导致 KV 读写变慢Agent 跑久了会话历史会越来越长。如果每次都把完整历史读出来、拼进 prompt、再写回去KV 的读写量会迅速膨胀延迟也会上升。我的做法是分层存储加滑动窗口。最近 N 条消息存在 KV 里做快速读取更早的历史归档到 D1 或 R2 里。每次读取时只取最近 N 条需要回溯时才去查归档。N 的取值根据模型上下文窗口和成本来定一般 10 到 20 条是个比较平衡的值。另外写入 KV 时要注意序列化开销。如果历史记录是复杂的嵌套对象JSON 序列化和反序列化的开销不可忽视。可以考虑用更紧凑的格式或者只存必要的字段。5.4 工具调用失败时的优雅降级工具调用失败是常态不是异常。外部 API 可能超时、可能返回错误、可能限流。如果每次失败都让整个 Agent 崩溃那这个 Agent 就没法用了。我的做法是给每个工具调用包一层错误处理加降级逻辑。工具执行失败时不直接抛错而是返回一个结构化的错误信息给 LLM让 LLM 决定是重试、换工具、还是告诉用户这个功能暂时不可用。这样 Agent 的行为会更鲁棒用户体验也更好。具体实现上可以用Promise.race加超时const result await Promise.race([ tool.execute(params), new Promise((_, reject) setTimeout(() reject(new Error(工具调用超时)), 5000) ) ]).catch(err ({ error: err.message }));这样即使工具卡住5 秒后也会返回错误信息不会无限等待。6. 这套东西还能怎么扩展跑通最小 Agent 之后能扩展的方向其实挺多的。我列几个我觉得比较有价值的。多 Agent 协作。热词里出现了多ai协作这在 Workers 上完全可行。你可以把不同的 Agent 部署成不同的 Worker通过 Service Binding 互相调用。比如一个负责规划、一个负责执行、一个负责审核各司其职。Service Binding 的好处是调用不走公网延迟低且不消耗额外的请求配额。接入更多模型。cloudflare-os本身不绑定特定模型你可以通过 AI SDK 接入多家模型服务。Workers AI 是 Cloudflare 自家的推理服务和 Workers 集成最紧密延迟最低。如果要用外部模型注意把 API key 存在环境变量里不要硬编码在代码中。加持久化记忆。除了会话历史还可以给 Agent 加长期记忆。用 Vectorize 做向量存储把重要的信息 embedding 后存进去需要时做相似度检索。这样 Agent 就能记住跨会话的信息而不是每次对话都从零开始。做可观测性。Agent 的行为比较难调试建议从第一天就加上日志和追踪。每次 LLM 调用、每次工具执行都记一条日志包含输入、输出、耗时、token 消耗。这些数据存到 D1 里后面做分析和优化时非常有用。成本控制。LLM 调用是按 token 计费的Agent 跑起来 token 消耗可能很惊人。建议加一个 token 计数器超过阈值就截断或者降级到更便宜的模型。另外缓存重复的 LLM 调用结果也能省不少钱——如果两个请求的 prompt 完全一样直接返回缓存结果就行。我在实际项目里最深的体会是Agent 的难点从来不是怎么调模型而是怎么管理状态和怎么处理失败。模型调用本身几行代码就搞定了但要让 Agent 在真实环境下稳定运行状态管理、错误处理、成本控制这些脏活累活才是真正花时间的地方。cloudflare-os这类框架的价值就在于它把这些脏活累活封装好了让你能专注于 Agent 的业务逻辑本身。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑