Files
DevFlow/docs/02-架构设计/构想审查/aichat交互体验改进方案-2026-06-14.md
绝尘 998a2f243d 文档: 架构方案文档(意图识别论证+多主题愿景/论证+文档物理分类+边界清晰化)
squash合并:
- 意图识别层论证(8维度+10业界佐证)
- 多主题上下文管理愿景+并存论证+补充论证(多轮agentic)
- 架构设计文档物理分类(四子目录+INDEX+命名规范+引用同步+边界清晰化)
- 前端架构技术债清单归档
2026-06-19 15:04:04 +08:00

13 KiB
Raw Blame History

AIChat 交互体验改进方案

创建: 2026-06-14 | 状态: 待讨论 范围: 消息发送、流式渲染、对话管理、错误恢复、技能/Provider、窗口布局等非授权类交互


一、消息输入与发送

1.1 无法编辑已发送消息

现象:用户发出消息后发现措辞有误,只能重新打一条新消息。无法像 ChatGPT/Claude 那样编辑上一条用户消息并重新生成。

根因sendMessageuseAiSend.tspush 后的消息是只追加不可变的。前端没有编辑入口,后端 ai_chat_send 也没有"替换最后一条 user 消息并重跑"的语义。

方案

  • 用户消息气泡 hover 显示"编辑"按钮
  • 点击后消息内容回填到输入框,用户修改后发送时后端截断该消息之后的所有历史(含 AI 回复),重新跑 agentic loop
  • 后端新增 ai_chat_edit 命令,接收 message + 截断位置,替换 messages 数组中对应 user 消息并清掉后续消息,然后走正常 run_agentic_loop

1.2 无法重新生成 AI 回复

现象AI 回答不满意时没有"重新生成"按钮,只能重新措辞追问。

根因AiChat.vue 的 AI 消息气泡上没有任何操作按钮。后端也没有 ai_regenerate 命令。

方案

  • AI 消息气泡 hover 显示操作栏(复制 | 重新生成)
  • "重新生成":后端删除最后一条 AI 消息,用倒数第二条 user 消息重新触发 run_agentic_loop
  • 后端新增 ai_regenerate 命令

1.3 无法一键复制消息内容

现象:代码或文本只能手动选中复制。流式渲染中选中文字会被后续 delta 打断。

根因:消息气泡上没有复制按钮。

方案

  • AI 消息气泡 hover 显示"复制"按钮
  • 点击后 navigator.clipboard.writeText(msg.content)toast 提示"已复制"
  • 代码块单独提供"复制代码"按钮hover 代码块右上角浮出)

1.4 缺少 @ 实体引用

现象用户无法在输入时引用某个项目、任务或文件。描述需求时无法精确指定上下文AI 可能猜错对象。

根因:输入框只有 / 技能联想,没有 @ 实体引用机制。

方案

  • 输入框支持 @ 触发实体联想浮层(复用技能联想的 popover 架构)
  • 联想源:项目列表、任务列表、最近编辑的文件
  • 选中后在消息中展开为 [项目: u-desk] 等标记文本,后端 system prompt 注入对应实体的上下文摘要

1.5 输入框高度过紧

现象textarea 最大高度 120px约 5-6 行),超过后内部滚动。用户写长提示词时看不到全貌。

根因autoResizeMath.min(el.scrollHeight, 120) 限制太紧。

方案

  • 最大高度提升到 200px约 10 行),超过后再内部滚动
  • 或改为可拖拽调整高度(底部 resize handle

二、流式渲染与消息展示

2.1 流式渲染中无法稳定选中文字

现象AI 正在流式输出时,用户尝试选中已渲染的文字,新的 delta 触发 DOM 更新导致选区丢失。

根因renderContent 在流式时返回 streamingHtml.valuerAF 节流重 parse每次更新都 v-html 替换整个 DOM 子树,浏览器选区被清除。

方案

  • 方案 A推荐检测到用户正在选择文字时selectionchange 事件 + 选区非空且在消息容器内),暂停 rAF 流式 parse选区结束后恢复
  • 方案 B流式渲染时在已完成的块blockCache 命中的块)上使用独立 DOM 节点不参与 v-html 替换,仅末块动态更新

2.2 代码块无语法高亮、无复制按钮

现象Markdown 代码块只有纯文本渲染,没有语法高亮,也没有一键复制按钮。

根因useMarkdown composable 配置了 marked + DOMPurify但没有集成 highlight.js / Prism 等高亮器。

方案

  • 集成 highlight.js体积小、语言全在 marked renderer 的 code 回调中调用 hljs.highlightAuto 或按 info string 指定语言
  • 代码块右上角浮出"复制"按钮纯前端hover 显示)
  • 高亮器按需加载(与 marked 一样后台预热,不阻塞首屏)

