视频会议是如何实现AI会议纪要功能的?

  对于视频会议软件而言,会议纪要功能现在几乎成了标配,为会议结束后回顾会议内容提供了极大的方便,省去了大量的人力。接下来我们以 傲瑞会议(OrayMeeting)为例,来介绍会议纪要功能的具体实现。

  傲瑞会议纪要功能基于会议实时音视频数据流,实时采集会议语音音频。借助AI能力完成语音实时转写、语义规整与核心信息提炼,自动生成标准化的会议纪要。 并且客户端可实现全文内容与重点摘要分层渲染展示,全面满足会议全流程智能化纪要生成及归档管理的业务需求。 傲瑞视频会议的纪要功能的效果如下图所示:

一、功能说明   

(1)主持人创建会议时,可勾选控制是否开启会议纪要。
(2)如果创建时没有开启纪要,也可以在会议进行过程中通过设置菜单手动开启。
(3)纪要功能开启后,服务端实时采集参会人发言音频,通过ASR语音转写与智能纪要接口自动整理成文,并实时推送至所有参会人员。
(4)参会者可实时查看动态更新的纪要内容,会议纪要完成后还能一键复制导出完整纪要内容,方便留存查阅。

二、实现原理

  要实现会议纪要功能,有两个地方需要用到AI功能。
(1)实时语音转文字(ASR)。
(2)会议摘要生成。
  傲瑞会议支持两种方式接入AI功能:
(1)利用大厂发布的AI服务(如讯飞在公网提供的服务接口)。
(2)私有部署本地大模型(如FunASR、QWen)。
  接入的两种AI能力,通过统一的接口规范起来,在部署的时候可以自由选择配置。

三、服务端实现

(1)AI 能力对接

  服务端实时拉取会议音频码流,同步转发至 AI 语音转写模型,实现语音到文本的实时转换;经由 AI 大模型完成语句规整、冗余语气词过滤、内容梳理归纳及会议重点萃取。
  首先是语音识别接口IASRWebApi,其定义如下:

复制代码
/// <summary>
/// 语音识别WebApi调用接口
/// </summary>
public interface IASRWebApi
{
    /// <summary>
    /// 每返回一段有效识别文本时触发
    /// </summary>
    event Action<VoiceDictationModel> AudioConvertSucceeded;

    /// <summary>
    /// 初始化ASR识别会话
    /// </summary>
    /// <param name="index">会话索引</param>
    /// <param name="speakerID">说话人</param>
    /// <param name="startTime">本次语音片段采集起始时间</param>
    void Init(int index, string speakerID, DateTime startTime);

    /// <summary>
    /// 单次完整说话结束调用,告知ASR引擎做断句、完整识别输出
    /// </summary>
    /// <param name="endTime">语音采集结束时间</param>
    /// <param name="isEnd">是否为整轮会话完全结束;true=关闭连接销毁会话,false=仅单句停顿结束</param>
    void AudioEndNotify(DateTime endTime, bool isEnd = false);

    /// <summary>
    /// 异步入队一段PCM音频分片数据,推送到ASR WebApi进行流式识别
    /// </summary>
    /// <param name="pcmData">PCM音频</param>
    /// <param name="_status">音频的发送状态;0:第一帧;1:继续帧;2:最后一帧  等</param>
    Task EnqueueAudioChunk(byte[] pcmData, int _status = -1);
}

  会议纪要接口IMeetingSumWebApi,其定义如下:

复制代码
/// <summary>
/// 会议纪要生成WebApi调用接口
/// </summary>
internal interface IMeetingSumWebApi
{
    /// <summary>
    /// 发起会议纪要/摘要生成请求
    /// </summary>
    /// <param name="speechModels">语音识别结果集合</param>
    /// <param name="isCompleteContent">true生成完整正式纪要,false为实时片段摘要</param>
    /// <returns>
    /// 第一个bool:true=接口调用正常,第二个string=生成的会议摘要/完整纪要文案;失败时返回错误描述
    /// </returns>
    Task<(bool, string)> Ask(List<VoiceDictationModel> speechModels, bool isCompleteContent = false);
}

  接下来我们给出 IMeetingSumWebApi 接口的讯飞版(调用讯飞的API)实现:

