FreeSwitch通过Websocket(流式双向语音)对接AI实时语音大模型技术方案(mod_ppy_aduio_stream)

FreeSwitch通过WebSocket对接AI实时语音大模型插件技术方案

在这里插入图片描述

1. 方案概述

基于FreeSWITCH的实时通信能力,通过WebSocket协议桥接AI大模型服务,实现低延迟、高并发的智能语音交互系统。支持双向语音流处理、实时ASR/TTS转换和动态业务指令执行。
1753095153158#pic_center)

有这么方面项目需要的可联系。https://cwn1.x3322.net:7777/down/0UgRahEtbPEa.so

类似技术参考:https://www.ddrj.com/callcenter/largemodel.html

2. 架构设计

graph LRA[FreeSWITCH] -->SIP/RTPB(WebSocket网关/SFU)B -->双向WebSocketC(AI Gateway)C -->HTTP/GRPC StreamD(大模型服务)D -->文本/控制指令CC -->TTS音频/指令BB -->RTP音频A

3. 核心组件

组件技术选型核心功能
媒体网关FreeSWITCH 1.10+处理SIP呼叫、RTP音频流、DTMF事件管理
协议桥接层mod_websocket (ESL+自定义模块)音频转WebSocket二进制流(支持OPUS/PCM)
AI网关Node.js/Python (Tornado)双向WS通信、ASR/TTS调度、会话状态机管理
大模型接口GRPC Stream/HTTP2 Server流式对话处理&指令生成(200ms级响应)
ASR/TTS引擎阿里云/讯飞/DeepSeek RTS实时语音<=>文本转换(<300ms延迟)
模型推理层DeepSeek-V2/GLM-4 API流式对话生成,支持SSML控制指令

4. 关键流程

4.1 语音输入流 (User → AI)

FreeSWITCH --(RTP)–> mod_websocket --(WS Binary/OPUS)–> AI网关 --(ASR API)–> 大模型

  • 数据封装
    json
    {
    “call_id”: “call-123456”,
    “seq”: 1024,
    “is_final”: false,
    “timestamp”: 1721541687000,
    “payload”: “BASE64_OPUS”
    }

4.2 AI响应流 (AI → User)

大模型 --(SSML指令)–> AI网关 --(WS控制消息)–> TTS服务 --(RTP)–> FreeSWITCH

  • 中断响应机制
    • DTMF #键触发barge-in事件
    • TTS首包到达时间<100ms

4.3 控制指令示例

json
// ASR识别结果
{“event”:“asr_result”, “text”:“查余额”, “confidence”:0.95}

// TTS响应指令
{“event”:“ai_response”, “type”:“tts”, “audio”:“chunk_123.opus”}

// 业务转移指令
{“event”:“action”, “command”:“transfer:6001”}

5. 性能优化

  • 音频分片处理:80ms/帧(160采样@16kHz)
  • 双缓冲ASR策略:预加载静音语音模型加速首字响应
  • 动态抖动缓冲:网络延迟>150ms时自动补偿
  • 会话热插拔:通话保持时维持AI对话上下文
  • 熔断机制:模型响应>2s时转人工服务

6. 异常处理机制

