ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

用Cloudflare Workers免费搭建AI聚合网关:模型路由与部署实战

用Cloudflare Workers免费搭建AI聚合网关:模型路由与部署实战 AI 聚合网关最近在开发者圈里讨论得很热闹。简单说就是把多个模型厂商的 API 收拢到一个统一入口后面对外只暴露一个 OpenAI 兼容接口调用方不需要关心背后到底接的是哪家服务。Cloudflare 的 Workers 和 Pages Functions刚好能把这套网关免费部署在云上所以很多个人开发者会选择拿它做自己的 AI 网关。这个项目我实际搭过之后最大的感受是门槛比想象中低但要做得稳定坑也不少。免费额度足够个人折腾但如果你用的是“一键部署完就扔一边”的思路后面大概率会因为环境变量、模型前缀、流式请求这些东西返工多次。下面按我自己落地的顺序拆一遍。先看它到底解决什么问题再动手搭最小版最后聊部署、验证和生产化要考虑的边界。1. 先理解 AI 聚合网关到底在解决什么问题1.1 它不是模型平台而是一个统一路由入口很多人第一次看到“AI 聚合网关”这个词会以为它是另一个大模型平台。实际上它完全不做模型训练也不存知识库不做向量检索更不生成内容。它做的事情更像一个路由器和门卫接收客户端请求。判断这个请求应该发往哪个模型厂商。从服务端读取对应厂商的 API Key。把请求转发过去。把上游响应原样返回。如果只有一个模型厂商这个网关基本没有存在价值。但当你同时要接 OpenAI、Anthropic、Google Gemini或者国内几家大模型服务时问题立刻变复杂。没有网关的时候调用方要面对的是每个厂商一个 API Key。每个厂商一套 baseURL 和鉴权方式。每个厂商返回结构和错误格式都不同。想切换模型经常要改代码。有网关之后调用方只需要知道一个地址、一个网关 Token然后用统一的 OpenAI 兼容格式发请求。至于这个请求最终是发给哪家、用什么 Key、做不做重试全部由网关处理。所以一句话总结聚合网关的价值不在“模型”而在“管理和分发”。1.2 统一成 OpenAI 兼容接口后客户端改动最小现在很多开源工具比如各类聊天客户端、知识库项目、自动化脚本都已经支持自定义 OpenAI 兼容接口的 baseURL 和 API Key。这意味着只要你把网关暴露成 OpenAI 兼容格式这些工具几乎不需要改代码就能接入。我比较推荐用模型名前缀来做路由。例如openai:gpt-4o-mini表示走 OpenAI 上游。anthropic:claude-...表示走 Anthropic 上游。google:gemini-...表示走 Google 上游。网关拿到模型名后先按冒号拆分前面的部分是 provider 名后面的部分是真正要发给上游的模型名。这样做的好处是可以直接透传大部分请求体不需要为每个模型单独做字段映射。缺点是不同厂商的模型上下文、参数支持范围不一样有些参数会被上游忽略或报错。如果只想快速跑通这个方案最省事。如果要在生产环境用建议再加一层参数白名单或参数转换把不适用的字段去掉。2. 为什么把网关放在 Cloudflare而不是自己维护服务器2.1 免费额度和免运维是最大优势标题里说的“白嫖”本质就是使用 Cloudflare 的免费额度。对个人网关来说Workers 的免费方案通常够用但注意“通常够用”不等于“无限够用”。我建议把官方控制台显示的配额当作唯一标准。不同时期、不同账号可用的免费额度可能会有调整。一般个人测试、内部小范围调用每天跑几百上千次请求不会有什么压力。但如果你打算把请求量跑到几万甚至十万级或者每个请求都是长时间流式输出就要提前关注 CPU 时间和请求数限制。Workers 的另一个优势是免运维。代码部署上去之后Cloudflare 帮你处理节点调度和基本扩容不需要自己装环境、盯进程、做备份。对个人项目来说这部分省下来的时间比服务器本身更值钱。2.2 自己架服务器要面对哪些事自己买一台服务器看起来更“可控”但实际要处理的事情不少安装运行时和依赖。配置反向代理、HTTPS。写 systemd 服务保证进程挂了能自动重启。定期打补丁防扫描。看日志、盯磁盘、盯内存。流量稍微上来一点还要考虑带宽成本。这些工作不是不能做而是对“只想聚合几个模型接口给内部用”的场景来说成本太分散。Cloudflare Workers 把这些操作压缩成了几个名词写代码配置环境变量部署看日志。你不需要关心机器在哪个机房也不需要处理凌晨三点进程崩溃的问题因为大部分运行逻辑都封装在平台里。2.3 Workers 和 Pages Functions 怎么选如果只是做一个纯 API 网关我推荐 Workers。它的入口就是一个 fetch event直接处理所有 HTTP 请求结构最简单。如果你后面还想加一个网页配置后台比如在浏览器里修改模型路由、看调用统计那 Pages Functions 更适合。Pages 可以同时托管静态页面再用 Functions 提供 API 接口两者天然长在一起。从底层能力上看两者都依赖 Cloudflare 的边缘函数运行时很多代码能直接复用。区别主要在工程结构和部署方式。我实际选择的是 Workers。原因是这个网关最核心的交互就是 curl 和 SDK 请求不需要管理界面。如果你想要“带后台的一体化方案”Pages 更合适但入口函数规范要按 Pages Functions 的写法调整。3. 动手前需要准备的环境与前置条件3.1 账号、工具链和依赖开始之前先把下面几样准备好Cloudflare 账号。GitHub 账号虽然不是必须但后续做自动部署会用到。Node.js 环境建议 18 或更高版本。npm 或 pnpm 包管理器。wrangler 命令行工具。wrangler 是 Cloudflare 官方提供的命令行工具本地调试、部署、查看日志都要用到它。可以全局安装也可以在项目里用 npx 调用。npm install -g wrangler然后登录wrangler login登录后会在浏览器里打开授权页面确认后本机就绑定了你的 Cloudflare 账号。除了这些还需要准备你要接入的各家模型服务的 API Key。这里必须强调一点只使用你有权限、合法购买或申请来的官方 API 服务不要把别人的 Key 或者来路不明的接口地址拿来做测试。网关是帮你管理密钥不是帮你绕过授权。3.2 最小工程目录与配置我的习惯是先搭一个最小的工程结构再往里填代码。目录大概长这样ai-gateway/ src/ index.js wrangler.toml package.json .dev.vars .gitignorewrangler.toml 是 Worker 的配置文件至少要有 name、main、compatibility_date。name ai-gateway main src/index.js compatibility_date 2024-01-01.dev.vars 是本地开发时用的环境变量文件里面存放.dev.vars例如GATEWAY_TOKENtest-gateway-token UPSTREAM_OPENAI_KEY你的openai_key UPSTREAM_ANTHROPIC_KEY你的anthropic_key这个文件一定不要提交到 Git 仓库。.gitignore 里加上.dev.vars .env3.3 环境变量和密钥边界先想清楚很多人部署完发现网关一直返回 401大部分原因是环境变量没配置对。上面代码里UPSTREAM_OPENAI_KEY这类变量是上游厂商的密钥只能放在服务端绝对不能下发到浏览器或客户端。客户端访问网关时只需要知道一个网关 Token也就是GATEWAY_TOKEN。逻辑是这样客户端请求时带Authorization: Bearer GATEWAY_TOKEN。Worker 收到请求后校验这个 Token。校验通过后再从环境变量里取对应的上游 Key。上游 Key 不会返回给客户端。这样的设计至少保证了两件事客户端不需要知道上游密钥换上游 Key 时也不用改客户端。如果某个上游 Key 泄露或需要轮换直接在 Cloudflare 控制台改环境变量即可。4. 写一个可运行的最小 AI 聚合网关4.1 请求从进入到返回的完整链路先把这个处理流程写清楚后面看代码就不会乱客户端向网关注入POST /v1/chat/completions请求。Worker 读取 Authorization 头校验网关 Token。解析请求体里的 model 字段。根据 model 前缀找到对应的上游配置。从环境变量读取上游 Key。把请求转发给上游厂商保留 stream 参数。把上游响应体原样返回。这个流程里最关键的是第四步和第六步。模型路由决定请求去哪流式转发决定客户端能不能实时看到输出。4.2 上游路由和模型映射我建议用一个配置对象来管理上游const PROVIDERS { openai: { baseUrl: https://api.openai.com/v1, keyEnv: UPSTREAM_OPENAI_KEY, }, anthropic: { baseUrl: https://api.anthropic.com/v1, keyEnv: UPSTREAM_ANTHROPIC_KEY, }, google: { baseUrl: https://generativelanguage.googleapis.com/v1, keyEnv: UPSTREAM_GOOGLE_KEY, }, };这里要注意不同厂商的接口路径、请求格式、鉴权头不一定完全一样。比如 OpenAI 的/v1/chat/completions和 Anthropic 的/v1/messages差异就很大。如果你要让网关内部直接替代这些差异就不是简单透传能解决的需要为每个厂商写一层适配。个人使用的话我的建议是先只用 OpenAI 兼容格式做最小验证跑通后再接其他厂商。前期不要追求所有模型都能转先把一个链路打通后面加适配才有参照。4.3 密钥注入、流式转发和超时处理转发请求时要做两件事第一把请求的 Authorization 头替换成上游 Key。不能拿客户端的网关 Token 直接请求上游否则上游会拒绝。第二保留上游返回的 Content-Type。如果上游返回的是text/event-stream说明这是流式响应直接返回upRes.body给客户端客户端就能收到 SSE 数据流。超时处理容易被忽略。上游模型响应时间可能很长尤其是流式输出。如果你在 Worker 里设置一个非常短的超时客户端可能刚收到第一行内容请求就被掐断了。我的做法是非流式请求设置 60 秒超时。流式请求不建议用固定短超时可以放宽到 120 秒甚至更长。重试逻辑要谨慎。上游已经返回错误时可以重试一次但如果已经返回部分流式内容绝对不要重试。如果固定超时时间太长又会占用 Worker 的 CPU 配额。所以这个参数需要按你的实际场景调不能照搬网上任何人的数值。4.4 一个简化版的网关核心代码下面是一个可运行的最小骨架。注意这是简化版不是开箱即用的全功能网关。const PROVIDERS { openai: { baseUrl: https://api.openai.com/v1, keyEnv: UPSTREAM_OPENAI_KEY, }, anthropic: { baseUrl: https://api.anthropic.com/v1, keyEnv: UPSTREAM_ANTHROPIC_KEY, }, }; export default { async fetch(request, env) { const gatewayToken env.GATEWAY_TOKEN; if (!gatewayToken) { return new Response(gateway token not configured, { status: 500 }); } const auth request.headers.get(Authorization) || ; if (auth ! Bearer ${gatewayToken}) { return new Response(unauthorized, { status: 401 }); } const url new URL(request.url); if (url.pathname ! /v1/chat/completions) { return new Response(not found, { status: 404 }); } let body; try { body await request.json(); } catch (error) { return new Response(invalid json, { status: 400 }); } const model body.model || ; const providerName model.split(:)[0]; const provider PROVIDERS[providerName]; if (!provider) { return new Response(unknown provider: ${providerName}, { status: 400, }); } const upstreamKey env[provider.keyEnv]; if (!upstreamKey) { return new Response(upstream key not configured: ${providerName}, { status: 500, }); } const upstreamBody { ...body, model: model.split(:).slice(1).join(:), }; const upstreamUrl ${provider.baseUrl}/chat/completions; const controller new AbortController(); const timer setTimeout(() controller.abort(), 60000); try { const upstreamResponse await fetch(upstreamUrl, { method: POST, headers: { Content-Type: application/json, Authorization: Bearer ${upstreamKey}, }, body: JSON.stringify(upstreamBody), signal: controller.signal, }); return new Response(upstreamResponse.body, { status: upstreamResponse.status, headers: { Content-Type: upstreamResponse.headers.get(Content-Type) || application/json, }, }); } finally { clearTimeout(timer); } }, };这段代码解决了最核心的链路网关 Token 校验、模型路由、上游密钥注入、响应转发。但还缺少 CORS、日志、限流、错误结构化、模型参数清理这些内容。先跑通这段等于把地基打好了。5. 一键部署到 Cloudflare 的完整流程5.1 用 wrangler 从本地部署最快在项目根目录执行npx wrangler deploy第一次执行时wrangler 会读取wrangler.toml然后把你本地代码部署到 Cloudflare Workers。部署成功后终端会输出一个workers.dev结尾的地址这个就是你的网关入口。部署后还要配置环境变量。普通配置可以写在 wrangler.toml 的[vars]里但敏感信息建议用 secret 方式wrangler secret put GATEWAY_TOKEN wrangler secret put UPSTREAM_OPENAI_KEY这样 Token 和上游 Key 不会出现在代码仓库中控制台里显示为加密状态。5.2 绑定 GitHub 仓库后自动构建部署如果不想每次改代码都手动执行命令可以把工程推到 GitHub然后在 Cloudflare 控制台做 Git 集成。Pages 的 Git 集成比较成熟但 Pages Functions 的入口写法和 Workers 稍微不同。Pages Functions 需要把入口文件放到functions目录下并且导出onRequest方法而不是默认的fetch。如果你的代码已经在 Workers 上跑通了最快的 CI 方式不是改入口而是写一个 GitHub Actions在 push 时调用 wrangler 部署。这样代码不改结构也能做到自动发布。GitHub Actions 里几件事都要做好安装依赖、配置CLOUDFLARE_API_TOKEN、执行wrangler deploy。这个方式适合已经熟悉 GitHub Workflow 的开发者。如果完全不想碰 CI最省事的就是本地手动部署。对个人项目来说足够了。5.3 环境变量、域名和管理页面部署完成后环境变量建议优先在 Cloudflare Dashboard 里确认一遍。我见过很多人本地跑得好好的部署后却报错结果发现是环境变量没填到线上环境。Cloudflare Dashboard 的 Workers 页面里可以单独为每个 Worker 配置变量和 Secret。Secrets 会加密显示普通变量明文可见。建议把GATEWAY_TOKEN和所有上游 Key 都放 Secrets不要放普通变量。默认的workers.dev域名可以直接用。如果你有自己的域名可以在控制台里添加自定义域。添加后 Cloudflare 会自动处理 DNS 和证书不需要自己配置 HTTP 路由。还有一个很实用的操作给网关加一个/health路由返回简单的状态信息方便排查服务是否活着。6. 部署完成后怎么验证网关真的可用6.1 先用 curl 跑一条非流式请求部署完成后第一件事不是接客户端而是用 curl 跑一条最简单的请求。curl https://your-gateway.workers.dev/v1/chat/completions \ -H Authorization: Bearer your-gateway-token \ -H Content-Type: application/json \ -d { model: openai:gpt-4o-mini, messages: [ { role: user, content: 你好请回复ok } ] }这里的模型名是openai:gpt-4o-mini网关会去掉openai:前缀把模型名转成上游真实名称。成功响应应该有两个特征HTTP 状态码 200返回体里有choices数组。如果看到“unauthorized”说明网关 Token 不对如果看到“unknown provider”说明模型前缀没匹配上。我建议把这条 curl 命令保存成一个 shell 文件后面每次改代码、改配置都要重跑一遍。6.2 再验证流式输出和错误返回流式请求是最容易出问题的地方。验证时加一个 stream 参数并用-N保持连接。curl -N https://your-gateway.workers.dev/v1/chat/completions \ -H Authorization: Bearer your-gateway-token \ -H Content-Type: application/json \ -d { model: openai:gpt-4o-mini, stream: true, messages: [ { role: user, content: 讲一个小故事 } ] }正确的结果是多次返回data:开头的文本块最后一行是data: [DONE]。如果你看到一次性返回整个 JSON说明 stream 参数没有透传成功。如果连接建立后长时间没有数据先确认上游本身是否支持该模型再检查超时设置。还需要验证错误返回。把 model 改成unknown:test应该看到 400 或 404而不是 500。如果网关返回 500说明你的错误处理有漏洞很可能把状态码和错误信息吞掉了。6.3 客户端 SDK 通过网关切换不同模型curl 跑通后再用真实 SDK 测一遍。很多 AI 应用支持自定义 OpenAI 兼容 baseURL所以你可以用最简单的 OpenAI Python 客户端来做验证。from openai import OpenAI client OpenAI( api_keyyour-gateway-token, base_urlhttps://your-gateway.workers.dev/v1, ) resp client.chat.completions.create( modelopenai:gpt-4o-mini, messages[ {role: user, content: 用一句话介绍自己} ], ) print(resp.choices[0].message.content)这里有一个容易踩的坑Python SDK 会把base_url和后面的路径拼接所以网关入口必须能正确响应/v1/chat/completions这个路径。如果你的网关路径写错了请求会一直 404但代码看起来没有任何问题。想切换模型时把 model 改成anthropic:claude-...或google:gemini-...再配合对应的上游配置即可。客户端代码不需要改这个体验就是聚合网关最大的价值。7. 从个人能用走向多用户稳定还要补哪些设计7.1 限流、配额和缓存该怎么取舍个人自用不需要太复杂的限流。一旦网关要共享给几个人或者部署到公网上就必须考虑限流。Cloudflare 免费方案里的限流能力有限。如果要做精确的按用户限流通常需要 Durable Objects 或外部存储这可能会涉及额外成本。对个人项目来说我建议先做两个保守措施网关 Token 不要泄露定期轮换。在代码里做一个简单的内存请求计数防止单客户端短时间打爆上游。但要知道在 Workers 的边缘分布式环境下内存计数不精确。它只能挡住一部分明显异常流量不能当作正式的多租户限流方案。缓存也是一样。如果你想缓存相同请求的结果KV 可以做但免费 KV 的写入次数很有限不能每请求都写。更适合的做法是只对高重复、低延时的请求做缓存并且控制写入频率。7.2 失败重试与上游降级网关里做重试要小心。上游返回 429 表示限流马上重试大概率还是 429反而会加重请求压力。更合理的做法是退避重试或者等一小段时间再试。超时场景下可以考虑降级。比如 OpenAI 上游超时了如果配置里有备用 provider就自动切换过去。这个逻辑听起来不复杂但实际操作时要注意请求是否已经部分写回给客户端如果还没有返回任何内容降级是安全的如果已经返回了部分流式内容就不能再切上游了只能把连接断开。免费额度下重试和降级都会额外消耗 Worker 的请求数和 CPU 时间。所以不要写一个无上限的重试循环。我的建议是至多重试一次失败后返回上游的真实错误。7.3 多用户隔离、统计与日志如果多人共用网关不要让大家共用同一个网关 Token。可以给每个用户分配一个 Token在网关里记录 Token 对应的用户信息。日志记录要克制。不要记录完整请求体和上游 Key建议只记录请求时间。用户标识或 Token 前缀。模型名。上游名称。响应状态。耗时。输入输出 token 数。有了这些字段你就能回答两个关键问题哪些用户在调用、哪个模型最耗钱。成本统计需要上游返回 usage 信息。不同厂商 usage 结构不一样所以网关里要做一层统一转换。这一步很花时间但比手工看各家控制台方便得多。持久化又是一个问题。KV 适合低频读取不适合高并发写入。个人使用频率低时可以在 KV 里攒着量大了就要引入数据库或日志服务。8. 免费额度边界与常见问题排查8.1 免费额度不是无限额度Cloudflare 的免费方案在个人项目里很够用但边界必须清楚。我建议把这些维度当成唯一判断标准维度个人常见感受需要注意的点请求数量日常测试够用不要在高并发下长时间跑CPU 时间普通短请求没问题长时间流式输出会消耗更多KV 操作读多写少可以接受不要每个请求都写 KV存储空间小配置足够日志和缓存要定期清理具体数值要以 Cloudflare 控制台显示的配额为准因为我实测和网上资料经常有出入官方也可能会调整策略。原则是一样的先看配额再决定要不要批量跑。8.2 常见问题排查顺序网关出问题时不要急着改代码。按这个顺序排查通常能定位到大部分问题。先看 Worker 日志。Cloudflare Dashboard 有实时日志也可以本地执行wrangler tail。用 curl 请求/health确认服务是否活着。检查 Authorization 头确认网关 Token 是否正确。检查请求体里的 model确认前缀是否匹配。检查环境变量确认上游 Key 是否配置、是否有空格。直接请求上游确认上游本身是否正常。检查超时时间确认是不是响应太慢被掐断。很多报错表面上是网关代码问题实际都是环境变量、模型名拼写、Key 前后空格这些低级问题。先做手工验证再动代码。8.3 几个高频报错的判断标准现象大概率原因先做什么401 unauthorized网关 Token 没配置或不对检查 GATEWAY_TOKEN400 unknown providermodel 前缀不匹配检查 PROVIDERS 配置500 上游错误上游 Key 或 baseURL 有问题直接调上游接口验证流式没有输出超时太短或上游流式不稳定缩短测试内容放宽超时CORS 报错浏览器跨域问题网关增加 CORS 响应头CPU limit exceeded免费 CPU 配额耗尽降低并发或升级方案这些判断标准可以当成一份速查表。遇到问题先看现象再找对应原因不要从头到尾读一遍代码。9. 最后复盘这个方案适合谁不适合谁9.1 适合的场景和用户如果你是个人开发者想在自己项目里同时接入多个模型厂商并且不想维护一堆 Key 和调用地址那这个方案很适合。它成本低、部署快、改动小尤其适合学习 AI 应用开发和 API 网关设计。小团队内部做测试也可以用它。把统一入口给前端或后端同事他们就不用关心每个模型厂商的调用差异。只要模型名约定好切换模型只是改一个字符串而已。还想更省事的可以在这个基础上加一个简单的配置页面。但前提是先用最小版跑通不要第一版就把后台、数据库、统计全塞进去。9.2 不适合的场景和用户高并发生产环境不适合把这个免费方案当成正式依赖。不是说 Cloudflare Workers 不能跑生产而是免费额度和分布式限流能力有限。如果业务对 SLA 有严格要求或者需要精确的按用户限流、详细的审计、复杂成本分摊那还是需要额外投入。像长时间流式并发、大批量任务调度免费方案的 CPU 时间很容易被打满。也不要指望“免费”等于“零成本维护”。代码上线后仍然要看日志、盯配额、更新依赖、轮换 Key。只是这些运维工作比自建服务器轻很多。9.3 一周做下来的经验沉淀最后留几个我自己排查时优先看的点也算这周踩坑的笔记第一先用最小样例跑通。很多人第一版就想把 OpenAI、Anthropic、Google 全部接上结果一路在一个厂商的鉴权细节上卡住。不如先接一个上游把请求链路走通再扩展。第二不要急着调并发和大参数。免费额度下先跑单条请求再跑流式最后再考虑批量。每一步都要确认日志、响应和错误返回都正常。第三环境变量和模型名是最大的坑源。代码逻辑往往没有错错的是 Key 没有配置、模型前缀写错、上游环境变量名和代码里不一致。第四真正的成本不是搭建而是后续维护。一周搭出来不难难的是持续观察请求量、上游变更和配额损耗。这个项目最值得保留的东西不是那几行代码而是“先做最小版再逐步加固”的思路。等你想把它变成生产级网关时这套思路会帮你避开很多返工。
返回列表