【直播切片工作台】第25章:ASR 结果结构化:分句与词级时间戳

简介: 第25章详解ASR结果结构化:将豆包原始JSON安全翻译为带说话人、分句及词级毫秒时间戳的业务结构,强调防御式解析(空/坏数据返回空切片)、嵌套字段拉平与供应商解耦,Word为全链路精度原子。(239字)

第25章:ASR 结果结构化:分句与词级时间戳

本章分析 internal/pkg/asr 的结果解析族:utterances.go(分句/词结构)、result.go(时长解析)、format.go(格式探测)。核心问题只有一个:如何把豆包的原始 JSON 稳定地翻译成业务结构,并保证「任何脏数据都返回空数组而非崩溃」。

流程

转写完成后,原始 JSON 整体存进 live_material.live_asr(第 10 章)。后续所有消费都经过一道翻译函数:

live_asr 原始 JSON
  ├─ FormatUtterancesForAPI → []Utterance(分句 + 说话人 + 词级时间戳)
  ├─ ParseDurationMs        → int64 毫秒(audio_info.duration)
  └─ (后处理 Worker)→ asr_paragraphs 段落层(第 30 章)

FormatUtterancesForAPI 的防御链条:空串与 "{}"(默认值)直接返回空切片 → Unmarshal 失败返回空切片 → 逐条把 result.utterances[] 翻译成业务结构(豆包把说话人藏在 additions.speaker 嵌套层,翻译函数负责拉平)→ words 数组同样逐条转换。返回值永远是初始化的切片而非 nil,调用方 range 零值安全。

这一层的关键定位:豆包的 JSON 字段命名(additions、result 嵌套、毫秒时间戳)被严格限制在 asr 包内部;业务层(model.ASRParagraph、ClipWord)只面对规整后的结构。换 ASR 供应商时理论上只改这一个包。

实现

实现模式的三个特征:

  • 匿名结构体精确建模:var payload struct{ Result struct{ Utterances []struct{...} } }——只声明用到的路径,供应商加字段不影响解析,缺字段零值降级。
  • 双层循环翻译:utterances 层与 words 层各自独立建切片(make(..., 0, len(...)) 预分配容量),一个词结构损坏不影响其他词。
  • 降级而非报错:解析函数返回 (值) 而非 (值, error)——因为原始 JSON 已经过客户端成功返回,格式异常属于「数据已坏」场景,报错调用方也无法处理;返回空数组让上层自然走「无转写内容」的既有分支(如字幕导出返回 ErrASRSubtitleEmpty,第 21 章)。

Word 结构(text + start/end 毫秒)是整个精度体系的原子:字幕按词对齐(第 27 章)、clips1 的 words 从段落词表截取(第 36 章)、草稿字幕轨时间轴基于它(第 41 章)。所有时间单位全链统一毫秒,与 ffmpeg 交互时才除以 1000(prepare 包 float64(clip.StartTime) / 1000.0)。

format.go 的 DetectFormat:从 URL 后缀(.mp3/.wav/.m4a 等)推断提交参数里的 format 字段;无法识别时报错阻止提交(宁可失败也不猜错格式让远端白跑)。

配套测试覆盖:空 JSON、"{}"、损坏 JSON、正常完整结构四个分支(utterances_test.go / result_test.go / format_test.go)——防御性解析的每个分支都有断言。

📌 设计决策

  • 翻译层独立成包:供应商协议被包住,业务结构稳定;换供应商的改动半径 = asr 包 + 配置。
  • 降级返回空切片:脏数据不炸链路,上层按「无内容」既有路径处理;代价是「真无内容」与「解析坏了」不可区分——权衡点在于后者概率极低且有 live_asr 原文可人工排查。
  • 说话人字段拉平进 Utterance.Speaker:下游(段落聚合同说话人,第 30 章)不必知道豆包的嵌套细节。

代码示例

业务分句结构(全链路的精度原子):

// internal/pkg/asr/utterances.go
// Utterance API 返回的 ASR 分句结构。
type Utterance struct {
   
    Speaker   string `json:"speaker"`
    EndTime   int64  `json:"end_time"`
    StartTime int64  `json:"start_time"`
    Text      string `json:"text"`
    Words     []Word `json:"words"`
}

// Word API 返回的 ASR 字级时间戳。
type Word struct {
   
    EndTime   int64  `json:"end_time"`
    StartTime int64  `json:"start_time"`
    Text      string `json:"text"`
}

防御式翻译函数的骨架:

