跳至正文
来两杯美式
返回

Vercel AI SDK 自定义 OpenAI 模型接入

By 来两杯美式
发布于更新于

概述

本文档记录在 Next.js + T3 Stack 项目中如何使用 Vercel AI SDK 接入自定义 OpenAI 兼容的 AI 模型(如百度千帆、DeepSeek 等)。

架构说明

核心库 vs Provider

作用
aiVercel AI SDK 核心库,提供 generateTextstreamTextstreamObject 等核心函数
@ai-sdk/openai官方 OpenAI provider,适用于 OpenAI API
@ai-sdk/openai-compatible通用 OpenAI 兼容 provider,适用于任何符合 OpenAI 格式的 API
@ai-sdk/anthropicAnthropic Claude provider
其他社区 providervercel-minimax-ai-provider

选择原则

环境配置

1. 安装依赖

pnpm add ai @ai-sdk/openai-compatible

2. 配置环境变量

# AI Model Configuration
AI_BASE_URL=https://qianfan.baidubce.com/v2
AI_API_KEY=your-api-key-here
AI_DEFAULT_MODEL=deepseek-v3.2

常见的 AI_BASE_URL 示例:

3. 在 env.js 中声明

// src/env.js
AI_BASE_URL: z.string().url().optional().default("https://api.openai.com/v1"),
AI_API_KEY: z.string().min(1),
AI_DEFAULT_MODEL: z.string().optional().default("gpt-4o"),

代码实现

创建 AI Provider

推荐在 src/server/ai/ 目录下组织代码:

// src/server/ai/index.ts
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
import { generateText, type ModelMessage, streamText } from "ai";
import { env } from "~/env";

// 创建 provider 实例
const provider = createOpenAICompatible({
  name: "custom-provider", // 用于日志和调试
  apiKey: env.AI_API_KEY,
  baseURL: env.AI_BASE_URL,
});

// 生成文本(非流式)
export async function generateAIText(
  prompt: string,
  options?: {
    model?: ModelType;
    system?: string;
  }
) {
  const modelId = env.AI_DEFAULT_MODEL;

  const result = await generateText({
    model: provider(modelId),
    prompt,
    system: options?.system,
  });

  return result.text;
}

// 流式文本生成
export async function streamAIText(
  messages: ModelMessage[],
  options?: {
    model?: ModelType;
    system?: string;
  }
) {
  const result = streamText({
    model: provider(env.AI_DEFAULT_MODEL),
    messages,
    system: options?.system,
  });

  return result.toUIMessageStreamResponse();
}

export type ModelType = "default" | "vision" | "fast" | "reasoning";

在 tRPC router 中使用

// src/server/api/routers/resume.ts
import { generateAIText } from "~/server/ai";

export const resumeRouter = createTRPCRouter({
  optimizeMarkdown: protectedProcedure
    .input(
      z.object({
        markdownContent: z.string().min(1),
        instructions: z.string().optional(),
      })
    )
    .mutation(async ({ input }) => {
      const prompt = `请优化以下简历的 Markdown 内容,使其更专业、更有条理。${input.instructions ? `\n\n额外要求:${input.instructions}` : ""}\n\n${input.markdownContent}`;

      const optimized = await generateAIText(prompt, {
        system:
          "你是一个专业的简历顾问。请优化简历内容,保持 Markdown 格式。只返回优化后的 Markdown,不要添加任何解释说明。",
      });

      return { optimizedMarkdown: optimized };
    }),
});

常见问题与解决方案

问题 1: Invalid JSON response / Type validation failed

错误信息

Error [AI_TypeValidationError]: Type validation failed
Invalid input: expected array, received undefined

原因:目标 API 返回的响应格式与 AI SDK 期望的 OpenAI 格式不兼容。

解决方案:使用 @ai-sdk/openai-compatible 替代 @ai-sdk/openai

// ❌ 错误 - 使用官方 OpenAI provider
import { createOpenAI } from "@ai-sdk/openai";
const openai = createOpenAI({
  baseURL: env.AI_BASE_URL,
  apiKey: env.AI_API_KEY,
});

// ✅ 正确 - 使用 OpenAI 兼容 provider
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
const provider = createOpenAICompatible({
  name: "custom-provider",
  apiKey: env.AI_API_KEY,
  baseURL: env.AI_BASE_URL,
});

问题 2: API 返回 404

原因:baseURL 路径错误。百度千帆使用 /v2,其他大多用 /v1

解决方案

问题 3: fetch failed / ECONNRESET

原因:服务器无法访问外部 AI API(网络问题)

解决方案

  1. 检查服务器是否能访问外网:curl -v https://api.openai.com
  2. 如需代理,在 .env 中配置:
HTTP_PROXY=http://your-proxy:port
HTTPS_PROXY=http://your-proxy:port

问题 4: 模型名称无效

原因:模型名称与平台要求的不一致

解决方案

  1. 查阅平台文档获取正确的模型名
  2. 常见模型名格式:
    • OpenAI: gpt-4o, gpt-4o-mini
    • DeepSeek: deepseek-chat, deepseek-coder
    • 百度千帆: ernie-4.0-8k, ernie-3.5-8k

调试技巧

添加详细日志

export async function generateAIText(prompt: string, options?: {...}) {
  console.log(`[AI] baseURL: ${env.AI_BASE_URL}`);
  console.log(`[AI] model: ${env.AI_DEFAULT_MODEL}`);
  console.log(`[AI] prompt length: ${prompt.length}`);

  try {
    const result = await generateText({
      model: provider(modelId),
      prompt,
      system: options?.system,
    });
    console.log(`[AI] result length: ${result.text.length}`);
    return result.text;
  } catch (error) {
    console.error("[AI] Error:", error);
    throw error;
  }
}

测试 API 是否正常

在终端直接调用 API 测试:

curl -X POST "https://qianfan.baidubce.com/v2/chat/completions" \
  -H "Authorization: $AI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "deepseek-v3.2",
    "messages": [{"role": "user", "content": "hello"}],
    "max_tokens": 100
  }'

项目结构参考

src/server/ai/
├── index.ts          # 核心导出:generateAIText, streamAIText
├── provider.ts       # provider 配置(可选)
├── vision.ts         # 视觉模型支持(可选)
└── ...

分享这篇文章:
通过邮件分享这篇文章✓ 链接已复制
查看系列全部文章
  1. 01.T3 Stack 开发规范
  2. 02.Next.js 并行路由完全指南
  3. 03.Web 会话与身份验证完整指南
  4. 04.Auth Guard 与 tRPC 中间件
  5. 05.T3 Stack 会话认证指南
  6. 06.Next.js 子域名、反向代理与分享链路最佳实践
  7. 07.CORS 与 Web 安全实战白皮书
  8. 08.Web 多主题架构最佳实践
  9. 09.Vercel AI SDK 自定义 OpenAI 模型接入
  10. 10.AI 驱动动态看板架构指南
  11. 11.VitePress 动态渲染全栈落地方案

上一篇
AI 驱动动态看板架构指南
下一篇
Web 多主题架构最佳实践