Files
MyAiDesk/nomifun-tauri/docs/architecture/overview.zh.md
T
freedak f7a720204a Update: 将子项目从 submodule 转为完整内容
- 移除 GovAI, nomifun-tauri, 算力盒子 的 submodule 引用
- 添加所有子项目的完整源代码
- 保留原始 .git 为 .git.bak 备份
2026-07-04 19:20:46 +08:00

139 lines
9.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 架构总览
NomiFun 围绕一个核心原则构建:**一份 Rust 后端、两种宿主形态、一份前端**。无论你启动桌面产品 **NomiFun**,还是自托管 Web 服务器,同一个 `axum` HTTP/WS 服务器(`nomifun-app`,二进制 `nomicore`)都在宿主进程中执行。`ui/` 下的 React 19 SPA 是唯一客户端,它始终通过普通的 HTTP 与 WebSocket 通信 —— 没有 Electron preload,也没有 Tauri 自定义协议。
本文档是这张地图的总图。配套的四篇文档分别深入介绍各个部分:
- [`backend-crates.md`](backend-crates.zh.md) —— 29 个 `nomifun-*` crate。
- [`agent-engine.md`](agent-engine.zh.md) —— 15 个 `nomi-*` crateAI 引擎)。
- [`frontend.md`](frontend.zh.md) —— React SPA、适配层、路由。
- [`communication.md`](communication.zh.md) —— HTTP / WebSocket / Tauri IPC / ACP / MCP。
- [`data-and-storage.md`](data-and-storage.zh.md) —— SQLite、工作区、运行时。
## 双宿主模型
```
┌─────────────────────────────────────┐
│ ui/ React 19 SPA (Vite build) │
│ HashRouter · SWR · Arco · UnoCSS │
│ http://127.0.0.1:<port>/api + /ws│
└─────────────────────────────────────┘
▲ ▲
HTTP/REST│ WebSocket│ /ws
│ │
┌───────────────── desktop ─────┴────┐ ┌─────── web ───────┴──────┐
│ apps/desktop (nomifun-desktop) │ │ apps/web (nomifun-web) │
│ Tauri 2 shell · WebView2/WKWebKit │ │ standalone axum server │
│ ─ thread "nomifun-backend" │ │ serves /api + /ws │
│ └ tokio · nomifun_app embedded │ │ + ServeDir(ui/dist) SPA │
│ picks free localhost port, │ │ port 8787 (default) │
│ injects window.__backendPort │ │ authenticated by default│
│ injects window.__nomiLocalTrust │ │ --insecure-no-auth opts │
│ AuthPolicy::TrustLocalToken │ │ into no-auth mode │
│ Tauri command: check_for_updates │ │ serves SPA as fallback │
└────────────────────────────────────┘ └──────────────────────────┘
│ │
▼ ▼
┌─────────────────────────────────────┐
│ nomifun-app (binary nomicore) │
│ composition root · axum router │
│ bootstrap → data layer → services │
│ /api · /ws · Routes from 29 crates │
└─────────────────────────────────────┘
│ │
▼ ▼
┌─────────────────────┐ ┌─────────────────────┐
│ nomifun-* (29) │ │ nomi-* (15) │
│ backend crates │◀─▶│ agent engine crates │
│ data, auth, MCP, │ │ via the SEAM: │
│ conversation, etc. │ │ nomifun-ai-agent │
└─────────────────────┘ └─────────────────────┘
├─▶ SQLite (sqlx) see data-and-storage.md
├─▶ ACP agent CLIs see agent-engine.md
├─▶ MCP stdio bridges see communication.md
└─▶ bundled bun runtime see data-and-storage.md
```
## 一次请求的流转
一个典型的用户消息 ——“向会话 X 中的 Claude agent 发送一条聊天” —— 会穿过图中的每一层。下方追踪过程列出了真实参与的类型与文件。
```
1. UI keypress → React handler
ui/src/renderer/pages/conversation/...
calls ipcBridge.conversation.sendMessage.invoke(...)
(a thin wrapper produced by the adapter factory in ui/src/common/adapter)
2. httpBridge → fetch
ui/src/common/adapter/httpBridge.ts
POST http://127.0.0.1:<port>/api/conversations/{id}/messages
In WebUI mode, the CSRF cookie is echoed into x-csrf-token (double-submit).
3. axum router (composition root)
crates/backend/nomifun-app/src/router/ — assembled in create_router()
middlewares: trace, body-limit, CORS, auth, CSRF, rate-limit, response wrapper
4. Conversation service
crates/backend/nomifun-conversation/src/service.rs
persists the message, looks up the conversation's bound agent
5. Agent seam
crates/backend/nomifun-ai-agent — the only backend crate that sees nomi-*
AgentRegistry / WorkerTaskManager dispatches to the right agent kind
6. Agent run
nomi-agent drives the engine: providers (anthropic/openai/bedrock/vertex),
tools (bash/read/write/...), MCP servers, skills, plan/confirm/output sinks
For ACP-protocol agents (Claude Code, Codex, Gemini CLI, ...), the backend
speaks ACP over stdio to a child process spawned with the bundled runtime
7. Streaming back to the UI
nomifun-realtime broadcasts each token as a WS event over /ws
ui/src/common/adapter/httpBridge.ts ensureWs() routes events to listeners
8. UI renders the streaming reply (react-markdown + KaTeX + mermaid)
```
## 三大 crate 分组
Cargo 工作区(根 [`Cargo.toml`](../../Cargo.toml)`resolver = "3"``edition = "2024"`)按三个文件夹分组,使边界不仅在包名中可见,在磁盘上也可见:
| 目录 | 用途 | Crate 前缀 | 数量 |
| --- | --- | --- | --- |
| `crates/agent/` | AI 引擎 —— providers、tools、sessions、MCP、skills、browser/computer-use | `nomi-*` | 15 |
| `crates/backend/` | HTTP/WS 服务器、数据、认证、各项功能 | `nomifun-*` | 29 |
| `crates/shared/` | 真正跨层共享工具 | mixed | 2 |
agent 分组是**基本自包含的** —— `nomi-*` crate 不引用 `nomifun-*` crate、工作区根目录或 Tauri / sqlx / axum 等后端框架。反向依赖默认通过 `nomifun-ai-agent` 这条接缝汇集,它再导出 `nomi_config``nomi_types``RequirementSink`。当前 `nomifun-app``nomifun-gateway` 为 browser/computer-use bridge 存在 feature-gated 直接依赖例外;新增例外必须有明确 feature gate 和文档说明。
## 各部分的位置
```
nomifun-tauri/
├─ apps/
│ ├─ desktop/ nomifun-desktop (Tauri 2 shell, this is "NomiFun" the product)
│ └─ web/ nomifun-web (standalone server: /api + SPA on one port)
├─ crates/
│ ├─ agent/ 15 nomi-* crates → see agent-engine.md
│ ├─ backend/ 29 nomifun-* crates → see backend-crates.md
│ └─ shared/ 2 shared crates
├─ ui/ React 19 + Vite 6 + Arco + UnoCSS → see frontend.md
└─ docs/
├─ architecture/ (this folder)
└─ specs/ dated engineering design specs
```
## 品牌与标识
- **NomiFun** —— 桌面产品和项目 / 品牌字标(驼峰式书写,N 与 F 大写)。在散文中使用此写法。
- 小写的 `nomifun` 仅保留给技术标识符 —— npm/JS 包 id、Rust crate 前缀 `nomifun-*`、Tauri bundle 标识符 `com.nomifun.desktop`、环境变量 `NOMIFUN_*`,以及仓库 / 目录名。
## 宿主一览
| 维度 | 桌面(`nomifun-desktop` | Web`nomifun-web` |
| --- | --- | --- |
| 二进制 | `nomifun-desktop`Tauri 外壳) | `nomifun-web`axum 服务器) |
| 后端 | 进程内嵌入(独立线程 + tokio runtime | 进程内嵌入 |
| 认证模式 | `TrustLocalToken`:仅信任带本次启动 secret 的 WebView 请求 | 默认要求认证;可通过 `--insecure-no-auth` 关闭 |
| 端口 | 启动时选取的空闲 localhost 端口(`bind 127.0.0.1:0` | `127.0.0.1:8787`(可通过 `--host`/`--port` 配置) |
| 后端端口如何送达 SPA | 初始化脚本 `window.__backendPort = <p>` | 同源(`/api``/ws` 与 SPA 在同一端口提供) |
| 静态 SPA | 打包进 Tauri 应用(`tauri.conf.json` 的 distDir | 由 `tower_http::services::ServeDir``ui/dist` 提供 |
| 操作系统外壳特性 | 窗口控制、深链接、自动更新、开机启动、对话框、通知、单实例 | 无 —— 浏览器即宿主 |
| Tauri 命令 | 更新检查、WebUI 状态/启停、companion 同步、keep-awake、托盘标签等桌面能力 | 不适用 |
桌面二进制的 `main.rs`[`apps/desktop/src/main.rs`](../../apps/desktop/src/main.rs))有意保持精简 —— 大部分逻辑都在 `nomifun_app::run_embedded_server` 中。Web 二进制([`apps/web/src/main.rs`](../../apps/web/src/main.rs))复用同样的引导辅助函数(`init_environment``init_data_layer``AppServices::from_config``create_router`),并补充了 SPA 回退以及首次运行管理员预置(`ensure_admin_credentials`)。