基于硅基流动API构建智能聊天应用的完整指南

一、引言:AI编程工具重塑开发范式
人工智能编程工具正在彻底改变软件开发的方式,使开发者能够快速构建以前需要大量专业知识的复杂应用。本文将深入探讨如何使用硅基流动(SiliconFlow)的API,结合现代Web技术,构建一个功能完整的智能聊天应用。
硅基流动平台提供了强大的自然语言处理能力,基于类似GPT的大模型技术,让开发者能够轻松集成先进的AI对话功能到自己的应用中。我们将从基础概念开始,逐步构建一个具有美观界面、完整功能和性能优化的HTML应用。
1.1 硅基流动API概述
硅基流动API提供了类似于OpenAI的接口规范,但具有更灵活的配置选项和更具竞争力的定价策略。其主要特点包括:
- 多模型支持:提供多种预训练模型选择,从轻量级到超大参数模型
- 灵活参数调整:支持温度(temperature)、top_p、top_k等精细控制参数
- 思考预算机制:独特的enable_thinking和thinking_budget参数控制模型推理深度
- 流式响应:支持SSE(Server-Sent Events)实现实时流式输出
1.2 技术栈选择
本文将使用以下技术栈构建应用:
- 前端:HTML5、CSS3、JavaScript(ES6+)
- 样式框架:Tailwind CSS用于快速UI开发
- API通信:Fetch API与硅基流动REST端点交互
- 状态管理:自定义轻量级状态管理方案
- 构建工具:使用Vite作为构建工具(可选)
二、硅基流动API核心机制解析
2.1 API请求结构与参数详解
硅基流动API的请求基于标准的HTTP POST请求,包含特定的头部和主体参数:
javascript
const url = 'https://api.siliconflow.com/v1/chat/completions';
const options = {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY_HERE',
'Content-Type': 'application/json'
},
body: JSON.stringify({
"model": "openai/gpt-oss-20b",
"max_tokens": 512,
"enable_thinking": true,
"thinking_budget": 4096,
"min_p": 0.05,
"temperature": 0.7,
"top_p": 0.7,
"top_k": 50,
"frequency_penalty": 0.5,
"n": 1,
"messages": [
{
"content": "how are you today",
"role": "user"
}
]
})
};
2.1.1 关键参数解析
- model: 指定使用的模型,硅基流动提供多种预训练模型选择
- max_tokens: 控制响应生成的最大token数量
- enable_thinking: 启用模型的深度思考模式
- thinking_budget: 设置模型思考过程的计算预算
- temperature: 控制生成随机性的参数(0-1之间)
- top_p: 核采样参数,控制候选token集合的大小
- top_k: 从概率最高的k个token中采样
- frequency_penalty: 频率惩罚,降低重复token的概率
- messages: 对话历史数组,包含角色(role)和内容(content)
2.2 API响应结构分析
成功的API调用将返回JSON格式的响应,包含以下关键字段:
javascript
{
"id": "chatcmpl-7Z5qD5z5QXqQXqQXqQXqQXqQXqQXq",
"object": "chat.completion",
"created": 1686657285,
"model": "openai/gpt-oss-20b",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "I'm doing well, thank you for asking! How can I assist you today?"
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 10,
"completion_tokens": 15,
"total_tokens": 25
}
}
三、应用架构设计与实现
3.1 项目结构与初始化
首先创建项目的基本结构:
siliconflow-chat-app/
├── index.html # 主HTML文件
├── styles/
│ └── main.css # 自定义样式
├── scripts/
│ ├── app.js # 主应用逻辑
│ ├── api.js # API通信模块
│ └── ui.js # UI管理模块
├── assets/
│ └── icons/ # 图标资源
└── config.js # 配置参数
3.1.1 HTML基础结构
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>硅基流动智能聊天应用</title>
<!-- Tailwind CSS CDN -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- 自定义样式 -->
<link rel="stylesheet" href="./styles/main.css">
<!-- 图标库 -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
</head>
<body class="bg-gray-100 h-screen flex flex-col">
<!-- 导航栏 -->
<header class="bg-blue-600 text-white p-4 shadow-md">
<div class="container mx-auto flex justify-between items-center">
<h1 class="text-2xl font-bold">硅基流动智能助手</h1>
<div class="flex items-center space-x-4">
<button id="settings-btn" class="p-2 rounded hover:bg-blue-700">
<i class="fas fa-cog"></i>
</button>
<button id="clear-btn" class="p-2 rounded hover:bg-blue-700">
<i class="fas fa-broom"></i>
</button>
</div>
</div>
</header>
<!-- 主聊天区域 -->
<main class="flex-1 container mx-auto p-4 overflow-y-auto" id="chat-container">
<div class="w-full max-w-4xl mx-auto bg-white rounded-lg shadow-md h-full flex flex-col">
<div class="flex-1 overflow-y-auto p-4 space-y-4" id="message-container">
<!-- 消息将动态添加到这里 -->
<div class="message ai-message flex items-start">
<div class="bg-blue-100 p-3 rounded-lg max-w-xs md:max-w-md">
<p>您好!我是基于硅基流动API构建的智能助手,有什么我可以帮助您的吗?</p>
</div>
</div>
</div>
<!-- 输入区域 -->
<div class="border-t p-4">
<div class="flex space-x-2">
<textarea
id="message-input"
placeholder="输入您的问题..."
class="flex-1 p-3 border rounded-lg resize-none focus:outline-none focus:ring-2 focus:ring-blue-500"
rows="1"
></textarea>
<button
id="send-btn"
class="bg-blue-600 text-white p-3 rounded-lg hover:bg-blue-700 transition disabled:opacity-50"
>
<i class="fas fa-paper-plane"></i>
</button>
</div>
<div class="mt-2 flex flex-wrap gap-2">
<button class="param-btn active" data-param="temperature" data-value="0.7">创意模式</button>
<button class="param-btn" data-param="temperature" data-value="0.2">精确模式</button>
<button class="param-btn" data-param="enable_thinking" data-value="true">深度思考</button>
</div>
</div>
</div>
</main>
<!-- 设置面板 -->
<div id="settings-panel" class="fixed inset-y-0 right-0 w-80 bg-white shadow-xl transform translate-x-full transition-transform duration-300">
<div class="p-4 border-b">
<div class="flex justify-between items-center">
<h2 class="text-lg font-bold">设置</h2>
<button id="close-settings">
<i class="fas fa-times"></i>
</button>
</div>
</div>
<div class="p-4 space-y-4">
<div>
<label class="block text-sm font-medium mb-1">API密钥</label>
<input type="password" id="api-key" class="w-full p-2 border rounded">
</div>
<div>
<label class="block text-sm font-medium mb-1">选择模型</label>
<select id="model-select" class="w-full p-2 border rounded">
<option value="openai/gpt-oss-20b">GPT OSS 20B</option>
<option value="openai/gpt-oss-40b">GPT OSS 40B</option>
<option value="siliconflow/llama2-70b">Llama 2 70B</option>
</select>
</div>
<div>
<label class="block text-sm font-medium mb-1">温度: <span id="temp-value">0.7</span></label>
<input type="range" id="temp-slider" min="0" max="1" step="0.1" value="0.7" class="w-full">
</div>
<div>
<label class="block text-sm font-medium mb-1">最大生成长度: <span id="max-tokens-value">512</span></label>
<input type="range" id="max-tokens-slider" min="50" max="1024" step="50" value="512" class="w-full">
</div>
<button id="save-settings" class="w-full bg-blue-600 text-white p-2 rounded">保存设置</button>
</div>
</div>
<!-- 脚本引入 -->
<script src="./scripts/config.js"></script>
<script src="./scripts/api.js"></script>
<script src="./scripts/ui.js"></script>
<script src="./scripts/app.js"></script>
</body>
</html>
3.2 核心API通信模块
创建API通信处理模块,封装与硅基流动API的所有交互:
javascript
// scripts/api.js
class SiliconFlowAPI {
constructor() {
this.baseURL = 'https://api.siliconflow.com/v1/chat/completions';
this.apiKey = '';
this.model = 'openai/gpt-oss-20b';
this.defaultParams = {
max_tokens: 512,
enable_thinking: true,
thinking_budget: 4096,
min_p: 0.05,
temperature: 0.7,
top_p: 0.7,
top_k: 50,
frequency_penalty: 0.5,
n: 1
};
}
// 配置API参数
configure(config) {
if (config.apiKey) this.apiKey = config.apiKey;
if (config.model) this.model = config.model;
if (config.params) {
this.defaultParams = { ...this.defaultParams, ...config.params };
}
}
// 发送消息到API
async sendMessage(messages, params = {}) {
if (!this.apiKey) {
throw new Error('API密钥未设置,请在设置面板中配置');
}
const requestParams = { ...this.defaultParams, ...params };
const requestOptions = {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: this.model,
messages: messages,
...requestParams
})
};
try {
const response = await fetch(this.baseURL, requestOptions);
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.error?.message || `API请求失败: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error('API请求错误:', error);
throw error;
}
}
// 流式传输消息
async sendMessageStream(messages, params = {}, onChunk) {
if (!this.apiKey) {
throw new Error('API密钥未设置,请在设置面板中配置');
}
const requestParams = { ...this.defaultParams, ...params, stream: true };
const requestOptions = {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: this.model,
messages: messages,
...requestParams
})
};
try {
const response = await fetch(this.baseURL, requestOptions);
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.error?.message || `API请求失败: ${response.status}`);
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { value, done } = 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: ') && line !== 'data: [DONE]') {
try {
const data = JSON.parse(line.slice(6));
if (data.choices && data.choices[0].delta.content) {
onChunk(data.choices[0].delta.content);
}
} catch (e) {
console.warn('解析流数据失败:', e, line);
}
}
}
}
} catch (error) {
console.error('API流式请求错误:', error);
throw error;
}
}
// 获取可用模型列表
async getModels() {
if (!this.apiKey) {
throw new Error('API密钥未设置');
}
try {
const response = await fetch('https://api.siliconflow.com/v1/models', {
headers: {
'Authorization': `Bearer ${this.apiKey}`
}
});
if (!response.ok) {
throw new Error(`获取模型列表失败: ${response.status}`);
}
const data = await response.json();
return data.data || [];
} catch (error) {
console.error('获取模型列表错误:', error);
throw error;
}
}
}
// 创建全局API实例
window.siliconFlowAPI = new SiliconFlowAPI();
3.3 UI管理模块
创建UI管理模块,处理所有界面交互和状态管理:
javascript
// scripts/ui.js
class ChatUI {
constructor() {
this.messageContainer = document.getElementById('message-container');
this.messageInput = document.getElementById('message-input');
this.sendButton = document.getElementById('send-btn');
this.settingsPanel = document.getElementById('settings-panel');
this.apiKeyInput = document.getElementById('api-key');
this.modelSelect = document.getElementById('model-select');
this.tempSlider = document.getElementById('temp-slider');
this.tempValue = document.getElementById('temp-value');
this.maxTokensSlider = document.getElementById('max-tokens-slider');
this.maxTokensValue = document.getElementById('max-tokens-value');
this.isGenerating = false;
this.conversationHistory = [];
this.initializeEventListeners();
this.loadSettings();
this.adjustTextareaHeight();
}
// 初始化事件监听器
initializeEventListeners() {
// 发送消息事件
this.sendButton.addEventListener('click', () => this.sendMessage());
this.messageInput.addEventListener('keydown', (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
this.sendMessage();
}
});
// 输入框高度自适应
this.messageInput.addEventListener('input', () => this.adjustTextareaHeight());
// 设置面板事件
document.getElementById('settings-btn').addEventListener('click', () => this.openSettings());
document.getElementById('close-settings').addEventListener('click', () => this.closeSettings());
document.getElementById('save-settings').addEventListener('click', () => this.saveSettings());
// 清空对话事件
document.getElementById('clear-btn').addEventListener('click', () => this.clearConversation());
// 参数按钮事件
document.querySelectorAll('.param-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
this.toggleParamButton(e.target);
});
});
// 滑块事件
this.tempSlider.addEventListener('input', (e) => {
this.tempValue.textContent = e.target.value;
});
this.maxTokensSlider.addEventListener('input', (e) => {
this.maxTokensValue.textContent = e.target.value;
});
}
// 发送消息处理
async sendMessage() {
const message = this.messageInput.value.trim();
if (!message || this.isGenerating) return;
// 添加用户消息到界面
this.addMessage(message, 'user');
this.conversationHistory.push({ role: 'user', content: message });
// 清空输入框
this.messageInput.value = '';
this.adjustTextareaHeight();
// 禁用输入和发送按钮
this.setGeneratingState(true);
try {
// 添加AI消息占位符
const messageId = this.addMessage('思考中...', 'ai', true);
// 收集当前参数设置
const params = {
temperature: parseFloat(this.tempSlider.value),
max_tokens: parseInt(this.maxTokensSlider.value),
enable_thinking: document.querySelector('[data-param="enable_thinking"][data-value="true"]')?.classList.contains('active') || false
};
let fullResponse = '';
// 使用流式API
await siliconFlowAPI.sendMessageStream(
this.conversationHistory,
params,
(chunk) => {
fullResponse += chunk;
this.updateMessage(messageId, fullResponse);
}
);
// 更新对话历史
this.conversationHistory.push({ role: 'assistant', content: fullResponse });
} catch (error) {
this.updateMessage(messageId, `错误: ${error.message}`);
console.error('发送消息错误:', error);
} finally {
this.setGeneratingState(false);
}
}
// 添加消息到界面
addMessage(content, role, isStreaming = false) {
const messageDiv = document.createElement('div');
const messageId = 'msg-' + Date.now();
messageDiv.className = `message ${role}-message flex items-start ${isStreaming ? 'streaming' : ''}`;
messageDiv.id = messageId;
// 添加头像
const avatar = document.createElement('div');
avatar.className = `w-8 h-8 rounded-full flex items-center justify-center mr-3 ${role === 'user' ? 'bg-blue-500' : 'bg-green-500'}`;
avatar.innerHTML = `<i class="fas ${role === 'user' ? 'fa-user' : 'fa-robot'} text-white"></i>`;
// 添加消息内容
const contentDiv = document.createElement('div');
contentDiv.className = `p-3 rounded-lg max-w-xs md:max-w-md ${role === 'user' ? 'bg-blue-600 text-white' : 'bg-gray-100'}`;
// 处理换行和格式化
const formattedContent = content.replace(/\n/g, '<br>');
contentDiv.innerHTML = formattedContent;
messageDiv.appendChild(avatar);
messageDiv.appendChild(contentDiv);
this.messageContainer.appendChild(messageDiv);
// 滚动到底部
this.scrollToBottom();
return messageId;
}
// 更新消息内容
updateMessage(messageId, content) {
const messageDiv = document.getElementById(messageId);
if (!messageDiv) return;
const contentDiv = messageDiv.querySelector('div:last-child');
const formattedContent = content.replace(/\n/g, '<br>');
contentDiv.innerHTML = formattedContent;
// 移除流式状态类
messageDiv.classList.remove('streaming');
this.scrollToBottom();
}
// 设置生成状态
setGeneratingState(isGenerating) {
this.isGenerating = isGenerating;
this.sendButton.disabled = isGenerating;
this.messageInput.disabled = isGenerating;
if (isGenerating) {
this.sendButton.innerHTML = '<i class="fas fa-spinner fa-spin"></i>';
} else {
this.sendButton.innerHTML = '<i class="fas fa-paper-plane"></i>';
}
}
// 调整文本区域高度
adjustTextareaHeight() {
this.messageInput.style.height = 'auto';
this.messageInput.style.height = Math.min(this.messageInput.scrollHeight, 120) + 'px';
}
// 滚动到底部
scrollToBottom() {
this.messageContainer.scrollTop = this.messageContainer.scrollHeight;
}
// 打开设置面板
openSettings() {
this.settingsPanel.classList.remove('translate-x-full');
}
// 关闭设置面板
closeSettings() {
this.settingsPanel.classList.add('translate-x-full');
}
// 保存设置
saveSettings() {
const settings = {
apiKey: this.apiKeyInput.value,
model: this.modelSelect.value,
params: {
temperature: parseFloat(this.tempSlider.value),
max_tokens: parseInt(this.maxTokensSlider.value)
}
};
localStorage.setItem('siliconFlowSettings', JSON.stringify(settings));
// 更新API配置
siliconFlowAPI.configure(settings);
this.closeSettings();
alert('设置已保存!');
}
// 加载设置
loadSettings() {
const savedSettings = localStorage.getItem('siliconFlowSettings');
if (savedSettings) {
const settings = JSON.parse(savedSettings);
this.apiKeyInput.value = settings.apiKey || '';
this.modelSelect.value = settings.model || 'openai/gpt-oss-20b';
this.tempSlider.value = settings.params?.temperature || 0.7;
this.tempValue.textContent = this.tempSlider.value;
this.maxTokensSlider.value = settings.params?.max_tokens || 512;
this.maxTokensValue.textContent = this.maxTokensSlider.value;
// 配置API
siliconFlowAPI.configure(settings);
}
}
// 切换参数按钮状态
toggleParamButton(button) {
const param = button.dataset.param;
const value = button.dataset.value;
if (param === 'enable_thinking') {
// 切换按钮状态
button.classList.toggle('active');
} else {
// 处理互斥按钮组
document.querySelectorAll(`[data-param="${param}"]`).forEach(btn => {
btn.classList.remove('active');
});
button.classList.add('active');
}
}
// 清空对话
clearConversation() {
if (confirm('确定要清空当前对话吗?')) {
this.messageContainer.innerHTML = '';
this.conversationHistory = [];
// 添加欢迎消息
this.addMessage('您好!我是基于硅基流动API构建的智能助手,有什么我可以帮助您的吗?', 'ai');
}
}
}
3.4 主应用模块
创建主应用模块,初始化整个应用:
javascript
// scripts/app.js
class ChatApplication {
constructor() {
this.ui = null;
this.initializeApp();
}
// 初始化应用
initializeApp() {
document.addEventListener('DOMContentLoaded', () => {
this.ui = new ChatUI();
this.applyCustomStyles();
this.checkApiKey();
});
}
// 应用自定义样式
applyCustomStyles() {
const style = document.createElement('style');
style.textContent = `
.message {
margin-bottom: 1rem;
animation: fadeIn 0.3s ease-in;
}
.user-message {
flex-direction: row-reverse;
}
.user-message div:last-child {
background-color: #3b82f6;
color: white;
}
.ai-message {
flex-direction: row;
}
.streaming div:last-child::after {
content: '▊';
animation: blink 1s infinite;
}
.param-btn {
padding: 0.25rem 0.5rem;
border-radius: 0.375rem;
font-size: 0.875rem;
background-color: #f3f4f6;
border: 1px solid #e5e7eb;
transition: all 0.2s;
}
.param-btn.active {
background-color: #3b82f6;
color: white;
border-color: #3b82f6;
}
.param-btn:hover {
background-color: #e5e7eb;
}
.param-btn.active:hover {
background-color: #2563eb;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes blink {
0%, 50% { opacity: 1; }
51%, 100% { opacity: 0; }
}
#message-input {
transition: height 0.2s;
}
#chat-container {
scrollbar-width: thin;
}
#message-container {
scrollbar-width: thin;
scrollbar-color: #cbd5e1 #f1f5f9;
}
#message-container::-webkit-scrollbar {
width: 6px;
}
#message-container::-webkit-scrollbar-track {
background: #f1f5f9;
}
#message-container::-webkit-scrollbar-thumb {
background-color: #cbd5e1;
border-radius: 3px;
}
`;
document.head.appendChild(style);
}
// 检查API密钥
checkApiKey() {
setTimeout(() => {
const savedSettings = localStorage.getItem('siliconFlowSettings');
if (!savedSettings || !JSON.parse(savedSettings).apiKey) {
this.ui.openSettings();
alert('请先配置您的硅基流动API密钥');
}
}, 1000);
}
}
// 启动应用
new ChatApplication();
3.5 配置文件
创建配置文件,存储应用常量:
javascript
// scripts/config.js
const AppConfig = {
DEFAULT_MODEL: 'openai/gpt-oss-20b',
DEFAULT_TEMPERATURE: 0.7,
DEFAULT_MAX_TOKENS: 512,
DEFAULT_THINKING: true,
THINKING_BUDGET: 4096,
API_BASE_URL: 'https://api.siliconflow.com/v1',
ENABLE_STREAMING: true,
MAX_MESSAGE_HISTORY: 20, // 保留最近20条消息作为历史
RATE_LIMIT_DELAY: 1000, // 消息发送最小间隔(毫秒)
VERSION: '1.0.0'
};
// 导出配置
window.AppConfig = AppConfig;
四、高级功能实现
4.1 对话历史管理
扩展UI类,添加对话历史管理功能:
javascript
// 在scripts/ui.js中添加以下方法
class ChatUI {
// ... 原有代码 ...
// 保存对话历史
saveConversation() {
const conversation = {
timestamp: Date.now(),
messages: this.conversationHistory,
model: this.modelSelect.value,
params: {
temperature: parseFloat(this.tempSlider.value),
max_tokens: parseInt(this.maxTokensSlider.value)
}
};
// 获取已保存的对话列表
let conversations = JSON.parse(localStorage.getItem('savedConversations') || '[]');
// 添加新对话
conversations.push(conversation);
// 限制保存的对话数量(最多20个)
if (conversations.length > 20) {
conversations = conversations.slice(-20);
}
localStorage.setItem('savedConversations', JSON.stringify(conversations));
return conversation.timestamp;
}
// 加载对话历史
loadConversation(timestamp) {
const conversations = JSON.parse(localStorage.getItem('savedConversations') || '[]');
const conversation = conversations.find(c => c.timestamp === timestamp);
if (conversation) {
// 清空当前对话
this.messageContainer.innerHTML = '';
this.conversationHistory = [];
// 重新渲染消息
conversation.messages.forEach(msg => {
this.addMessage(msg.content, msg.role);
this.conversationHistory.push(msg);
});
// 更新模型和参数设置
this.modelSelect.value = conversation.model;
this.tempSlider.value = conversation.params.temperature;
this.tempValue.textContent = conversation.params.temperature;
this.maxTokensSlider.value = conversation.params.max_tokens;
this.maxTokensValue.textContent = conversation.params.max_tokens;
// 配置API
siliconFlowAPI.configure({
model: conversation.model,
params: {
temperature: conversation.params.temperature,
max_tokens: conversation.params.max_tokens
}
});
return true;
}
return false;
}
// 导出对话历史
exportConversation(format = 'json') {
const conversation = {
timestamp: Date.now(),
messages: this.conversationHistory,
model: this.modelSelect.value,
params: {
temperature: parseFloat(this.tempSlider.value),
max_tokens: parseInt(this.maxTokensSlider.value)
}
};
let data, filename, type;
if (format === 'json') {
data = JSON.stringify(conversation, null, 2);
filename = `conversation-${Date.now()}.json`;
type = 'application/json';
} else if (format === 'txt') {
data = conversation.messages.map(msg =>
`${msg.role.toUpperCase()}: ${msg.content}`
).join('\n\n');
filename = `conversation-${Date.now()}.txt`;
type = 'text/plain';
}
const blob = new Blob([data], { type });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
URL.revokeObjectURL(url);
}
// 导入对话历史
importConversation(file) {
const reader = new FileReader();
reader.onload = (e) => {
try {
let conversation;
if (file.name.endsWith('.json')) {
conversation = JSON.parse(e.target.result);
} else if (file.name.endsWith('.txt')) {
// 简单解析文本格式
const content = e.target.result;
const messages = [];
let currentRole = null;
let currentContent = [];
content.split('\n').forEach(line => {
if (line.match(/^(USER|ASSISTANT):/)) {
if (currentRole && currentContent.length > 0) {
messages.push({
role: currentRole.toLowerCase(),
content: currentContent.join('\n').trim()
});
}
currentRole = line.split(':')[0];
currentContent = [line.substring(currentRole.length + 1).trim()];
} else {
currentContent.push(line);
}
});
if (currentRole && currentContent.length > 0) {
messages.push({
role: currentRole.toLowerCase(),
content: currentContent.join('\n').trim()
});
}
conversation = {
timestamp: Date.now(),
messages: messages,
model: this.modelSelect.value,
params: {
temperature: parseFloat(this.tempSlider.value),
max_tokens: parseInt(this.maxTokensSlider.value)
}
};
} else {
throw new Error('不支持的格式');
}
// 加载对话
this.loadConversationFromData(conversation);
} catch (error) {
alert('导入失败: ' + error.message);
}
};
reader.readAsText(file);
}
// 从数据加载对话
loadConversationFromData(conversation) {
// 清空当前对话
this.messageContainer.innerHTML = '';
this.conversationHistory = [];
// 重新渲染消息
conversation.messages.forEach(msg => {
this.addMessage(msg.content, msg.role);
this.conversationHistory.push(msg);
});
// 更新模型和参数设置(如果提供了)
if (conversation.model) {
this.modelSelect.value = conversation.model;
}
if (conversation.params) {
if (conversation.params.temperature) {
this.tempSlider.value = conversation.params.temperature;
this.tempValue.textContent = conversation.params.temperature;
}
if (conversation.params.max_tokens) {
this.maxTokensSlider.value = conversation.params.max_tokens;
this.maxTokensValue.textContent = conversation.params.max_tokens;
}
}
// 配置API
siliconFlowAPI.configure({
model: conversation.model || this.modelSelect.value,
params: {
temperature: parseFloat(this.tempSlider.value),
max_tokens: parseInt(this.maxTokensSlider.value)
}
});
}
}
4.2 消息模板和快捷命令
添加快捷命令和消息模板功能:
javascript
// 在scripts/ui.js中添加以下方法
class ChatUI {
// ... 原有代码 ...
// 初始化模板和命令
initTemplatesAndCommands() {
this.templates = {
'代码解释': '请解释以下代码的功能和工作原理:\n```\n[你的代码]\n```',
'错误调试': '我遇到了以下错误,请帮我分析原因和解决方案:\n```\n[错误信息]\n```',
'文章写作': '请帮我写一篇关于[主题]的文章,要求:\n- 字数约800字\n- 结构清晰\n- 包含实际案例',
'学习计划': '我想学习[技能/科目],请帮我制定一个为期4周的学习计划,包括:\n- 每周学习目标\n- 推荐资源\n- 实践项目'
};
this.commands = {
'/clear': () => this.clearConversation(),
'/export': () => this.exportConversation('json'),
'/export txt': () => this.exportConversation('txt'),
'/help': () => this.showHelp(),
'/model': (args) => this.changeModel(args),
'/temp': (args) => this.changeTemperature(args)
};
}
// 处理命令
processCommand(input) {
const parts = input.split(' ');
const command = parts[0].toLowerCase();
const args = parts.slice(1).join(' ');
if (this.commands[command]) {
this.commands[command](args);
return true;
}
return false;
}
// 显示帮助信息
showHelp() {
const helpMessage = `
可用命令:
/clear - 清空当前对话
/export - 导出对话为JSON
/export txt - 导出对话为文本
/model [模型名] - 切换模型
/temp [值] - 设置温度参数(0-1)
/help - 显示帮助信息
可用模板:
${Object.keys(this.templates).map(t => `- ${t}`).join('\n')}
`.trim();
this.addMessage(helpMessage, 'ai');
}
// 更改模型
changeModel(modelName) {
if (!modelName) {
this.addMessage('请指定模型名称,例如: /model openai/gpt-oss-20b', 'ai');
return;
}
this.modelSelect.value = modelName;
siliconFlowAPI.configure({ model: modelName });
this.addMessage(`已切换模型为: ${modelName}`, 'ai');
this.saveSettings();
}
// 更改温度
changeTemperature(tempValue) {
const temp = parseFloat(tempValue);
if (isNaN(temp) || temp < 0 || temp > 1) {
this.addMessage('温度值必须在0到1之间,例如: /temp 0.7', 'ai');
return;
}
this.tempSlider.value = temp;
this.tempValue.textContent = temp;
siliconFlowAPI.configure({
params: {
temperature: temp,
max_tokens: parseInt(this.maxTokensSlider.value)
}
});
this.addMessage(`已设置温度为: ${temp}`, 'ai');
this.saveSettings();
}
// 在sendMessage方法中添加命令检查
async sendMessage() {
const message = this.messageInput.value.trim();
if (!message || this.isGenerating) return;
// 检查是否是命令
if (message.startsWith('/')) {
if (this.processCommand(message)) {
this.messageInput.value = '';
this.adjustTextareaHeight();
return;
}
}
// 检查是否是模板
if (message.startsWith('@')) {
const templateName = message.substring(1);
if (this.templates[templateName]) {
this.messageInput.value = this.templates[templateName];
this.adjustTextareaHeight();
return;
}
}
// ... 原有的发送消息逻辑 ...
}
}
4.3 性能优化和错误处理
添加性能监控和错误处理机制:
javascript
// 在scripts/api.js中添加性能监控和错误处理
class SiliconFlowAPI {
// ... 原有代码 ...
// 增强的sendMessage方法,添加性能监控
async sendMessage(messages, params = {}) {
if (!this.apiKey) {
throw new Error('API密钥未设置,请在设置面板中配置');
}
const requestParams = { ...this.defaultParams, ...params };
const startTime = performance.now();
const requestOptions = {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: this.model,
messages: messages,
...requestParams
})
};
try {
const response = await fetch(this.baseURL, requestOptions);
const endTime = performance.now();
const responseTime = endTime - startTime;
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
const error = new Error(errorData.error?.message || `API请求失败: ${response.status}`);
error.status = response.status;
throw error;
}
const data = await response.json();
// 记录性能数据
this.recordPerformance({
model: this.model,
responseTime,
promptTokens: data.usage?.prompt_tokens || 0,
completionTokens: data.usage?.completion_tokens || 0,
totalTokens: data.usage?.total_tokens || 0
});
return data;
} catch (error) {
// 记录错误
this.recordError(error);
throw error;
}
}
// 记录性能数据
recordPerformance(metrics) {
let performanceData = JSON.parse(localStorage.getItem('apiPerformance') || '[]');
performanceData.push({
timestamp: Date.now(),
...metrics
});
// 保留最近100条记录
if (performanceData.length > 100) {
performanceData = performanceData.slice(-100);
}
localStorage.setItem('apiPerformance', JSON.stringify(performanceData));
}
// 记录错误
recordError(error) {
let errorData = JSON.parse(localStorage.getItem('apiErrors') || '[]');
errorData.push({
timestamp: Date.now(),
message: error.message,
status: error.status,
model: this.model
});
// 保留最近50条错误记录
if (errorData.length > 50) {
errorData = errorData.slice(-50);
}
localStorage.setItem('apiErrors', JSON.stringify(errorData));
}
// 获取性能统计
getPerformanceStats() {
const performanceData = JSON.parse(localStorage.getItem('apiPerformance') || '[]');
if (performanceData.length === 0) {
return null;
}
const totalCalls = performanceData.length;
const totalTokens = performanceData.reduce((sum, item) => sum + item.totalTokens, 0);
const avgResponseTime = performanceData.reduce((sum, item) => sum + item.responseTime, 0) / totalCalls;
// 按模型分组统计
const modelStats = {};
performanceData.forEach(item => {
if (!modelStats[item.model]) {
modelStats[item.model] = {
calls: 0,
totalTokens: 0,
totalResponseTime: 0
};
}
modelStats[item.model].calls++;
modelStats[item.model].totalTokens += item.totalTokens;
modelStats[item.model].totalResponseTime += item.responseTime;
});
// 计算每个模型的平均值
Object.keys(modelStats).forEach(model => {
modelStats[model].avgResponseTime = modelStats[model].totalResponseTime / modelStats[model].calls;
modelStats[model].avgTokensPerCall = modelStats[model].totalTokens / modelStats[model].calls;
});
return {
totalCalls,
totalTokens,
avgResponseTime,
modelStats
};
}
}
五、界面优化与响应式设计
5.1 自定义CSS样式
创建更丰富的自定义样式:
css
/* styles/main.css */
/* 基础样式 */
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
/* 消息动画 */
@keyframes messageSlideIn {
from {
opacity: 0;
transform: translateY(1rem) scale(0.95);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
.message {
animation: messageSlideIn 0.3s ease-out;
}
/* 流式消息光标效果 */
.streaming .message-content::after {
content: '▊';
animation: blink 1s infinite;
font-weight: bold;
color: #4b5563;
}
/* 设置面板过渡效果 */
.settings-panel {
transition: transform 0.3s ease-in-out;
}
/* 暗色模式支持 */
@media (prefers-color-scheme: dark) {
.dark-mode {
background-color: #1f2937;
color: #f9fafb;
}
.dark-mode .message.user .message-content {
background-color: #3b82f6;
color: white;
}
.dark-mode .message.ai .message-content {
background-color: #374151;
color: #f9fafb;
}
.dark-mode .param-btn {
background-color: #374151;
border-color: #4b5563;
color: #f9fafb;
}
.dark-mode .param-btn.active {
background-color: #3b82f6;
border-color: #3b82f6;
}
}
/* 移动设备优化 */
@media (max-width: 768px) {
.container {
padding-left: 1rem;
padding-right: 1rem;
}
.message .message-content {
max-width: 85%;
}
.settings-panel {
width: 100%;
}
}
/* 高对比度模式支持 */
@media (prefers-contrast: high) {
.message.user .message-content {
border: 2px solid currentColor;
}
.message.ai .message-content {
border: 2px solid currentColor;
}
}
/* 减少动画模式支持 */
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
/* 自定义滚动条 */
.custom-scrollbar::-webkit-scrollbar {
width: 8px;
}
.custom-scrollbar::-webkit-scrollbar-track {
background: #f1f5f9;
border-radius: 4px;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background: #cbd5e1;
border-radius: 4px;
}
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
background: #94a3b8;
}
/* 加载动画 */
.loading-dots::after {
content: '';
animation: loadingDots 1.5s infinite;
}
@keyframes loadingDots {
0%, 20% {
content: '.';
}
40% {
content: '..';
}
60%, 100% {
content: '...';
}
}
/* 渐入动画 */
.fade-in {
animation: fadeIn 0.5s ease-in;
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
/* 按钮悬停效果 */
.btn-hover {
transition: all 0.2s ease;
}
.btn-hover:hover {
transform: translateY(-1px);
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
}
/* 输入框焦点效果 */
.input-focus:focus {
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.2);
}
5.2 暗色模式支持
添加暗色模式切换功能:
javascript
// 在scripts/ui.js中添加暗色模式支持
class ChatUI {
// ... 原有代码 ...
// 初始化暗色模式
initDarkMode() {
// 检查系统偏好
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const savedMode = localStorage.getItem('darkMode');
// 确定初始模式
let darkMode = false;
if (savedMode !== null) {
darkMode = savedMode === 'true';
} else {
darkMode = prefersDark;
}
// 应用模式
this.toggleDarkMode(darkMode);
// 添加模式切换按钮
this.addDarkModeToggle();
// 监听系统模式变化
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
if (localStorage.getItem('darkMode') === null) {
this.toggleDarkMode(e.matches);
}
});
}
// 添加暗色模式切换按钮
addDarkModeToggle() {
const toggleBtn = document.createElement('button');
toggleBtn.id = 'dark-mode-toggle';
toggleBtn.className = 'p-2 rounded hover:bg-blue-700';
toggleBtn.innerHTML = '<i class="fas fa-moon"></i>';
toggleBtn.title = '切换暗色模式';
toggleBtn.addEventListener('click', () => {
const isDark = document.documentElement.classList.contains('dark');
this.toggleDarkMode(!isDark);
localStorage.setItem('darkMode', !isDark);
});
// 添加到导航栏
document.querySelector('header .flex').prepend(toggleBtn);
}
// 切换暗色模式
toggleDarkMode(enable) {
if (enable) {
document.documentElement.classList.add('dark');
document.getElementById('dark-mode-toggle').innerHTML = '<i class="fas fa-sun"></i>';
} else {
document.documentElement.classList.remove('dark');
document.getElementById('dark-mode-toggle').innerHTML = '<i class="fas fa-moon"></i>';
}
}
// 在initializeEventListeners方法中添加暗色模式初始化
initializeEventListeners() {
// ... 原有代码 ...
this.initDarkMode();
}
}
六、部署与优化
6.1 使用Vite构建优化版本
创建Vite配置文件,优化生产环境部署:
javascript
// vite.config.js
import { defineConfig } from 'vite';
export default defineConfig({
base: './',
build: {
outDir: 'dist',
assetsDir: 'assets',
rollupOptions: {
output: {
manualChunks: undefined,
entryFileNames: 'assets/[name]-[hash].js',
chunkFileNames: 'assets/[name]-[hash].js',
assetFileNames: 'assets/[name]-[hash].[ext]'
}
},
minify: 'terser',
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true
}
}
},
server: {
open: true,
port: 3000
}
});
6.2 创建服务端代理(可选)
对于生产环境,可以创建简单的Node.js代理服务器来避免CORS问题:
javascript
// server/proxy.js
const express = require('express');
const cors = require('cors');
const fetch = require('node-fetch');
const app = express();
const PORT = process.env.PORT || 3001;
app.use(cors());
app.use(express.json());
// 硅基流动API代理端点
app.post('/api/chat', async (req, res) => {
try {
const response = await fetch('https://api.siliconflow.com/v1/chat/completions', {
method: 'POST',
headers: {
'Authorization': req.headers.authorization,
'Content-Type': 'application/json'
},
body: JSON.stringify(req.body)
});
const data = await response.json();
res.json(data);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// 获取模型列表
app.get('/api/models', async (req, res) => {
try {
const response = await fetch('https://api.siliconflow.com/v1/models', {
headers: {
'Authorization': req.headers.authorization
}
});
const data = await response.json();
res.json(data);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
app.listen(PORT, () => {
console.log(`代理服务器运行在端口 ${PORT}`);
});
6.3 PWA支持
添加PWA支持,使应用可以离线使用:
javascript
// sw.js (Service Worker)
const CACHE_NAME = 'siliconflow-chat-v1';
const urlsToCache = [
'/',
'/index.html',
'/styles/main.css',
'/scripts/app.js',
'/scripts/api.js',
'/scripts/ui.js',
'/scripts/config.js',
'/assets/icons/icon-192.png',
'/assets/icons/icon-512.png'
];
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => cache.addAll(urlsToCache))
);
});
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(response => response || fetch(event.request))
);
});
html
<!-- 在index.html中添加manifest -->
<link rel="manifest" href="/manifest.json">
json
// manifest.json
{
"name": "硅基流动智能聊天",
"short_name": "硅基聊天",
"description": "基于硅基流动API的智能聊天应用",
"start_url": "/",
"display": "standalone",
"background_color": "#3b82f6",
"theme_color": "#3b82f6",
"icons": [
{
"src": "/assets/icons/icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/assets/icons/icon-512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
七、测试与调试
7.1 添加调试工具
创建调试面板,方便开发和故障排除:
javascript
// scripts/debug.js
class DebugPanel {
constructor() {
this.isVisible = false;
this.initPanel();
}
initPanel() {
// 创建调试面板
this.panel = document.createElement('div');
this.panel.id = 'debug-panel';
this.panel.style.cssText = `
position: fixed;
bottom: 0;
right: 0;
width: 300px;
height: 200px;
background: white;
border: 1px solid #ccc;
z-index: 10000;
padding: 10px;
overflow: auto;
font-family: monospace;
font-size: 12px;
display: none;
box-shadow: -2px -2px 10px rgba(0,0,0,0.1);
`;
document.body.appendChild(this.panel);
// 创建切换按钮
this.toggleBtn = document.createElement('button');
this.toggleBtn.textContent = '调试';
this.toggleBtn.style.cssText = `
position: fixed;
bottom: 10px;
right: 10px;
z-index: 10001;
padding: 5px 10px;
background: #3b82f6;
color: white;
border: none;
border-radius: 3px;
cursor: pointer;
`;
this.toggleBtn.addEventListener('click', () => this.toggle());
document.body.appendChild(this.toggleBtn);
}
toggle() {
this.isVisible = !this.isVisible;
this.panel.style.display = this.isVisible ? 'block' : 'none';
}
log(message, type = 'info') {
if (!this.isVisible) return;
const entry = document.createElement('div');
entry.style.marginBottom = '5px';
entry.style.color = type === 'error' ? 'red' :
type === 'warning' ? 'orange' : 'black';
const timestamp = new Date().toLocaleTimeString();
entry.textContent = `[${timestamp}] ${message}`;
this.panel.appendChild(entry);
this.panel.scrollTop = this.panel.scrollHeight;
}
clear() {
this.panel.innerHTML = '';
}
}
// 创建全局调试实例
window.debugPanel = new DebugPanel();
// 重写console方法以捕获日志
const originalLog = console.log;
const originalError = console.error;
const originalWarn = console.warn;
console.log = function(...args) {
debugPanel.log(args.join(' '), 'info');
originalLog.apply(console, args);
};
console.error = function(...args) {
debugPanel.log(args.join(' '), 'error');
originalError.apply(console, args);
};
console.warn = function(...args) {
debugPanel.log(args.join(' '), 'warning');
originalWarn.apply(console, args);
};
7.2 性能监控面板
添加性能监控面板,实时显示API调用性能:
javascript
// scripts/performance.js
class PerformanceMonitor {
constructor() {
this.metrics = {
totalCalls: 0,
totalTokens: 0,
totalTime: 0,
byModel: {}
};
this.initPanel();
this.startMonitoring();
}
initPanel() {
this.panel = document.createElement('div');
this.panel.id = 'performance-panel';
this.panel.style.cssText = `
position: fixed;
top: 10px;
right: 10px;
width: 300px;
background: white;
border: 1px solid #ccc;
z-index: 9999;
padding: 10px;
font-family: monospace;
font-size: 12px;
display: none;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
`;
document.body.appendChild(this.panel);
}
startMonitoring() {
// 监听API调用
const originalSendMessage = siliconFlowAPI.sendMessage;
const originalSendMessageStream = siliconFlowAPI.sendMessageStream;
siliconFlowAPI.sendMessage = async (...args) => {
const start = performance.now();
const result = await originalSendMessage.apply(siliconFlowAPI, args);
const end = performance.now();
this.recordCall(args[1]?.model || siliconFlowAPI.model, end - start, result.usage);
return result;
};
siliconFlowAPI.sendMessageStream = async (...args) => {
const start = performance.now();
const result = await originalSendMessageStream.apply(siliconFlowAPI, args);
const end = performance.now();
// 流式调用无法获取token使用量,只记录时间
this.recordCall(args[1]?.model || siliconFlowAPI.model, end - start);
return result;
};
}
recordCall(model, time, usage = null) {
this.metrics.totalCalls++;
this.metrics.totalTime += time;
if (usage) {
this.metrics.totalTokens += usage.total_tokens || 0;
}
if (!this.metrics.byModel[model]) {
this.metrics.byModel[model] = {
calls: 0,
time: 0,
tokens: 0
};
}
this.metrics.byModel[model].calls++;
this.metrics.byModel[model].time += time;
if (usage) {
this.metrics.byModel[model].tokens += usage.total_tokens || 0;
}
this.updatePanel();
}
updatePanel() {
let html = `<h3 style="margin-top:0">性能监控</h3>
<div>总调用: ${this.metrics.totalCalls}</div>
<div>总耗时: ${this.metrics.totalTime.toFixed(0)}ms</div>
<div>总token: ${this.metrics.totalTokens}</div>`;
if (this.metrics.totalCalls > 0) {
html += `<div>平均耗时: ${(this.metrics.totalTime / this.metrics.totalCalls).toFixed(0)}ms/调用</div>`;
html += `<div>平均token: ${(this.metrics.totalTokens / this.metrics.totalCalls).toFixed(0)}/调用</div>`;
}
html += '<h4>按模型统计:</h4>';
for (const model in this.metrics.byModel) {
const data = this.metrics.byModel[model];
html += `<div><b>${model}</b>: ${data.calls}调用, ${data.time.toFixed(0)}ms, ${data.tokens}tokens</div>`;
}
this.panel.innerHTML = html;
}
toggle() {
this.panel.style.display = this.panel.style.display === 'none' ? 'block' : 'none';
}
clear() {
this.metrics = {
totalCalls: 0,
totalTokens: 0,
totalTime: 0,
byModel: {}
};
this.updatePanel();
}
}
// 创建全局性能监控实例
window.performanceMonitor = new PerformanceMonitor();
// 添加键盘快捷键显示/隐藏监控面板
document.addEventListener('keydown', (e) => {
if (e.ctrlKey && e.shiftKey && e.key === 'P') {
e.preventDefault();
performanceMonitor.toggle();
}
});
八、安全性与最佳实践
8.1 API密钥安全
增强API密钥的安全性处理:
javascript
// scripts/security.js
class SecurityManager {
constructor() {
this.encryptionKey = null;
this.init();
}
async init() {
// 尝试从安全存储获取加密密钥
this.encryptionKey = await this.getEncryptionKey();
}
async getEncryptionKey() {
// 使用Web Crypto API生成或获取加密密钥
const keyName = 'sf-api-encryption-key';
try {
// 尝试获取现有密钥
const existingKey = localStorage.getItem(keyName);
if (existingKey) {
return await crypto.subtle.importKey(
'jwk',
JSON.parse(existingKey),
{ name: 'AES-GCM' },
false,
['encrypt', 'decrypt']
);
}
// 生成新密钥
const key = await crypto.subtle.generateKey(
{ name: 'AES-GCM', length: 256 },
true,
['encrypt', 'decrypt']
);
// 导出并存储密钥
const exportedKey = await crypto.subtle.exportKey('jwk', key);
localStorage.setItem(keyName, JSON.stringify(exportedKey));
return key;
} catch (error) {
console.error('加密密钥处理失败:', error);
return null;
}
}
async encrypt(text) {
if (!this.encryptionKey) return text;
try {
const iv = crypto.getRandomValues(new Uint8Array(12));
const encoded = new TextEncoder().encode(text);
const encrypted = await crypto.subtle.encrypt(
{ name: 'AES-GCM', iv },
this.encryptionKey,
encoded
);
// 将IV和加密数据组合并转换为Base64
const combined = new Uint8Array(iv.length + encrypted.byteLength);
combined.set(iv);
combined.set(new Uint8Array(encrypted), iv.length);
return btoa(String.fromCharCode.apply(null, combined));
} catch (error) {
console.error('加密失败:', error);
return text;
}
}
async decrypt(encryptedText) {
if (!this.encryptionKey) return encryptedText;
try {
// 从Base64解码
const combined = new Uint8Array(
atob(encryptedText).split('').map(c => c.charCodeAt(0))
);
// 提取IV和加密数据
const iv = combined.slice(0, 12);
const encrypted = combined.slice(12);
const decrypted = await crypto.subtle.decrypt(
{ name: 'AES-GCM', iv },
this.encryptionKey,
encrypted
);
return new TextDecoder().decode(decrypted);
} catch (error) {
console.error('解密失败:', error);
return encryptedText;
}
}
// 安全存储API密钥
async storeApiKey(apiKey) {
try {
const encrypted = await this.encrypt(apiKey);
localStorage.setItem('encryptedApiKey', encrypted);
return true;
} catch (error) {
console.error('存储API密钥失败:', error);
return false;
}
}
// 安全获取API密钥
async getApiKey() {
try {
const encrypted = localStorage.getItem('encryptedApiKey');
if (!encrypted) return null;
return await this.decrypt(encrypted);
} catch (error) {
console.error('获取API密钥失败:', error);
return null;
}
}
// 清除所有安全数据
async clearAll() {
localStorage.removeItem('encryptedApiKey');
localStorage.removeItem('sf-api-encryption-key');
this.encryptionKey = null;
await this.init();
}
}
// 创建全局安全管理器实例
window.securityManager = new SecurityManager();
// 修改UI代码以使用安全存储
// 在ChatUI类的saveSettings方法中:
async saveSettings() {
const apiKey = this.apiKeyInput.value;
const encrypted = await securityManager.storeApiKey(apiKey);
if (!encrypted) {
alert('保存API密钥时出错');
return;
}
const settings = {
model: this.modelSelect.value,
params: {
temperature: parseFloat(this.tempSlider.value),
max_tokens: parseInt(this.maxTokensSlider.value)
}
};
localStorage.setItem('siliconFlowSettings', JSON.stringify(settings));
siliconFlowAPI.configure({ model: settings.model, params: settings.params });
this.closeSettings();
alert('设置已保存!');
}
// 在loadSettings方法中:
async loadSettings() {
const savedSettings = localStorage.getItem('siliconFlowSettings');
const apiKey = await securityManager.getApiKey();
if (apiKey) {
this.apiKeyInput.value = apiKey;
siliconFlowAPI.configure({ apiKey });
}
if (savedSettings) {
const settings = JSON.parse(savedSettings);
this.modelSelect.value = settings.model || 'openai/gpt-oss-20b';
this.tempSlider.value = settings.params?.temperature || 0.7;
this.tempValue.textContent = this.tempSlider.value;
this.maxTokensSlider.value = settings.params?.max_tokens || 512;
this.maxTokensValue.textContent = this.maxTokensSlider.value;
siliconFlowAPI.configure({
model: settings.model,
params: {
temperature: settings.params.temperature,
max_tokens: settings.params.max_tokens
}
});
}
}
九、结论与未来展望
通过本文的详细指导,我们成功构建了一个功能完整的基于硅基流动API的智能聊天应用。这个应用不仅具备了基本的聊天功能,还包括了许多高级特性:
- 现代化的用户界面:使用Tailwind CSS构建响应式设计,支持暗色模式
- 流式消息处理:实现实时流式响应,提升用户体验
- 对话管理:支持对话历史保存、导出和导入
- 性能优化:包含性能监控和错误处理机制
- 安全增强:使用加密技术保护API密钥
- 开发者工具:集成调试和性能监控面板
9.1 未来扩展方向
这个应用还有很大的扩展空间,以下是一些可能的改进方向:
- 多会话支持:允许用户同时进行多个独立的对话
- 插件系统:开发插件系统支持功能扩展
- 语音交互:集成语音识别和合成功能
- 多模态支持:处理图像、音频等多模态输入
- 知识库集成:连接外部知识库提供更精准的回答
- 团队协作:支持多人协作和对话共享
9.2 性能优化建议
对于生产环境部署,还可以考虑以下优化措施:
- CDN加速:使用CDN分发静态资源
- 代码分割:实现按需加载减少初始包大小
- 缓存策略:优化API响应缓存
- 负载均衡:对于高流量场景实现多API密钥负载均衡
- 监控告警:集成APM工具监控应用性能
9.3 学习资源
要深入了解相关技术,可以参考以下资源:
- 硅基流动官方文档 - 官方API文档和指南
- Tailwind CSS文档 - CSS框架详细文档
- JavaScript现代教程 - 全面的JavaScript学习资源
- Web Crypto API指南 - 浏览器加密API文档
- Progressive Web Apps - PWA开发最佳实践
通过不断迭代和优化,这个应用可以发展成为功能强大、用户体验优秀的AI助手平台,为用户提供高质量的智能对话服务。
完整代码仓库 : GitHub - SiliconFlow Chat Application
在线演示 : SiliconFlow Chat Demo
API参考 : SiliconFlow API Documentation
问题反馈 : 创建Issue
希望本文对您构建基于AI编程工具的应用有所帮助!如有任何问题或建议,欢迎在评论区留言或通过GitHub提交Issue。