Files
DevFlow/docs/05-代码审查/aichat可用性与交互成本研究-2026-09-29.md
T

17 KiB

aichat 可用性与交互成本研究(2026-09-29)

三动线走查(提问与恢复/回复消费/多会话与审批)+ 真实异常循环截图实证 + 2026-09-28 走查合并排序。 走查代理产出,主代理汇总核验。守 session-role-diagnose-only,除标注「已修」外未改代码。 前置:《aichat 交互体验走查(2026-09-28)》(同目录)17 项经核验全部未修,本文合并排序不重复展开。

总体定性:aichat 的交互基建(per-conv 状态机/流式 memo/审批兜底/失败保文)成熟度高,当前使用成本集中在四个结构性热点——改措辞动线整体断裂(编辑死链+无重生成+无历史重发)、停止能力只存在于文本流出的窄窗口(工具执行期不可干预)、异常循环无机制拦截(G2 熔断对写删交替结构性漏网,截图实证)、多会话「需要我」信号全链缺失(后台审批黑洞+状态伪装+零通知,事件路由与状态层按单会话设计)。前两者对应高频动作失能与信任伤害,后两者分别对应成本失控与多任务模式失效。

一、真实异常循环实证:「写完就删」跑到轮次耗尽,熔断全程未触发

用户截图(u-switch sn-kimi 渠道):AI 执行「清理临时目录」任务时陷入 rename→delete→write→list→delete→write 循环,AI 自己在气泡中承认「我陷入写完就删的异常循环」,系统零介入,直到 5/5 轮次耗尽自然停止。每轮 36k in · 0 cache(kimi 渠道缓存未命中),循环期间每轮全价。

根因:G2 熔断签名机制对「写删交替」结构性漏网(实证)

agentic/helpers.rs:275 tool_call_signature:grep/read_file/run_command/list_dir/http_request/fetch_url 有特化签名,write_file/delete_file/rename_file 走兜底分支 name + args 全量 JSON。

截图序列 6 个调用(恰达 REPETITION_MIN_SAMPLE=6 判定门槛)逐一套签名:

调用 签名形态
rename_file(A→B) 兜底,唯一
delete_file(B) 兜底,唯一
write_file(A, content v1) 兜底,content 全文入签名
list_directory(dir) path 特化,唯一
delete_file(A) 兜底,与 delete_file(B) 路径不同 → 唯一
write_file(A, content v2) content 相同则与 v1 同(计 2);微调则唯一

判定条件(helpers.rs:325):唯一率 <0.4 或单签名 ≥3 次。实测:唯一率 5/6≈0.83(全异时 1.0),单签名最高 2 次——两个条件全程不满足,连 WARN_FIRST 警示都不触发。采样窗口 STALL_BREAKER_SAMPLE_SIZE=12(mod.rs:251)、THRESHOLD=3、两段式先警示再熔断的设计目标死「同调用反复重试」,而本例本质是目标打架(旧清理目标反复作用于自己刚产出的文件),签名层面完全等同于「持续翻新的正常探索」。

模型侧:kimi 弱规划能力(B 路线范畴)无法维持「刚产出的文件不清理」约束;机制侧:现有熔断模式盲区可治——

改进方向(登记待办):G2 增加「破坏性回环」检测——同一规范化路径上「write 后 N 步内 delete / delete 后 N 步内重写」计数,达阈值警示再熔断。该模式与正常探索天然互斥(正常清理不删自己刚写的文件),误杀率极低,可拦住整类自毁循环。

