ai AI 集成
统一的 AI/LLM 集成层,支持多提供商切换
本页面介绍 AI 包 API 使用方法。AI 辅助开发工作流请参考 开发指南。
概述
@mono/ai 是项目的 AI 集成包,提供了统一的接口来调用各种 AI 服务。它基于 Vercel AI SDK 构建,但增加了一层封装来简化配置、支持多提供商切换,并提供更符合业务需求的 API。
为什么要封装一层?
你可能会疑惑:既然已经有 Vercel AI SDK 了,为什么还要再封装一层?
业界实践
这不是我们独创的做法。Vercel 官方的 next-forge 模板、supastarter 等知名项目都采用了相同的架构模式。
封装的核心价值
1. 统一配置管理
// 不需要每次都传配置
import { generateText } from "@mono/ai";
// 配置从环境变量自动读取
const { text } = await generateText("你好,世界!");支持多种环境变量,按优先级自动选择:
AI_API_KEY>OPENAI_API_KEY>ARK_API_KEYAI_BASE_URL>OPENAI_BASE_URL>ARK_BASE_URLAI_MODEL>OPENAI_MODEL>ARK_MODEL
2. 多 AI 提供商无缝切换
所有兼容 OpenAI API 格式的提供商都可以通过修改环境变量切换,无需改代码:
# 使用 OpenAI
AI_API_KEY=sk-...
AI_BASE_URL=https://api.openai.com/v1
AI_MODEL=gpt-4o-mini
# 切换到 DeepSeek
AI_API_KEY=sk-...
AI_BASE_URL=https://api.deepseek.com/v1
AI_MODEL=deepseek-chat
# 切换到通义千问
AI_API_KEY=sk-...
AI_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
AI_MODEL=qwen-turbo
# 切换到字节跳动豆包
AI_API_KEY=...
AI_BASE_URL=https://ark.cn-beijing.volces.com/api/v3
AI_MODEL=ep-...3. 简化的 API
对比原始 AI SDK 和封装后的调用:
// ❌ 原始 AI SDK - 每次都要配置
import { generateText } from "ai";
import { createOpenAI } from "@ai-sdk/openai";
const client = createOpenAI({
apiKey: process.env.AI_API_KEY,
baseURL: process.env.AI_BASE_URL
});
const result = await generateText({
model: client.chat(process.env.AI_MODEL!),
prompt: "Hello",
temperature: 0.7,
});
// ✅ 封装后 - 简洁直观
import { generateText } from "@mono/ai";
const result = await generateText("Hello", {
temperature: 0.7
});4. 模型白名单控制
防止用户使用昂贵的模型导致成本失控:
// packages/ai/src/ai/chat.ts
export const allowedModels = [
"deepseek-v3.2",
"gemini-3-flash-preview",
"deepseek-chat",
"gpt-4o-mini",
] as const;
// 自动验证和降级
const model = isAllowedModel(userModel) ? userModel : defaultModel;5. 业务逻辑封装
可以封装常用的 prompt 模板和业务逻辑:
// packages/ai/src/ai/lib/prompts.ts
export const translatePrompt = (text: string, targetLang: string) => {
return `Translate the following text to ${targetLang}:\n\n${text}`;
};
export const summarizePrompt = (content: string, maxWords: number) => {
return `Summarize the following content in ${maxWords} words:\n\n${content}`;
};6. 类型安全和回调适配
// 简化复杂的回调签名
export async function streamText(
prompt: string,
options?: StreamTextOptions,
) {
return aiStreamText({
// ...
onFinish: options?.onFinish
? async (event) => {
// 适配:从复杂的 event 对象提取需要的部分
await options.onFinish?.(event.text);
}
: undefined,
});
}7. 解耦和可维护性
- 解耦:应用代码不直接依赖 AI SDK 的具体实现
- 升级:AI SDK 版本升级时,只需修改封装层
- 测试:可以轻松 mock AI 调用进行测试
- 监控:统一添加日志、错误处理、性能监控
快速开始
基础配置
在 .env.local 中配置:
AI_API_KEY=your-api-key
AI_BASE_URL=https://api.openai.com/v1
AI_MODEL=gpt-4o-mini文本生成
import { generateText } from "@mono/ai";
const { text } = await generateText("介绍一下 Next.js", {
temperature: 0.7,
maxOutputTokens: 500,
});
console.log(text);流式输出
import { streamText } from "@mono/ai";
const { textStream } = await streamText("写一个故事", {
temperature: 0.9,
onFinish: async (text) => {
console.log("生成完成:", text);
},
});
for await (const chunk of textStream) {
process.stdout.write(chunk);
}结构化输出
import { generateObject } from "@mono/ai";
import { z } from "zod";
const schema = z.object({
name: z.string(),
age: z.number(),
hobbies: z.array(z.string()),
});
const { object } = await generateObject(
"生成一个用户资料",
{ schema }
);
console.log(object); // 类型安全的对象聊天对话
import { streamChatText } from "@mono/ai";
const messages = [
{ role: "user", content: "你好" },
{ role: "assistant", content: "你好!有什么可以帮你的吗?" },
{ role: "user", content: "介绍一下 TypeScript" },
];
const result = await streamChatText({
messages,
model: "deepseek-chat",
onFinish: async (messages, isAborted) => {
if (!isAborted) {
// 保存对话历史
}
},
});文本嵌入(Embeddings)
import { embedText } from "@mono/ai";
// 单个文本
const { embedding } = await embedText("Hello, world!");
// 批量文本
const { embeddings } = await embedText([
"文本1",
"文本2",
"文本3",
]);高级用法
运行时覆盖配置
import { generateText } from "@mono/ai";
// 临时使用不同的配置
const { text } = await generateText(
"Hello",
{ temperature: 0.5 },
{
apiKey: "sk-custom-key",
baseURL: "https://api.deepseek.com/v1",
model: "deepseek-chat",
}
);自定义客户端
import { createAIClient, getModelName } from "@mono/ai";
const client = createAIClient({
apiKey: "your-key",
baseURL: "https://custom-endpoint.com/v1",
model: "custom-model",
});
// 使用自定义客户端
const model = client.chat(getModelName());API 参考
核心函数
generateText(prompt, options?, config?)- 生成文本streamText(prompt, options?, config?)- 流式生成文本generateObject(prompt, options, config?)- 生成结构化对象embedText(value, options?, config?)- 生成文本嵌入
聊天函数
streamChatText(options)- 流式聊天对话createChatClient(config?)- 创建聊天客户端
工具函数
createAIClient(config?)- 创建 AI 客户端getModelName(config?)- 获取模型名称getAIConfig(overrides?)- 获取 AI 配置isAllowedModel(model)- 检查模型是否在白名单中
类型定义
type AIConfig = {
apiKey: string;
baseURL: string;
model: string;
};
type GenerateTextOptions = {
temperature?: number;
maxOutputTokens?: number;
topP?: number;
frequencyPenalty?: number;
presencePenalty?: number;
stopSequences?: string[];
};
type AllowedModel =
| "deepseek-v3.2"
| "gemini-3-flash-preview"
| "deepseek-chat"
| "gpt-4o-mini";支持的 AI 提供商
所有兼容 OpenAI API 格式的提供商都支持,包括但不限于:
| 提供商 | Base URL | 说明 |
|---|---|---|
| OpenAI | https://api.openai.com/v1 | 官方 API |
| DeepSeek | https://api.deepseek.com/v1 | 高性价比 |
| 通义千问 | https://dashscope.aliyuncs.com/compatible-mode/v1 | 阿里云 |
| 字节豆包 | https://ark.cn-beijing.volces.com/api/v3 | 字节跳动 |
| Moonshot | https://api.moonshot.cn/v1 | 月之暗面 |
| 智谱 AI | https://open.bigmodel.cn/api/paas/v4 | 清华系 |
最佳实践
1. 使用环境变量
不要在代码中硬编码 API Key:
// ❌ 不要这样
const { text } = await generateText("Hello", {}, {
apiKey: "sk-hardcoded-key"
});
// ✅ 使用环境变量
const { text } = await generateText("Hello");2. 错误处理
try {
const { text } = await generateText("Hello");
} catch (error) {
console.error("AI 调用失败:", error);
// 降级处理或返回默认值
}3. 成本控制
- 使用
maxOutputTokens限制输出长度 - 选择性价比高的模型(如 DeepSeek)
- 实现请求频率限制
- 监控 API 使用量
4. 缓存策略
对于相同的输入,考虑缓存结果:
import { cache } from "react";
export const getCachedCompletion = cache(async (prompt: string) => {
return await generateText(prompt);
});故障排查
API Key 无效
检查环境变量是否正确设置:
echo $AI_API_KEYBase URL 格式错误
确保 URL 格式正确,封装层会自动添加 /v1 后缀:
# ✅ 正确
AI_BASE_URL=https://api.openai.com/v1
# ✅ 也正确(会自动添加 /v1)
AI_BASE_URL=https://api.openai.com模型不在白名单
如果使用的模型不在 allowedModels 中,会自动降级到 defaultModel。
要添加新模型,修改 packages/ai/src/ai/chat.ts:
export const allowedModels = [
"deepseek-v3.2",
"your-new-model", // 添加新模型
] as const;