# 项目架构设计文档 ## 📐 整体架构 本项目采用**前后端分离 + 共享层**的单体仓库(monorepo)架构,严格遵循 MVC 设计模式。 ``` ┌─────────────────────────────────────────────┐ │ SillyTavern Repalice │ ├─────────────────────────────────────────────┤ │ │ │ ┌──────────┐ ┌──────────┐ │ │ │ Client │◄──►│ Shared │ │ │ │ (Vue3) │ │ (Types) │ │ │ └──────────┘ └──────────┘ │ │ ▲ ▲ │ │ │ HTTP/API │ Type Safety │ │ ▼ ▼ │ │ ┌──────────┐ ┌──────────┐ │ │ │ Server │◄──►│ Shared │ │ │ │(NestJS) │ │(Schemas) │ │ │ └──────────┘ └──────────┘ │ │ │ │ │ ▼ │ │ ┌──────────┐ │ │ │ Data │ │ │ │ (Local) │ │ │ └──────────┘ │ └─────────────────────────────────────────────┘ ``` ## 🎯 核心设计决策 ### 1. 双数据格式策略 **问题**: 需要兼容 SillyTavern 的导入导出,同时支持自定义扩展功能 **解决方案**: - **外部格式** (`ST*` 前缀): 严格遵循 SillyTavern 规范 - **内部格式** (无前缀): 继承并扩展,添加自定义字段 - **转换器**: 双向转换,保证数据完整性 **示例**: ```typescript // SillyTavern 格式 interface STWorldInfoEntry { constant?: boolean; // 永久激活标志 selective?: boolean; // RAG 标志 } // 内部格式 interface WorldInfoEntry extends STWorldInfoEntry { activationType: ActivationType; // 4种枚举 logicExpression?: LogicExpression; ragConfig?: RAGConfig; } ``` ### 2. MVC 分层架构 #### Model (模型层) ``` server/src/ ├── services/ # 业务逻辑 ├── persistence/ # 数据访问 │ ├── interfaces/ # 仓储接口 │ └── implementations/ # 具体实现 └── dto/ # 数据传输对象 ``` **职责**: - Service: 业务规则、数据验证、事务管理 - Repository: CRUD 操作、数据持久化 - DTO: API 输入输出验证 #### View (视图层) ``` client/src/ ├── components/ # UI 组件 │ ├── TopBar/ │ ├── LeftPanel/ │ ├── CenterPanel/ │ ├── RightPanel/ │ └── common/ ├── layouts/ # 页面布局 └── stores/ # 状态管理 ``` **职责**: - Components: UI 渲染、用户交互 - Layouts: 页面结构、路由视图 - Stores: 客户端状态管理 #### Controller (控制层) ``` server/src/controllers/ # API 端点 client/src/router/ # 前端路由 ``` **职责**: - 接收请求、参数验证 - 调用 Service 处理业务 - 返回响应 ### 3. 依赖注入 (DI) **后端 DI** (NestJS): ```typescript @Injectable() export class CharacterService { constructor( @Inject(CHARACTER_REPOSITORY) private repo: CharacterRepository, private llmService: LLMService, ) {} } ``` **优势**: - 松耦合:模块间通过接口通信 - 可测试:轻松 mock 依赖 - 可替换:不同实现无缝切换 ### 4. 工具层 (Tools Layer) 独立于 MVC 的工具层,提供通用能力: ``` server/src/tools/ ├── context-chunker.ts # 上下文分块 ├── prompt-assembler.ts # 提示词组装 └── token-counter.ts # Token 计数 ``` **特点**: - 无状态纯函数 - 不依赖 DI 容器 - 可在任何层调用 ## 🗂️ 目录结构设计原则 ### 后端 (NestJS) ``` server/src/ ├── modules/ # 按业务领域划分 │ ├── chat/ # 聊天管理 │ ├── character/ # 角色卡管理 │ ├── llm/ # LLM 集成 │ ├── import-export/# 导入导出 │ └── workflow/ # 工作流引擎 │ ├── persistence/ # 持久化抽象 │ ├── interfaces/ # 仓储接口 │ ├── implementations/ # 实现 │ └── file-system/ # 文件系统实现 │ ├── core/ # 基础设施 │ ├── config/ # 配置管理 │ ├── filters/ # 异常过滤 │ ├── interceptors/ # 拦截器 │ └── guards/ # 守卫 │ ├── controllers/ # 路由层 (横向切割) ├── services/ # 业务层 (横向切割) └── tools/ # 工具层 (横向切割) ``` **设计原则**: 1. **垂直切片**: 每个 module 自包含 controller + service 2. **水平分层**: persistence/core 跨模块复用 3. **依赖方向**: controllers → services → persistence ### 前端 (Vue3) ``` client/src/ ├── components/ # 按布局区域划分 │ ├── TopBar/ # 顶部栏 │ ├── LeftPanel/ # 左侧面板 │ ├── CenterPanel/ # 中央面板 │ ├── RightPanel/ # 右侧面板 │ └── common/ # 通用组件 │ ├── layouts/ # 布局模板 ├── stores/ # 全局状态 ├── router/ # 路由配置 ├── composables/ # 组合式函数 └── api/ # API 客户端 ``` **设计原则**: 1. **布局驱动**: 组件按 UI 区域组织 2. **关注点分离**: 状态、路由、API 独立管理 3. **组合优于继承**: 使用 composables 复用逻辑 ### 共享层 (Shared) ``` shared/ ├── types/ # TypeScript 类型 │ ├── sillytavern.types.ts # ST 兼容格式 │ ├── internal.types.ts # 内部扩展格式 │ └── converters.ts # 转换器 │ └── schemas/ # Zod 运行时验证 ├── sillytavern.schemas.ts └── internal.schemas.ts ``` **设计原则**: 1. **单一真相源**: 前后端共用类型定义 2. **编译时 + 运行时**: TypeScript + Zod 双重验证 3. **向后兼容**: 转换器保证旧数据可用 ## 🔄 数据流 ### 典型请求流程 ``` User Action (Frontend) ↓ Vue Component ↓ Pinia Store ↓ API Client (axios) ↓ HTTP Request ↓ NestJS Controller ↓ Service (Business Logic) ↓ Repository (Data Access) ↓ File System ↓ Response (JSON) ↓ Update UI ``` ### 数据转换流程 ``` SillyTavern Import File ↓ Zod Schema Validation (ST Schema) ↓ Parse to ST Types ↓ Converter (convertSTToInternal) ↓ Internal Types ↓ Save to File System ``` ## 🧪 测试策略 ### 单元测试 - **目标**: Services, Tools, Utils - **工具**: Jest - **Mock**: 外部依赖 (Repository, LLM API) ### 集成测试 - **目标**: Controller + Service 协作 - **工具**: Supertest - **范围**: 单个模块的完整流程 ### E2E 测试 - **目标**: 关键用户流程 - **工具**: Playwright / Cypress - **场景**: 创建角色、发送消息、导入导出 ## 🚀 部署架构 ### 开发环境 ``` Client (Vite Dev Server) :5173 ↓ Proxy /api Server (NestJS) :3000 ↓ Local File System ./data ``` ### 生产环境 (Docker) ``` Client (Nginx) :80 ↓ Proxy /api Server (Node.js) :3000 ↓ Volume Mount /app/data ``` ## 📊 关键技术选型理由 | 技术 | 选型理由 | |------|---------| | **NestJS** | 内置 DI、模块化、TypeScript 优先、企业级 | | **Vue 3** | 组合式 API、响应式系统、学习曲线平缓 | | **Vite** | 极速开发体验、原生 ESM、热更新快 | | **Zod** | TypeScript 优先、运行时验证、 schema 推导 | | **Vercel AI SDK** | 统一 LLM 接口、流式支持、工具调用 | | **Pinia** | Vue 3 官方推荐、TypeScript 友好、轻量 | | **文件系统** | 零依赖、易备份、用户可控、无需数据库 | ## 🎨 设计模式应用 ### 1. Repository Pattern ```typescript interface CharacterRepository { findById(id: string): Promise; save(character: CharacterCard): Promise; delete(id: string): Promise; } class FileSystemCharacterRepository implements CharacterRepository { // 实现细节隐藏 } ``` ### 2. Factory Pattern ```typescript class LLMProviderFactory { static create(provider: ProviderType): LLMProvider { switch (provider) { case 'openai': return new OpenAIProvider(); case 'anthropic': return new AnthropicProvider(); } } } ``` ### 3. Strategy Pattern ```typescript interface ActivationStrategy { shouldActivate(entry: WorldInfoEntry, context: string): boolean; } class KeywordActivationStrategy implements ActivationStrategy { // 关键词匹配逻辑 } class RAGActivationStrategy implements ActivationStrategy { // 向量检索逻辑 } ``` ### 4. Adapter Pattern ```typescript // 适配器:SillyTavern 格式 → 内部格式 class SillyTavernAdapter { static toInternal(stCard: STCharacterCard): CharacterCard { return convertSTCharacterCardToInternal(stCard); } static toExternal(card: CharacterCard): STCharacterCard { return convertCharacterCardToST(card); } } ``` ## 🔐 安全性考虑 1. **输入验证**: Zod schemas 在边界验证所有输入 2. **文件上传**: 限制类型、大小,扫描恶意内容 3. **API 密钥**: 环境变量管理,不提交到版本控制 4. **CORS**: 严格配置允许的来源 5. **路径遍历**: 验证文件路径,防止 `../` 攻击 ## 📈 性能优化 1. **懒加载**: 前端路由和组件按需加载 2. **缓存**: LLM 响应缓存、角色卡元数据缓存 3. **分页**: 聊天记录、角色列表分页加载 4. **流式响应**: SSE 传输 LLM 生成内容 5. **索引**: 为搜索字段建立内存索引 ## 🔄 扩展性设计 ### 新增 LLM Provider 1. 实现 `LLMProvider` 接口 2. 注册到 `LLMProviderFactory` 3. 更新配置 schema ### 新增持久化方式 1. 实现 Repository 接口 2. 创建新模块 (如 `persistence/sqlite/`) 3. 通过 DI 切换实现 ### 新增激活方式 1. 扩展 `ActivationType` 枚举 2. 实现新的 `ActivationStrategy` 3. 更新世界书 UI ## 📝 编码规范 ### TypeScript - 严格模式: `strict: true` - 禁止 `any`,使用 `unknown` 代替 - 接口命名不加 `I` 前缀 - 枚举使用 PascalCase ### Vue 3 - 优先使用 `