357 lines
17 KiB
Markdown
357 lines
17 KiB
Markdown
## SillyTavern 完全兼容优化方案(Go + Gin + Postgres + React)
|
||
|
||
> 目标:基于现有 Go + React 系统,构建一个与 SillyTavern(下称 ST)高度兼容的角色卡 / 世界书 / 正则 / 预设 / 对话平台。
|
||
|
||
---
|
||
|
||
### 1. 总体目标与设计原则
|
||
|
||
- **技术栈统一**:所有核心功能(角色卡、世界书、正则、预设、聊天、AI 集成)全部收敛到:
|
||
- **后端**:`server/` 下的 Go + Gin + PostgreSQL
|
||
- **前端**:`projects/web-app` 下的 React + TypeScript + Tailwind
|
||
- **SillyTavern 完全兼容**:
|
||
- 支持 ST 角色卡 V2/V3(chara_card_v2 / v3)导入导出;
|
||
- 支持 ST 世界书字段及触发逻辑(keys/secondary_keys/regex/depth/sticky/cooldown/probability/position 等);
|
||
- 支持 ST Regex Scripts 配置(placement/runOnEdit/markdownOnly/promptOnly/substituteRegex/min/max depth);
|
||
- 支持 ST 风格预设与 prompt 注入顺序(prompt_order / injection depth/position)。
|
||
- **单一真相来源(SSOT)**:
|
||
- **数据库即真相**:Postgres 负责持久化所有 ST 相关实体;
|
||
- **前端只是 UI**:React 只做编辑/展示,请求都经过 API,不再有“前端内存版预设/世界书”。
|
||
- **可扩展性**:
|
||
- 提前为插件系统预留 Hook(onUserInput/onWorldInfoScan/beforePromptBuild/onAssistantDone 等);
|
||
- 方便接入未来的 WebSocket/SSE 流式聊天、统计系统、市场/分享功能。
|
||
|
||
---
|
||
|
||
### 2. 当前 Go + React 系统现状(基于现有文档与代码)
|
||
|
||
#### 2.1 后端(server/)
|
||
|
||
根据 `projects/docs/development-progress.md`:
|
||
|
||
- ✅ **用户系统** 已完成(2024-02-26):
|
||
- 模型:`AppUser`, `AppUserSession`
|
||
- API:`/app/auth/register`, `/app/auth/login`, `/app/auth/refresh`, `/app/auth/logout`, `/app/auth/userinfo` 等
|
||
- JWT、会话、用户资料、密码修改均已实现
|
||
- ✅ **角色卡管理(AICharacter)** 已完成:
|
||
- 模型:`AICharacter`(完全兼容 ST V2 格式),使用 JSONB 存储 `tags/alternateGreetings/characterBook/extensions` 等复杂结构
|
||
- API:
|
||
- `POST /app/character`
|
||
- `GET /app/character`(分页、搜索、标签筛选)
|
||
- `GET /app/character/:id`
|
||
- `PUT /app/character/:id`
|
||
- `DELETE /app/character/:id`
|
||
- `POST /app/character/upload`(PNG/JSON 角色卡)
|
||
- `GET /app/character/:id/export`(导出 JSON)
|
||
- 工具:`utils/character_card.go`(PNG tEXt chunk / ST V2 工具)
|
||
- 🚧 **预设管理**:
|
||
- 前端页面 `/presets` 已完成(导入 JSON、复制、导出、编辑参数)。
|
||
- 后端预设 API 尚未实现(`development-progress.md` 已给出规划端点)。
|
||
- 📋 **对话系统 & AI 集成**:
|
||
- 前端基础 UI 已完成(`/chat` + 侧边栏 + 输入框)。
|
||
- 后端对话 API、AI 流式集成、世界书/正则逻辑尚在规划阶段。
|
||
|
||
#### 2.2 前端(`projects/web-app`)
|
||
|
||
- **用户系统前端**:`LoginPage/RegisterPage/ForgotPasswordPage/ProfilePage` 已完成,`api/client.ts + api/auth.ts` 已完成 token 注入与刷新。
|
||
- **角色卡管理前端**:`CharacterManagePage` + `api/character.ts`
|
||
- 功能:
|
||
- 上传 PNG/JSON 角色卡(调用 `/app/character/upload`)
|
||
- 编辑角色卡核心字段、内嵌 `characterBook`(世界书)字段
|
||
- 导出 JSON(调用 `/app/character/:id/export`)
|
||
- 搜索、分页、删除
|
||
- **预设管理前端**:`PresetManagePage`
|
||
- 当前为 **前端内存里的假数据** + 文件导入/导出;尚未接入真实后端 `/app/preset`。
|
||
- **聊天前端**:`ChatPage + Sidebar + ChatArea + CharacterPanel + SettingsPanel`
|
||
- 已实现基础布局、会话切换、角色选择、背景图/主题色设置等 UI。
|
||
- 消息发送、历史加载、预设切换、世界书/正则开关还需后端配合。
|
||
- **AI 配置前端**:`api/aiConfig.ts`(对应 `/app/ai-config` 系列)。
|
||
|
||
> 结论:**角色卡链路基本打通**,用户系统成熟;预设/对话/AI/世界书/正则目前主要停留在前端 UI 或规划层面,正好适合作为“集中下沉到 Go 后端”的突破口。
|
||
|
||
---
|
||
|
||
### 3. 目标架构(SillyTavern 兼容实现)
|
||
|
||
#### 3.1 核心领域模型(后端)
|
||
|
||
建议在现有 `model/app` 中引入或规范以下模型(部分已存在,可扩展):
|
||
|
||
- `AICharacter`(已存在)
|
||
- ST 角色卡 V2/V3 的标准化版本 + 原始 JSON 存档。
|
||
- `Worldbook` & `WorldbookEntry`
|
||
- 支持角色内世界书与全局世界书。
|
||
- `RegexScript`
|
||
- 支持 scope:`global | character | preset`。
|
||
- `Preset` & `PresetPrompt` & `PresetRegexBinding`
|
||
- 表达 ST preset 的参数、prompt 列表和 regex 绑定。
|
||
- `Conversation` & `Message`
|
||
- 对话与消息记录,支持与 `AICharacter`、`Preset` 和 AI 配置关联。
|
||
|
||
#### 3.2 运行时 Pipeline
|
||
|
||
所有客户端(现阶段只有 React Web,将来可增加其他)统一通过 Go 后端完成以下流程:
|
||
|
||
1. **输入正则脚本处理**(global/character/preset)
|
||
2. **世界书扫描**(keys/secondary_keys/regex/depth/sticky/cooldown 等)
|
||
3. **Prompt 构建**(角色 system + world info + preset prompts + 历史消息)
|
||
4. **AI 调用**(OpenAI/Anthropic/custom via `/app/ai-config`)
|
||
5. **输出正则脚本处理**
|
||
6. **持久化消息与统计**
|
||
|
||
React 前端只负责:
|
||
|
||
- 提供管理与编辑界面;
|
||
- 将用户选择的 preset/worldbook/regex 传给后端;
|
||
- 使用 SSE/WebSocket 将 AI 流输出展示给用户。
|
||
|
||
---
|
||
|
||
### 4. 后端详细优化方案(Go + Gin + Postgres)
|
||
|
||
#### 4.1 模型与数据库设计(概念级)
|
||
|
||
> 不强制你立刻改现有表名与字段;这部分作为“目标状态”,可通过迁移脚本或视图逐步对齐。
|
||
|
||
- **AICharacter**(已有)
|
||
- 新增/规范字段:
|
||
- `raw_card_json JSONB`:存原始 ST 角色卡,用于无损导出。
|
||
- `bound_worldbook_ids UUID[]`:角色绑定世界书 ID 列表(可选)。
|
||
- `bound_regex_ids UUID[]`:角色绑定正则脚本 ID 列表(可选)。
|
||
- **Worldbook / WorldbookEntry**
|
||
- Worldbook:`id, name, owner_char_id (nullable), meta JSONB, created_at, updated_at`
|
||
- WorldbookEntry:包含 ST 全字段:
|
||
- `uid, keys, secondary_keys, comment, content, constant, disabled, use_regex, case_sensitive, match_whole_words, selective, selective_logic, position, depth, order, probability, sticky, cooldown, delay, group, extra JSONB`
|
||
- **RegexScript**
|
||
- `id, name, find_regex, replace_with, trim_string`
|
||
- `placement INT[]`
|
||
- `disabled, markdown_only, run_on_edit, prompt_only`
|
||
- `substitute_regex, min_depth, max_depth`
|
||
- `scope, owner_char_id, owner_preset_id, raw_json`
|
||
- **Preset / PresetPrompt / PresetRegexBinding**
|
||
- 基本采样参数 + prompt 列表 + regex 绑定。
|
||
- **Conversation / Message**
|
||
- Conversation:`id, user_id, character_id, preset_id, ai_config_id, title, settings JSONB, created_at, updated_at`
|
||
- Message:`id, conversation_id, role, content, token_count, created_at`
|
||
|
||
#### 4.2 角色卡导入/导出(巩固现有实现)
|
||
|
||
> 从 `development-progress.md` 看,`/app/character` 模块已经“完全兼容 ST V2”,这里更多是规范与扩展。
|
||
|
||
- **导入 `/app/character/upload` 已具备**:
|
||
- PNG:使用 `utils/character_card.go` 提取 `chara` tEXt chunk → JSON。
|
||
- JSON:直接解析,填充 `AICharacter`。
|
||
- 优化点:
|
||
- 当角色卡中包含 `character_book` 时:
|
||
- 自动在 `worldbooks/worldbook_entries` 中创建对应记录;
|
||
- 将 worldbook.id 写入 `AICharacter.bound_worldbook_ids`(或冗余到 `characterBook` 字段中)。
|
||
- 当包含 `extensions.regex_scripts` 时:
|
||
- 自动创建 `RegexScript`(scope=character, owner_char_id=该角色)。
|
||
- 将脚本 id 写入 `AICharacter.bound_regex_ids` 或 `extensions` 中。
|
||
- **导出 `/app/character/:id/export`**:
|
||
- 若 `raw_card_json` 存在,优先以它为基础;将 DB 中新增的信息 patch 回去(例如补上最新的 worldbook/regex 变更)。
|
||
- 若不存在,则按 ST V2 规范组装 JSON(兼容 V1/V3 的 data 字段)。
|
||
|
||
#### 4.3 世界书(World Info)引擎
|
||
|
||
1. **世界书激活来源**:
|
||
- 全局启用列表(per-user 或全局 settings 中的 `active_worldbook_ids`)。
|
||
- 角色绑定:`AICharacter.bound_worldbook_ids`。
|
||
- 会话特定选项:`conversation.settings.activeWorldbookIds`(从前端设置面板传入)。
|
||
2. **触发与过滤算法**:
|
||
- 遍历所有激活 worldbook 的 entries:
|
||
- 忽略 `disabled` 的 entry。
|
||
- 若 `constant=true`:无视关键词匹配,直接进入候选(仍受 sticky/cooldown/delay/probability 控制)。
|
||
- keys 匹配:
|
||
- `use_regex=true`:将每个 key 作为正则(考虑 `caseSensitive`、`matchWholeWords` 标志)。
|
||
- 否则:普通字符串包含匹配(可选大小写敏感)。
|
||
- 若 `selective=true`:根据 `selectiveLogic` 结合 secondary_keys:
|
||
- `0=AND`:secondary_keys 至少一个命中;
|
||
- `1=OR`:主 keys 或 secondary_keys 任一命中;
|
||
- `2=NOT`:主 keys 命中,但 secondary_keys 不命中。
|
||
- depth:仅在最近 `entry.depth` 条消息(或 tokens)中搜索关键字。
|
||
- sticky/cooldown/delay:基于会话级状态(比如 Redis/内存/DB)存储 entry 上次触发时间或 sticky 状态。
|
||
- probability:按百分比随机决定最终是否注入。
|
||
3. **注入到 prompt 中**:
|
||
- 简化版:将所有命中 entry.content 拼接为 `[World Info]` 段,附加在 system prompt 后;
|
||
- 高级版:按 ST 行为,依据 position/depth/order,放到不同位置(角色描述前/后、历史中等)。
|
||
|
||
#### 4.4 正则脚本(Regex Scripts)引擎
|
||
|
||
1. **脚本来源**:
|
||
- Global:`scope='global'` 且 `disabled=false`。
|
||
- Character:`scope='character' AND owner_char_id=当前角色`。
|
||
- Preset:`preset_regex_bindings` 中绑定到当前 preset 的脚本。
|
||
2. **执行阶段(placement)**:
|
||
- 输入阶段(placement=1):用户消息入库前;
|
||
- 输出阶段(placement=2):模型输出生成后、入库/返回前;
|
||
- 世界书阶段(placement=3):扫描 / 注入 world info 内容前;
|
||
- 推理阶段(placement=4):如有单独 reasoning prompt,可在构建 reasoning 时处理。
|
||
3. **实现细节(Go)**:
|
||
- 解析 JS 风格正则 `/pattern/flags`,支持 `i/m/s`。
|
||
- 替换宏:
|
||
- `substitute_regex=1` → 用当前用户名称替换 `{{user}}`;
|
||
- `=2` → 用当前角色名替换 `{{char}}`;
|
||
- `trim_string`:按行定义需要从文本中删掉的子串。
|
||
- minDepth/maxDepth:结合消息历史深度决定是否执行脚本。
|
||
- runOnEdit:在“消息编辑接口”中也调用该脚本集。
|
||
- markdownOnly/promptOnly:通过标志决定作用在 UI 文本 or prompt 文本。
|
||
|
||
#### 4.5 Prompt Pipeline 与对话 API
|
||
|
||
将现有/规划中的对话 API 统一收敛到一个 pipeline,例如 `/app/conversation/:id/message`(或 `/conversations/:id/messages`,按统一风格即可):
|
||
|
||
1. **接收请求**:
|
||
|
||
```json
|
||
{
|
||
"content": "用户输入文本",
|
||
"options": {
|
||
"presetId": "uuid-xxx",
|
||
"overrideModel": "gpt-4",
|
||
"stream": true
|
||
}
|
||
}
|
||
```
|
||
|
||
2. **管线步骤**:
|
||
1. 根据 conversationId 加载:
|
||
- Conversation、AICharacter、Preset、AI 配置、世界书/正则等上下文;
|
||
- 合并 settings(会话内 + 用户全局)。
|
||
2. 输入文本经过 regex 引擎(placement=1)。
|
||
3. 写入一条 user message 到 DB。
|
||
4. 从最近 N 条消息构造 world info 触发文本,调用世界书引擎得到 entries。
|
||
5. 构建 prompt:
|
||
- system:角色 system + scenario + authorsNote + preset.systemPrompt + world info;
|
||
- history:最近若干 user/assistant 消息;
|
||
- preset.prompts:按 depth/position 注入额外 messages。
|
||
6. 调用 AI 服务(依据 `/app/ai-config` 中 baseUrl/apiKey/model):
|
||
- 支持流式:通过 SSE/WebSocket 对前端推送 tokens。
|
||
7. 将完整 assistant 输出经过 regex 引擎(placement=2)。
|
||
8. 写入一条 assistant message 到 DB。
|
||
9. 返回响应给前端(同步返回最终内容 + 可选流式过程)。
|
||
|
||
---
|
||
|
||
### 5. 前端详细优化方案(React `projects/web-app`)
|
||
|
||
#### 5.1 角色卡管理(CharacterManagePage)
|
||
|
||
目标:完全对齐 ST 角色卡 V2/V3 字段,并为世界书/正则后端拆分打好基础。
|
||
|
||
优化点:
|
||
|
||
- **字段映射明确化**:
|
||
- 确保 `Character` 接口与后端模型一致,并在注释中标明 ST 对应字段:
|
||
- `firstMes` ↔ `first_mes`
|
||
- `mesExample` ↔ `mes_example`
|
||
- `systemPrompt` ↔ `system_prompt`
|
||
- `postHistoryInstructions` ↔ `post_history_instructions`
|
||
- `characterBook` ↔ `character_book`
|
||
- `extensions` ↔ `extensions`
|
||
- **世界书编辑 UI 扩展**:
|
||
- 目前 `WorldBookEntry` 较简化(keys/content/enabled/insertion_order/position)。
|
||
- 建议增加更多高级属性编辑项(可以先折叠到“高级设置”中):
|
||
- `secondary_keys` / `constant` / `use_regex` / `case_sensitive` / `match_whole_words`;
|
||
- `selective` / `selective_logic`;
|
||
- `depth` / `order` / `probability` / `sticky` / `cooldown` / `delay` / `group`。
|
||
- 保存时将这些字段一并放入 `characterBook.entries`,后端负责拆分入 DB。
|
||
- **导入/导出保持 ST 兼容**:
|
||
- 当前上传/导出流程基本正确;随着后端增强,无需大改,只要保证前端不破坏 JSON 结构即可。
|
||
|
||
#### 5.2 预设管理(PresetManagePage)
|
||
|
||
目标:把当前的“前端内存预设”完全改造成**后端驱动的 ST 预设系统**。
|
||
|
||
实施步骤:
|
||
|
||
1. 后端按 `development-progress.md` 规划实现 `/app/preset` API:
|
||
|
||
```text
|
||
POST /app/preset
|
||
GET /app/preset
|
||
GET /app/preset/:id
|
||
PUT /app/preset/:id
|
||
DELETE /app/preset/:id
|
||
POST /app/preset/import
|
||
GET /app/preset/:id/export
|
||
```
|
||
|
||
2. 前端新建 `src/api/preset.ts` 对应这些端点。
|
||
3. 将 `PresetManagePage` 中的 `useState` 初始数据删除,改为:
|
||
- `const { data } = await presetApi.getPresetList()`;
|
||
- 导入、导出、编辑、删除全部通过后端。
|
||
4. 预设 JSON 的读写字段与 ST 对齐:
|
||
- `temperature/top_p/top_k/frequency_penalty/presence_penalty/system_prompt/stop_sequences/...`。
|
||
|
||
#### 5.3 聊天与设置面板(ChatPage / SettingsPanel / CharacterPanel)
|
||
|
||
目标:把“选 preset / 选世界书 / 选正则”的入口统一放到设置面板,并通过 `conversation.settings` 与后端 pipeline 串联。
|
||
|
||
建议:
|
||
|
||
- 在 `SettingsPanel` 中增加:
|
||
- 当前会话使用的 preset 选择器(下拉 list:来自 `/app/preset`)。
|
||
- 可选的世界书列表(来自未来的 `/app/worldbook`,初期可只展示角色内 worldbook)。
|
||
- 可选的全局正则脚本列表(来自未来的 `/app/regex`)。
|
||
- 保存时调用 `/app/conversation/:id/settings`(或 `PUT /app/conversation/:id`):
|
||
|
||
```ts
|
||
settings: {
|
||
backgroundImage: string
|
||
themeColor: string
|
||
presetId?: string
|
||
activeWorldbookIds?: string[]
|
||
activeRegexIds?: string[]
|
||
}
|
||
```
|
||
|
||
- 发送消息时,前端不再参与世界书/正则逻辑,只负责传 `content`,后端从 conversation/preset/character 中解析所有配置。
|
||
|
||
---
|
||
|
||
### 6. 分阶段落地路线图(仅 Go + React)
|
||
|
||
#### 阶段 1:打通所有核心 CRUD 与数据流(短期)
|
||
|
||
- 后端:
|
||
- 巩固 `/app/character` 模块(确保 ST V2/V3 完全兼容)。
|
||
- 实现 `/app/preset` 模块(CRUD + 导入/导出)。
|
||
- 设计并实现 `worldbooks/worldbook_entries` 与 `regex_scripts` 的数据结构与基础 API。
|
||
- 前端:
|
||
- 改造 `PresetManagePage` 接入真实 API。
|
||
- 在 `CharacterManagePage` 中补全世界书 entry 字段,保持 ST 兼容。
|
||
|
||
#### 阶段 2:实现 Prompt Pipeline 与对话 API(中期)
|
||
|
||
- 后端:
|
||
- 聚合世界书与正则逻辑,形成独立的 `chatPipeline` service。
|
||
- 在 `/app/conversation/:id/message`(或 `/conversations/:id/messages`)中调用 pipeline,完成一次“完整的 ST 风格对话请求”。
|
||
- 引入流式输出(SSE 或 WebSocket)。
|
||
- 前端:
|
||
- 在 `ChatPage/SettingsPanel` 中增加 preset/worldbook/regex 的选择与设置保存。
|
||
- 调整 ChatArea 接收流式输出并实时渲染。
|
||
|
||
#### 阶段 3:高级特性与插件系统(长期)
|
||
|
||
- 后端:
|
||
- 引入插件概念(`plugins` 表 + manifest + hooks)。
|
||
- 实现插件执行沙箱(WASM 或 goja),并在 pipeline 中注入插件 hooks。
|
||
- 增加统计、日志与审计功能。
|
||
- 前端:
|
||
- 增加插件管理页面与可视化配置。
|
||
- 对接统计与调试视图(例如:查看某次回复中哪些 world info/regex/插件生效)。
|
||
|
||
---
|
||
|
||
### 7. 总结
|
||
|
||
- 你当前的 Go + React 系统已经完成:
|
||
- **用户系统**(认证/资料);
|
||
- **角色卡管理**(完整 ST V2 兼容导入/导出);
|
||
- **预设管理与对话 UI 的前端骨架**。
|
||
- 接下来最重要的三件事是:
|
||
- **在后端固化 ST 兼容的领域模型与 Prompt Pipeline**;
|
||
- **让 `/app/conversation` 成为唯一的“对话真相源”,React 只是 UI 壳**。
|
||
|