squash合并: - 意图识别层论证(8维度+10业界佐证) - 多主题上下文管理愿景+并存论证+补充论证(多轮agentic) - 架构设计文档物理分类(四子目录+INDEX+命名规范+引用同步+边界清晰化) - 前端架构技术债清单归档
148 lines
16 KiB
Markdown
148 lines
16 KiB
Markdown
# 架构审查报告(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<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,参数统一 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 路线) |
|