循环场景的视觉层问题(并入动线清单)

  • C1. 一轮动作拆成「AI 文本气泡+1 工具卡」碎序列,5 轮循环=10+ 块,滚动成本高(P3,与 R-13 同源)
  • C2. delete_file 与普通卡视觉权重接近,「AI 删自己刚写的文件」异常序列无聚合警示(P2)
  • C3. 右上角 agentic 进度文案实证为 agenticProgressNoPending(AiChat.vue:218-224):「🔄 循环 5 · ✅5个工具完成」——只报轮次与完成数,零异常信号;异常循环期间它给用户的观感是「一切正常推进」,与实际自毁形成反差。max_rounds 未透传(9-28 已知 #4 关联),第 5 轮是否到顶不可知

二、动线一:提问与失败恢复(14 项)

正面确认:排队动线完整(入队/编辑/插队/清空/30s 超时强发)、发送失败完整回填文本+图片+mention、生成中可自由切换会话、错误气泡分级出「重试/去设置」、未发送输入有 beforeunload 保护。

P1

  • I1. 停止按钮只在「文本正在流出」窄窗口存在——ChatInput.vue:290-297 stopBtnState 仅 streaming && !textIdle 显停止;textIdle 在 300ms 无文本 delta 即 true(useAiPendingState.ts:19-26),工具执行期(30s heartbeat 无 delta)与轮间 LLM 等待期按钮回落发送图标;grep stopChat 全组件仅 ChatInput 两处,无其他停止入口。工具跑错方向时用户不可干预,只能等 90s 看门狗或眼看副作用继续(截图循环正是此场景:5 轮自毁全程无停止窗口)。方向:停止态判定改读 per-conv generating 态,不依赖文本活跃
  • I2. 编辑已发消息入口死链(=9-28 P1,本轮补全证据)——MessageItem.vue:98 用户气泡仅 copy 无编辑按钮;MessageList.vue:36 声明 start-edit 全组件零调用;下游 EditingMsgId/编辑态发送/Esc 取消全链完整但不可达;.ai-msg-edit-btn 样式残留。git 实证 2c2b2d7 抽离 MessageItem 时按钮未迁移。改措辞重问只能整条重打

P2

  • I3. 停止无快捷键,Esc 不作用于生成(AiChat.vue:351-365 全局 Esc 仅处理 confirmDialog 与分离窗口);急停必须鼠标移 28px 小按钮(且按 I1 还会消失)
  • I4. AiError 静默清空该会话排队消息(=9-28 #4)——useAiLifecycleEvents.ts:167-171 filter 整队列,无提示不回填;对照 drainQueue 失败路径专门回填队首(useAiSend.ts:296-298),同域不一致
  • I5. 未匹配错误原文直出(与 9-28 #7 同修点合并)——friendlyError 仅 4 类正则(aiShared.ts:26-32),不命中 return raw;429/额度耗尽落 unknown:英文原文直出+可重试,额度未恢复重试必再败(08-11 实证 429 占 55%)。方向:补 quota/rate_limit 分类+兜底通用文案,原文折叠为详情
  • I6. 重新生成无 UI 入口——仅隐藏快捷键 Ctrl+R(AiChat.vue:324);i18n 文案已备(zh-CN/aiChat.ts:156)但零模板引用
  • I7. 无输入历史快速重发——ArrowUp 仅用于浮层导航,无发送历史栈(grep inputHistory 全仓 0 命中)

P3

  • I8. 停止后队列继续 drain(useAiLifecycleEvents.ts:161),「停止」与「全部停下」预期不符,多轮排队场景继续耗额度;方向:停止时队列非空给一次性提示
  • I9. 附件通道仅图片,文本/代码文件无入口(粘贴仅认 image/、拖入仅过滤图片、@ 仅三类实体)
  • I10. 「可点继续重试」文案名实不符——重试是整轮重跑,已完成工具会重新执行(副作用重复风险);文案先对齐为「重试」
  • I11. 输入框手工拉伸高度被下一次按键压回(autoResize 强制 120px,CSS 却允许 resize:vertical 至 200px)
  • I12. 快捷键零提示无聚合说明(Ctrl+N/R/L/K 无任何 UI 提示)
  • I13. 无「聚焦输入框」快捷键(Ctrl+K 去向是会话搜索)

本动线最大成本热点(3 条)

  1. 改措辞/重问动线整体断裂(I2+I6+I7):高频动作「换个问法」唯一路径是重打整条字;修 I2 一项回收最大成本
  2. 停止能力窄窗(I1+I3+I8):生成失控时用户实际不可干预,信任层面伤害
  3. 失败恢复的可读性与不丢字承诺(I4+I5+I10):每次失败额外付理解成本+重打成本

三、动线二:回复消费(13 项,其中 2 项已修)

正面确认:scroll anchor 策略成熟(跟随意图锁存+上滑不拽走+回底按钮)、归档/压缩段折叠克制、JSON 结果弱化为灰块、审批卡自动展开定位、失败命令输出默认展开。

已修(本会话,工作区待提交)

  • R1. read_file/list_directory 单行摘要为空——已补 TOOL_SUMMARIES 登记(useToolCard.ts),复用现有 i18n key
  • R2. 流式与重载后分组折叠态不一致——默认折叠改造时已落地「签名变化幂等补齐」watch(ToolCardList.vue:158-168)

P2

  • R3. 长回复固定点击税——>1500 字符默认折叠到 400px(MessageList.vue:674/687-693),读完整回复必点「展开」且不知道藏了多少;按钮文案仅「展开/收起」无剩余量提示。日常高频:每次消费长回复 +1 点击 +1 决策。方向:按钮带量提示(「展开 · 约 3 屏」)或首屏+提示条
  • R4. 代码块无复制按钮/语言标注——useMarkdown.ts:120-125 裸 pre>code;复制单段代码全靠手动划选(长块易断);语言仅存 class 不可见。取用代码是最高频取用物。方向:pre hover 复制按钮+语言角标(marked renderer 注入)
  • R5. 历史回找零工具——唯一搜索入口匹配仅会话标题(ai.ts:290-295);无会话内消息搜索;叠加多层折叠(工具组/长消息/归档段),找旧结论成本随会话长度线性上涨。方向:消息文本搜索(后端已持久化,加 IPC+高亮跳转)
  • R6. 文件路径/grep 命中位置不可复制不可点击——ToolResultBody.vue 全部纯 span;想把 AI 读过的文件拿去本地操作只能手动重敲。方向:file-bar path 点击复制+可选「打开」
  • R7. C2(见一章):异常序列无聚合警示;多工具轮无「N 个工具 · M 失败」汇总行,失败信号分散单卡红边,10+ 卡需逐行扫

P3

  • R8. 流式中裸 diff 渲染为普通段落,完成后突变红绿代码块,阅读锚点丢失(wrapNakedDiff 仅完成态调用;流式 parseBlock 复用纯函数即可)
  • R9. 单条消息内无目录/锚点,长方案型回复线性滚动找章节
  • R10. 上翻阅读时新轮次事件强制收起正浏览的工具卡(collapseInactive 不判 isFollowingBottom)
  • R11. 回到底部按钮无「新内容」信号,非跟随态下新回复到达无提示
  • R12. write_file 完成后无 diff 回看;patch_file diff 截 120 行无展开入口
  • R13. assistant 时间戳每条常显(~14px/条),信息价值低;方向:并入 hover 或间隔 >5min 才显

本动线最大成本热点(3 条)

  1. 读完一条长回复的固定点击税(R3+R4):全动线最高频重复成本
  2. 历史回找零工具(R5):长会话痛点无解
  3. 单行折叠的信息量缺口(R6+R7):「扫一眼判断它干了什么」退化为逐卡点开

四、动线三:多会话与审批(12 条)

三条 P1 经主代理独立核验(useAiEvents.ts:95-126、conv_state.rs:59-67 均复读确认)。

P1

  • V1. 侧栏只有「生成中」一个状态点,等审批伪装成在跑——ConversationSidebar.vue:134 唯一状态指示 ai-conv-gen-dot;后端 conv_state.rs:66 注释明文「审批挂起期间 ConvState 仍为 Generating(审批是 loop 内暂停点,非独立状态)」;前端 useAiEvents.ts:124-126 AiApprovalRequired 到达也 setConvState('generating')。多会话并行时「哪个任务卡在我这里」无法从列表判断,用户会无限等一个永远不动的会话。方向:ConvState 增加 awaiting_approval 态(后端设计决策需 revisiting)+侧栏三态徽标
  • V2. 非活跃会话的审批事件被整体丢弃——后台会话审批是黑洞——useAiEvents.ts:95-118 isCurrent 守卫仅特判 AiCompleted/AiError/AiHelpRequired,AiApprovalRequired/AiDirAuthRequired 直接 return 不进 pendingApprovals;而浮层(ApprovalOverlay.vue:54)/系统浮窗(useApprovalPopup.ts:69)/顶栏徽标(TopBar.vue:15)全部只读 pendingApprovals;切会话时 useAiConversations.ts:460 还整体替换列表。并行任务每次等审批=任务停摆+人肉轮询。方向:pendingApprovals 改跨会话聚合(per-conv Map+汇总视图)
  • V3. 后台会话完成/失败零通知——useAiEvents.ts:97-111 后台终止仅 refresh+drain;grep unread|setTitle|notification 全仓无命中;托盘仅「显示主窗口/退出」两菜单(tray.rs:18-40),无 OS notification 插件。后台产出沉睡,多任务=人肉轮询。方向:后台终止类事件经现有 ai-toast 通道弹本地通知+托盘 tooltip

P2

  • V4. 系统浮窗审批跳过高风险二次确认——ToolCard 内 High 风险工具强制 useConfirm(useToolApproval.ts:26-29),浮窗 onApprove 直调 runApproval(ApprovalPopup.vue:421-423)无 HIGH_RISK 检查,且数字键「1」即批准首条(:263-293);注意力分散场景单键放行 run_command/delete_file
  • V5. 审批卡片/浮层/浮窗均不显示所属会话——conversationId 数据已在(useAiToolEvents.ts:67)但 UI 从不渲染;「打开审批面板」落的又是主窗口当前活跃会话,未必是审批所属会话
  • V6. 切会话清空全部审批计时器且重置倒计时;后台审批实际「永不超时」——switchConversation 等均调 clearAllApprovalTimers()(useAiConversations.ts:279),切回重新起算 15min;前端计时器是自动拒绝唯一驱动者(aiShared.ts:160-187),后端 cleanup_expired_approvals 仅 try_continue_agent_loop 入口惰性触发——没人碰永不清理。修 V2 时需一并重做,否则黑洞换位置
  • V7. 排队消息仅活跃会话可见,无侧栏提示;且队列倒计时不走钟——AiChat.vue:69-70 只渲染当前会话队列,队列却按会话保留,切走彻底不可见(回来后 drain 造成重复发送);AiChat.vue:400-407 queueWaitSeconds 是读 Date.now() 的 computed 无响应式时钟,(Ns) 冻结,30s 超时强发按钮可能永不出现
  • V8. 输入草稿全局单份跨会话串台——ChatInput.vue:148 单例 ref,切会话不动草稿;给 A 打半段话切到 B 顺手发出=错发进 B。方向:草稿按 activeConversationId 存取(小改大收益)
  • V9. 会话搜索只匹配标题不含消息内容,placeholder 未披露范围(ai.ts:290-296)——与 R5 同修点合并

P3

  • V10. 重命名仅双击且提示只在标题为空时显;「...」菜单无重命名项;Ctrl+N 生成中静默失效(AiChat.vue:315-318 有 streaming 守卫)与按钮新建无守卫自相矛盾,与 F-09 真并发语义冲突
  • V11. 分离窗口默认置顶(useAiWindow.ts:86)盖住其他应用;reattachPanel「一键收回全部分离窗」全仓零调用;分离窗切会话写共享 KV df-ai-active-conv,重启后主窗口恢复到分离窗最后看的会话
  • V12. 切会话滚动位置不保留一律回底部(MessageList.vue:745-747),回读被打断需重新滚找

本动线最大成本热点(3 条)

  1. 「需要我」信号全链缺失(V1+V2+V3 同根):审批等待/完成/失败三关键事件在后台会话场景下不改状态点、不进聚合、无通知——事件路由与状态层都按单会话设计,多会话并行实际用法退化为「人肉轮询每个会话」
  2. 审批动线只为当前会话服务(V2+V5+V6 连带):聚合层单例、计时器切会话清空、条目无来源,须一并重做
  3. 轻量状态跨会话串台(V7+V8):制造「发错会话/重复发送」直接伤害,改动小收益立竿见影

五、合并优先级排序(四源:9-28 走查 + 本轮三动线 + 截图实证)

按「日常主入口」受伤程度排批次(9-28 的 17 项去重并入,I/R/V 编号沿用上文):

第一批:日常使用直接受伤(P1 级)

  1. I1 停止能力窄窗——失控不可干预(截图循环实证),改 per-conv generating 判定,与 I3 快捷键一并修;小改大收益
  2. I2 编辑入口死链(=9-28 P1)——下游链路完整只缺按钮,MessageItem 用户气泡补回编辑按钮即通,单点修复回收最大成本
  3. G2 破坏性回环熔断(一章)——机制层一行模式拦整类自毁循环,后端 helpers/mod 各一小块
  4. V2+V5+V6 审批聚合链重做——非活跃会话审批黑洞(V2 是本动线最高成本)+条目会话来源+计时器 per-conv,三者须一并做否则黑洞换位置;工程量最大,独立排期
  5. V1+V3 后台信号轻量面——侧栏 awaiting_approval 状态点(需 revisiting conv_state.rs:66 设计决策)+后台终止事件本地通知,与 #4 可分阶段先行

第二批:高频成本(P2)

  1. V8 输入草稿 per-conv(小改,消除「发错会话」直接伤害)
  2. R3 长回复展开带量提示 / R4 代码块复制+语言角标(消费端最高频)
  3. I5+9-28#7 错误分类与兜底文案(429 实证 55% 占比)
  4. I4+9-28#4 AiError 静默清队列 → 回填或提示
  5. 9-28#2 rejectedHint key 错(一行)/ 9-28#3 英文环境重试气泡残留(匹配逻辑改 i18n)
  6. 9-28#6 错误气泡按 convId 路由 / 9-28#8 审批徽标跨会话过滤(与 #4 聚合链协同)
  7. V4 浮窗审批高风险二次确认(安全语义对齐 ToolCard)
  8. V7 排队可见性+响应式时钟(强发自救入口修复)
  9. R5+V9 消息内容搜索(会话内查找+会话搜索范围,中等工程量合并一个检索底座)
  10. R6 路径可复制 / 9-28#5 清队列残留两处收口
  11. I6 重新生成按钮(文案已备)/ R7 轮级失败汇总行+C2 异常聚合警示

第三批:P3 攒批

9-28 #9-#17 与 I8-I13、R8-R13、V10-V12、C1/C3 按主题攒批处理;其中 9-28#17(图片默认上传外部服务硬编码域名)涉及开源边界,需单独决策。

销账核对项

  • 9-28 报告已知项 #6:B-260619-04 步骤③实际已落地(MessageList.vue:335-341),建议 todo.md 销账

六、方法与证据

  • 动线一/二/三:走查代理只读走查,主代理独立核验关键结论——动线一 I1(ChatInput.vue:290-297+useAiPendingState.ts)、动线二 R1/R2(useToolCard.ts+ToolCardList.vue 复读,均已修)、动线三 V1/V2/V3(useAiEvents.ts:95-126 复读+conv_state.rs:59-67 确认审批挂起状态为设计决策)
  • 截图实证:用户提供的真实会话截图,签名判定逐调用套 helpers.rs:275/325 规则复算;常量值读 mod.rs:217/225/251 确认;「5/5」徽标语义经 AiChat.vue:218-224+ai.ts:58 对照确认
  • 9-28 走查 17 项:主代理抽查三项(rejectedHint key、start-edit 死链、硬编码'正在重试(')均确认未修
  • 已修两项(R1/R2)在工作区待提交改动中,连同工具卡默认折叠改造一并提交