2.3 历史消息无分页懒加载

现象:切换对话时 switchConversation 一次性加载全部 messages 到 state.messages。超长对话可能一次加载几百条消息。

根因switchConversationuseAiConversations.ts直接 state.messages = rawMsgs.filter().map(),无分页。

方案

  • 首次加载最近 50 条,滚动到顶部时加载更多(前端 slice + 后端支持 offset/limit 查询 messages
  • 或前端全量加载但虚拟滚动只渲染可视区域(见 7.2

2.4 消息不显示时间戳

现象:消息气泡上不显示发送时间。用户无法判断某条消息是多久前发的。

根因:模板中 AI/用户消息都没有渲染 msg.timestamp

方案

  • 消息气泡下方或侧边以极小字号9px+ dim 颜色展示相对时间(formatRelativeZh
  • hover 时 tooltip 展示完整时间

三、对话管理

3.1 对话搜索缺失

现象:侧栏对话列表只能滚动浏览,没有搜索框。对话多了之后找不到特定对话。

根因:侧栏 .ai-conv-list 直接渲染 groupedActive,上方只有"新建"按钮,没有搜索输入框。

方案

  • 侧栏 header 下方增加搜索输入框(实时过滤 state.conversations,匹配 title
  • 搜索时取消分组,按相关度/时间平铺展示
  • 支持 Ctrl+K 快捷键聚焦搜索框

3.2 对话无法置顶

现象:没有置顶或收藏功能。重要对话会被新对话挤到下面。

根因:对话列表只按 updated_at 排序,无 pinned 字段。

方案

  • ai_conversations 表增加 pinned INTEGER DEFAULT 0
  • 排序逻辑改为 pinned DESC, updated_at DESC
  • 侧栏对话项 hover 显示"置顶/取消置顶"按钮(图钉图标)

3.3 对话无法导出

现象:无法将对话导出为 Markdown / JSON / 文本文件。

根因:没有导出 IPC 命令和前端入口。

方案

  • 后端新增 ai_conversation_export(conv_id, format) 命令,支持 markdown / json / txt 三种格式
  • 前端侧栏对话项 hover 显示"导出"按钮,或对话操作菜单中提供
  • Markdown 格式:## 用户 / ## 助手 交替,代码块保留围栏

3.4 新建对话强制中断已有生成

现象:用户在 AI 生成中点"新建对话",后端 ai_conversation_create 会强制复位 generating=false + 置 stop_flag,中断当前生成。

根因commands.rs ai_conversation_createif session.generating 分支主动结束生成B-260615-10 设计)。

方案

  • 生成中点"新建对话"时弹 ConfirmDialog"当前对话正在生成,确定要新建对话并中断吗?"
  • 用户确认后才执行中断+新建;取消则不操作
  • 或改为不中断:新对话仅切换视图,旧对话后台继续生成(需 AiSession 多实例化支持,属远期方案)

四、错误处理与恢复

4.1 错误气泡无操作入口

现象:错误气泡只显示一行文字(如 [GLM-4] AI 调用失败(HTTP 401): Unauthorized),没有"重试"按钮或"去设置"链接。

根因AiError 事件只携带 error: String,前端 handleEvent push 一条 isError: true 的消息气泡,无操作按钮。

方案

  • 错误气泡底部增加操作按钮区:
    • 重试:取上一条 user 消息重新发送(调 ai_chat_send
    • 去设置(仅 401/403/Provider 未配置时显示):跳转到 Settings → AI Tab
  • 错误消息结构扩展:后端 AiError 增加 error_type: Option<ErrorType> 枚举(auth / network / timeout / provider_config / unknown),前端据此决定显示哪些按钮

4.2 流式中断丢失已生成文本

现象网络波动导致流式中断idle timeout 或 mid-stream error已接收的文本被丢弃stream_llm return None → agentic.rs emit AiError 并退出),用户看到错误气泡,之前生成的几百字全部丢失。

根因stream_llm 遇到错误时 return None,不保留已接收的部分文本。agentic.rs 收到 None 后直接结束。

方案

  • stream_llm mid-stream error 时改为返回 Some((partial_text, tool_calls, usage)) + 一个 incomplete: bool 标志
  • agentic.rs 收到 incomplete=true 时:
    • 已有文本正常入库(标注 truncated
    • emit AiCompleted(而非 AiError让前端正常展示已生成内容
    • 在消息末尾追加系统提示:"⚠ 响应因网络中断不完整"
  • 前端 AI 消息气泡底部显示"继续生成"按钮(用最后一条 user 消息重新触发,后端识别不完整消息做续写或重跑)

五、技能与 Provider

5.1 Provider 切换零反馈

现象:点击 provider bar 循环切换 providercycleProvider),切换后只在 bar 上显示名字变化,没有 toast 或动画反馈。

根因cycleProvider 直接调 store.setProvider,无 UI 反馈。

方案

  • 切换后 toast 提示"已切换到 {providerName}"
  • provider bar 切换时加 0.15s 淡入动画
  • bar 上增加 provider 状态指示model 名称小字展示)

5.2 技能联想不展示参数用法

现象:技能联想浮层显示 name + description + source但不显示技能的参数格式或示例。选中后 placeholder 里的 argument_hint 太短。

根因:浮层设计只展示概要信息,argument_hint 仅在选中后作为 placeholder 显示。

方案

  • 联想浮层每项增加一行参数提示(argument_hint 以等宽字体小字展示)
  • 选中技能后输入框上方 chip 展示完整的参数格式说明(而非仅 name + description
  • 或选中技能后自动在输入框填入模板骨架(如 /commit ),光标定位到参数位置

六、窗口与布局

6.1 侧栏宽度不可调

现象:侧栏固定 160px不可拖拽调整宽度。长对话标题被截断。

根因.ai-conv-sidebar { width: 160px; min-width: 160px; } 硬编码。

方案

  • 侧栏右边缘增加 2px 拖拽条(cursor: col-resize
  • 拖拽时实时更新 width范围 120~280px
  • 宽度持久化到 df-ai-ui 设置(与 sidebarOpen/maximized 同存)

6.2 分离窗口关闭后生成态不同步

现象:分离窗口关闭时,如果正在生成,主窗口虽然 panelOpen=true 恢复,但 state.streaming / state.currentText 可能不同步——如果生成中的对话不是主窗口当前活跃对话,主窗口看不到生成态。

根因:分离窗口关闭走 closeDetachedWindow,清了 localStorage 快照,但主窗口的状态依赖事件路由自然恢复。

方案

  • 分离窗口关闭时,主窗口检测 state.generatingConvId 非空,自动切换到正在生成的对话
  • 或在主窗口 header 显示"对话 X 正在生成中"提示条,点击切换过去

七、其他交互细节

7.1 无键盘快捷键

现象:除了 Enter 发送 / Shift+Enter 换行,没有其他快捷键。

方案

快捷键 功能
Ctrl+N 新建对话
Ctrl+K 搜索对话
Ctrl+L 清空当前对话
Ctrl+Shift+C 复制最后一条 AI 消息
Ctrl+R 重新生成最后一条 AI 回复
Esc 关闭面板(嵌入模式)/ 关闭窗口(分离模式)
Ctrl+B 切换侧栏

7.2 消息列表无虚拟滚动

现象:消息列表直接 v-for 渲染所有消息,长对话几百条消息全量渲染 DOM滚动卡顿。

根因:没有使用虚拟滚动库。

方案

  • 集成 vue-virtual-scroller 或自研 IntersectionObserver 懒渲染
  • 仅渲染可视区域 ±缓冲区的消息节点
  • 注意:流式渲染的最后一条消息需要始终保持挂载

7.3 空状态无引导

现象:首次打开 AI Chat有 provider 配置),空状态只显示标题+提示语,没有示例问题或快捷操作。

方案

  • 空状态展示 3-4 个示例问题卡片(如"帮我创建一个新项目"、"查看当前任务列表"、"分析这段代码的问题"
  • 点击卡片自动填入输入框并发送
  • 无 provider 配置时展示"去配置 AI Provider"引导按钮

7.4 对话标题生成对用户不透明

现象:对话标题由后端 ensure_conversation_title 异步生成,侧栏对话从"新对话"突然变成某个标题,没有过渡。

方案

  • 标题生成后加 0.3s 淡入动画
  • 或在标题前加小图标标识"AI 自动生成"(可 hover 查看)

八、落地优先级

批次 痛点 理由
第一批 2.1 流式选中文字、1.2+1.3 消息操作栏(复制/重新生成、4.1 错误重试、4.2 断线保文 每次对话都会遇到,最高频痛点
第二批 2.2 代码高亮+复制、3.1 对话搜索、7.1 键盘快捷键、3.4 新建不中断生成 显著提升日常效率
第三批 1.1 编辑重发、1.4 @ 引用、3.3 导出、7.2 虚拟滚动、6.1 侧栏可调 按需推进,锦上添花
第四批 2.3 分页加载、2.4 时间戳、3.2 置顶、5.1+5.2 技能/Provider 反馈、7.3+7.4 空状态/标题 打磨细节