541 lines
12 KiB
Markdown
541 lines
12 KiB
Markdown
|
# 大语言模型聊天网站设计文档
|
|||
|
|
|||
|
## 概述
|
|||
|
|
|||
|
本项目是一个全栈大语言模型聊天网站,支持用户与AI进行实时对话。系统采用前后端分离架构,前端使用Vue.js构建用户界面,后端使用Node.js提供API服务,通过调用兼容OpenAI格式的第三方API实现智能对话功能。
|
|||
|
|
|||
|
### 核心特性
|
|||
|
- 实时AI对话体验
|
|||
|
- 对话历史记录与管理
|
|||
|
- 用户会话状态保持
|
|||
|
- 响应式Web界面
|
|||
|
- 高性能缓存机制
|
|||
|
- 轻量级本地数据存储
|
|||
|
|
|||
|
### 技术栈
|
|||
|
- **前端**: Vue.js 3, Vue Router, Pinia, Axios, Element Plus
|
|||
|
- **后端**: Node.js, Express.js, SQLite3, LRU-Cache
|
|||
|
- **包管理**: pnpm
|
|||
|
- **数据库**: SQLite (本地文件数据库)
|
|||
|
- **第三方集成**: OpenAI兼容API服务
|
|||
|
|
|||
|
## 系统架构
|
|||
|
|
|||
|
### 整体架构图
|
|||
|
|
|||
|
```mermaid
|
|||
|
graph TB
|
|||
|
subgraph "客户端"
|
|||
|
A[Vue.js前端]
|
|||
|
A1[聊天界面]
|
|||
|
A2[历史记录]
|
|||
|
A3[用户设置]
|
|||
|
end
|
|||
|
|
|||
|
subgraph "服务端"
|
|||
|
B[Node.js后端]
|
|||
|
B1[Express路由]
|
|||
|
B2[会话管理]
|
|||
|
B3[缓存层]
|
|||
|
B4[数据访问层]
|
|||
|
end
|
|||
|
|
|||
|
subgraph "数据存储"
|
|||
|
C[SQLite数据库]
|
|||
|
C1[用户表]
|
|||
|
C2[对话表]
|
|||
|
C3[消息表]
|
|||
|
end
|
|||
|
|
|||
|
subgraph "外部服务"
|
|||
|
D[第三方LLM API]
|
|||
|
D1[OpenAI兼容接口]
|
|||
|
end
|
|||
|
|
|||
|
A -->|HTTP/WebSocket| B
|
|||
|
B -->|SQL查询| C
|
|||
|
B -->|API调用| D
|
|||
|
B3 -->|LRU缓存| B2
|
|||
|
```
|
|||
|
|
|||
|
### 数据流架构
|
|||
|
|
|||
|
```mermaid
|
|||
|
sequenceDiagram
|
|||
|
participant U as 用户
|
|||
|
participant F as Vue前端
|
|||
|
participant B as Node.js后端
|
|||
|
participant C as LRU缓存
|
|||
|
participant DB as SQLite
|
|||
|
participant API as 第三方LLM API
|
|||
|
|
|||
|
U->>F: 发送消息
|
|||
|
F->>B: POST /api/chat/send
|
|||
|
B->>C: 检查缓存
|
|||
|
alt 缓存命中
|
|||
|
C-->>B: 返回缓存结果
|
|||
|
else 缓存未命中
|
|||
|
B->>DB: 保存用户消息
|
|||
|
B->>API: 调用LLM API
|
|||
|
API-->>B: 返回AI回复
|
|||
|
B->>DB: 保存AI回复
|
|||
|
B->>C: 更新缓存
|
|||
|
end
|
|||
|
B-->>F: 返回对话结果
|
|||
|
F-->>U: 显示AI回复
|
|||
|
```
|
|||
|
|
|||
|
## 前端架构
|
|||
|
|
|||
|
### 组件层次结构
|
|||
|
|
|||
|
```mermaid
|
|||
|
graph TD
|
|||
|
A[App.vue] --> B[Layout组件]
|
|||
|
B --> C[Header组件]
|
|||
|
B --> D[Sidebar组件]
|
|||
|
B --> E[Main组件]
|
|||
|
|
|||
|
E --> F[ChatView]
|
|||
|
E --> G[HistoryView]
|
|||
|
E --> H[SettingsView]
|
|||
|
|
|||
|
F --> I[ChatInput]
|
|||
|
F --> J[MessageList]
|
|||
|
F --> K[TypingIndicator]
|
|||
|
|
|||
|
J --> L[MessageItem]
|
|||
|
L --> M[UserMessage]
|
|||
|
L --> N[AIMessage]
|
|||
|
|
|||
|
D --> O[ConversationList]
|
|||
|
O --> P[ConversationItem]
|
|||
|
```
|
|||
|
|
|||
|
### 组件定义
|
|||
|
|
|||
|
#### 核心聊天组件
|
|||
|
|
|||
|
**ChatView.vue** - 主聊天界面
|
|||
|
- 管理当前对话状态
|
|||
|
- 处理消息发送逻辑
|
|||
|
- 实现消息流式显示
|
|||
|
|
|||
|
**MessageList.vue** - 消息列表容器
|
|||
|
- 虚拟滚动优化
|
|||
|
- 消息懒加载
|
|||
|
- 自动滚动到底部
|
|||
|
|
|||
|
**ChatInput.vue** - 消息输入组件
|
|||
|
- 多行文本输入
|
|||
|
- 发送按钮状态管理
|
|||
|
- 快捷键支持(Ctrl+Enter)
|
|||
|
|
|||
|
#### 布局组件
|
|||
|
|
|||
|
**Layout.vue** - 主布局容器
|
|||
|
- 响应式布局设计
|
|||
|
- 侧边栏展开/收缩
|
|||
|
- 移动端适配
|
|||
|
|
|||
|
**Sidebar.vue** - 侧边栏组件
|
|||
|
- 对话历史列表
|
|||
|
- 新建对话按钮
|
|||
|
- 对话搜索功能
|
|||
|
|
|||
|
### 状态管理架构
|
|||
|
|
|||
|
使用Pinia进行状态管理,主要包含以下Store:
|
|||
|
|
|||
|
**chatStore.js** - 聊天状态管理
|
|||
|
```javascript
|
|||
|
{
|
|||
|
currentConversation: null,
|
|||
|
messages: [],
|
|||
|
isLoading: false,
|
|||
|
isTyping: false
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
**conversationStore.js** - 对话管理
|
|||
|
```javascript
|
|||
|
{
|
|||
|
conversations: [],
|
|||
|
activeConversationId: null,
|
|||
|
searchQuery: ''
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
**userStore.js** - 用户设置
|
|||
|
```javascript
|
|||
|
{
|
|||
|
settings: {
|
|||
|
theme: 'light',
|
|||
|
fontSize: 'medium',
|
|||
|
autoSave: true
|
|||
|
}
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
### 路由配置
|
|||
|
|
|||
|
```javascript
|
|||
|
const routes = [
|
|||
|
{
|
|||
|
path: '/',
|
|||
|
component: Layout,
|
|||
|
children: [
|
|||
|
{ path: '', redirect: '/chat' },
|
|||
|
{ path: '/chat/:id?', component: ChatView },
|
|||
|
{ path: '/history', component: HistoryView },
|
|||
|
{ path: '/settings', component: SettingsView }
|
|||
|
]
|
|||
|
}
|
|||
|
]
|
|||
|
```
|
|||
|
|
|||
|
### API集成层
|
|||
|
|
|||
|
**apiClient.js** - HTTP客户端封装
|
|||
|
- Axios实例配置
|
|||
|
- 请求/响应拦截器
|
|||
|
- 错误处理机制
|
|||
|
- 重试逻辑
|
|||
|
|
|||
|
**chatApi.js** - 聊天相关API
|
|||
|
```javascript
|
|||
|
export const chatApi = {
|
|||
|
sendMessage: (conversationId, message) => {},
|
|||
|
getConversations: () => {},
|
|||
|
getMessages: (conversationId) => {},
|
|||
|
createConversation: () => {},
|
|||
|
deleteConversation: (id) => {}
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
## 后端架构
|
|||
|
|
|||
|
### API端点设计
|
|||
|
|
|||
|
#### 对话管理端点
|
|||
|
|
|||
|
| 方法 | 路径 | 描述 | 请求体 | 响应 |
|
|||
|
|------|------|------|--------|------|
|
|||
|
| POST | `/api/conversations` | 创建新对话 | `{ title?: string }` | `{ id, title, createdAt }` |
|
|||
|
| GET | `/api/conversations` | 获取对话列表 | - | `[{ id, title, lastMessage, updatedAt }]` |
|
|||
|
| GET | `/api/conversations/:id` | 获取对话详情 | - | `{ id, title, messages }` |
|
|||
|
| DELETE | `/api/conversations/:id` | 删除对话 | - | `{ success: true }` |
|
|||
|
|
|||
|
#### 消息处理端点
|
|||
|
|
|||
|
| 方法 | 路径 | 描述 | 请求体 | 响应 |
|
|||
|
|------|------|------|--------|------|
|
|||
|
| POST | `/api/chat/send` | 发送消息 | `{ conversationId, message, stream? }` | `{ message, response }` |
|
|||
|
| GET | `/api/messages/:conversationId` | 获取消息历史 | - | `[{ id, role, content, timestamp }]` |
|
|||
|
|
|||
|
### 数据模型设计
|
|||
|
|
|||
|
#### 数据库表结构
|
|||
|
|
|||
|
**conversations表**
|
|||
|
```sql
|
|||
|
CREATE TABLE conversations (
|
|||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|||
|
title TEXT NOT NULL DEFAULT '新对话',
|
|||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|||
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|||
|
);
|
|||
|
```
|
|||
|
|
|||
|
**messages表**
|
|||
|
```sql
|
|||
|
CREATE TABLE messages (
|
|||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|||
|
conversation_id INTEGER NOT NULL,
|
|||
|
role TEXT NOT NULL CHECK (role IN ('user', 'assistant')),
|
|||
|
content TEXT NOT NULL,
|
|||
|
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|||
|
FOREIGN KEY (conversation_id) REFERENCES conversations(id) ON DELETE CASCADE
|
|||
|
);
|
|||
|
```
|
|||
|
|
|||
|
### 业务逻辑层
|
|||
|
|
|||
|
#### 对话服务 (ConversationService)
|
|||
|
|
|||
|
```javascript
|
|||
|
class ConversationService {
|
|||
|
// 创建新对话
|
|||
|
async createConversation(title) {}
|
|||
|
|
|||
|
// 获取对话列表
|
|||
|
async getConversations() {}
|
|||
|
|
|||
|
// 获取对话详情
|
|||
|
async getConversationById(id) {}
|
|||
|
|
|||
|
// 删除对话
|
|||
|
async deleteConversation(id) {}
|
|||
|
|
|||
|
// 更新对话标题
|
|||
|
async updateConversationTitle(id, title) {}
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
#### 聊天服务 (ChatService)
|
|||
|
|
|||
|
```javascript
|
|||
|
class ChatService {
|
|||
|
constructor(llmApiClient, cache) {
|
|||
|
this.llmApiClient = llmApiClient;
|
|||
|
this.cache = cache;
|
|||
|
}
|
|||
|
|
|||
|
// 处理用户消息
|
|||
|
async processMessage(conversationId, userMessage) {
|
|||
|
// 1. 保存用户消息到数据库
|
|||
|
// 2. 构建上下文(包含历史消息)
|
|||
|
// 3. 调用LLM API
|
|||
|
// 4. 保存AI回复到数据库
|
|||
|
// 5. 更新缓存
|
|||
|
// 6. 返回结果
|
|||
|
}
|
|||
|
|
|||
|
// 流式响应处理
|
|||
|
async processStreamMessage(conversationId, userMessage) {}
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
#### LLM API客户端 (LLMApiClient)
|
|||
|
|
|||
|
```javascript
|
|||
|
class LLMApiClient {
|
|||
|
constructor(apiConfig) {
|
|||
|
this.baseURL = apiConfig.baseURL;
|
|||
|
this.apiKey = apiConfig.apiKey;
|
|||
|
this.model = apiConfig.model;
|
|||
|
}
|
|||
|
|
|||
|
// 调用聊天完成API
|
|||
|
async createChatCompletion(messages, options = {}) {
|
|||
|
const response = await fetch(`${this.baseURL}/chat/completions`, {
|
|||
|
method: 'POST',
|
|||
|
headers: {
|
|||
|
'Authorization': `Bearer ${this.apiKey}`,
|
|||
|
'Content-Type': 'application/json'
|
|||
|
},
|
|||
|
body: JSON.stringify({
|
|||
|
model: this.model,
|
|||
|
messages: messages,
|
|||
|
stream: options.stream || false,
|
|||
|
max_tokens: options.maxTokens || 2000,
|
|||
|
temperature: options.temperature || 0.7
|
|||
|
})
|
|||
|
});
|
|||
|
return response;
|
|||
|
}
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
### 缓存策略
|
|||
|
|
|||
|
#### LRU缓存配置
|
|||
|
|
|||
|
```javascript
|
|||
|
const LRU = require('lru-cache');
|
|||
|
|
|||
|
const cache = new LRU({
|
|||
|
max: 500, // 最大缓存条目数
|
|||
|
maxAge: 1000 * 60 * 30 // 30分钟过期
|
|||
|
});
|
|||
|
|
|||
|
// 缓存键策略
|
|||
|
const getCacheKey = (conversationId, messageHash) => {
|
|||
|
return `conv:${conversationId}:msg:${messageHash}`;
|
|||
|
};
|
|||
|
```
|
|||
|
|
|||
|
#### 缓存使用场景
|
|||
|
|
|||
|
1. **对话上下文缓存** - 缓存最近的对话上下文,减少数据库查询
|
|||
|
2. **API响应缓存** - 对相同输入的LLM响应进行缓存
|
|||
|
3. **用户会话缓存** - 缓存用户会话信息
|
|||
|
|
|||
|
### 中间件系统
|
|||
|
|
|||
|
#### 请求日志中间件
|
|||
|
|
|||
|
```javascript
|
|||
|
const requestLogger = (req, res, next) => {
|
|||
|
console.log(`${new Date().toISOString()} - ${req.method} ${req.path}`);
|
|||
|
next();
|
|||
|
};
|
|||
|
```
|
|||
|
|
|||
|
#### 错误处理中间件
|
|||
|
|
|||
|
```javascript
|
|||
|
const errorHandler = (err, req, res, next) => {
|
|||
|
console.error('Error:', err);
|
|||
|
res.status(500).json({
|
|||
|
error: 'Internal Server Error',
|
|||
|
message: process.env.NODE_ENV === 'development' ? err.message : '服务器内部错误'
|
|||
|
});
|
|||
|
};
|
|||
|
```
|
|||
|
|
|||
|
#### 速率限制中间件
|
|||
|
|
|||
|
```javascript
|
|||
|
const rateLimit = require('express-rate-limit');
|
|||
|
|
|||
|
const chatRateLimit = rateLimit({
|
|||
|
windowMs: 1 * 60 * 1000, // 1分钟
|
|||
|
max: 20, // 限制每个IP每分钟最多20次请求
|
|||
|
message: '请求过于频繁,请稍后再试'
|
|||
|
});
|
|||
|
```
|
|||
|
|
|||
|
## 项目结构
|
|||
|
|
|||
|
```
|
|||
|
chat-website/
|
|||
|
├── package.json
|
|||
|
├── pnpm-workspace.yaml
|
|||
|
├── README.md
|
|||
|
├── .gitignore
|
|||
|
├── frontend/
|
|||
|
│ ├── package.json
|
|||
|
│ ├── vite.config.js
|
|||
|
│ ├── index.html
|
|||
|
│ ├── src/
|
|||
|
│ │ ├── main.js
|
|||
|
│ │ ├── App.vue
|
|||
|
│ │ ├── components/
|
|||
|
│ │ │ ├── layout/
|
|||
|
│ │ │ │ ├── Layout.vue
|
|||
|
│ │ │ │ ├── Header.vue
|
|||
|
│ │ │ │ └── Sidebar.vue
|
|||
|
│ │ │ ├── chat/
|
|||
|
│ │ │ │ ├── ChatView.vue
|
|||
|
│ │ │ │ ├── MessageList.vue
|
|||
|
│ │ │ │ ├── MessageItem.vue
|
|||
|
│ │ │ │ ├── ChatInput.vue
|
|||
|
│ │ │ │ └── TypingIndicator.vue
|
|||
|
│ │ │ └── common/
|
|||
|
│ │ │ ├── Loading.vue
|
|||
|
│ │ │ └── ErrorMessage.vue
|
|||
|
│ │ ├── stores/
|
|||
|
│ │ │ ├── index.js
|
|||
|
│ │ │ ├── chat.js
|
|||
|
│ │ │ ├── conversation.js
|
|||
|
│ │ │ └── user.js
|
|||
|
│ │ ├── api/
|
|||
|
│ │ │ ├── client.js
|
|||
|
│ │ │ ├── chat.js
|
|||
|
│ │ │ └── conversation.js
|
|||
|
│ │ ├── router/
|
|||
|
│ │ │ └── index.js
|
|||
|
│ │ ├── styles/
|
|||
|
│ │ │ ├── main.css
|
|||
|
│ │ │ ├── variables.css
|
|||
|
│ │ │ └── components.css
|
|||
|
│ │ └── utils/
|
|||
|
│ │ ├── helpers.js
|
|||
|
│ │ └── constants.js
|
|||
|
│ └── public/
|
|||
|
│ └── favicon.ico
|
|||
|
└── backend/
|
|||
|
├── package.json
|
|||
|
├── app.js
|
|||
|
├── server.js
|
|||
|
├── config/
|
|||
|
│ ├── database.js
|
|||
|
│ └── llm.js
|
|||
|
├── routes/
|
|||
|
│ ├── index.js
|
|||
|
│ ├── chat.js
|
|||
|
│ └── conversations.js
|
|||
|
├── services/
|
|||
|
│ ├── ConversationService.js
|
|||
|
│ ├── ChatService.js
|
|||
|
│ └── LLMApiClient.js
|
|||
|
├── models/
|
|||
|
│ ├── database.js
|
|||
|
│ ├── Conversation.js
|
|||
|
│ └── Message.js
|
|||
|
├── middleware/
|
|||
|
│ ├── auth.js
|
|||
|
│ ├── rateLimit.js
|
|||
|
│ ├── logger.js
|
|||
|
│ └── errorHandler.js
|
|||
|
├── utils/
|
|||
|
│ ├── cache.js
|
|||
|
│ └── helpers.js
|
|||
|
└── database/
|
|||
|
├── schema.sql
|
|||
|
└── chat.db
|
|||
|
```
|
|||
|
|
|||
|
## 测试策略
|
|||
|
|
|||
|
### 前端测试
|
|||
|
|
|||
|
#### 单元测试 (Vitest)
|
|||
|
- 组件逻辑测试
|
|||
|
- Store状态管理测试
|
|||
|
- 工具函数测试
|
|||
|
|
|||
|
#### 组件测试 (Vue Test Utils)
|
|||
|
- 组件渲染测试
|
|||
|
- 用户交互测试
|
|||
|
- Props和Events测试
|
|||
|
|
|||
|
#### E2E测试 (Playwright)
|
|||
|
- 完整对话流程测试
|
|||
|
- 跨浏览器兼容性测试
|
|||
|
- 移动端响应式测试
|
|||
|
|
|||
|
### 后端测试
|
|||
|
|
|||
|
#### 单元测试 (Jest)
|
|||
|
|
|||
|
**API端点测试**
|
|||
|
```javascript
|
|||
|
describe('Chat API', () => {
|
|||
|
test('should send message successfully', async () => {
|
|||
|
const response = await request(app)
|
|||
|
.post('/api/chat/send')
|
|||
|
.send({
|
|||
|
conversationId: 1,
|
|||
|
message: '你好'
|
|||
|
});
|
|||
|
|
|||
|
expect(response.status).toBe(200);
|
|||
|
expect(response.body).toHaveProperty('response');
|
|||
|
});
|
|||
|
});
|
|||
|
```
|
|||
|
|
|||
|
**服务层测试**
|
|||
|
```javascript
|
|||
|
describe('ChatService', () => {
|
|||
|
test('should process message correctly', async () => {
|
|||
|
const chatService = new ChatService(mockApiClient, mockCache);
|
|||
|
const result = await chatService.processMessage(1, '测试消息');
|
|||
|
|
|||
|
expect(result).toHaveProperty('userMessage');
|
|||
|
expect(result).toHaveProperty('aiResponse');
|
|||
|
});
|
|||
|
});
|
|||
|
```
|
|||
|
|
|||
|
#### 集成测试
|
|||
|
- 数据库操作测试
|
|||
|
- 第三方API集成测试
|
|||
|
- 缓存机制测试
|
|||
|
|
|||
|
#### 性能测试
|
|||
|
- API响应时间测试
|
|||
|
- 并发请求处理测试
|
|||
|
- 内存使用监控
|