引言
在现代应用开发中,集成AI能力已成为提升产品竞争力的关键。本文将详细介绍如何在全栈Node.js应用中调用主流AI提供商(如OpenAI、Anthropic、Google Gemini等)的API token进行产品开发,涵盖从环境配置到前后端集成的完整流程。
一、项目初始化与配置
1.1 创建Node.js项目
bash
mkdir ai-integration-app
cd ai-integration-app
npm init -y
1.2 安装核心依赖
bash
npm install express dotenv axios cors
npm install --save-dev nodemon
1.3 项目结构设计
vbscript
ai-integration-app/
├── .env
├── .gitignore
├── package.json
├── server/
│ ├── config/
│ │ └── aiConfig.js
│ ├── controllers/
│ │ └── aiController.js
│ ├── routes/
│ │ └── aiRoutes.js
│ ├── services/
│ │ └── aiService.js
│ └── server.js
├── client/
│ └── public/
│ ├── index.html
│ ├── css/
│ └── js/
└── scripts/
└── test-api.js
二、AI提供商Token管理
2.1 获取API密钥
主流AI提供商获取token的方式:
- OpenAI: platform.openai.com/api-keys
- Anthropic: console.anthropic.com/settings/ke...
- Google Gemini: makersuite.google.com/app/apikey
2.2 安全存储配置
.env
文件示例:
env
# OpenAI
OPENAI_API_KEY=your_openai_key_here
OPENAI_ORG_ID=your_org_id_here
# Anthropic
ANTHROPIC_API_KEY=your_anthropic_key_here
# Google Gemini
GEMINI_API_KEY=your_gemini_key_here
# Server
PORT=3000
CORS_ORIGIN=http://localhost:8080
2.3 配置管理模块
server/config/aiConfig.js
:
javascript
require('dotenv').config();
module.exports = {
openai: {
apiKey: process.env.OPENAI_API_KEY,
organization: process.env.OPENAI_ORG_ID,
baseUrl: 'https://api.openai.com/v1'
},
anthropic: {
apiKey: process.env.ANTHROPIC_API_KEY,
baseUrl: 'https://api.anthropic.com/v1'
},
gemini: {
apiKey: process.env.GEMINI_API_KEY,
baseUrl: 'https://generativelanguage.googleapis.com/v1beta'
},
// 添加其他提供商配置...
};
三、后端服务实现
3.1 创建AI服务层
server/services/aiService.js
:
javascript
const axios = require('axios');
const aiConfig = require('../config/aiConfig');
class AIService {
constructor() {
this.providers = {
openai: this._createOpenAIClient(),
anthropic: this._createAnthropicClient(),
gemini: this._createGeminiClient()
};
}
_createOpenAIClient() {
return axios.create({
baseURL: aiConfig.openai.baseUrl,
headers: {
'Authorization': `Bearer ${aiConfig.openai.apiKey}`,
'OpenAI-Organization': aiConfig.openai.organization,
'Content-Type': 'application/json'
}
});
}
_createAnthropicClient() {
return axios.create({
baseURL: aiConfig.anthropic.baseUrl,
headers: {
'x-api-key': aiConfig.anthropic.apiKey,
'anthropic-version': '2023-06-01',
'Content-Type': 'application/json'
}
});
}
_createGeminiClient() {
return axios.create({
baseURL: aiConfig.gemini.baseUrl,
params: {
key: aiConfig.gemini.apiKey
},
headers: {
'Content-Type': 'application/json'
}
});
}
async chatCompletion(provider, payload) {
try {
let response;
switch(provider.toLowerCase()) {
case 'openai':
response = await this.providers.openai.post('/chat/completions', payload);
break;
case 'anthropic':
response = await this.providers.anthropic.post('/messages', payload);
break;
case 'gemini':
response = await this.providers.gemini.post(`/models/gemini-pro:generateContent`, {
contents: [{
parts: [{
text: payload.prompt
}]
}]
});
break;
default:
throw new Error('Unsupported AI provider');
}
return this._formatResponse(provider, response.data);
} catch (error) {
console.error(`AI API Error (${provider}):`, error.response?.data || error.message);
throw new Error(`AI service error: ${error.message}`);
}
}
_formatResponse(provider, data) {
// 统一不同提供商的响应格式
switch(provider.toLowerCase()) {
case 'openai':
return {
text: data.choices[0].message.content,
usage: data.usage
};
case 'anthropic':
return {
text: data.content[0].text,
usage: data.usage
};
case 'gemini':
return {
text: data.candidates[0].content.parts[0].text,
usage: null // Gemini目前不提供使用量数据
};
default:
return data;
}
}
}
module.exports = new AIService();
3.2 创建控制器
server/controllers/aiController.js
:
javascript
const aiService = require('../services/aiService');
exports.chat = async (req, res) => {
try {
const { provider, message, model, temperature, maxTokens } = req.body;
// 验证输入
if (!provider || !message) {
return res.status(400).json({ error: 'Provider and message are required' });
}
// 构建提供商特定的payload
let payload;
switch(provider.toLowerCase()) {
case 'openai':
payload = {
model: model || 'gpt-3.5-turbo',
messages: [{ role: 'user', content: message }],
temperature: temperature || 0.7,
max_tokens: maxTokens || 1000
};
break;
case 'anthropic':
payload = {
model: model || 'claude-2.1',
messages: [{ role: 'user', content: message }],
max_tokens: maxTokens || 1000,
temperature: temperature || 0.7
};
break;
case 'gemini':
payload = {
prompt: message,
// Gemini参数略有不同
generationConfig: {
temperature: temperature || 0.7,
maxOutputTokens: maxTokens || 1000
}
};
break;
default:
return res.status(400).json({ error: 'Unsupported provider' });
}
const result = await aiService.chatCompletion(provider, payload);
res.json(result);
} catch (error) {
console.error('Chat error:', error);
res.status(500).json({ error: error.message });
}
};
exports.listModels = async (req, res) => {
try {
const { provider } = req.params;
// 在实际应用中,你可能需要缓存这些模型列表
const models = {
openai: ['gpt-4', 'gpt-3.5-turbo', 'text-davinci-003'],
anthropic: ['claude-2.1', 'claude-instant-1.2'],
gemini: ['gemini-pro', 'gemini-ultra']
};
if (!models[provider]) {
return res.status(404).json({ error: 'Provider not found' });
}
res.json(models[provider]);
} catch (error) {
console.error('List models error:', error);
res.status(500).json({ error: error.message });
}
};
3.3 创建路由
server/routes/aiRoutes.js
:
javascript
const express = require('express');
const router = express.Router();
const aiController = require('../controllers/aiController');
// 聊天接口
router.post('/chat', aiController.chat);
// 获取模型列表
router.get('/models/:provider', aiController.listModels);
module.exports = router;
3.4 创建Express服务器
server/server.js
:
javascript
const express = require('express');
const cors = require('cors');
const dotenv = require('dotenv');
const aiRoutes = require('./routes/aiRoutes');
dotenv.config();
const app = express();
const port = process.env.PORT || 3000;
// 中间件
app.use(cors({
origin: process.env.CORS_ORIGIN || '*'
}));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// 路由
app.use('/api/ai', aiRoutes);
// 健康检查
app.get('/health', (req, res) => {
res.status(200).json({ status: 'healthy' });
});
// 错误处理
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ error: 'Something went wrong!' });
});
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
四、前端集成
4.1 基本HTML结构
client/public/index.html
:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI Integration Demo</title>
<link rel="stylesheet" href="/css/styles.css">
</head>
<body>
<div class="container">
<h1>AI Chat Interface</h1>
<div class="provider-selector">
<label for="provider">AI Provider:</label>
<select id="provider">
<option value="openai">OpenAI</option>
<option value="anthropic">Anthropic</option>
<option value="gemini">Google Gemini</option>
</select>
<label for="model">Model:</label>
<select id="model">
<!-- 动态加载 -->
</select>
</div>
<div class="chat-container">
<div id="chat-history" class="chat-history"></div>
<div class="input-area">
<textarea id="user-input" placeholder="Type your message here..."></textarea>
<button id="send-button">Send</button>
</div>
</div>
<div class="settings">
<label>
Temperature:
<input type="range" id="temperature" min="0" max="1" step="0.1" value="0.7">
<span id="temp-value">0.7</span>
</label>
<label>
Max Tokens:
<input type="number" id="max-tokens" min="100" max="4000" value="1000">
</label>
</div>
</div>
<script src="/js/app.js"></script>
</body>
</html>
4.2 JavaScript交互逻辑
client/public/js/app.js
:
javascript
document.addEventListener('DOMContentLoaded', () => {
const providerSelect = document.getElementById('provider');
const modelSelect = document.getElementById('model');
const chatHistory = document.getElementById('chat-history');
const userInput = document.getElementById('user-input');
const sendButton = document.getElementById('send-button');
const temperatureSlider = document.getElementById('temperature');
const tempValueDisplay = document.getElementById('temp-value');
const maxTokensInput = document.getElementById('max-tokens');
// 初始化模型列表
providerSelect.addEventListener('change', async () => {
const provider = providerSelect.value;
try {
const response = await fetch(`/api/ai/models/${provider}`);
const models = await response.json();
modelSelect.innerHTML = '';
models.forEach(model => {
const option = document.createElement('option');
option.value = model;
option.textContent = model;
modelSelect.appendChild(option);
});
} catch (error) {
console.error('Failed to load models:', error);
alert('Failed to load models. Check console for details.');
}
});
// 触发初始加载
providerSelect.dispatchEvent(new Event('change'));
// 温度滑块显示
temperatureSlider.addEventListener('input', () => {
tempValueDisplay.textContent = temperatureSlider.value;
});
// 发送消息
sendButton.addEventListener('click', sendMessage);
userInput.addEventListener('keydown', (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
});
async function sendMessage() {
const message = userInput.value.trim();
if (!message) return;
const provider = providerSelect.value;
const model = modelSelect.value;
const temperature = parseFloat(temperatureSlider.value);
const maxTokens = parseInt(maxTokensInput.value);
// 添加到聊天历史
addMessageToHistory('user', message);
userInput.value = '';
try {
// 显示加载状态
const loadingId = addMessageToHistory('assistant', 'Thinking...', true);
const response = await fetch('/api/ai/chat', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
provider,
message,
model,
temperature,
maxTokens
})
});
const data = await response.json();
// 更新加载中的消息
updateMessageInHistory(loadingId, 'assistant', data.text);
} catch (error) {
console.error('Chat error:', error);
addMessageToHistory('assistant', `Error: ${error.message}`);
}
}
function addMessageToHistory(role, content, isLoading = false) {
const messageId = `msg-${Date.now()}`;
const messageElement = document.createElement('div');
messageElement.id = messageId;
messageElement.className = `message ${role} ${isLoading ? 'loading' : ''}`;
messageElement.textContent = content;
chatHistory.appendChild(messageElement);
chatHistory.scrollTop = chatHistory.scrollHeight;
return messageId;
}
function updateMessageInHistory(id, role, content) {
const messageElement = document.getElementById(id);
if (messageElement) {
messageElement.className = `message ${role}`;
messageElement.textContent = content;
}
}
});
4.3 基本样式
client/public/css/styles.css
:
css
body {
font-family: Arial, sans-serif;
line-height: 1.6;
margin: 0;
padding: 20px;
background-color: #f5f5f5;
}
.container {
max-width: 800px;
margin: 0 auto;
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.provider-selector {
display: flex;
gap: 15px;
margin-bottom: 20px;
align-items: center;
}
.chat-container {
border: 1px solid #ddd;
border-radius: 8px;
overflow: hidden;
}
.chat-history {
height: 400px;
overflow-y: auto;
padding: 15px;
background: #fafafa;
}
.message {
margin-bottom: 15px;
padding: 10px 15px;
border-radius: 18px;
max-width: 80%;
}
.message.user {
background: #007bff;
color: white;
margin-left: auto;
border-bottom-right-radius: 4px;
}
.message.assistant {
background: #e9ecef;
margin-right: auto;
border-bottom-left-radius: 4px;
}
.message.loading {
color: #666;
font-style: italic;
}
.input-area {
display: flex;
border-top: 1px solid #ddd;
padding: 10px;
background: white;
}
.input-area textarea {
flex: 1;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
resize: none;
min-height: 60px;
}
.input-area button {
margin-left: 10px;
padding: 0 20px;
background: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.input-area button:hover {
background: #0056b3;
}
.settings {
margin-top: 20px;
padding: 15px;
background: #f8f9fa;
border-radius: 8px;
}
.settings label {
display: block;
margin-bottom: 10px;
}
.settings input[type="range"] {
width: 200px;
vertical-align: middle;
}
五、测试与优化
5.1 API测试脚本
scripts/test-api.js
:
javascript
const axios = require('axios');
const dotenv = require('dotenv');
dotenv.config();
const BASE_URL = 'http://localhost:3000/api/ai';
async function testChat(provider, message) {
try {
console.log(`Testing ${provider} with message: "${message}"`);
const response = await axios.post(`${BASE_URL}/chat`, {
provider,
message,
temperature: 0.7,
maxTokens: 500
});
console.log('Response:', {
provider,
text: response.data.text,
usage: response.data.usage
});
} catch (error) {
console.error(`Error with ${provider}:`, error.response?.data || error.message);
}
}
async function runTests() {
const testMessage = "Explain the concept of artificial intelligence in simple terms";
await testChat('openai', testMessage);
await testChat('anthropic', testMessage);
await testChat('gemini', testMessage);
}
runTests();
5.2 性能优化建议
- 缓存层实现:
javascript
// 在aiService.js中添加缓存
const NodeCache = require('node-cache');
const cache = new NodeCache({ stdTTL: 600 }); // 10分钟缓存
class AIService {
async chatCompletion(provider, payload) {
const cacheKey = `${provider}:${JSON.stringify(payload)}`;
const cached = cache.get(cacheKey);
if (cached) return cached;
// ...原有逻辑...
const result = this._formatResponse(provider, response.data);
cache.set(cacheKey, result);
return result;
}
}
- 速率限制中间件:
javascript
// 创建rateLimiter.js
const rateLimit = require('express-rate-limit');
const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15分钟
max: 100, // 每个IP限制100次请求
message: 'Too many requests from this IP, please try again later'
});
module.exports = apiLimiter;
// 在aiRoutes.js中使用
const apiLimiter = require('../middleware/rateLimiter');
router.post('/chat', apiLimiter, aiController.chat);
- 负载均衡策略:
javascript
// 在aiService.js中添加提供商轮询
class AIService {
constructor() {
this.providerQueue = ['openai', 'anthropic', 'gemini'];
this.currentIndex = 0;
}
async smartChatCompletion(payload) {
// 简单轮询负载均衡
const provider = this.providerQueue[this.currentIndex];
this.currentIndex = (this.currentIndex + 1) % this.providerQueue.length;
try {
return await this.chatCompletion(provider, payload);
} catch (error) {
console.log(`Provider ${provider} failed, trying next`);
return this.smartChatCompletion(payload); // 递归尝试下一个
}
}
}
六、部署与监控
6.1 生产环境部署
- 使用PM2进行进程管理:
bash
npm install pm2 -g
pm2 start server/server.js --name "ai-api"
pm2 save
pm2 startup
- 环境变量管理:
bash
# 创建生产环境.env文件
echo "NODE_ENV=production" > .env.production
echo "OPENAI_API_KEY=prod_key_here" >> .env.production
# ...其他变量...
- Docker化部署:
dockerfile
# Dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install --production
COPY . .
ENV NODE_ENV production
ENV PORT 3000
EXPOSE 3000
CMD ["node", "server/server.js"]
6.2 监控与日志
- 添加健康检查端点:
javascript
// 在server.js中添加
const health = require('express-ping');
app.use(health.ping());
- 日志记录中间件:
javascript
const fs = require('fs');
const morgan = require('morgan');
// 创建日志目录
const logDir = './logs';
if (!fs.existsSync(logDir)) {
fs.mkdirSync(logDir);
}
// 设置日志
app.use(morgan('combined', {
stream: fs.createWriteStream(`${logDir}/access.log`, { flags: 'a' })
}));
- 错误跟踪集成:
javascript
// 使用Sentry进行错误跟踪
const Sentry = require('@sentry/node');
Sentry.init({
dsn: process.env.SENTRY_DSN,
tracesSampleRate: 1.0
});
app.use(Sentry.Handlers.requestHandler());
app.use(Sentry.Handlers.errorHandler());
七、安全最佳实践
- API密钥轮换:
javascript
// 实现密钥自动轮换
const keyVault = {
openai: {
current: process.env.OPENAI_API_KEY,
backup: process.env.OPENAI_API_KEY_BACKUP
}
// 其他提供商...
};
function rotateKey(provider) {
if (keyVault[provider]?.backup) {
const temp = keyVault[provider].current;
keyVault[provider].current = keyVault[provider].backup;
keyVault[provider].backup = temp;
console.log(`Rotated ${provider} API key`);
}
}
// 定期轮换或基于错误触发
setInterval(() => rotateKey('openai'), 86400000); // 每天轮换
- 请求验证:
javascript
// 在aiController.js中添加验证中间件
exports.validateRequest = (req, res, next) => {
const { provider, message } = req.body;
// 检查必需字段
if (!provider || !message) {
return res.status(400).json({ error: 'Missing required fields' });
}
// 检查消息长度
if (message.length > 10000) {
return res.status(400).json({ error: 'Message too long' });
}
// 检查支持的提供商
const validProviders = ['openai', 'anthropic', 'gemini'];
if (!validProviders.includes(provider)) {
return res.status(400).json({ error: 'Invalid provider' });
}
next();
};
// 在路由中使用
router.post('/chat', aiController.validateRequest, aiController.chat);
- 输出内容过滤:
javascript
// 在aiService.js中添加内容过滤
const badWords = ['敏感词1', '敏感词2']; // 实际应用中应该从数据库或文件加载
function filterContent(text) {
let filtered = text;
badWords.forEach(word => {
const regex = new RegExp(word, 'gi');
filtered = filtered.replace(regex, '***');
});
return filtered;
}
// 在_formatResponse方法中应用
_formatResponse(provider, data) {
const result = ...; // 原有逻辑
result.text = filterContent(result.text);
return result;
}
八、成本优化策略
- 使用量监控:
javascript
// 在aiService.js中添加使用量跟踪
class AIService {
constructor() {
this.usageStats = {
openai: { count: 0, tokens: 0 },
anthropic: { count: 0, tokens: 0 },
gemini: { count: 0 }
};
}
async chatCompletion(provider, payload) {
// ...原有逻辑...
// 更新使用统计
this.usageStats[provider].count += 1;
if (result.usage) {
this.usageStats[provider].tokens += result.usage.total_tokens || 0;
}
return result;
}
getUsageStats() {
return this.usageStats;
}
}
// 添加统计端点
router.get('/stats', (req, res) => {
res.json(aiService.getUsageStats());
});
- 成本限制中间件:
javascript
// 创建costLimiter.js
class CostLimiter {
constructor(monthlyLimit) {
this.monthlyLimit = monthlyLimit;
this.currentUsage = 0;
this.resetDate = new Date();
this.resetDate.setMonth(this.resetDate.getMonth() + 1);
}
checkLimit(estimatedCost) {
if (this.currentUsage + estimatedCost > this.monthlyLimit) {
return false;
}
this.currentUsage += estimatedCost;
return true;
}
timeUntilReset() {
return this.resetDate - new Date();
}
}
// 使用示例
const costLimiter = new CostLimiter(100); // $100每月限制
// 在路由中使用
router.post('/chat', (req, res, next) => {
const estimatedCost = 0.02; // 根据模型和token数估算
if (!costLimiter.checkLimit(estimatedCost)) {
return res.status(429).json({
error: 'Monthly cost limit exceeded',
resetIn: costLimiter.timeUntilReset()
});
}
next();
});
结语
通过本文的详细指南,您已经掌握了如何在Node.js全栈应用中集成多个AI提供商的API token。关键要点包括:
- 安全地管理和轮换API密钥
- 统一不同AI提供商的接口规范
- 实现前后端完整的交互流程
- 添加性能优化和安全防护措施
- 监控使用量和控制成本
随着AI技术的快速发展,建议定期更新各提供商的SDK和API规范,同时关注新兴的AI服务提供商,不断扩展您的应用能力。