From 5c15b72d3df98f30f9fa45bf9a59c05dfbc5fdc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BB=9D=E5=B0=98?= <237809796@qq.com> Date: Fri, 19 Jun 2026 02:19:26 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=A1=E6=9F=A5=E5=9B=9E=E5=A1=AB:=20CR-04(F?= =?UTF-8?q?-09=E6=89=B92)+CR-05(batch5)=20PASS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - CR-04 batch2 双写桥接 PASS(⚪1 low:注释「共享同一Arc」误导,批4清顶层时清) - CR-05 batch5 llm_concurrency会话级 PASS(⚪1 WATCH修正:审批disarm+return不占global槽,prompt描述不准) - 待修项回流 todo: 无 独立 grep/read + cargo check --workspace 0 warning 核验 --- docs/待审查.md | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/docs/待审查.md b/docs/待审查.md index 8ec5b8b..79c89e5 100644 --- a/docs/待审查.md +++ b/docs/待审查.md @@ -736,6 +736,76 @@ - **⚪ WATCH-1**: F-09 批2 迁移进行中(agentic.rs/commands.rs/audit.rs/conversation.rs/title.rs/knowledge_inject.rs 调用方迁移 + 顶层字段降级死字段),批2 完成后需独立审查(范围:调用方迁移完整性 + 顶层字段是否真成死字段 + cargo test 单会话全功能回归)。批1 本身 PASS 不受批2 影响。 - **待修项回流 todo**: **无** 🔴/🟡 项(批1 范围内) +### CR-260619-04 F-09批2 调用方迁移per-conv·双写桥接(agent f09-batch2·主代严格兜底·commit 2a4d745) — ✅ 已审(PASS·⚪1 low·独立 grep/read 核验) + +- **范围**(8 文件):agentic.rs(GeneratingGuard conv_id + 三处退出校验改 conv 存在性 `!per_conv.contains_key` 决策e + stop_flag/notify/messages/iteration per_conv + try_continue conv_id 参数)+ commands.rs(IPC 写路径双写桥接:per_conv 新真相源+顶层双写,批4删)+ audit/conversation/title/knowledge_inject/lib.rs(per_conv 读写迁移)+ mod.rs(conv/conv_read 去 allow)。 +- **共存期双写**:per_conv 主 + 顶层双写(兼容批4前 IPC 读路径 ai_is_generating 读 per_conv fallback 顶层)。 + +**复审结论(2026-06-19·独立 grep/read 核验源码当前形态·稳定态 commit d960417·cargo check 0 warning)**: ✅ **PASS** — 🔴0 🟡0 ⚪1 + +**逐项核验表(file:line 佐证 + 判定)**: + +| 项 | 核验点 | 佐证 | 判定 | +|---|---|---|---| +| A-GeneratingGuard | guard 持 conv_id + reset/Drop 双写 per_conv+顶层 | `agentic.rs:79-84` struct{session,conv_id,done} · `:94-102 reset()` 双写 `session.conv(conv_id).generating=false` + `session.generating=false` · `:112-124 Drop` spawn 双写 · `:107-109 disarm()` 审批等待 return 前置 done=true 跳过复位(B-260615-26 续生成修复保留) | ✅ | +| B-三处退出校验 | conv 存在性 `!per_conv.contains_key` 决策e | `agentic.rs:552`(loop 入口切换检测) / `:875`(MidStream 保文前) / `:926`(stream 后 push 前)三处均 `if !session.per_conv.contains_key(&conv_id) { warn + return }`,语义一致防 push 写回已删 conv 污染。**prompt 声明 :541/864/917 略漂移,实为 :552/875/926(注释块行号差)** | ✅ | +| C-双写一致性 | commands 写路径全双写 per_conv+顶层 | `commands.rs:227-265` ai_chat_send `session.conv(&active).*`(generating/stop_flag/iteration_used/model_override/messages)+ 同锁内 `session.*` 顶层双写 · `:98-114` ai_regenerate · `:760-777` ai_edit · `:882-930` force_send 双写复位 old+顶层+占用 new · `:1020-1045` ai_chat_stop 双写 stop_flag + `:1042-1046` notify per_conv(else 顶层) · `:1117-1160` ai_stop_loop 双写 | ✅ | +| D-读路径fallback | ai_is_generating/各读 per_conv fallback 顶层 | `commands.rs:175-180` ai_is_generating `session.conv_read(&active).map(c.generating).unwrap_or(session.generating)` · `:130` regenerate 读 messages fallback · `:214/752` generating 拦截 fallback · audit/conversation/title/knowledge_inject 各 conv_read 后 `unwrap_or(顶层)` | ✅ | +| E-conv_id上下文 | loop 入参/IPC param/approval.conversation_id/try_continue 显式参数 | `agentic.rs:342` run_agentic_loop(conv_id:String)入参 · `:352` guard.new(conv_id.clone()) · `:492-495` stop_flag/notify 取 per_conv(conv_id 同源) · `:520-521` acquire_per_conv(&conv_id) · `commands.rs:385/488` try_continue 传 approval.conversation_id · `agentic.rs:1080-1083` try_continue(conv_id:&str)显式入参 · `:1099-1100` has_pending 按 conv_id 过滤(决策e 真并发准备) · `:1216` 续跑传 conv_id_owned | ✅ | +| F-stop_flag/notify互通 | loop 取 per_conv Arc.clone + IPC 写 per_conv(双写互通) | `agentic.rs:492-495` `(conv.stop_flag.clone(), conv.notify.clone())` 取 per_conv Arc · `commands.rs:1023/1035/1026/1037` ai_chat_stop **双写** per_conv.stop_flag + 顶层 stop_flag(行为互通非依赖共享 Arc) · `:1042-1046` notify 走 per_conv.notify.clone()(同源 loop 监听的 per_conv Arc)→ stream_llm select! 监听同源 notify_one 互通 | ✅(见 low) | +| G-conv/conv_read去allow | 批2 迁移后访问器去 allow | `mod.rs:427 conv` / `:437 conv_read` 均**无 #[allow(dead_code)]**(已消费);仅 `:541 PerConvState struct` 仍标 allow(SW-260618-21 b 预留/批2 字段全活跃后 allow 注释待清,非阻塞) | ✅ | +| H-audit迁移 | process_tool_calls per_conv 读写 | `audit.rs:464` conv_read 取 messages 缓存 · `:640` conv_read 读 session_trust · `:678/708/769/823` conv() 写 tool_result messages · conv_id 来源函数入参(本 conv 工具调用) | ✅ | +| I-conversation/title/knowledge/lib迁移 | 各模块 per_conv 读写 + fallback | `conversation.rs:154` conv_read fallback 顶层 messages · `title.rs:50` conv_read fallback(switch 触发 spawn 可能无 per_conv) · `knowledge_inject.rs:257` conv_read fallback 顶层 messages.len() · `lib.rs:43-54` L0 兜底读 per_conv.generating fallback 顶层 + 双写复位(启动残留 generating 清理) | ✅ | +| J-编译/测试 | cargo check 0 warning | `cargo check --workspace` EXIT 0 **0 warning**(Finished in 0.64s,无 dead_code 新增——批2 字段全活跃) | ✅ | + +**对抗核验印证**: +- **三处退出校验语义一致?** ✅ 三处 `!per_conv.contains_key(&conv_id)` + warn + return,防 push 写回已删 conv 污染他人(决策 e 真并发准备)。conv_id 来源同源(run_agentic_loop 入参),无串台风险。 +- **双写覆盖所有写路径?** ✅ grep commands.rs 全部 `session.conv(` 写路径后均有对应顶层 `session.*` 双写(generating/stop_flag/messages/iteration_used/model_override/agent_language),批4 IPC 迁移后顶层移除(注释明确标注批4)。 +- **stop_flag/notify 是否真共享同一 Arc?** ⚠️ **注释不准确但行为正确**:`PerConvState::new`(mod.rs:576-588)创建**全新** `Arc::new(AtomicBool)`/`Arc::new(Notify)`,commands.rs ai_chat_send 入口 `session.conv(&active)` 惰性创建新默认 PerConvState(**未搬入顶层 Arc**)。注释 agentic.rs:490/commands.rs:1041 声称「入口桥接时顶层 Arc 直接搬入」「共享同一 Arc」**与实现不符**。但**行为正确**:ai_chat_stop 双写 per_conv.stop_flag + 顶层 stop_flag(:1035/1037),loop 读 per_conv.stop_flag 被命中;notify 走 per_conv.notify.clone()(:1042)与 loop 监听同源。双写兜底保证互通,非依赖共享 Arc。详见 low-1。 +- **try_continue conv_id 解耦正确?** ✅ R-PD-6:审批等待态 active_conversation_id 可被切走,try_continue 用 approval.conversation_id 显式入参(:1083)+ has_pending 按 conv_id 过滤(:1099),不读全局单例。续跑 run_agentic_loop 传 conv_id_owned(:1216)。 +- **单会话全功能回归?** ✅ send(ai_chat_send 双写占用+spawn)/stop(ai_chat_stop 双写 stop_flag+notify)/approve(commands.rs:385 try_continue 传 approval.conversation_id)/switch(active_conversation_id 切换 + 旧 loop conv 存在性退出)/regenerate/edit/force_send 全路径双写,行为等价批1 前单 active 场景。 + +**⚪ low(可选·非必修)**: +1. **stop_flag/notify「共享同一 Arc」注释误导**(`agentic.rs:490`/`commands.rs:1041`/`mod.rs` 相关注释):声称 per_conv.stop_flag/notify 与顶层共享同一 Arc(入口桥接搬入),实际 `PerConvState::new` 新建 Arc,commands.rs 入口未搬入。**行为正确**(双写 + 同源 per_conv Arc clone 互通),仅注释与实现不符。建议注释改为「双写兜底互通(loop 取 per_conv Arc.clone,IPC 写 per_conv 同源),非共享同一 Arc 实例」,或批4 IPC 迁移后顶层 stop_flag/notify 删除时一并清注释。 + +- **关联**:F-09 阶段2 批2。 +- **待修项回流 todo**: **无** 🔴/🟡 项(⚪1 low 注释修正可选,非阻塞,批4 清顶层字段时一并处理) + +### CR-260619-05 F-09 batch5 llm_concurrency会话级(决策c)(agent f09-batch5·主代兜底·commit d960417) — ✅ 已审(PASS·⚪1 WATCH 修正·独立 grep/read 核验) + +- **范围**(7 文件):state.rs(per_conv HashMap + per_conv_permits AtomicUsize 热改 + acquire_per_conv(conv_id) + release_conv + set_per_conv 软收敛)+ agentic.rs(loop 入口 acquire_global+acquire_per_conv(conv_id) 整 loop 持有,会话级并发上限 3)+ compress/title/knowledge_inject(签名 conv_id + acquire_per_conv)+ project.rs(扫描合成 key)+ commands.rs(ai_conversation_delete release_conv)。 +- **核心**:global LLM 调用并发→并发会话数上限(每 loop 1 permit 整 loop);per_conv 单信号量→HashMap(每对话 permits=2);F-260616-12 retry permit 自洽。 + +**复审结论(2026-06-19·独立 grep/read 核验源码当前形态·稳定态 commit d960417·cargo check 0 warning)**: ✅ **PASS** — 🔴0 🟡0 ⚪1 + +**逐项核验表(file:line 佐证 + 判定)**: + +| 项 | 核验点 | 佐证 | 判定 | +|---|---|---|---| +| A-global语义 | 会话级·每 loop 入口拿 1 permit 持整 loop | `state.rs:116-117` global:Arc>> · `:116` 注释「permits=默认 3,run_agentic_loop 入口拿 1 持整 loop」· `:143-146` acquire_global lock+clone Arc+acquire_owned · `agentic.rs:520` `let _conv_global_permit = llm_concurrency.acquire_global().await` **在 for iteration 循环外**(loop 入口),整 loop 持有含工具执行/审批等待/重试,函数返回 Drop 释放 | ✅ | +| B-per_conv HashMap | 取/建/清理 + permits 热改 | `state.rs:120` per_conv:Arc>>> · `:125` per_conv_permits:Arc(热改) · `:150-158` acquire_per_conv lock HashMap → or_insert_with(permits 当前值)→ clone Arc → 释放 lock → acquire_owned · `:165-167` release_conv lock+remove · `:178-182` set_per_conv store AtomicUsize + clear HashMap(软收敛) | ✅ | +| C-release_conv时机 | 仅 ai_conversation_delete 调用(conv 删即清) | `commands.rs:1737` `state.llm_concurrency.release_conv(&conversation_id).await` 在 ai_conversation_delete 内 · `:1726` per_conv.remove 同步 · `:1736` drop(session) 再取 LlmConcurrency 锁(避免锁序) · 注释 :1731-1735 说明 loop 正常收敛/达 MAX/stop 但 conv 未删不清理(复用限流计数) | ✅ | +| D-F-260616-12 retry | retry 同 loop 内持 per_conv 合理 | `agentic.rs:521` acquire_per_conv(&conv_id) loop 入口 · `:512-519` 注释明确「retry 同 loop 内,持 per_conv 合理;global 是会话级,retry 不再阻塞他对话(原每轮 acquire/drop 语义下 global 短暂释放,现整 loop 持有更贴合会话级并发上限语义)」 · retry 在 stream_one_provider 内(:236 重试),同 loop 同 conv,permit 不释放正确 | ✅ | +| E-两HashMap独立 | AiSession.per_conv(状态) vs LlmConcurrency.per_conv(限流) | `mod.rs:357` AiSession.per_conv:HashMap(会话状态真相源) · `state.rs:120` LlmConcurrency.per_conv:Arc>>>(限流槽) · 两者独立 HashMap 无耦合 · 批2 双写 AiSession(per_conv 状态+顶层桥接)与 batch5 限流无冲突 | ✅ | +| F-set_per_conv软收敛 | 热改 permits + 清 HashMap | `state.rs:174-182` set_per_conv store AtomicUsize + clear HashMap · 注释 :175-177「旧 permit 随 Drop 释放,新对话 acquire 用新 permits 重建 Semaphore;已建对话若仍在跑,旧 Semaphore 不变,下次该 conv 新 acquire 时因 HashMap 已清空会重建为新 permits」 · 软收敛语义正确(旧 permit 不抢占) · `commands.rs:1886` ai_set_concurrency_config 调 set_per_conv | ✅ | +| G-compress/title/knowledge接入 | 签名 conv_id + acquire_per_conv | `compress.rs:85` acquire_per_conv(conv_id) · `title.rs:190` acquire_per_conv(conv_id)(注释 :188 per_conv 改 HashMap,标题针对本对话) · `knowledge_inject.rs:393` acquire_per_conv(conv_id)(注释 :391 知识提炼针对本对话) · 三处共享同 conv 限流槽(permits=2 容纳主 loop + 1 后台) | ✅ | +| H-project合成key | 扫描用 "__project_scan__" 合成 key | `project.rs:553/649` acquire_per_conv("__project_scan__") 合成 key · 非真实 conv_id 但统一限流(扫描任务共享一槽,防并发扫描失控) | ✅ | +| I-编译/测试 | cargo check 0 warning | `cargo check --workspace` EXIT 0 **0 warning** | ✅ | + +**对抗核验印证**: +- **global 整 loop 持有(含审批等待)?** ✅ acquire_global 在 for iteration 外(:520 loop 入口),审批等待 return(:991)/收敛/达 MAX/stop 各 return 点函数返回,_conv_global_permit Drop 释放槽位。 +- **审批等待占 global 槽?** ⚠️ **修正 prompt WATCH⑥**:审批等待路径(:988-991)disarm guard + `return`,函数返回释放 _conv_global_permit/_conv_per_conv_permit。**审批等待期间不占 global 槽**,续跑 try_continue → run_agentic_loop(:1216)重新 acquire。prompt WATCH⑥「审批等待占 global 槽阻塞他对话」**描述不准确**,实际审批等待释放槽位不阻塞他对话——这是**合理设计**(审批等待是用户交互态,不应占并发槽)。详见 WATCH-1。 +- **per_conv permits=2 容量够?** ✅ 主 loop stream_llm(1)+ 后台标题/压缩/提炼(各 1)同 conv 共享 permits=2。常态主 loop 持 1 + 至多 1 后台任务(标题或压缩或提炼之一)持 1 = 2 满载,第 3 个后台任务 await 排队。单对话内并发上限 2 合理(防失控)。 +- **release_conv 仅 delete 无残留?** ✅ ai_conversation_delete(:1737)conv 删即清 LlmConcurrency.per_conv 条目。已持 permit 不受影响(permit 绑旧 Arc 随 Drop 释放),仅阻止新条目累积防 HashMap 无限增长。loop 正常收敛但 conv 未删时不清理(下次发消息复用,限流计数连续)。 +- **set_per_conv 清 HashMap 是否丢正在跑对话的限流?** ✅ 软收敛:已建对话旧 Semaphore 不变(permit 绑旧 Arc),HashMap 清空仅影响新 acquire(重建为新 permits)。旧 Semaphore 随最后 permit 释放而 drop。无丢限流风险。 +- **与批2 双写兼容?** ✅ batch5 仅改 LlmConcurrency(state.rs)+ 接入点 acquire/release,不动 AiSession.per_conv 状态结构。两 HashMap 独立,批2 双写 AiSession(per_conv 状态+顶层)与 batch5 限流无耦合。cargo check 0 warning 印证兼容。 + +**⚪ WATCH**: +1. **审批等待占 global 槽(修正)**:prompt WATCH⑥ 声称「审批等待占 global 槽阻塞他对话(决策c有意)」**描述不准确**。实际审批等待路径(agentic.rs:988-991)disarm guard + return,函数栈帧退出释放 _conv_global_permit,**审批等待不占 global 槽**。续跑 try_continue→run_agentic_loop 重新 acquire。这是合理设计(审批是用户交互态不占并发槽)。**无问题**,仅修正 prompt 描述。另:达 MAX 未收敛转暂停态(:1003- 路径)同样 return 释放槽位,续跑 ai_continue_loop→try_continue→run_agentic_loop 重新 acquire,一致。 + +- **关联**:F-09 阶段2 batch5(决策c-1 落地)。 +- **待修项回流 todo**: **无** 🔴/🟡 项(WATCH-1 为 prompt 描述修正,非代码问题) + --- ## 已审归档