一位金融科技客户在 2023 年年底给我打电话,声音里充满恐慌,因为他的开发团队花了六周时间在 OpenAI API 之上构建流式聊天界面,结果在 mobile Safari 上仍然无法正常工作。竞态条件。Token 分块问题。UI 会在响应中途冻结。六周。我看了他们的代码库,正是你能想象的样子:手写的 ReadableStream 实现、用于流式 token 的定制状态管理层、从三年前的 Stack Overflow 答案复制来的重试逻辑。整个东西是用胶带粘在一起的。ReadableStream implementation, a bespoke state management layer for the streaming tokens, retry logic copied from a three-year-old Stack Overflow answer. The thing was held together with tape.
我用 Vercel AI SDK 在大约两天内重写了核心部分。流式处理工作了。Mobile Safari 工作了。客户停止打电话了。Vercel AI SDK in about two days. Streaming worked. Mobile Safari worked. The client stopped ringing.
这不是推销。这只是发生的事实。这就是为什么我想走一遍这个 SDK 实际上做什么、它在哪里真正为你节省时间,以及你仍然会碰到的问题。
Vercel AI SDK 究竟是什么
人们听到"Vercel AI SDK",会假设它被锁定在 Vercel 基础设施中。其实不是。你可以在任何 Node.js 服务器上运行它,包括你自己的 VPS、Railway、Render,随便哪里。它实际上是:一个 TypeScript 库,把构建 LLM 驱动功能到 Web 应用中最混乱的部分抽象掉。
有两个主要的包你会用到。ai 是核心运行时。@ai-sdk/openai(或 @ai-sdk/anthropic、@ai-sdk/google 等)是提供商适配器。SDK 使用统一接口,所以从 GPT-4o 切换到 Claude 3.5 Sonnet 在大多数情况下真的只需要改一行代码。我测试过了。它确实有效。ai is the core runtime. @ai-sdk/openai (or @ai-sdk/anthropic , @ai-sdk/google, etc.) are the provider adapters. The SDK uses a unified interface so switching from GPT-4o to Claude 3.5 Sonnet is genuinely a one-line change in most cases. I've tested this. It holds up.
核心原语
SDK 的核心有三个东西:
streamText,逐个 token 流式传输文本响应,非常适合聊天界面, streams a text response token by token, ideal for chat interfacesgenerateText,等待完整响应,适合后台任务或不需要流式 UI 的情况, waits for the full response, good for background jobs or when you don't need streaming UXgenerateObject,返回根据 Zod schema 验证的结构化 JSON,这就是真正有趣的地方, returns structured JSON validated against a Zod schema, which is where things get properly interesting
streamText 是大多数人首先想要的。它处理 ReadableStream 的管道、编码和分块。我提到的那个六周的金融科技噩梦?这正是 streamText 在第一天就能解决的问题。 is what most people want first. It handles the ReadableStream plumbing, the encoding, and the chunking. That six-week fintech nightmare I mentioned? That's exactly what streamText would have solved on day one.
毫不费力地设置它
假设你在 Next.js 14 或 15 上使用 App Router(如果你在 2025 年读到这个,你可能就是这样),设置真的很快。
- 安装包:npm install ai @ai-sdk/openai
npm install ai @ai-sdk/openai - 在 app/api/chat/route.ts 中创建一个路由处理器
app/api/chat/route.ts - 导入 streamText 和你的提供商,传入你的模型和消息,将结果返回为 StreamingTextResponse
streamTextand your provider, pass in your model and messages, return the result as aStreamingTextResponse - 在客户端,使用来自 ai/react 包的 useChat 钩子
useChathook from theai/reactpackage
就这样。如果你对 Next.js 相当熟悉,一小时内就能有一个可用的流式聊天。useChat 钩子管理消息数组、加载状态、输入字段绑定和流式更新。你不需要自己写任何这些东西。useChat hook manages the message array, the loading state, the input field binding, and the streaming updates. You don't write any of that yourself.
我会立即添加到任何生产环境中的东西:一个适当的系统提示词、速率限制(我用 Upstash 做这个,他们的 Redis 后端速率限制器与边缘函数配合得很好),以及某种 token 使用日志记录。SDK 在响应对象上暴露了使用数据,所以你可以把提示 token 和完成 token 记录到你的数据库,无需任何额外的 API 调用。Upstash for this, their Redis-backed rate limiter plays nicely with Edge Functions), and some kind of token usage logging. The SDK exposes usage data on the response object, so you can log prompt tokens and completion tokens to your database without any extra API calls.
generateObject 是你忽视的功能
实话实说:streamText 抢占了所有头条,但 generateObject 是改变了我如何架构 AI 功能的那个。streamText gets all the press but generateObject is the one that's changed how I architect AI features.
想法很简单。你定义一个 Zod schema,将其传给 generateObject,SDK 会指示模型返回符合它的 JSON。你得到的是一个类型化对象,而不是一个你必须解析和祈祷的字符串。generateObject, and the SDK instructs the model to return JSON that conforms to it. You get back a typed object, not a string you have to parse and pray over.
Seahawk 去年有一个为房产管理公司做的项目。他们想从上传的租赁文件中提取结构化数据——租户名称、租金金额、中止条款、续期日期。旧方法是:提示模型、得到文本、写一个正则解析器、哭。有了 generateObject,我们用 Zod 定义了一个 LeaseSchema,模型每次运行都返回干净的类型化数据。我们在一周内就把提取的租赁文件推送到了 Postgres 表中。generateObject , we defined a LeaseSchema with Zod, and the model returned clean typed data on every run. We were pushing extracted leases into a Postgres table within a week.
让人困惑的事情是:不是所有模型都同样支持结构化输出。GPT-4o 和 Claude 3.5 Sonnet 可靠地处理它。某些较小或较老的模型会幻觉字段或完全忽略 schema。坚持使用旗舰模型做这个,至少在你验证了你的使用案例之前。
工具调用:真正的力量所在
如果 generateObject 被低估了,那么工具调用则是被主动误解了。大多数开发者认为"工具调用"意味着 AI 可以浏览互联网或运行代码。有时确实如此。但实际上,工具就是你定义的函数,模型可以在响应过程中选择调用。generateObject is underused, tool calling is actively misunderstood. Most developers think "tool calling" means the AI can browse the internet or run code. Sometimes it does. But in practice, tools are just functions you define that the model can choose to invoke during a response.
如何理解它
假设你正在为一个 SaaS 产品构建支持机器人。用户问"我当前的订阅计划是什么?"模型不可能知道这个。但你可以定义一个 getUserSubscription 工具,它接收用户 ID 并从数据库返回计划数据。模型识别了意图,调用了工具,获得了数据,并将其纳入响应。用户只看到一个连贯的答案。getUserSubscription tool that takes a user ID and returns the plan data from your database. The model recognises the intent, calls the tool, gets the data back, and incorporates it into the response. The user just sees a coherent answer.
SDK 的 tools 参数在 streamText 和 generateText 上接收一个对象,每个键是工具名称,每个值有一个 description(模型用它来决定何时调用),一个 parameters schema(还是 Zod),以及一个 execute 函数。execute 函数在服务器端安全运行,远离客户端。tools parameter on streamText and generateText takes an object where each key is a tool name, and each value has a description (which the model uses to decide when to call it), a parameters schema (Zod again), and an execute function. The execute function runs server-side, safely, away from the client.
我用这种方式构建过多步骤智能体。SDK 支持 maxSteps,所以模型可以链式调用工具,调用一个工具,使用结果,调用另一个工具,综合一切,然后响应。这不是魔法。你仍然需要仔细考虑工具描述和系统提示。但所有的接线工作都为你处理好了。maxSteps so the model can chain tool calls, call one tool, use the result, call another tool, synthesise everything, respond. It's not magic. You still need to think carefully about your tool descriptions and your system prompt. But the wiring is all handled for you.
中间件、包装和管道模型
直到我深入阅读文档后才意识到的一件事:SDK 有一个中间件概念,让你可以包装模型调用来添加日志、缓存或自定义行为,而不必触及你的实际特性代码。
wrapLanguageModel 接收一个模型和一个中间件对象。你可以在请求到达 API 之前拦截它们,修改参数,缓存响应。我在为一家出版商构建的内容生成工具上用过这个,他们的用例有大量重复的提示(同一篇文章摘要每天被请求多次),我们用中间件层在 Redis 中缓存了响应。第一个月成本下降了约 35%。 takes a model and a middleware object. You can intercept requests before they hit the API, modify parameters, cache responses. I used this on a content generation tool we built for a publisher, their use case had a lot of repeated prompts (same article summary being requested multiple times a day), and we cached responses in Redis using the middleware layer. Cost dropped by about 35% in the first month.
这是你通常会笨拙地硬贴在 API 调用外面的东西。将它作为 SDK 中的一等概念意味着它是可组合和可测试的。
它不做什么(对自己诚实一点)
好的。让我帮你省去一些麻烦。
SDK 不会为你处理内存或长期对话历史。useChat 在客户端状态中维护消息数组,但用户一刷新页面,数据就消失了。如果你需要持久化对话,那是你自己要搭建的。Postgres 或 MongoDB 存储消息,在挂载时获取数据来恢复聊天,这些都不提供。实际上,这是对的做法。SDK 不应该管你的数据层。但新手常常期望它能做到。useChat maintains the message array in client state, but the moment the user refreshes, that's gone. If you need persistent conversations, you're building that yourself. Postgres or MongoDB for message storage, fetch on mount to rehydrate the chat, none of that is provided. This is correct behaviour, actually. The SDK shouldn't own your data layer. But newcomers often expect it to.
它也不能以一种抽象掉所有复杂性的方式原生处理多模态输入。你可以在消息数组中传递图片 URL,GPT-4o 这样的模型会处理得很好,但搭建一个完整的图片上传流程(包括预览、压缩和存储)仍然是你的工作。我在 Next.js 上用 Uploadthing 来做这个,大概需要 30 分钟来接入。Uploadthing for this when I'm on Next.js, takes about 30 minutes to wire up.
RAG(检索增强生成)也不包括在内。没有内置的向量搜索,没有嵌入管道,没有分块逻辑。要用这些功能,你需要借助 Postgres 上的 pgvector 或 Pinecone 这样的专门服务。SDK 处理 LLM 调用。检索架构的其余部分由你自己搭建。pgvector on Postgres or a dedicated service like Pinecone. The SDK handles the LLM call. The rest of the retrieval architecture is yours to build.
部署:Vercel 对比其他地方
是的,SDK 在 Vercel 上表现最好。StreamingTextResponse 可以直接用 Vercel 的 Edge Runtime,Edge Functions 的冷启动时间比无服务器 Node.js 函数低得多。如果你的聊天响应感觉慢,第一个 token 的延迟很关键,Edge 能显著降低它。StreamingTextResponse works with Vercel's Edge Runtime out of the box, and cold starts on Edge Functions are much lower than on serverless Node.js functions. If your chat responses feel sluggish, the latency until the first token matters a lot, and Edge cuts that down.
但我在 Railway 上也部署过(Node.js,不是 Edge),运行得很好。你只需用带上正确流传输头的 Response,而不是 Vercel 专用的助手函数,SDK 文档覆盖了这一点。别因为叫"Vercel AI SDK"就以为你被锁定了。Response with proper streaming headers instead of the Vercel-specific helpers, and the SDK docs cover this. Don't let "Vercel AI SDK" make you think you're locked in.
有一点我会真诚地建议:保持你的 AI 路由处理程序精简。不要在一个函数里做数据库查询、认证检查和 LLM 调用。中间件(Next.js 中间件,不是 SDK 中间件)应该在请求到达你的 AI 路由前就处理认证。这样既快,也易于调试。
常见问题
Vercel AI SDK 免费使用吗?
SDK 本身是开源的、免费的。你要付钱给底层模型 API,比如 OpenAI、Anthropic、Google,不管你调用谁。那些成本由你自己管理。SDK 不会加价。
我能用它配合 OpenAI 以外的模型吗?
可以,这也是它真正的优势之一。有官方适配器支持 Anthropic、Google(Gemini)、Mistral、Groq、Cohere 等等。社区也为 Ollama 写了适配器,如果你想跑本地模型的话。统一的接口意味着你切换供应商时,特性代码不用改。
它能配合 React Server Components 用吗?
部分支持。generateText 和 generateObject 可以直接在 Server Components 里运行,它们就是异步函数。streamText 配合 useChat hook 需要客户端状态,那部分得放在 Client Component 里。这是标准的 Next.js 架构,如果你理解了边界划分就不会有问题。generateText and generateObject can run in Server Components without issues, they're just async functions. streamText with the useChat hook requires client-side state, so that part lives in a Client Component. This is standard Next.js architecture and shouldn't cause you any grief if you understand the boundary.
API 宕机时我怎么处理错误?
SDK 抛出的是类型化错误,你可以捕获并处理。实际上我会把 LLM 调用包在 try/catch 里,返回一个友好的降级消息,而不是让流错误冒泡到 UI 变成破损的部分响应。SDK 文档里的错误处理部分详细讲了错误类型,值得在上线前读一遍。SDK docs on error handling cover the error types in detail and are worth reading before you go to production.
token 限制的情况怎样?
SDK 不会替你管理上下文窗口限制。如果你在构建长运行的对话,又不修剪消息历史,你会撞上模型的上下文窗口限制,然后报错。简单的解决办法:保留最后 N 条消息(我通常用 20 条)加上一个固定的系统提示词。对大多数聊天应用来说够用了。
---
听我说,Vercel AI SDK 不是什么灵丹妙药。它是对一些真正繁琐的底层细节的精妙抽象。它正确处理流式传输,给你类型化的结构化输出,让工具调用变得易于使用,并且可以跨供应商工作。对于我承接的 80% 的 AI 功能工作,它是正确的起点。
顺便说一下,那个金融科技客户在我重写后的两周内就上线了重建的聊天功能。在移动端 Safari 上没有任何问题。没有竞态条件。六周变成了两天。有时候,正确的抽象是值得的。
