llm-chat/.qoder/quests/llm-chat-website.md

12 KiB
Raw Blame History

大语言模型聊天网站设计文档

概述

本项目是一个全栈大语言模型聊天网站支持用户与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服务

系统架构

整体架构图

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

数据流架构

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回复

前端架构

组件层次结构

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 - 聊天状态管理

{
  currentConversation: null,
  messages: [],
  isLoading: false,
  isTyping: false
}

conversationStore.js - 对话管理

{
  conversations: [],
  activeConversationId: null,
  searchQuery: ''
}

userStore.js - 用户设置

{
  settings: {
    theme: 'light',
    fontSize: 'medium',
    autoSave: true
  }
}

路由配置

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

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表

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表

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)

class ConversationService {
  // 创建新对话
  async createConversation(title) {}
  
  // 获取对话列表
  async getConversations() {}
  
  // 获取对话详情
  async getConversationById(id) {}
  
  // 删除对话
  async deleteConversation(id) {}
  
  // 更新对话标题
  async updateConversationTitle(id, title) {}
}

聊天服务 (ChatService)

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)

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缓存配置

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. 用户会话缓存 - 缓存用户会话信息

中间件系统

请求日志中间件

const requestLogger = (req, res, next) => {
  console.log(`${new Date().toISOString()} - ${req.method} ${req.path}`);
  next();
};

错误处理中间件

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 : '服务器内部错误'
  });
};

速率限制中间件

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端点测试

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');
  });
});

服务层测试

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响应时间测试
  • 并发请求处理测试
  • 内存使用监控