文档中心>实践教程>语音识别>通过实时语音识别实现会议场景话者分离

通过实时语音识别实现会议场景话者分离

最近更新时间:2026-04-22 17:39:12

我的收藏

操作背景

在线会议、访谈录制、庭审记录,这些常见场景都有一个共同的需求:会后快速生成纪要,并清楚标注“谁说了什么”

传统的实时语音识别只能输出一段连续的文字流。会后整理时,人工标注说话人身份既耗时又容易出错。腾讯云实时语音识别提供了句子模式话者分离的组合能力,可以在实时转写的同时,以完整句子为单位输出,并自动区分说话人,天然适合会议纪要的实时上屏和事后归档。

本文将带您从零搭建一个会议实时转写 Demo。您可以先运行体验效果,再深入了解接入细节。

快速体验

我们准备了一个开箱即用的 Demo(Go 后端 + HTML 前端),按照下面步骤可快速启动。

环境要求

Go 1.18 或以上
腾讯云账号,已开通 语音识别服务
准备好 API 密钥:AppID、SecretID、SecretKey

启动步骤

1. 进入 Demo 目录。
cd tencent-realtime-asr-demo
2. 首次使用:自动创建配置文件 config.json。
make setup
3. config.json 中填入您的密钥:
{
"port": 8080,
"appid": "您的AppID",
"secret_id": "您的SecretID",
"secret_key": "您的SecretKey"
}
4. 编译并启动服务(自动打开浏览器)
make run
浏览器会自动打开 http://localhost:8080,您应该看到如下界面:

开启话者分离开关,单击开始识别,对着麦克风说话,即可看到实时转写效果。


整体架构

Demo 采用 浏览器 > 后端 > 腾讯云 的 WebSocket 双层桥接架构:

1. 浏览器采集麦克风音频(16kHz / 16bit / 单声道 PCM),通过 WebSocket 实时发送给后端。
2. 后端将音频透传至腾讯云实时语音识别服务。
3. 腾讯云返回识别结果,后端转发给浏览器渲染。

为什么需要后端桥接?因为直连腾讯云需要 SecretKey 签名,密钥不能暴露在前端。后端负责签名和转发,前端只需处理录音和展示。


接入指南

核心概念

在开始接入前,您需要先了解以下两个关键能力:
句子模式:服务端每次回调都返回当前所有句子的完整列表,包含正在识别的和已经确认的。客户端只需用最新列表覆盖渲染,无需自行拼接文字或维护句子状态。在腾讯云 Go SDK 中,使用 SpeakerRecognizer 类会自动启用句子模式。
话者分离:在句子模式的基础上,为每句话标注说话人编号(speaker_id)。服务端自动判断有几个人在说话,无需预设人数。

会议场景推荐:同时启用句子模式和话者分离。 无论是多人会议还是单人演讲,句子模式都是更好的选择,上屏逻辑简单,体验流畅。多人场景再叠加话者分离即可。

关键参数

使用 SpeakerRecognizer 时,SDK 会自动处理连接签名和句子模式设置。开发者只需关注以下参数:
参数
推荐值
说明
engine_model_type
16k_zh_en_speaker
话者分离专用引擎,支持中英文混合识别
speaker_diarization
1
开启话者分离
sentence_strategy
01
分句策略,决定定稿的颗粒度,详见下方说明

分句策略:单句 vs 段落

两种策略都会实时输出中间结果,用户都能即时看到文字。区别在于什么时候确认一段文字为最终结果
分句策略
定稿条件
话者分离展示效果
0 :语义单句
一句话说完即定稿
每句话是一个独立气泡
1 :段落
段落超过 50 字或包含 3 个以上子句时定稿
同一人连续说的多句话合并为一个段落气泡
两种策略的实时性没有差异,都是边说边出字。差别在于阅读体验
单句模式适合需要快速逐句确认的场景,每句独立,定稿快。
段落模式适合希望段落聚合展示的场景,阅读更流畅,更接近会议纪要的最终形态。


代码示例

以下代码为核心思路示意,为突出逻辑主线进行了精简。完整工程(含异常处理、日志、配置管理等)见附录的完整工程。

使用 腾讯云语音 Go SDK 创建识别器:
import (
"github.com/tencentcloud/tencentcloud-speech-sdk-go/asr"
"github.com/tencentcloud/tencentcloud-speech-sdk-go/common"
)

credential := common.NewCredential(secretID, secretKey)
listener := &MyListener{} // 实现 SpeakerRecognitionListener 接口

rec := asr.NewSpeakerRecognizer(appID, credential, "16k_zh_en_speaker", listener)
rec.SpeakerDiarization = 1 // 开启话者分离
rec.SentenceStrategy = 0 // 0=语义单句,1=段落

