FastAPI + WebRTC + RAG 搭建实时语音助手:Voice Agent Demo 完整步骤
目录一、整体链路从麦克风到实时转写二、为什么 Voice Agent 要优先接流式 ASR三、前端浏览器获取麦克风音频四、后端FastAPI 负责信令aiortc 负责接音频五、aiortc 接收到的音频帧怎么转 PCM六、把 ASR 封装成一个可替换接口七、识别结果怎么回传前端八、实时 ASR 接入后还要看哪些指标九、接上 RAG 后链路会变成什么样十、常见问题FastAPI WebRTC 怎么实现实时语音识别WebRTC 音频怎么传给后端 ASRaiortc 接收到的音频帧怎么转 PCMVoice Agent 为什么需要流式 ASRDataChannel 和 WebSocket 哪个更适合返回识别结果实时 ASR 延迟应该怎么优化十一、总结上一篇我用 FastAPI WebRTC RAG 跑通了一个简化版 Voice Agent Demo浏览器和后端能建连用户问题可以通过 DataChannel 发到后端后端再用一个本地知识库返回回答。但那个 demo 还缺最关键的一步浏览器麦克风里的声音怎么实时变成文字如果要用 FastAPI WebRTC 接入流式 ASR可以把链路拆成四步浏览器用getUserMedia()获取麦克风音频WebRTC 把音频轨道传给后端后端用aiortc接收音频帧并转换成 ASR 可用的 PCM 数据流式 ASR 返回识别文本再通过 DataChannel 或 WebSocket 推回前端。也就是说Voice Agent 从“文本问答 Demo”走向“语音助手 Demo”中间最关键的一层就是流式 ASR。没有 ASRWebRTC 只是在传音频接入 ASR 后系统才真正能理解用户说了什么。一、整体链路从麦克风到实时转写一个最小的实时语音识别链路可以这样理解浏览器麦克风 - WebRTC audio track - FastAPI / aiortc 接收音频帧 - 转成 PCM / wav chunk - 发送给流式 ASR - 得到 transcript - DataChannel / WebSocket 返回前端 - 后续接 RAG / LLM / TTS这里有几个点很容易混在一起FastAPI 不是直接处理音频流它主要负责 HTTP 接口、信令、页面托管和业务接口WebRTC 负责低延迟传输浏览器音频aiortc负责在 Python 后端接住 WebRTC audio trackASR 负责把音频转成文字DataChannel 或 WebSocket 负责把识别结果推回浏览器。如果是 AI 语音客服、网页语音助手、实时语音对话系统这条链路基本都绕不开。差别只在于ASR 用本地模型还是云服务后面接不接 RAG最后是否用 TTS 把回答读出来。二、为什么 Voice Agent 要优先接流式 ASR做 Voice Agent 时我不建议一开始就把 ASR、RAG、LLM、TTS、转人工全部堆到一个 demo 里。更稳的顺序是先跑通 WebRTC 建连再接流式 ASR让语音能变成文字然后接 RAG把回答拉回知识库再接 LLM 生成回答最后接 TTS、打断、转人工和日志。原因很简单语音链路出问题时排查成本很高。如果用户说话后系统没反应可能是麦克风没采到音也可能是 WebRTC 没建连可能是音频格式不对也可能是 ASR 没返回还可能是后面的模型卡住了。所以第二步先接 ASR是为了把“实时音频输入”这件事单独验证清楚。三、前端浏览器获取麦克风音频前端先创建RTCPeerConnection再通过getUserMedia()获取麦克风音频轨道。constpcnewRTCPeerConnection();constdcpc.createDataChannel(events);dc.onmessage(event){constdataJSON.parse(event.data);console.log(ASR result:,data);};conststreamawaitnavigator.mediaDevices.getUserMedia({audio:true});for(consttrackofstream.getTracks()){pc.addTrack(track,stream);}这里的audio track会通过 WebRTC 传到后端。前端不需要自己把音频拆成 PCM也不需要手动上传 wav 文件。如果只是做最小 demo浏览器端只要负责三件事采集麦克风建立 WebRTC 连接接收后端返回的识别结果。四、后端FastAPI 负责信令aiortc 负责接音频后端先提供一个/offer接口接收浏览器传来的 SDP offer并返回 answer。fromfastapiimportFastAPI,Requestfromfastapi.responsesimportJSONResponsefromaiortcimportRTCPeerConnection,RTCSessionDescription appFastAPI()pcsset()app.post(/offer)asyncdefoffer(request:Request):paramsawaitrequest.json()offerRTCSessionDescription(sdpparams[sdp],typeparams[type])pcRTCPeerConnection()pcs.add(pc)pc.on(track)defon_track(track):iftrack.kindaudio:# 这里把音频轨道交给 ASR 处理start_asr_task(track)awaitpc.setRemoteDescription(offer)answerawaitpc.createAnswer()awaitpc.setLocalDescription(answer)returnJSONResponse({sdp:pc.localDescription.sdp,type:pc.localDescription.type,})这段代码只做两件事完成 WebRTC 信令协商并在收到音频轨道时启动 ASR 任务。真正关键的地方在track.recv()。五、aiortc 接收到的音频帧怎么转 PCM在aiortc里音频轨道是一个不断产生 frame 的对象。后端可以在循环里持续接收音频帧。asyncdefconsume_audio(track,asr_client):whileTrue:frameawaittrack.recv()pcmframe.to_ndarray()awaitasr_client.send_audio(pcm)这个例子只是说明思路。真实接 ASR 时还要处理三个问题采样率是否符合 ASR 要求例如 16k 或 48k声道数是否需要转成 monoframe 数据是否需要重新编码成 PCM bytes。很多 ASR 服务并不直接接受numpy.ndarray而是要求bytes。这时可以把音频帧转成指定格式后再发送。asyncdefconsume_audio(track,asr_client):whileTrue:frameawaittrack.recv()pcm_arrayframe.to_ndarray()# 根据 ASR 服务要求做重采样、声道转换和编码pcm_bytesconvert_to_pcm16(pcm_array)awaitasr_client.send_audio(pcm_bytes)这里的convert_to_pcm16()可以根据实际 ASR 服务来实现。比如本地模型、云厂商 ASR、FunASR、Whisper streaming、SenseVoice、讯飞、阿里云、火山等对输入格式的要求都不完全一样。六、把 ASR 封装成一个可替换接口为了避免 demo 和某一个 ASR 厂商绑死建议先抽象一个StreamingASRClient。classStreamingASRClient:asyncdefstart(self):passasyncdefsend_audio(self,pcm_bytes:bytes):passasyncdefreceive_text(self)-dict:passasyncdefclose(self):pass后面无论接本地模型还是云服务都尽量保持这几个方法不变。这样做的好处是Voice Agent 的主链路不用关心 ASR 厂商是谁只关心“音频发出去文字拿回来”。一个识别结果可以统一成这样的结构{type:asr_partial,text:我想查一下订单,is_final:false,latency_ms:320}最终句可以这样返回{type:asr_final,text:我想查一下订单为什么还没有发货,is_final:true,latency_ms:860}这两个字段很重要is_finalfalse表示中间识别结果前端可以实时显示is_finaltrue表示一句话基本结束可以交给 RAG 或 LLM。七、识别结果怎么回传前端识别结果可以通过 DataChannel 回传也可以通过 WebSocket 回传。如果这只是一个 WebRTC 语音助手 DemoDataChannel 更简单。因为音频轨道和事件消息都挂在同一个 PeerConnection 上状态更集中。pc.on(datachannel)defon_datachannel(channel):pc.asr_channelchannel当 ASR 返回文字时channel.send(json.dumps({type:asr_final,text:我想查一下订单为什么还没有发货,is_final:True},ensure_asciiFalse))如果你希望 ASR 和 WebRTC 解耦比如后面要接多个页面、多个会话、客服工作台、日志系统那么 WebSocket 会更清晰。我的建议是demo 阶段用 DataChannel生产环境根据会话管理复杂度选择 WebSocket 或消息队列无论用哪种方式都要带上session_id、trace_id和时间戳。八、实时 ASR 接入后还要看哪些指标接入 ASR 不等于语音助手就能用了。至少要看这几个指标首字延迟用户开始说话后多久能看到第一段识别文本最终句延迟用户说完后多久返回稳定结果识别准确率业务名词、数字、地址、订单号是否识别正确噪声鲁棒性办公室、门店、电话外放环境下是否能用中断处理用户说到一半停顿系统是否误判结束热词能力产品名、品牌名、业务术语能不能识别日志追踪每次识别是否记录音频时长、延迟、模型版本和错误原因。对 AI 语音客服来说ASR 错了后面的 RAG 和 LLM 很可能都会错。所以 ASR 是 Voice Agent 链路里最应该先测清楚的一层。九、接上 RAG 后链路会变成什么样当 ASR 返回asr_final后就可以把文字交给 RAG。asyncdefon_asr_final(text:str):docsretrieve(text)answercompose_answer(text,docs)returnanswer这一步的重点不是“让大模型自由发挥”而是让回答有依据。比如用户问“我的订单为什么还没发货”系统应该先根据用户身份和订单号查业务系统再检索售后政策、物流规则、发货说明。如果知识库和业务系统都没有依据就应该返回“当前资料不足需要转人工”而不是编一个看起来很顺的答案。这也是 Voice Agent 和普通语音聊天机器人的区别它不只是把话听懂还要知道哪些话能回答哪些话必须交给人工。十、常见问题FastAPI WebRTC 怎么实现实时语音识别核心做法是浏览器用 WebRTC 发送麦克风音频FastAPI 提供/offer信令接口后端用aiortc接收 audio track再把音频帧转换成 ASR 可用的 PCM 数据最后把识别结果通过 DataChannel 或 WebSocket 返回前端。WebRTC 音频怎么传给后端 ASR浏览器通过getUserMedia()获取麦克风音频再用pc.addTrack()加入 PeerConnection。后端aiortc会在on_track事件里拿到 audio track然后通过await track.recv()持续接收音频帧。aiortc 接收到的音频帧怎么转 PCM可以先用frame.to_ndarray()取出音频数据再根据 ASR 服务要求做重采样、声道转换和 PCM16 编码。不同 ASR 服务对采样率、声道数、编码格式要求不同不能直接假设所有服务都接受同一种格式。Voice Agent 为什么需要流式 ASR因为 Voice Agent 需要低延迟理解用户说话内容。如果只等用户整段说完再上传音频文件响应会很慢也很难支持打断、实时字幕和多轮对话。流式 ASR 可以边听边识别是实时语音助手和 AI 语音客服的基础能力。DataChannel 和 WebSocket 哪个更适合返回识别结果demo 阶段 DataChannel 更简单因为音频和文本事件都在同一个 WebRTC 连接里。生产环境如果需要更复杂的会话管理、日志系统、客服工作台或多端同步WebSocket 或消息队列会更清晰。实时 ASR 延迟应该怎么优化可以从几个方向入手减少音频 buffer选择更低延迟的 ASR 服务避免不必要的格式转换使用 VAD 判断说话边界把 ASR 中间结果及时返回前端并记录首字延迟和最终句延迟。十一、总结用 FastAPI WebRTC 接入流式 ASR本质上是在解决一个问题浏览器里的实时语音如何稳定地变成后端可处理的文字。最小链路可以概括为getUserMedia 采集麦克风 - WebRTC 传输音频 - aiortc 接收音频帧 - 转成 PCM - 流式 ASR 返回 transcript - DataChannel / WebSocket 推回前端 - 后续接 RAG、LLM、TTS 和转人工如果第一篇文章解决的是“Voice Agent 的最小闭环怎么搭”那么这一篇解决的是“语音输入层怎么补上”。下一步再往下做就可以把asr_final接到 RAG要求回答必须带引用依据再把回答接到 TTS形成真正的实时语音问答闭环。但在继续加能力之前我建议先把 ASR 这一层测清楚首字延迟、最终句延迟、识别准确率、业务热词、噪声鲁棒性和日志追踪。因为在 AI 语音客服里听错一句话后面所有智能都可能变成“认真地答错”。

