Files
xianren_studio/docs/ARCHITECTURE.md
T

291 lines
19 KiB
Markdown
Raw Normal View History

# Xianren Studio(仙人工作室)代码组织与布局
> 本文档描述代码仓库的组织结构、模块职责、数据流与关键实现。**功能变更后如涉及结构、流程或数据模型,必须同步更新本文档。**
最后更新:2026-08-16
---
## 1. 总览
技术栈:Tauri 2Rust 壳 + 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 UIui/src
│ Tauri invokeapi.ts 封装)+ 事件监听(onEvent)
apps/desktop/src/commands.rs ← 所有 Tauri 命令(invoke_handler 注册于 lib.rs
├── crates/coreSQLitemodels / agents / workflows / scheduled_tasks / sessions / settings
├── crates/enginellama-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`
- `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``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(定时计划复用)。
**工作流执行引擎(`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 兼容远程 APIBearer 认证)。
- `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 | 预测条数(15 |
| `tavily_api_key` | (内置演示值) | 联网搜索 API Key |
| `tool_web_search_enabled` | true | 联网搜索工具开关 |
| `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`