{T}

前端集成大模型 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模型标识特点
DeepSeekhttps://api.deepseek.comdeepseek-chat / deepseek-reasoner高性价比、深度思考
Moonshothttps://api.moonshot.cn/v1moonshot-v1-128k128K 超长上下文
通义千问https://dashscope.aliyuncs.com/compatible-mode/v1qwen-max / qwen-plus阿里生态
OpenAIhttps://api.openai.com/v1gpt-4o / gpt-4o-mini行业标准
Ollama(本地)http://localhost:11434/v1qwen3: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实现请求队列 + 指数退避重试

参考资源