前言
随着近两年 AI 大模型的井喷式增长,国内外涌现出了众多大模型厂商。国外有 ChatGPT、Claude、Gemini、Grok 等,国内则有 DeepSeek、通义千问、豆包、KIMI 等。此外还有我们常用的量化模型部署框架 Ollama。尽管很多厂商已经提供了与 OpenAI 兼容的接口,但像 Gemini、Ollama 等仍然没有兼容。为了兼容所有 AI 模型厂商的接口,开发者往往需要自行编写兼容层。
那么今天,将为大家介绍一个由 Vercel 开源的新 npm 库------ai
。这个库不仅提供了官方支持的几个 AI 厂商接口的兼容层,还包含了社区贡献的 AI 接口兼容层,如 Ollama、LLamaCpp、Chrome AI 等。有了它,开发者无需再担心接口规范不统一的问题。
AI SDK
安装 AI SDK
AI SDK 分为两部分:AI Core SDK 和 AI Provider SDK,也就是核心包和 AI 供应商接口包
AI Core SDK:
bash
npm install ai
AI Provider SDK:
每个 AI 供应商都有自己的接口实现。例如,如果我们使用 OpenAI、DeepSeek 和 Ollama,就需要安装以下三个包:
bash
npm install @ai-sdk/openai @ai-sdk/deepseek ollama-ai-provider
这里要特别说明一下,尽管提供了单独的 @ai-sdk/deepseek
包,但由于 DeepSeek 本身兼容 OpenAI 接口,因此也可以直接使用 @ai-sdk/openai
包。
另外,OpenAI 和 DeepSeek 这两个包是由 Vercel 官方提供的,而 Ollama 则是社区贡献的包。从包名上就能看出二者的区别。
使用示例
AI Core SDK 提供的接口并不多,但如果我们仅用于对话和文本生成,常用的接口主要有两个。
js
import { generateText } from 'ai'
import { createDeepSeek } from '@ai-sdk/deepseek'
const deepseek = createDeepSeek({
baseURL: 'https://api.deepseek.com/v1',
apiKey: process.env.DEEPSEEK_API_KEY,
})
const { text } = await generateText({
model: deepseek('deepseek-reasoner'),
prompt: '你好,请介绍一下你自己,不少于 100 字',
})
console.log(text)
可以看到,调用起来非常简单。以下是输出结果:
常用方法
上面是基本方法,接下来我们将介绍 AI SDK 常用的几个方法。
生成文本
在上面的示例中,我们使用了生成文本的方法:generateText
。
js
const { text } = await generateText({
model: ollama('qwen2.5:7b'),
prompt: '你好,请介绍一下你自己,不少于 100 字',
})
以下是一部分参数和说明,另外还支持传入 temperature
、topP
、topK
、presencePenalty
等大模型参数,这里不再一一列举。
参数名 | 参数类型 | 参数描述 |
---|---|---|
model |
LanguageModel |
指定使用的语言模型实例。需通过构造函数初始化模型,示例:openai('gpt-4-turbo') 表示调用 OpenAI 的 GPT-4 Turbo 版本模型。 |
system |
string |
系统级角色定义指令,用于设定模型在对话中的基础行为模式(如角色定位、响应格式要求等)。该指令具有最高优先级,会持续影响后续对话的生成逻辑。 |
prompt |
string |
单轮对话的文本生成输入。适用于非连续对话场景,与 messages 参数互斥,二者只能选其一。 |
messages |
`Array<CoreSystemMessage | CoreUserMessage |
maxTokens? |
number |
输出内容的长度限制(按token计算)。1个token≈0.75个英文单词或1个汉字。设置过低可能导致输出截断,建议根据模型上下文窗口合理设定。 |
maxRetries? |
number |
API调用失败时的最大重试次数(默认2次)。设置为0将禁用重试机制。注意:仅对可重试错误(如网络抖动、速率限制)生效,认证错误等致命错误不会重试。 |
abortSignal? |
AbortSignal |
支持传入AbortController的signal对象,用于实现生成过程的主动中断控制。典型应用场景:用户取消生成、页面跳转时终止未完成请求。 |
headers? |
Record<string, string> |
自定义HTTP请求头。适用于需要添加代理、自定义认证、特殊路由等高级场景。注意:仅对基于REST API的模型提供商生效,SDK方式集成的模型可能忽略此参数。 |
Tips:
参数传入
prompt
是单轮对话文本生成,但需要多轮对话时,参数使用messages
。
messages
中的role
只有user
和assistant
,系统提示词通过system
参数传入。
流式文本
我们最常见的就是用于 AI Chat 对话的流式生成接口,通过 streamText
实现。
获取到 textStream
后,通过 for await
可以获取每个流的文本内容。
streamText
的参数和 generateText
基本一样,这里不再重复说明。
js
const { textStream } = await streamText({
model: ollama('qwen2.5:7b'),
prompt: '你好,请介绍一下你自己,不少于 100 字',
})
for await (const chunk of textStream) {
console.log(chunk)
}
调用结果示例:
生成对象
使用 LLM 为给定的提示和模式生成类型化、结构化的对象。它可用于 LLM 返回结构化数据,例如用于信息提取、合成数据生成或分类任务。
例如,下面的示例通过 generateObject
生成了十组数据。需要注意的是,该方法依赖于 zod
库,需要提前安装。
js
import { z } from 'zod'
const { object } = await generateObject({
model: ollama('qwen2.5:7b'),
output: 'array',
prompt: '请你生成三组数据,数据包含姓名、性别、年龄、身高、体重、地址、电话',
schema: z.object({
name: z.string(),
sex: z.string(),
age: z.number(),
height: z.number(),
weight: z.number(),
address: z.string(),
phone: z.string(),
})
})
console.log(JSON.stringify(object, null, 2))
以下是简化后的参数表格,几乎支持大部分 generateText
参数,这里不再列举。主要介绍 generateObject
中比较重要和独有的参数。
参数名 | 类型 | 描述 |
---|---|---|
model |
LanguageModel |
语言模型实例化对象,需通过构造函数初始化。示例:openai('gpt-4-turbo') 调用OpenAI的GPT-4 Turbo模型 |
output |
`'object' | 'array' |
mode |
`'auto' | 'json' |
schema |
`Zod Schema | JSON Schema` |
schemaName |
`string | undefined` |
schemaDescription |
`string | undefined` |
enum |
string[] |
枚举约束 。限定输出值的可选范围,仅当output='enum' 时生效 |
调用结果示例:
流式对象
流式生成对象,参数和 generateObject
基本一致,这里不再赘述。通过遍历 partialObjectStream
获取每一次的 chunk
,每次获取都是一个完整的对象。
js
import { z } from 'zod'
const { partialObjectStream } = await streamObject({
model: ollama('qwen2.5:7b'),
output: 'array',
prompt: '请你生成三组数据,数据包含姓名、性别、年龄、身高、体重、地址、电话',
schema: z.object({
name: z.string(),
sex: z.string(),
age: z.number(),
height: z.number(),
weight: z.number(),
address: z.string(),
phone: z.string(),
})
})
for await (const chunk of partialObjectStream) {
console.log(JSON.stringify(chunk, null, 2))
}
调用结果示例:
剩下还有诸如
generateImage
、jsonSchema
、embed
等等工具方法,这里就不一一列举了。
封装方法
对于不同的 AI Provider SDK,Vercel 提供了一套标准,所有接入的包都遵循此标准开放对应的接口。因此,如果我们需要接入多个 AI 厂商,可以创建一个工厂,统一管理所有的包。后续新增、删除都可以在这个工厂中进行操作。如下,我们封装了一个 AIProviderFactory
方法,可以通过 createProvider
创建 provider 实例。
js
import { createDeepSeek } from '@ai-sdk/deepseek'
import { createOpenAI } from '@ai-sdk/openai'
import { createOllama } from 'ollama-ai-provider'
export const AIProviderType = {
OLLAMA: 'ollama',
OPENAI: 'openai',
DEEPSEEK: 'deepseek',
}
export const AIProviderFactories = {
[AIProviderType.OLLAMA]: createOllama,
[AIProviderType.OPENAI]: createOpenAI,
[AIProviderType.DEEPSEEK]: createDeepSeek,
}
export class AIProviderFactory {
/**
* 创建 AI Provider
* @param {string} providerType - AI Provider 类型
* @param {Object} config - AI Provider 配置
* @returns {Object} - AI Provider 实例
*/
static createProvider(providerType, config) {
const factory = AIProviderFactories[providerType]
if (!factory) {
throw new Error(`不支持的 AI Provider 类型: ${providerType}`)
}
return factory({
baseURL: config.baseURL || undefined,
apiKey: config.apiKey || undefined,
})
}
}
我们只需要在调用生成文本之前,创建一个 provider 即可。
js
import { AIProviderFactory, AIProviderType } from './src/ai.js'
import { generateText } from 'ai'
async function main() {
const provider = AIProviderFactory.createProvider(AIProviderType.OLLAMA, {
baseURL: 'http://localhost:11434/api',
})
const { text } = await generateText({
model: provider('qwen2.5:7b'),
prompt: '你好,请介绍一下你自己,不少于 100 字',
})
console.log(text)
}
main()
调用结果示例:
通过封装 provider factory,可以统一管理 AI Provider,简化了调用时的方法和复杂度。
结语
通过 Vercel 推出的 AI SDK,我们能够大幅简化对接不同 AI 厂商的工作。同时,AI SDK 提供了许多工具类,能够方便地用于 AI 的使用和调用。如果是 React 项目,我们还可以使用 AI SDK UI
进一步简化前端的开发工作量。
相关链接
- AI SDK: sdk.vercel.ai/
- AI SDK 仓库: github.com/vercel/ai