规定数据类型
This commit is contained in:
429
ARCHITECTURE.md
Normal file
429
ARCHITECTURE.md
Normal file
@@ -0,0 +1,429 @@
|
||||
# 项目架构设计文档
|
||||
|
||||
## 📐 整体架构
|
||||
|
||||
本项目采用**前后端分离 + 共享层**的单体仓库(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
|
||||
Reference in New Issue
Block a user