文档: 待办与决策更新

This commit is contained in:
2026-06-24 00:29:02 +08:00
parent bb8b3c6c59
commit bdf3f96491
4 changed files with 397 additions and 0 deletions

View File

@@ -0,0 +1,185 @@
# AI Chat 真实会话实测分析(上下文 / 工具 / Agent 行为)
> 日期:2026-06-22 | 方法:从生产 DB(`%APPDATA%/top.1216.devflow/devflow-dev.db`)实测会话 `84dc36c8` 全量 86 条消息反推,Python 脚本统计(`.temp/analyze_session.py`)
> 基线:会话 prompt **1,320,713** / completion **7,578** tokens(completion/prompt 比 0.57%)
> 产物:**3 P0 / 3 P1 / 2 P2**
> 性质:**实测数据反推**(非静态代码审查)。finding 的源码根因为"假设",须独立 grep/read 核验后再定论
> 关联:[[devflow-compress-ui-no-fold]] [[devflow-generating-statemachine]] [[aichat-techdebt-audit-2026-06-21]] [[code-review-anti-contamination]] [[devflow-aichat-streaming-md]]
---
## 会话概况
| 项 | 值 |
|---|---|
| conversation_id | `84dc36c8-b416-44bc-a7c6-c4e00de82b6f` |
| 标题 | `D:\Downloads\kms` |
| 模型 | glm-5.2 |
| 跨度 | 2026-06-22 01:29 ~ 12:23(中间大间隔,有效工作段 ~11:5712:23) |
| 消息数 | 86 |
| prompt / completion tokens | 1,320,713 / 7,578 |
| completion/prompt 比 | **0.57%**(极端输入重) |
| 工具调用 | patch_file **16** / read_file **10** / list_directory 3 / grep 1 |
| tool 返回总量 | **194KB 跨 41 条** |
| `parts` 字段 | 0/86(全 null) |
| 用户催促次数 | 7(继续×2 / 完成了吗×2 / 仔细检查 / 核对 收尾 / 核对 内容) |
| 任务 | 整理 `D:\Downloads\kms` 下 README/INDEX + 三知识库 HTML 头尾修复 |
---
## P0(3 项)
### [P0-1] 上下文管理实质失效:压缩标记 `compressed` 未降 prompt,token 累积至 1.32M
- **现象**:seq 065 `status=compressed`(仅个别 active),但最终 `prompt_tokens=1,320,713`。压缩"生效"了,prompt 却没降。
- **db 证据**:全量 86 条中 66 条 `status='compressed'`;conv 元数据 `prompt_tokens=1320713 / completion_tokens=7578`
- **根因假设**:`compressed` 仅压历史消息的展示态/落库态,**未真正削减送入 LLM 的 prompt**。与 [[devflow-compress-ui-no-fold]](2026-06-18 决策"后端省 token 不前端藏")冲突——决策要求后端真省,实测后端没省。
- **影响**:1.32M 接近模型上下文上限,长会话必爆;成本几乎全烧在重复搬运文件内容。
- **待核验源码点**:compress 模块(`src-tauri/src/commands/ai/` 下 compress 相关)——压缩后重组 messages 时是否真替换为摘要,还是仅打标记原样回灌。
- **关联**:印证 [[aichat-techdebt-audit-2026-06-21]] 3-compress 单元的 2 个 smell。
### [P0-2] 工具返回值协议不一致:`patch_file` / `read_file` 部分路径裸吐文件全文
- **现象**:同一工具返回结构两副面孔:
- seq 16 patch_file → `{"changed":true,"diff":...}` ✅ JSON 正常
- seq 37/40/42/46/49/51/53/60/63/65/75/76 patch_file → `raw 6204c` ❌ 裸文本(parse 失败)
- seq 6/7 read_file → `{"content":...}` ✅ 正常
- seq 11/29/31/33 read_file → `raw 6204c` ❌ 裸文本
- **db 证据**:41 条 tool 返回总计 194KB;`6204` 字符数反复出现(≈被操作 HTML 文件大小)。
- **根因假设**:`patch_file` 成功路径本应只回 `changed`+`diff`,却把整个文件内容回灌;或某分支(超长 / 特定路径)跳过 JSON 包装直接返回文件串。`6204c` 即文件实际长度被全文塞回 context。
- **影响**:**这是 prompt 1.32M 的直接元凶**。每次 patch 一次 = 文件全文再进一遍 context。16 次 patch × ~6KB ≈ 100KB 仅 patch 回灌。
- **待核验源码点**:`tool_registry.rs` patch_file handler(~1247 区,[[devflow-patch-file-design]] 落地点)与 read_file handler(~892-976)的返回序列化分支——确认哪条路径丢 JSON 包装。
- **关联**:与 [[aichat-techdebt-audit-2026-06-21]] P1「expected_hash schema 契约破裂」同源(工具契约失配家族)。
### [P0-3] 重复 tool 结果:同一结果多次持久化,去重缺失
- **现象**:
- seq 11/12/13/14 → 连续 4 条 `raw 6204c`,同一 read 结果存 4 份
- seq 69/70/71 → 3×6204c;seq 83/84/85 → 3×6204c
- seq 75/76 → 2×6204c
- **db 证据**:上述 seq 的 content 长度完全一致(6204),且时间戳相同(同一秒)。
- **根因假设**:一次 assistant 回合并行触发多个 read(读 mysql+redis+linux 三文件),结果落库时按 tool_call 重复写,或同一 `tool_call_id` 结果被多次持久化;前端流式接收去重逻辑缺失。
- **影响**:context 进一步虚胖;且会污染后续压缩/摘要(同一内容被当作多条独立事实)。
- **待核验源码点**:tool 结果落库路径(`ai_messages` 写入)——按 `tool_call_id` 是否做唯一约束/去重;流式 patch 合并逻辑。
---
## P1(3 项)
> **总纲(用户归纳)——编排规划能力弱**:P1-1(无自检)与 P1-3(无并行调度)同源。agent 无任务分解、无并行 fan-out、无宣称完成前的自检规划,单链 ReAct 逐工具串行推进,识别不出"N 个独立子任务可并行"。这是 agent 自主性/可靠性的核心缺口,印证 [[aichat-decision-capability]] 与 [[aichat-b-route-parallel-multiround]]。归 B 路线立项。
### [P1-1] Agent 过早宣称完成,无 checklist 自检,用户被迫 7 次催促
- **现象**:assistant 3 次宣称完成,3 次被用户推翻:
| seq | 时刻 | 宣称 | 推翻证据 |
|---|---|---|---|
| #26 | 12:01 | "整理完成" | #27 用户"文档内容需要按要求整理" |
| #66 | 12:10 | "**全部完成了!**" | #67"仔细检查" → #72 发现 `theme.js` vs `claude-artifact-theme.js` 引用名错(漏改 2 个文件) |
| #80 | 12:19 | "全部核对完毕,7 个标记全部对齐无遗漏" | #81"核对 内容"(用户不信,要求再核)→ 会话中断于此 |
- **db 证据**:`theme.js` 引用名错误本可一次 grep 抓出,却要用户肉眼挑(实际 #78 才首次用 grep,且是用户第三次催后)。
- **根因假设**:无宣称完成前的强制自检机制(如 grep 引用一致性 / 闭合标记核对)。Agent 倾向于 patch 完即宣布成功,不验证。
- **影响**:用户对 agent 完成度零信任,交互成本极高;本质是 agent 自主性/可靠性的核心缺口。
- **待核验/落点**:属 agent 行为策略层(prompt / loop 收尾钩子),非单点代码 bug。建议与 [[aichat-b-route-parallel-multiround]] B 路线(决策能力)合并立项。
- **关联**:[[aichat-decision-capability]](单链 ReAct,无规划式智能)的现实印证。
### [P1-2] 末尾会话中断:tool 全部返回后无 assistant 回复(generating 状态机卡死)
- **现象**:#82 assistant 触发读三文件 → #83/84/85 tool 全部正常返回(`status=active`)→ **之后再无 assistant 消息**,会话 12:23 停尸。
- **db 证据**:seq 85 是最后一条,role=tool;无 seq 86 的 assistant 总结回复。`active` 状态说明 tool 执行完成,但后续生成未发生。
- **根因假设**:tool 执行完但 `generating` 状态未复位,后续回复被吞。高度吻合 [[devflow-generating-statemachine]] 记录的"generating 复位散布 + 无 panic 兜底"卡死模式。
- **影响**:用户看到"读完了"就没下文,必须手动重发;且该会话 token 已 1.32M,重发极可能直接触上限。
- **待核验源码点**:agentic loop 的 tool-后-续生成衔接(`mod.rs` 收尾 / `try_continue` / per-conv generating 复位)。
### [P1-3] 三个独立文件整理任务严格串行,无并行 fan-out 调度
- **现象**:`mysql_guide` / `redis_guide` / `linux_ubuntu_guide` 三个 HTML 整理是独立子任务(无依赖),本可并行。但 agent 严格串行:mysql(12:0212:06)→ redis(12:0712:08)→ linux(12:0812:10),**时段完全不重叠**;每个文件内还 read→patch→read→patch 多轮。assistant 从未在一个 turn 内对三文件 fan-out 工具调用。
- **db 证据**:时间线三文件处理时段分明不重叠;seq 68"仔细检查"后只 read mysql 一个,未并行触发 redis/linux 的核对。
- **根因假设**:单链 ReAct 无任务分解层。缺"识别 N 个独立子任务 → 同 turn 并行发起 N 路工具调用"的编排能力。
- **影响**:耗时线性叠加;同一指令在 context 里反复加载三遍;用户等待感差。**直接印证 [[aichat-b-route-parallel-multiround]]**(用户 2026-06-20 已决策"单对话并行多轮必须做")。
- **归并**:与 P1-1 同属"编排规划能力弱"(见 P1 区总纲),归 B 路线。
---
## P2(2 项)
### [P2-1] system 消息污染 + 时序错乱
- **现象**:
- seq 0 `role=system`,content 却是 assistant 语义残留:"我需要查看 mysql\_guide.html 的最后几行来确认尾部内容..."
- seq 0 timestamp `12:08:02`,seq 1 却是 `01:29:35`——seq 0 比 seq 1 晚 10 小时。
- **根因假设**:system/上下文注入消息的 role 标记错位;timestamp 用"插入时刻"而非"会话时刻"。
- **影响**:排序、压缩、计费全受污染;system 消息混入 assistant 内容会误导模型。
- **待核验源码点**:system prompt 注入路径(`ai_messages` 写入 system 行的位置)+ timestamp 赋值逻辑。
### [P2-2] 同文件反复 read,无缓存 / 无 diff 感知
- **现象**:`mysql_guide.html` read **4×**`linux_ubuntu_guide.html` read **4×**;patch_file 共 16× 改 3 个文件。read→patch→read→patch 循环。
- **根因假设**:每次 patch 后又全文 read 确认(叠加 P0-2 全文回灌);无 session 级文件缓存或"patch 后不重读"约定。
- **影响**:context 雪崩放大器;与 P0-2 叠加是 prompt 爆炸的复合原因。
- **待核验/落点**:工具侧可加"会话内文件指纹缓存,LLM 重读同文件时返回缓存命中提示";或 prompt 侧约束 patch 后不重读。
---
## 附录:`parts` 字段全空(待观察项)
- **现象**:`msgs_with_parts=0/86`。[[devflow-aichat-streaming-md]] 记录块级 memo(memo=mapped.lexer+v-memo)已落地(ARC-260615-08)。
- **不定论**:本会话可能早于改造,或 `parts` 存于别处 / 未回填。需对照**新产生**的会话核验,不能据此断定流式渲染回退。
---
## 落点建议(未实施,遵守 [[session-role-diagnose-only]])
| 项 | 落点 |
|---|---|
| P0-1 / P0-2 / P0-3 | 并入 [[aichat-techdebt-audit-2026-06-21]] P1 主线,或单列"上下文/工具返回"专项 |
| P1-2 中断 | 印证 [[devflow-generating-statemachine]] 待落地链(已有详案) |
| P1-1 自检 + P1-3 并行调度 | 同属「编排规划能力弱」总纲,归 [[aichat-b-route-parallel-multiround]] B 路线(任务分解 + 并行 fan-out + 宣称前自检) |
| P2-1 / P2-2 | 低优登记 todo |
**最高杠杆**:P0-2(工具返回全文回灌)——改一处(工具序列化返回 diff/changed 而非全文),prompt 直接降一个量级。
---
## 复现方法
```bash
# 分析脚本(已留存)
python E:/wk-lab/devflow/.temp/analyze_session.py
# DB 只读连接
sqlite3 "file:%APPDATA%/top.1216.devflow/devflow-dev.db?mode=ro" -uri
# 表:ai_conversations / ai_messages / ai_tool_executions
```
数据快照:2026-06-22 12:36 抓取,会话可能仍在变化(WAL 活跃)。
---
## 2026-06-23 修正与落地(自主推进,290 src-tauri + 324 df-ai 测试全过)
**核验推翻**(实测 DB tool_call_id + timestamp 独立核验,[[code-review-anti-contamination]]):
- **P0-3 撤销**:两会话(4ae73423/84dc36c8)"重复 tool 结果"实测 tool_call_id **全不同** + timestamp 间隔数分钟/小时 → LLM 不同时刻重调,非落库去重 bug。原 P0-3 误判(06-22 报告 + 核验 agent 都错,实测数据证伪)。
- **P0-2 性质修正**:非"prompt 爆炸元凶"(prompt 用内存完整态,非落库截断版)。真因是 `conversation.rs:72` truncate_for_persist 对**序列化 JSON 中间截断**插裸 `\n\n[...省略 N 字符...]\n\n`,破坏 JSON 致**落库坏 JSON**(reload/审计 parse FAIL,pos3072=TRUNCATE_HEAD)。
- **中断A 非bug**:续轮机制完整(mod.rs:1362-1478),84dc36c8 seq85 后无回复疑达 max_iterations 或自然收敛。
- **中断B 真缺陷**:4ae73423 末尾 advance_task 审批 `__PENDING__` 卡死(mod.rs:1464 return 无超时),非 generating 卡死。
**本次修复落地**:
| 项 | 位置 | 修复 |
|---|---|---|
| intent 收敛误杀(4ae73423 主因) | intent.rs | L0 strip_mention_tags 剥离 mention 前缀 + L1 Project/Task/Idea 加 File domain |
| write_file parent 误拒(P0,授权内文件被拒) | tool_registry.rs:1114/1421/1559 | 改 allowed_dirs.is_authorized(脱 workspace_root) |
| read_file limit 忽略 | tool_registry.rs:1023 | 无 offset 尊重 LLM limit(原固定 500) |
| truncate 破坏 JSON(P0-2) | conversation.rs:175 | 跳 tool role 截断 |
| 压缩丢末条消息 | agentic/mod.rs:1225 | 全 provider 失败 return 前补 save_conversation |
| run_command && PS5 失败 | df-execute/shell.rs | pwsh(PS7)优先探测 + PS5 兜底(OnceLock 缓存) |
| remote_bridge args default | remote_bridge.rs:97 | custom default 返空 Object(#[serde(default)] 给 Null) |
| remote_bridge r1 双轨测试 | remote_bridge.rs:872 | 设态对齐双轨(conv_state=Generating) |
**写 docs/待决策.md**(非确定性,需决策):
- BUG-260623-03 审批 pending 无超时(中断B,推荐 b 前端倒计时)
- ARC-260623-01 workspace_root 写死残留(delete_file trash/resolve/run_command working_dir,分发适配)
**生效**:需重启 devflow app(cargo tauri dev / 重新 build),新会话才用新二进制。