相关新闻

服装零售管理效率测评:进销存系统如何影响库存周转与利润透明度

服装零售管理效率测评:进销存系统如何影响库存周转与利润透明度

“客流少、复购低、客单小、压库存,不知道利润从哪里提升”——这是过去十年实体服装店反复提及的痛点,但真正可怕的是:多数老板根本不知道自己哪一笔钱赚得糊涂,哪一笔钱亏得冤枉。 中国服装协会《数字化转型选型目录》的调研显示…

2026/6/27 18:16:36阅读更多 →
终极指南:茉莉花插件如何一键解决中文文献管理难题,提升科研效率90%

终极指南:茉莉花插件如何一键解决中文文献管理难题,提升科研效率90%

终极指南:茉莉花插件如何一键解决中文文献管理难题,提升科研效率90% 【免费下载链接】jasminum A Zotero add-on to retrive CNKI meta data. 一个简单的Zotero 插件,用于识别中文元数据 项目地址: https://gitcode.com/gh_mirrors/ja/jasm…

2026/6/27 18:16:36阅读更多 →
Hitboxer终极指南:告别按键冲突,释放游戏操作潜能

Hitboxer终极指南:告别按键冲突,释放游戏操作潜能

Hitboxer终极指南:告别按键冲突,释放游戏操作潜能 【免费下载链接】socd Key remapper for epic gamers 项目地址: https://gitcode.com/gh_mirrors/so/socd 你是否曾在激烈的游戏对决中,因同时按下左右方向键而导致角色卡顿&#xff…

