Files
DevFlow/docs/02-架构设计/功能决策记录.md
绝尘 cf017f81e2 新增: Phase2 阶段收尾(Sprint 1-20)
重构:删 5 零引用 crate(df-evolve/plugin/stages/task/traceability)+ 清死模块、ai.rs 拆 11 子 module、ai.ts 拆 6 composable、i18n 拆目录
功能:知识库全栈(df-project/scan + CRUD + 时间线 + 前端)、Settings 拆分、appSettings KV 迁移、模型池、LLM 并发 Semaphore
修复:审批持久化根治、ConditionEngine 默认拒绝、NodeRegistry unimplemented 清除、promote 补偿删除、工具结果截断 50KB、路径校验防 symlink 逃逸
文档:B-03 人工审批设计、决策记录三分档、规格契约自检、经验记录、todo 看板、PROGRESS 更新

详见 PROGRESS.md。src-tauri/儿童每日打卡应用/ 与本项目无关,已排除。
2026-06-14 14:08:20 +08:00

473 lines
66 KiB
Markdown
Raw 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.
# 功能决策记录
> 日常开发中对各功能做的**需求规格 + 设计决策规格**(功能粒度,补 [Phase 1 架构决策](./Phase1架构决策.md) 之下的实现层选择)。聚焦「要做什么 / 为什么这么定」,便于日后回溯。
>
> 创建2026-06-12 | 范围Sprint 510 | 维护:随开发追加
## 约定
**判断标准**3 个月后回看,这条是否仍影响对系统/功能设计的理解?是 → 留本文档;否 → 分流。
**本文档只记两类**
- **设计决策规格**(✅ 已落地 / 🚧 待实测 / 📐 设计未实施)——「为什么这么定」。三要素:决策 → 原因/取舍 → 状态。来源标 `[Sprint N]``[日期]`
- **需求规格 / 待办**(📋)——「要做什么 / 为什么需要」。与 PROGRESS 流水区分:这里记「要做什么 / 为什么需要」PROGRESS 记「做了啥」。
**经验性内容**(踩坑 / 约定 / 技巧 / bug 排查教训)→ [经验记录.md](./经验记录.md)。
**老条 / 纯流水 / UX 微调 / 已被取代** → [功能决策记录-归档.md](./功能决策记录-归档.md)。
记录规则见 [文档记录规范](./文档记录规范.md)。
## 人机协同设计基准
### AI coding 下的「过度设计」判断基准 [2026-06-13]
- **决策**:项目为一人开发 + 全程 AI coding人定方向/审查AI 实现),代码被 AI 反复读写。权衡设计时采用新基准——**AI 反复读写的代码,「结构清晰」和「隐性耦合显式化」权重高于传统判断**;但「过度抽象」(多层 trait/Builder/工厂)仍不做,因 AI 读简单直白代码 > 读层层抽象。
- **原因/取舍**:① AI 每次理解代码靠注释和结构比人更依赖1600 行单文件(如 AiChat.vue消耗大量 context拆分反而降低 AI 理解成本;② 隐性依赖(如分离窗口 localStorage 跨 webview人凭经验避开、AI 易踩坑。故:组件拆分从「不做」升为「值得做」;隐性耦合早修或显式标注。③ 规模没到的优化list_all LIMIT、白名单拆表仍按数据量客观判断不因 AI coding 而变。
- **状态**:📐 基准原则(指导后续取舍)
## AI Chat 可靠性
### 流式可靠性三重保险 [Sprint 6 + 2026-06-13]
- **决策**:① reqwest Client 加 `connect_timeout(30s)`**不设请求总 timeout**;② `stream_llm``tokio::time::timeout(120s)` 包**单个** `stream.next()`idle 间隔),不包整个流;③ 前端 stores/ai.ts 加 streaming watchdog——发送启动 60s 计时,收到 AiTextDelta/工具事件/审批结果重置AiApprovalRequired 暂停审批等待不计AiCompleted/AiError 清除60s 无活跃事件则置 streaming=false + push 错误「响应中断」。
- **原因/取舍**:连接阶段防无限 hang总 timeout 会误砍流式长生成任务流式可持续数分钟。idle 120s 防「连上后中途静默」无限 hang只卡单 chunk 间隔,不卡总时长。前端 60s 独立计时(短于后端 120s双保险——后端任何路径漏发收尾崩溃/事件丢失/agent loop 异常退出)前端永久 streaming=true 卡死。审批等待暂停(不计超时)避免误判用户思考。
- **边界**审批组件未渲染见「需求与待办·审批可见性缺口」AiApprovalRequired 暂停后永久卡watchdog 救不了,需审批可见性兜底。
- **状态**:✅ Sprint 6 + 2026-06-13
### 断连丢弃残缺响应
- **决策**:维护 `finished_received` 标志;流尽未收到 finished 信号 → emit AiError 并**丢弃残缺响应,不当完整入库**。
- **原因**:防脏历史污染对话记录(半截回复入库后无法续接)。
- **状态**:✅ Sprint 6
### 停止生成:保留已生成文本
- **决策**`AiSession.stop_flag: Arc<AtomicBool>` + 多检查点响应;停止后**保留已生成文本**。
- **原因**:用户主动停止 ≠ 丢弃成果已输出内容有价值。idle无输出时最多等 120sP2 可用 `tokio::sync::Notify` 优化为绝对即时)。
- **状态**:✅ Sprint 6运行时待实测
### 切对话:从「拒绝切换」演进到「不中断路由」
- **决策**Sprint 6 生成中**拒绝切换**(防 `active_conversation_id` 被改致旧 loop 串台写库)→ Sprint 8 改为**后台对话按 `conversation_id` 路由,生成中可切换不打断**。
- **原因**:拒绝切换体验差;后端给所有 event 加 `conversation_id` + spawn 前快照 conv_id + 前端按 id 路由(后台对话事件不污染当前视图),既不串台又不打断。
- **状态**:✅ Sprint 8部分场景待实测
## AI Chat 工具调用与审批
### Agentic Loop 最多 10 轮
- **决策**`run_agentic_loop` 上限 10 轮(`MAX_AGENT_ITERATIONS`)。
- **原因**:防失控循环;单链 ReAct 10 轮覆盖绝大多数任务。超出需规划式B 路线)。
- **状态**:✅ Sprint 5
### 风险门控Low 自动 / Medium+High 审批
- **决策**:工具按 `RiskLevel` 分级Low 自动执行Medium/High 暂停等人工审批。
- **原因**:读操作放行,写/删操作把关——可靠性 vs 效率的平衡点。
- **状态**:✅ Sprint 5
### `tool_calls` 按 index 排序
- **决策**assistant 消息与 tool_result 两处均按 `index` 排序。
- **原因**:消除 HashMap 迭代乱序致多工具结果错位。
- **状态**:✅ Sprint 6
### 路径校验:拒 `..` 遍历 + 扩敏感目录
- **决策**:正斜杠→反斜杠规范化 + 拒 `..` 路径遍历 + 扩 `.aws`/`.gnupg`
- **原因**最小加固防越权读写根治级workspace 白名单 + canonicalize待边界明确后再做。
- **状态**:✅ Sprint 6边界加固待续
### list_directory 递归防爆:噪音目录剪枝 + 条目上限 + skip_noise_dirs 开关 [2026-06-14]
- **决策**`list_dir_recursive` 递归时跳过噪音目录(`.git`/`node_modules`/`target`/`dist`/`build`/`.next`/`.cache`/`__pycache__`/`.venv`/`venv`/`.idea`)——**列出但不深入内部**;硬上限 1000 条 + `truncated` 标志;默认 `max_depth` 3→2`skip_noise_dirs` 参数(默认 `true`)。
- **原因**AI 广扫项目根传 `recursive:true` 时,`.git`/`node_modules`/`target` 铺平致 13782 项塞进对话 messageUI 卡 + token 爆)。剪枝防爆炸,但保留访问能力:① 噪音目录仍列出(看得见存在 + 大小);② 想看内部时 `list_directory` 直接指向该目录depth=0 起算),或传 `skip_noise_dirs:false` 强制递归进去(仍受 1000 上限 + truncated 保护,适合看编译产物 dist / 运行结果 target 做比对。1000 上限 + truncated 让"想全扫"退化为"分层定点查",不丢信息。
- **状态**:✅ 2026-06-14 落地cargo check 通过)
### `max_tokens` 8192 + `length` 算 finished
- **决策**max_tokens 4096→8192`finish_reason="length"`(截断)纳入 finished。
- **原因**:大任务输出撞 4096 上限被误判断连、丢弃整段响应8192 贴合实际,截断视为正常完成。
- **状态**:✅ Sprint 6
### 审批:删全屏 Modal 保留行内卡片 [Sprint 8]
- **决策**:移除全屏 Tool Approval Modal保留工具卡片内联审批按钮。
- **原因**:全屏 Modal 打断对话流,行内审批更轻量。
- **状态**:✅ Sprint 8
## AI Chat Provider 协议
### 按 `provider_type` 路由 OpenAI / Anthropic
- **决策**:新增 `anthropic_compat.rs` 实现 Anthropic Messages API`provider_type` 分发到 OpenAICompat 或 AnthropicCompat分发处用 `Box<dyn LlmProvider>` trait object。
- **原因**GLM 等订阅端点走 Anthropic 协议(`x-api-key` + 顶层 `system` + 必填 `max_tokens` + SSE content_block统一 Provider trait 屏蔽差异,上层 Agentic Loop / AiNode 零改动(不感知协议)。
- **状态**:✅ Sprint 8用户实测对话流式 OK
### 端点 URL 三段智能拼接
- **决策**`messages_url()` 按 base_url 末段判断——已含 `/v1/messages` 直用;以 `/v1` 结尾补 `/messages`;仅域名(如 `…/api/anthropic``api.anthropic.com`)补 `/v1/messages`
- **原因**GLM 订阅端点 `open.bigmodel.cn/api/anthropic` 与 Claude 官方 `api.anthropic.com` 约定不同(前者无 `/v1`后者需补不强制用户填全路径降低配置门槛。GLM 端点已实测。
- **状态**:✅ Sprint 8
### `max_tokens` 必填兜底 4096 / tool_result 连续合并为一条 user
- **决策**:① Anthropic 协议 `max_tokens` 必填(协议无默认),`DEFAULT_MAX_TOKENS=4096` 兜底OpenAI 协议 max_tokens 可选,缺则报错);② 连续多条 `role=Tool`tool_result累积遇非 Tool 消息 flush 为单条 user 消息含多个 `tool_result` 块。
- **原因**:① 统一兜底避免上层每个调用点都要传值(与 OpenAI Provider 的 8192 上限独立,此处仅缺省兜底)。② Anthropic 要求 tool_result 必须在 user 角色内;多工具并发结果合并为一条 user 而非一对一,贴合协议「一回合一组结果」语义,减少消息碎片。
- **状态**:✅ Sprint 8
### 默认标识is_default 落库为真相源 [Sprint 10 → 2026-06-13]
- **决策**`ai_set_provider` 互斥写 DB目标 `is_default=true`、其余 `false`,仅写变化记录);`ai_save_provider` 新建时若全表尚无默认则自动设为默认(首个);`ai_list_providers` 直接返 DB 值。`session.active_provider_id` 降为运行时缓存,由 `set_provider` 同步,重启清零不影响——`get_active_provider` 兜底取 DB `is_default`
- **原因/取舍**Sprint 10 原 active 作真相源 → 致「重启默认丢失」bugactive 是内存态重启清零,而 `is_default` 字段恒写 false重启后无默认可恢复。`is_default` 字段本为持久化默认而存在回归本职最自然session 持久化需额外存储,重复造轮子。互斥写库保证「唯一默认」语义。
- **边界**:互斥写库逐条 `update_full` **不加事务**——repo 未暴露事务接口;桌面单用户无并发触发,失败即报错、重试自愈。多用户/高并发场景需给 repo 补 `execute_transaction`
- **状态**:✅ 2026-06-13 落地(重启保默认 / 互斥写库 / 首个自动默认实测待补)
### 📋 delete_provider IPC 缺失(前端假删除)[Sprint 10]
- **需求**Settings 删 Provider 当前只前端 filter 移除,不调后端(无 `ai_provider_delete` 命令),重启后配置回归。
- **原因**交互欺骗——用户以为删除成功DB 实际未动。需补 `ai_provider_delete` IPC`lib.rs` 注册 + `ai.rs` 实现 + 前端真调),并加二次确认。
- **状态**:📐 待实施 → ✅ Sprint 10 已实现(`ai_delete_provider` 命令 + 删默认清 active + 自建 `confirmDialog` 二次确认)
## 模型能力与路由Model Capability & Auto-Routing
### 能力声明:复用 `ai_providers.models` JSON 字段,不建新表 [2026-06-13]
- **决策**:每个 Provider 下可选模型的能力声明(模态/功能/成本等级)存入已有 `ai_providers.models`JSON 数组),**不新建独立表**。新增 `crates/df-ai/src/model_capability.rs` 定义 `ModelCapability`name / modalities / functions / max_tokens / cost_tier+ `TaskRequirements` + `Modality` / `CostTier` 枚举。
- **原因/取舍**:模型能力是 Provider 配置的内在属性,非独立实体——无生命周期管理、无跨表 JOIN 需求JSON 嵌入单行足够Provider 通常 1-5 个);`models` 列已在 V9 建表且全链路预留,复用零 schema 变更;独立表需外键/级联/JOIN对桌面应用过度工程备份迁移友好配置自包含一行。代价无法 SQL 查询「所有有 vision 的模型」,但此查询当前和近期均不需要。
- **状态**:📐 设计未实施Phase 1数据模型 + 场景级路由)
### 路由层:纯函数 ModelRouter重写现有骨架 [2026-06-13]
- **决策**:重写 `crates/df-ai/src/router.rs` 已有但空的 `ModelRouter`——从 `TaskRequirements` + Provider model_pool → 按硬性要求筛选候选 → 按 cost_tier 升序取最便宜。路由是同步纯函数(无 I/O、无全局可变态可单测。7 个 LLM 调用点统一接入。`override_model` 字段支持用户显式指定(聊天手动切)和工作流节点 config.model节点作者指定两种覆盖优先级最高。
- **原因**:路由是确定性计算(静态配置→模型名),无需 async/service 化;纯函数可单测;统一入口避免散装 if-else。
- **各调用点路由策略**:主对话 `chat(has_image)`→Standard/Premium标题生成 `title_generation()`→Economy 最便宜;知识提炼 `knowledge_extraction()`→Economy/Standard工作流 AiNode `workflow_node()` + override=config.model→节点指定优先**Embedding (×2) 不走路由器**(独立路径,不同模型类)。
- **向后兼容**`models=None`(老记录)→ model_pool 空 → 所有 route 返回 default_model → 行为不变。
- **状态**:📐 设计未实施Phase 1
### Embedding 不进通用路由器 [2026-06-13]
- **决策**Embedding 模型不走 ModelRouter继续走独立路径`KnowledgeConfig.embedding_model` + `embedding_provider_id`)。
- **原因**Embedding 是完全不同的模型类——API 不同(`/v1/embeddings` vs `/v1/chat/completions`)、用途不同(向量化 vs 生成)、通常更小更专用。混入通用模型池会混淆用户并增加路由分支复杂度。
- **状态**:📐 设计未实施Phase 1 确认不动 embedding 路径)
### 分阶段实施路线 [2026-06-13]
- **Phase 1**(本次):数据模型 + ModelRouter 重写 + 7 调用点接入 + Settings 模型池编辑 UI + AiChat 模型下拉。ChatMessage.content 保持 String 不改。
- **Phase 2**后续多模态消息——ChatMessage.content: String → Vec<ContentPart>(Text/Image);前端粘贴/拖拽图片vision 模型自动路由。
- **Phase 3**后续Agent 内智能路由——Agentic Loop 每轮按子任务构造不同 TaskRequirements成本预算控制模型级联降级跨 Provider 搜索。
- **状态**:📐 设计未实施
### 📋 模型能力系统 — 完整改动文件清单 [2026-06-13]
- **后端 Rust**`crates/df-ai/src/model_capability.rs`**新增** ModelCapability / TaskRequirements / Modality / CostTier/ `router.rs`**重写** ModelRouter 匹配逻辑)/ `lib.rs`re-export/ `df-storage/src/migrations.rs`V10 版本号推进,无需 ALTER/ `src-tauri/src/commands/ai.rs`7 调用点加 routerai_save_provider 加 models 参数;新增 ai_set_chat_model_override
- **前端 TS/Vue**`src/api/types.ts`(新增 ModelCapability / Modality / FunctionCapabilities / CostTier 类型)/ `src/api/ai.ts`saveProvider 加 models 参数;新增 setChatModelOverride()/ `src/stores/ai.ts`availableModels / activeModelOverride 状态 + setModelOverride action/ `src/views/Settings.vue`Provider 表单增加模型池编辑区)
- **状态**:📐 待实施Phase 1 全量清单)
## 灵感模块(评估闭环)
### 启发式评分维度
- **决策**:三维固定 5.0 → 内容启发式priority / 描述充实度 / tags / 中英关键词clamp 0-10。
- **原因**:固定值无区分度;启发式基于 idea 内容给差异化评分。
- **状态**:✅ Sprint 9启发式未接 LLM
### 对抗评估:启发式 fallback 待接 LLM
- **决策**:正反方论点/evidence 基于真实 idea 内容生成confidence 由评分驱动;未接 df-ai LlmProvider。UI 诚实标注——评估区标题加「启发式」黄标(`.eval-mode-tag`),对齐实现深度避免名实不符,接 LLM 后摘除。
- **原因**先打通评估闭环LLM 生成论点 + 启发式 fallback 为后续增强。
- **→ 接入方式已定**:走全局 AI trait 下沉(见 [crate 治理 — AI trait 下沉拆 df-ai-core](#ai-trait-下沉拆-df-ai-core-轻层确立全局-ai-接入标准-2026-06-14-))。`evaluate()``LlmProvider` 注入参数,启发式降级为 fallbackF-03 原 A/B/C 选型据此收敛为「全局统一 trait」。
- **状态**:📐 设计未实施LLM 接入)
### 前后端标签对齐
- **决策**`recommendation` 全小写空格(后端原 "With Resources" 不匹配前端 map key`assessmentClass` 映射到 CSS 类名(`.immediate`/`.soon`/...)。
- **原因**:大小写/类名不一致致中文标签不显示、badge 无色。
- **状态**:✅ Sprint 9
### 评分 0-10crate→ 0-100前端IPC 缩放 + 多字段写回用单事务 [Sprint 9/10]
- **决策**:① crate 内 IdeaScores 维持 0-10IPC 层 evaluate_idea 组装 scores JSON 时 *10 缩放为 0-100并用中文维度键可行性/影响力/紧急度/综合)。② evaluate_idea / promote_idea 写回想法用 `update_full`(单事务覆盖整条记录),放弃多次 `update_field`
- **原因**:① 前端雷达图直接当百分比渲染、零前端改动crate 内 0-10 符合评分直觉IPC 层做单位适配。② 多次 update_field 各自独立连接中途失败致数据半成品update_full 原子。
- **状态**:🚧 Sprint 9/10编译/构建通过,未 tauri dev 实测)
## 想法立项promotion
### 复用 df-project 领域层crate do_promote 留纯决策 TODO [Sprint 10]
- **决策**`promote_idea` IPC 复用 `df_project::manager::ProjectManager::create_from_idea` 构造项目实体 + 映射 ProjectRecord 持久化 + update_full 回写想法df-ideas crate 内 `IdeaPromoter/do_promote` 保留纯决策 TODO不真正创建项目。
- **原因**crate 不依赖 df-storage/df-project避免循环依赖、保持可单测手动立项无需 Auto/Manual/SemiAuto 策略判断(用户点即确认),副作用放 IPC 组合,与 evaluate_idea 同模式crate 纯决策留待自动/半自动晋升场景复用。
- **状态**:🚧 Sprint 10编译/构建通过,未 tauri dev 实测)
### promoted_to 非空拒绝重复立项 [Sprint 10]
- **决策**promote_idea 取想法后校验 promoted_to 已存在则返回错误,不创建新项目。
- **原因**:防同一想法多次点「立项」生成多个项目;幂等保护。
- **状态**:🚧 Sprint 10
## 项目管理(删除 / 回收站)
### 项目删除软删回收站deleted_at + 应用层级联),非物理删 [2026-06-13]
- **决策**:删项目改为**软删**——`projects``deleted_at TEXT`V11 迁移),`delete_project``deleted_at`(进回收站,可恢复);`list_projects` 过滤 `deleted_at IS NULL`;回收站(`list_deleted_projects`)可「恢复」(清 deleted_at或「彻底删除」`purge_project` 事务级联物理删 branches→releases→tasks→projects不可逆。子表软删时不动FK 仍满足,项目数据完整保留待恢复。
- **原因/取舍**:① 用户要求「可恢复」——纯 CASCADE 物理删不可逆,误删难挽回;软删 + 回收站给反悔余地。② SQLite `ALTER TABLE` 改不了已有表 FK 约束,给老库 projects 加 `ON DELETE CASCADE` 须重建表(高风险),故不走 DDL 级联,改**应用层级联**purge 时事务内顺序删子表),语义等价且可测、不依赖 `PRAGMA foreign_keys`。③ 软删只标记 projects 行、子表不动——恢复时项目连同历史任务/分支/发布完整还原。④ 两级风险分级:日常软删可逆 / 回收站 purge 二次确认后物理删不可逆。
- **边界**`ProjectRecord` 不带 `deleted_at` 字段,纯靠 SQL `WHERE deleted_at IS NULL` 过滤models/types 零变更。`ai.rs build_system_prompt` 同步改用 `list_active`(防软删项目泄漏进 AI 上下文)。`soft_delete`/`restore` 守卫对称,重复操作幂等。
- **状态**:✅ 2026-06-13 落地V11 迁移 + ProjectRepo 5 方法 + 3 IPC 命令 + 回收站 modal + 删除入口cargo check + vue-tsc + 11 integration test 全绿)
## 项目管理(目录绑定 / 技术栈探测)
### 项目绑定真实代码目录:扩 ProjectRecord + df-project scan [2026-06-13]
- **决策**`ProjectRecord``path`(绑定目录绝对路径)+ `stack`(技术栈 JSON 数组字符串两字段V12 迁移nullable技术栈探测逻辑放**新建 `df-project/src/scan.rs::detect_stack`**(纯函数,浅读根目录标志文件识别 rust/go/python/java/csharp/vue/react/angular/svelte/next/vite/typescript/node/tauricommands 层薄封装 4 命令(`scan_project_stack`/`check_path_binding`/`relocate_project_path`/`check_path_exists``check/relocate``canonicalize` 规范化路径防绕过重复检查。新建项目可选绑定目录→自动探测栈,详情页支持重定位 + 目录失联检测 + 防重复绑定。
- **原因/取舍**:① **扩 df-storage ProjectRecord 而非激活 df-project ProjectContext 空壳**——`ProjectContext` 虽早设计 `root_path`/`tech_stack`/`repo_url`/`ai_context` 字段,但 `manager.rs` 标注 TODO 从未接存储/运行时;激活需新建 `project_contexts` 表 + 填充暂不需要字段,第一步过重,守 YAGNI。ProjectRecord 是实际运行链路,直接扩最快见效。② **scan 放 df-project 而非 commands 层**——`ProjectContext.tech_stack` 本就是 df-project 职责字段scan 是其天然能力且可被 df-ai/df-workflow 复用,放 commands 变一次性代码。③ **migration nullable**——老项目 path/stack=NULL 零影响。④ **一致性原则**:先在「新建流」验证,第二步「导入历史项目」复用同一套 scan/relocate/checkBinding。
- **边界**`path` 规范化canonicalize仅用于比较存库保留用户输入的原始可读路径。程序化创建项目想法晋升、AI 工具path/stack = None不绑定目录`df-project``ProjectContext` 刻意未激活ai_context/repo_url 等暂留空)。
- **状态**:✅ 2026-06-13 落地V12 迁移 + detect_stack + 4 IPC 命令 + 选目录/查重/卡片栈 + 重定位/目录状态cargo build + scan 4 单测 + vue-tsc 全绿)。📋 导入历史项目(第二步):复用 scan/relocate/checkBinding加 monorepo 子目录识别 + README 首段抽 description + 批量。
### 导入/绑定项目 AI 扫描填信息:规则探测兜底 + LLM 增强,采样与 LLM 调用分层 [2026-06-14]
- **决策**:导入/绑定项目时规则 `detect_stack`(快/免费/准)必跑兜底 + LLM 分析采样README+目录树+清单,不读源码)产出 description 摘要与 stack 细化LLM 失败降级纯规则。采样逻辑放 df-project纯 IOLLM 调用放 commands 层。
- **原因/取舍**:① 规则兜底保证 LLM 不稳定时仍有基础信息LLM 只补规则搞不定的摘要。② 采样与 LLM 分层——采样纯 IO 属 df-project 职责可复用LLM 调用依赖 df-ai放 commands 使 df-project 保持无 LLM 依赖(防循环)。③ 不读源码控 token+隐私。④ 结果预览让用户把关防 LLM 瞎编。
- **状态**:✅ 2026-06-14 落地scan_project_with_ai 命令 + 前端 AI 扫描预览;编译/单测/类型全绿)。
### AI 工具绑定目录bind_directory 专用工具 + 工具层白名单同步 + prompt 禁冒充 [2026-06-14]
- **决策**:① update_project 工具白名单补 path/stack同步 db 新字段);② 新增 bind_directory 专用工具(绑定目录不走通用 update③ 系统 prompt 加约束:工具失败须明说,禁用替代操作冒充原意图成功。
- **原因/取舍**:① review AI 对话发现 update_project(path) 被工具层白名单拒db 字段加了但工具层漏同步AI 转而改写 description 却回复「已记录」冒充绑定成功误导用户。② 专用工具语义清晰,防 AI 走通用 update 捷径冒充。③ prompt 约束防单链 ReAct「自我圆场」幻觉失败时用替代谎报成功
- **状态**:✅ 2026-06-14 落地(白名单同步 / bind_directory / prompt 中英约束;编译全绿)。📋 待清:工具层白名单与 crud 白名单双份去重(详见经验记录)。
## 知识库df-evolve / 共享记忆层)
> 核心定位与设计决策。详细字段/参数级决策见各条目。
### 定位 + 被动 Service 退化 [2026-06-13]
- **决策**知识库df-evolve定位为**整个 DevFlow 的共享记忆层**——每个模块idea/task/workflow/review/chat既是知识生产者也是消费者而非孤立展示功能页。df-evolve 褫夺「自动进化引擎」角色Sprint 2 对抗论证已砍,自用阶段 ROI 低/过度工程),**退化为被动 Service 层**,只暴露 `search/retrieve/record_reuse/feedback/save` 供各模块调用;被动 Service 复用 df-ai 检索做消费侧EventBus 做事件驱动提示(非自动抓取)。
- **原因**:手脑(各业务模块)分离无学习能力;知识库做「肌肉记忆」中枢系统才越用越懂你。砍的是自动挖矿,非知识库本身。
- **状态**:📐 设计未实施(用户拍板定位)
### 沉淀审核机制知识状态机AI 只产草稿 [2026-06-13]
- **决策**:知识加 `status` 字段,状态机 `candidate → pending_review → published → archived`**AI 提炼的知识一律进 candidate绝不直接入正式库**;草稿进「待审核收件箱」,人工逐条编辑(内容/分类/标签)→ 发布或丢弃。`verified` = 发布审核时一次性人工标intake 决断动作,非 ongoing 评分)。沿用 IdeaRecord 的 `pending_review` 状态机模式。
- **原因**沉淀必须有人工把关用户要求「人工能够编辑或调整必须有这些过程」——AI 提议、人裁决、系统如实记,非黑箱自动学习。
- **状态**:📐 设计未实施(用户明确要求加审核机制)
### AI 提炼产出:字段集 + 置信度(唯一指标)+ 查重 [2026-06-13]
- **决策**AI 提炼一条 candidate 时产出——**内容字段**`kind`分类AI 判定 7 类之一)、`title``content``tags``source_ref`(原始证据片段);**质量指标**`confidence`High/Medium/LowAI 自评,**唯一质量指标**);提炼时另做**查重**(比对 published 库,重复则不产/标合并,非存储字段)。**克制边界**:质量指标只留 confidence——不加 generality/specificity/novelty 等维度(过工程化 + 多耗 token适用范围并入 `tags` 不单列 scope 字段。
- **原因**`kind``source_ref` 是 AI 必填但易漏的两项confidence 服务降噪 + 审核分诊 + 透明,但**不绕过人审门**high 也不自动发布)。
- **状态**:📐 设计未实施
### 透明化provenance 溯源 + 收件箱 + 注入告知 [2026-06-13]
- **决策**:① 每条知识标来源(哪次 Chat/task/review 产出 + 原始片段),可跳回——复用现有 `source_project` + `source_ref` 字段;② 「待审核收件箱」作明确信息渠道;③ 复用时显式告知本次注入了哪几条、为什么命中。
- **原因**用户要求「透明化让人们有很好的信息获取渠道」——不黑箱。provenance 字段现有模型已有,零新增成本。
- **状态**:📐 设计未实施
### 克制原则:宁缺毋滥,小步迭代 [2026-06-13]
- **决策**:① **检索注入保守**——精确匹配(标签/关键词)优先,语义模糊匹配**后做**top-N 限 1-3 条,置信不够一条都不塞;② **关联不自动推断**——IdeaGraph 自动聚类/关联发现**先不做**,只支持人工标注;③ **沉淀不主动监听全量事件**——仅「一键沉淀」或事件提示后「确认」才产 candidate**从小到大**——先 AI Chat 单点双向跑通验证手感,再串 review/idea。
- **原因**:用户要求「尽可能克制,不要做大胆的连接,从小到大」——贯彻 Sprint 2「scope 砍 60%」精神到知识库,避免重蹈「自动进化」过度工程覆辙。
- **状态**:📐 设计未实施(用户明确要求克制)
### 指标客观化reuse_count 唯一信号,撤销 effectiveness 人工评分 [2026-06-13]
- **决策**:知识排名/淘汰**只用 `reuse_count` 一个客观信号**(检索注入自动 +1**撤销 `effectiveness` 的人工 👍/👎 评分**主观、有摩擦、信号不准淘汰改客观——reuse_count=0 且超 N 天未用 → 提示归档。
- **演进**初版三指标reuse_count + effectiveness + verified共同排序权重 → 同日修正:用户指出 👍/👎 是「人为、主观、非准确」的非必要干预,撤销。「用过 ≠ 有用」的质量顾虑改由两层客观兜底——① intake 审核门(一次性决断)② 发现噪音直接删。召回不准根因在标签/搜索质量,靠 intake 打准标签解决。
- **状态**:📐 设计未实施
### 📋 分层落地 Tier 1/2/3 + 来源/去向审查 [2026-06-13]
- **需求**:知识库联动分三层——**Tier 1必做**AI Chat ↔ 知识库双向 + 手动录入(沉淀 + 检索注入 + reuse_count + 审核收件箱 + 状态机,**无人工评分****Tier 2串创作流带前置**ai_node 检索 prompt_template、工作流 NodeFailed → pitfall**仅失败时**)、决策记录 → architecture_pattern**前置:先补 df-traceability 持久化****Tier 3**evolve_engine 自动挖事件。
- **审查(来源)**:① **/review 源移出**——devflow 无代码审查功能df-stages/coding.rs 审查节点是 TODO 空壳);② **想法评估源存疑**——对抗论点是「一次性结论」非可复用知识;③ **决策记录源标前置**——`DecisionJournal` 所有 SQLite 查询 TODO 未持久化;④ **工作流源限定失败时**——运行日志≠提炼知识。
- **审查(去向)****Chat 轴是唯一 Tier 1 就绪消费端**(检索→注入对话/提示词ai_node prompt 注入属 Tier 2工作流无主动消费仅被动产 pitfall决策溯源消费半残。→ 来源/去向双收敛到 Chat 轴。
- **状态**:📐 待实施(先做 Tier 1Chat + 手动录入)
### 对外暴露MCP Server 双向协议 [2026-06-13]
- **决策**:知识库对外 API 采用 **MCP Server** 形式暴露,**双向**(读+写。Tier 1 先做 DevFlow 内部闭环Tauri IPC**命令层设计完全对齐 MCP 语义**Tier 1+ 套 MCP server封装已有 command。MCP 暴露:① **Resource (读)**list/search/get**Tool (写)**create_candidate**Tool (计数)**record_reuse。
- **原因**:① Claude Code 原生吃 MCP——本机主力工具零集成成本② Cursor 也支持 MCP**双向价值**:外部工具(尤其 Claude Code 做代码审查/重构时是高质量知识来源——审查结论→review_rule、踩坑经历→pitfall接 MCP 自动回流知识库等于开「第二来源入口」。克制Tier 1 不实现 MCP 本身,但 6 个核心命令search/list/get/create/update_status/record_reuse全部按可暴露设计。
- **状态**:📐 设计未实施Tier 1+ 事项)
### 矛盾知识处理:纯标签+内容自述source_project 仅溯源 [2026-06-13]
- **决策**:矛盾知识**不建冲突关系表、不加 scope 字段、不做 access control**。消歧靠 tags`["Go","微服务"]` vs `["Go","单体"]`+ content 自述适用范围 + source_project 仅作来源溯源展示不参与检索过滤。AI 提炼 prompt 加约束:「适用范围有限制必须在 content 或 tags 中标注」。零数据结构变更。
- **原因/取舍**:矛盾是少数场景,为 minority 建关系系统是过度工程source_project 若做绑定/过滤会提高维护门槛 + 降低通用知识复用率;检索 top-N≤3 返回时内容本身场景描述足够消费者判断。
- **状态**:📐 设计未实施
### AI 提炼触发 + 知识注入:可配置 [2026-06-13]
- **决策**:① AI 提炼**默认自动触发**(后台 detached task4 个配置项:`auto_extract`default true/ `trigger_mode`on_complete|on_idle|manual_onlydefault on_complete/ `min_messages`default 4/ `idle_timeout_ms`default 30000。② Chat system prompt 知识注入**加开关**`auto_inject: bool`default true关闭时首行返回空字符串零开销
- **原因/取舍**手动按钮依赖用户记得点→遗忘→空库死循环自动触发保证持续流入候选。但用户控制欲不同——4 配置项覆盖从「全自动」到「全手动」。每次 complete() <500 input/<200 output token可关零成本。注入开关给用户「先积累再开启 / 调试不被干扰」的控制权。
- **状态**:📐 提炼设计未实施 / ✅ 注入已实施Tier 1
### 检索方案LIKE + top-N向量检索提前到 Tier 1Phase 5.5[2026-06-13]
- **决策**Tier 1 检索用 SQLite `title/content LIKE '%query%'` + `ORDER BY reuse_count DESC LIMIT 3`;收件箱排序用 `CASE WHEN confidence 'high'→3/'medium'→2/'low'→1`(非纯 TEXT 字典序)。**向量检索从 Tier 2 提前到 Tier 1 同步实施**Phase 5.5),加 **Settings 开关**`vector_enabled`,默认 false
- **原因/取舍**:知识库核心消费场景是 AI 自动注入(拿用户自然语言 query 检索非关键词精确搜索——「部署后白屏」匹配不到「Nginx SPA 路由」,纯 LIKE 语义盲区从第一天就存在。开关化解「本地优先/零依赖」哲学冲突:默认关闭纯 LIKE 零外部调用,开启后才走 embed API。
- **实施细节**:① `LlmProvider` trait 加 `embed()`OpenAICompat 实现Anthropic 不支持);② V8 幂等补 `embedding BLOB`f32 小端序列化NULL=未嵌入走 LIKE**嵌入时机=发布时**candidate 不浪费 embedpublished 才参与检索);④ 纯 Rust 余弦(<50k 条暴力遍历够用,不引 sqlite-vec 避免 Windows C 扩展编译风险);⑤ 三层降级链开关关→LIKE开但 provider 缺/embed 失败→自动回 LIKE正常→混合检索双信号>LIKE 单>向量单cos≥0.3 滤噪)。
- **状态**:✅ Tier 1 LIKE + 向量混合检索Phase 5.5)均已实施,编译通过待实测
### 知识生命线:独立 knowledge_events 表(非 JSON 嵌主表)[2026-06-13]
- **决策**:知识产生/审核/引用/归档四类审计事件存**独立 `knowledge_events` 表**V10 迁移),而非塞进 `knowledges.context_json` 字段。
- **原因/取舍**:事件是追加型(只增不改删),语义与主表 CRUD 完全不同一条知识可被引用数百次JSON 嵌主表致行膨胀 + 写更新竞争(每次引用都重写整行)。独立表可建 `(knowledge_id,event_type)` 复合索引事件表写失败只丢审计、不影响知识本身fire-and-forget 隔离)。未来加新事件类型只加一行 insert不动主表 schema。
- **状态**:✅ 已实施V10 迁移 + KnowledgeEventsRepo + 前端生命线时间线)
### 📋 知识详情页 + 编辑能力candidate 审核闭环)[2026-06-13]
- **需求**:卡片点不开详情、不能编辑、看不到「为什么产生」。详情页需呈现完整生命线 + candidate 可编辑修正后发布。
- **决策**Knowledge.vue 重构为 Ideas 式左右分栏(左卡片列表 @click 选中 / 右详情面板四分区:①基本信息 ②溯源 ③引用记录 ④生命周期时间线);可编辑字段 title/content/tags/confidence/reasoning 走 `knowledge_update`(部分更新)。
- **原因/取舍**candidate 编辑是审核闭环刚需——AI 提炼必有水分/措辞瑕疵,只能原样发布或整条拒绝会让审核空转。详情复用 Ideas 已验证的 master-detail 模式。published 编辑不做(有归档+重提炼替代)。
- **状态**:✅ 已实施(编译+vue-tsc+df-storage 21 单测全绿GUI 实测待 #54
## AI Chat 上下文窗口与并发控制(架构结论)
> 实现细节见 [归档文档](./功能决策记录-归档.md)「AI Chat 上下文窗口与并发控制」。
### 设计决策 [2026-06-13]
- **ContextManager 类型替换为 messages 真相源**`AiSession.messages``Vec<ChatMessage>` 改为 `ContextManager`(非 wrapper 包装层),消息真相源唯一,避免 Vec + ContextManager 双存导致状态分裂。裁剪仅影响发送视图(`build_for_request` 返回裁剪版,`all_messages_clone` 返全量落库)。
- **淘汰算法:分组滑动窗口 + 三元组保护**`Assistant(tool_calls) + Tool(result)* + Assistant(final_text)` 工具调用三元组作为原子整体;最后 6 条≈2 个完整用户轮次)设为保护区永不淘汰。
- **Token 计数零依赖**`chars_count × 0.35` 粗估(误差 ±15% 可接受),不引入 tiktoken-rs5MB BPE 数据文件对 Tauri 打包不友好)。
- **双层 Semaphore 并发控制**AppState `LlmConcurrency`——全局并发默认 3 / 单对话默认 2permit 在 3 个叶子 LLM 调用点 acquire`run_agentic_loop` stream_llm 前、`generate_title_via_llm``extract_knowledge_from_conversation`),工具执行不受控。`per_conv` 实为应用级单一信号量(因 AiSession 单例 + generating 互斥,命名宽泛但当前语义正确,多对话路线时改 HashMap。Semaphore 重建用「软收敛」策略(替换内层 Arc旧 permit 不受影响)。
- **裁剪策略与模型选择正交**ContextConfig 不含 mode/模型选择字段;「高精度/低精度对话」属 LLM 调用层参数,与裁剪策略是正交维度。
- **状态**:✅ 已落地2026-06-13cargo check + vue-tsc 通过,待 tauri dev 实测)
## i18n
### legacy:false + zh-CN 默认 + locale 拆分 + glob 聚合 [Sprint 7 + 2026-06-13]
- **决策**:① `legacy:false` / `globalInjection` / zh-CN 默认 + en fallback / `localStorage df-language` 持久化。② `zh-CN.ts`/`en.ts` 单文件 → `zh-CN/*.ts` + `en/*.ts` 按模块拆分nav/dashboard/ai/common/settings/ideas/knowledge/projects/projectDetail/tasks/aiChat/aiTool`index.ts``import.meta.glob('./*.ts', { eager, import: 'default' })` 自动聚合(排除 index 自身)。新增模块文件即生效,不改 index。
- **原因**:① Composition API 模式;默认中文贴合自用,英文兜底。② 全量 i18n 接入8 view ~640 处中文)用多代理并行,模块隔离零冲突(每代理建自己模块 + 改自己 view不动共享 index比单文件扩 key多代理改同一 `zh-CN.ts` 冲突更适合并行。glob eager 运行时聚合,动态新增模块即拾取。
- **状态**:✅ Sprint 7 + 2026-06-13curl 验证 vite 正确展开 globvue-tsc PASS
### 状态枚举 i18nconstants 存 keyview 包 $t
- **决策**`constants/project.ts``PROJECT_STATUS_LABELS`/`TASK_STATUS_LABELS` 值从中文文案改存 i18n key`planning: 'projects.status.planning'``projectStatusLabel`/`taskStatusLabel` 返回 keyview 显示处包 `$t(projectStatusLabel(x))``PRIORITY_LABELS`P0/P1是代号非文案不动。
- **原因**constants 是纯数据/结构层,不应含展示文案(违反分层);文案归 localeconstants 管映射结构。
- **状态**:✅ 已落地
### 📋 项目 status 字段语义混乱(生命周期 vs 开发阶段)
- **现象**:后端 `projects.status DEFAULT 'active'` + `list_active`/软删除active/deleted 生命周期),但前端 `PROJECT_STATUS_LABELS` 是 planning/in_progress/paused/completed/cancelled开发阶段两套塞一个 status 字段。DB 实际只有 `status='active'`(新建默认,阶段值从没产生),前端 map 不认 → 显示英文 "active"。
- **治标**:补 `projects.status.active`(🚀进行中),不再显示英文。→ 根本未除。
- **治理方向**:① 后端支持阶段流转planning→in_progress…② 前端 map 对齐后端真实值active/deleted/archived③ 拆双字段status 生命周期 + stage 开发阶段)。
- **状态**:📐 待治理(治标已落地,根本病根未除)
## 状态持久化
### 窗口位置/大小:用 tauri-plugin-window-state纯 Rust 层)
- **决策**:窗口位置/大小/最大化用 `tauri-plugin-window-state` 插件Rust 层自动接管),而非前端 localStorage + setPosition/restore 方案。
- **原因**:插件自动覆盖主窗口 + 动态创建的 `ai-detached` 子窗口,零前端代码、零竞态;前端方案需手动同步且对子窗口生命周期处理复杂。需配套 `window-state:default` capability 权限。
- **状态**:✅ Sprint 10
### detached/docked 不持久化
- **决策**UI 布局持久化,但 `detached`/`docked` 重启后强制回 `false`,不随 `df-ai-ui` 落盘。
- **原因**:重启后分离窗口必然不存在,若恢复为 `true` 会让 UI 状态指向不存在的窗口按钮失灵、panelOpen 错乱)。这两个态是运行时临时态,不属可恢复布局。
- **状态**:✅ Sprint 10
> UI 布局 localStorage + 模块级恢复的细节见 [归档文档](./功能决策记录-归档.md)。
## 应用启动 / 数据库配置
### Dev 与 Build 拆分独立数据库 [2026-06-13]
- **决策**`lib.rs` 启动时按 `cfg!(debug_assertions)` 选 DB 文件名——debugDev 模式)用 `devflow-dev.db`releaseBuild 模式)用 `devflow.db`,两库同处 `app_data_dir()``top.1216.devflow`)下,靠文件名区分。
- **原因/取舍**原启动代码无编译模式分支Dev 与 Build 共用一个 `devflow.db`——Dev 频繁改动/清空会污染 Build 侧真实运行数据。拆分后 Dev 库可随意折腾Build 库长期保留作运行效果基线。**文件名区分而非子目录**——改动最小lib.rs 一行 if两库平铺同目录便于备份/查看。**现有 `devflow.db` 文件名未变归 Build**零迁移零数据丢失Dev 首次启动自动建空库。
- **边界**`app_data_dir()``tauri.conf.json``identifier` 决定、与编译模式无关故拆分前两种模式确读同一文件。docs/使用手册备份命令 `cp devflow.db` 仍正确(备份 Build 真实数据)。
- **状态**:✅ 2026-06-13 落地(`lib.rs:24`
## UI 反馈与弹层
### toast/confirm 自建,不引 Arco / 不用 window.confirm [Sprint 10]
- **决策**Settings 页轻量提示与删除确认用自建 `toast`(顶部 fixed3s 自动消失)+ `confirmDialog`(遮罩 + 卡片Promise 化),而非引入 Arco Message/Modal 或原生 `window.confirm`
- **原因**:① `@arco-design/web-vue` 虽在依赖但 `main.ts``app.use` 注册,引 Message/Modal 要补全局注册 + 样式加载,过重违反做减法;② `window.confirm` 在 Tauri webview2 带「来自 localhost:端口」来源信息,无法去除,体验差。自建零依赖、样式可控(主题色)、`await confirmDialog()` 语义贴近原生 confirm。
- **演进** [2026-06-13]AiChat 删对话需确认 → 第二处复用落地。抽成 `src/components/ConfirmDialog.vue``visible`/`msg`/`dangerLabel` props + `@result` emit。按钮样式内联自包含不依赖外部 `.btn-*`——因 Settings 是 `scoped`,组件拿不到其内定义的 `.btn-danger`。选 SFC 组件而非 `useConfirm()` composable模板/遮罩/Transition 动画/CSS 才是真正重复主体Promise 封装留在调用方(~8 行)。
- **状态**:✅ Sprint 10
## 技能 / 联想
### 首批 Claude 3 类 + path 去重
- **决策**:技能联想首批数据源 = Claude skills / commands / plugins 三类SKILL.md frontmatter按 path 去重(`cache/``marketplaces/` 重复)。
- **原因**frontmatter 格式统一(`name`/`description`/`user_invocable`可统一解析Codex frontmatter 一致后续可扩展openclaw 属 agent 选择层不纳入。
- **状态**:✅ Sprint 8待实测
## 决策治理产品化评估2026-06-13
> 审视 DevFlow 是否应把决策治理(记录/锚点/完成度/自检/漂移)做成产品功能。机制设计详见 [规格契约自检机制.md](./规格契约自检机制.md)。
### 5 痛点产品内未覆盖,真实运转的寄生 Claude Code 层 [2026-06-13]
- **决策**DevFlow 产品内对决策治理 5 痛点「设计满格、代码两极」——df-traceability 死代码(无表/无 IPC/无前端);契约锚点/AI 自检/漂移检测=规格契约自检机制.md 纯设计稿0 行代码);完成度无聚合视图。唯一真实运转的(功能决策记录.md + decision-record skill + dr-check hook寄生在 Claude Code 协作层,未沉淀进产品。
- **原因/取舍**:没用 Claude Code 的用户DevFlow 给不了任何决策治理能力。这套能力寄生在协作工具上,核心价值未进产品。
- **状态**:📐 待产品定位决策
### df-traceability 是锚点雏形Sprint 2 被砍(死代码可复活)[2026-06-13]
- **决策**`crates/df-traceability/``Annotation.location`(文件路径+行号)= 规格契约自检机制设计的「代码锚点」雏形,`Decision` struct 精确对应决策记录痛点。但 Sprint 2 对抗论证时被砍/降级,此后无表、无 IPCquery 方法全 `vec![]`
- **原因/取舍**:非显然关联——文档层设计的活契约+锚点机制,本质是产品外部用更轻方式重发明被砍的 df-traceability 轮子。是否复活取决于产品定位抉择Sprint 2 砍的理由(优先级低/过度设计)现需重新评估。
- **状态**:📐 待评估
### 产品化推荐路径 C 混合,完成度驾驶舱起步 [2026-06-13]
- **决策**三路径——A 全产品化(复活 df-traceability 全栈+spec 自检 AI成本大/重蹈 Sprint 2 覆辙风险B 纯寄生(承认是 Claude Code 协作层,只优化 skill/hook产品核心价值存疑**C 混合推荐——产品做数据底座decisions 表+完成度聚合+视图AI 验证/漂移留协作层**。最小起步:只做完成度驾驶舱(痛点 3
- **原因/取舍**C 分离「确定的数据层」与「不确定的智能层」,先做确定的低风险项。完成度驾驶舱起步:①最痛(记不住做了/没做②技术已存在tasks/ideas 有 status缺聚合 IPC+Dashboard 视图)③立刻可见④验证真会用再扩(避免 Sprint 2 式膨胀)。
- **状态**:📐 待用户拍板DevFlow 要否成为「决策治理/完成度驾驶舱」产品)
## crate 治理 / 模块结构2026-06-14
> 跨 crate 的删留与拆分决策。涉及 df-evolve 领域保留决策的推翻、coordinator 空壳的去留、ai.rs god file 的拆分方式。
### 删除 5 个零引用 crate推翻 df-evolve 领域保留决策)[2026-06-14]
- **决策**:整删 5 个 crate——`df-evolve` / `df-plugin` / `df-stages` / `df-task` / `df-traceability`。**推翻既有「df-evolve 领域类型保留」决策**:连同 `Knowledge`/`PromptTemplate`/`ReviewRule` 领域类型一起整删,知识库领域统一走 `df_storage::models::KnowledgeRecord`,不再维护独立领域模型层。
- **原因/取舍**
- **零引用铁证**:全仓跨 crate 引用为 0——`src-tauri/src` 下 0 处 `use`,其他 crate `Cargo.toml` 不依赖,仅 `src-tauri/Cargo.toml` 声明 `df-evolve` 但源码零用。整坨孤立骨架。
- **推翻归档决策**`功能决策记录-归档.md:355` 原记「`Knowledge`/`PromptTemplate`/`ReviewRule` 领域类型有意保留待 Tier 1 Service 复用」——本次决定**放弃 Tier 1 独立领域模型路线**。理由:① 知识库 Tier 1 已在 `KnowledgeRecord`df-storage上落地 LIKE + 向量混合检索 + 状态机,实际运转的领域模型就是 `KnowledgeRecord`df-evolve 的领域类型成为「理想但悬空」的另一套定义,重复且误导;② 维护两套领域类型是「未来可能复用」的预期成本 vs 「现在重复定义 + 误导性地雷」的实际危害,用户选消灭后者。
- **df-task::Task 一并删**`Task`(含 `branch_id`/`tags`/`estimate_hours` 等比 `TaskRecord` 更丰富的字段)作为「理想任务领域模型」长期悬空零引用,任务领域统一用 `df_storage::models::TaskRecord`
- **df-traceability 整删**:原被记为「锚点雏形可复活」(见上方「决策治理产品化评估」)。本次决定删除——如未来真需锚点机制,重新评估而非保留死码。死码「可复活」是一种伪期权,实际价值是误导后续维护者以为它在运转。
- **df-plugin / df-stages 属过早设计**df-pluginWASM/动态库、df-stages11 阶段节点 `execute()` 全空壳未启动即删与「人机协同设计基准」中「AI 反复读写的代码不养空壳」一致。
- **影响**:① `src-tauri/Cargo.toml` 需移除 `df-evolve` 依赖声明;② 「决策治理产品化评估」中 df-traceability 相关条目(锚点雏形 / 完成度驾驶舱数据底座)状态需重新标注为「无现存代码可复用」;③ 知识库功能域(`## 知识库`)所有决策继续适用,但实现载体明确为 `KnowledgeRecord` 而非 df-evolve 领域类型。
- **状态**:✅ 2026-06-14 落地(用户拍板删 5 crate + 领域类型)
### coordinator.rs 保留B 路线占位空壳,不删)[2026-06-14]
- **决策**`crates/df-ai/src/coordinator.rs``AgentCoordinator`(多 agent 协作占位空壳,`run()` 返回硬编码 TODO**保留不删**加注释标注「B 路线待立项」。
- **原因/取舍**:与 aichat 升级 A/B 路线拆分一致——A 路线先做 UX 快赢已进行B 路线单独立项补多 agent 协作决策能力。coordinator 是 B 路线的入口锚点,**有意保留占位**而非删除:① B 路线立项时有现成挂载点trait + 结构骨架),不必从零设计;② 与上述 5 crate 删除不矛盾——5 crate 是「无路线图占位的纯死码」coordinator 是「有明确后续路线B 路线)的占位」,二者判断标准不同。区别在「是否绑定明确的演进路线」。
- **状态**:📐 B 路线待立项(保留空壳 + 加注释)
### ai.rs 拆分:低风险子 module 而非下沉 crate [2026-06-14]
- **决策**`src-tauri/src/commands/ai.rs`2663 行 god file拆成 `commands/ai/` 子 module 目录11 个文件mod + commands + agentic + stream_recv + conversation + title + audit + skills + prompt + tool_registry + knowledge_inject`pub use` 保持 `state.rs`/`lib.rs`/`knowledge.rs` 引用路径零改动。**采用低风险子 module 方案而非下沉到 df-ai crate**。
- **原因/取舍**:① 2663 行单文件消耗大量 AI context每次理解靠结构与「人机协同设计基准」中「组件拆分从『不做』升为『值得做』」一致。② **选子 module 不选下沉 crate**——下沉到 df-ai 需动 crate 依赖图df-ai 反向依赖 src-tauri 的 state/types引入循环依赖风险 + 跨 crate 重构成本;子 module 仅在同 crate 内切目录,`pub use` 保持对外 API 不变,零引用路径改动,重构面最小。③ 子 module 仍能达成「职责单一」的 AI 可读性目标,与下沉 crate 收益相当但风险低一个数量级。
- **实施验证2026-06-14 落地)**
- 11 文件合计 2833 行,最大 knowledge_inject.rs 557 行含测试commands.rs 529 行17 个 IPC 命令),其余均 < 400 行。
- 路径契约全保:`commands::ai::{AiSession, build_ai_tool_registry, restore_pending_approvals, spawn_embedding_for_knowledge, trigger_extraction_now}` + 17 个 invoke 命令state.rs/lib.rs/knowledge.rs/commands/mod.rs **零改动**
- `cargo check` 0 error`cargo test ai::` 19 passed。剩 3 warning 全为预存非本次引入。
- 顺带修 bug`ai_conversation_list``models` 字段从 JSON 字符串直塞改为解析为 `Vec<String>` 数组下发(前端期望数组,原代码传字符串)。
- **状态**:✅ 2026-06-14 落地11 子 module + glob 重导出 + models bug 修复cargo check/test 通过)
### 前端 ai.ts 拆分路线Astore 留 state 单例 + composable不引入 Pinia [2026-06-14]
- **决策**`src/stores/ai.ts`758 行 god store拆成 store 骨架(留 reactive state 单例 + 模块级私有变量)+ `src/composables/ai/` 下 6 个 composable`useAiEvents`/`useAiStream`/`useAiSend`/`useAiConversations`/`useAiWindow`/`useAiPanel`)。`useAiStore()` 统一入口展开所有 composable 方法,**返回 shape 不变,组件零改动**。
- **原因/取舍**
1. **与项目既有 store 风格一致**——project/knowledge/settings 全是「手写 reactive + 工厂函数」模式,引入 Pinia 会破坏一致性、增加心智负担。
2. **组件零改动**——AiChat.vue/AiDetached.vue 等用 `const { state, sendMessage } = useAiStore()` 解构,保持 `useAiStore` 返回 shape 不变即可。
3. **选路线 Acomposable 挪逻辑、store 留 state而非路线 BPinia 多 store**——后者要改 19 个 state 字段归属和所有组件 import风险远大于收益。
4. **与后端 ai.rs 子 module 拆分配对**——前后端 god file/god store 同步拆解采用各自生态的惯用拆法Rust 子 module / Vue composable不强求统一模式。
- **影响**:确立项目 store 架构约定(手写 reactive + composable不用 Pinia影响所有未来 store 设计。
- **状态**:🚧 执行中workflow w20yb6n6b 编排vue-tsc 自验证)
### AI trait 下沉:拆 df-ai-core 轻层,确立全局 AI 接入标准 [2026-06-14 📐]
- **决策**:将 `LlmProvider` trait + AI 数据类型(`ChatMessage` / `CompletionRequest` / `ToolDefinition` 等)从 `df-ai` 拆出,下沉到新轻量 crate `df-ai-core`(零 http 依赖);`df-ai` 保留为「实现 + `ModelRouter` + provider 工厂」hub持有 `reqwest`/`openai_compat`/`anthropic_compat`);所有消费 crate`df-ideas` 接对抗评估、未来 `df-knowledge`/`df-workflow` 等)**只依赖 `df-ai-core` 的 trait不直接依赖 `df-ai`**;真实 provider 由 `src-tauri` 最上层装配注入。**F-260614-03 及后续所有 AI 接入点统一照此,不再 per-module 自定义 trait。**
- **原因/取舍**(纯 ai-coding 工作模式下重算 F-03 原 A/B/C 选型):
1. **砍「人审查的契约面」而非「打字量」**——一份全局 trait = 一份契约给人过目 + 规格契约 self-check 锚一点per-module 自定义 trait原 A= N 份发散契约,审查负担与漂移风险同涨。纯 ai-coding 下接线/mock 全由 AI 吸收,故 A 的「适配器成本」、B 的「mock 成本」论点作废。
2. **保住纯逻辑 crate 零摩擦自检**——`df-ideas`/`df-storage` 只依赖 trait 不拖 `reqwest`,规格契约机制 A/B 测试自动跑无 http 依赖;裸 Bdf-ideas 直接依赖 df-ai会污染纯逻辑 crate、自检摩擦上升。
3. **与现有 roadmap 对齐**——`ModelRouter`F-260614-01、多 Provider 负载均衡池F-260614-04、provider 工厂(已落地)本就在 df-ai 内走「gateway」方向消费方选型应顺此而非另起 N 个 trait。
4. **无环且与 ai.rs 拆分不冲突**`df-ai-core`trait+类型)← `df-ai`(impl) / `df-ideas`(use trait)`src-tauri` 装配。「ai.rs 子 module 不下沉 crate」规避的是 src-tauri→df-ai 反向依赖;本决策是 df-ai→df-ai-core 正向拆分,方向相反、互不矛盾。
- **否决项**:纯 Aper-module trait= 全局 N 份发散契约,反模式;裸 Bdf-ideas 直接依赖 df-ai= 纯逻辑 crate 被 reqwest 污染、自检摩擦上升。
- **退路**:若不愿加新 cratetrait 可放 `df-core`语义稍糙——LLM 非领域类型,但零新 crate可接受
- **状态**:📐 设计决策已定2026-06-14未实施。落地链① 新建 `df-ai-core` + 迁 trait/类型;② `df-ai` 改依赖 `df-ai-core` + 留实现;③ `df-ideas``df-ai-core` 依赖、`AdversarialEngine::evaluate` 加 provider 注入参数;④ src-tauri 装配真实 provider。
## 工作流人工审批节点B-03
### HumanNode 审批响应机制subscribe→send→select! 广播过滤等待 [2026-06-14 📐]
- **决策**df-workflow `HumanNode.execute` 改为「先 `subscribe()` → 发 `HumanApprovalRequest``tokio::select!` 循环等 `HumanApprovalResponse`」,按 `execution_id + node_id` 双键过滤命中后返回 NodeOutput`select!` 三分支 = 响应 / 超时(配置 `timeout_secs` 默认 3600s/ 取消500ms 轮询 `is_cancelled`)。复用既有 `EventBus`(broadcast) / `HumanApprovalResponse` 事件 / `approve_human_approval` IPC / 前端 store——零新增基础设施仅改 HumanNode 一处。
- **原因/取舍**
1. **订阅时序铁律**tokio broadcast 不回放历史,必须 `subscribe()` 先于 `send(Request)`,否则 receiver 错过 Response 死等超时。
2. **双键过滤**node_id 单键不够跨工作流可能重复、execution_id 单键不够(同层多 HumanNode双键才完备。
3. **Lagged 容忍**capacity 256 + 审批低频,漏自身 Response 概率极低;`continue` 优于丢弃(丢弃误判超时更糟)。
4. **decision 强制校验**options 非空时强制 `decision ∈ options`非法值报错而非静默放行——审批门控不能被脏输入绕过options 空时允许自由文本。
5. **B-06/B-07 是并发隔离/取消的前置,非单流功能前置**:单工作流 B-03 照常工作;并发安全等 B-06execution_id 下沉);取消机制需 B-07 + `set_cancelled` + cancel IPC单列 **B-03b**。B-03a响应等待 + 超时)不依赖 B-07。
- **边界**:取消分支在 B-07 + `set_cancelled` 补齐前恒 false等价无取消功能不残跨工作流并发 HumanNode 在 B-06 修前有 Response 错配风险。
- **状态**:📐 设计完成2026-06-14未实施。详见 [B-03-人工审批响应机制.md](./B-03-人工审批响应机制.md)。
## 需求与待办
> 汇集散落于各决策条目状态(📐/🚧)的待办 + 新增需求细节 + 需求澄清。单一清单,避免遗漏。
### 📋 待做需求
| 需求 | 功能域 | 来源 | 优先级 |
|---|---|---|---|
| Sprint 9/10 多项编译过未 tauri dev 实测(评分 IPC 缩放 / update_full / promote_idea / Store getter | 灵感/立项/Store | Sprint 910 🚧 | P1 |
| 切对话不中断路由:部分场景运行时实测 | AI Chat 可靠性 | Sprint 8 🚧 | P1 |
| 技能联想「使用」:首批 3 类联想已做,联想后实际触发/执行技能未实现 | 技能/联想 | Sprint 8 | P2 |
| 灵感对抗评估接 LLM论点/evidence 由 df-ai LlmProvider 生成(现启发式 fallback | 灵感模块 | Sprint 9 📐 | P2 |
| 知识库 Tier 1AI Chat ↔ 知识库双向闭环(沉淀+检索注入+reuse_count+审核收件箱+状态机+provenance 溯源+克制检索,无人工评分) | 知识库 | 2026-06-13 ✅ 已实施(Sprint 15) | P1 |
| 路径校验根治workspace 白名单 + canonicalize现仅拒 `..` + 敏感目录) | 工具调用 | Sprint 6 | P2 |
| 停止生成 idle 即时优化:`tokio::sync::Notify` 替代 120s 轮询 | AI Chat 可靠性 | Sprint 6 | P3 |
| 多 Provider 负载均衡池(备用模型/多账号聚合,全局容量=min(各 provider 上限之和, global_cap) | AI Chat 并发控制 | 2026-06-13 📐 | P2 |
| 裁剪/压缩消息按需召回Query Function + 分层存储: TrimRecord 追踪被移除范围 → DB 全量归档按需检索 → 精准注入 build_for_request触发方式待定:自动/手动/语义检索) | 上下文窗口管理 | 2026-06-13 📐 | P3 |
| ✅ IPC参数驼峰/蛇形不对齐误报澄清Tauri v2 自动将前端 camelCase 参数名转后端 snake_case`approve({toolCallId})` / `setConcurrencyConfig({globalLimit})` 实际正确、功能正常——无需修 | AI Chat | 2026-06-13 审查误报 | — |
| 🔴 df-workflow ConditionEngine 默认 true所有未识别条件表达式均通过工作流条件分支形同虚设`Ok(false)``Err` 一行可修 | 工作流引擎 | 2026-06-13 代码审查 | P0 |
| 🔴 df-workflow DagExecutor execution_id 硬编码 "dummy-execution-id":所有执行 ID 相同,追踪/审计失效 | 工作流引擎 | 2026-06-13 代码审查 | P1 |
| 📐 df-workflow HumanNode 假实现execute 注释"等待审批"但首次迭代直接 return "同意" — **设计完成 [B-03-人工审批响应机制.md](./B-03-人工审批响应机制.md),待实施**(依赖 B-06 并发隔离 / B-07 取消前置) | 工作流引擎 | 2026-06-13 多代理探索 → 2026-06-14 设计 | P0 |
| 🔴 df-workflow NodeRegistry::default() 的 script 工厂 unimplemented! panic用 default() 构建注册表 + 跑 script 节点即崩溃进程(非优雅 Err | 工作流引擎 | 2026-06-13 多代理探索 | P0 |
| 🔴 df-workflow executor 每节点拿全新空 StateMachineself.state_machine 从不传入 NodeContextHumanNode is_cancelled 恒 false取消机制失效 | 工作流引擎 | 2026-06-13 多代理探索 | P1 |
| 🟡 promote_idea 两步写非事务INSERT project 成功后若 UPDATE idea 失败,项目存在但想法状态未变,补偿删除可修 | 灵感/立项 | 2026-06-13 代码审查 | P1 |
| 🔴 分离窗口detached跨窗口状态失效用 localStorage 传递生成态快照df-ai-gen/textTauri 多 webview 不共享 localStorage 致静默失效;用户点 X 关闭(非 closeDetachedWindow后主窗口 `detached` 永真卡死reattachPanel 死代码未接线)。需改 Tauri 全局 emit/listen 同步 + 窗口销毁事件复位 | AI Chat 分离窗口 | 2026-06-13 代码审查 | P1 |
| 模型能力声明与自动路由系统 Phase 1ModelCapability 数据模型 + ModelRouter 重写 + 7 调用点接入 + Settings 模型池编辑 UI + AiChat 模型下拉。核心:按任务需求(模态/功能/成本)自动匹配合适模型,不再所有场景共用 default_model | 模型能力与路由 | 2026-06-13 📐 设计完成 | P1 |
| 模型能力系统 Phase 2多模态消息支持——ChatMessage.content: String → Vec<ContentPart>(Text/Image);前端粘贴/拖拽图片vision 模型自动路由 | 模型能力与路由 | 2026-06-13 📐 | P2 |
| 模型能力系统 Phase 3Agent 内智能路由——Agentic Loop 每轮按子任务构造不同 TaskRequirements成本预算控制模型级联降级跨 Provider 搜索 | 模型能力与路由 | 2026-06-13 📐 | P3 |
| 📋 待澄清「显示多开」:用户报"设置里勾选'显示多开'但 AiChat 未显示"。全 src grep `多开\|多窗口\|multi\|multiInstance` 零命中Settings.vue `settings` 对象仅 8 字段无此项。AiChat 唯一相关的是常驻「分离窗口」按钮(不受设置控制)。疑似:① 用户指分离窗口按钮(本就常驻不需设置);② 看的是打包旧版本界面;③ 想新增"允许分离窗口"设置开关。待用户截图/确认位置再定 | AI Chat | 2026-06-13 需求澄清 | — |
| 🔴 待审批持久化根治(重启恢复)未生效——两处逻辑断裂致恢复链路跑不通:① `ai_conversation_switch` 无条件 `pending_approvals.clear()` 清空 `restore_pending_approvals`(init)重建的内存 HashMap`ai_pending_tool_calls`/`ai_approve` 均依赖内存态 → 重启后前端 `switchConversation` 触发 clear → 审批卡片查空永不显示、审批报"未找到挂起的审批";② `ai_approve` 的 recovered 守卫跳过 `save_conversation`(注释称"防空 messages 污染老对话"前提不成立——switch 时 `restore_from_messages` 已载完整历史,审批时 messages 非空 → 执行的工具结果不落库,重启后 toolCard 显示 completed 但 result 仍是占位"需要用户审批,等待确认"。修复方向pending 恢复链路改查 DB`ai_tool_executions` WHERE status='pending' 持久化真相源)绕过内存 clear`ai_approve` 内存 miss 时 fallback DB 单条重建再执行recovered 审批通过后正常 save。可顺带删 `restore_pending_approvals`DB 即真相源)。阻断用户"功能逻辑层面解决"诉求——现"根治"实为表面修复 | AI Chat 审批持久化 | 2026-06-13 /review 审查①② | P0 |
| 📋 审批可见性缺口pendingApprovals 无兜底渲染→卡死 [2026-06-13]AI 发起 Med/High 工具审批AiApprovalRequired后暂停等审批不发 delta前端审批唯一出口是 ToolCard 的 pending_approval 内联卡片(靠 findToolCall 置 tc.status但 state.pendingApprovals 数组有数据却零渲染AiChat.vue 仅 @approve 转发,无 pendingApprovals 模板)。若 tc 卡片未显示审批,用户看不到审批按钮 → AI 永久等 → 文字停卡死。待修A. AiChat.vue 加 pendingApprovals 醒目渲染(顶部条/浮层)兜底审批可见性;或 B. 运行时确认 tc 卡片是否渲染。配套watchdog 在 AiApprovalRequired 暂停,审批没弹则 watchdog 盲点,需加"审批超时未响应"提示 | AI Chat 审批 | 2026-06-13 | 📋 A/B 待定 |
| 📋 node_executions 全表 list当前只写不读若未来前端要看某次工作流执行的节点明细需**新增** `list_node_executions(execution_id)` 命令 | 工作流引擎 | 2026-06-13 代码审查 | 📐 待需求驱动 |
### 📋 需求澄清
- **「决策」术语边界**2026-06-12devflow 语境「决策/决策需求点」= 日常开发功能细节取舍(为什么这么定),**非** aichat 决策能力升级B 路线 coordinator/conditions。后者属架构层记 Phase2计划/模块文档,不混入本文档。
- **代码审查甄别原则**2026-06-13审查发现问题时按「运行时失败/数据损坏 → 简单清理 → 记录不动 → 不做」四档甄别。当前项目规模下list_all 无 LIMIT、ALLOWED_COLUMNS 不分表、bool→int 重复等属「记录不动」——个人工具表不超千行,加分页/拆白名单是过度设计,维护成本 >> 收益。原则:**真实 bug 修、简单清理做、规模不到位的优化先不动**,保持全局简洁和扩展容易。
**相关文档**
- [Phase 1 架构决策](./Phase1架构决策.md) — 架构级决策ADR
- [经验记录](./经验记录.md) — 踩坑/约定/技巧/bug 排查教训
- [功能决策记录-归档](./功能决策记录-归档.md) — 纯流水/老 Sprint/UX 微调/已被取代
- `PROGRESS.md` — 各 Sprint 工作流水与遗留
- [Phase 2 计划](../07-项目管理/Phase2计划.md)