11 KiB
11 KiB
项目架构设计文档
📐 整体架构
本项目采用前后端分离 + 共享层的单体仓库(monorepo)架构,严格遵循 MVC 设计模式。
┌─────────────────────────────────────────────┐
│ SillyTavern Repalice │
├─────────────────────────────────────────────┤
│ │
│ ┌──────────┐ ┌──────────┐ │
│ │ Client │◄──►│ Shared │ │
│ │ (Vue3) │ │ (Types) │ │
│ └──────────┘ └──────────┘ │
│ ▲ ▲ │
│ │ HTTP/API │ Type Safety │
│ ▼ ▼ │
│ ┌──────────┐ ┌──────────┐ │
│ │ Server │◄──►│ Shared │ │
│ │(NestJS) │ │(Schemas) │ │
│ └──────────┘ └──────────┘ │
│ │ │
│ ▼ │
│ ┌──────────┐ │
│ │ Data │ │
│ │ (Local) │ │
│ └──────────┘ │
└─────────────────────────────────────────────┘
🎯 核心设计决策
1. 双数据格式策略
问题: 需要兼容 SillyTavern 的导入导出,同时支持自定义扩展功能
解决方案:
- 外部格式 (
ST*前缀): 严格遵循 SillyTavern 规范 - 内部格式 (无前缀): 继承并扩展,添加自定义字段
- 转换器: 双向转换,保证数据完整性
示例:
// 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):
@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/ # 工具层 (横向切割)
设计原则:
- 垂直切片: 每个 module 自包含 controller + service
- 水平分层: persistence/core 跨模块复用
- 依赖方向: controllers → services → persistence
前端 (Vue3)
client/src/
├── components/ # 按布局区域划分
│ ├── TopBar/ # 顶部栏
│ ├── LeftPanel/ # 左侧面板
│ ├── CenterPanel/ # 中央面板
│ ├── RightPanel/ # 右侧面板
│ └── common/ # 通用组件
│
├── layouts/ # 布局模板
├── stores/ # 全局状态
├── router/ # 路由配置
├── composables/ # 组合式函数
└── api/ # API 客户端
设计原则:
- 布局驱动: 组件按 UI 区域组织
- 关注点分离: 状态、路由、API 独立管理
- 组合优于继承: 使用 composables 复用逻辑
共享层 (Shared)
shared/
├── types/ # TypeScript 类型
│ ├── sillytavern.types.ts # ST 兼容格式
│ ├── internal.types.ts # 内部扩展格式
│ └── converters.ts # 转换器
│
└── schemas/ # Zod 运行时验证
├── sillytavern.schemas.ts
└── internal.schemas.ts
设计原则:
- 单一真相源: 前后端共用类型定义
- 编译时 + 运行时: TypeScript + Zod 双重验证
- 向后兼容: 转换器保证旧数据可用
🔄 数据流
典型请求流程
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
interface CharacterRepository {
findById(id: string): Promise<CharacterCard>;
save(character: CharacterCard): Promise<void>;
delete(id: string): Promise<void>;
}
class FileSystemCharacterRepository implements CharacterRepository {
// 实现细节隐藏
}
2. Factory Pattern
class LLMProviderFactory {
static create(provider: ProviderType): LLMProvider {
switch (provider) {
case 'openai': return new OpenAIProvider();
case 'anthropic': return new AnthropicProvider();
}
}
}
3. Strategy Pattern
interface ActivationStrategy {
shouldActivate(entry: WorldInfoEntry, context: string): boolean;
}
class KeywordActivationStrategy implements ActivationStrategy {
// 关键词匹配逻辑
}
class RAGActivationStrategy implements ActivationStrategy {
// 向量检索逻辑
}
4. Adapter Pattern
// 适配器:SillyTavern 格式 → 内部格式
class SillyTavernAdapter {
static toInternal(stCard: STCharacterCard): CharacterCard {
return convertSTCharacterCardToInternal(stCard);
}
static toExternal(card: CharacterCard): STCharacterCard {
return convertCharacterCardToST(card);
}
}
🔐 安全性考虑
- 输入验证: Zod schemas 在边界验证所有输入
- 文件上传: 限制类型、大小,扫描恶意内容
- API 密钥: 环境变量管理,不提交到版本控制
- CORS: 严格配置允许的来源
- 路径遍历: 验证文件路径,防止
../攻击
📈 性能优化
- 懒加载: 前端路由和组件按需加载
- 缓存: LLM 响应缓存、角色卡元数据缓存
- 分页: 聊天记录、角色列表分页加载
- 流式响应: SSE 传输 LLM 生成内容
- 索引: 为搜索字段建立内存索引
🔄 扩展性设计
新增 LLM Provider
- 实现
LLMProvider接口 - 注册到
LLMProviderFactory - 更新配置 schema
新增持久化方式
- 实现 Repository 接口
- 创建新模块 (如
persistence/sqlite/) - 通过 DI 切换实现
新增激活方式
- 扩展
ActivationType枚举 - 实现新的
ActivationStrategy - 更新世界书 UI
📝 编码规范
TypeScript
- 严格模式:
strict: true - 禁止
any,使用unknown代替 - 接口命名不加
I前缀 - 枚举使用 PascalCase
Vue 3
- 优先使用
<script setup> - Props 用 TypeScript 定义
- 组合式逻辑提取为 composables
NestJS
- 每个模块一个文件夹
- Controller 只处理 HTTP 层
- Service 不包含 HTTP 相关代码
- 使用 DTO 验证输入
🎯 下一步计划
- 实现基础 CRUD: 角色卡、聊天、世界书
- 集成 Vercel AI SDK: 流式聊天
- 构建 UI 组件: 聊天界面、角色编辑器
- 实现转换器: 完整的导入导出功能
- 添加测试: 覆盖核心业务逻辑
- 编写文档: API 文档、用户指南
文档版本: 1.0
最后更新: 2026-04-24