前端集成大模型 API
当前主流大模型 API 均遵循 OpenAI 兼容协议,前端通过统一的 Chat Completions 接口即可对接 DeepSeek、Moonshot、通义千问等多种模型。多模态能力(图像、语音、视觉)通过独立服务或统一 API 集成。
OpenAI 兼容协议
协议标准
绝大多数国内外大模型 API 均兼容 OpenAI Chat Completions 协议,核心请求结构如下:
typescript
// 请求
POST /v1/chat/completions
Content-Type: application/json
Authorization: Bearer <API_KEY>
{
"model": "deepseek-chat",
"messages": [
{ "role": "system", "content": "You are a helpful assistant." },
{ "role": "user", "content": "你好" }
],
"stream": false,
"temperature": 0.7,
"max_tokens": 2048
}响应结构
typescript
interface ChatCompletionResponse {
id: string;
object: "chat.completion";
created: number;
model: string;
choices: Array<{
index: number;
message: {
role: "assistant";
content: string;
tool_calls?: ToolCall[];
};
finish_reason: "stop" | "tool_calls" | "length";
}>;
usage: {
prompt_tokens: number;
completion_tokens: number;
total_tokens: number;
};
}主流模型 API 对照
| 平台 | Base URL | 模型标识 | 特点 |
|---|---|---|---|
| DeepSeek | https://api.deepseek.com | deepseek-chat / deepseek-reasoner | 高性价比、深度思考 |
| Moonshot | https://api.moonshot.cn/v1 | moonshot-v1-128k | 128K 超长上下文 |
| 通义千问 | https://dashscope.aliyuncs.com/compatible-mode/v1 | qwen-max / qwen-plus | 阿里生态 |
| OpenAI | https://api.openai.com/v1 | gpt-4o / gpt-4o-mini | 行业标准 |
| Ollama(本地) | http://localhost:11434/v1 | qwen3:1.7b 等 | 私有化部署 |
文本大模型集成
基础调用(非流式)
typescript
// composables/useLLM.ts
import { ref } from 'vue';
interface LLMConfig {
endpoint: string;
apiKey: string;
model: string;
}
export function useLLM(config: LLMConfig) {
const reply = ref('');
const loading = ref(false);
const error = ref<string | null>(null);
async function chat(userMessage: string, systemPrompt?: string) {
loading.value = true;
error.value = null;
const messages = [
...(systemPrompt ? [{ role: 'system' as const, content: systemPrompt }] : []),
{ role: 'user' as const, content: userMessage },
];
try {
const response = await fetch(`${config.endpoint}/chat/completions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${config.apiKey}`,
},
body: JSON.stringify({
model: config.model,
messages,
stream: false,
}),
});
if (!response.ok) {
const errData = await response.json().catch(() => null);
throw new Error(errData?.error?.message || `HTTP ${response.status}`);
}
const data = await response.json();
reply.value = data.choices[0]?.message?.content || '';
} catch (e) {
error.value = e instanceof Error ? e.message : String(e);
} finally {
loading.value = false;
}
}
return { reply, loading, error, chat };
}流式响应(SSE)
流式传输通过 Server-Sent Events 实现逐字输出,显著降低用户感知等待时间:
typescript
async function chatStream(userMessage: string, onChunk: (text: string) => void) {
const response = await fetch(`${config.endpoint}/chat/completions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${config.apiKey}`,
},
body: JSON.stringify({
model: config.model,
messages: [{ role: 'user', content: userMessage }],
stream: true,
}),
});
const reader = response.body!.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop() || '';
for (const line of lines) {
if (!line.startsWith('data: ')) continue;
const data = line.slice(6).trim();
if (data === '[DONE]') return;
try {
const parsed = JSON.parse(data);
const delta = parsed.choices[0]?.delta?.content;
if (delta) onChunk(delta);
} catch {
// 忽略不完整的 JSON 片段
}
}
}
}服务端代理(BFF 模式)
生产环境中 API Key 不应暴露在前端代码中,需通过 BFF 层代理:
图表渲染中…
typescript
// server.ts
import express from 'express';
import dotenv from 'dotenv';
dotenv.config({ path: ['.env.local', '.env'] });
const app = express();
app.use(express.json());
app.post('/api/chat', async (req, res) => {
const { message } = req.body;
const response = await fetch(
`${process.env.LLM_BASE_URL}/chat/completions`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.LLM_API_KEY}`,
},
body: JSON.stringify({
model: process.env.LLM_MODEL,
messages: [{ role: 'user', content: message }],
stream: true,
}),
}
);
// 透传 SSE 流
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
const reader = response.body!.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
res.write(decoder.decode(value, { stream: true }));
}
res.end();
});
app.listen(3000);图像生成模型集成
可灵 AI(JWT 鉴权)
可灵 AI 采用 JWT 鉴权机制,需通过 AccessKey 生成 Token:
typescript
// server/auth.ts
import jwt from 'jsonwebtoken';
export function generateKlingToken(): string {
const now = Math.floor(Date.now() / 1000);
const payload = {
iss: process.env.ACCESS_KEY_ID,
exp: now + 1800, // 30 分钟有效
nbf: now - 5, // 提前 5 秒生效
};
return jwt.sign(payload, process.env.ACCESS_KEY_SECRET!, { algorithm: 'HS256' });
}图像生成通用流程
图表渲染中…
图像生成通常为异步任务,需通过轮询或 Webhook 获取结果。
语音合成(TTS)集成
火山引擎语音合成
核心参数配置:
| 参数 | 说明 | 推荐值 |
|---|---|---|
voice_type | 音色选择 | 按场景选择(多语言场景选多语言音色) |
encoding | 音频格式 | mp3(兼容性最佳) |
rate | 采样率 | 24000(高质量) |
speed_ratio | 语速 | 1.0(正常) |
volume_ratio | 音量 | 1.0(正常) |
pitch_ratio | 音调 | 1.0(正常) |
emotion | 情感 | happy / calm / neutral |
compression_rate | 压缩率 | 按需调整 |
前端调用示例
typescript
// composables/useTTS.ts
export function useTTS() {
const audioUrl = ref<string | null>(null);
const generating = ref(false);
async function synthesize(text: string) {
generating.value = true;
try {
const response = await fetch('/api/tts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
text,
voice_type: 'zh_female_shuangkuaisisi_moon_bigtts',
encoding: 'mp3',
rate: 24000,
speed_ratio: 1.0,
emotion: 'happy',
}),
});
const blob = await response.blob();
audioUrl.value = URL.createObjectURL(blob);
} finally {
generating.value = false;
}
}
return { audioUrl, generating, synthesize };
}Vite 代理配置(解决跨域)
typescript
// vite.config.ts
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'http://localhost:3000',
rewrite: (path) => path.replace(/^\/api/, ''),
},
'/tts': {
target: 'https://openspeech.bytedance.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/tts/, ''),
},
},
},
});视觉模型集成
视觉模型支持图像输入 + 文本输出,用于图像理解、OCR、场景分析等:
typescript
// 多模态消息格式(OpenAI 兼容)
const messages = [
{
role: 'user',
content: [
{ type: 'text', text: '请描述这张图片的内容' },
{
type: 'image_url',
image_url: { url: 'https://example.com/image.jpg' },
},
],
},
];
const response = await fetch(`${endpoint}/chat/completions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${apiKey}`,
},
body: JSON.stringify({ model: 'gpt-4o', messages }),
});环境变量与安全
环境变量配置
bash
# .env.local(不要提交到版本控制)
LLM_API_KEY=sk-xxxxxxxxxxxx
LLM_BASE_URL=https://api.deepseek.com
LLM_MODEL=deepseek-chat
# 语音服务
TTS_APP_ID=5934290469
TTS_ACCESS_TOKEN=c-xxxxxxxxxxxx
TTS_CLUSTER_ID=volcano_tts
# 图像服务
IMAGE_ACCESS_KEY_ID=8f617xxxxxxxxx
IMAGE_ACCESS_KEY_SECRET=36092xxxxxxxxx安全最佳实践
| 规则 | 说明 |
|---|---|
| API Key 仅存服务端 | 前端通过 BFF 代理访问,永远不在客户端暴露密钥 |
| 使用环境变量 | 通过 dotenv 加载,.env.local 加入 .gitignore |
| 请求频率限制 | BFF 层实现 rate limiting,防止滥用 |
| HTTPS 传输 | 所有 API 通信必须走 HTTPS |
| Token 有效期 | JWT 鉴权设置合理过期时间(≤30min) |
常见问题与陷阱
| 问题 | 原因 | 解决方案 |
|---|---|---|
| CORS 跨域错误 | 直接调用第三方 API | 配置 Vite proxy 或通过 BFF 代理 |
| 流式响应中断 | 网络超时或 buffer 溢出 | 实现断线重连 + 心跳检测 |
| Token 超限 | 上下文过长 | 实现对话截断策略或摘要压缩 |
| 响应乱码 | 编码不一致 | 确保 TextDecoder 使用 UTF-8 |
| 并发限制 | 触发 API rate limit | 实现请求队列 + 指数退避重试 |