From 3f3f5053a45a5130394ba69a59f0f1aa713faca9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BB=9D=E5=B0=98?= <237809796@qq.com> Date: Thu, 1 Oct 2026 02:41:21 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=87=E6=A1=A3:=20aichat=20=E8=B5=B0?= =?UTF-8?q?=E6=9F=A5=E4=B8=8E=E9=9B=86=E6=88=90=E5=8F=AF=E8=A1=8C=E6=80=A7?= =?UTF-8?q?=E7=A0=94=E7=A9=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...devflow集成u-claw-可行性研究-2026-09-28.md | 94 ++++++++++ .../u-switch接入分析-2026-09-28.md | 53 ++++++ docs/05-代码审查/aichat交互走查-2026-09-28.md | 53 ++++++ .../aichat可用性与交互成本研究-2026-09-29.md | 174 ++++++++++++++++++ 4 files changed, 374 insertions(+) create mode 100644 docs/02-架构设计/专项设计/devflow集成u-claw-可行性研究-2026-09-28.md create mode 100644 docs/04-功能迭代/u-switch接入分析-2026-09-28.md create mode 100644 docs/05-代码审查/aichat交互走查-2026-09-28.md create mode 100644 docs/05-代码审查/aichat可用性与交互成本研究-2026-09-29.md diff --git a/docs/02-架构设计/专项设计/devflow集成u-claw-可行性研究-2026-09-28.md b/docs/02-架构设计/专项设计/devflow集成u-claw-可行性研究-2026-09-28.md new file mode 100644 index 0000000..f34e051 --- /dev/null +++ b/docs/02-架构设计/专项设计/devflow集成u-claw-可行性研究-2026-09-28.md @@ -0,0 +1,94 @@ +# devflow → u-relay → u-claw 集成可行性研究 + +> 2026-09-28 · 状态:可行性已定,待 devflow/u-claw 两侧细勘察补全 +> 需求原话:「考虑 devflow -> u-relay -> u-claw, 让 devflow 集成 u-claw 能力」 + +## 一、定性结论 + +**不是从零集成,是复用已生产验证的通路,唯一工作面在 devflow 侧接入。** +u-claw 通用 WS 渠道已于 2026-08-29 随 u-ppt 试点上线(dev-ops 容器,心跳正常),u-relay 零改动,协议有现成 TS 参考实现。 + +## 二、知识脉络时间线(防断档,按此追溯) + +| 时间 | 事件 | 留痕 | +|------|------|------| +| 2026-08-29 01:50 | swarm 试点拍板:u-claw 加通用 WS 渠道 + u-ppt Agent 面板经 u-relay 中继,点对点配对不实现 G1 | decisions.jsonl `2026-08-29 01:50` | +| 2026-08-29 02:25 | u-claw b401480 push + 发版:WS 渠道随 start 部署,`cfg.WS==nil` 跳过零行为变化;dev-ops 容器 20260829-0206 心跳正常 | decisions.jsonl `02:25` | +| 2026-08-29 02:47 | Node 探针模拟 miniapp 身份 E2E:hello_ack→agent_request→progress→agent_result 全通,2s 双端互证 | decisions.jsonl `02:47`;u-relay `scripts/e2e-miniapp.mjs` | +| 2026-08-29 起 | u-claw WS 渠道持续演进:368beba 初版(device 身份+独立 ws 子命令)→490280e 热重建僵尸连接→08e3f02 空闲超时分级判死 | u-claw git log | +| 2026-09-28 | 并行会话完成 u-family×devflow 四仓组合分析,落 KMS(基线与本文档一致) | `E:/wk-lab/docs/kms/u-family-devflow-组合分析.md` | +| 2026-09-28 | 用户提出 devflow 接入同一链路;本次三仓勘察(u-relay/devflow 已完,u-claw 收尾中) | 本文档 | + +## 三、链路事实(勘察确认,非推断) + +### 链路拓扑 + +``` +devflow(WS客户端,Miniapp角色) ──wss──> u-relay(9180,零改动) <──出站WS── u-claw WS渠道(Device角色,已上线) +wss://u-relay.1216.top/ws/miniapp device_id 配对 /ws/device +``` + +### 协议(三层,均有实现背书) + +- 传输层(relay):Hello{kind,device_id,token} 10s 握手超时 → hello_ack;帧=BroadcastMessage{device_id,kind,source,from,payload,ts};方向路由 Event→Miniapp / Command→Device;帧上限 1MiB;miniapp ping relay 本地回 pong +- 业务层(u-claw↔客户端,relay 纯透传):`agent_request{request_id,prompt}` 上行 → `agent_progress{request_id,text}` / `agent_result{request_id,text}` 下行 +- 心跳:ping/pong 保活;u-claw 侧有空闲超时分级判死 + 热重建防僵尸连接 + +### 现成参考实现(最大捷径) + +- **u-ppt `src/core/relay.ts`(223 行,TS)**:`RelayClient` 类,握手看门狗/hello_ack/progress|result 分发/`agent_request` 帧,devflow 前端同为 TS 可直接移植 +- u-relay `scripts/e2e-miniapp.mjs`:探针脚本,协议逐字段参考 +- u-claw `368beba` 起的 WS 渠道源码 + +### 部署拓扑(KMS 服务索引) + +- u-relay:suke-dev systemd `u-relay.service`,9180,nginx wss 终止,musl 单二进制 ~3.6MB +- u-claw:多实例容器化(5+ bot),dev-ops 容器已含 WS 渠道版本;u-switch 模型路由公网口 `switch.1216.top`(devflow 若只要模型调用可直连,不必经 u-claw) + +## 四、风险与决策点 + +1. **鉴权债**:relay 全局共享 token(`devflow-relay-default-token`,per-device token 留 Phase3 未做),持 token 者可冒充任意 device_id。自用可接受;devflow 与 miniapp 共用 relay 时建议补 +2. **能力边界(勘察中)**:u-claw WS 渠道背后是完整 agent(会话/技能/意志卡)还是简化执行,决定「集成 u-claw 能力」含金量 —— 待 u-claw 侧勘察 +3. **流式粒度**:progress/result 块级聚合非逐 token,aichat 首版按块级渲染 +4. **多轮上下文**:request_id 单次请求语义,跨轮会话锚点在 u-claw 侧如何维持 —— 待勘察 +5. **离线即丢**:relay 无离线缓存,devflow 需自行处理 u-claw 掉线(重试/超时) + +## 五、devflow 侧接入方案(devflow 勘察已回,2026-09-28) + +### 关键认知:角色方向 + +devflow 现有 df-tunnel 是 **Device 角色**(连 `/ws/device`,服务 miniapp 反控通路)。接 u-claw 须**另开一条 Miniapp 角色连接**(连 `/ws/miniapp`,仿 u-ppt `relay.ts`),用**独立 device_id** 与 u-claw 配对(先例:`u-ppt-dev-01`),与现有 miniapp 通路隔离,两路并存互不干扰(relay 同 device_id 支持多连接,但建议配对独立)。 + +### 接入形态三选(按成本升序,可组合) + +1. **AI 工具形态(推荐首发)**:u-claw 包装为 aichat 工具——`declare_tool!` 注册 `ask_uclaw(prompt)`,devflow 的 AI 对话中可直接委托 u-claw 处理(用它侧的 agent/技能/Claude Code),progress 流式回流工具卡片。改动面:`src-tauri/src/commands/ai/tools/` 加一个 register 文件 + `tool_registry.rs:508` 挂载。零侵入 agentic loop。 +2. **消息渠道形态**:出站仿 tunnel-subscriber 再订阅 `ai_event_bus`(event_bus.rs:138),入站仿 remote_bridge 写 payload 路由器(`remote_bridge.rs:145` 加 cmd 臂)。适合「aichat 会话直接由 u-claw 代言」,改动大于形态 1。 +3. **反向 MCP**:devflow 已内嵌 MCP HTTP server(df-mcp,20 工具,`src-tauri/src/mcp.rs:45`),u-claw 可反向消费 devflow 能力——与形态 1/2 互补,不冲突。 + +### 传输层与配置 + +- WS 客户端:Rust 层新写轻量 client(TunnelClient trait 可并存新实现,`df-tunnel/src/tunnel.rs:61`)或前端 TS 移植 relay.ts;推荐 Rust 层(与工具形态同侧) +- 配置落位:地址/独立 device_id → `app_settings` KV(照抄 `relay_url` 模式,`lib.rs:194-217`);token → OS keyring(照抄 provider api_key 模式);默认关,对齐 u-claw `cfg.WS==nil` 零行为变化模式 +- 注意:现有 token 硬编码在 `lib.rs:219`(`devflow-relay-default-token`),本项实施时顺带配置化 + +## 六、待补槽位(勘察代理回报后增量更新) + +- [x] ~~devflow 侧~~ ✅ 2026-09-28 已填(第五节;另有:`build_provider`/`AiToolRegistry`/`EventBus`/`SettingsRepo`/keyring 扩展点清单已勘察,见会话或按第七节基线 grep 复核) +- [x] ~~u-claw 能力边界~~ ✅ KMS 组合分析已明:**u-claw = 常驻 `claude -p`(stream-json)的多渠道 Agent 网关**,WS 是继邮件/飞书后第三渠道(`wsch.go`),每渠道一条 chain=一个常驻 claude 子进程(prompt cache 热)——即 WS 渠道背后是**完整 Claude Code 能力**(技能/工具全量),「集成 u-claw 能力」含金量高 +- [ ] u-claw 侧收尾(勘察代理进行中):WS 渠道的多轮会话锚定细节(request_id↔chain 映射、会话持久化边界) + +## 关联阅读 + +- `E:/wk-lab/docs/kms/u-family-devflow-组合分析.md`(四仓画像+快照基线,本文档上游) +- `~/.claude/kms/服务索引-全机器.md`(部署拓扑) +- `~/.u-claw/willcard/decisions.jsonl`(8-29 试点留痕) + +## 七、增量基线(下次免重探索,只补漂移) + +``` +基线 HEAD(2026-09-28): + devflow 4840f7e u-relay 9a2d376 u-claw d8dc003 u-ppt 4e68773 +漂移检查命令(各仓): + git -C log --oneline <基线HEAD>..HEAD +关键漂移点:u-claw WS 渠道演进(ws 相关提交)、u-relay 角色鉴权(per-device token 是否落地)、devflow df-tunnel 变更 +``` diff --git a/docs/04-功能迭代/u-switch接入分析-2026-09-28.md b/docs/04-功能迭代/u-switch接入分析-2026-09-28.md new file mode 100644 index 0000000..897ad47 --- /dev/null +++ b/docs/04-功能迭代/u-switch接入分析-2026-09-28.md @@ -0,0 +1,53 @@ +# u-switch 接入 devflow 分析 + +> 2026-09-28 · 目标:评估让 devflow 的 AI 调用走 u-switch(本地 Anthropic 协议模型路由网关)需要做哪些。 +> 结论先行:**协议层已天然兼容,零代码改动即可打通主链**;真正要做的是 devflow 侧接入配置 + 两个小缺口决策(旁路端点、额度码透传);多租户/管理面属产品化远期,当前不需要。 + +## 一、两舱现状 + +| 维度 | devflow(客户端) | u-switch(网关) | +|------|------------------|-----------------| +| 协议 | 双协议适配器:OpenAI 兼容 + Anthropic 兼容(`crates/df-ai/src/anthropic_compat.rs`) | 仅 Anthropic `/v1/messages` + `GET /v1/models`(`main.go:800-802`) | +| 供应商模型 | 一个供应商 = base_url + key + 模型池,weight 负载均衡 + 跨供应商 fallback | 模型别名→物理名映射 + 主备有序 failover + provider 级分级熔断 | +| 密钥 | OS keyring,DB 不存 key | 手写 switch.yml(auth_key admin + auth_keys 成员) | +| 流式 | 自研 SSE 解析,兼容各厂商 chunk 变体 | SSE 首事件预读回退 + 中断补 error + usage 流式采集(纯透传不改写) | +| 用量 | 上游 usage 回传 + 缺失降级估算 | 透传原样 + SQLite 事后明细(无限流/配额/计费) | +| 工具调用 | 双协议标准 tool-use 语义 | 纯透传(Anthropic 语义内无障碍) | + +## 二、零改动已具备(已源码核对) + +1. **协议匹配**:devflow `provider_type="anthropic"` 走 `{base}/v1/messages`(anthropic_compat.rs:98-107)→ u-switch 数据面唯一数据端点即此。 +2. **鉴权头**:devflow 发 `x-api-key`(anthropic_compat.rs:501-506);u-switch checkAuth 接受 `X-Api-Key`(main.go:658-683)。 +3. **模型目录**:u-switch `GET /v1/models` 返回 `{data:[{id,display_name,type}]}` 字典序别名(main.go:763-785);devflow anthropic 探测恰好吃该形状(model_fetch.rs:103-126),字段交集设计命中。 +4. **header 透传**:u-switch sendUpstream 透传除 Authorization/X-Api-Key/Content-Length 外全部头(main.go:1081-1085),`anthropic-version` 原样到达上游。 +5. **SSE / usage**:u-switch 逐行保真透传不改写 → devflow 收到的 chunk/usage 与直连上游同构(message_start/message_delta 累计 output 等 anthropic_helpers.rs 假设不被破坏)。 +6. **工具调用 / thinking**:两边都是 Anthropic 语义透传;u-switch 的 thinking 自愈 + drop_thinking 与 devflow 的 thinking 处理互补不冲突。 + +## 三、要做的事 + +### A. devflow 侧接入动作(无代码,纯配置) + +1. Settings→ProviderPanel 新建供应商:provider_type=Anthropic,base_url=u-switch 地址(如 `http://127.0.0.1:6816`),key=u-switch 的 auth_key/成员令牌;default_model 填 u-switch 别名(逻辑名)。 +2. `ai_fetch_models` 拉别名列表 → **手工校准模型元数据**(关键):u-switch 返回的是逻辑别名,devflow 的 probe 按名字推断 context_window/模态会失真;路由过滤 `context_window >= estimated_context`(router.rs:101)可能误过滤 → 前端模型表手工改对 context_window 或关掉该模型。 +3. **路由双层决策**(人定):devflow 自带 ProviderPool weight + 跨供应商 fallback,与 u-switch 路由/failover 职责重叠。推荐起步姿势 = devflow 只保留 u-switch 一个启用 provider(本地 LB 自然退化,网关全权路由,保 prompt cache 亲和);或保留 devflow 多 provider 直连兜底、u-switch 只管其中一路。 + +### B. u-switch 侧小缺口(可选,按需排期) + +1. **旁路端点不接管**(真实缺口):u-switch 只认 `/v1/messages` + `/v1/models`。devflow 的知识库 embedding(`/v1/embeddings`)与图像生成(`/v1/images/generations`)走网关会 404。二选一: + - devflow 侧把这两类功能指向别的直连 provider(当前架构旁路本就不经主链,配置可分离); + - u-switch 加透传端点(违背其「Anthropic 协议专用」极简定位,不推荐)。 +2. **额度码透传**(体验优化):u-switch 全渠道耗尽固定回 502,devflow 的额度耗尽识别(429+「额度/上限」文本 → Fatal 不重试,stream_recv.rs:639-660)在网关后失效,会白重试 3 次。可选:全渠道耗尽时若最后根因是 402/401/429,透传该状态码而非 502。 + +### C. 产品化远期(仅 devflow 分发给多用户时才需要,当前不做) + +key 签发/撤销 API(现手写 yml)、配额/限流(现仅事后明细)、管理 UI(现仅 `/_switch` 三端点)、多租户渠道视图。u-switch 定位是单用户自用本地路由器,与 devflow「现自用」阶段匹配。 + +## 四、验证清单(打通步骤) + +1. [ ] u-switch 起本地实例,switch.yml 配 auth_key + ≥1 个 Anthropic 兼容渠道别名 +2. [ ] devflow 新建 Anthropic provider 指向 u-switch,`ai_test_connection` 通过 +3. [ ] `ai_fetch_models` 拉到别名列表,手工校准 context_window +4. [ ] 非流式对话一轮 → 流式长对话一轮(验证 SSE 透传 + usage 落库非估算) +5. [ ] 带工具调用会话一轮(验证 tool_use/tool_result 透传闭环) +6. [ ] 人为断上游验证 failover:devflow 侧应无感或收到可重试错误 +7. [ ] u-switch SQLite 核对 u_requests 明细与 devflow 消息级 usage 一致 diff --git a/docs/05-代码审查/aichat交互走查-2026-09-28.md b/docs/05-代码审查/aichat交互走查-2026-09-28.md new file mode 100644 index 0000000..575b3ba --- /dev/null +++ b/docs/05-代码审查/aichat交互走查-2026-09-28.md @@ -0,0 +1,53 @@ +# aichat 交互体验走查报告(2026-09-28) + +> 走查代理产出,主代理核验登记。背景:devflow 启动定位=日常 AI 交互调度中枢(替代飞书侧),按「日常主入口」标准走查。 +> 方法:per-conv 状态机/流式/审批/失败保文全链路代码走查 + 对照 todo.md 与 05/06 目录历史报告区分已知/新发现 + git 实证回归点。守 session-role-diagnose-only,未改代码。 + +**总体定性**:aichat 前端交互基建已相当成熟(per-conv 状态机、流式块级 memo、审批超时兜底、失败保文、乐观更新+回滚大量落地),历史登记债绝大多数已销账且与代码相符。本轮新发现集中在两类:**God 组件拆分时丢功能的链路断点**(编辑入口回归丢失为最重)与**多会话并发收口的残留缝隙**(队列/错误气泡/审批徽标的 per-conv 未收尾)。 + +## 已知已登记(核验确认,不重复报告) + +1. useAiSend(562行)/useToolCard(741行) 大文件债(fe-arch P0-1/P0-3)仍成立;useAiEvents 已拆 12 个 composable(WC-P2 销账属实) +2. 信息密度构想:痛点部分缓解(长消息1500字折叠/工具卡分组/归档段折叠已有),会话详情面板不存在、消息恒渲染(B-260618-01 有意移除虚拟滚动) +3. 压缩消息展示符合规范(compressed 原文展开+摘要置顶,MessageList.vue:641-659/843-847) +4. agentic 进度条 max 轮次降级省略(AiChat.vue:50 TODO,后端未透传 max_rounds) +5. 效率走查-2026-08-09 前端项(S2-3/F2-5/S1-2 等)不重复 +6. **todo 滞后**:B-260619-04 步骤③「新轮到达自动收起旧轮」标 ⏸️,实际 MessageList.vue:335-341 已落地,建议销账 + +## 新发现清单(按严重度) + +### P1 + +1. **编辑末条 user 消息入口丢失(回归)**——UX-09 下游链路完整(编辑态 prop/startEditFocus/editMessage 截断重生成全在),但触发按钮不存在:MessageList.vue:36 声明 emit('start-edit') 全文件无触发点;AiChat.vue:37 @start-edit 绑定与 :294-298 startEdit 悬空;MessageItem.vue:20-22 emits 仅 copy;.ai-msg-edit-btn 成死样式(MessageList.vue:1303-1325)。git 实证:2c2b2d7 提取 MessageItem 时按钮未迁移(旧 MessageList:1003 有),f719546 只清悬空声明。用户感知:已发消息想改措辞重问,无任何入口 + +### P2 + +2. **审批拒绝原因显示原始 i18n key**——useAiToolEvents.ts:90 与 useAiApproval.ts:90 用 t('ai.aiTool.rejectedHint'),正确 key 是 aiTool.rejectedHint(多加了 ai. 前缀)→ 拒绝后工具卡显示 key 字面量而非「用户拒绝了此操作」 +3. **英文环境重试过渡气泡永久残留**——收尾清理按中文硬编码匹配 '正在重试('(useAiLifecycleEvents.ts:62),en 文案是 "retrying (" → 切英文后 AiStreamRetry 气泡收尾时匹配失败不清除 +4. **AiError 静默清空该会话排队消息**——useAiLifecycleEvents.ts:166-171 出错即 filter 整个会话队列,无「排队消息已丢弃」提示;对照 drainQueue 失败路径专门不静默丢输入回填队首(useAiSend.ts:296-298),同域行为不一致 +5. **全局清队列残留两处(07-17 报告 P1-1 判已修销账,实际未收口)**——useAiApproval.ts:99(审批 IPC 失败 state.queue=[])与 streamingGuard.ts:103(forceResetStreaming,被 per-conv 看门狗超时调用)仍清全部会话队列;streamingGuard.ts:91-94 注释自知「queue 清理由 F-09 统一改」但未改 +6. **后台会话终止类错误气泡推错视图**——审批超时气泡(aiShared.ts:178-184)与看门狗超时气泡(useAiStream.ts:104-110)push 到当前活跃视图不按所属 convId 路由;hasCompletedTool 区分文案同样扫错视图(useAiStream.ts:87-97)。用户感知:B 会话后台卡死,A 会话冒「响应中断」 +7. **429/额度耗尽无分类提示且重试按钮照显**——friendlyError 仅四类匹配(aiShared.ts:26-32),AiErrorType 无 quota/rate_limit(types.ts:391)→ 429 落 unknown:原始英文直出+canRetry=true,额度未恢复时重试必再败(08-11 实证 429 占 55%;后端 retry.rs:65 亦归 Retryable) +8. **审批徽标跨会话计数误导且点击无反馈**——pendingApprovals 全局数组,TopBar 徽标(AiChat.vue:210)不按当前会话过滤;点击 scrollToFirstPending(MessageList.vue:299-310)在当前视图找不到卡静默 no-op;ApprovalOverlay/浮窗「打开审批面板」同样找不到卡 + +### P3 + +9. 消息删除能力缺失(全链无单条删除 IPC 与入口,仅整会话 clearChat) +10. user 消息无时间戳(ai-msg-time 仅 AI 分支渲染 MessageList.vue:1015-1020;.ai-msg-time--user :1267-1269 死样式) +11. compressed 分段死代码(messageSegments 已不产出 compressed 段,kind 类型 :632/sep 模板 :821-827/CSS :1539-1548 残留) +12. 会话级 token 总量只写不读(convTokenTotal 写入 useAiConversations.ts:396/useAiLifecycleEvents.ts:130-135,无 UI 消费——「会话详情面板」构想的现成数据源闲置) +13. 中间错误气泡重试错位(handleRetry 不带 msg 参数 MessageList.vue:592-601,恒重跑末轮而非该错误) +14. 批量审批部分失败无用户提示(useAiApproval.ts:156-159 仅 console.warn) +15. AiToolCallStarted 工具卡丢弃边缘竞态(仅末条 assistant 才 push,useAiToolEvents.ts:26-30) +16. 注释与实现矛盾(AiChat.vue:263-266 新建对话失败注释称错误气泡反馈,实际仅 console.error;实际由 withConvOp 兜底,无用户影响但注释误导) +17. 图片默认上传外部服务 file.1216.top(fileUpload.ts:9 硬编码默认,可 df-file-upload-base 覆盖)——业务域名入代码,对产品分发定位与开源边界是待决策项 + +## 修复优先建议(按「日常主入口」标准) + +- 第一批(日常使用直接受伤):#1 编辑入口回归、#4+#5 排队消息被静默丢、#6 错误气泡推错视图、#7 429 分类 +- 第二批(多会话体验):#8 审批徽标、#3 en 气泡残留、#2 i18n key +- 第三批:P3 项随批清理 + +## 走查覆盖文件 + +docs/todo.md、docs/06-前端开发/前端架构技术债-2026-06-19.md、docs/05-代码审查/{aichat-对话功能走查-2026-07-17,aichat效率走查-2026-08-09}.md;src/components/{AiChat.vue,ai/MessageList.vue,ai/MessageItem.vue,ai/ChatInput.vue,ai/ApprovalOverlay.vue,ai/EmptyState.vue,ToolCard.vue,ToolCardList.vue,ToolResultBody.vue};src/composables/ai/{useAiSend,useAiEvents,useAiStreamingEvents,useAiToolEvents,useAiLifecycleEvents,useAiApproval,useToolApproval,useToolCardHeader,aiShared,useAiStream,streamingGuard,useAiConversations,useApprovalPopup,fileUpload}.ts;src/stores/ai.ts、src/api/types.ts、src/i18n/zh-CN/{ai,aiTool}.ts、src/i18n/en/ai.ts、crates/df-ai/src/retry.rs(定向) diff --git a/docs/05-代码审查/aichat可用性与交互成本研究-2026-09-29.md b/docs/05-代码审查/aichat可用性与交互成本研究-2026-09-29.md new file mode 100644 index 0000000..e3d96c0 --- /dev/null +++ b/docs/05-代码审查/aichat可用性与交互成本研究-2026-09-29.md @@ -0,0 +1,174 @@ +# 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) + +6. V8 输入草稿 per-conv(小改,消除「发错会话」直接伤害) +7. R3 长回复展开带量提示 / R4 代码块复制+语言角标(消费端最高频) +8. I5+9-28#7 错误分类与兜底文案(429 实证 55% 占比) +9. I4+9-28#4 AiError 静默清队列 → 回填或提示 +10. 9-28#2 rejectedHint key 错(一行)/ 9-28#3 英文环境重试气泡残留(匹配逻辑改 i18n) +11. 9-28#6 错误气泡按 convId 路由 / 9-28#8 审批徽标跨会话过滤(与 #4 聚合链协同) +12. V4 浮窗审批高风险二次确认(安全语义对齐 ToolCard) +13. V7 排队可见性+响应式时钟(强发自救入口修复) +14. R5+V9 消息内容搜索(会话内查找+会话搜索范围,中等工程量合并一个检索底座) +15. R6 路径可复制 / 9-28#5 清队列残留两处收口 +16. 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)在工作区待提交改动中,连同工具卡默认折叠改造一并提交