数据截至 (上 游 commit 2174f202c21f)
Vercel AI SDK — 架构与原理
30 秒导读: Vercel AI SDK 是一个 provider 无关的 TypeScript 工具包。你写一次
generateText({ model, prompt, tools }),它负责:把统一的消息格式翻译成某个厂商(OpenAI / Anthropic / Google…)的请求、跑"调模型 → 执行工具 → 再调模型"的循环、把结果(或流)以一套标准协议吐给你的前端。换模型只改一个字符串。
1. 这是什么(零基础也能懂)
一句话定义: 它是 LLM 应用的"中间层"——把"和大模型对话"这件事,从"对着某一家厂商的 HTTP API 拼 JSON",变成一组与厂商无关的函数调用。
解决什么问题 / 给谁用:
假设你要做一个聊天机器人,今天用 OpenAI,明天老板说改用 Anthropic,后天又要支持工具调用、流式输出、结构化 JSON。如果直接对着各家 HTTP API 写,每家的请求体、流格式、工具协议都不一样,代码会被厂商细节绑死。
AI SDK 把这些差异藏进统一接口之下。它给三类人用:
- 应用开发者 —— 调
generateText/streamText写后端逻辑。 - 前端开发者 —— 用
@ai-sdk/react的useChat把流接到 UI。 - provider 作者 —— 实现一个标准接口(
LanguageModelV4),自家模型就能被整个生态使用。
它能做什么(功能):
- 文本生成(一次性
generateText/ 流式streamText) - 工具调用循环(模型要求调工具 → SDK 执行 → 把结果喂回模型,自动循环)
- Agent(
ToolLoopAgent:把模型 + 工具 + 系统提示打包成可复用对象) - 结构化输出(
generateObject/Output:强制模型吐出符合 schema 的 JSON) - 前端流式 UI(一套
UIMessage协议 + 框架 hook) - 还有 embed / 图片 / 语音 / 转写 / rerank 等多模态能力(本文聚焦文本与 agent 主线)
用起来什么样: 最小例子——换模型只换字符串:
import { generateText } from 'ai';
const { text } = await generateText({
model: 'anthropic/claude-opus-4.6', // 换成 'openai/gpt-5.4' 即切厂商
prompt: 'What is an agent?',
});
这里 model 是个字符串,SDK 默认把它路由到 Vercel AI Gateway(一个统一网关),所以你连 SDK 包都不用装。要直连厂商,就传一个 provider 对象:anthropic('claude-opus-4-6')。两种写法,下游走的是同一套抽象(见 01)。
一句话直觉/类比: 把 AI SDK 当成 LLM 世界的 ODBC / JDBC——数据库各家方言不同,但你对着统一的驱动接口写代码,换数据库只换连接串。这里"数据库驱动"就是 LanguageModelV4,"连接串"就是那个 model 字符串。
2. 顶层全景(它大概怎么转)
这一节给你看清"大盘":一次 generateText 调用,数据从你的代码流到模型、再流回来,中间经过哪些部件。
2.1 分层结构
AI SDK 是一个 monorepo,核心分三层。理解这三层,就理解了整个库的骨架:
你的应用代码 (generateText / streamText / useChat)
│ 传:统一的 prompt / messages / tools
▼
┌──────────────────────────────────────────────┐
│ 包 ai —— "编排层" (orchestration) │
│ · generateText 多步工具循环 │
│ · streamText 流式 + UI 协议 │
│ · ToolLoopAgent / generateObject │
│ 把"统一格式"翻译成 LanguageModelV4 的调用 │
└──────────────────────────────────────────────┘
│ 调:model.doGenerate(...) / model.doStream(...)
▼
┌──────────────────────────────────────────────┐
│ 包 @ai-sdk/provider —— "规格层" (spec) │
│ 只有类型,没有实现:LanguageModelV4 接口定义 │
│ 规定"一个模型必须长什么样" │
└──────────────────────────────────────────────┘
│ 被实现
▼
┌──────────────────────────────────────────────┐
│ 各厂商包 —— "实现层" (adapters) │
│ @ai-sdk/openai / anthropic / google / gateway… │
│ 各自把统一调用映射成自家 HTTP API │
└──────────────────────────────────────────────┘
怎么读这张图: 上面是你的代码,越往下越靠近厂商。关键分界在中间那条线——@ai-sdk/provider 只定义"模型应该长什么样"(纯类型),不含任何实现;编排层只对着这个接口编程,所以永远不知道底下接的是 OpenAI 还是 Anthropic。这就是"provider 无关"的全部秘密。
2.2 部件一句话职责
| 部件 | 干什么 | 在哪 |
|---|---|---|
LanguageModelV4 | 模型的统一接口:doGenerate / doStream 两个方法 | packages/provider/src/language-model/v4/language-model-v4.ts |
generateText | 非流式主入口 + 多步工具循环 | packages/ai/src/generate-text/generate-text.ts |
streamText | 流式主入口,产出多种 stream 视图 | packages/ai/src/generate-text/stream-text.ts |
ToolLoopAgent | 把模型/工具/提示打包,薄封装 generateText/streamText | packages/ai/src/agent/tool-loop-agent.ts |
tool() | 定义一个工具(schema + execute) | packages/provider-utils/src/types/tool.ts |
resolveLanguageModel | 把 model 字符串路由到 AI Gateway,或把对象规整为 v4 | packages/ai/src/model/resolve-model.ts |
wrapLanguageModel | 用中间件包住模型(改参数/拦截结果) | packages/ai/src/middleware/wrap-language-model.ts |
| UI Message Stream | 后端→前端的标准流协议 | packages/ai/src/ui-message-stream/ |
AbstractChat / useChat | 前端聊天状态机 | packages/ai/src/ui/chat.ts |