总体分层
这是典型的“Web 技术做体验、Rust 做本地能力”的桌面架构。Tauri IPC 通过 invoke() 把前端工作流接到原生文件系统和数据库。五层是理解视角,不代表各层可以独立替换;文件约定、事件名、错误语义和缓存格式仍是跨层契约。
前端:工作面与状态模型
src/App.tsx 是启动编排中心:恢复上次项目、模型配置、搜索配置、Embedding、多模态、MinerU、自动导入、聊天、审核、Lint 与缩放等状态。
布局层
app-layout.tsx 组合图标侧栏、知识/文件树、中部内容、右侧预览和活动面板;面板尺寸可调。
状态层
Zustand stores 按领域拆分:wiki、chat、review、research、lint、activity、zoom、update,避免单个全局 Store 膨胀。
阅读与编辑
Milkdown 承担 Markdown 编辑/预览,React Markdown 渲染聊天,KaTeX 与 Mermaid 提供公式和图表。
图谱体验
Sigma.js 显示大图,Graphology 构图与社区分析,ForceAtlas2 负责布局;布局计算可进入 Worker。
状态并非都塞进浏览器存储:全局偏好进 Tauri Store,项目相关聊天、审核和运行数据落入项目的 .llm-wiki/。
TypeScript:语义工作流编排
TypeScript 层最重的文件是 src/lib/ingest.ts。它不只是“发一个请求”,而是维护一套多阶段流水线:读源、缓存判断、图片处理、两阶段模型调用、输出解析、合并、修复、审核、缓存与向量更新。它有提交锁,却不是跨文件事务。
src/lib/ingest.ts摄入状态机、提示词、FILE/REVIEW 协议、合并与修复。src/lib/ingest-queue.ts持久化任务、恢复、暂停、取消、三次重试和用量限制后恢复。src/lib/source-lifecycle.ts导入、文件夹复制、来源删除与 Wiki 级联清理。src/lib/embedding.ts文本切块、批量调用 embedding、向 Rust LanceDB 写入和重建状态。src/lib/wiki-graph.ts构造可视图、计算社区、缓存大图结果。src/lib/deep-research.ts收集网络/本地证据、验证引用、保存研究页并回流摄入。涉及 LLM 提示、页面语义和 UI 状态的逻辑留在 TypeScript;涉及系统权限、重型解析、进程、网络服务与向量存储的能力下沉 Rust。
边界审计:四个主要压力点
| 压力点 | 案例现状 | 通用改进 |
|---|---|---|
| 编排单体 | ingest.ts、runtime.rs、tools.rs 等核心文件达到数千行,策略、状态机与 I/O 交织。 | 拆出纯领域状态机、端口接口与适配器;用契约测试锁定行为后再拆分。 |
| 跨运行时协议 | TypeScript 与 Rust 通过命令名、JSON、事件和磁盘格式协作,编译器无法覆盖全部边界。 | 共享版本化 Schema,生成双方类型;为错误码、取消、幂等和兼容性建立契约测试。 |
| 共享文件状态 | Wiki、缓存、队列、历史和向量索引并非一个事务域,锁主要解决进程内并发。 | 增加 manifest、写前计划、提交日志、恢复扫描和跨进程文件锁,明确每类派生物的重建规则。 |
| 本地服务面 | 桌面进程同时暴露 API、剪藏和 Agent 能力;回环地址降低暴露面,但不是身份边界。 | 默认最小监听、严格 Origin、短期能力令牌、拒绝 URL Token,并对危险工具实施独立授权与审计。 |
不要数目录层级,要做替换实验:换模型、换存储、升级 Schema、让进程崩溃、并发写同一项目时,影响是否被限制在一条明确契约内。
Rust:原生能力与服务边界
src-tauri/src/lib.rs 注册插件、状态、后台服务和全部 Tauri Commands。应用启动时还会拉起两个本地服务,并设置系统托盘、关闭行为、代理和 CLI 子进程注册表。
| 模块 | 职责 | 关键库 |
|---|---|---|
| commands/fs.rs | 文件读写、Office/PDF/电子书解析、路径检查、缓存 | pdfium-render、AnyDoc、docx-rs、calamine |
| commands/vectorstore.rs | 页面块向量增删查、优化和旧表迁移 | LanceDB、Arrow |
| agent/runtime.rs | Agent 规划、工具循环、事件流、取消、引用和最终生成 | reqwest、tokio |
| api_server.rs | 19828 JSON/SSE API、鉴权、限流、项目绑定 | tiny_http |
| clip_server.rs | 19827 浏览器剪藏入口与待摄入队列 | tiny_http |
| commands/file_sync.rs | 监听 raw/sources 外部变化并生成同步任务 | notify、walkdir |
Release 配置选择 panic = "unwind",再在命令边界用 panic guard 把第三方解析器崩溃转成错误,避免单个坏文件杀死整个桌面应用。
项目目录承载领域模型
project/
├── purpose.md # 方向:目标、问题、范围、论点
├── schema.md # 规则:类型、命名、frontmatter、工作流
├── raw/
│ ├── sources/ # 原始资料,只读证据层
│ └── assets/ # 原始附件
├── wiki/
│ ├── index.md # 内容导航
│ ├── overview.md # 全局概要
│ ├── log.md # 演进账本
│ ├── entities/ concepts/ sources/
│ ├── queries/ comparisons/ synthesis/
│ └── media/ # 从来源抽取并配文的图片
├── .llm-wiki/ # 缓存、队列、聊天、历史、技能等内部状态
├── .obsidian/ # 兼容配置
└── agent-workspace/ # Agent 可见生成物
项目模板还能增加研究、阅读、个人成长、商业等领域类型。开放目录提高可读性和退出能力,但它仍然是 Schema:需要版本、迁移、校验、并发规则和恢复策略。Git 可以提供历史,却不会自动解决语义冲突、私密资料进入历史或多进程写入。
生产级文件存储至少需要 manifest 与校验和、Schema 版本、迁移器、快照、恢复日志和损坏扫描。否则“人能打开 Markdown”只能提升可检查性,不能保证一致性。
一次启动发生了什么
- 前端应用主题与平台样式先加载,避免闪屏。
- Rust 注册 Tauri 插件、代理、Agent/CLI/FileSync 状态。
- 19827 Clipper 与 19828 API 后台线程启动,托盘随后初始化。
- React 恢复全局配置与最近项目,打开项目并重建文件树。
- 项目级聊天、审核、Lint、自动导入、Source Watch 和 API 项目注册被恢复。
- 界面进入稳定工作态,后台再延迟执行版本更新检查。
当前 Cargo.toml 明确设置 rust-version = "1.88",因为 AnyDoc 及相关依赖需要该版本;README 中“Rust 1.70+”已落后。
src/main.tsx、src/App.tsx、src/lib/ingest.ts、src-tauri/src/lib.rs、src-tauri/src/agent/*、src-tauri/Cargo.toml。边界替换实验与文件事务要求是本次审计的通用建议。