重构: AI聊天可靠性批次(审批累计计数/write_file diff预览/会话隔离补清/token缓存)

This commit is contained in:
2026-06-16 19:56:29 +08:00
parent ba7f35552b
commit d00b30f0ba
12 changed files with 238 additions and 45 deletions

View File

@@ -23,11 +23,35 @@
## 当前队列
### CR-260616-35 batch59 CR-34-1 守卫 + CR-30 流式重试对齐决策a1 + UX-2025-04 断线保文 — 待审
### CR-260616-36 batch60 F-09A 隔离补清 + F-13 性能优化 + AE-03 write_file diff 预览(路径B) — ⏳ 待审
- **范围**: 3 agent 合并批(workflow whae812z5,主代独立核查全过 + cargo check --workspace EXIT 0 + vue-tsc EXIT 0)。①**F-09 A 路线**(单例软隔离补漏清字段,不动 B 多会话架构):前端 `useAiConversations.ts:42-45` newConversation 补清 `queue=[]/generatingConvId=null/agentRound=0/searchQuery=''`(防旧会话排队消息带进新会话 + 后台事件错路由 + 侧栏旧搜索过滤);后端 `commands.rs:842-843` ai_conversation_create 补清 `stop_flag.store(false,SeqCst)`(关键:上方生成中分支置 true 停旧 loop 不复位则新会话 loop 启动即见 stop 退出) + `agent_language=None`(防沿用旧语言设置)。②**F-13 性能**:`agentic.rs:173` system_prompt token loop 外算一次缓存为 sys_tokens(整个 loop 不变参数,原每轮+每次重试重算);`:219-225` 外层 messages 构建用 sys_tokens;`:262-270` 重试循环改 `messages: messages.clone()` 复用外层 Vec(删每次重试的 session_arc.lock + build_for_request 全量 clone + estimate_text)。安全前提核验:stream_llm 不接 session_arc + 重试块到 process_tool_calls 间无 push,重试内 session.messages 与外层一致,重建等价复用。③**AE-03 路径 B**(审批 payload 加 diff,非路径 A):`tool_registry.rs:32` generate_diff fn→pub(crate)(复用 F-260615-10 LCS);`audit.rs:508-520` 新增 build_write_file_diff(从 args 取 path+content → 预读旧文件 tokio::fs::read_to_string → 无变化/旧文件缺失/读失败均 None → generate_diff);`audit.rs:588-609` 仅 write_file draft 挂起审批前预计算 approval_diff 注入 PendingApproval.diff(:600 clone)+AiApprovalRequired.diff(:609 move);恢复路径 diff=None(文件可能已变);`mod.rs:101/269` AiApprovalRequired+PendingApproval 加 `diff: Option<String>`;`types.ts:198` AiApprovalRequired 加 `diff?: string`;`useAiEvents.ts` event.diff 传 toolCall 信息;`ToolCard.vue:40-41` 模板 `v-if="tc.diff"` diff 块 + :394 diffLines computed(按 +/-/space 前缀拆分)+ :677-705 样式(add 绿 del 红 ctx 灰 + token 复用)。
- **维度**: ①F-09 单例字段补清完整性(stop_flag 复位是否覆盖所有生成中→新建路径/agent_language 影响面) + B 路线边界(确认未误动 AiSession 单例) ②F-13 行为不变性核验(sys_tokens 缓存是否真不变/messages.clone() 复用是否等价重建/stream_llm 不持 session_arc 前提是否成立) + 锁持有期缩短收益确认 ③AE-03 路径 B 安全性(预读旧文件只读不改/审批拒绝不执行 handler/路径不校验风险——恶意路径读失败仅 None) + diff 注入完整性(实时挂起+恢复路径双覆盖/前端消费链 event→toolCall→模板) + generate_diff pub(crate) 暴露面。
- **commit**: 待提交(累积 ~12 文件攒批 + 4 决策回填)。
### CR-260616-37 F-11 审批续跑 iteration 累计计数(跨审批不重置) — ⏳ 待审
- **范围**: F-260616-11 决策a 落地(与 batch60 三项合批 commit)。`mod.rs:215/230` AiSession 加 `iteration_used:usize` 字段+new() init 0;`agentic.rs:114` run_agentic_loop 加 `start_iteration:usize` 参数 +`:176` loop 边界改 `start_iteration..max_iterations` +`:210` 一致性校验块更新 `session.iteration_used=iteration+1` +`:578` try_continue_agent_loop 加 start_iteration 参数 +`:675` spawn 透传 +`:499-501` 注释区分两路径;`commands.rs:55/152/412` ai_chat_send/ai_regenerate/ai_edit_last reset iteration_used=0(新生命周期)+`:260-262` ai_approve 拒绝续跑读 session.iteration_used 累计传 +`:313-315` ai_approve 通过续跑累计传 +`:601` ai_continue_loop reset iteration_used=0 传 0(F-03 决策a 达max重计区分)。两路径严格区分:审批续跑(ai_approve 累计透传 iteration_used) vs 达max续跑(ai_continue_loop reset+传0,F-03 决策a 用户授权重来) vs 新消息(reset 0)。
- **维度**: ①iteration_used 字段生命周期完备性(所有会话生命周期入口 reset 覆盖——create/send/regenerate/edit_last/continue_loop 是否齐全无遗漏路径) ②start_iteration 透传链完整(agentic run_agentic_loop←try_continue_agent_loop←ai_approve 两个调用点,无断链) ③两路径区分正确性(ai_approve 累计 vs ai_continue_loop 重计,F-11/F-03 决策 a 分别归属无混淆) ④边界 case(start≥max loop 空区间→converged=false→AiMaxRoundsReached,防无限审批烧 token,符合 F-11 语义) ⑤并发安全(iteration_used 在一致性 lock 块更新,与现有 lock 模式一致)。
- **commit**: 待提交(同 CR-36 合批)。
- **主代独立核查**: ✅ 全过(useAiConversations:42-45 补清 / commands:842-843 stop_flag.store(false)+agent_language=None / agentic:173 sys_tokens loop 外缓存 + :219-225 外层用 + :262-270 重试 messages.clone() 复用 + 保文退避逻辑未动(Partial 不重试/retry_deadline 30s) / tool_registry:32 generate_diff pub(crate) / audit:508-520 build_write_file_diff 预读+无变化/缺失 None+复用 generate_diff / audit:588-609 仅 write_file 生成 diff 注入 PendingApproval(clone)+AiApprovalRequired(move) / mod:101+269 diff 字段 / types:198 diff? / ToolCard:40-41 模板 + :394 diffLines + :677-705 样式 / cargo+vue-tsc 双 EXIT 0)。
- **审查 agent 待复审重点**: ①F-13 重试 messages.clone() 复用等价性——核验 stream_llm 签名(stream_recv.rs:137 不接 session_arc) + 重试块到 process_tool_calls 间确实无 session.messages.push(若遗漏则重试复用旧 messages 致 tool_calls 丢失) ②F-13 sys_tokens 缓存——核验 system_prompt 确为 run_agentic_loop 不变参数(整个 loop 期间无 mutate) ③AE-03 build_write_file_diff 安全——预读旧文件 `tokio::fs::read_to_string(path)` 路径未校验(恶意 path 读失败仅 None 不产生危害,但核验无 symlink 逃逸读敏感文件风险——审批只读,write_file handler 自身 validate_path 执行时兜底) ④AE-03 diff 注入双路径——实时挂起(audit:588-609 有 diff)+恢复路径(PendingApproval diff 字段是否持久化,若仅内存则重启恢复审批无 diff) ⑤文件锁独立性——3 agent 改动文件无重叠(F-09: useAiConversations/commands / F-13: agentic / AE-03: mod/tool_registry/audit/types/useAiEvents/ToolCard),useAiEvents.ts Agent C 独占(A/B 未碰)确认无冲突。
---
### CR-260616-35 batch59 CR-34-1 守卫 + CR-30 流式重试对齐决策a1 + UX-2025-04 断线保文 — ✅ 已审(PASS)
- **范围**: 3 agent 合并批(workflow wdlt0xfl6,主代独立核查全过)。①**CR-34-1** useAiPanel.ts:102 守卫加有效性检查 `|| !list.some(p=>p.id===activeProvider)`(deleted provider 残留回落 DB is_default) ②**CR-30-1/CR-30-2 + UX-2025-04 合并**:stream_recv.rs `StreamResult` enum(Complete/Partial/InitFailed 三分支) + `classify_status_or_class`(4xx Fatal/5xx+429+timeout Retryable 镜像 retry::is_status_retryable) + agentic.rs 重试对齐决策 a1(Init 失败重试复用 retry::backoff_delay 1s→2s→4s±20%jitter + 30s 总预算 retry_deadline + Fatal 分类 4xx 立即放弃; MidStream Partial 不重试保文→入库 + AiCompleted(incomplete=true) + 系统提示「响应因网络中断不完整」) + 删 3 处误导注释(agentic.rs:12/commands.rs/state.rs) + retry.rs `pub mod` + `backoff_delay`/`is_status_retryable``pub` 暴露供 src-tauri 复用 + useAiEvents.ts:177 补 `case 'AiStreamRetry'`(消除死事件穿透) + AiCompleted case 处理 incomplete + types.ts:204/mod.rs `incomplete?:boolean` 字段(所有 emit 点补 None 向后兼容) + i18n aiStreamRetry/responseIncomplete 双语。
- **维度**: 流式核心中风险(重试循环正确性/保文语义/跨 crate pub 暴露影响面) + 前端事件消费完整性 + 决策 a1 对齐度(Init 可重试 ≤max_retries+jitter+30s+Fatal, MidStream 不重试) + B-260615-05 保留(空文本无 finished 仍 InitFailed 交重试)。
- **commit**: 待提交(累积 ~30 文件攒批)。
- **复审结论(2026-06-16·CR-30 agent 亲跑 cargo + 主线程 CR-34-1 预核验)**: ✅ **PASS** — 🔴0 🟡0 ⚪0
- **验证**: `cargo check --workspace` EXIT 0(5 warning 全 pre-existing dead_code)/ `cargo test -p df-ai retry` **6 passed**(backoff_is_monotonic_within_jitter/status_classification/fatal_aborts_immediately/first_attempt_ok/retry_once_then_ok/all_retryable_exhausts)/ `cargo test stream_recv` **20 passed**(classify_429_retryable/classify_4xx_fatal 等)。
- **CR-30-1(7 项全 PASS)**:agentic.rs:320 真用 `retry::backoff_delay(retry_attempt+1)`(非自写,旧 `1<<retry_attempt` 仅留注释)/ backoff 含 ±20% jitter(retry.rs:74-85 SystemTime nanos)/ 30s 总预算(agentic.rs:242 retry_deadline + :290 budget_exhausted + :321 min 截断)/ 4xx Fatal 立即放弃(agentic.rs:293 `!retryable` return + stream_recv.rs:411-434 classify_status_or_class 镜像 is_status_retryable)/ 三处注释修正(commands.rs:1114 / state.rs:184 / agentic.rs:38)/ DRY 无重写(retry_with_backoff 仍 pub(crate) 流式不走)/ AttemptOutcome+is_reqwest_error_retryable 暴露合理。
- **CR-30-2(7 项全 PASS)**:useAiEvents.ts:177-186 `case 'AiStreamRetry'` 完整(取最后 isError 消息 + i18n t 更新「重试 n/m」,break 非穿透)/ 看门狗 reset 正确(AiStreamRetry 不在 NO_RESET_WATCHDOG Set)/ MidStream 决策 agentic.rs:274-285 Partial→incomplete=true + break 不重试,:286-342 InitFailed retryable 分支重试 / currentText 清空(useAiEvents.ts:316 AiError case `state.currentText=''` 再 AiStreamRetry 更新气泡,无累积重复)/ AiCompleted incomplete 系统提示(后端 agentic.rs:384 push session.messages 落库 + 前端 useAiEvents.ts:278-285 气泡)/ types.ts:215-217 variant + :202-204 incomplete?:boolean / i18n aiStreamRetry+responseIncomplete 双语齐。
- **回归(4 项无回归)**:permit RAII 释放(agentic.rs:405-406 显式 drop + loop 作用域 Drop)/ guard.reset 耗尽复位(8 exit 点全覆盖 :137/:300/:314/:352/:393/:457/:480/:543)/ request 每轮重建(:251-266 messages 从 session lock 重构)/ 无新死代码(AttemptOutcome/is_reqwest_error_retryable/is_status_retryable/backoff_delay/AiStreamRetry/classify_status_or_class 全有消费)。
- **CR-34-1(主线程预核验 PASS)**:useAiPanel.ts:102 守卫 `|| !list.some(p=>p.id===activeProvider)` 残留无效回落 DB is_default,cycleProvider 不受影响(findIndex 不再 -1 跳过 ps[0])。
- **UX-2025-04 断线保文**:核心 ✅(MidStream Partial 保文入库 + incomplete 系统提示);「继续生成」按钮未做(可复用 UX-2025-03 重试能力按需补,非阻断)。
- **待修项回流 todo**: **无**(CR-34-1/30-1/30-2 三条确认修复,todo 勾完成)。
- **主代独立核查**: ✅ 全过(useAiPanel:102 守卫 / stream_recv:97 enum 三分支 + 注释 / agentic:242 retry_deadline + :274 Partial 不重试 + :286 InitFailed 重试 + :320 backoff_delay 复用 + :360 if incomplete 保文入库 + :400 emit incomplete:Some(true) / useAiEvents:177 AiStreamRetry case / types:204 incomplete / retry.rs:62+74 pub + agentic:14 `use df_ai::retry` 跨 crate 通)。
- **审查 agent 待复审重点**: ①stream_recv StreamResult 三分支边界(Partial vs InitFailed 空文本路径,B-260615-05 保留点) ②agentic 重试循环 permit RAII 释放/边界(max_retries=0/guard.reset) ③跨 crate retry pub 暴露是否过度(retry_with_backoff 仍 pub(crate)) ④AiCompleted incomplete 前端消费完整性(系统提示气泡镜像后端) ⑤UX-2025-04「继续生成」按钮**未做**(保文核心 ✅,按钮可复用 UX-2025-03 重试能力或按需补)。