Samien b4cb0c3a4a 更正三端验收报告中 iOS 基线的分支归属 1 month ago
..
results e26699c133 记录 Bot 优化包从历史正式版升级的成品验证结果 1 month ago
scripts c1c17c9bec 实现 Bot 持久化流式补丁并完成三端收发性能验收 1 month ago
DESKTOP-UPGRADE.md e26699c133 记录 Bot 优化包从历史正式版升级的成品验证结果 1 month ago
README.md b4cb0c3a4a 更正三端验收报告中 iOS 基线的分支归属 1 month ago
RUNBOOK.md c1c17c9bec 实现 Bot 持久化流式补丁并完成三端收发性能验收 1 month ago
baseline-android-long.png c1c17c9bec 实现 Bot 持久化流式补丁并完成三端收发性能验收 1 month ago
baseline-android-short.png c1c17c9bec 实现 Bot 持久化流式补丁并完成三端收发性能验收 1 month ago
baseline-web-long.png c1c17c9bec 实现 Bot 持久化流式补丁并完成三端收发性能验收 1 month ago
baseline-web-short.png c1c17c9bec 实现 Bot 持久化流式补丁并完成三端收发性能验收 1 month ago
optimized-android-long.png c1c17c9bec 实现 Bot 持久化流式补丁并完成三端收发性能验收 1 month ago
optimized-android-reading.png c1c17c9bec 实现 Bot 持久化流式补丁并完成三端收发性能验收 1 month ago
optimized-android-short.png c1c17c9bec 实现 Bot 持久化流式补丁并完成三端收发性能验收 1 month ago
optimized-ios-long-1.png c1c17c9bec 实现 Bot 持久化流式补丁并完成三端收发性能验收 1 month ago
optimized-ios-short-6.png c1c17c9bec 实现 Bot 持久化流式补丁并完成三端收发性能验收 1 month ago
optimized-web-long.png c1c17c9bec 实现 Bot 持久化流式补丁并完成三端收发性能验收 1 month ago
optimized-web-reading.png c1c17c9bec 实现 Bot 持久化流式补丁并完成三端收发性能验收 1 month ago
optimized-web-short.png c1c17c9bec 实现 Bot 持久化流式补丁并完成三端收发性能验收 1 month ago

README.md

Bot 模式三端收发优化

两个阶段均在三个仓库各自的 codex/bot-message-latency 分支、独立 worktree 中完成。没有合并主线、发布版本或安装到真机。

开发目录:/Users/samien/git/bot-message-latency/{yaoyao,yaoyao-mobile,yaoyao-android}。基线为 Web/服务端 bed10cb(0.4.16)、Android 8ed6079(1.3/168)。iOS 基线归属更正:从 main 52e9f12 创建分支后,实施时快进到了功能分支 codex/ios-chat-thinking-dots 的 aceb477(1.3/200),额外带入了 Markdown、媒体展示等提交;它不是主分支基线。因此本报告的 iOS 数据只能说明 aceb477 到收发优化版本的变化,不能排除相对 main 的整体性能回退。原工作区的暂存头像和其他文件未带入分支。

实现

第一阶段提交:Web/服务端 ae7c388,iOS 88f276e,Android b7733bf。

  • /api/app/capabilities 通过原有 CSRF 签发器返回 csrfToken,并设置 Cache-Control: no-store;移动端在进入 Bot 模式时预热,共用正在进行的初始化,旧服务端回退到 bootstrap。
  • 发送接口保留 run,增加 requestId、message、conversation、task、cursor。三端确认后解除发送状态,回执按实体版本合并,既不推进全局 SSE 游标,也不覆盖更新的事件。
  • 两秒未追上回执时才进行后台详情补偿;旧协议缺少游标时立即后台补偿。列表、能力和机器人刷新不再阻塞发送,补偿失败不改变已接受的提交结果。
  • 冻结提交时的上下文和请求 ID;重试保留原 ID。新草稿、修改后又恢复原文的草稿,以及切换会话后的草稿都不被旧确认清除。

第二阶段客户端提交:iOS 8e72ea2,Android 975bd76。

第二阶段:

  • 消息增加 revision,旧记录按零处理。纯文本追加使用持久化 message.patch:id、conversationId、conversationTaskId、baseRevision、revision、contentAppend、reasoningAppend。
  • 完整消息行与补丁日志在同一事务中提交后才推送;内部观察者仍得到完整消息。首次、重写、可见性/工具/附件变化及终态使用全量消息。
  • 服务端首段立即冲刷,后续 33 ms 合并;末尾有定时冲刷。摘要最多每 250 ms 更新,并补上暂停后最后一段摘要的尾刷。结束状态不再等待用量查询;迟到用量不能覆盖后续 run。
  • 三端先校验并还原补丁,再使用原有历史和快照合并逻辑。缺少基线或版本不连续时不推进游标,重新同步;重连保留已加载历史,快照未覆盖的旧任务缓存标记为待核对。
  • Web 保留已完成消息显示对象,避免破坏 v-memo;每个 Markdown 渲染器使用独立、固定配置的净化器,每段 HTML 仍经过相同净化规则。Bot 直接渲染服务端已合并的文本,普通聊天默认 80 ms 节流保持不变。
  • Android 根据实际布局的末尾锚点跟随,修复长回复和异步排版后停在消息开头的问题;用户拖动上翻后停止跟随。

协议协商与回退

新能力为 workspace-message-patch-v1,新客户端使用 /api/app/events/stream?format=patch-v1。没有能力时继续使用原协议。

