298 lines
20 KiB
Markdown
298 lines
20 KiB
Markdown
# Xianren Studio(仙人工作室)代码组织与布局
|
||
|
||
> 本文档描述代码仓库的组织结构、模块职责、数据流与关键实现。**功能变更后如涉及结构、流程或数据模型,必须同步更新本文档。**
|
||
|
||
最后更新:2026-08-16
|
||
|
||
---
|
||
|
||
## 1. 总览
|
||
|
||
技术栈:Tauri 2(Rust 壳 + WebView2) + React 18 / TypeScript / Vite / Tailwind,推理引擎为 llama.cpp 的 `llama-server` 子进程。
|
||
|
||
```
|
||
xianren_studio/
|
||
├── apps/desktop/ Tauri 桌面壳(Rust 命令层、tauri.conf.json、推荐模型/预制智能体/预制工作流默认 JSON、工作流执行引擎 workflow.rs)
|
||
├── crates/core/ 领域核心:SQLite(模型注册表、智能体、工作流、定时计划、会话、消息、设置)
|
||
├── crates/engine/ llama-server 生命周期 + 流式/非流式聊天(本地与远程 OpenAI 兼容)
|
||
├── crates/download/ 分片断点续传下载器
|
||
├── crates/api/ OpenAI 兼容本地 API 服务(axum)
|
||
├── ui/ React 前端
|
||
├── scripts/ 构建 / 引擎与测试模型下载脚本
|
||
├── docs/ 产品与技术方案、功能记录、本文档
|
||
└── AGENTS.md 开发/维护约定(含文档同步规则)
|
||
```
|
||
|
||
---
|
||
|
||
## 2. 分层与数据流
|
||
|
||
```
|
||
React UI(ui/src)
|
||
│ Tauri invoke(api.ts 封装)+ 事件监听(onEvent)
|
||
▼
|
||
apps/desktop/src/commands.rs ← 所有 Tauri 命令(invoke_handler 注册于 lib.rs)
|
||
│
|
||
├── crates/core(SQLite:models / agents / workflows / scheduled_tasks / sessions / settings)
|
||
├── crates/engine(llama-server 子进程 / 远程 OpenAI API)
|
||
├── crates/download(下载任务)
|
||
└── crates/api(本地 API 服务)
|
||
```
|
||
|
||
前端通过 `ui/src/api.ts` 的 `api.*` 方法调用后端命令;后端通过 Tauri `emit` 向前端推送事件(见 §7 事件通道)。
|
||
|
||
---
|
||
|
||
## 3. 前端结构(`ui/`)
|
||
|
||
```
|
||
ui/src/
|
||
├── main.tsx 入口
|
||
├── App.tsx 导航栏、路由(9 个页面)、全局事件订阅(引擎/下载/模型/建议/定时计划/会话)
|
||
├── api.ts Tauri invoke 封装 + 全部请求/事件 TypeScript 类型
|
||
├── store.ts zustand 全局状态(见下)
|
||
├── styles.css 全局样式
|
||
├── components/Icon.tsx SVG 图标库
|
||
└── pages/
|
||
├── ChatPage.tsx 对话页(最大的页面,含会话快照、消息气泡、模型下拉、建议标签)
|
||
├── AgentsPage.tsx 智能体列表(预制/自定义分组、新建/编辑/删除/恢复预制)
|
||
├── AgentChatPage.tsx 智能体会话页(按路由加载智能体并渲染 ChatPage 智能体模式)
|
||
├── WorkflowsPage.tsx 工作流列表(预制/自定义分组、新建/删除/恢复预制)
|
||
├── WorkflowCanvasPage.tsx 工作流画布(节点拖入/拖拽/连线、滚轮缩放、空白平移、节点/连线右键菜单、导入导出、运行与结果展示)
|
||
├── ModelsPage.tsx 模型管理(本地部署 + 在线 API 启用开关)
|
||
├── ModelPlazaPage.tsx 模型广场(HF/ModelScope 搜索与下载)
|
||
├── TasksPage.tsx 任务(下载/部署进度)
|
||
├── ToolsPage.tsx 工具(Tavily 联网搜索)
|
||
├── ServerPage.tsx 服务管理(本地 OpenAI API)
|
||
└── SettingsPage.tsx 设置
|
||
```
|
||
|
||
### 3.1 全局状态(`store.ts`)
|
||
|
||
主要状态字段:
|
||
|
||
| 字段 | 说明 |
|
||
| --- | --- |
|
||
| `agents` / `agentsLoaded` | 智能体列表(预制 + 自定义)与加载标记 |
|
||
| `agentConversations` | 智能体 id → 会话列表(各智能体会话隔离,key 为智能体 id) |
|
||
| `workflows` / `workflowsLoaded` | 工作流列表(预制 + 自定义)与加载标记 |
|
||
| `scheduledTasks` | 定时计划列表(任务页) |
|
||
| `models` | 模型列表(含 `enabled`、`kind`、`file_name` 等) |
|
||
| `engine` | 引擎状态(running / model / port) |
|
||
| `deployStates` / `deployProgress` | 各模型部署状态(loading/ready/error)与进度 |
|
||
| `chatSuggestionsByMessage` | 消息 ID → 预测建议列表(全局接收,跨选项卡不丢) |
|
||
| `conversations` | 会话列表 |
|
||
| `tasks` | 下载/部署任务 |
|
||
| `server` | 本地 API 服务状态 |
|
||
|
||
### 3.2 会话快照(`ChatPage.tsx`)
|
||
|
||
切换选项卡时 React Router 会卸载页面,因此 `ChatPage.tsx` 顶部维护了模块级 `chatSession` 对象(当前会话、所选模型、消息、输入草稿、请求参数、面板开合、工具、版本、附件),每次渲染后写回;重新挂载时用快照初始化,实现「切走再回来保持原状」。
|
||
|
||
---
|
||
|
||
## 4. 后端命令层(`apps/desktop`)
|
||
|
||
### 4.1 `lib.rs`
|
||
|
||
- `run()`:初始化数据目录(`%APPDATA%\XianrenStudio`)、日志、panic 钩子;构建 Tauri 应用并注册 `invoke_handler`。
|
||
- 注册插件:`tauri-plugin-opener`、`tauri-plugin-autostart`(开机启动,Windows 写注册表启动项)。
|
||
- `setup` 末尾启动**自动加载模型**任务(`auto_load_models`:按设置列表顺序逐个部署,最后一个保持运行)。
|
||
- `setup` 末尾启动**定时计划后台调度循环**(tokio interval 每 30 秒调用 `run_due_scheduled_tasks`)。
|
||
- 数据目录:`logs/`、`models/`、`engines/`、`recommendations/`。
|
||
|
||
### 4.2 `commands.rs`(命令分组)
|
||
|
||
| 分组 | 命令 |
|
||
| --- | --- |
|
||
| 智能体 | `list_agents`、`add_agent`、`update_agent`、`remove_agent`、`set_agent_enabled`、`restore_preset_agents` |
|
||
| 工作流 | `list_workflows`、`add_workflow`、`update_workflow`、`remove_workflow`、`set_workflow_enabled`、`restore_preset_workflows`、`run_workflow` |
|
||
| 定时计划 | `list_scheduled_tasks`、`add_scheduled_task`、`update_scheduled_task`、`remove_scheduled_task`、`set_scheduled_task_enabled`、`run_scheduled_task_now` |
|
||
| 应用/设置 | `app_info`、`autostart_status`、`autostart_set`、`settings_get`、`settings_set` |
|
||
| 模型 | `list_models`、`import_model`、`remove_model`、`set_model_enabled`、`scan_models`、`add_remote_model` |
|
||
| 模型广场 | `search_models`、`list_model_files`、`list_recommended_models`、`import_recommendations`、`fetch_model_page` |
|
||
| 会话 | `list_conversations`(可按 `agent_id` 过滤)、`create_conversation`(可选 `agent_id`,自动继承智能体系统提示词与默认模型)、`rename_conversation`、`set_conversation_pinned`、`set_conversation_favorite`、`import_conversation`、`set_conversation_tools`、`delete_conversation`、`get_messages` |
|
||
| 消息 | `chat_send`、`chat_stop`、`regenerate_message`、`edit_message`、`list_message_versions`、`apply_message_version` |
|
||
| 引擎 | `engine_start`、`engine_stop`、`engine_status`、`deploy_model` |
|
||
| 工具 | `web_search`(Tavily)、`list_skills`、`add_skill`、`update_skill`、`remove_skill`、`set_skill_enabled`、`test_skill`、`list_mcp_servers`、`add_mcp_server`、`update_mcp_server`、`remove_mcp_server`、`set_mcp_server_enabled`、`mcp_test_server`、`mcp_list_tools`、`mcp_call_tool` |
|
||
| 下载 | `download_enqueue` |
|
||
| 服务 | `server_start`、`server_stop`、`server_status` |
|
||
| 其他 | `report_error`、`open_path` |
|
||
|
||
**聊天相关核心函数(均在 `commands.rs`):**
|
||
|
||
- `chat_send`:写入用户消息 → 构建历史 → 拉起后台生成任务。
|
||
- `run_generation_and_stream`:统一生成入口(本地引擎 / 远程 API),流式转发 token、统计用量、写库、发 `chat://done`,随后触发标题生成与建议生成。
|
||
- `inject_system_prompts`:把会话/智能体系统提示词(`conversations.system_prompt`)与工具说明统一注入消息历史最前;`chat_send` / `regenerate_message` / `edit_message` 三条链路共用。
|
||
- `maybe_generate_suggestions` → `call_suggestion_model` → `parse_suggestions`:回答完成后预测用户接下来可能说的话。统一要求模型输出 JSON 数组;最多重试 3 次;过滤元信息行与回答原文片段;每条 ≤ 30 字。
|
||
- `maybe_generate_conversation_title`:首轮对话自动生成标题。
|
||
- `create_conversation`:先在指定作用域(普通对话或某智能体)查找空会话(`find_empty_conversation`)复用,没有才新建;智能体会话写入该智能体的系统提示词与默认模型。
|
||
- `seed_preset_agents`:启动时首次写入 `default_agents.json` 中的预制智能体(`settings.preset_agents_seeded` 标记只执行一次);`restore_preset_agents` 命令可随时补齐缺失预制体。
|
||
- `run_workflow`:加载工作流节点/连线 → 交给 `workflow.rs` 执行引擎按拓扑顺序运行;LLM 节点经 `resolve_workflow_node_model` 解析模型(节点 → 工作流默认 → 运行参数 → 自动回退),`call_model_text` 非流式调用本地引擎或远程 API,逐节点推送 `workflow://node-status` 事件。
|
||
- `seed_preset_workflows`:启动时首次写入 `default_workflows.json` 中的预制工作流(`settings.preset_workflows_seeded` 标记只执行一次);`restore_preset_workflows` 命令可随时补齐缺失预制体。
|
||
- `run_due_scheduled_tasks`:后台调度入口,由 `lib.rs` 启动的 tokio 循环每 30 秒调用一次;先清理重启前中断的「运行中」任务,再取出到点的启用计划逐个标记并异步执行。
|
||
- `execute_scheduled_task` / `run_scheduled_task_once`:定时计划单次执行——解析执行智能体(指定智能体已删除时回退「通用助手」)与模型(`resolve_scheduled_model`:智能体默认模型 → 第一个本地模型 → 第一个已启用在线模型),在该智能体作用域内创建/复用空会话,写入用户消息并注入智能体系统提示词,本地模型未运行则自动拉起引擎,最后经 `call_model_messages` 调用模型并把助手回答写回数据库。
|
||
- `call_model_messages`:`call_model_text` 的通用版,可携带 system + 多轮消息调用本地引擎或远程 OpenAI 兼容 API(定时计划复用)。
|
||
- `auto_load_models`:应用启动时读取设置键 `auto_load_models`(JSON 数组,本地模型 id 按序排列),逐个调用 `start_engine_and_wait` 部署,非最后一个模型加载完即停止,最后一个保持运行。
|
||
- `start_engine_and_wait`:本地模型引擎启动的公共实现(构造 `EngineConfig`、推送 `engine://deploy` / `engine://deploy-progress` 事件、轮询加载进度、更新 `engine_base`);`deploy_model` 命令与 `auto_load_models` 共用。
|
||
- `autostart_status` / `autostart_set`:查询 / 设置开机启动(`tauri-plugin-autostart` 的 `ManagerExt`)。
|
||
|
||
**工作流执行引擎(`apps/desktop/src/workflow.rs`):**
|
||
|
||
- `topo_order`:Kahn 拓扑排序,检测循环 / 未知节点 / 自连。
|
||
- `resolve_template`:解析 `{{input}}` 与 `{{节点id}}` 变量。
|
||
- `execute_workflow`:按拓扑顺序执行 start / text / llm / end 节点,收集各节点输出并汇总最终结果;通过注入的 `call_model` 闭包调用模型,与具体模型解耦(单元测试用假模型跑通 5 个案例,另有真实模型端到端测试 `real_model_translation_workflow_e2e`)。
|
||
|
||
**工具协议(技能 / MCP):**
|
||
|
||
- `apps/desktop/src/tools.rs`:`ToolDef`、标记解析(`[[skill:名]]` / `[[mcp:服务:工具:参数]]`)、`MarkerFilter`(流式输出时过滤标记)、`run_tool_round`(执行工具并回填模型,最多 3 轮)、系统提示词构建。
|
||
- `apps/desktop/src/mcp_client.rs`:MCP Streamable HTTP 客户端(`initialize` / `tools/list` / `tools/call`,兼容 SSE 响应)。
|
||
- 聊天链路:`chat_send` / `regenerate_message` / `edit_message` 解析会话工具并注入系统提示词 → `run_generation_and_stream` 流式输出时过滤标记 → 完成后执行工具循环并保存最终内容。
|
||
|
||
---
|
||
|
||
## 5. 核心库(`crates/core`)
|
||
|
||
### 5.1 `app.rs`
|
||
|
||
- `CoreApp`:持有 SQLite 连接(`Mutex<Connection>`)与数据目录。
|
||
- `seed_default_settings`:启动时用 `insert_default` 补齐缺失的设置键(不覆盖用户已保存值)。
|
||
- `open_db`:执行 `schema.sql` + **增量迁移** `ensure_column`(为旧库补充新列,幂等)。
|
||
|
||
### 5.2 `schema.sql`(表结构)
|
||
|
||
| 表 | 关键字段 | 说明 |
|
||
| --- | --- | --- |
|
||
| `models` | `kind`(local/remote)、`enabled`、`file_name`、`file_path`、`status`、`base_url`、`api_key`、`api_model`、`meta_json` | 模型注册表;`enabled` 决定在线 API 模型是否出现在聊天页可选列表 |
|
||
| `settings` | `key`/`value` | 键值设置 |
|
||
| `conversations` | `title`、`model_id`、`system_prompt`、`agent_id`、`pinned`、`favorite`、`tools_json` | 会话;`agent_id` 为空表示普通对话,否则属于对应智能体 |
|
||
| `agents` | `kind`(preset/custom)、`name`、`icon`、`description`、`system_prompt`、`model_id`、`enabled` | 智能体(预制 + 自定义) |
|
||
| `workflows` | `kind`(preset/custom)、`name`、`icon`、`description`、`nodes_json`、`edges_json`、`model_id`、`enabled` | 工作流(节点/连线以 JSON 存储,节点含 type/label/position/data) |
|
||
| `scheduled_tasks` | `name`、`agent_id`、`prompt`、`interval_minutes`、`enabled`、`next_run_at`、`last_run_at`、`last_status`(idle/running/success/error)、`last_result`、`last_error` | 定时计划;`next_run_at` 由 SQLite `datetime('now', '+N minutes')` 计算(UTC),每次执行完成后推进 |
|
||
| `messages` | `role`、`model_id`、`content`、`tokens_in/out`、`elapsed_ms`、`first_token_ms`、`images_json` | 消息;`model_id` 记录该回答所用模型(回答下方展示模型名) |
|
||
| `message_versions` | `content`、`tokens_out`、`seq` | 重新生成前的旧版本 |
|
||
| `skills` | `name`、`description`、`content`、`enabled` | 技能工具库 |
|
||
| `mcp_servers` | `name`、`description`、`url`、`auth_token`、`enabled` | MCP 服务配置 |
|
||
|
||
迁移清单(`ensure_column`):`models.kind/base_url/api_key/api_model/enabled`、`messages.elapsed_ms/first_token_ms/images_json/model_id`、`conversations.pinned/favorite/tools_json/agent_id`。
|
||
|
||
### 5.3 `models.rs`
|
||
|
||
模型 CRUD、`set_enabled`(启用/停用)、`scan_directory`(扫描 GGUF 目录)、量化猜测 `guess_quant`。
|
||
|
||
### 5.4 `sessions.rs`
|
||
|
||
会话/消息/版本 CRUD;`find_empty_conversation`(新建对话复用空会话);`import_message`(导入对话用,可指定创建时间)。
|
||
|
||
### 5.5 `agents.rs`
|
||
|
||
智能体 CRUD(`list/get/insert/update/delete/set_enabled`)与预制体批量写入 `insert_presets_if_missing`(按 id 忽略已存在,供首次种子与「恢复预制智能体」复用)。
|
||
|
||
### 5.6 `settings.rs`
|
||
|
||
`get/set/insert_default/all`。
|
||
|
||
### 5.7 `workflows.rs`
|
||
|
||
工作流 CRUD(`list/get/insert/update/delete/set_enabled`)与预制体批量写入 `insert_presets_if_missing`(按 id 忽略已存在);节点 `WorkflowNode`(type=start/llm/text/end、position、data)与连线 `WorkflowEdge` 以 JSON 列存储,序列化/反序列化在读写时完成。
|
||
|
||
### 5.8 `scheduled_tasks.rs`
|
||
|
||
定时计划 CRUD 与运行状态落库:`list/get/insert/update/delete/set_enabled`(停用时清空 `next_run_at`,启用时重新计算)、`mark_running` / `finish_run`(写执行结果并推进下次执行时间)、`list_due`(到点且未运行中的启用计划)、`reset_stale_running`(应用重启后把中断的「运行中」标记为失败)。
|
||
|
||
---
|
||
|
||
## 6. 引擎与远程调用(`crates/engine`)
|
||
|
||
### 6.1 `manager.rs`(`EngineManager`)
|
||
|
||
- `start`:以子进程启动 llama-server(`CREATE_NO_WINDOW`),轮询 `/health` 等待就绪;最多 180s。
|
||
- `stop`:先请求 `/shutdown`,超时再 kill。
|
||
- `stream_chat` / `chat`:流式 / 非流式聊天(`/v1/chat/completions`)。
|
||
- `status`:返回 running / port / model。
|
||
|
||
### 6.2 `remote.rs`
|
||
|
||
- `stream_chat_remote` / `chat_remote`:OpenAI 兼容远程 API(Bearer 认证)。
|
||
- `normalize_base`:Base URL 自动补 `/v1`。
|
||
|
||
### 6.3 `types.rs`
|
||
|
||
- `ChatMessage`:role/content/images,多模态时序列化为 content 数组。
|
||
- `ChatRequest`:model / messages / temperature / top_p / max_tokens / stream。
|
||
- `ChatStreamEvent`:Text / Reasoning(思考过程)/ Usage。
|
||
|
||
---
|
||
|
||
## 7. 事件通道(Tauri emit → 前端 onEvent)
|
||
|
||
| 事件 | 方向 | 说明 |
|
||
| --- | --- | --- |
|
||
| `chat://token` | 后端→前端 | 流式增量文本 |
|
||
| `chat://reasoning` | 后端→前端 | 思考过程增量 |
|
||
| `chat://done` | 后端→前端 | 回答完成(含 message_id、model_id、用量) |
|
||
| `chat://suggestions` | 后端→前端 | 预测建议列表(全局订阅,存入 store) |
|
||
| `chat://tool-status` | 后端→前端 | 工具调用状态(running/done),界面提示 |
|
||
| `chat://message-updated` | 后端→前端 | 消息内容更新(恢复版本后) |
|
||
| `chat://title-updated` | 后端→前端 | 标题更新 |
|
||
| `chat://error` | 后端→前端 | 生成错误 |
|
||
| `engine://status` | 后端→前端 | 引擎状态变化 |
|
||
| `engine://deploy` | 后端→前端 | 部署状态(loading/ready/error) |
|
||
| `engine://deploy-progress` | 后端→前端 | 部署进度百分比/阶段 |
|
||
| `models://updated` | 后端→前端 | 模型列表变化 |
|
||
| `download://started/progress/done/error` | 后端→前端 | 下载任务进度 |
|
||
| `server://status` | 后端→前端 | 本地 API 服务状态 |
|
||
| `workflow://node-status` | 后端→前端 | 工作流节点运行状态(running/done/error)与输出文本 |
|
||
| `scheduled://updated` | 后端→前端 | 定时计划状态变化(创建/编辑/启停/执行完成),前端刷新列表 |
|
||
| `conversations://updated` | 后端→前端 | 定时计划执行后会话列表变化(供智能体会话页刷新) |
|
||
|
||
---
|
||
|
||
## 8. 设置项(设置键 / 默认值)
|
||
|
||
定义于 `crates/core/src/app.rs` 的 `seed_default_settings`:
|
||
|
||
| 键 | 默认值 | 用途 |
|
||
| --- | --- | --- |
|
||
| `model_dir` | 数据目录/models | 模型目录 |
|
||
| `engine_bin` | engines/cpu/llama-server.exe | llama-server 路径 |
|
||
| `backend` | auto | 后端(auto/cpu/cuda/vulkan) |
|
||
| `hf_endpoint` | https://hf-mirror.com | 模型下载源 |
|
||
| `api_port` / `api_key` / `api_enabled` | 1234 / 空 / false | 本地 API 服务 |
|
||
| `upload_max_mb` | 10 | 上传大小上限 |
|
||
| `auto_title` | true | 自动生成标题 |
|
||
| `suggest_enabled` | true | 预测用户接下来说的话开关 |
|
||
| `suggest_count` | 3 | 预测条数(1–5) |
|
||
| `tavily_api_key` | (内置演示值) | 联网搜索 API Key |
|
||
| `tool_web_search_enabled` | true | 联网搜索工具开关 |
|
||
| `auto_load_models` | `[]` | 启动时自动加载的本地模型 id 列表(JSON 数组,顺序即加载顺序) |
|
||
| `recommend_dir` | 数据目录/recommendations | 推荐列表目录 |
|
||
| `preset_agents_seeded` | 首次启动后为 `1` | 预制智能体是否已写入(内部标记,避免覆盖用户删除) |
|
||
| `preset_workflows_seeded` | 首次启动后为 `1` | 预制工作流是否已写入(内部标记,避免覆盖用户删除) |
|
||
|
||
---
|
||
|
||
## 9. 构建与运行
|
||
|
||
```powershell
|
||
# 开发模式(自动拉起 Vite + Tauri)
|
||
npm --prefix apps/desktop run dev
|
||
|
||
# 正式版(必须带 custom-protocol 特性,前端资源才会内嵌进 exe)
|
||
npm run build --prefix ui
|
||
cargo build --release -p xianren-desktop --features custom-protocol
|
||
|
||
# 或一键打包(tauri build 会自动启用该特性并打 NSIS 安装包)
|
||
npm --prefix apps/desktop run build
|
||
```
|
||
|
||
> 注意:不带 `custom-protocol` 编译出的 release exe **不会内嵌前端页面**,直接运行会白屏,因此正式发布必须带该特性。
|
||
|
||
---
|
||
|
||
## 10. 文档维护约定
|
||
|
||
- 功能变化 → 更新 `docs/FEATURES.md` 对应章节,并在「改动记录」追加。
|
||
- 结构/流程/数据模型变化 → 更新本文档对应章节。
|
||
- 大版本信息(技术栈、目录说明)变化 → 同步更新 `README.md`。
|