01MVP 标识01MVP
包文档AI 与语音ai AI 集成

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_KEY
  • AI_BASE_URL > OPENAI_BASE_URL > ARK_BASE_URL
  • AI_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说明
OpenAIhttps://api.openai.com/v1官方 API
DeepSeekhttps://api.deepseek.com/v1高性价比
通义千问https://dashscope.aliyuncs.com/compatible-mode/v1阿里云
字节豆包https://ark.cn-beijing.volces.com/api/v3字节跳动
Moonshothttps://api.moonshot.cn/v1月之暗面
智谱 AIhttps://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_KEY

Base 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;

相关资源