ARTICLE DETAIL

资讯详情

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

Vercel AI SDK 与 AI Gateway 实践:从流式对话到网关缓存与回退

Vercel AI SDK 与 AI Gateway 实践:从流式对话到网关缓存与回退 Vercel AI SDK 和 AI Gateway 是最近很多人问的一对工具组合。它们解决的问题一句话可以讲明白AI SDK 负责让聊天式、流式输出的 AI 功能快速接到你的前端应用里AI Gateway 负责让请求在多个模型服务和多种策略之间走得更稳、更省、更可控。如果你正在用 Next.js、React 这类生态做应用又不想被某个模型厂商的 API 细节绑死这篇文章会比较对胃口。我会从环境准备、最小 Demo、网关接入一直讲到报错排查尽量按实际落地顺序拆开写。整个实践过程中我自己的最大感受是这两个东西单个看都不复杂但很多人把它们理解成同一个东西结果在排查问题时分不清是页面端、服务端还是网关层出了问题。下面先解决这个认知问题再进入实操。1. 先弄清AI SDK 和 AI Gateway 是两层东西不是一个概念1.1 AI SDK 解决的是应用侧“怎么把 AI 能力接进来”Vercel AI SDK 是一套面向 JavaScript / TypeScript 应用的 AI 开发工具。它最常见的价值是把“前端页面里的输入框、消息列表、流式文本渲染”和“后端调用模型接口的逻辑”封装成一套统一写法。举个例子如果没有 SDK你要自己处理用户输入、组装 messages 数组、调用模型接口、解析流式响应、从返回的 chunk 里拼文本、还要处理中断和错误。这个链路第一次写可能觉得不难但一旦要支持流式输出、多轮对话、停止生成、重新生成代码复杂度会立刻上来。AI SDK 里的 useChat、streamText 这类 API就是把这几层重复工作收拢让你把注意力留在业务上。我建议先把它理解成SDK 是站在应用开发者这边的工具库它解决的是“我如何在最快时间内写好一个带 AI 对话功能的应用”。1.2 AI Gateway 解决的是请求该怎么走后端链路AI Gateway 和 AI SDK 不在一个层级。它更靠近模型服务提供方是一个代理层。实际开发中你会遇到这些场景同一个应用要根据业务切换不同模型甚至在同一套代码里做 A/B 对比。多条业务线都调用模型接口需要统一看到谁在调用、消耗了多少 token、响应延迟是多少。某个模型服务出问题或限流时希望请求自动落到备用模型上而不是用户直接看到报错。同一个请求重复发生模型接口返回内容完全一样但每次都要付费、都要等网络时间。AI Gateway 这类代理层就是在模型 API 前面再加一层把统一入口、缓存、模型回退、日志监控、简单的限流这一类通用能力下沉到网关。应用代码不再直接面对某个模型厂商的 baseURL 和 key而是面对你自己的网关地址和网关 key。这时候你会发现 SDK 和 Gateway 的边界其实很清楚SDK 负责你的应用怎么调用Gateway 负责你的调用到了网关之后怎么被转发、缓存、观察和容错。1.3 为什么两件事要先分开理解很多教程会直接给一个“AI SDK 接入 AI Gateway”的示例代码但如果你没分清层遇到问题时会很痛苦。举几个我实际见过的现象前端按钮一直转圈你以为是 useChat 用错了实际是服务端 Route Handler 还没被正确调用。服务端已经收到请求但模型返回 401你以为是 SDK 问题实际是调用的网关地址或者密钥填错了。同一个问题反复问结果每次都一样你以为是模型“记住了历史”实际是网关缓存命中了。换了模型效果变化不大你以为是 prompt 没写好实际是网关缓存一直返回旧模型的结果。这些问题如果一开始就把 SDK 和 Gateway 的职责分开排查路径会短很多。后面每个大问题我会再结合具体表现展开。2. 动手前先准备环境账号、项目、密钥三件套2.1 注册一个 Vercel 免费账号创建测试项目如果你还没有 Vercel 账号可以先去官网注册一个免费账号。注意点有两个一是注册流程本身并不复杂GitHub 账号可以直接授权登录二是免费账号适合做学习测试但在正式批量业务之前最好再去确认一下当前套餐的资源限制和计费规则尤其是想重度使用网关缓存、日志和模型调用时免费档并不等于无限档。创建项目时最简单的路径不是从零初始化一个复杂应用而是先用 Vercel 提供的模板创建或者在本地先建一个 Next.js 项目再通过 Git 仓库导入。我自己的习惯是第一轮测试尽量用最小项目不要一开始就引入 UI 组件库、状态管理、数据库这种额外依赖。AI SDK 相关的排错前置依赖越少越好判断。项目建好后你会得到类似xxx.vercel.app的默认访问域名。这个域名用来验证“部署本身是否成功”是足够的。但要注意默认域名在不同网络环境下的访问速度可能差别很大这个问题通常不是代码问题而是网络链路问题。如果是为了正式业务记得提前规划自定义域名和生产环境部署位置不要等演示时才意识到访问不通。2.2 环境变量别把模型密钥硬编码进页面这是新手最容易踩的坑。调用模型服务和 AI Gateway 都需要密钥。密钥应该放在环境变量里而不是直接写在代码中。在 Next.js 项目里通常需要在项目根目录创建.env.local文件写入类似下面的内容# 这里的内容属于示例占位实际值需要从你的模型服务商或网关控制台获取 AI_GATEWAY_BASE_URL你的网关地址 AI_GATEWAY_API_KEY你的网关密钥字符串别加引号这里写的是示意具体看你在哪个环境里写。.env.local默认不应该提交到 Git 仓库。有一点要特别提醒Next.js 里环境变量分客户端和服务端。凡是要在浏览器端读取的变量必须以NEXT_PUBLIC_开头而模型密钥、网关密钥这类变量只允许在服务端代码中使用。如果你把密钥写进客户端组件等于把密钥直接暴露给所有访问页面的用户。等上了生产环境再发现可能已经产生不必要的费用甚至安全问题。AI Gateway 本身的价值之一就是帮你少暴露真实模型 key。你的应用只需要保存一个“网关侧分配的 key”真实模型服务商的 key 可以留在网关配置里。这样即使前端应用出问题真实模型 key 也不会直接流出去。2.3 本地运行前先把版本依赖看一遍跑示例项目前我建议先确认几项环境信息Node.js 版本是否与企业项目要求匹配常见问题是某台机器 Node 版本太老装新依赖时报 engine 不匹配。Next.js 版本。AI SDK 在不同时期对 Next.js App Router 的支持程度有差异如果你的项目是旧的 Pages Router 结构代码写法要做对应调整。包管理器是否统一。团队里不要一个用 npm、一个用 pnpm、一个用 yarn否则锁文件反复冲突浪费不少时间。这里给的不是精确版本号因为这些依赖更新频率很高。落地时最好的做法是打开官方文档的 Quick Start按照它当前推荐的版本组合安装。不要拿几个月前的博客代码直接跑很多时候报错不是 API 变了而是你用的 SDK 版本和教程里的不一样。安装依赖的基础命令通常是npm install ai ai-sdk/react如果你计划在服务端调用模型可能还需要装对应模型服务商的适配包。实际要装哪些包以你使用的 SDK 文档和模型适配方式为准。3. 用 AI SDK 跑通一个最小流式对话3.1 页面端 useChat 接入先别贪多目标就一个页面上有一个输入框用户输入后能收到一段流式文本回复。在 Next.js App Router 项目里最简单的页面结构可以是这样。这里以客户端组件为例因为 useChat 这个 API 负责管理前端聊天状态use client; import { useChat } from ai-sdk/react; export default function ChatPage() { const { messages, input, handleInputChange, handleSubmit, isLoading } useChat(); return ( div style{{ maxWidth: 600, margin: 40px auto, padding: 0 16px }} div style{{ minHeight: 300, border: 1px solid #ddd, padding: 12 }} {messages.map((m) ( div key{m.id} style{{ marginBottom: 8 }} strong{m.role user ? 你 : 助手}:/strong span{m.content}/span /div ))} /div form onSubmit{handleSubmit} style{{ display: flex, gap: 8, marginTop: 16 }} input value{input} onChange{handleInputChange} placeholder输入消息 style{{ flex: 1, padding: 8 }} / button typesubmit disabled{isLoading} {isLoading ? 生成中 : 发送} /button /form /div ); }代码里没有写任何模型 API 细节。useChat 默认会向当前站点的/api/chat发送 POST 请求并把多轮消息数组传给服务端。这是 AI SDK 给前端带来的直接好处状态管理、消息追加、流式文本接收都已经被封装过一轮。新手最容易在这里犯一个认知错误以为 useChat 真的能“自己调用模型”。实际上它只是帮你组织请求和渲染流式文本真正调用模型的位置在服务端接口里。缺少服务端接口时按钮点击后大概率是报 404 或者一直 loading。3.2 服务端 Route Handler 流转发接下来需要新建一个接口接收前端传过来的 messages然后调用模型服务。在 App Router 中对应目录是app/api/chat/route.ts。一个最小实现可以长这样import { streamText } from ai; // 这里的 createOpenAI 只是示例。 // 不同模型服务商的接入方式以 SDK 当前文档为准。 import { createOpenAI } from ai-sdk/openai; const provider createOpenAI({ baseURL: process.env.AI_GATEWAY_BASE_URL, apiKey: process.env.AI_GATEWAY_API_KEY, }); export async function POST(req: Request) { const { messages } await req.json(); const result streamText({ model: provider(你使用的模型标识), messages, }); return result.toDataStreamResponse(); }这段代码的逻辑是服务端收到前端消息后把它转发给一个“看起来像 OpenAI 服务”的地址。这里我把 baseURL 指向了网关地址模型 key 也使用网关 key。如果一切配置正确模型生成的文本会以流式方式返回给页面。streamText 返回的对象不是普通 JSON而是一个可流式输出的响应体。useChat 能自动识别并解析这种数据流所以页面端无需手动处理 getReader 或 read() 这类底层逻辑。从我个人测试经验看这段代码能不能一次跑通通常取决于三个外部条件网关地址是不是真的可访问。网关 key 是否具有对应模型调用权限。你填写的模型标识符是否在网关配置里存在。如果页面一直不输出先不要怀疑 useChat先 curl 一下服务端接口或看服务端日志确认请求是否真的发出去了。3.3 判断成功和失败别只看“有没有字”最小 Demo 跑通后不要只看“页面能出字”就认为一切正常还要观察几个维度。第一是否是流式输出。如果页面等待很久后一次性出现整段回复说明流式链路可能没生效或者中间走了一层非流式代理。流式输出会让首字出现得更早体验更接近真人打字。第二是否支持多轮。连续发两条消息第二条消息是否把第一条内容作为上下文带给模型。这取决于前端 useChat 维护的消息结构和服务端 messages 透传是否完整。AI SDK 默认会把历史消息一起发送你自己写服务端转发时一定要保持 messages 原样透传不要只取最后一条。第三浏览器网络面板有没有把/api/chat的响应流完整铺开。你可以打开控制台看响应类型是不是text/event-stream或者类似流式类型。如果不是大概率是 SDK 方法或模型服务不支持流式返回。第四加载状态是否正常结束。如果按钮一直处于“生成中”但网络面板已经拿到完整响应那问题多半在前端事件处理或者响应解析上。注意第一次跑 Demo 时不要同时开模型回退、缓存、多路输出这些功能。先把最基础的一条链路跑通再逐步加层。4. 把 AI Gateway 插到中间观察它的生产作用4.1 Gateway 统一入口与 SDK 远程模型配置真正把 AI Gateway 接进来后你的应用代码结构变化通常不大主要变的是模型调用地址和密钥。以前你的服务端代码可能直接连某个模型厂商的接口现在改成连接你的网关地址以前应用里可能保存多个厂商的 key现在只保存一个网关 key。模型的真实服务能力在网关控制台里进行配置。这种结构调整最直接的好处是模型厂商切换不再需要改应用代码。比如今天你的业务用模型 A明天想把默认模型切成模型 B。如果没有网关你需要改服务端代码、重发版本、重新配置密钥。如果有网关你可以在控制台把模型 A 的流量部分或全部指向模型 B甚至做灰度比例。应用代码里填写的始终是网关地址和网关 key。AI SDK 在这里扮演的角色是“统一调用语法”。它允许你通过 provider 适配器来调用不同模型服务而 SDK 层的流式处理、错误处理、消息输出方式可以保持稳定。所以一个比较舒适的架构是页面层使用 AI SDK 的 useChat不关心后端用的什么模型。服务层使用 AI SDK 的 streamText 等 API把模型请求转发给网关。网关层配置真实模型、缓存策略、回退模型、日志采集。4.2 缓存和自动回退是最容易感知的两个收益很多人第一次接触 AI Gateway会觉得它只是一个“换了个 baseURL 的代理”没什么特别。直到业务里出现重复请求或者上游不稳定网关的价值才明显。先看缓存。某些业务场景里用户会频繁触发相同或相似的提问例如在线文档里的“帮我重写这段文案”同一段文案在一天内可能被几十个人改写。没有缓存时每次请求都会消耗一次模型服务额度并等待完整的网络延迟。网关开启缓存后如果请求参数和命中策略匹配可以直接把之前保存的响应返回给应用。缓存带来的收益不是“模型变聪明了”而是“相同问题不必重复付费”。对学习项目来说它让你做接口测试时更从容对生产项目来说它可以明显降低重复调用的成本和延迟。再看自动回退。你的依赖链是应用 → 网关 → 主模型。如果主模型服务出现限流、故障或者特定格式参数不支持网关可以按预设顺序自动切换到备用模型。用户可能只会觉得响应稍慢了一点点或者根本没有感知。需要注意边界回退不是把不同模型的输出强行保证一致。不同模型的输出风格、响应格式、上下文能力都有差别。回退只解决“可用性”问题不解决“完全一致”问题。如果你的业务对输出格式要求极其严格回退后也要通过校验层把关。4.3 日志、token 和成本不能靠猜网关的另一个价值是观测。直接调用模型服务时你要查看一次请求用了多少 token、延迟多高、成本和错误码通常需要登录模型服务商后台。如果应用只调一个模型还好一旦涉及多个模型、多个业务线后台切换成本很高。通过网关后一次请求从进入到转发到返回整条链路的日志可以统一沉淀。实际中我比较关注几个指标请求总数、成功数、失败数。平均首字延迟和完整响应耗时。token 消耗量尤其是缓存命中前后 token 的明显对比。错误码分布例如 401、429、500 各占多少。回退是否频繁发生如果频繁发生说明主模型稳定性或配置可能有问题。不需要一开始就把所有指标做成仪表盘。先跑几天看日志确认哪些接口被大量调用、哪些模型消费占比高再决定是否需要成本告警和限流策略。注意网关缓存能降低重复成本但它不能替代业务层的权限控制和数据脱敏。如果你的应用向模型发送用户隐私数据仍然需要在业务侧做好授权和过滤。5. 从“能跑”到“能用”的几个关键经验5.1 流式输出不是越快越好很多人在刚接入流式输出时会追求“字出得越快越好”。但这里有个容易忽略的点流式输出只是把结果分块传输它不等于最终回答时间变短。如果模型本身响应很快当然体验好。如果模型本身需要几十秒才能跑完流式只是让用户更早看到第一个字用户的等待感知确实会降低但服务端处理时间并没有缩短。真正影响体验的因素还包括首字延迟与模型服务商、网关转发耗时、输入内容长度有关。输出长度长回答即使首字很快也需要较长时间才能完整结束。前端渲染如果你的页面在接收流式文本时做了重渲染并且消息列表很长可能出现输入卡顿。所以判断“流式链路是否健康”不要只看“字有没有蹦出来”还要关注首字延迟、整体耗时和页面渲染性能。5.2 错误处理不是只返回一个错误 JSON把 AI SDK 接入网关后常见的错误不止一种网关拒绝了请求返回 401 或 403。网关到达模型服务但模型服务超时或限流。客户端在生成过程中主动停止。模型返回了格式正确但内容不符合预期的结果。错误处理要分层去做。前端至少要有“加载中”“停止生成”“失败重试”的交互。服务端接口需要能捕获上游错误并把可读信息返回给前端。网关层要记得设置超时和重试策略不能无限等待上游模型。我踩过的一个典型问题前端点击生成后useChat 弹了一个异常但服务端日志里根本没有请求记录。原因是函数组件里某些变量为 undefined请求还没发就已经抛错了。调试时先看浏览器 Network再看服务端日志不要上来就怀疑模型。5.3 边界条件切换模型、内容长度、超时都要单独测不要把“一个模型测试通过”等同于“所有模型都能通过”。真实业务中不同模型的能力边界差异很大上下文长度不同。你发给模型的 messages 如果非常大超出上下文窗口的模型可能直接报错或被截断。支持的输入格式不同。有的模型擅长工具调用有的模型擅长结构化输出但同一个参数不一定能原样通用。审核策略不同。同样一段内容在模型 A 可以通过在模型 B 可能被拦截。建议在接网关时把模型切换这个动作拆成一个个独立测试用例不要只测“能否回复”。我一般会准备几类测试输入一句话提问验证基础连通性。多轮对话验证上下文传递。超长文本验证上下文窗口和耗时。格式要求文本例如“只输出 JSON”验证结构化输出能力。空输入或异常格式验证参数校验。每一类输入都记录是否成功、耗时多少、结果是否稳定、有没有触发缓存或回退。这样你在真正上线时对行为边界有把握。6. 常见报错和排查顺序6.1 看到 401、403 先查 baseURL 和 key这类状态码通常不是模型能力问题而是认证问题。排查顺序先确认页面请求到达了服务端接口。再确认服务端读取环境变量的位置是否正确。然后确认网关地址和网关 key 是否配对。最后检查该网关 key 是否真的具备调用目标模型的权限。非常常见的一种情况是本地环境变量改动了但本地开发服务没有重启进程里还保留旧变量。改完.env.local后务必重启开发服务。如果你用的是 Windows 环境还要注意环境变量字符串中是否有特殊字符某些符号在 shell 中会被转义导致 key 被截断。6.2 页面能打开但回答不动先分前端还是后端这个问题几乎每轮测试都会遇到。我的排查顺序是这样的先看浏览器 Network 面板。如果 POST/api/chat请求为 404说明路由文件路径不对或服务端没有正确导出。如果请求为 200 但一直没有数据再往下查。然后看服务端日志确认 Route Handler 是否真的进入了执行逻辑。如果请求进来了但直接卡住多半是模型调用超时。如果服务端提示请求已经发送给网关但网关日志没有记录那就是应用到网关这一步的网络或地址配置有问题。此时不要继续分析 prompt先把链路通断解决。如果网关有记录但模型响应报错再把网关控制台里的回退状态、上游请求结果打开看真实模型服务返回的错误信息。6.3 网关缓存命中但结果不对时清缓存并核对参数带缓存的系统最容易出现一种“诡异问题”明明代码已经改了 prompt但返回内容还是旧版本。先判断是不是缓存命中。如果连续几次请求完全一样返回时间非常快且网关日志显示 cache hit那基本可以确认是缓存命中。这时候需要看一下缓存 key 的设计。常见网关默认会组合请求参数生成 key。不同的参数组合会影响命中率。如果你的业务希望模型每次都重新输出就需要在网关里关闭对应接口的缓存或者手动清除缓存。排查时要记得缓存造成的错误不是“功能 bug”而是配置与预期不一致。先不要改模型参数先确认当前生效的缓存策略。6.4 部署到 Vercel 后访问、域名和资源限制要提前确认本地跑通之后部署到 Vercel通常会有几类新问题。环境变量在本地正常但部署后不生效最常见原因是在 Vercel 控制台的项目设置里没有添加生产环境变量或者本地.env.local没有同步到平台。默认域名访问不稳定这不是代码逻辑问题。如果业务面向特定地区用户建议把自定义域名、DNS 解析、部署区域这些事项一并规划。Vercel 提供的默认域名适合开发和演示正式对外服务时要按你的目标用户网络环境选择合理部署方式。部署后请求量上来还需要注意平台的函数超时时间、免费额度内资源限制、日志保留时间等因素。不要等线上流量上来才发现服务被限流或日志找不到了。我个人最后建议学习这套组合时先把“单条请求能通”当成第一里程碑再开缓存和回退观察行为变化最后才考虑多模型灰度、成本统计和团队协作。按这个顺序踩坑你会少很多“不知道哪一层出问题”的时刻。这套东西真正好玩的地方不是某一个 API 多神奇而是你亲手把“前端聊天体验、后端模型调用、中间网关容错”三层串起来之后再换模型、做缓存、调策略时改动成本明显变低。把基础链路跑稳剩下的很多玩法都是在这个骨架上长出来的。
返回列表