Files
DevFlow/docs/05-代码审查/aichat-会话实测分析-2026-06-22.md
2026-06-24 00:29:02 +08:00

13 KiB
Raw Blame History

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-capabilityaichat-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/86devflow-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 直接降一个量级。


复现方法

# 分析脚本(已留存)
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),新会话才用新二进制。