复制代码
/// <summary>
/// 调用 AI 会议纪要生成接口
/// </summary>  
public async Task<(bool, string)> Ask(List<VoiceDictationModel> speechModels, bool isCompleteContent = false)
{
    CancellationToken token = new CancellationToken();
    string resultStr = string.Empty;
    try 
    {
        string domain = "generalv3";
        _webSocketClient = new ClientWebSocket();
        await _webSocketClient.ConnectAsync(new Uri(authUrl), token);
        var speechTexts = speechModels.Select(s => $"{s.SpeakerID}:{s.ConvertText}");
        string allContent = string.Join("\n", speechTexts); 
        List<ReuqestContent> reuqestContents = new List<ReuqestContent>() {
            new ReuqestContent()
            {
                Role = "system",
                Content = isCompleteContent ? prompt1 : prompt2
            },
            new ReuqestContent()
            {
                Role = "user",
                Content = allContent
            }
        }; 
        var request = new JsonRequest()
        {
            Header = new RequestHeader()
            {
                AppId = _appId,
                Uid = "12345"
            },
            Parameter = new RequestParameter()
            {
                Chat = new RequestChat()
                {
                    Domain = domain,
                    Temperature = 0.5,
                    MaxTokens = 1024,
                }
            },
            Payload = new RequestPayload()
            {
                Message = new RequestMessage()
                {
                    Text = reuqestContents
                }
            }
        };
        var jsonStr = Newtonsoft.Json.JsonConvert.SerializeObject(request);
        await _webSocketClient.SendAsync(new ArraySegment<byte>(Encoding.UTF8.GetBytes(jsonStr)), WebSocketMessageType.Text, true, token); 
        var recvBuffer = new byte[1024];
        while (true)
        {
            WebSocketReceiveResult result = await _webSocketClient.ReceiveAsync(new ArraySegment<byte>(recvBuffer), token);
            if (result.CloseStatus.HasValue)
            { 
                return (true, "");
            }
            if (result.MessageType == WebSocketMessageType.Text)
            {
                string recvMsg = Encoding.UTF8.GetString(recvBuffer, 0, result.Count);
                var response = Newtonsoft.Json.JsonConvert.DeserializeObject<JsonResponse>(recvMsg);
                if (response.Header.Code != 0)
                { 
                    return (false, response.Header.Message);
                } 
                if (response.Payload.Choices.Status == 2)
                {
                    resultStr += string.Concat(response.Payload.Choices.Text.Select(x => x.Content)); 
                    return (true, resultStr);
                }
                resultStr += string.Concat(response.Payload.Choices.Text.Select(x => x.Content)); 
            }
            else if (result.MessageType == WebSocketMessageType.Close)
            {
                return (false, result.CloseStatusDescription);
            }
        }
    }
    catch (Exception e)
    {
        return (false, e.Message);
    }
    finally
    {
        await _webSocketClient?.CloseAsync( WebSocketCloseStatus.NormalClosure,"client raise close request",token);
    }
}

/// <summary>
/// 实时获取会议音频数据,混音分贝值排名第一的用户
/// </summary>
private void MicMixer_AudioMixed(string userID, byte[] bytes)
{ 
    AudioMixed?.Invoke(userID, bytes); 
}

/// <summary>
/// 对接语音听写服务接口
/// </summary>
/// <param name="curStatus">0:首次发送;1:发送中;2:结束发送</param>
/// <param name="data">音频数据</param> 
private void onSend(int curStatus, string data)
{
    if (curStatus == 0)
    {
        var json = new
        {
            common = new
            {
                app_id = $"{_appID}"
            },
            business = new
            {
                language = "zh_cn",
                domain = "iat",
                accent = "mandarin",
                eos = 10000
            },
            data = new
            {
                status = 0,
                format = "audio/L16;rate=16000",
                encoding = "raw",
                audio = $"{data}"
            }
        };
        string jsonStr = JsonConvert.SerializeObject(json, Formatting.Indented);
        _ws.SendAsync(new ArraySegment<byte>(Encoding.UTF8.GetBytes(jsonStr)), WebSocketMessageType.Binary, true, new CancellationToken());
    }
    else if (curStatus == 1)
    { 
        var json = new
        {
            data = new
            {
                status = 1,
                format = "audio/L16;rate=16000",
                encoding = "raw",
                audio = $"{data}"
            }
        };
        string jsonStr = JsonConvert.SerializeObject(json, Formatting.Indented);
        _ws.SendAsync(new ArraySegment<byte>(Encoding.UTF8.GetBytes(jsonStr)), WebSocketMessageType.Binary, true, new CancellationToken());
    }
    else if (curStatus == 2)
    { 
        var json = new
        {
            data = new
            {
                status = 2
            }
        };
        string jsonStr = JsonConvert.SerializeObject(json, Formatting.Indented);
        _ws.SendAsync(new ArraySegment<byte>(Encoding.UTF8.GetBytes(jsonStr)), WebSocketMessageType.Binary, true, new CancellationToken());
    }
}

