Install any skill in seconds. Free to start, no credit card required.
Get Started Free →当处理听课模式的流式音频、buffering、TTS 请求门禁与播放连续性问题时使用本技能。统一音频来源选择、分段合并策略和请求触发时机。
.claude/skills/ai-shifu-listen-mode-audio-streaming/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-04 | ✗→✓ | ▲ Improved | 19% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 57% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 61% | 0% |
| case-06 | ✗→✓ | ▲ Improved | -18% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 40% | 0% |
is_speakable 且暂无可播音频时视为 buffering,不自动跳过。audioTracks,不可播时才回退 audio_url/audio_segments。element_id + position + segment_index,并允许后到分段提升 isFinal。audio_segments 后再写回状态,不直接全量替换。run 请求体 listen 必须跟随当前真实听课状态:只有 learningMode === 'listen' 且课程 tts_enabled 可用时才发送 listen=true;阅读模式即使课程支持听课也必须发送 listen=false。LIKE_STATUS 仅表示流结束信号,TTS 还需校验 is_speakable 或已有可播音频。generated-blocks/:id/tts,禁止 onStepChange 自动补拉。listen-mode 映射 elementList 时显式透传 isAudioStreaming。type/error 或 event_type/error,立即弹出 destructive toast。is_speakable 控制:当 is_speakable === false 时不展示播放按钮。audio_segment/audio_complete 时禁止直接把 Record<string, unknown> 断言为目标类型,必须先做字段级归一化再写入 upsertAudioSegment/upsertAudioComplete。markdown-flow-ui 回放 data.json 排查时,elementList 的组装路径必须和 ai-shifu 听课模式保持同构,禁止额外做 story 专用的音频归属重排补偿。elementList 时,若内容本身是 <iframe data-tag="video" ...></iframe> 形式的视频 iframe,即使后端 element_type 仍是通用类型,也要优先把 slide element 的 type 推断为 video。record 历史接口时,若 payload.audio.subtitle_cues 存在,必须在 elementList 中显式透传为 subtitle_cues,不要只保留 content 导致字幕时间轴元数据在渲染层丢失。run 的 audio_segment 若已生成当前累计字幕 cue,必须同步透传到对应 element patch 的 payload.audio.subtitle_cues,不要等到 audio_complete 才返回字幕。tts_enabled 虽然沿用后端字段名,但前端产品语义视为“听课模式开关”;改动作者端文案或 learner 端默认模式时,必须同时检查设置页文案、课程信息映射和 /c/... 布局层默认 learningMode 是否一致。/run SSE;只有重修、离开课时、lesson reset 等真实生命周期结束场景才触发全局 stream stop。is_speakable !== false 且有有效 element_bid 的内容块;历史回放内容天然可补,流式 /run 新内容必须等后端在原有事务 commit 之后发出 audio_backfill_ready 才能调用 generated-blocks/:id/tts?listen=true,禁止用尚未提交的 streaming element 直接补音频。补生成对同一 generated block 做 in-flight 去重;第一段音频可播后允许进入播放,整条 TTS SSE 继续补齐后续 position;没有可补正文或补音频失败时,不再提示重修或强制切回阅读,只保持当前模式并用 toast/缓冲态反馈。subtitle_cues[].text 透传给 markdown-flow-ui/slide 前,要先过滤尾部终止标点:默认删除句号、逗号、冒号、分号等收尾符号,但保留问号、叹号、省略号,以及成对标点的后半个(如 ”、)、》)。element 事件只把完整 audio_url 放在 element 顶层或 payload.audio,而当前 item 已经有 audioTracks/audio_segments,必须把完整 URL 回填到对应 position 的 audioTracks;不要让 Slide 映射层因为优先使用 audioTracks 而丢掉完整 mp3 URL。AudioPlayer 已经播放过流式 audio_segment 后,不要在同一播放会话中切到 audio_complete.audio_url 续播;完整 URL 只用于未来重播或零 segment 缓存回放,否则 mp3 拼接/seek 偏差会导致句首几个字重复。run fixture,按 SSE 顺序重放 data: 片段。| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-04 | fail→pass | 16,773 | 12,133 | -28% | 1 | 1 | 0% | 2,952 | 3,527 | +19% | 0 | 0 | — |
case-01 | fail→fail | 26,882 | 29,921 | +11% | 1 | 1 | 0% | 5,481 | 7,570 | +38% | 0 | 0 | — |
case-02 | fail→pass | 21,594 | 29,327 | +36% | 1 | 1 | 0% | 4,800 | 7,552 | +57% | 0 | 0 | — |
case-03 | fail→pass | 21,616 | 26,611 | +23% | 1 | 1 | 0% | 4,278 | 6,898 | +61% | 0 | 0 | — |
case-05 | pass→pass | 11,167 | 6,152 | -45% | 1 | 1 | 0% | 2,157 | 2,586 | +20% | 0 | 0 | — |
case-06 | fail→pass | 14,077 | 4,314 | -69% | 1 | 1 | 0% | 2,420 | 1,976 | -18% | 0 | 0 | — |
case-07 | fail→pass | 12,926 | 9,428 | -27% | 1 | 1 | 0% | 2,058 | 2,878 | +40% | 0 | 0 | — |
case-08 | pass→pass | 14,294 | 3,315 | -77% | 1 | 1 | 0% | 2,661 | 1,853 | -30% | 0 | 0 | — |
case-09 | pass→pass | 14,137 | 2,620 | -81% | 1 | 1 | 0% | 2,496 | 1,768 | -29% | 0 | 0 | — |
case-10 | fail→pass | 10,449 | 2,749 | -74% | 1 | 1 | 0% | 1,558 | 1,714 | +10% | 0 | 0 | — |
case-11 | fail→pass | 14,554 | 4,492 | -69% | 1 | 1 | 0% | 2,273 | 2,082 | -8% | 0 | 0 | — |
case-12 | pass→pass | 10,942 | 3,516 | -68% | 1 | 1 | 0% | 1,835 | 1,913 | +4% | 0 | 0 | — |
case-13 | pass→pass | 11,124 | 1,497 | -87% | 1 | 1 | 0% | 1,607 | 1,513 | -6% | 0 | 0 | — |
case-14 | fail→pass | 5,951 | 1,704 | -71% | 1 | 1 | 0% | 944 | 1,545 | +64% | 0 | 0 | — |
case-15 | fail→pass | 12,862 | 4,184 | -67% | 1 | 1 | 0% | 1,886 | 2,023 | +7% | 0 | 0 | — |
case-16 | fail→pass | 15,669 | 21,224 | +35% | 1 | 1 | 0% | 3,049 | 5,671 | +86% | 0 | 0 | — |
case-17 | pass→pass | 11,912 | 3,155 | -74% | 1 | 1 | 0% | 1,739 | 1,835 | +6% | 0 | 0 | — |
case-18 | pass→pass | 10,872 | 3,875 | -64% | 1 | 1 | 0% | 1,573 | 1,800 | +14% | 0 | 0 | — |
case-19 | pass→pass | 9,225 | 2,361 | -74% | 1 | 1 | 0% | 1,428 | 1,682 | +18% | 0 | 0 | — |
case-20 | pass→pass | 13,026 | 8,229 | -37% | 1 | 1 | 0% | 1,950 | 2,461 | +26% | 0 | 0 | — |
case-21 | pass→pass | 9,997 | 4,720 | -53% | 1 | 1 | 0% | 1,608 | 2,123 | +32% | 0 | 0 | — |
case-22 | fail→fail | 14,051 | 4,568 | -67% | 1 | 1 | 0% | 2,129 | 2,125 | -0% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +45 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.