2026/6/27 18:16:36阅读更多 →
如何快速精通猫抓工具:新手终极实战手册

如何快速精通猫抓工具:新手终极实战手册

如何快速精通猫抓工具:新手终极实战手册 【免费下载链接】cat-catch 猫抓 浏览器资源嗅探扩展 / cat-catch Browser Resource Sniffing Extension 项目地址: https://gitcode.com/GitHub_Trending/ca/cat-catch 还在为网页上的精彩视频无法保存而烦恼吗&…

2026/6/27 19:51:42阅读更多 →
两位华西博士+两位副高:皓贝一口腔医院的专家底气

两位华西博士+两位副高:皓贝一口腔医院的专家底气

皓贝一口腔医院的专家团队配置,围绕“两位华西博士两位副主任医师”的核心架构展开。两位华西博士分别来自四川大学华西口腔医学院和华西医科大学口腔医学院,在种植和正畸两个方向各有专攻。种植方向的博士专家,拥有15年三甲口腔专科医院种植…

2026/6/27 19:51:42阅读更多 →
筛选自动化获客工具,至臻AI覆盖抖音全链路

筛选自动化获客工具,至臻AI覆盖抖音全链路

如何理性看待“青岛AI员工软件排名”与选型维度在寻找“青岛AI员工软件排名前十的品牌有哪些”相关答案时,企业主往往会面临信息不对称的问题。实际上,行业内并没有官方发布的绝对排名,不同工具的技术路径和适用场景差异巨大。与其纠结于模糊…

