优化: todo销账(F-05/T-06/B-05误判)+夜间6决策记录+CR-65审查PASS回填

This commit is contained in:
2026-06-17 04:02:04 +08:00
parent a622bdca61
commit 9d1e5c2737
3 changed files with 69 additions and 16 deletions

View File

@@ -112,6 +112,15 @@
- **原因**交互欺骗——用户以为删除成功DB 实际未动。需补 `ai_provider_delete` IPC`lib.rs` 注册 + `ai.rs` 实现 + 前端真调),并加二次确认。
- **状态**:📐 待实施 → ✅ Sprint 10 已实现(`ai_delete_provider` 命令 + 删默认清 active + 自建 `confirmDialog` 二次确认)
### 多 Provider 负载均衡池ProviderPool 归 app 层 + 模型亲和排序 + fallback 分类 [2026-06-17]
- **决策**ProviderPool 实现select/fallback/capacity放在 `src-tauri/commands/ai/` 目录下(非 df-ai crate`prompt.rs::get_active_provider` / `secret.rs::build_provider_for` 同属「消费 AiProviderRecord 的 app 层」。select 排序规则:模型亲和(含 model_id 的 provider 排前)> weight 降序 > is_default 兜底。fallback 策略InitFailedretryable耗尽候选后切下一个 providerFatal4xx 非 429立即放弃整个 fallback 链。per-provider 并发 cap = global_cap差异化 cap 留后续)。否决健康度路由(需持久化健康状态,过度工程;瞬态故障由 fallback 吸收)和纯轮询(加权是轮询超集)。
- **原因/取舍**
- **位置选 commands/ai/ 非 df-ai**df-ai 定位是「协议适配 + Provider trait」存储无关引入 ProviderPool 会创建 df-ai→df-storage 依赖(读 AiProviderRecord破坏存储无关边界。commands/ai/ 本就是 app 层装配点get_active_provider / build_provider_for 都在此层ProviderPool 放此处语义一致。
- **模型亲和排第一**F-01 智能路由已按任务需求选定模型+provider 组合,若 select 排序不含模型亲和可能换到不含该模型的 provider导致路由结果失效。模型亲和保「路由选的模型一定在选中 provider 上可用」。
- **否决健康度路由**:健康检查需持久化状态(上次成功时间/连续失败计数),桌面单用户场景 provider 数量少(通常 2-5 个),瞬态故障由 fallback 重试吸收即可。健康度增加的复杂度(定时探测/状态序列化/启动恢复)远大于收益。
- **Fatal 立即放弃**:对齐 retry.rs 的 Fatal 分类4xx 非 429 = 请求本身非法,重试无意义),避免无效重试浪费 quota 和延迟。
- **状态**:✅ 已落地commit 79b6a43 / b3684f4 / 80c0955
## 模型能力与路由Model Capability & Auto-Routing
### 能力声明:复用 `ai_providers.models` JSON 字段,不建新表 [2026-06-13]
@@ -142,6 +151,14 @@
- **前端 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 全量清单)
### 多模态消息content:String 保留 + parts 新增,务实偏离 Vec<ContentPart> 原案 [2026-06-17]
- **决策**Phase 2 多模态原设计 `content: Vec<ContentPart>` 改为 **`content: String` 保留不变 + 新增 `parts: Option<Vec<ContentPart>>`**。老 JSON 反序列化时 parts=None 零回归。后续若严格对齐 content:Vec 需先解禁 4 个 forbidden 文件的 content 消费点(改用 content_text() / flattened_parts() 辅助方法)。
- **原因/取舍**
- **4 个 forbidden 文件直接消费 m.content:String**——`audit.rs`(审计日志读 content/ `title.rs:50`(结构体字面量 ChatMessage{ content: ..., role: ... }/ `commands.rs`IPC 层序列化)/ `knowledge_inject.rs`(知识注入读取)。其中 title.rs 是结构体字面量构造Rust 不支持字段默认值,加任何 required 字段必炸编译。
- **不改 content 为 Vec 的代价可控**——parts 携带多模态数据content 保留纯文本降级路径。前端/LLM 层按 parts 是否 Some 判断是否多模态,老路径不受影响。
- **向后兼容**——serde `#[serde(default)]` 让缺失 parts 字段的老 JSON 反序列化为 None零回归风险。
- **状态**:✅ 已落地commit e3cd448
## 灵感模块(评估闭环)
### 启发式评分维度
@@ -342,6 +359,14 @@
> UI 布局 localStorage + 模块级恢复的细节见 [归档文档](./功能决策记录-归档-2026-06-14.md)。
### 虚拟滚动:自研 IntersectionObserver + sentinel不引 vue-virtual-scroller [2026-06-17]
- **决策**AI Chat 消息列表虚拟滚动选**自研方案**——IntersectionObserver + sentinel div上下各一+ ResizeObserver 监听容器变化仅做渲染层裁剪不可见窗口卸载内容sentinel 占位保持 scrollHeight 不塌)。不选 vue-virtual-scroller 的 DynamicScroller。流式末条 pinnedKeys 保活不卸载。
- **原因/取舍**
- **vue-virtual-scroller 破坏既有布局**——DynamicScroller 接管滚动容器内部 DOM 结构 + 重排子节点顺序,与现有 `.ai-messages``display: flex; flex-direction: column; gap: 14px` 布局冲突。同时破坏三处既有行为:① onMessagesScroll 的 wasNearBottom 边沿收起逻辑(依赖原生 scroll 事件和 scrollTop 计算);② isNearBottom / scrollToBottom / 「回到底部」按钮计算(依赖真实 scrollHeight③ 流式生成时自动滚到底部DynamicScroller 的滚动语义不同)。
- **自研只做渲染裁剪**——不接管滚动容器、不改 DOM 结构、不动事件监听。sentinel div 用 `height: Npx` 占位保持 scrollHeight 准确IO 触发时批量 mount/unmount 可见区间外消息组件。既有滚动/收起/流式/回到底部全零感知。
- **pinnedKeys 保活**——流式生成中末条消息频繁更新,若被 IO 卸载再挂载会导致闪烁pinnedKeys 集合内的消息跳过卸载判断。
- **状态**:✅ 已落地commit e38474b
## 应用启动 / 数据库配置
### Dev 与 Build 拆分独立数据库 [2026-06-13]
@@ -436,6 +461,14 @@
- **退路**:若不愿加新 cratetrait 可放 `df-core`语义稍糙——LLM 非领域类型,但零新 crate可接受
- **状态**:📐 设计定稿2026-06-144 项决策已定:① 拆分边界=仅 trait+数据结构 ② provider 注入=构造注入 `Engine::new(Option<Arc<dyn LlmProvider>>)` ③ LLM 失败=自动降级启发式+warn+`EvaluatedBy` 标记 ④ provider 构造=应用层 src-tauri 装配注入),未实施。详见 [F-07-df-ai-core-trait下沉设计-2026-06-14.md](./F-07-df-ai-core-trait下沉设计-2026-06-14.md)。
### df-core → df-types 改名:类型库非核心,语义明示「类型契约层」[2026-06-17]
- **决策**crate `df-core` 改名为 `df-types`。全 workspace 机械改名54 处源码引用 + git mv + 9 个 Cargo.toml 依赖声明 + Cargo.lock 自动迁移)。
- **原因/取舍**
- **「core」名称误导**——df-core 含 0 业务逻辑、0 内部依赖、0 宏自引用,纯粹是跨 crate 共享的类型定义ProjectRecord / TaskRecord / IdeaRecord / KnowledgeRecord / ChatMessage 等)+ re-export 聚合。叫「core」暗示它是核心业务层实际是「类型契约层」。
- **改名收益 > 成本**——54 处机械替换纯字符串无语义改动git mv 保历史Cargo.lock 跟随 Cargo.toml 自动更新。一次性 10 分钟操作,消除后续所有新贡献者的认知摩擦。
- **不影响 df-ai-core**——df-ai-core 是 AI trait 层LlmProvider / CompletionRequestdf-types 是领域模型层ProjectRecord 等存储实体),两者职责清晰不重叠。
- **状态**:✅ 已落地commit 4be1591
## 工作流人工审批节点B-03
### HumanNode 审批响应机制subscribe→send→select! 广播过滤等待 [2026-06-14 📐]
@@ -473,6 +506,14 @@
- **原因/取舍**工作流成功与任务推进是两个独立事实解耦避免「工作流成功但任务推进失败时回滚已完成工作流」的复杂性失败退回让审查拒绝能回流上一态review_rounds+1。CAS 已防回调与手动 advance 并发撞advance_task_atomic 捕获 InvalidState 降级。跨表事务缺失阶段2 回调失败降级阶段3/4 补 Database.transaction())。
- **状态**:📐 设计定稿待实施batch32 做 ②-5 HumanNode reject 语义化batch33 做 ②-3/②-4 回调)。
### AiNode 自审闸门:内部 return Err 复用 executor first_err方案 A[2026-06-17]
- **决策**AiNode 自审闸门选**方案 AAiNode 内部 return Err**——自检失败时 return Err(SelfReviewFailed) 复用 DagExecutor 已有的 first_err 收敛 + ②-4 回调错误路径。不选方案 BDAG edges 条件 + ConditionEngine依赖暂缓的 T-260614-11和方案 CDagExecutor 核心循环改闸门钩子。gate 配置默认 false 向后兼容阶段2 行为不变testing 模板可设 gate:true 启用。
- **原因/取舍**
- **方案 A 零 executor 核心改动**——Err 沿既有 execute() → run_node() → first_err 路径自然冒泡executor 核心循环零行变更。②-4 回调已处理 failed 分支(退回上一态),闸门失败自动走此路径无需额外代码。
- **方案 B 依赖 ConditionEngine**——T-260614-11 条件表达式引擎尚在 📐 设计阶段,为单个闸门功能拉入未完成的依赖链路风险高。
- **方案 C 改 executor 核心循环**——在 node 执行前后插钩子before/after execute是通用扩展点但当前仅 AiNode 一个消费者,为单一场景改核心循环过度工程;且钩子语义(是否中断后续节点、是否影响 DAG 继续执行)需详细设计,复杂度远超方案 A 的 1 行 return Err。
- **状态**:✅ 已落地commit e16d038
## 需求与待办
> 汇集散落于各决策条目状态(📐/🚧)的待办 + 新增需求细节 + 需求澄清。单一清单,避免遗漏。
@@ -501,7 +542,7 @@
| 模型能力声明与自动路由系统 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 Chat 窗口多开)[2026-06-17]:用户原意是「多个会话之间切换都可继续对话」(当前切换会话后旧会话生成态丢失)。关联 F-09 多会话架构决策——A 路线(单例 AiSession + 软隔离Sprint 8 已落地切对话不中断路由待实测验证是否满足需求T-260614-02B 路线真多会话AiSession 单例→多实例)是备选但触及 memory 记录的「AiSession 单例未动」架构约束。→ 2026-06-17 澄清为「多会话并发」需求,推荐先实测 A 路线再定是否需 B 路线。已记 todo L652 + 待决策.md🟡 A/B 路线决策) | AI Chat / 多会话 | 2026-06-13 待澄清 → 2026-06-17 已澄清 | 🟡 待 A/B 路线决策 |
| 🔴 待审批持久化根治(重启恢复)未生效——两处逻辑断裂致恢复链路跑不通:① `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 代码审查 | 📐 待需求驱动 |