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

16 KiB
Raw Blame History

架构审查报告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.mdbug/性能 FR-*)、架构与缺陷复核报告-2026-06-14.md(复核+回归审计)、aichat审查报告-2026-06-14.mdAR 系列)互补——本报告只记架构层面(模块边界/依赖方向/抽象层次/扩展性/状态管理/技术债),不重复 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:7pub mod shell;shell.rs 全文 73 行只 1 个 execute 函数且带 TODO: 完整实现:33。全仓库唯一调用方是 df-nodes/src/script_node.rs:34,42src-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-nodesscript_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_nodeapi/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+useAiEventsuseAiConversations→useAiEvents+useAiPaneluseAiWindow→其余三者。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-18useAiConversations.ts:44 switchId、stores/project.ts:27,329。多窗口(主窗口+ai-detached各独立 JS realm模块级单例不跨窗口共享useAiWindow.ts:30-31localStorage.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/无抽象/无行为契约。真正核心 traitNode 在 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:82do_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,187scanStack/checkBinding/scanWithAi+ ProjectDetail.vue:211 混用 store 与 apiSettings.vue:374,387 完全不走 storeprovider CRUD 直接 aiApi + 本地 ref自带 toast(:390) 与 App.vue 全局 toast 重复。分层不一致——同性质「读后端数据」有的进 store 有的留 view 本地。建议scan/checkBinding 预览型只读可接受Settings provider 列表应进 storestores/ai.ts:39 providers 合一避免双份真源toast 二选一。

⑪ [stores/index.ts] barrel 漏导 useAiStore

stores/index.ts 导出 project/knowledge/settings/appSettingsuseAiStorestores/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]=valuefield 是 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-opdf-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 Structstate.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_caselist_projects/ai_前缀(ai_chat_send)混存;参数 camelCaseconvIdvs snakeprojectId)混。新增 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 路线)