故障场景解决方案
WebSocket断连10秒自动重连+20秒音频缓存
ASR识别冲突基于时间戳的序列仲裁
模型响应超时播放「正在思考」提示音
DTMF中断事件立即停止TTS并清空队列
编码格式不匹配OPUS/PCM/G.711动态切换
local cjson = require "dkjson"
local pts   = require "ppytools"local ws_addr = "ws://127.0.0.1:20000"
ws_addr = "wss://127.0.0.1:12345"
--ws_addr = "wss://ai.xxx.com:12345"local records_base = "/workspace/records"local script_path = debug.getinfo(1, "S").source:sub(2)
local script_name = script_path:match("([^/\\]+)$") or "unknown"local fs_api = freeswitch.API()function fslog(msg, log_level)log_level = (log_level ~= nil) and log_level or "info"  -- 严格判断nilfreeswitch.consoleLog(log_level, "[".. script_name .. "] "..msg)
endfunction main()local session_lega = sessionlocal session_lega_uuid = session_lega:get_uuid()fslog(string.format("[START][%s]\n", session_lega_uuid))session_lega:answer()local datetime_dir, records_dir = pts.create_compact_date_dir(records_base)local caller_id_number = session_lega:getVariable("caller_id_number")local destination_number = session_lega:getVariable("destination_number")fslog(string.format("session_lega_uuid: %s , caller_id_number: %s , destination_number: %s\n", session_lega_uuid, caller_id_number, destination_number))--后台通话录音if records_dir ~= nil then-- 启用双声道录音session_lega:setVariable("RECORD_STEREO", "true")  local records_str = string.format("bgapi uuid_record %s start %s/%s.wav 1000 0 0", session_lega_uuid, records_dir, session_lega_uuid)fslog(records_str)fs_api:executeString(records_str) --CDR自定义变量session_lega:setVariable("record_file_uri_path", string.format("%s/%s.wav", datetime_dir, session_lega_uuid))end--缺省将用户语音数据通过二进制方式发送到AI服务器。--如果这个参数设置为true,则通过JSON格式发送。和AI服务器发给FS的JSON格式一致session_lega:setVariable("STREAM_MESSAGE_SENDJSON", "true")local con = freeswitch.EventConsumer()con:bind("CUSTOM", "mod_audio_stream::json")con:bind("CUSTOM", "mod_audio_stream::connect")con:bind("CUSTOM", "mod_audio_stream::disconnect")con:bind("CUSTOM", "mod_audio_stream::error")local start_time = os.date("%Y-%m-%d %H:%M:%S", os.time())local metadata_obj = {type = "init",sid  = session_lega_uuid,phone_number = caller_id_number,timestamp = start_time}local metadata = cjson.encode(metadata_obj)fslog("metadata:" .. metadata)local result, err = fs_api:execute("uuid_audio_stream", string.format("%s start %s mono 8k %s", session_lega_uuid, ws_addr, metadata))if result thenfslog(string.format("Function executed successfully: %s\n", result), "notice")elsefslog(string.format("Error executing function: %s\n", err), "err")endwhile session_lega:ready() dolocal event = con:pop()if event thenlocal event_uuid = event:getHeader("Unique-ID")if event_uuid == session_lega_uuid thenlocal event_name = event:getHeader("Event-Name")local event_sub = event:getHeader("Event-Subclass")local body = event:getBody()fslog(string.format("JSON executing function, Event-Subclass: %s, body: %s\n", event_sub, body))if event_sub == "mod_audio_stream::connect" then--elseif event_sub == "mod_audio_stream::disconnect" thenbreakelseif event_sub == "mod_audio_stream::json" thenlocal data = cjson.decode(body)if data.type == "sentence" and data.status == "start" thenlocal metadata_obj = {type = "sentence_callback",sentence_id  = data.sentence_id,status = "play",timestamp = os.date("%Y-%m-%d %H:%M:%S", os.time())}local metadata = cjson.encode(metadata_obj)fslog("[send_text]metadata:" .. metadata)fs_api:execute("uuid_audio_stream", string.format("%s send_text %s", session_lega_uuid, metadata))endif data.type == "streamText" thenif data.assistant thenfslog(data.assistant)endendif data.toHuman thenbreakelseif data.stop thenfslog("data stop", "err")elseif data.clear thenfslog("data clear", "err")endelseif event_sub == "mod_audio_stream::error" thenbreakelse--endendelseif session_lega thensession_lega:sleep(20)elsebreakendendend--fs_api:execute("uuid_record", string.format("%s stop", session_lega_uuid))fslog(string.format("[END][%s]\n", session_lega_uuid))
endmain()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/915610.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/915610.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

航班调度优化策略全局概览

在机场关闭场景下的航班恢复工作&#xff0c;是将机场关闭期间所有的航班进行取消然后恢复还是将机场关闭期间航班全部延误而后调整呢&#xff1f;简单来说&#xff0c;在实际操作中&#xff0c;既不是无差别地全部取消&#xff0c;也不是无差别地全部延误。这两种“一刀切”的…

