Files
DevFlow/docs/05-代码审查/架构审查-2026-06-15.md
绝尘 998a2f243d 文档: 架构方案文档(意图识别论证+多主题愿景/论证+文档物理分类+边界清晰化)
squash合并:
- 意图识别层论证(8维度+10业界佐证)
- 多主题上下文管理愿景+并存论证+补充论证(多轮agentic)
- 架构设计文档物理分类(四子目录+INDEX+命名规范+引用同步+边界清晰化)
- 前端架构技术债清单归档
2026-06-19 15:04:04 +08:00

148 lines
16 KiB
Markdown
Raw Permalink 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.
# 架构审查报告2026-06-15
> 范围Rust workspace8 crate + src-tauri 汇聚层14586 行)+ 前端Vue3+Pinia11830 行),共约 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 间零横向依赖**——业务域 crateideas/project不依赖持久化层storage靠 src-tauri 在 IPC 层组合。
### 1.2 前端分层地图
| 层 | 关键文件 | 职责 | 备注 |
|----|---------|------|------|
| stores | project(337) / ai(84) / knowledge(157) / appSettings(139) / settings(99) | 领域状态 | project 是上帝 storesettings 死代码 |
| 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 eventemit/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` 完全不走 storeprovider 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-catchSettings/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_modelno-op`df-ideas/promotion.rs:67` SemiAuto TODO。coordinator 有明确归属保留conditions/router 若长期无调用方依赖其"未来能力"可删减负YAGNI。决策性。
**⑭ [AiSession] 单例 + generating bool 锁死多会话并发** — `state.rs:164` `ai_session: Arc<Mutex<AiSession>>`(全局唯一),`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参数统一 camelCaseTauri 自动转)。
---
## §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 对称,路由全懒加载带 metaapi 层 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 路线) |