(2)记录存储

  将语音实时转写文本、AI 完整会议纪要、会议重点摘要及相关元数据封装为统一数据对象,以 JSON 格式序列化后写入 TXT 文件进行本地持久化保存。统一文件命名与存储目录规范,便于后续纪要查阅、内容复用与历史记录检索,保障会议全量资料完整留存、可追溯可复用。

复制代码
   internal void Save()
   {
       try
       {
           string data = JsonConvert.SerializeObject(this);
           ESBasic.Helpers.FileHelper.GenerateFile(MeetingCacheFilePath, data);
       }
       catch (Exception ee)
       {
           Program.Logger.Log(ee, "MeetingMinutesCache.Save", ErrorLevel.Standard);
       }
   }

四、通信消息交互

  傲瑞会议采用 ESFramework完成客户端与服务端的实时通信,统一制定与会议纪要相关的业务消息协议。定义会议起始、AI 纪要分段下发、纪要生成完成等标准消息指令。依托标准协议标识规整消息交互逻辑,依托协议编号区分不同业务数据流,便于后续交互流程核查、问题定位与功能迭代,保障通信交互规范可追溯、易维护。

复制代码
/// <summary>
/// 开启会议纪要
/// </summary>
public int OpenMeetingMinutes { get; set; } = 665;

/// <summary>
/// 关闭会议纪要
/// </summary>
public int CloseMeetingMinutes { get; set; } = 666;

/// <summary>
/// 实时转写的纪要内容 | 服务端=>客户端
/// </summary>
public int MeetingSpeechSegment { get; set; } = 667;

/// <summary>
/// 完整的会议纪要 | 服务端=>客户端
/// </summary>
public int MeetingSummary { get; set; } = 668;

/// <summary>
/// 客户端请求历史会议纪要
/// </summary>
public int RequestMeetingMinutes { get; set; } = 669; 

五、客户端

(1)UI 控制

  会议主界面预留会议纪要功能独立入口,支持手动启停纪要服务。同时提供纪要面板展开 / 收起、内容复制等常用操作入口,界面布局简洁易用,操作逻辑直观便捷。

(2)纪要内容渲染

  基于 ESFramework 内置 IChatRender 消息渲染组件,可快速实现类似腾讯会议的纪要消息排版展示效果。客户端监听服务端实时推送的分段纪要数据,采用增量拼接、动态渲染机制,完成内容实时刷新。对完整纪要、AI 重点摘要、发言人信息、时间节点等重要信息进行清晰的展示。
  
  以上就是 傲瑞视频会议 借助AI来实现会议纪要功能的基本原理和方案,有什么疑问或建议的,欢迎留言讨论交流,谢谢!

相关推荐
水龙吟啸14 天前
机器学习安全:图像多分类任务的测试时对抗样本转移攻击实战(一)
机器学习·图像分类·安全性测试·asr·混淆矩阵·auc·转移攻击
逗逗班学Python22 天前
基于 Faster-Whisper 的本地语音转字幕与会议纪要系统:从音频转写到 SRT 字幕与 Markdown 纪要完整项目实战
python·语音识别·faster-whisper·字幕生成·会议纪要
三千军22 天前
Buzz语音转文字离线免费版安装使用(含Whisper最新模型)
whisper·免费·语音转文字·离线·buzz
云山雾村1 个月前
零基础也能用!科哥版Paraformer语音识别WebUI保姆级教程
语音识别·asr·星图gpu·中文语音转文字
瓷tun1 个月前
小白也能懂:Qwen3-ASR-0.6B语音识别入门教程
语音识别·asr·qwen3·星图gpu
胡耀超1 个月前
告别ModelScope魔搭联网依赖!sherpa-onnx + SenseVoice 完全离线语音识别部署指南(2026版,离线语音识别、声纹鉴定、sherpa-onnx、SenseVoice)
语音识别·funasr·语音转文字·sherpa-onnx·声纹鉴定·声纹比对·说话人识别
Luke Ewin1 个月前
Fun-ASR-Nano实时语音识别并区分说话人 | FunASR | 开源实时语音识别模型
人工智能·语音识别·asr·fun-asr
siv771 个月前
一站式 AI 视频翻译的技术架构:ASR → NMT → TTS → 字幕压制的全链路设计
whisper·tts·asr·nmt·ai视频翻译·视频翻译架构·字幕压制
HDD9852 个月前
2026年录音转文字工具实测:免费且好用的选择有哪些?
人工智能·语音识别·效率工具·语音转文字