# 架构审查报告(2026-06-15) > 范围:Rust workspace(8 crate + src-tauri 汇聚层,14586 行)+ 前端(Vue3+Pinia,11830 行),共约 26k 行。 > 方法:2 个 general-purpose 子代理并行审「Rust crate 架构」「前端架构」,主代理直读 df-core/df-execute/coordinator/conditions 核实空壳与抽象层,并对 3 个 🔴 删代码/可见断言做 grep 复验(全部坐实)。 > 去重:与 [全栈代码审查报告-2026-06-14.md](全栈代码审查报告-2026-06-14.md)(bug/性能 FR-*)、[架构与缺陷复核报告-2026-06-14.md](架构与缺陷复核报告-2026-06-14.md)(复核+回归审计)、[aichat审查报告-2026-06-14.md](../02-架构设计/构想审查/aichat审查报告-2026-06-14.md)(AR 系列)互补——**本报告只记架构层面**(模块边界/依赖方向/抽象层次/扩展性/状态管理/技术债),不重复 bug、性能、aichat 专项项。 > 性质:**dry — 仅审查 + 文档,不改代码**。 --- ## §1 架构总览 ### 1.1 Crate 地图(Rust) | Crate | 职责 | 内部依赖 | 行数 | |-------|------|---------|------| | **df-core** | 纯类型定义(ID 别名/状态枚举/new_id)+ error + events | 无(根) | 429 | | **df-storage** | SQLite 持久化(11 个 Repo + models + migrations) | df-core | 2522 | | **df-workflow** | 工作流引擎(DAG/Node trait/Executor/Registry/StateMachine/EventBus/Conditions) | df-core | 1120 | | **df-ai** | AI 编排(LlmProvider trait + OpenAI/Anthropic compat + 工具注册 + 上下文 + 流式) | df-core | 2588 | | **df-nodes** | 3 个内置节点(AiNode/ScriptNode/HumanNode) | df-core + df-workflow + df-ai + df-execute | 773 | | **df-execute** | 仅一个 `shell::execute` 函数(带 TODO) | df-core | 76 | | **df-ideas** | 想法池领域(capture/scoring/adversarial 启发式/promotion 占位) | df-core | 759 | | **df-project** | 项目领域(create_from_idea / scan 技术栈探测) | df-core | 486 | | **src-tauri** | 汇聚层:47 个 IPC 命令 + AppState + 跨 crate 业务编排 | 全部 8 crate | 5711 | **依赖图(无环,星型收敛)**: ``` df-core (零依赖根) / / | \ \ \ storage workflow ai ideas project execute | | | └───┴── nodes (唯一多依赖者) | src-tauri (依赖全部 8 crate) ``` 除 df-nodes 依赖 df-workflow+df-ai 外,**6 个 crate 间零横向依赖**——业务域 crate(ideas/project)不依赖持久化层(storage),靠 src-tauri 在 IPC 层组合。 ### 1.2 前端分层地图 | 层 | 关键文件 | 职责 | 备注 | |----|---------|------|------| | stores | project(337) / ai(84) / knowledge(157) / appSettings(139) / settings(99) | 领域状态 | project 是上帝 store;settings 死代码 | | composables/ai | 6 文件(events/stream/send/conversations/window/panel) | AI 业务逻辑 | 为拆而拆,循环依赖 | | views | 9 个(4875 行) | 页面 | Ideas 929 / Settings 1032 偏重 | | components | AiChat(1359) / ToolCard(760) 等 | UI 组件 | AiChat 巨型 | | api | 8 文件(736 行) | Tauri invoke 封装 | 分层清晰 | --- ## §2 架构级问题清单 ### 🔴 结构风险(6) #### ① [df-execute] 空壳 crate + src-tauri 死依赖 — 应合并删除 **现状**:`crates/df-execute/src/lib.rs:7` 仅 `pub mod shell;`;`shell.rs` 全文 73 行只 1 个 `execute` 函数且带 `TODO: 完整实现`(:33)。全仓库唯一调用方是 `df-nodes/src/script_node.rs:34,42`。`src-tauri/Cargo.toml:32` 声明 `df-execute` 依赖,但 grep 确认 `src-tauri/src/` **零处引用 `df_execute`**(已复验)。 **架构影响**:一个 76 行/2 文件、仅暴露 1 函数的 crate,是过度拆分典型。其存在理由("执行运行时",原含 docker/git_ops/ssh)重构删除后已不成立。src-tauri 那条依赖是残留死代码。 **建议**:合并 df-execute 进 df-nodes(作 `script_node` 内部模块或 `df-nodes::shell`),删该 crate + `src-tauri/Cargo.toml:32` 死依赖。净减 1 crate + 1 死依赖。 #### ② [stores/settings.ts] mock 上帝 store 死代码 — 零消费者 **现状**:`stores/settings.ts:32-75` 持硬编码 mock 数据(Anthropic/Zhipu/DeepSeek provider、5 连接、theme/dataDir),全写死展示字符串。grep 确认仅 `stores/index.ts:6` 导出,**全 src 零组件 import `useSettingsStore`**(已复验)。真实 provider CRUD 走 `api/ai.ts` + Settings.vue 本地 ref,真实偏好走 appSettings。 **架构影响**:双轨幻觉——同名 `settings` 一真(appSettings/SQLite)一假(settings.ts/mock),后者 `AIProvider`/`Connection` interface 还被 re-export,新人易误用。与「做减法」画像直接冲突。 **建议**:删 `stores/settings.ts` + `stores/index.ts:6-7` 导出;interface 若有外部依赖迁 `api/types.ts`。零风险减法。 #### ③ [stores/project.ts] 上帝 store(四领域强耦合 + 越层 invoke) **现状**:`project.ts` 同时管 projects/tasks/ideas/workflowExecutions/liveEvents/pendingApproval 六类 state(:9-25),方法覆盖四领域 CRUD + 事件监听 + 审批。`project.ts:252` `invoke('approve_human_approval')`、`:271` `invoke('cancel_workflow_node')` **直接调 invoke 绕过 api 层**——全项目仅此一处 store 越层(其余 50+ invoke 全在 `api/*.ts`)。 **架构影响**:违反单一职责;workflow 审批逻辑寄生 project store;分层不彻底。 **建议**:拆 project/task/idea/workflow 四 store(至少 workflow+审批独立);`approve_human_approval`/`cancel_workflow_node` 沉 `api/workflow.ts`。 #### ④ [composables/ai/] 6 文件为拆而拆 — events↔stream 循环依赖 **现状**:6 个 composable 全部 `import { state } from stores/ai`(共享单例),且互相模块级 import 形成环:`useAiEvents.ts:18` → useAiStream(reset);`useAiStream.ts:12` → useAiEvents(nextMsgId) ← **双向**;`useAiSend`→useAiStream+useAiEvents;`useAiConversations`→useAiEvents+useAiPanel;`useAiWindow`→其余三者。`useAiStore()` 只是 `...useAiXxx()` 展开,每个 composable 恰被一处展开、零外部复用。 **架构影响**:拆分未降耦合,反把一个内聚状态机(流式事件→看门狗→队列→对话)打散到 6 文件,靠 ES module 循环 import 维持。events↔stream 双向依赖靠函数提升侥幸可运行,属脆弱结构。 **建议**:要么承认是单 store 内聚逻辑、合回 `stores/ai.ts`(最简);要么把 `nextMsgId`/`state` 等共享提独立 `aiShared.ts` 破环。 #### ⑤ [App.vue:273] `/decisions` 路由死链 — 用户可见坏链 **现状**:`App.vue:273` secondaryNav 含 `{ path: '/decisions', label: 'nav.decisions' }`,但 `router/index.ts` 无此路由(已复验,仅 i18n 文案存在)。点击落空白/重定向。 **架构影响**:导航与路由不同步,用户可感。 **建议**:补路由+视图,或从 secondaryNav 删该项(务实减法)。 #### ⑥ [前端] 模块级全局态散落 + 多窗口 localStorage 手搓同步 **现状**:跨组件状态大量用模块级 `let`/`const` 而非 store——`useAiEvents.ts:27-32` 四个、`useAiStream.ts:18` watchdog、`useAiWindow.ts:17-18`、`useAiConversations.ts:44` switchId、`stores/project.ts:27,329`。多窗口(主窗口+ai-detached)各独立 JS realm,模块级单例**不跨窗口共享**;`useAiWindow.ts:30-31` 用 `localStorage.setItem('df-ai-gen'/'df-ai-text')` 手动快照,分离窗口 `resumeInDetached`(:82-95) 读回——手搓跨窗口 IPC,仅覆盖 currentText 两字段。 **架构影响**:模块级 state 单窗口内是隐式全局,调试难追踪;「双窗口同时操作同一对话」竞态靠 localStorage 单字段兜不住。 **建议**:跨窗口状态显式走 Tauri event(emit/listen)而非 localStorage 快照;模块级私有态集中文档化。 ### 🟡 改进(6) #### ⑦ [df-core] 名不副实——类型库而非核心 `lib.rs:5` 自述"核心类型定义",但 `types.rs` 全是 `pub type XId = String` 别名 + 状态枚举 + `new_id()`,**无 trait/无抽象/无行为契约**。真正核心 trait(`Node` 在 df-workflow、`LlmProvider` 在 df-ai)各自下沉到功能 crate。命名误导读者对其角色预期。**建议**:改名 `df-types`,或注释明确"只放跨 crate 共享数据类型,不放 trait"。低优先。 #### ⑧ [src-tauri] IPC 层成事实业务编排层(5711 行) crate 故意解耦(ideas 不依赖 project/storage),代价是「两 crate 怎么协作」无处安放,全部上浮 src-tauri。典型 `commands/idea.rs:96-161` `promote_idea`:取 IdeaRecord → 调 df_project 构造 Project → 插 df_storage → 补偿删除 → 回写 ideas,其中 `record_to_idea`(:252)、分数 `*10` 缩放(:224)、JSON 组装(:208) 全在 IPC 文件。`df-ideas/promotion.rs:82` 的 `do_promote` 反而是空壳——领域逻辑被分置两处,领域 crate 残缺。src-tauri(5711) > 最大 crate(df-ai 2588),IPC 不是薄转发而是应用服务层。**建议**:中期引入 `df-app` 或 src-tauri 内 `services/` 收编跨 crate 编排,让 IPC 回归薄转发;至少把 `record_to_idea` 这类纯映射下沉回 df-ideas。技术债,不紧急。 #### ⑨ [src-tauri/commands/ai] AI agent loop 在 IPC 层而非 df-ai `commands/ai/` 11 文件 3438 行,含 `agentic.rs`(292) ReAct 主循环、`audit.rs`(342) 工具审计、`knowledge_inject.rs`(557) 提炼。df-ai 提供 trait + provider 实现,但「怎么编排多轮工具调用」这层智能在 src-tauri,使 df-ai 退化为「LLM 调用 SDK」。与 aichat 审查「单链 ReAct、coordinator 空壳」一致——智能层无处安放暂栖 IPC。**建议**:观察项,B 路线(多 agent 协作)立项时一并从 IPC 抽出,现不宜妄动。 #### ⑩ [views] 3 个 view 绕 store 调 api `Projects.vue:160,163,187`(scanStack/checkBinding/scanWithAi)+ `ProjectDetail.vue:211` 混用 store 与 api;`Settings.vue:374,387` 完全不走 store(provider CRUD 直接 aiApi + 本地 ref),自带 toast(:390) 与 App.vue 全局 toast 重复。分层不一致——同性质「读后端数据」有的进 store 有的留 view 本地。**建议**:scan/checkBinding 预览型只读可接受;Settings provider 列表应进 store(与 `stores/ai.ts:39` providers 合一,避免双份真源);toast 二选一。 #### ⑪ [stores/index.ts] barrel 漏导 useAiStore `stores/index.ts` 导出 project/knowledge/settings/appSettings,**漏 `useAiStore`**(`stores/ai.ts:73`)。故 App.vue/AiChat.vue/AiDetached.vue 全部直连 `stores/ai` 文件绕过 barrel。barrel 形同虚设,ai store 成「特殊公民」。**建议**:补 `export { useAiStore } from './ai'`。 #### ⑫ [api/types.ts] 前后端类型契约手写 + `as any` 绕过 `api/types.ts:1` 注释"与 Rust Record 严格对齐"但纯手写无代码生成。漂移风险点:`stores/project.ts:234` `payload.event as unknown as HumanApprovalRequest`(字段存在性无校验);`:73,145,182` `(state.xxx[idx] as any)[field]=value`(field 是 string 无约束);`useAiConversations.ts:71` 历史消息全程 `(m:any)`、`:81` JSON.parse 无 try-catch;Settings/ToolCard 多处 `as any`。契约脆弱集中在「动态字段更新」和「事件 payload 强转」。**建议**:动态 field 改 keyof 约束或具名方法;payload 强转改类型守卫;长期考虑 ts-rs 代码生成。 ### ⚪ 观察(4) **⑬ [df-ai/coordinator.rs:3 等] 空壳是 B 路线有意占位** — `coordinator.rs:3` 注释 `⚠ B 路线占位...勿删`;`df-workflow/conditions.rs:4` `TODO: 实现完整条件表达式`(当前只识 true/false 字面量,保守拒绝=安全);`df-ai/router.rs:42` ModelRouter 全返 default_model(no-op);`df-ideas/promotion.rs:67` SemiAuto TODO。coordinator 有明确归属保留;conditions/router 若长期无调用方依赖其"未来能力",可删减负(YAGNI)。决策性。 **⑭ [AiSession] 单例 + generating bool 锁死多会话并发** — `state.rs:164` `ai_session: Arc>`(全局唯一),`commands.rs:45,48` `generating` bool 充当全局对话互斥锁,`state.rs:93-96` 注释自承认"per_conv 是应用级单一信号量非 map,未来多对话并发需改 HashMap"。同一时刻全局只能有一个 AI 对话生成。是有意识的设计取舍,符合单链 ReAct 现状,但是异步审批/多会话(见 memory devflow-async-approval-concept)的结构前置障碍。B 路线范畴,现不阻塞。 **⑮ [AppState] God Struct** — `state.rs:135-185` 含 15 个 Repo + event_bus + registry + ai_tools + ai_session + knowledge_config + llm_concurrency + workflow_state_registry。所有 IPC 共享巨型 State,无边界隔离。桌面应用规模可接受,若 IPC 做 service 化(⑧)State 可随之分组。 **⑯ [前端] IPC 命名风格不统一** — 命令名 snake_case(`list_projects`)/`ai_`前缀(`ai_chat_send`)混存;参数 camelCase(`convId`)vs snake(`projectId`)混。新增 IPC 需改 api+store+view 约 3 处,散弹度可接受。**建议**:命令名统一 snake_case,参数统一 camelCase(Tauri 自动转)。 --- ## §3 亮点 - **扩展点单点注册,抽象得当**:新增工作流节点改 2 处(df-nodes 加 impl Node + `state.rs:225 build_registry` 注册一行);新增 AI 工具改 1 处(`tool_registry.rs` register);新增 provider 改 1 处(`df-ai/lib.rs:21 build_provider` match)。Registry/工厂用最朴素 HashMap,没搞插件动态加载那套过度抽象。**架构最健康的一面**。 - **依赖图无环、星型收敛**:crate 间零横向耦合,编译/测试隔离好,6 crate 不互相牵连。 - **空壳占位有显式标注**:coordinator 的 B 路线注释、conditions 的保守拒绝设计,非失控,是有意识的路线占位。 - **前端减法实践**:`useConfirm`(抽 4 视图重复)、`constants/project`(统一状态映射)、`utils/time`(根治 Invalid Date)——好的减法。 - **i18n/路由/api 分层清晰**:i18n 按模块分文件 zh/en 对称,路由全懒加载带 meta,api 层 8 文件按领域分 + barrel 统一导出(除 ③ project store 越层、⑪ ai store 漏导两处瑕疵)。 - **工程素养细节**:LlmConcurrency 双层 Semaphore、promote_idea 补偿事务删除、FR-S7/S8 安全防护——显工程功底。 --- ## §4 总评 **架构成熟度:中等偏清晰(Rust 7/10,前端 6/10)**。 **核心张力(1 个真问题 + 几个取舍)**: - **真问题**:df-execute 空壳化 + src-tauri 死依赖(①),应做减法合并。这是唯一违背「做减法」画像的硬伤。 - **核心张力**:crate 刻意解耦 → 跨 crate 编排全部上浮 src-tauri → IPC 层 5711 行成事实业务层(⑧⑨)。这是「高内聚低耦合」的代价,当前规模可忍,是中期技术债。 - **前端回潮**:AI 模块过度拆分(④)、project 上帝 store(③)、settings 死代码(②)三处结构性债。 **务实画像契合度**:整体偏「做减法、反过度抽象」——没为插件化搞动态加载、没给 df-core 塞虚 trait、Registry 用朴素 HashMap。违背处集中在 df-execute 存在(应删)和 src-tauri 膨胀(编排无处去)+ 前端 AI 模块拆分。 **行动建议(按 ROI)**: | 优先级 | 项 | 动作 | 成本 | |--------|-----|------|------| | 立即 | ② | 删 stores/settings.ts 死代码 | 零风险 | | 立即 | ⑤ | /decisions 死链(补路由 or 删 nav) | 零风险 | | 短期 | ① | df-execute 合并进 df-nodes + 删死依赖 | 低(1 crate+1 依赖) | | 短期 | ⑪ | stores/index.ts 补 useAiStore 导出 | 极低 | | 中期 | ③ | project.ts 拆分 + 审批沉 api 层 | 中 | | 中期 | ④ | ai composables 合回 or 破环 | 中 | | B 路线 | ⑧⑨⑭ | IPC 编排层抽取 / agent loop 下沉 / AiSession 多会话 | 大(随 B 路线) |