Files
sillytavern-repalice/ARCHITECTURE.md
2026-04-24 01:45:41 +08:00

430 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 项目架构设计文档
## 📐 整体架构
本项目采用**前后端分离 + 共享层**的单体仓库(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<CharacterCard>;
save(character: CharacterCard): Promise<void>;
delete(id: string): Promise<void>;
}
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
- 优先使用 `<script setup>`
- Props 用 TypeScript 定义
- 组合式逻辑提取为 composables
### NestJS
- 每个模块一个文件夹
- Controller 只处理 HTTP 层
- Service 不包含 HTTP 相关代码
- 使用 DTO 验证输入
## 🎯 下一步计划
1. **实现基础 CRUD**: 角色卡、聊天、世界书
2. **集成 Vercel AI SDK**: 流式聊天
3. **构建 UI 组件**: 聊天界面、角色编辑器
4. **实现转换器**: 完整的导入导出功能
5. **添加测试**: 覆盖核心业务逻辑
6. **编写文档**: API 文档、用户指南
---
**文档版本**: 1.0
**最后更新**: 2026-04-24