spring boot 异步线程@Async 传递 threadLocal数据

将父类的 threadLocal 的数据 在线程池时&#xff0c;可以转给子线程使用。 Async 的使用。 第一步在启动服务加上 EnableAsync 注解。 EnableAsync public class NetCoreApplication {... ... }第二步&#xff1a;导入阿里 线程工具类<dependency><groupId>com.a…

AI产品经理成长记《零号列车》第一集 邂逅0XAI列车

《零号列车》绝非传统意义上的 AI 产品经理教程 —— 它是我沉淀二十多年跨行业数字化转型与工业 4.0 实战经验后,首创的100集大型小说体培养指南。那些曾在千行百业验证过的知识与经验,不再是枯燥的文字堆砌,而是化作一场沉浸式的学习旅程。​ 这里没有生硬的理论灌输,而…

[C++11]范围for循环/using使用

范围for循环 范围for循环&#xff08;Range-based for loop&#xff09;是 C11 引入的一种简洁的循环语法&#xff0c;用于遍历容器中的元素或者其他支持迭代的数据结构。 范围for循环可以让代码更加简洁和易读&#xff0c;避免了传统for循环中索引的操作。 下面是范围for循环的…

简单了解下npm、yarn 和 pnpm 中 add 与 install(i) 命令的区别(附上两图带你一目明了)

目录 pnpm 中 add 和 i 的区别 npm 中 add 和 i 的区别 yarn 中 add 和 i 的区别 附上两图带你一目明了&#xff1a; npm、yarn和pnpm的三者区别图&#xff1a; i 和 add 的核心区别图&#xff1a; 个人建议&#xff1a;在项目中保持命令使用的一致性&#xff0c;选择一种…

ESP32-S3学习笔记<2>:GPIO的应用

ESP32-S3学习笔记&#xff1c;2&#xff1e;&#xff1a;GPIO的应用1. 头文件包含2. GPIO的配置2.1 pin_bit_mask2.2 mode2.3 pull_up_en和pull_down_en2.4 intr_type3. 设置GPIO输出/获取GPIO输入4. 中断的使用4.1 gpio_install_isr_service4.2 gpio_isr_handler_add4.3 gpio_…

得物视觉算法面试30问全景精解

得物视觉算法面试30问全景精解 ——潮流电商 商品鉴别 视觉智能&#xff1a;得物视觉算法面试核心考点全览 前言 得物App作为中国领先的潮流电商与鉴别平台&#xff0c;持续推动商品识别、真假鉴别、图像搜索、内容审核、智能推荐等视觉AI技术的创新与落地。得物视觉算法岗位…

[Linux入门] Linux 账号和权限管理入门:从基础到实践

一、Linux 用户账号&#xff1a;谁能访问系统&#xff1f; 1️⃣超级用户&#xff08;root&#xff09; 2️⃣普通用户 3️⃣程序用户 二、组账号&#xff1a;让用户管理更高效 1️⃣组的类型 2️⃣特殊组 三、用户与组的 “身份证”&#xff1a;UID 和 GID 四、配置文…

阿里云ssl证书自动安装及续订(acme)

目录 一、shell命令安装 二、docker run安装 三、docker compose安装 一、shell命令安装 # 安装acme curl https://get.acme.sh | sh -s emailfloxxx5163.com# 注册zerossl .acme.sh/acme.sh --register-account -m flowxxx25163.com --server zerossl# 获取证书 export Al…

@fullcalendar/vue 日历组件

功能&#xff1a;日程安排&#xff0c;展示日历&#xff0c;可以用来做会议日历&#xff0c;可以跨日期显示日程。 Fullcalendarvue3 日历组件 参考文档&#xff1a;【vue2】一个完整的日历组件 fullcalendar&#xff0c;会议预约功能 中文说明文档&#xff1a;https://www.he…

Dijkstra 算法求解多种操作