2026/6/27 19:51:42阅读更多 →
5分钟解锁网易云音乐NCM格式:跨平台音乐解密全攻略

5分钟解锁网易云音乐NCM格式:跨平台音乐解密全攻略

5分钟解锁网易云音乐NCM格式:跨平台音乐解密全攻略 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 你是否曾经下载了网易云音乐的歌曲,却发现它们被加密成NCM格式,只能在特定客户端播放&#xff1…

2026/6/27 19:51:42阅读更多 →
【JAVA毕设源码分享】基于SpringBoot的宠物领养一站式服务系统设计与实现(程序+文档+代码讲解+一条龙定制)

【JAVA毕设源码分享】基于SpringBoot的宠物领养一站式服务系统设计与实现(程序+文档+代码讲解+一条龙定制)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

2026/6/27 19:51:41阅读更多 →
从30分钟到5分钟!用C++20模块化与分布式编译榨干硬件性能

从30分钟到5分钟!用C++20模块化与分布式编译榨干硬件性能

作为一名深耕C多年的技术专家,我深知编译效率对开发者的意义——它不仅是生产力的放大器,更是项目成败的隐形推手。想象一下,修改一行代码,却要等待30分钟才能看到结果,这种“时间黑洞”足以扼杀任何灵感。今天&#x…

