|
| 1 | +# 头脑风暴记录:AI 精修非功能扩展 + 全链路问题修复(v0.8.0 补强) |
| 2 | + |
| 3 | +## 基本信息 |
| 4 | + |
| 5 | +| 字段 | 内容 | |
| 6 | +|------|------| |
| 7 | +| 主题 | AI 精修「能用、用得好、用得顺」——全链路问题检测 + 非功能扩展设计(任务中心化 + 协议 v2 + 评测框架) | |
| 8 | +| 日期 | 2026-08-21 | |
| 9 | +| 参与者 | 用户 + AI 代理(对话式多轮) | |
| 10 | +| 目标 | 产出 AI 精修补强设计:P0/P1 问题修复全量纳入 + 非功能扩展(任务中心/并发重试/成本控制/质量评测/可观测性)按里程碑分层交付 | |
| 11 | +| 关联 | [v0.8.0 规划](../versions/v0.8.0.md) · [v0.8 AI 精修头脑风暴归档(2026-08-19)](../2026-08-19/brainstorming-v0.8-ai-note-refine.md) · REQ-141/142/143/144/145/147 · ADR-010(AI 为增强层·本地兜底铁律) | |
| 12 | + |
| 13 | +--- |
| 14 | + |
| 15 | +## 1. 问题定义 |
| 16 | + |
| 17 | +### 核心问题 |
| 18 | +v0.8.0 的 AI 精修/知识补充代码建设完成,但**真机不可用**:AI 调用有记录(SiliconFlow 侧产生费用)而返回结果未被实际使用(前端永久"排队中");此外存在丢图、成本失真、零审计、配额失控、切片结构错乱等 P1 问题,非功能扩展(任务可恢复性/并发/评测)为零。 |
| 19 | + |
| 20 | +### 检测方法 |
| 21 | +- 全链路代码审查:前端(AiRefineCard/EnrichPanel/AiServicePanel/NotePreviewView/types.ts)→ 命令层(commands_ai_refine/commands_ai_enrich/commands_ai_settings)→ 任务状态机(ai_task)→ 云端适配器(ai_client/ai_note_refine/ai_note_enrich)→ 协议校验(ai_refine_protocol/ai_enrich_protocol)→ 落库(ai_refine_apply/versioned_save/db_ai_usage) |
| 22 | +- 契约实证:临时单测实测 serde 序列化输出(已还原) |
| 23 | +- 文档对照:v0.8.0 规划验收项逐条核对 |
| 24 | + |
| 25 | +### 关键现状(检测结论) |
| 26 | +- **P0 契约断裂(已实证)**:`AiTaskState` 带 `#[serde(rename_all = "camelCase")]`,枚举变体名被小写化(`"succeeded"`/`{"running":..}`/`{"failed":..}`/`"pending"`);前端按 PascalCase 比较(`st === "Succeeded"`、`"Running" in st`、`"Failed" in st`)→ **全部失配**。任务后台真实执行成功(有调用记录、产生费用),前端永不进成功分支 → 永久"排队中" → 结果永不取回/展示/采纳。30s 卡住检测同样失效(`st !== "Pending"` 恒 true)。 |
| 27 | +- **P0 附:失败原因契约错位**:`AiTaskFailure` camelCase 输出 `{"insufficientBalance":..}`/`{"invalidResponse":..}`,前端期待 `"balance"`/`"invalid"` → 引导文案错位。 |
| 28 | +- **P1 丢图**:规则草稿含 `- ` 配图行,精修协议仅 5 种文本块、无 image 类型 → 精修版配图全丢。 |
| 29 | +- **P1 成本失真**:估算只按输入字符;**无模型→单价映射**,切付费模型后确认弹窗仍显示 ¥0 → 实际扣费无感知。 |
| 30 | +- **P1 零审计**:精修/补充调用不上 `ai_audit_list`(仅余额/测试连接/复核有记录)→ REQ-140 轨迹可见化缺失。 |
| 31 | +- **P1 配额失控**:精修/补充不消费 DailyQuota;「记住此选择」后完全跳过确认 → 无限烧钱;无任务去重,双击/重进可重复扣费。 |
| 32 | +- **P1 切片上下文丢失**:多片独立请求,每片输出完整章节结构,合并仅 `\n\n` 拼接 → 章节标题重复、结构错乱。 |
| 33 | +- **P1 一片失败=全任务失败**:REQ-145「部分失败可单独重试」未实现。 |
| 34 | +- **P1 任务结果内存态**:任务注册表在 AppState 内存,重启即失。 |
| 35 | +- **已做对**:授权红线(enabled+authorized 双 gate)、DPAPI 密钥、余额查询+低余额提醒、schema 强校验、降级本地规则版保留、版本链+回滚+成本落库、事件+轮询双通道、闭包 ref 修复、trim_tasks 容量守卫。 |
| 36 | + |
| 37 | +### 约束条件 |
| 38 | +- 本地优先 + AI 为增强层;数据不出本机;AI 调用须用户授权且默认关闭(AGENTS.md 铁律) |
| 39 | +- Windows 桌面(Tauri 2 + Rust + React);单文件 ≤300 行;Rust 侧有 1271 测试基线 |
| 40 | +- 范围纪律:不铺开新 AI 能力(问答/VLM/闪卡仍排除),聚焦精修/补充一条线做深 |
| 41 | +- 网络环境:本机 TLS 拦截,新增 crate 依赖受限(DPAPI 先例) |
| 42 | + |
| 43 | +### 成功标准 |
| 44 | +1. 真机精修全流程走通:预估→确认→任务→diff→采纳落库,无"排队中"卡死 |
| 45 | +2. 精修版不丢配图;成本显示真实(模型单价映射);精修/补充调用上审计 |
| 46 | +3. 任务结果重启可恢复;完成通知可见;长笔记并发提速;单片失败可重跑 |
| 47 | +4. 协议 v2 向后兼容;结构回归评测全绿;现有 1271 测试不劣化 |
| 48 | + |
| 49 | +--- |
| 50 | + |
| 51 | +## 2. 发散阶段(用户裁决的扩展方向) |
| 52 | + |
| 53 | +| # | 方向 | 内容 | 裁决 | |
| 54 | +|---|------|------|------| |
| 55 | +| 1 | 修复优先 | P0 契约断裂 + P1 全量修复(丢图/成本/审计/配额/去重/切片上下文) | ✅ 全量纳入 | |
| 56 | +| 2 | 任务中心与可恢复性 | 任务记录持久化(SQLite)+ 结果重启恢复 + 完成通知 | ✅ 全量纳入 | |
| 57 | +| 3 | 并发与重试 | 切片并发 2-3 + 单片重试 + 任务去重 | ✅ 全量纳入 | |
| 58 | +| 4 | 成本控制完整化 | 模型→单价映射表 + 余额预校验硬拦截 | ✅ 硬拦截 + 单价映射 | |
| 59 | +| 5 | 质量评测基准 | golden 结构回归评测(内置小样本集,不联网) | ✅ 结构回归(模型对比/人工评分不做) | |
| 60 | +| 6 | 可观测性 | 精修/补充上审计 + 用量概览(任务表聚合) | ✅ 全量纳入 | |
| 61 | +| 7 | 全局任务面板 | 前端历史任务列表/查看结果/重跑 | ✅ 纳入 | |
| 62 | +| 8 | 架构取向 | 方案 C:任务中心化 + 协议 v2 + 评测框架全量重构 | ✅ 用户裁决 C | |
| 63 | + |
| 64 | +### 灵感触发(市场对照) |
| 65 | +- 通义听悟/Otter 系:异步任务 + 完成通知是标配 → 本次 B3 完成通知落地 |
| 66 | +- NotebookLM citation:AI 输出可溯源是信任核心 → 本次 image 块溯源 + 片间上下文防结构漂移 |
| 67 | +- llm-ux-patterns:token-cost transparency + quota fallback → 本次单价映射 + 硬拦截 + 配额接入 |
| 68 | + |
| 69 | +--- |
| 70 | + |
| 71 | +## 3. 收敛阶段(设计定稿) |
| 72 | + |
| 73 | +### 总体架构:任务中心化(方案 C) |
| 74 | + |
| 75 | +**核心洞察**:P0/P1 多数问题(契约断裂、零审计、无去重、结果易失、无重试)同根——任务注册表是无持久化的内存 HashMap。以「持久化任务中心」为支点,修复项与扩展项统一承接,一次架构动作解决全部问题。 |
| 76 | + |
| 77 | +### 3.1 修复层(F0 + F1) |
| 78 | + |
| 79 | +**A1 契约断裂修复(P0,ai_task.rs)** |
| 80 | +- `AiTaskState`:去掉 `rename_all = "camelCase"` → 变体名恢复 PascalCase(Pending/Running/Succeeded/Failed),字段保持 snake_case(finished_slices/total_slices)→ 与前端 types.ts 逐字匹配 |
| 81 | +- `AiTaskFailure`:保持 camelCase,显式 rename 两个变体:`InsufficientBalance → "balance"`、`InvalidResponse → "invalid"` → 与前端四类出口对齐 |
| 82 | +- **不动** `AiTaskHandle`(taskId camelCase 契约 2026-08-21 已修正确,勿改) |
| 83 | +- **新增契约快照单测**:断言 serde 序列化输出等于前端期望 JSON 字面量(本次事故教训,防再断裂) |
| 84 | + |
| 85 | +**A2 丢图修复(双层)** |
| 86 | +- 协议 v2 增加 image 块(见 3.3 C2) |
| 87 | +- 降级路径:本地合并——精修落库时把规则版 `- ` 配图行按章节锚点合并回精修版(协议未返回 image 块时启用,零模型成本) |
| 88 | + |
| 89 | +**A3 成本失真修复(ai_cost.rs)** |
| 90 | +- 新增模型→单价映射表(内置:免费档 ¥0、DeepSeek-V3/Qwen3-235B 实测价格;设置页可编辑;env 覆盖保留) |
| 91 | +- 预估 = 输入token×单价 + 输出token×单价(输出按输入比例系数粗估) |
| 92 | +- 确认弹窗/成本落库统一用映射表单价 → 消灭「切付费模型仍显示 ¥0」 |
| 93 | + |
| 94 | +**A4 审计补齐(commands_ai_refine / commands_ai_enrich)** |
| 95 | +- 任务完成/失败时 push_audit(op 类型 + task_id + 结果 + 成本)→ 本地审计列表可见精修/补充调用轨迹 |
| 96 | + |
| 97 | +**A5 配额接入 + 任务去重** |
| 98 | +- 精修/补充任务启动时消费 DailyQuota(按片计,耗尽 → 明确拦截引导) |
| 99 | +- 同 session/note 存在进行中任务时拒绝重复启动(防双击/重进重复扣费) |
| 100 | + |
| 101 | +### 3.2 任务中心(F2) |
| 102 | + |
| 103 | +**B1 SQLite 任务表(新表 ai_tasks,迁移)** |
| 104 | +- 字段:task_id, op_type(refine|enrich), ref_id(session/note), state, result_json, cost_yuan, elapsed_ms, model, error, created_at, finished_at |
| 105 | +- 状态流转与内存注册表同步写(锁内 read-modify-write;写库失败不阻断 AI 调用——内存注册表继续 + 明确日志,见 H2) |
| 106 | + |
| 107 | +**B2 结果恢复** |
| 108 | +- 应用启动时从 ai_tasks 恢复未采纳的成功结果 → 前端任务面板可见历史结果,重启不丢 |
| 109 | +- 保留策略:每类型保留最近 50 条,超限清理旧终态任务(防表膨胀) |
| 110 | + |
| 111 | +**B3 全局任务面板 + 完成通知** |
| 112 | +- 前端新增「AI 任务」视图(设置页 Tab 或独立面板):历史任务列表(时间/类型/状态/成本/模型)、查看结果(diff 预览复用)、重跑入口 |
| 113 | +- 完成通知:ai:task-update 事件全局监听 + 顶栏角标/提示(跨卡片生效,REQ-145「完成通知」真正落地) |
| 114 | + |
| 115 | +**B4 并发与单片重试** |
| 116 | +- 切片并发 2-3(配额计数并发安全改造——v0.8.0 开放问题「切片并发 vs 每日配额」一并解决) |
| 117 | +- 单片失败自动重试 1 次(幂等片)→ 仍失败则保留已成功片 + 标记部分成功(partial_failed:x/y 片成功,可重跑失败片;REQ-145 兑现) |
| 118 | + |
| 119 | +### 3.3 协议 v2(F3) |
| 120 | + |
| 121 | +**C1 版本化与向后兼容** |
| 122 | +- 响应增加 schema_version: u32(缺省=1,v2 显式=2) |
| 123 | +- 反序列化宽容:v1 响应(无 version 字段)仍按 v1 解析 → 旧任务结果/缓存不崩 |
| 124 | + |
| 125 | +**C2 image 块类型** |
| 126 | +- AiRefineBlockType 增加 Image:content 为本地配图引用(session-images/{sid}/{rel} 相对路径) |
| 127 | +- 提示词 v2(note_refine.json)输出约束增加 image 块说明:配图行必须原样保留为 image 块,不得改写路径 |
| 128 | +- 校验:content 非空、路径须匹配 session-images/ 前缀(防注入)、每节 image 块数 ≤ 5 |
| 129 | +- to_markdown 渲染为 `- ` 原行 → diff 预览/落库保留配图 |
| 130 | + |
| 131 | +**C3 片间上下文字段** |
| 132 | +- AiRefineRequest 增加:slice_index, slice_total, prev_summary(前片结尾 200 字摘要), next_summary |
| 133 | +- 提示词约束:本片只整理 content 对应内容,沿用 chapters 全局章节,禁止发明新章节标题 |
| 134 | +- 合并逻辑升级为「按全局章节归并」(同标题节合并块,不重复输出标题) |
| 135 | + |
| 136 | +### 3.4 成本拦截完整化(F3) |
| 137 | + |
| 138 | +**D1 启动前硬拦截** |
| 139 | +- 流程:预估(映射表单价)→ 查余额 → 余额 < 预估×安全系数1.2 → 拒绝启动 + 三出口引导(充值/切免费档模型/放弃) |
| 140 | +- 前端确认弹窗同步显示「余额不足将无法启动」提示 |
| 141 | + |
| 142 | +**D2 单价映射表** |
| 143 | +- 内置表:免费档 ¥0、DeepSeek-V3-0324/Qwen3-235B-A22B 实测价(未知模型回退 ¥0 + 警告「该模型单价未登记,费用可能不准确」) |
| 144 | +- 设置页「AI 服务」可编辑单价表(存 ai_settings.json);env SILICONFLOW_PRICE_PER_1M_TOKENS 仍可整体覆盖(开发路径) |
| 145 | + |
| 146 | +### 3.5 评测框架(F3) |
| 147 | + |
| 148 | +**E1 内置样本集(src-tauri/fixtures/refine_golden/)** |
| 149 | +- 3-5 个典型样本:网课讲义式(含术语/章节)、实操步骤式、口播摘要式、含表格+公式+配图场景(覆盖 image 块回归) |
| 150 | +- 每样本 = 输入(切片 markdown + profile + glossary + chapters)+ 期望断言(章节集合、配图保留、块类型合法、schema_version=2) |
| 151 | + |
| 152 | +**E2 回归测试(refine_golden_tests.rs)** |
| 153 | +- 走 mock 适配器全链路(不联网):mock v2 响应 → 协议校验 → to_markdown → 断言结构不漂移 |
| 154 | +- 提示词 golden 冒烟:固定样本断言输出结构(防提示词改动破坏契约) |
| 155 | + |
| 156 | +### 3.6 可观测性(F3) |
| 157 | + |
| 158 | +**G1** 任务表即用量数据源:聚合 SQL(按日/按类型:次数/费用/耗时/模型)→ 设置页「用量概览」小卡(不建完整报表页,YAGNI) |
| 159 | +**G2** 审计补齐后 ai_audit_list 统一展示全部 AI 调用轨迹 |
| 160 | + |
| 161 | +### 3.7 错误处理与测试总纲 |
| 162 | + |
| 163 | +**H1** 每层错误归一 AiTaskFailure(四类 + server/invalid),任务表记录 error 原文 |
| 164 | +**H2** 新边界:任务表写入失败 → 内存注册表继续(不阻断 AI 调用)+ 明确日志;恢复时以任务表为准 |
| 165 | +**H3** 单片重试仍失败 → partial_failed(保留已成功片,前端显示「部分成功:x/y 片,可重跑失败片」) |
| 166 | + |
| 167 | +**I1** 契约快照测试(A1,本次事故教训):serde 输出 = 前端期望 JSON 字面量 |
| 168 | +**I2** 协议 v2:image 块校验/片间字段/版本兼容单测 |
| 169 | +**I3** 任务中心:CRUD/恢复/保留策略/并发计数单测(内存库) |
| 170 | +**I4** 评测回归(E2):mock 全链路 golden |
| 171 | +**I5** 前端:无 Vitest 基建维持真机手动清单(契约测试已在 Rust 侧兜底) |
| 172 | +**I6** 回归:cargo test + cargo clippy 全绿;现有 1271 测试不劣化 |
| 173 | + |
| 174 | +--- |
| 175 | + |
| 176 | +## 4. 里程碑分层(一份设计,分批交付) |
| 177 | + |
| 178 | +| 里程碑 | 内容 | 验收 | |
| 179 | +|--------|------|------| |
| 180 | +| F0 修复快线(先行,可单独发版) | A1 契约断裂 + 契约快照单测 | 真机精修全流程走通(排队中→diff→采纳) | |
| 181 | +| F1 修复补齐 | A2-A5(丢图降级、成本映射、审计、配额去重) | 精修不丢图、成本显示真实、审计可见、无重复扣费 | |
| 182 | +| F2 任务中心 | B1-B4(任务表/恢复/面板/通知/并发重试) | 重启恢复结果、全局面板可用、完成通知可见、长笔记并发提速 | |
| 183 | +| F3 协议 v2 + 评测 | C1-C3 + D + E + G | 配图经 image 块保留、切片结构不乱、单价映射生效、回归全绿 | |
| 184 | + |
| 185 | +## 5. 行动计划 |
| 186 | + |
| 187 | +| 项 | 下一步 | 负责人 | 截止 | |
| 188 | +|----|--------|--------|------| |
| 189 | +| 设计文档 | 本文件落库 + commit | AI 代理 | 2026-08-21 | |
| 190 | +| F0 修复快线 | 契约修复 + 快照单测 + 真机验证 | AI 代理 | F0 门禁 | |
| 191 | +| F1 修复补齐 | 丢图降级/成本映射/审计/配额去重 | AI 代理 | F1 门禁 | |
| 192 | +| F2 任务中心 | ai_tasks 表/恢复/面板/通知/并发 | AI 代理 | F2 门禁 | |
| 193 | +| F3 协议 v2 + 评测 | image 块/片间上下文/单价拦截/golden | AI 代理 | F3 门禁 | |
| 194 | +| 真机验收 | 用户真机走通「精修→采纳→版本时间线」全链 | 用户 | 每里程碑 | |
| 195 | + |
| 196 | +## 6. 遗留问题 / 风险 |
| 197 | + |
| 198 | +- [ ] 输出 token 估算系数(输入→输出比例)需实测校准(golden 冒烟附带测量) |
| 199 | +- [ ] 切片并发 2-3 对 DeepSeek 长输出的实际提速效果待真机验证 |
| 200 | +- [ ] 单价映射表内置价格需按 2026-08 实测定档(免费档已验证 ¥0) |
| 201 | +- [ ] ai_tasks 表保留策略(50 条/类型)与磁盘占用实测 |
| 202 | +- [ ] 前端任务面板交互形态(Tab vs 独立面板)真机评审 |
| 203 | + |
| 204 | +## 7. 回顾 |
| 205 | + |
| 206 | +### 本次头脑风暴效果 |
| 207 | +- 产出:全链路检测报告(P0 契约实证 + P1 八项 + 已做对清单)+ 方案三选(用户裁决 C)+ 六轴扩展全量收敛 + 里程碑四层设计 |
| 208 | +- 关键转折:契约断裂的**实证**(临时单测实测 serde 输出)把「调用有记录但结果未使用」从现象变为根因;「任务注册表无持久化」洞察让修复与扩展统一到任务中心一个支点 |
| 209 | + |
| 210 | +### 改进建议 |
| 211 | +- 真机问题应先做契约层实证再发散(本次 serde 实测直接锁定根因,避免在错误方向上设计) |
| 212 | +- 扩展方向全量裁决时明确「不做」清单(模型对比/人工评分/完整报表页已排除),保持窄而深 |
0 commit comments