跳到主要内容

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/reactuseChat 把流接到 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/streamTextpackages/ai/src/agent/tool-loop-agent.ts
tool()定义一个工具(schema + execute)packages/provider-utils/src/types/tool.ts
resolveLanguageModel把 model 字符串路由到 AI Gateway,或把对象规整为 v4packages/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

2.3 主线走一遍(高层,不进代码)

以一次"带工具的对话"为例,端到端是这样:

输入: { model, messages, tools }

1. resolveLanguageModel —— model 字符串 → 具体 LanguageModelV4

2. standardizePrompt —— 把 system/prompt/messages 规整成内部统一格式

3. ┌─ 进入 step 循环 ─────────────────────────┐
│ a. convertToLanguageModelPrompt │
│ 内部格式 → LanguageModelV4Prompt │
│ b. model.doGenerate(...) ← 真正打模型 │
│ c. parseToolCall —— 校验/修复模型给的工具调用 │
│ d. executeToolCall —— 跑你的工具 execute │
│ e. 工具结果拼回 messages │
└───── 还有工具要跑 且 没到 stopWhen?→ 回到 a ─┘

4. 循环结束 → 解析最终输出(文本或结构化 JSON)

输出: GenerateTextResult { text, steps, toolCalls, usage, ... }

这个 step 循环就是整个库的心脏,02 会逐行拆。


3. 阅读地图(按这个顺序读)

本项目较大,拆成 5 章,由浅入深:

  1. 01 · Provider 抽象 — 先搞懂最底下那条分界线:LanguageModelV4 长什么样、字符串 model 怎么路由到 Gateway、中间件怎么"包"模型。理解了它,后面所有"provider 无关"的说法才落地。

  2. 02 · generateText 工具循环 — 全库最核心的一段代码。一次 step 内发生什么、do…while 的继续条件、stopWhen 怎么终止。读完你能讲清"agent loop"在这里到底是什么。

  3. 03 · 工具系统 — 工具的四种形态(function / dynamic / provider-executed / provider-defined)、tool() 助手、解析失败时的 repair、以及 tool approval(人审批)机制。

  4. 04 · 流式与 UIstreamText 的多种流视图、后端→前端的 UI Message Stream 协议、useChat 客户端状态机怎么消费它。Web 互操作的精华。

  5. 05 · 结构化输出generateObjectOutput:怎么把自由文本逼成符合 zod schema 的 JSON。

建议路径: 想理解 agent → 读 01 → 02 → 03;做全栈聊天 → 读 02 → 04;只关心抽取结构化数据 → 读 01 → 05。


4. 代码地图(导航索引)

主题文件符号
公共 API 出口packages/ai/src/index.tsexport * 各目录
模型接口规格packages/provider/src/language-model/v4/language-model-v4.tsLanguageModelV4
Provider 接口packages/provider/src/provider/v4/provider-v4.tsProviderV4
多步循环主入口packages/ai/src/generate-text/generate-text.tsgenerateText
流式主入口packages/ai/src/generate-text/stream-text.tsstreamText
Agentpackages/ai/src/agent/tool-loop-agent.tsToolLoopAgent
模型路由packages/ai/src/model/resolve-model.tsresolveLanguageModel
中间件packages/ai/src/middleware/wrap-language-model.tswrapLanguageModel
工具定义packages/provider-utils/src/types/tool.tstool, Tool
停止条件packages/ai/src/generate-text/stop-condition.tsisStepCount, hasToolCall
UI 流协议packages/ai/src/ui-message-stream/ui-message-chunks.tsuiMessageChunkSchema
前端聊天packages/ai/src/ui/chat.tsAbstractChat(具体 Chatpackages/react/src/chat.react.ts)