一、问题背景与核心需求 需要找到从a到b的最优操作序列&#xff0c;使得总花费最小。三种操作的规则为&#xff1a; 操作 1&#xff1a;x → x1&#xff0c;花费c1&#xff1b;操作 2&#xff1a;x → x-1&#xff0c;花费c2&#xff1b;操作 3&#xff1a;x → x*2&#xff0…

本地项目提交到git教程

创建远程仓库 登录 GitHub&#xff0c;点击右上角 New repository。 填写仓库名称&#xff08;如 my-project&#xff09;、描述&#xff0c;选择公开 / 私有。 不要初始化 README、.gitignore 或 LICENSE&#xff08;保持空仓库&#xff09;&#xff0c;点击 Create repositor…

Linux 密码生成利器:pwgen 命令详解

往期好文&#xff1a;统信 UOS 运行 Windows 应用新利器&#xff01;彩虹虚拟化软件 V3.2 全新上线&#xff0c;限时30天免费体验 在日常运维、安全测试、用户管理等场景中&#xff0c;随机密码的生成是一项常见需求。为了避免人工设置密码带来的重复性弱密码问题&#xff0c;…

Qt 应用程序入口代码分析

Qt 应用程序入口代码分析 这段代码是 Qt GUI 应用程序的标准入口点&#xff0c;相当于 Qt 程序的"心脏"。让我详细解释每一部分的作用&#xff1a; int main(int argc, char *argv[]) {// 1. 创建 Qt 应用程序对象QApplication a(argc, argv);// 2. 创建主窗口对象Wi…

基于springboot+mysql的中小型医院网站(源码+论文+开题报告)

一、开发环境 Java技术 描述&#xff1a;Java是一种非常常用的编程语言&#xff0c;在全球编程语言排行榜上总是前三。Java的跨平台能力十分强大&#xff0c;只需一次编译&#xff0c;任何地方都可以运行。除此之外&#xff0c;它还拥有简单的语法和实用的类库&#xff0c;让…

【Docker基础】Docker-compose常用命令实践(三):镜像与配置管理

目录 前言 1 镜像与配置管理概述 1.1 核心概念解析 2 镜像构建命令详解 2.1 构建镜像&#xff08;build命令&#xff09; 2.2 基本语法 2.3 常用选项 2.4 构建过程流程 2.5 实际应用案例 3 配置验证命令详解 3.1 验证配置&#xff08;config命令&#xff09; 3.2 基…

Android 实例 - 分页器封装实现(上一页按钮、下一页按钮、当前页码 / 总页数、每页条数、总记录数)

一、需求分页器需要包含&#xff1a;【上一页按钮】、【下一页按钮】、【当前页码 / 总页数】、【每页条数】、【总记录数】点击【上一页按钮】&#xff0c;渲染上一页的数据&#xff0c;如果当前页码为第一页&#xff0c;则禁用【上一页按钮】点击【下一页按钮】&#xff0c;渲…

从代码学习深度强化学习 - SAC PyTorch版

文章目录 前言 SAC处理连续动作空间问题 (Pendulum-v1) 核心代码实现 **工具函数与环境初始化** **ReplayBuffer、网络结构与SAC算法** **训练与结果** SAC处理离散动作空间问题 (CartPole-v1) 核心代码实现 **工具函数与环境初始化** **ReplayBuffer、网络结构与SAC算法 (离散…

物联网安装调试-温湿度传感器

以下为温湿度传感器在物联网安装调试中的全流程技术指南,涵盖选型、安装、调试及故障排查,结合工业/农业/家居三大场景实操要点: 一、传感器选型核心参数表 参数 工业场景 农业大棚 智能家居 选型建议 精度 0.5℃/1.5%RH 1℃/3%RH 1℃/5%RH 工业级首选Sensirion SHT3x系列 防…

MySQL 核心知识点梳理(1)

目录 1.什么是数据库? 关系型数据库 非关系型数据库 2.Mysql出现性能差的原因? 3.MySQL的内联,左外联,右外连接的区别 4.为什么要有三大范式 建表需要考虑的问题? char和varchar的区别 blob和text的区别? DATETIME和TIMESTAMP的区别 in和exists的区别 null值陷 …