diff --git a/doc/index.html b/doc/index.html
new file mode 100644
index 0000000..b0d142b
--- /dev/null
+++ b/doc/index.html
@@ -0,0 +1,77 @@
+
+
+
+
+
+dudu 文档索引
+
+
+
+
+
+
+
+
+
+ 设计方案
+
+ - 前端交互与界面设计 — 五端交互流、界面规格与设计系统对接(v1.1)
+ - 后端与数据架构 — API、WS 流式协议、时长账本计费、Redis/PG 设计(v1.1)
+
+
+
+
+
+
+ 知识库调研 / 验收报告
+
+ - 12B 延迟验收结论(暂定) — 2026-07-10 · 全链路延迟分解、#23/#24 修复、暂定新目标与 paraformer 待决策
+
+
+
+
+
+
+
+
diff --git a/doc/latency-acceptance-12b.html b/doc/latency-acceptance-12b.html
new file mode 100644
index 0000000..3ddc235
--- /dev/null
+++ b/doc/latency-acceptance-12b.html
@@ -0,0 +1,181 @@
+
+
+
+
+
+dudu 12B 延迟验收结论(暂定)
+
+
+
+
+
+ 12B 延迟验收结论(暂定)
+ 网关全链路实测 · 延迟分解 · 修复与调优 · 暂定新目标
+
+ 2026-07-10
+ 工具:server/cmd/latencyprobe
+ 分支:feat/12b-latency-probe
+ provider:gummy-realtime-v1(真实)
+
+
+
+
+
+
+ 一、暂定结论(TL;DR)
+
+ | 指标 | 原目标 P95 | 实测(预连接后) | 暂定新目标 |
+
+ first_partial(按键→首字) |
+ <500ms |
+ P50 942ms · P95 950ms(极稳定) |
+ P95 < 1.2s |
+
+
+ release_to_commit(松键→上屏) |
+ <300ms |
+ 短句 ~170ms · 8s 会话 350~500ms |
+ ≤3s 会话 <300ms;长会话 <600ms |
+
+
+
+ 为什么 500ms 结构性不可达:first_partial = max(握手, DashScope 音频块阈值 ~850ms) + 推理 ~90ms。
+ 地板是 DashScope 的音频块阈值,与网关代码无关(直连同样 ~1s)。要破地板只能换模型(见第四节)。
+
+
+ 体感修正:前导静音实验证明阈值按「音频量」而非「语音量」计——400ms 静音只使 first_partial 推迟 ~100ms。
+ 用户按键后自然停顿 ~300ms 再开口时,从开口算的首字延迟仅 ~640ms,体感优于指标数字。
+
+
+
+
+ 二、延迟分解(实测)
+
+ | 环节 | 耗时 | 处置 |
+ | 网关预检(限流 / 配额 / 设备槽) | ~10ms | 无需优化 |
+ | DashScope dial + task-started 握手 | 暖 ~130ms · 抖动 60ms~3.7s | 已消除 spare 预连接(#24) |
+ | DashScope 音频块阈值 | ~850ms(地板) | 换模型才能降(paraformer ~520ms) |
+ | 推理 + 回传 | ~90ms | 无手段 |
+ | final flush(松键后) | 短句 ~170ms · 8s 会话 350~540ms | 随内容量增长,属 provider 物理时间 |
+
+ 测量口径(对齐桌面端 dictation.rs)
+
+ first_partial:start 帧发出 → 首个 partial(不含麦克风启动,桌面另有 audio.start_ms)
+ release_to_commit:stop 帧发出 → 尾部 final(不含注入耗时 <10ms)
+ - 工具:
go run ./cmd/latencyprobe -wav xx.wav -n 12(单持久连接 + 100ms/帧实时推流,复刻桌面行为)
+
+
+
+
+ 三、本轮落地的修复与优化
+ #23 stop 后周期 usage 帧抢跑尾部 final(已修,服务端根治)
+
+ - 症状:8s 会话 4/4 复现 usage 先于尾部 final 到达,客户端以 partial 文本提前上屏、丢失 final 修正(iOS 21A 修过客户端侧,桌面端仍暴露)
+ - 根因:finish 等尾部 final 期间(最长 3s)usageLoop 仍在 tick
+ - 修复:finish 先冻结会话并停 usageLoop 再 flush——stop 后唯一的 usage 是结算帧且必在全部 final 之后,五端客户端「final 或 usage 即提交」逻辑自动变安全,无协议改动。修复后 6/6 归零
+ - 桌面端收尾兜底 350ms → 800ms(final flush 实测 350~540ms,350ms 会截丢尾 final)
+
+ #24 gummy spare 预连接(已实现)
+
+ - 常备一条已完成 run-task 握手的 DashScope 会话,start 直取、异步补位、40s 定期换新
+ - 依据:DashScope 空闲 60s 存活、120s 报 Idle timeout(实测),留余量 45s
+ - 效果:网关 start 处理 120~670ms → 3~4ms;dial 抖动(60ms~3.7s)移出关键路径,否则 P95 可达 1.5~4.5s
+
+ 顺手修复
+
+ - 滑动窗口 Lua「只查不记」与实现不符:val=0 的检查不再写入 0 值成员污染窗口 ZSET
+ - gummycheck 增加
-model / -pace / -idle 参数,支持模型对比与闲置存活实验
+
+
+
+
+ 四、待决策:要不要换 paraformer
+
+ | gummy-realtime-v1(现用) | paraformer-realtime-v2 |
+ | 音频块阈值(首字地板) | ~850ms | ~520ms |
+ | partial 刷新节奏 | ~1s / 次 | ~500ms / 次(更跟手) |
+ | 识别质量(单样本 TTS 实测) | 全对 | 「各自模块的进度汇报」→「各字文案者进入汇报」 |
+ | partial 时间戳(计量交叉校验用) | 每条带 end_time | 仅 final 带 |
+
+
+ 决策前置条件:用 eval 框架(eval/)跑真实数据集对比两模型 WER,再决定是否为 ~300ms 首字收益换质量。
+ 切换成本低:GummyProvider.Model 一行配置。
+
+
+
+
+ 五、复测方法
+ # 起服务端(pg/redis 容器已在跑)
+cd server && ./run-dev.sh
+
+# 全链路延迟实测(另开终端)
+go run ./cmd/latencyprobe -wav /path/to/16k-mono.wav -n 12
+
+# 直连模型对比 / 闲置存活实验
+rbw get dashscope-api-key | go run ./cmd/gummycheck -wav xx.wav -pace 100 -model paraformer-realtime-v2
+rbw get dashscope-api-key | go run ./cmd/gummycheck -wav xx.wav -pace 100 -idle 60
+
+ - 注意设备 30 分钟滑动窗口上限 30 次会话,probe 换
-device 规避
+ - 观测日志:
gummy: session ready(dial/task-started 耗时)、gummy: session from spare、gateway: start handled
+
+
+
+
+
+
+
+