概述
本文档记录在 Next.js + T3 Stack 项目中如何使用 Vercel AI SDK 接入自定义 OpenAI 兼容的 AI 模型(如百度千帆、DeepSeek 等)。
架构说明
核心库 vs Provider
| 包 | 作用 |
|---|---|
ai | Vercel AI SDK 核心库,提供 generateText、streamText、streamObject 等核心函数 |
@ai-sdk/openai | 官方 OpenAI provider,适用于 OpenAI API |
@ai-sdk/openai-compatible | 通用 OpenAI 兼容 provider,适用于任何符合 OpenAI 格式的 API |
@ai-sdk/anthropic | Anthropic Claude provider |
| 其他社区 provider | 如 vercel-minimax-ai-provider 等 |
选择原则:
- OpenAI → 用
@ai-sdk/openai - Anthropic → 用
@ai-sdk/anthropic - 其他兼容 OpenAI 格式的 API → 用
@ai-sdk/openai-compatible
环境配置
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 示例:
- OpenAI:
https://api.openai.com/v1 - 百度千帆:
https://qianfan.baidubce.com/v2 - DeepSeek:
https://api.deepseek.com/v1
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
解决方案:
- 百度千帆:
https://qianfan.baidubce.com/v2 - OpenAI/DeepSeek:
https://api.xxx.com/v1
问题 3: fetch failed / ECONNRESET
原因:服务器无法访问外部 AI API(网络问题)
解决方案:
- 检查服务器是否能访问外网:
curl -v https://api.openai.com - 如需代理,在
.env中配置:
HTTP_PROXY=http://your-proxy:port
HTTPS_PROXY=http://your-proxy:port
问题 4: 模型名称无效
原因:模型名称与平台要求的不一致
解决方案:
- 查阅平台文档获取正确的模型名
- 常见模型名格式:
- OpenAI:
gpt-4o,gpt-4o-mini - DeepSeek:
deepseek-chat,deepseek-coder - 百度千帆:
ernie-4.0-8k,ernie-3.5-8k
- OpenAI:
调试技巧
添加详细日志
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 # 视觉模型支持(可选)
└── ...