21 KiB
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、邮件发送 mail.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 |
| 邮件 | mail_test(用当前 SMTP 配置发送测试邮件) |
| 应用/设置 | 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)。run_scheduled_task_once:大模型执行完成后,若任务开启邮件,按内容模式发送——固定模式用填写的主题/正文,LLM 模式用智能体回答作正文(主题留空取回答首行);邮件发送失败时任务标记失败但保留回答内容。send_task_email/load_smtp_config/split_recipients:读取设置页「邮件」SMTP 配置、拆分多收件人并调用mail.rs发送。
邮件发送模块(apps/desktop/src/mail.rs):
send_email:用 lettre 通过 SMTP 发送纯文本邮件;加密方式按设置分支——隐式 SSL 用relay(465)、STARTTLS 用starttls_relay(587)、无加密用builder_dangerous(25),均可自定义端口与超时。
工作流执行引擎(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、email_enabled、email_to、email_mode(fixed/llm)、email_subject、email_body |
计划任务;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。
新增列迁移:scheduled_tasks.email_enabled / email_to / email_mode / email_subject / email_body(ensure_column 幂等补齐)。
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(应用重启后把中断的「运行中」标记为失败);任务行含邮件配置字段(email_enabled/email_to/email_mode/email_subject/email_body)。
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 数组,顺序即加载顺序) |
smtp_host / smtp_port / smtp_tls |
空 / 465 / wrapper | SMTP 发件服务器、端口、加密方式(wrapper/starttls/none) |
smtp_user / smtp_password |
空 / 空 | SMTP 用户名(通常为发件邮箱)与授权码/密码 |
smtp_from / smtp_from_name |
空 / 仙人工作室 | 发件人地址(留空用用户名)与显示名称 |
recommend_dir |
数据目录/recommendations | 推荐列表目录 |
preset_agents_seeded |
首次启动后为 1 |
预制智能体是否已写入(内部标记,避免覆盖用户删除) |
preset_workflows_seeded |
首次启动后为 1 |
预制工作流是否已写入(内部标记,避免覆盖用户删除) |
9. 构建与运行
# 开发模式(自动拉起 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。