新协议默认启用。设置 HERMES_YAOYAO_WORKSPACE_MESSAGE_PATCHES=0 并重启该兼容版本后端,可关闭补丁写入和协商,恢复全量传输及 100 ms 消息节奏。旧客户端实时接收按约 100 ms 有序合并的全量消息;遇到补丁历史重放时通过已有 reset 重新取得完整快照。非文本和终态前先冲刷待发文本。

事件日志已有新格式,因此回退应使用这个开关并保留本版日志兼容代码,不能直接用不认识补丁日志的旧后端替换。没有数据库结构迁移,也没有放松 CSRF、账号或文件权限检查。

实测结果

相同 160 ms 人工往返延迟(80 ms 上行、80 ms 下行;SSE 增加 80 ms 下行),假上游首段等待 250 ms、每 20 ms 产生一片文本。各端先采集自身基线,再测优化版;短回复每组 6 次,第一轮单列,后 5 次取中位数;每端另测一条 8,000 字符长回复。主性能样本共 42 次发送,额外阅读位置测试不计入这 42 次。

端 发送恢复:改前 → 改后 优化后确认额外等待 文本更新:改前 → 改后
iOS 675.09 → 173.66 ms 0.62 ms 101.97 → 33.90 ms
Android 519 → 178 ms 8 ms 100 → 36 ms
Web 595 → 171 ms 13 ms DOM 185 → 34 ms

Web 内部状态更新为 98 → 33 ms。表中 Web 改用真实 DOM 文本变化,而非仅凭状态更新判断显示速度:初次优化曾出现“状态 34 ms、DOM 67 ms”,去掉 Bot 的二次节流后才达到 DOM 34 ms。

iOS 使用 iPhone 17 Pro / iOS 26.5 模拟器、Xcode 26.6,优化构建保留测试入口和时间探针。Android 使用专用 Pixel 5 / API 31 模拟器,实际 WorkspaceScreen、控制器、网络层和 Compose 界面,校准模拟器与主机时钟。Web 使用 Chromium、实际生产构建和 DOM 观察器。iOS/Android 更新点为原生界面观察/组合到新文本;这些值不是物理屏幕 FPS。每端只与自己的基线比较。

首段进入界面的中位数为:iOS 435.48 → 468.39 ms(增加 32.91 ms,未超过约定的 33 ms);Android 439 → 442 ms;Web 471 → 434 ms。首段总时间包含夹具适配器、服务端与 UI 调度,不能解释为真实模型生成速度变化。

五路并发与完整 SSE 流量

指标 全量协议 补丁协议
并发 Bot / 每 Bot 正文 5 / 8,000 字符 5 / 8,000 字符
完整 SSE 字节(含状态、摘要、终态) 2,778,172 497,075
持久化与事件编码 P95 3.91 ms 1.43 ms
CPU 总耗时 432.96 ms 568.73 ms
事件日志有效载荷新增 2,719,888 字节 403,065 字节
WAL 文件观测大小 4,190,072 字节 4,165,352 字节

完整 SSE 减少 82.1%,并发写入/编码 P95 低于 10 ms。CPU 总耗时增加约 31.4%,是更密集更新的代价;约 4.3 秒窗口消耗 0.57 核秒。WAL 数字是观测文件大小,受 checkpoint/文件复用影响,不等于总写入量。五条回复均按完整文本与持久化记录逐字核对。

正确性与界面验收

覆盖:回执/SSE 乱序、旧回执和同游标解码缓存隔离、重复事件、新草稿保护、CSRF 预热与原请求 ID 重试、补丁断层和任务隔离、事务回滚、重启后补丁读取、旧协议 reset、关闭补丁后的兼容、Unicode、终态/工具/附件变化、暂停后的尾刷、历史保留和隐藏消息。

  • iOS:58 项 WorkspaceChatTests 通过;7 次基线及 7 次优化原生 UI 发送,优化回复完整文本核对通过。签名模拟器验证,避免未签名 Keychain -34018。
  • Android:54 项模型测试、43 项网络测试通过,App 和测试 APK 编译通过;7 次基线及 7 次优化原生发送逐字核对。长回复末尾可见;上翻后接收另一条消息保持阅读位置,手动回到底部通过。
  • Web:7 次基线及 7 次优化 UI 发送逐字核对;上翻后触发 snapshot reset、接收新回复,滚动位置保持(误差不超过 4 px)。Markdown 净化、普通聊天默认节流和按钮行为回归通过。
  • 服务端:补丁与全量路径均验证,事务失败前不会通知观察者;默认启用后的针对性完整回归和 TypeScript 检查通过。Web/服务端最终 193 项针对性测试通过,TypeScript 检查通过。

测试脚本使用每次唯一的回复标记,防止重跑时历史消息误判成功。Android 最初的启动挂起由继承代理环境引起,移除子进程的代理变量后正常启动;只改了测试进程环境。

没有验证真机、真实模型或真实公网条件。本次未运行模型、Worker 启动或普通聊天业务改造;共享组件的默认行为和安全规则保持兼容。

证据与复现

完整结果包保留于 /tmp/bot-latency-acceptance/evidence/ios-final.xcresult,iOS 最后单元测试包为 /tmp/bot-latency-ios-cursor-final.xcresult。临时构建目录可能被系统清理;本目录 JSON、截图、脚本及日志随 Git 保留。被修正或失败的中间测量不计入最终样本。