最近在技术社区里,Vercel AI SDK和AI Gateway是出现频率很高的两个关键词。前者是一套面向大模型应用的开源 TypeScript 工具库,后者则是把模型请求统一收口、缓存、限流、记日志的网关层。很多同学看完官方文档的第一反应是:概念能看懂,但不知道从哪里开始玩。
这篇文章会带大家走一条比较完整的动手路线:先用create-next-app创建一个 Next.js 项目,然后接入 Vercel AI SDK,实现一个可以流式返回结果的聊天接口和页面;接着聊聊 AI Gateway 的核心价值,并自己在项目里实现一个最小可运行版本。零散踩过坑的开发者可以把它当配置手册,刚入门的新手也能照着一步步把 Demo 跑起来。
先补充一句重要提醒:Vercel 官方云端 AI Gateway 产品本身迭代很快,接入方式、免费额度、模型支持列表都可能变化。所以本文的重点放在“网关应该解决什么问题 + 如何自建一个最小可运行实现”上,而不是让你依赖某篇旧文章里的固定 URL 去接入云端产品。
1. 背景与核心概念:AI SDK 和 AI Gateway 分别解决什么问题
1.1 从一个高频痛点说起
做 AI 应用时,很多人第一版代码是这样写的:
const response = await fetch("https://api.xxx.com/v1/chat/completions", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${process.env.API_KEY}`, }, body: JSON.stringify({ model: "xxx", messages }), });单独看没有问题,但项目一多,问题就暴露了:
- 每个模型厂商的请求格式、鉴权方式、返回结构都略有不同,换一个模型就要改一遍调用层代码。
- 如果前端直接调用模型接口,API Key 很容易被暴露在浏览器里。
- 没有统一的缓存、限流、失败重试、请求日志,每次模型调用都像“裸奔”。
- 想把 OpenAI 切换到 Anthropic、Google Gemini 或其他 OpenAI 兼容服务,改造成本很高。
Vercel AI SDK和AI Gateway分别站在不同层解决上面这些痛点。
1.2 Vercel AI SDK:一套统一的模型调用层
通俗理解,Vercel AI SDK 是“模型厂商封装层”的上层统一接口。它把不同大模型服务商的差异封装起来,让你用几乎相同的代码调用不同模型。开发者只需要选择对应的 provider 包,比如@ai-sdk/openai、@ai-sdk/anthropic,再传入模型 ID 即可。
它提供的核心能力包括:
generateText:非流式生成完整文本。streamText:流式生成文本,适合实现打字机效果。generateObject:按 JSON Schema 生成结构化对象。useChat等前端 Hooks:帮助快速接入聊天 UI。- 工具调用(Tool Calling)与 Agent 循环支持。
这套设计让“换模型”变成了一件成本很低的事情,甚至可以把模型 ID 放在环境变量里,运行时动态切换。
1.3 AI Gateway:AI 请求的交通枢纽
AI Gateway 更像是一个位于“应用”和“模型服务商”之间的代理层。它的职责不是帮你写模型调用代码,而是统一管理所有模型请求。
在企业级 AI 应用中,网关通常负责:
- 统一入口:所有模型请求走同一个网关地址,而不是散落在各个服务里。
- API Key 统一管理:网关沉淀密钥,下游应用不需要直接接触各家厂商的 Key。
- 缓存:相同请求直接命中缓存,减少重复计费。
- 限流与配额:避免某个应用或某个用户把预算打爆。
- 日志与观测:记录每次请求的模型、Token 消耗、延迟、错误类型。
- 降级与重试:主模型不可用时,自动切换到备用模型。
Vercel 官方也提供 AI Gateway 云产品,把上述能力托管起来。如果你在看官方资料,建议直接以官方文档为准,避免使用半年前的旧教程。
1.4 两者的关系:不是二选一
AI SDK 和 AI Gateway 解决的是不同层的问题,实际项目中经常配合使用:
浏览器 / App ↓ 你的业务服务(内部调用 Vercel AI SDK 编写代码) ↓ AI Gateway(缓存、限流、日志、统一密钥) ↓ OpenAI / Anthropic / Google / 其他模型服务商这篇文章后续的实战部分,会先用 AI SDK 写一个聊天 Demo,然后自己在 Next.js 里扮演一层“最小 AI Gateway”。
2. 环境准备与版本说明
2.1 前置条件
在开始动手前,你需要准备以下环境:
- Node.js:建议使用 18.18 或更高的 LTS 版本。AI SDK 的最新版本对 Node 版本有一定要求,版本过低会出现依赖安装或运行错误。
- npm / pnpm / yarn:任一包管理器即可,下文以 npm 为例。
- Vercel 账号:可以等部署时再注册,免费账号就够用。
- 一个大模型 API Key:本文以 OpenAI 兼容接口为例。如果你暂时没有官方 Key,也可以使用国内或海外提供 OpenAI 兼容接口的服务,但要注意合规与服务条款。
注意,Node.js 和 Next.js 的具体版本变化较快,本文示例不会把某一个版本写死。你会发现下面的代码在较新的 Next.js App Router 项目中可以直接运行;如果跑不起来,优先检查你的框架和 AI SDK 是否处于同一个大版本。
2.2 初始化 Next.js 项目
在终端执行:
npx create-next-app@latest ai-gateway-lab命令执行后,交互式提示一般会让你选择 TypeScript、ESLint、Tailwind CSS、src 目录等选项。建议按下面的组合选择:
| 选项 | 推荐选择 |
|---|---|
| TypeScript | Yes |
| ESLint | Yes |
| Tailwind CSS | 随意,本文代码不依赖它 |
| App Router | Yes |
| src directory | Yes |
| Turbopack | 可选 |
这样会生成一个使用 App Router 的现代 Next.js 项目。进入项目目录:
cd ai-gateway-lab2.3 项目最终结构预览
本文会创建以下文件,你可以先有一个整体印象:
ai-gateway-lab/ ├── .env.local └── src/ └── app/ ├── api/ │ ├── hello-ai/ │ │ └── route.ts │ ├── chat/ │ │ └── route.ts │ └── gateway/ │ └── route.ts ├── layout.tsx └── page.tsx后面每个文件都会给出完整代码。
3. AI SDK 核心用法拆解
3.1 Provider:先选择你的模型服务商
Vercel AI SDK 把“模型服务商”抽象成provider。不同厂商需要安装不同的 provider 包,例如:
npm install @ai-sdk/openai如果要用 Anthropic,就安装@ai-sdk/anthropic;如果要用其他模型服务,可以看官方 provider 文档,不建议凭记忆猜包名。
@ai-sdk/openai默认导出已经配置好的openai实例,最基础的用法是:
import { generateText } from "ai"; import { openai } from "@ai-sdk/openai"; const result = await generateText({ model: openai("gpt-4o-mini"), prompt: "用一句话解释什么是 Vercel AI SDK", }); console.log(result.text);这里的openai("gpt-4o-mini")表示“使用 OpenAI 兼容服务商下的 gpt-4o-mini 模型”。你看到的generateText是 AI SDK 提供的一个顶层函数,作用是让模型生成完整文本。
3.2 流式输出与普通输出的区别
普通接口用generateText,等服务端完整生成后再一次性返回。但聊天应用更常见的体验是“打字机式输出”,也就是一个字一个字往外蹦。这时候应使用streamText:
import { streamText } from "ai"; import { openai } from "@ai-sdk/openai"; const result = streamText({ model: openai("gpt-4o-mini"), prompt: "给我讲一个程序员冷笑话", }); // 在 Next.js 路由中通常返回给前端使用 return result.toUIMessageStreamResponse();注意,streamText返回的不是普通字符串,而是一个流式结果对象。服务端必须通过特定方法把这个流交给网络层,前端才能一段段收到内容。
3.3 useChat:前端的聊天 Hook
如果你不打算自己维护 messages 数组和流式解析逻辑,可以使用 AI SDK 提供的前端 HookuseChat。
它帮你封装了下面这些繁琐环节:
- 维护消息列表
messages。 - 管理输入框内容
input。 - 发送请求并逐步接收流式文本。
- 暴露加载状态
isLoading和停止方法stop。
在最新版本中,React 相关 Hook 从@ai-sdk/react导出。如果你看到旧文章写import { useChat } from "ai/react",说明那篇文章使用的是旧版本 AI SDK。
我们会在下一节把这三个核心知识点串成一个完整 Demo。
4. 实战:从 0 到 1 做一个流式聊天 Demo
4.1 安装依赖
在项目根目录安装 AI SDK 相关依赖:
npm install ai @ai-sdk/openai @ai-sdk/react安装完成后,可以打开package.json看一下版本。只要你使用的是 AI SDK 最新大版本,下面代码基本通用。
4.2 配置环境变量
创建.env.local文件,写入你的模型配置:
OPENAI_API_KEY=你的_API_Key OPENAI_MODEL=gpt-4o-mini # 如果你使用 OpenAI 兼容接口,可以在这里覆盖 baseURL # OPENAI_BASE_URL=https://你的兼容服务地址/v1配置说明:
OPENAI_API_KEY:模型服务商提供的密钥。这个文件不要提交到 Git 仓库。OPENAI_MODEL:默认模型 ID,方便以后切换而不改代码。OPENAI_BASE_URL:当服务商提供 OpenAI 兼容接口时使用。如果你直连 OpenAI,不需要配置这一项,因为@ai-sdk/openai默认会指向 OpenAI 官方地址。
在 Next.js 中,只有以NEXT_PUBLIC_开头的环境变量才会暴露给浏览器。OPENAI_API_KEY没有这个前缀,所以它只存在于服务端,这是保证 Key 不泄露的基本前提。
4.3 第一个接口:非流式调用 generateText
先创建一个最简单的接口,验证环境变量和模型调用链路是否正常。
文件路径:src/app/api/hello-ai/route.ts
import { generateText } from "ai"; import { openai } from "@ai-sdk/openai"; export async function POST(req: Request) { const body = await req.json(); const prompt = String(body.prompt ?? "你好"); try { const result = await generateText({ model: openai(process.env.OPENAI_MODEL ?? "gpt-4o-mini"), prompt, }); return Response.json({ text: result.text }); } catch (error) { console.error("generateText 调用失败:", error); return Response.json( { error: "模型调用失败,请检查 API Key 与模型 ID" }, { status: 500 } ); } }启动本地开发服务器:
npm run dev再用 curl 模拟一次请求:
curl -X POST http://localhost:3000/api/hello-ai \ -H "Content-Type: application/json" \ -d '{"prompt":"用一句话介绍你自己"}'预期会返回类似下面的 JSON:
{ "text": "我是一个人工智能助手,可以回答问题、编写代码并提供学习建议。" }实际文本由模型生成,内容不一定完全一致。只要能拿到 JSON,说明环境配置成功。
这也是一个非常方便的“无头测试”方式:不需要打开浏览器,用 curl 就能确认模型服务商、Key、prompt 是否能正常连通。
4.4 编写流式聊天接口
generateText适合工具脚本和非交互式场景。聊天页面需要流式输出,所以我们要写一个新的路由,使用streamText。
文件路径:src/app/api/chat/route.ts
import { openai } from "@ai-sdk/openai"; import { streamText } from "ai"; // Vercel Serverless 环境最长执行 30 秒;本地开发时该配置不影响 export const maxDuration = 30; export async function POST(req: Request) { const { messages } = await req.json(); const result = streamText({ model: openai(process.env.OPENAI_MODEL ?? "gpt-4o-mini"), system: "你是一位耐心的技术助手。回答请使用中文,并尽量条理化,避免冗长。", messages, }); return result.toUIMessageStreamResponse(); }这段代码里有三个关键点:
messages由前端发送过来,是完整的对话历史,模型需要依据上下文回答。system用来设定 AI 的人设或回答规则。toUIMessageStreamResponse()是 AI SDK 推荐在较新版本中配合useChat使用的响应方法,能把流式结果转换为前端可以消费的协议格式。
如果你使用的是旧版 AI SDK 3.x,这里大概率没有toUIMessageStreamResponse方法,需要参考官方迁移文档调整。
4.5 编写聊天页面
接下来把前端页面替换成聊天界面。
文件路径:src/app/page.tsx
"use client"; import { useChat } from "@ai-sdk/react"; export default function ChatPage() { const { messages, input, handleInputChange, handleSubmit, isLoading, stop } = useChat({ api: "/api/chat", }); return ( <main style={{ maxWidth: 720, margin: "0 auto", padding: 24, fontFamily: "sans-serif", }} > <h1>Vercel AI SDK 流式聊天 Demo</h1> <div style={{ minHeight: 320, border: "1px solid #e5e7eb", borderRadius: 8, padding: 16, }} > {messages.length === 0 ? ( <p style={{ color: "#6b7280" }}>先和模型打个招呼吧。</p> ) : ( messages.map((m) => ( <div key={m.id} style={{ marginBottom: 12 }}> <div style={{ fontWeight: 600, color: m.role === "user" ? "#2563eb" : "#111827", }} > {m.role === "user" ? "你" : "AI"} </div> <div style={{ whiteSpace: "pre-wrap", lineHeight: 1.7 }}> {m.content} </div> </div> )) )} </div> <form onSubmit={handleSubmit} style={{ display: "flex", gap: 8, marginTop: 16 }} > <input value={input} onChange={handleInputChange} placeholder="输入消息后回车" style={{ flex: 1, padding: "8px 12px", borderRadius: 6, border: "1px solid #d1d5db", }} /> <button type="submit" disabled={isLoading || !input.trim()} style={{ padding: "8px 16px", borderRadius: 6, border: "none", background: "#2563eb", color: "#fff", cursor: "pointer", }} > {isLoading ? "思考中…" : "发送"} </button> <button type="button" onClick={stop} disabled={!isLoading} style={{ padding: "8px 12px" }} > 停止 </button> </form> </main> ); }这里主要依赖useChat提供的能力:
messages:聊天记录数组。input、handleInputChange:输入框绑定。handleSubmit:提交表单并调用/api/chat。isLoading:是否正在等待模型响应。stop:手动中断当前流式响应。
4.6 运行与验证
在浏览器打开:
http://localhost:3000输入一句话并发送,你应该能看到文本像打字机一样逐字出现。打开浏览器 DevTools 的 Network 面板,还能看到/api/chat的响应是text/event-stream类型的流式数据。
到这一步,你已经完成了一个最小可用的 AI SDK 聊天应用。
如果想测试不同模型,只需要修改.env.local里的OPENAI_MODEL,然后重启开发服务器即可。你会发现业务代码完全不用动,这就是 provider 抽象带来的收益。
5. 手写一个最小 AI Gateway 并试玩
5.1 为什么需要 AI Gateway
上面的 Demo 直接调用了模型服务商。单机开发没问题,但一旦系统有多个服务、多个团队、多个模型,就会出现几个经典问题:
| 问题 | 直接调用模型时的表现 |
|---|---|
| 密钥分散 | 每个服务都要保存模型厂商 API Key,泄露面大 |
| 重复计费 | 多个用户问同一个热门问题时,每次都真实调用模型 |
| 无统一日志 | 出了问题难以定位是哪个服务、哪个模型、什么参数导致的 |
| 无法限流 | 某个异常流量可能打爆当天的预算 |
| 切换供应商困难 | 需要修改每个调用方的代码 |
AI Gateway 的解决思路就是在业务代码和模型服务商之间增加一层代理。所有请求先到网关,网关