02 / Architecture Audit

分层不等于
已经解耦

React、TypeScript、Rust、文件与模型形成了清晰的技术层,但共享目录、协议和超大模块仍制造真实耦合。架构审计要看变更如何传播,而不是只看目录图。

前端React + Zustand
桌面壳Tauri 2
能力层Rust Commands
数据主权Local Files First

总体分层

01 · Product UIReact 19、三栏工作台、图谱、搜索、编辑器、活动面板、设置与国际化。
02 · OrchestrationTypeScript 负责摄入工作流、状态协调、提示词、队列、研究、Lint 与页面合并。
03 · Native CoreRust 负责文件解析、LanceDB、Agent runtime、本地 HTTP 服务、文件监听和系统集成。
04 · Project Dataraw/、wiki/、.llm-wiki/、agent-workspace/;知识与运行状态各有边界。
05 · ExternalOpenAI 兼容模型、Anthropic、Azure、Web Search、MinerU、AnyTXT、Claude/Codex CLI。

这是典型的“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.tsruntime.rstools.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.rsAgent 规划、工具循环、事件流、取消、引用和最终生成reqwest、tokio
api_server.rs19828 JSON/SSE API、鉴权、限流、项目绑定tiny_http
clip_server.rs19827 浏览器剪藏入口与待摄入队列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”只能提升可检查性,不能保证一致性。

一次启动发生了什么

  1. 前端应用主题与平台样式先加载,避免闪屏。
  2. Rust 注册 Tauri 插件、代理、Agent/CLI/FileSync 状态。
  3. 19827 Clipper 与 19828 API 后台线程启动,托盘随后初始化。
  4. React 恢复全局配置与最近项目,打开项目并重建文件树。
  5. 项目级聊天、审核、Lint、自动导入、Source Watch 和 API 项目注册被恢复。
  6. 界面进入稳定工作态,后台再延迟执行版本更新检查。
构建注意

当前 Cargo.toml 明确设置 rust-version = "1.88",因为 AnyDoc 及相关依赖需要该版本;README 中“Rust 1.70+”已落后。

Next Chapter03 · 写入链路如何避免半成功
案例证据:src/main.tsxsrc/App.tsxsrc/lib/ingest.tssrc-tauri/src/lib.rssrc-tauri/src/agent/*src-tauri/Cargo.toml。边界替换实验与文件事务要求是本次审计的通用建议。