文档: 三路走查报告与编排经验蒸馏

This commit is contained in:
lxy
2026-10-03 15:35:37 +08:00
parent 2d43f90e50
commit 0b57126ac5
3 changed files with 270 additions and 7 deletions
@@ -0,0 +1,75 @@
# 多代理流水线实战经验蒸馏(2026-10-03)
> 来源:上下文管理任务两天接力(10-02 至 10-03),13+ 代理批、多次会话重启、跨会话协调实战。
> 四个角度:编排 / 技术 / UX / 流程。每条都是实战检验过的,非理论推演。
## 一、编排角度
### 1.1 派工颗粒 = 一轮自证
单代理任务必须一轮内带验收证据(命令+结果)回来;超一轮=颗粒过大拆批。实测 13+ 批无一超轮,关键是任务书里给足锚点(文件:行 或 符号名)+ 明确禁改清单 + 验收命令。
### 1.2 文件零交集矩阵
派工前用 grep 把每个子项的**真实落点文件**定位出来(不是按想象的功能域),按文件分组派路:
- 同文件子项 → 合并给同一路(串行在同代理内)
- 跨路文件 → 各自禁改清单写死
- 实测:「结构不符需请示」类中断仅 2 次,均为锚点与实际结构偏差(不是互踩)
### 1.3 双碰文件错峰授权
两路先后都要碰同一文件(如 AiChat.vue):前路**收口确认后**,主代理向候路代理 SendMessage 显式解锁。不预共享、不口头约定。候路代理遇未授权文件主动请示而非越界。
### 1.4 主代理独立核查三件套
子代理自验 ≠ 完成。主代理必做:
1. **全量测试**:`cargo test --workspace`,与基线计数对比(防「新测试绿了旧测试红了」)
2. **锚点实锤**:报告声称的每个 文件:行号 grep 验证存在且语义吻合
3. **语义抽查**:关键设计裁决读一眼(如「删除侧单点 emit 覆盖 9 出口」——判断是否真比逐处 patch 优)
### 1.5 勘察反转:零命中 ≠ 空白
S2 勘察反转案例:sub_agent.rs grep "approval" 零命中,看起来是「审批语义空白=安全债」;实际是审批以 `deny` 命名的 fail-closed 双层防线(Medium/High 一律就地拒绝)。**「缺失」类结论必须勘察先行再定性**,否则会为伪需求开三批工。
### 1.6 残留进程判活
会话重启后接手:45s 双 stat 观察 mtime 是否推进 + 特征锚点 grep(目标改动是否在增长),确认无活跃写入者再动手。重启通知 ≠ 代理死亡(前代教训)。
## 二、技术角度(代码库层面的可复用裁决)
### 2.1 单点覆盖优于逐处 patch
删除族终态事件:与其在 9 处 loop 早退路径逐个补 emit(每处还要区分「已删」与「接管」),不如在删除侧单点补发——epoch 递增使所有早退路径自然被守卫压制。**先找不变量(owner 守卫),让分支自然消失**。
### 2.2 兼容性设计的「缺席不占位」
tool_signature 补参:补参键不在 args 时签名串与旧格式**逐字一致**——8 处既有逐字断言零破坏。改共享格式前先 grep 逐字断言消费方。
### 2.3 字面任务书会杀人
execute_with_heartbeat「不在 pending 即退出」按字面实现会腰斩**全部已批准执行**(现网先 remove 后 execute)。入口快照(was_pending_at_entry)守卫救场。派工书描述机制时,代理必须先读现场调用方再动手。
### 2.4 测试锁死旧行为的连带改写
修 bug 时若既有测试断言的正是 bug 行为,改写测试是**必要偏离**——但要如实报告(sanitize 盲区修复改写 1 测试)。反例警示:测试与 helper 失配的半成品测试(v21 断点续迁)会带病潜伏。
### 2.5 fail-closed 是底线
子代理 Medium/High 工具一律就地拒绝(零挂起零放行)被证明是正确架构——「拒绝」可以后补结构化与报告协议,但「放行」收不回来。
## 三、UX 走查角度
### 3.1 系统性缺陷按模式全仓 grep
「输入法组词回车直发」不是一个个页面看出来的——grep `isComposing` 全仓零命中 = 系统性缺失,一次修 3 文件。走查先找「某模式全仓缺失」类系统债,收益大于逐页面挑刺。
### 3.2 三域拆分 + 防重复清单
对话/管理/设置三域并行走查,各自带「刚修过清单」(防重复登记)与「已登记清单」(防池子膨胀)。52 项中 P1×8 集中在「事件仲裁(Esc/Enter)、状态反馈缺失、配置静默丢失」三类母题。
### 3.3 UX P1 三母题
- **输入事件仲裁缺失**:IME 组词、Esc 冒泡、快捷键无焦点守卫——同一类
- **操作无反馈**:静默失败、切换无过渡、点击像失灵——同一类
- **配置/状态静默丢失**:探测重拉覆盖用户配置、错误态顶掉可用按钮——同一类
修母题 > 修表象。
## 四、流程角度
### 4.1 唯一等令点
全流程只在 T0(提交+换装)一处等用户令,其余自主流水。攒批规模随每批汇报透出,用户随时可下令。避免频繁请示打断流水。
### 4.2 门禁基线演进
624 → 629 → 631 → 1871(全 workspace)。每批销账记录基线,验收=「不低于基线+新增」。基线漂移即红灯。
### 4.3 攒批分组提交
多批混工作区时(本批+前批+六路),提交须按批分组——主代理维护各批文件清单(进度文件),提交时可机械分组。
### 4.4 进度文件是多会话接力的唯一事实源
两次会话压缩、多次重启,全靠 `temp/*-进度.md` 分段追加接续。写进度=对下代会话交钥匙。
@@ -0,0 +1,121 @@
# aichat 三路走查报告(2026-10-02)
> 走查代理:rev-backend(src-tauri/src/commands/ai)/ rev-frontend(src/composables/ai + src/components/ai)/ rev-crates(crates/df-ai + df-storage)。
> 背景:上下文管理信息组织批收口期增量走查,为批次2(后端高危)/批次3(前端体验)提供输入。硬性排除清单(已修 B1-B5/D1-D7/已登记 todo/范围外决策项)均已遵守,结论以当前源码为准。
> 主代理核验说明:后端 P0-1 已亲核实锤(chat.rs:1697-1740 conv_id 分裂确认);crates P0-1 与其余 P0/P1 为代理独立源码复核(报告内标注),未逐项亲验——批次开工时以现场源码为准再确认。
---
## 一、后端走查(rev-backend)
范围:src-tauri/src/commands/ai/ 约 24 个核心文件全文精读(主循环/流式中继/审计工具执行/审批门控/路径授权/会话持久化/事件总线/子代理/上下文生命周期/通知),其余约 55 文件(tools/*/augmentation/* 等)按四类模式扫描(裸 emit/裸 conv()/锁内 unwrap/状态复位配对)。
### P0-1 ai_chat_compress_context 的 conv_id 分裂:跨会话压缩污染 + compressing 标志永久卡死【已亲核实锤】
- 位置:src-tauri/src/commands/ai/commands/chat.rs:1697-1736(读取段)vs :1743/:1762/:1800-1805/:1827-1830(写入段)
- 根因:读取段全部落在 IPC 参数 conversation_id 上(:1701 conv()/:1710 set_compressing(true)/:1712 protect_start/:1727 克隆消息);但 :1734 `let cid = session.active_conversation_id.clone().unwrap_or_else(...)` 之后所有突变(复位 compressing/compress_old_messages/insert_at(0, 摘要)/save)全落 cid(active 会话)。F-260616-09 批4 决策 e 使「压缩后台会话」成为受支持路径,非边角。
- 影响(压缩任一非当前会话 100% 触发):①目标 conv is_compressing 置 true 后无出口复位→手动/自动压缩从此永久跳过;②active 会话前 compress_end 条消息被误标 compressed 并插入由后台会话内容生成的摘要——跨会话数据污染,经 save 持久化进 DB;③:1716 protect_start==0 早退分支 cid 无 fallback 与主路径不一致。
- 修复:统一单一标识,:1734 改 `let cid = conversation_id.clone()`(AiCompressing/AiManualCompressed 事件已按 conversation_id 路由);约 4 行,收益/风险比极高,批次2首位。
### P1(3 项)
- **P1-1 remote_bridge 6 处 AiError 单路 emit**:remote_bridge.rs:812/:871/:951/:1000/:1034/:1066,miniapp 专属错误回执用裸 app.emit(桌面前端通道),miniapp 只能经 ai_event_bus 收——手机端发送失败完全无感知(「发了没反应」),桌面反收无关错误气泡。同模块 AiUserMessage 已按 BUG-260805-02 修为 emit_ai_chat_event 双写,此 6 处系同族漏网。修复:替换为 emit_ai_chat_event(同文件已有 import),机械改动可批量。
- **P1-2 只读工具结果缓存无写失效**:audit/cache.rs find_cached_readonly_result 按「工具名+参数」命中即回放,写类操作后同参数读命中旧缓存→LLM 写后读回验证拿到写前内容,可能据此二次「修复」或误报失败。与 G2 写间隔豁免的设计哲学矛盾(写后验证恰是最需要真实读回的场景)。修复:per_conv 维护「自上次写类成功后的脏标记」,写后缓存整体失效(不建议按参数精细失效)。
- **P1-3 ai_conversation_delete 不停 in-flight loop**:conversation.rs:608-640 无 stop_flag/无 epoch bump(对比 ai_chat_clear 先置停)。删除后当前轮(流式+工具执行+审批等待)完整跑完:继续烧 token/继续执行已授权高危工具;审批挂起中被删要等满 15 分钟外层超时;audit/mod.rs 9 处 `session.conv(conv_id).messages.push` 收尾回填惰性重建已删 conv 条目→zombie 复活(deleted_convs 只拦 DB 写拦不住内存重建)。修复:对齐 ai_chat_clear 删除时锁内置停+notify+bump epoch;execute_with_heartbeat 等待循环加「conv 已删即提前退出」。
### P2(5 项)
- **P2-1** agentic/mod.rs 4 处裸 emit 绕过正典(:1863 heartbeat/:1939 熔断求助/:2072 写删回环熔断/:2102 探索熔断)→miniapp 收不到心跳与熔断卡;AiCompleted :2331 双路分叉是有意设计勿动。修复:替换 emit_ai_chat_event。
- **P2-2** maybe_auto_compress protect_start 跨锁过期可致切片 panic:context_lifecycle.rs:102 锁内算 :118 锁外用,窗口内 clear_context 清到剩 6 条→`[..94]` index out of bounds,loop task 溃亡(仅 F2 看门狗兜底,而 F2 前移正是已登记待办)。修复:锁内 `let cut = protect_start.min(mgr.len())` 一行钳制。
- **P2-3** try_continue_agent_loop 裸 emit 零 token AiCompleted 绕过 emit_ai_completed_once(helpers.rs:100/:171)→挂起审批时被 continue 接管会残留占位 tool_result 与 pending 条目(__PENDING__: 标记风险)。修复:改调正典出口或补 finalize 两行。
- **P2-4** save_conversation 幽灵复活防线 Ok(Some) 分支缺二段复检(conversation.rs save 路径):get_by_id 返回 Some→删除事务提交→update_full/insert_batch 把已删会话消息写回(孤儿行)。修复:写库前再查 deleted_convs。
- **P2-5** stream_relay Fatal 路径 emit 先于 save(:454-458)与「先持久化后 emit」约定相反,emit 后进程退→前端有错误 DB 丢最后一轮;同文件全候选 Exhausted 文案把「provider 池饱和跳过」报成「重试耗尽」误导排障。修复:调换顺序;文案区分饱和/重试。
### P3(合并)
command_lock.rs 全仓零调用(spawn_agent 上线后建议接线或标注预留);update_pinned_goals 部分去重判定偏差;ai_approve None 分支持锁跨 DB await;clear_once_allowed_dirs 全清误伤多会话 once 授权;save_checkpoint 用 persist_msgs.len() 冒充 token 阈值+捷径下永不触发(疑似死代码);prev_compressing 双锁窗口 auto+manual 并发双压缩;保护区间 insert_at(0) 与后台压缩并发致压缩边界偏移一条;GOAL_VERIFY 续轮跨轮 Tool 段重复计数;HashMap.values() 乱序仅影响展示。
---
## 二、前端走查(rev-frontend)
范围:src/composables/ai/ 24 文件 + src/components/ai/ 16 文件全量,i18n ai/aiChat/aiTool 命名空间全扫。已对照排除清单,与 todo 第一批/9-28 走查无重复。
### P1(2 项)
- **P1-1 后台会话审批/路径授权事件被 isCurrent 守卫整体丢弃→挂起审批不可见、会话无限期挂死**:useAiEvents.ts:94-123 `!isCurrent` 分支只处理 Completed/Error/HelpRequired,其余事件一律 return——AiApprovalRequired/AiDirAuthRequired 落缝。后台会话 B 审批挂起时:不建审批卡/不进 pendingApprovals/不启超时计时器/不弹浮窗,后端 loop 阻塞等审批 IPC 而前端零信号→无限期挂死(用户切回经 pendingToolCalls 恢复才可见,但无「该切回」提示)。与已登记 #8 徽标计数不同源(#8 是已有项的计数,本条是上游路由丢失)。修复:!isCurrent 分支对 ApprovalRequired/DirAuthRequired 放行给 handleToolEvent(两 handler 已写 conversationId,findToolCall 找不到卡片自然跳过,无视图污染)。
- **P1-2 switchConversation「对话不存在」兜底分支漏置 activeConversationId→视图/高亮/发送目标三重错配**:useAiConversations.ts:294-309 该分支新建会话并持久化+清空视图,但没置 state.activeConversationId 即 return(对比 newConversation:101 与正常路径:354 都有)。触发=点击后端已不存在的会话(重启后未落库虚拟会话/跨窗口删除竞态):侧栏高亮指旧会话,sendMessage 把消息发进用户刚离开的会话,持久化与内存分叉。修复:补一行置 activeId+对齐 per-conv 游标重算,或直接复用 switchConversation 统一路径。
### P2(6 项)
- **P2-1** _lastDeltas 去重 Map 双路径漏清(aiShared.ts:400-420;漏清点 useAiEvents.ts:96-115 后台终止/:154-161 跨窗口删除)→无界残留+切回后首条 delta 被去重误丢(回复开头缺字符)。修复:两处补 clearLastDelta 或收敛进 clearConvStreamState 成对清理。
- **P2-2** startListener 部分失败→listener 泄漏+双注册(useAiEvents.ts:142-175):onEvent 成功后续 listen() 抛错不回滚,_startPromise 清零后下次全量重注册→流式文字双倍。修复:catch 内补偿 unlisten 或单数组统一回滚。
- **P2-3** 看门狗 legacy/per-conv 双轨迁移半途三错配(useAiSend.ts:101 vs :169/:241/:512):stopChat 清不到 per-conv timer(停止成功 90s 后补莫名「已收敛」气泡或误报中断);regenerate/editMessage 走 legacy forceConverge→convStates.clear() 抹掉其他并发会话生成态;_resumeAttempts 会话完成后不清,下次续跑额度缩水。修复:统一迁移传 convId(债务本就要还)+clear 时顺带删 _resumeAttempts。
- **P2-4** removeGoal 乐观更新不回写 store/失败不回滚→已删目标「复活」(TopBar.vue:530-539;loadGoals 读 conv.pinned_goals 重灌)。修复:成功同步写 pinned_goals 或触发 refresh,失败回滚+toast。
- **P2-5** 切换/新建会话整体覆写 state.pendingApprovals(useAiConversations.ts:419/:462 同族 newConversation:110/clearChat useAiPanel.ts:191)→聚合层 per-conv 共存被旁路,切走期间后台会话审批入口全盲(与 P1-1 叠加成「后台审批双盲」)。修复:恢复路径按 conversationId 合并(保留其他 conv 段)。
- **P2-6** i18n 地雷:searchFilesBlocked 值含裸 @(zh-CN/aiChat.ts:198 en/aiChat.ts:197,`@[项目]` 未按 {'@'} 转义)——当前死键无消费方不炸,接线即踩。修复:转义或删死键。
### P3(8 项合并)
clearAllApprovalTimers 全清误伤并发会话审批倒计时;整轮收尾 clearAllToolSlowTimers 全清丢后台慢执行 toast;删除会话双调 switchConversation(冗余非 bug);activeId 空窗被后台首事件抢占;切会话未重置 expandedLongIds/JsonIds/SegmentIds/ThinkIds(跨会话展开态残留+fallback id 跨会话撞 key);分离窗口 X 关不清 df-ai-gen-*/df-ai-text-*;clearChat 清审批不清计时器(到点自动拒+气泡进已清空视图);@任务联想缓存组件生命周期不失效。
### 排查干净面(免重查)
事件监听器清理全成对;per-conv Map 主干写收敛正确;useToolCard/纯函数层无状态问题;AiUserMessage 自回灌疑点排除;组件 i18n 除 P2-6 无特殊字符地雷。
---
## 三、crates 走查(rev-crates)
范围:df-ai 30 文件+df-storage 24 文件全量,主代理亲读迁移链/message_repo/conversation_repo/idea_repo/settings 白名单/ai_tools 注册/context/retry/lib,4 并行子审查覆盖 compat/context/storage/编排网络层。未覆盖 secret.rs(权限拒绝)。
### P0-1 V21 迁移 COUNT 幂等窗口:中途崩溃→ai_messages 永久残缺且被版本号掩盖【代理已核验】
- 位置:crates/df-storage/src/migrations.rs:481-488, :570
- 根因:批量迁移(每 50 对话一事务)先 SELECT COUNT(*) FROM ai_messages,>0 即跳过全部数据迁移并写版本号;版本号 21 在所有批次跑完后才写。批次中途崩溃/断电,重跑时 COUNT>0→剩余对话永不再迁,但版本号照样补齐,迁移链视为完成。
- 影响:该批对话 ai_messages 永久缺行(list_recent 在用读路径翻历史缺消息;原始 JSON 还在 conversations.messages 列,非真丢)。
- 修复:按对话进度标记(临时表记已完成 conv_id)替代全局 COUNT;或每批成功后写进度,重跑从断点续。
### P1(12 项,逐条)
1. **OpenAI 流式 error 帧/usage 尾帧因 choices 必填被整帧静默丢弃**(openai_helpers.rs:193-203 choices 无 default,:260,:349 Err→debug! 吞):标准 error 帧 `{"error":{...}}` 无 choices 键→serde 失败被吞,:265-280 error 处理对最常见形态是死代码,中转站流中途报错呈「无声截断」。修复:choices/delta/index 加 default+补无-choices 单测。
2. **同步响应 .json() 的 serde 结构错被判 Retryable**(anthropic_compat.rs:629-638/openai_compat.rs:527-535):reqwest 0.12 把 serde 错误包成 is_decode()==true 先命中 Retryable——确定性结构错重试 3 次白烧 30s。修复:先 resp.text() 再 from_str(读中断→Retryable,结构错→Fatal)。
3. **Anthropic usage/id 必填非 Option**(anthropic_helpers.rs:91-105/:66):网关 200+缺 usage→整响应失败,有效文本回复整体丢弃(叠加#2 还重试 3 次)。修复:usage 改 Option+缺失 warn。
4. **stop_reason 全链路不可见→max_tokens 4096 截断静默零痕迹**(anthropic_helpers.rs:71/:176-184;OpenAI 侧有 FinishReason 告警,不对称)。修复:stop_reason=="max_tokens" warn。
5. **TokenUsage 口径两栈漂移**(openai_compat.rs:542-549 缺失全零静默入库 vs Anthropic 缺失硬失败;anthropic cache 段少记 prompt 不含 cache_read/creation)。修复:None warn 留痕;cache 场景 prompt=input+creation+read。
6. **embed() 无超时+返回条数不校验**(openai_compat.rs:449-474):body 可无限 hang;data.len()<texts.len() 不校验→检索向量错位。修复:tokio timeout+len 断言。
7. **model_fetch 空 id 列表返回 Ok([])→上层写回清空用户模型池**(model_fetch.rs:169-186/:147-163;src-tauri 写回点待核):一次畸形/空响应静默清空 model_configs(含 UserSet 标注)。修复:空结果返回 Err/标记;写回点空列表不落库。
8. **ai_messages INSERT OR IGNORE 静默丢写**(message_repo.rs:86-102/:289-307):UNIQUE(conv,seq) 冲突整条 IGNORE 仍 Ok(()),调用方只判 Err;增量 insert_batch 路径 seq 重排/双写即静默丢消息。修复:核对 affected 与 records.len()。
9. **整行/CAS 更新缺软删防护**(task_repo.rs:800/project_repo.rs:452/宏 crud/mod.rs:183-196):fbd8fae 给 update_field_active 加了 deleted_at IS NULL,整行路径没跟上——回收站任务可被 df-mcp CAS 改写。修复:CAS/整行 UPDATE 补 deleted_at IS NULL。
10. **module_dependency_repo::list_by_field 列名直拼无白名单**(module_dependency_repo.rs:72-81,全 crate 唯一例外;当前调用方均字面量暂不可达注入,API 约定破口)。修复:白名单限定 5 列。
11. **truncate_after_user_message 标 truncated 不回收 history_tokens**(context/mod.rs:385-406):唯一不同步扣还的路径,编辑重生成后 history_tokens 永久虚高→过早裁剪+under-trim 超预算发送。修复:标记时逐条 saturating_sub。
12. **sanitize 合并连续同 role 丢多模态 parts**(context/sanitize.rs:406-425):合并只拼 content+tool_calls,parts 不迁移,空 content 纯图消息整个消失;触发路径现实(裁剪概要摘要与首个保留 user 相邻必合并,该 user 带图则图对 LLM 隐身)。修复:合并分支 extend parts。
### P2(34 项,一行一条)
df-storage:①from_row 吞列错误为默认值(conversation_repo.rs:25,58-63,100);②手写 BEGIN/COMMIT panic 残留打开事务(:259-296 改 guard.transaction());③insert_checkpoint OR IGNORE 静默跳过(:609-636);④limit=0 语义分裂(message_repo.rs:164);⑤父任务软删校验缺失致回收站父下隐形子任务(task_validation.rs:50-71);⑥project_service 宏绕过 validate_record/D10(project_service_repo.rs:128-159);⑦模块删除未清依赖边 FK 报错(project_module_repo.rs:172-205);⑧CAS 以 TEXT 等值比较 updated_at(task_repo.rs:800);⑨未知 status 静默变默认值+update_full 回写放大(task_repo.rs:30/idea_repo.rs:107/project_repo.rs:114);⑩V43 confidence='high' 精确小写匹配 vs 提炼侧无归一化(migrations.rs:1304,非小写条目滞留靠 include_candidates 兜底)。
df-ai:⑪intent "hi"/"api" 子串无词边界,history 等全判 Chat 0.9(intent.rs:186,194,420-424);⑫restore_from_messages 重放造假 pending_topic_marker(mod.rs:308-319,加载后首轮假话题锚点);⑬URL 图 token 地板被 ×0.35 稀释 1600→560(context_helpers.rs:73-80);⑭sync_after_compress 吞 JSON 解析失败(:881-902);⑮build_injection 脏窗口 off-by-one 多注一轮(:837);⑯TOOL_MISSING_PREFIX 前缀=已配对盲区(sanitize.rs:236/315);⑰manager_tests 恒零断言+同义反复(:401-405/:484-489);⑱begin_turn 喂变换后文本(src-tauri chat.rs:810/2112 调用方漂移);⑲tool_signature 漂移:read_file 缺 search、search_files/grep 缺 offset/limit/-i/output_mode、run_command 缺 working_dir(合法换参重调共享签名,与 dd0afcf 同类);⑳ai_tools.rs:241 SigValKind::Json 组只用 keys[0](latent)。
编排/网络(多 latent):㉑git_worktree merge-tree 传参预检失效(git_worktree.rs:174-188);㉒coordinator JoinSet 子任务 panic 结果静默缺项(coordinator.rs:511-520);㉓worktree/branch 清理吞错恒 Ok(:224-295);㉔router 全禁用静默回落 default_model(router.rs:96-101);㉕plan_executor 串行冒充并行+重复 id 假环(plan_executor.rs:65-91);㉖namespace_store 上限可永久突破(:144-157);㉗retry 位移溢出+预算最坏 121s≠文档 30s(retry.rs:77/:126-135);㉘未知 provider_type 静默回落 OpenAI(lib.rs:143-150);㉙sse_parser BUF_MAX 溢出截头致 JSON 破损(sse_parser.rs:150-157);㉚message_start 缺失 prompt_tokens 兜底 0;㉛同步 tool_use input 缺失 args="";㉜content_filter 不算 finished,无 [DONE] 流无终态(openai_helpers.rs:303-305);㉝model_probe "-v"/"vl" 误判 Vision(:118)。
### P3(合并)
u64→u32 as 截断(不可达);complete 内联 headers 未复用;同步 reasoning_tokens 恒 0;BOM 首事件;begin_turn 首行注释与实现矛盾(context_helpers.rs:857-862);retry 单测界宽于实现。
### 已核对无漂移(免重查)
V42 is_estimated/V33 pending_approvals/V41 module_id/V29 三列/V24 related_ids 迁移链全对齐;V43 迁移本身幂等可重入测试完备;KNOWLEDGE_COLS 与 KnowledgeRecord 15 字段一致;.optional() 用法全对;anthropic 双栈 SSE 多行拼接/跨 chunk UTF-8/thinking/tool_call 空 id fallback 自洽。
### 待下批核对
save_conversation 是否检查 needs_full_rewrite 再走增量路径(advance_persisted_count 无条件清 dirty,漏查即静默漏更新旧行);secret.rs(本次权限拒绝未读)。
---
## 四、批次归并建议(主代理整理)
- **批次2(后端高危)并入**:后端 P0-1(首位,4 行)+P1-3(删除不停 loop,与已登记 conv 删除族同域合并做)+P2-2(切片钳制一行,与 F2 前移同域);crates P0-1(V21 迁移进度标记)+P1-1/P1-2(流式 error 帧与 Retryable 误判,同文件同批)+P1-12(sanitize 丢图)。
- **批次3(前端体验)并入**:前端 P1-1(后台审批路由,与本批 incomplete/守卫同文件)+P1-2(兜底漏置 activeId 一行)+P2-5(审批数组 per-conv 合并,与 P1-1 同域)+P2-1/P2-3(看门狗双轨迁移,本就要还的债)。
- **机械批(可独立小批)**:remote_bridge 6 处+agentic 4 处裸 emit 换 emit_ai_chat_event;P2-6 死键转义。
- **登记观察**:crates P1-7(model_fetch 清空模型池,src-tauri 写回点待核后定级);其余 P2 按域分批消化。
+73 -6
View File
@@ -15,7 +15,7 @@
> 背景:用户反馈交互刷屏已修(工具卡默认折叠)后,按「使用体验/使用成本」深入走查三动线;截图实证「写完就删」循环跑到轮次耗尽——G2 熔断签名机制对写删交替结构性漏网(唯一率 0.83≫0.4,单签名最高 2 次<3,全程连警示都不触发);与 9-28 走查 17 项合并排序。 > 背景:用户反馈交互刷屏已修(工具卡默认折叠)后,按「使用体验/使用成本」深入走查三动线;截图实证「写完就删」循环跑到轮次耗尽——G2 熔断签名机制对写删交替结构性漏网(唯一率 0.83≫0.4,单签名最高 2 次<3,全程连警示都不触发);与 9-28 走查 17 项合并排序。
> 详情:[05-代码审查/aichat可用性与交互成本研究-2026-09-29.md](./05-代码审查/aichat可用性与交互成本研究-2026-09-29.md) > 详情:[05-代码审查/aichat可用性与交互成本研究-2026-09-29.md](./05-代码审查/aichat可用性与交互成本研究-2026-09-29.md)
- [ ] 第一批 P1:I1 停止能力窄窗(工具执行期按钮消失,失控不可干预)+I2 编辑入口死链(=9-28#1)+G2 破坏性回环熔断(写后 N 步内删同路径计数,误杀率极低)+V2 审批黑洞(非活跃会话审批事件 isCurrent 守卫整体丢弃)+V1/V3 后台信号缺失(等审批伪装 generating+完成零通知) - [x] 第一批 P1 ✅2026-10-02 走查核验:I1 停止窄窗已修(ChatInput.vue:291 stopBtnState 读 conv_state)+I2 编辑死链已修(04acc42 canEdit+start-edit)+G2 破坏性回环熔断已实现(helpers.rs detect_destructive_loop)+V3 后台通知已修(e1dd0bc notify.rs 接线);V2 聚合层已修(useAiToolEvents 无条件入列,V5/V6 未逐项核)与 V1 审批徽标未修→留前端体验批
- [ ] 第二批 P2:V8 草稿 per-conv 防串台/R3 长回复带量提示+R4 代码块复制/I5 错误分类兜底(429 占 55%)/V4 浮窗高风险二次确认/V7 排队时钟冻结/R5+V9 消息内容检索 等 16 项,排序详报告第五章 - [ ] 第二批 P2:V8 草稿 per-conv 防串台/R3 长回复带量提示+R4 代码块复制/I5 错误分类兜底(429 占 55%)/V4 浮窗高风险二次确认/V7 排队时钟冻结/R5+V9 消息内容检索 等 16 项,排序详报告第五章
- [ ] 第三批 P3 攒批:I8-I13+R8-R13+V10-V12+9-28 #9-#17;其中 9-28#17 图片默认上传外部域名涉开源边界,单独决策 - [ ] 第三批 P3 攒批:I8-I13+R8-R13+V10-V12+9-28 #9-#17;其中 9-28#17 图片默认上传外部域名涉开源边界,单独决策
- [x] ✅ 同日已修:工具卡默认折叠刷屏+read_file/list_directory 单行摘要补齐+分组折叠幂等补齐(工作区待提交) - [x] ✅ 同日已修:工具卡默认折叠刷屏+read_file/list_directory 单行摘要补齐+分组折叠幂等补齐(工作区待提交)
@@ -26,12 +26,12 @@
> 详情:[05-代码审查/aichat知识库与记忆走查-2026-09-29.md](./05-代码审查/aichat知识库与记忆走查-2026-09-29.md) > 详情:[05-代码审查/aichat知识库与记忆走查-2026-09-29.md](./05-代码审查/aichat知识库与记忆走查-2026-09-29.md)
> ✅ 同日已修:上下文管理演进 T4/T5 生产侧空壳补齐(WorkingContext begin_turn/sync_after_compress 写入链 + 工作流 DAG 摘要注入链),编译门禁过,详见诊断文档第四章。 > ✅ 同日已修:上下文管理演进 T4/T5 生产侧空壳补齐(WorkingContext begin_turn/sync_after_compress 写入链 + 工作流 DAG 摘要注入链),编译门禁过,详见诊断文档第四章。
- [ ] P0 知识检索去整句 LIKE(分词/关键词多条件 OR 或默认开语义检索,idea_repo.rs:640-661 + knowledge_inject.rs:364-384) - [x] P0 知识检索去整句 LIKE ✅已修(2026-10-02 核验:7c62d8d 接 search_keywords 分词,knowledge_inject 与 knowledge_search 工具两调用点实证,idea_repo.rs:699 纯函数+单测齐)
- [ ] P0 candidate→published 断链打通(自动提炼产物按置信/复用自动晋升,人工审核改可选) - [x] P0 candidate→published 断链打通 ✅已修(机制先行 7c62d8d:复用自动晋升+knowledge_promote 工具+晋升补嵌入;存量晋升迁移 V43 2026-10-02 落地:candidate+confidence=high→published,库副本实证 854 条晋升 967/53→113/907,重跑幂等 0 行,df-storage 180 测试全绿;增量 confidence 分级与 auto_publish 默认值等 C3 拍板)
- [ ] P0 视图裁剪补滚动摘要(超预算淘汰单元无声丢弃,无补偿,context/mod.rs:206-253) - [x] P0 视图裁剪补滚动摘要 ✅已修(9b89610 淘汰补偿摘要插视图首部,user 角色保缓存指纹,纯视图不落库,eviction 11 测试全绿,2026-10-02 核验)
- [ ] P1 LLM 知识工具注册(tool_registry 复用 df-mcp search/insert_knowledge,agent 可按需查/存) - [x] P1 LLM 知识工具注册 ✅已修(7c62d8d 注册 knowledge_search/insert/promote 三工具,tool_registry.rs:798,检索与注入同源分词)
- [ ] P1 跨会话召回最小闭环(项目级记忆+会话结束提炼进知识库,source_project 已有字段未用) - [ ] P1 跨会话召回最小闭环(项目级记忆+会话结束提炼进知识库,source_project 已有字段未用)
- [ ] P1 存量测试修复 4 个(anthropic_compat usage 断言/model_fetch_helpers 过滤,疑似 6bafdcd 后未同步) - [x] P1 存量测试修复 4 个 ✅已修(df3ed55 断言同步+e7cd035 笔误修正,2026-10-02 全量 cargo test --workspace 1827 passed/0 failed 复核)
- [ ] P2 DB 落库截断分 role 定阈值(user 原话均 41 字被 3072 截断无必要)/namespace 重启懒重建/兜底压缩路径 sync_after_compress 复议 - [ ] P2 DB 落库截断分 role 定阈值(user 原话均 41 字被 3072 截断无必要)/namespace 重启懒重建/兜底压缩路径 sync_after_compress 复议
### 💡 2026-09-28 devflow 集成 u-claw 能力(可行性已定,待实施) ### 💡 2026-09-28 devflow 集成 u-claw 能力(可行性已定,待实施)
@@ -827,3 +827,70 @@ graph TD
**关联**:本走查与「2026-08-09 TaskDetail 操作按钮缺陷走查」同属 TaskDetail 交互审查线,但本段聚焦 AI 工作流推进链路(后端 + 审批 UI),按钮缺陷聚焦 TaskDetail 自身交互。修复时建议合并处理(都在 TaskDetail + workflow 链路)。 **关联**:本走查与「2026-08-09 TaskDetail 操作按钮缺陷走查」同属 TaskDetail 交互审查线,但本段聚焦 AI 工作流推进链路(后端 + 审批 UI),按钮缺陷聚焦 TaskDetail 自身交互。修复时建议合并处理(都在 TaskDetail + workflow 链路)。
---
# aichat agent loop 静默死亡走查待办(2026-10-02)
> 来源:中断根治批次全量走查(正常设计路径无漏 emit,风险集中四类)。P1-2/P0-1/P2-1 本批已修,以下为登记项。
### P0 run_agentic_loop 包 catch_unwind
- **位置**:`agentic/mod.rs` run_agentic_loop 主体(无 catch_unwind)
- **现状**:任一轮 prepare/stream/finalize/tools 段 panic → task 死亡 → GeneratingGuard::drop 只迁 Idle+emit AiConvStateChanged,**不 emit AiError/AiCompleted**,heartbeat 随作用域死亡——「工具完成后流断+无终止事件」的精确根因形状(前端探测式续跑已兜住后果,此为后端根治)。
- **修复**:整体包 `std::panic::catch_unwind(AssertUnwindSafe(...))`,unwind 时 emit AiError{error_type:Internal} 再收敛;guard Drop 兜底保留。核心 loop 改动,需谨慎设计+场景回放测试。
### P1 conv 删除类静默退出族 6 处补终态事件
- **位置**:`agentic/mod.rs:417/:1316/:1379/:1437/:1593/:1423`、`stream_relay.rs:520-525`(MidStream 保文时 conv-deleted 跳过 teardown emit,最刺眼)
- **现状**:生成期间会话被删,裸 return 仅 guard Drop 迁 Idle,无终止事件。
- **修复**:收敛到 conv-deleted 也 emit 带 conv_id 的 AiCompleted(incomplete) 或等价终态语义事件;至少 stream_relay.rs:525 处补 teardown。
### P2 审批超时惰性收敛改主动任务
- **位置**:`agentic/approval_timeout.rs:5-6`(cleanup_expired_approvals 仅 try_continue_agent_loop 入口调用)
- **现状**:用户不点任何按钮,超时审批永久占位、会话永远停审批态;注释语义(自动转拒绝)与实现(下次交互才转)不符。
- **修复**:审批挂起时 spawn timeout 定时任务到点主动 cleanup+emit(现有函数可复用)。
### 附:F2 兜底前移(可选)
- `ai_chat_stop` 的心跳 60s stale 判死+emit AiCompleted 兜底只在用户点停止时运行;可在 loop spawn 时自带一份,panic 死 loop 无需用户点停止也能收敛。
# aichat 三路走查增量待办(2026-10-02 深夜)
> 来源:上下文管理批收口期 rev-backend/rev-frontend/rev-crates 三路走查(历三次会话重启后续跑完成)。**全文与批次归并建议见 `docs/05-代码审查/aichat三路走查报告-2026-10-02.md`**。以下为可执行登记,开工以现场源码为准。
### 批次2(后端高危)并入项
- [ ] P0 压缩 conv_id 分裂【已亲核实锤】:`commands/chat.rs:1734` 起 cid 切 active——压缩后台会话时目标 conv compressing 永久卡死 + active 会话被插入他人摘要入库。修::1734 改 `cid = conversation_id.clone()`(约 4 行,首位)
- [ ] P0 V21 迁移 COUNT 幂等窗口:`df-storage/migrations.rs:481-488,570` 批次中途崩溃→剩余对话永不迁移但版本号补齐。修:按对话进度标记替代全局 COUNT
- [ ] P1 conv delete 不停 in-flight loop:`commands/conversation.rs:608-640` 无 stop_flag/epoch bump——zombie 复活+审批吊死。**与上方「conv 删除族 6 处」同域合并做**
- [ ] P1 只读结果缓存无写失效:`audit/cache.rs` 写后读回过期内容。修:per_conv 脏标记,写后整体失效
- [ ] P1 OpenAI 流式 error 帧被 choices 必填整帧丢弃:`df-ai/openai_helpers.rs:193-203`(无声截断)+ 同批 serde 结构错误判 Retryable(`anthropic_compat.rs:629`/`openai_compat.rs:527`)
- [ ] P1 sanitize 合并连续同 role 丢多模态 parts:`df-ai/context/sanitize.rs:406-425` 带图消息图对 LLM 隐身。修:合并分支 extend parts
- [ ] P2-2 maybe_auto_compress protect_start 跨锁过期切片 panic:`context_lifecycle.rs:118` 加 `min(len)` 钳制(一行,与 F2 前移同域)
- [ ] P2-3 try_continue_agent_loop 裸 emit 绕过 emit_ai_completed_once:`agentic/helpers.rs:100/:171` 占位 tool_result 与 pending 残留
### 批次3(前端体验)并入项
- [ ] P1 后台审批/授权事件被 isCurrent 丢弃→会话挂死:`useAiEvents.ts:94-123` !isCurrent 分支对 ApprovalRequired/DirAuthRequired 放行(与 P2-5 审批数组 per-conv 合并同域)
- [ ] P1 switchConversation「对话不存在」兜底漏置 activeId:`useAiConversations.ts:294-309` 补一行(视图/高亮/发送目标三重错配)
- [ ] P2 看门狗 legacy/per-conv 双轨三错配:useAiSend.ts:169/:241/:512 统一迁移传 convId + `_resumeAttempts` 收尾清(债务本就要还)
- [ ] P2 _lastDeltas 双路径漏清:useAiEvents.ts:96-115/:154-161 补 clearLastDelta
- [ ] P2 removeGoal 不回写 store/不回滚:TopBar.vue:530-539 对齐 withConvOp 模式
- [ ] P2 forceConverge 收敛/错误气泡缺 activeId 守卫(useAiStream.ts:115-121,与本批 onStreamTimeout 修复同家族)
### 机械批(可独立小批)
- [ ] remote_bridge 6 处 + agentic/mod.rs 4 处裸 emit 换 emit_ai_chat_event(miniapp 收不到错误/心跳/熔断卡)
- [ ] i18n searchFilesBlocked 死键裸 @(zh-CN/en aiChat.ts:198/197)转义或删
### 登记观察(定级前先核)
- [ ] model_fetch 空列表 Ok([]) 上层写回清空模型池(df-ai 侧已核验,src-tauri provider.rs 写回点待核后定级)
- [ ] save_conversation 需核 needs_full_rewrite 检查(advance_persisted_count 无条件清 dirty);secret.rs 未走查(权限拒绝)
# 后续规划方向(2026-10-02 用户口述,不分先后)
> 用户指令:完成 ai-chat 核心后,以下 9 项纳入后续规划,先记录后逐渐开展。此处为方向索引,开展时各自立项细化。
1. 打造核心 ai-chat 及上下文管理能力(当前上下文管理批即此项主线,持续演进)
2. 知识库、技能、记忆的管控管理(知识链路 B 系延伸;candidate 晋升/跨会话召回 C1/记忆空壳定案后续)
3. miniapp(关联 df-miniapp 跨端三层,Phase3 后端闭环已有,剩真机联调+UI+token 生产级)
4. 接入飞书 bot(启动定位=调度中枢的飞书侧动线,P0 打包+u-switch 接入已在本批)
5. devflow 云端化部署(分布式 ai 办公协同)
6. 接入企微
7. UX 体验交互体验(批次3 及后续 UX 批持续线)
8. 内置数据库、远程服务器连接能力(与代理工具生态 mysql-proxy/ssh-proxy 等整合)
9. 远程内网设备文件程序管控(中继+代理 client,内置于 u-desk/devflow 或全新设计,关联 u-relay)