// FormatUtterancesForAPI 将数据库 live_asr 原始 JSON 转为 API 分句数组。
// 无识别结果或解析失败时返回空数组。
func FormatUtterancesForAPI(liveASRJSON string) []Utterance {
   
    if liveASRJSON == "" || liveASRJSON == "{}" {
   
        return []Utterance{
   } // 默认值短路
    }
    var payload struct {
    /* 只声明需要的路径 */ }
    if err := json.Unmarshal([]byte(liveASRJSON), &payload); err != nil {
   
        return []Utterance{
   } // 解析失败降级
    }
    utterances := make([]Utterance, 0, len(payload.Result.Utterances))
    for _, item := range payload.Result.Utterances {
   
        words := make([]Word, 0, len(item.Words))
        for _, w := range item.Words {
   
            words = append(words, Word{
   /* 拉平翻译 */})
        }
        utterances = append(utterances, Utterance{
   
            Speaker:   item.Additions.Speaker, // 嵌套拉平
            StartTime: item.StartTime, EndTime: item.EndTime,
            Text: item.Text, Words: words,
        })
    }
    return utterances
}

小结

  • asr 解析族 = 供应商协议翻译层 + 防御式降级 + 拉平嵌套。
  • Word(字级毫秒时间戳)是字幕与切片精度的原子单位。
  • 换供应商的改动半径被锁定在一个包内。

思考题

  1. 「解析坏了」与「真无内容」不可区分的场景,如何用一条 Warn 日志低成本地区分?
  2. 若新 ASR 供应商输出秒级时间戳,应该在哪一层统一换算成毫秒?为什么?

项目信息

  • GitHub仓库:Chyona/live-mixer
相关文章
|
4天前
|
JavaScript API iOS开发
DeepSeek Harness下载、安装、配置一篇搞定(2026整理,送常用插件)
DeepSeek Harness(DSH)是DeepSeek于2026年开源的MIT协议AI Agent框架,支持插件化组装模型、工具、沙箱与UI,兼容主流大模型。本文提供免Node.js的桌面端安装包(Win/macOS),含详细图文教程及热门插件推荐。
|
云安全 安全 网络安全
|
5天前
|
iOS开发 Docker MacOS
【2026实测】Docker Desktop下载、安装、汉化、配置教程(附官网安装包)
Docker Desktop 是 Docker 官方推出的桌面容器管理工具,集成引擎、CLI、图形界面与 Kubernetes,支持 Win/macOS/Linux。2026 最新版 4.88.1 强化 ARM 原生运行、镜像加速及资源可视化,附中文汉化与国内镜像配置指南。
861 1
|
17天前
|
人工智能 API 开发工具
DeepSeek Harness 神插件:白嫖 13 家大厂模型
`dsh-our-free-model` 是 DeepSeek Harness 神级插件:零配置、零API Key、零账号,聚合13家大厂免费模型(含DeepSeek V4.1 Flash、Kimi K3等),支持本地网关、用量看板与自动降级,真正实现“白嫖即用”。
2971 49
|
11天前
|
JavaScript 安全 前端开发
Nodejs Brotli decoder 代码执行漏洞 (CVE-2026-111497)10629
Node.js发布安全更新修复Brotli解码器高危RCE漏洞(CVE-2026-111497),建议受影响版本用户立即升级。
2100 2
|
5天前
|
安全 算法 定位技术
《高性价比人生指南》完整原版:普通人用最少的资源,过好这一生的底层逻辑~How To Live Better
📖《高性价比人生指南》是一份基于实证、拒绝焦虑的生存手册,倡导以“人生时薪”为视角,重新计算时间、金钱与精力的成本与收益。它不承诺逆袭,只教人算清账:省下的钱若耗掉时间便亏;买体验比买物品更值;睡眠是最高杠杆的健康投资;关系贵在情绪价值而非表面热络;学习重在改变行为而非自我安慰。核心理念:用低成本、高回报的微小行动,换取长期的选择权——不是成为谁,而是掌控自己人生的主动权。
|
10天前
|
人工智能 JSON Linux
【保姆级】ComfyUI部署教程(下载+安装+配置+工作流,2026最新)
ComfyUI 是一款开源节点式AI生成工具,支持图像、视频、音频及3D内容创作。通过拖拽连线组合模型加载、提示词、采样等节点,构建可复用、易修改的工作流,兼容SD1.5、SDXL、Flux、Qwen Image等主流模型。
|
4天前
|
Linux 开发工具 iOS开发
Codex保姆级教程(下载+安装+登录+使用,含所有版本,2026最新)
Codex是OpenAI推出的开源AI编程智能体,本地运行,可理解整个代码库,支持自然语言指令改代码、执行命令、跑测试、修Bug,兼容Win/macOS/Linux。(239字)

热门文章

最新文章