先看一个会翻车的场景
下面是一个示意场景。甲队与乙队踢到第78分钟,比分1比1,甲队一名替补前锋在边路传中,乙队后卫解围时碰到了球,球滚进了自家球门。现场转播里,这个进球最初显示在甲队名下,几十秒后被更正为乙队队员的乌龙球。
如果主播直接依赖语言模型对“进球”这一类事件的常见写法,它很可能顺着惯性说出“甲队替补前锋建功,甲队2比1领先”。比分是对的,进球人是错的,而错误恰恰出在最容易被观众记住的那个名字上。反过来,如果主播只是等数据源确认了进球归属再开口,慢一点,却不会说错。
这个例子说明了一件事:体育播报的错误大多不是“语言不通顺”,而是事实和时间对不上。数字人的画面再精致,也帮不上这一步。所以我们把设计重点放在事实链路上,而不是形象渲染上。
事件:先确认发生了什么
体育直播里的“事件”不是一句话,而是一条带结构的记录。一个进球事件至少要包含:事件类型、发生时间(比赛时钟和真实时钟各一份)、涉及球队、涉及球员、事件状态(初报、已确认、已更正、已撤销)。
其中最容易被忽视的是状态字段。同一个事件在几十秒内可能经历“初报”到“更正”的变化:进球归属改变,红牌被撤回,越位判罚推翻了进球。播报系统如果只记录“发生过一个进球”,就丢掉了最关键的信息,因为它不知道这条记录现在还能不能信。
江南体育AI的设计是让事件层单独维护一个状态机:
- 收到初报,标记为“待确认”,允许播报,但只能使用不含具体归属的表述;
- 收到确认信号,升级为“已确认”,可以使用完整信息;
- 收到更正或撤销,立刻向下游广播,触发更正播报,并让此前依赖该事件生成的文本作废。
事件层不负责说话,它只回答一个问题:现在可以相信什么。
数据:比赛状态是唯一的真相来源
事件之上是比赛状态:当前比分、比赛时钟、场上球员、已出示的牌、换人记录、简单的统计,如射门次数、控球时间。这些内容我们统称为“当前状态快照”。
原则只有一条:主播说出口的每个数字、每个名字,都必须能在当前状态快照或经过验证的历史资料里找到。
这意味着几件具体的工程约束:
- 语言模型不能自己“回忆”球员的进球数、转会史或历史交锋,除非这些内容已经作为资料放进输入里;
- 球员名字不是从文本里生成的,而是通过球员编号映射到标准名称,同一个人无论在哪一环出现,名字都一致;
- 统计类数字在使用前,要检查它的口径和刷新时间,明显过期的统计宁可不说;
- 赛前资料(历史战绩、近期状态)和实时数据分开存放,赛前资料带有“截至某场比赛”的标记,避免被当作现在的状态。
这一层和赛前内容的数据核验是同一个原则:先决定哪些数据现在仍然有效,再决定怎么写。区别只在于,主播的数据有效期短得多,往往是几秒到几十秒。
一份状态快照长什么样
下面是一个示意的快照,字段名和数值都是举例,用来说明输入的形态,并非某场比赛的真实数据:
- 比赛时钟:第79分钟,下半场进行中;
- 比分:甲队2,乙队1(状态:已确认,最近刷新于数秒之前);
- 最近事件:进球,归属乙队后卫乌龙(状态:已更正,原归属甲队替补前锋);
- 场上最近一次换人:甲队第65分钟;
- 可用统计:射门次数、角球次数,口径为全场累计,刷新时间已标注。
模型看到的是这种被“整理过的现实”,而不是一段没有出处的描述。任何一项缺失或过期,都会以“状态未知”的形式显式告诉文本层,让它避开相应的表述。
文字:让语言模型只做改写,不做判断
有了事件和状态,才轮到语言模型出场。这里的关键是分清它该做什么和不该做什么。
该做的:把结构化的事件改写成自然口语,选择合适的语气,控制句子长度,让相邻两句话不重复,在比赛节奏紧张和平缓时使用不同的句式。
不该做的:判断比分、推断谁进的球、估算比赛时间、补充没有出现在输入里的背景。
实现上,常见的做法是给模型一份明确的输入,比如:
- 当前比分和比赛时钟;
- 刚刚确认的事件(含状态);
- 允许提及的球员和球队标准名称清单;
- 上一两句已经播出的内容,用来避免重复;
- 一份风格说明,比如“简短、平稳、不夸张”。
模型输出之后,还要有一道校验:把文本里出现的数字、球员名和球队名抽出来,与输入清单逐个比对;出现了清单之外的实体,或者数字对不上,就丢弃这次输出,回退到槽位化模板。这个回退很重要:模板句式虽然生硬,但每个空位只能填入已确认的值,是“不会错”的最后保障。关于事实和表达自然之间如何取舍,我们在 事实正确与表达自然的分层设计 里单独展开讨论。
声音与数字人:表现层不碰事实
文字确认无误之后,才进入语音合成。语音层要处理的主要问题是读音:球员名字的多音字和外文名,球队简称,数字的读法(“2比1”读成比分,还是读成“二比一”)。这些都应该通过发音词典解决,而不是交给合成模型随意发挥。
数字人的口型与表情由语音驱动,它对事实没有任何决定权。设计上我们让它遵守两条规则:
- 表情和肢体动作的强度由事件类型和语气标签决定,比如进球时可以更兴奋,但不会因为语气而改变说出的内容;
- 当系统处于“不确定”状态时,数字人的表现应当克制,例如语速放缓、避免夸张表情,而不是继续保持热烈的状态。
这与 数字人不只是会说话的3D头像 的思路一致:外观是表现层,背后的判断来自数据、规则和上下文。外观的真实感会让观众更容易信任它,这恰恰意味着,一旦内容出错,代价更大。
延迟:慢一点,还是先说再改
最后一环是时间。事件确认、文本生成、语音合成、口型驱动,每一步都会花时间。在直播里,太慢会让观众感觉主播落后于画面,太快又可能把没确认的事情说出去。
这里没有一个统一的答案,我们倾向于分级处理:
| 事件类型 | 建议处理方式 |
|---|---|
| 比赛开始、中场、结束等时钟事件 | 直接播报,出错风险低 |
| 进球、红牌等高影响事件 | 等待确认后再给出完整信息,先用不含归属的保守句式 |
| 换人、角球、任意球等常规事件 | 可以较快播报,出现更正时再补充 |
上表是示意性的分类思路,并不是某场比赛的实测配置。延迟与准确之间的取舍,以及为什么“撤回一次错误播报”比“晚一点说对”更伤体验,我们在 实时播报延迟的取舍 里有更具体的讨论。
出错以后:追溯、回放与更正
再严谨的系统也会出错,所以设计里要预留两件事:出错时能查清原因,出错后能体面地更正。
可追溯方面,每一句播出的话都应当保留一条记录:使用的状态快照版本、触发它的事件编号、模型输出与校验结果、最终采用的是自由生成还是模板回退。这样,当观众反馈“刚才那句比分说错了”,团队可以回放当时的输入,判断问题出在数据源、事件层、文本层还是语音层,而不是笼统地怪“AI幻觉”。
可更正方面,常见的错误可以分成几类,处理方式也不同:
| 错误来源 | 典型表现 | 处理思路 |
|---|---|---|
| 数据源更正 | 进球归属、牌的对象变了 | 事件层广播更正,主播用一句简短的话修正,不反复道歉 |
| 文本层越界 | 出现输入里没有的球员或数字 | 校验拦截,回退模板,记录一次告警 |
| 语音层读错 | 名字或数字读音不对 | 补充发音词典,不影响事实层 |
| 时序错位 | 讲的是上一回合的事 | 见延迟一节,过期内容应被丢弃而不是硬播 |
更正的语气也值得设计。一个平静的“更正一下,刚才的进球记在乙队后卫名下”,通常比含糊带过更容易被接受。观众能接受主播偶尔更正,接受不了的是发现它对错误毫无察觉。
什么时候该沉默
一个可靠的主播,判断力不仅体现在说什么,也体现在什么时候不说。在这套设计里,有几种情况我们宁愿让主播暂时收声或改用保守表述:
- 数据源短暂中断,当前比分无法确认,此时不重复播出已经无法核实的旧比分;
- 同一事件在短时间内被反复更正,说明信息还不稳定,等它稳定后再播;
- 比赛出现暂停、复核或伤情处理,此时应当用中性的说明性语句,避免主播凭空填充“猜测式”内容;
- 需要引用的统计口径不明,宁可不报,也不报一个可能对不上的数字。
沉默不等于失败。对体育观众来说,一个知道自己什么时候不该说话的主播,比一个永远滔滔不绝的主播更值得信任。
这套设计的边界
最后说清楚几件事,避免把设计思路误读成现成效果:
- 主播的可靠程度,上限取决于数据源本身。如果数据源的更正很慢,主播也会跟着慢,我们做的是让系统知道自己不确定,并在下游正确表达这种不确定;
- 语言模型仍然会偶尔产生不合适的措辞,校验层拦截的是事实类错误,不能替代人工对风格与敏感话题的把关;
- 现场直播中的突发状况,比如信号中断、数据源重复推送,都需要单独的异常处理,本文没有覆盖;
- 以上描述的是江南体育AI在设计上的做法,具体某场赛事能不能用、用哪些数据,需要等授权和数据接入落实之后再说。