rec.Start() // 建立连接
rec.Write(pcmData) // 持续写入音频数据
rec.Stop() // 结束识别
需要实现的回调接口:
type SpeakerRecognitionListener interface {
OnRecognitionStart(*SpeakerRecognitionResponse) // 连接建立
OnRecognitionSentences(*SpeakerRecognitionResponse) // 句子列表更新(核心)
OnSentenceEnd(*SpeakerRecognitionResponse) // 识别结束
OnFail(*SpeakerRecognitionResponse, error) // 错误
}
核心是 OnRecognitionSentences——每次被调用时,resp.Sentences.SentenceList 就是当前所有句子的最新快照,直接遍历渲染即可。


返回结果解析

句子列表

每次回调返回的数据结构如下:
{
"code": 0,
"voice_id": "abc1234-...",
"sentences": {
"sentence_list": [
{
"sentence_id": 0, // 句子编号
"sentence": "今天我们讨论一下项目进度", // 识别文本
"sentence_type": 1, // 1=已确认
"speaker_id": 0, // 说话人 0
"start_time": 1200, // 开始时间(ms)
"end_time": 3800 // 结束时间(ms)
},
{
"sentence_id": 1,
"sentence": "好的,我先汇报前端的情况",
"sentence_type": 1, // 1=已确认
"speaker_id": 1, // 说话人 1(不同的人)
"start_time": 4000,
"end_time": 6500
},
{
"sentence_id": 2,
"sentence": "本周完成了登录模块的",
"sentence_type": 0, // 0=识别中,文字还会变
"speaker_id": 1,
"start_time": 6600,
"end_time": 8000
}
]
}
}

关键字段

字段
含义
sentence_id
句子编号。用来追踪同一句话在多次回调中的变化
sentence
识别出的文字
sentence_type
当前句子识别状态,0表示识别中,文字还在变化;1表示已确认,不会再改变
speaker_id
说话人编号,从 0 开始。-1 表示暂时还无法判断是谁在说
start_time / end_time
时间戳(毫秒)

渲染逻辑

句子列表是一个不断增长的快照,客户端每次收到回调直接用最新列表整体渲染:
sentence_type=1 的句子已经确认,用正常样式展示
sentence_type=0 的句子还在识别中,用斜体或半透明展示,提示用户"文字可能还会变"
根据 speaker_id 为不同说话人分配颜色和位置


关于 speaker_id = -1

服务端需要积累足够的音频才能判断说话人身份。一句话刚开始时,speaker_id 可能是 -1。随着音频积累,后续回调会将其更新为具体编号。

推荐处理方式:将 speaker_id=-1 的文字以灰色斜体追加到上一位已确认说话人的气泡末尾。身份确认后,自动移入正确的气泡。这样可以避免界面跳动。


上屏方案

聊天气泡

会议场景推荐的展示方式。为每位说话人分配颜色,按出场顺序左右交替:
if (!(speakerId in speakerOrderMap)) {
speakerOrderMap[speakerId] = speakerOrderCounter++;
}
const orderIdx = speakerOrderMap[speakerId];
const side = orderIdx % 2 === 0 ? 'left' : 'right';
const color = COLORS[orderIdx % COLORS.length];
每次收到句子列表,按 sentence_id 创建或更新对应气泡
sentence_type=0 显示为斜体(识别中),sentence_type=1 显示为正常样式(已确认)
speaker_id 变化时(从 -1 变为具体值),将气泡移到正确位置


Demo 工程结构

以下为 Demo 工程的目录结构,及各文件的作用介绍。
tencent-realtime-asr-demo/
├── main.go # HTTP 服务入口
├── handler.go # WebSocket 桥接:音频转发 + 结果回传
├── config.go # 配置读取
└── static/
├── index.html # 页面结构
├── app.js # 录音、通信、渲染
└── style.css # 样式
handler.go:根据前端参数创建 SpeakerRecognizer 实例,实现回调接口,将句子列表通过 WebSocket 转发给前端。
app.js:麦克风采集、WebSocket 通信、气泡/字幕渲染。


效果调优建议

场景
建议
话者分离不够准确
确保不同说话人之间有明显的停顿;避免多人同时说话(重叠语音会影响分离效果)
环境噪音较大
使用带降噪功能的麦克风;尽量在安静环境下使用
会议人数较多(>5人)
话者分离在 3-5 人时效果最佳,人数过多可能出现误判
希望段落更完整
sentence_strategy 设为 1(段落模式),同一人连续发言的内容会聚合展示
识别结果有错别字
可配合热词功能(hotword_idhotword_list),将会议中的专业术语加入热词表


常见问题

为什么推荐用 SpeakerRecognizer 而不是 SpeechRecognizer?

SpeakerRecognizer 内置句子模式,每次返回全量句子列表,客户端只需遍历渲染即可,无需维护复杂的状态机。即使不开启话者分离,句子模式本身也非常适合上屏场景。SpeechRecognizer 则是逐片段流式推送,客户端需要自行处理句子的开始、更新、结束等状态。


相关链接