2026/6/27 19:46:41阅读更多 →
【人工智能】一文搞定到底什么是智能体

【人工智能】一文搞定到底什么是智能体

【人工智能】一文搞定到底什么是智能体 一文搞定到底什么是智能体【人工智能】一文搞定到底什么是智能体一. LM,WorkFlow,Agent分别有什么么不同二. Agent的思考过程是怎样的三. Agent的五个核心部分1)LLM2)Prompt3)Me…

2026/6/27 11:20:40阅读更多 →
嵌入式GUI控件实战:ROTARY、SCROLLBAR、SLIDER原理与应用

嵌入式GUI控件实战:ROTARY、SCROLLBAR、SLIDER原理与应用

1. 嵌入式GUI控件:从原理到实战的深度解析在嵌入式系统开发中,图形用户界面(GUI)的设计与实现往往是项目从“能用”到“好用”的关键一跃。不同于资源充沛的PC或移动平台,嵌入式设备的GUI需要在有限的CPU性能、内存空间…

2026/6/27 5:46:02阅读更多 →
Google AI Studio 300美元额度的真相与实战指南

Google AI Studio 300美元额度的真相与实战指南

1. 这300美金不是“送钱”,而是Google埋下的第一道技术门槛 你看到标题里那个醒目的“$300美金”时,第一反应可能是:又一个免费额度?领完就完事?我亲手试过——这300美金根本不是红包,而是一张入场券&…

2026/6/27 11:20:39阅读更多 →
10分钟AI语音克隆与实时变声:Retrieval-based-Voice-Conversion-WebUI完整指南

10分钟AI语音克隆与实时变声:Retrieval-based-Voice-Conversion-WebUI完整指南

10分钟AI语音克隆与实时变声&#xff1a;Retrieval-based-Voice-Conversion-WebUI完整指南 【免费下载链接】Retrieval-based-Voice-Conversion-WebUI Easily train a good VC model with voice data < 10 mins! 项目地址: https://gitcode.com/GitHub_Trending/re/Retrie…

2026/6/27 0:04:03阅读更多 →
Layerdivider:3分钟AI智能分层,彻底告别手动抠图时代

Layerdivider:3分钟AI智能分层,彻底告别手动抠图时代

Layerdivider&#xff1a;3分钟AI智能分层&#xff0c;彻底告别手动抠图时代 【免费下载链接】layerdivider A tool to divide a single illustration into a layered structure. 项目地址: https://gitcode.com/gh_mirrors/la/layerdivider 还在为复杂的图像分层工作烦…

2026/6/27 0:04:03阅读更多 →
Tomcat中X-Frame-Options配置实战:防御点击劫持的四种方法与最佳实践

Tomcat中X-Frame-Options配置实战:防御点击劫持的四种方法与最佳实践

1. 项目概述&#xff1a;为什么X-Frame-Options是Web安全的“防盗门”&#xff1f;最近在排查一个老项目的安全审计报告时&#xff0c;又被提到了“点击劫持”风险&#xff0c;矛头直指缺失的X-Frame-Options响应头。这已经不是第一次了&#xff0c;很多开发团队&#xff0c;尤…

2026/6/27 0:04:03阅读更多 →