先看这一天在干什么
可以把系统想成两条线,同时转: 运营线(人在电脑前)登录智控台 → 管理设备与绑定 → 给智能体选模型、音色、插件和知识库 → 配团队权限 → 配置下发到对应设备。业务接口还能按区域切换,账号体系相对固定。 会话线(人在设备前)
设备连上语音服务 → 按「这台设备」的配置装配能力 → 用户说话 → 系统听清、思考、必要时查知识或调工具 → 把话播出来 → 用户打断时,上一轮声音必须立刻停干净。 若两条线各干各的:现场只能改本地文件;助手停在单轮闲聊;多团队时数据边界含糊。这半年做的事,就是把它们收成一条能运营、能降级的闭环。
一次对话怎么走完
下面按时间顺序走一遍(经典语音路径)。括号里是人话解释。- 建连
设备通过 WebSocket 连上语音服务,一带上设备身份。一设备一连接,会话状态挂在这条连接上。 - 拉配置
若走智控台模式:先拿基座配置,再按设备拉「这一台」的智能体、模型、插件等差异项。模块没就绪前,不急着处理用户语音,避免半初始化乱答。本地开发也可以不连智控台,用本地配置跑通。 - 听(VAD)
从连续音频里判断「用户是不是在说话、说完没有」。这是后面一切的门闩。 - 识(ASR)
把语音打成文字。本地识别与云端识别在资源占用上不一样:有的可以多连接共享,有的必须每连接隔离。 - 想 / 办事(LLM + 工具)
模型根据上下文决定直接回答,还是先调工具(查知识、设备能力、外部 MCP 等)。工具不是散落在代码各处的 if,而是收成统一目录再分发。 - 说(TTS)
文字合成语音流式回传。理想情况是模型还没说完整段,设备已经开始播,体感才不像卡死。 - 打断
用户说「停」或重新说话时:停止采集只是第一步;还在飞的生成、播报、字幕必须标成「过期」丢掉,否则会出现幽灵播报——旧句子冷不丁冒出来。长耗时工具也要有开始/结束状态,打断时把界面收干净。
系统怎么拆
三层模块地图
我覆盖全链路,下面按层列能力——这是这半年「面」上做了什么。智控台前端
- 设备、智能体、模型、知识库、音色/克隆等管理界面
- 路由与按钮级权限(和后端权限字符串对齐)
- 团队上下文由具体业务请求显式带上,而不是全局偷偷注入(写错团队的代价很高)
- 账号域相对固定;业务 API 可按数据中心/区域切换
智控台后端
- 设备绑定与生命周期、智能体与模型配置、知识库运营入口
- 系统功能权限(菜单/角色)与 团队数据权限(资源归属)分开建模
- 读操作可在授权团队范围内聚合;写操作必须有明确团队上下文
- 关键查询侧带团队过滤,不只靠前端藏按钮
- 知识库通过适配器对接外部检索/向量引擎,业务层不绑死一家
- 给设备侧的配置接口用服务身份保护,和运营人员的登录会话分离;基座配置可缓存
设备侧语音服务
- 连接生命周期:握手、后台初始化、绑定/未绑定分支
- 按配置装配 VAD/ASR/LLM/TTS/记忆/意图等可替换模块
- 文本消息按类型路由(hello、打断、监听、IoT、MCP、视觉等),避免全堆在连接主逻辑里
- 统一工具调度:服务端插件、服务端/设备 MCP、设备 IoT 等
- 会话内按需检索知识(短超时,失败要说人话)
- 日志按模块打标,并带设备/会话上下文;配置日志脱敏
三个深挖点
1. 配置:既要统一运营,又要能本地调试
最早如果只认本地文件,运维根本管不住现场;如果一切必须走云端,联调又慢得难受。后来定成:- 有控制面地址 → 以云端基座为主,建连后再拉「这一台设备」的差异配置
- 默认可提交的结构 vs 密钥/现场覆盖分开,密钥不进默认模板
- 留少量本地可覆盖项,方便排障或灰度换模块,但优先级写清楚
- 字段名两边必须同一套;改契约就当改接口,双边一起动
2. 会话:双路径互斥,打断必须「死干净」
语音产品里,用户感知几乎等于「我说完有没有声音、我说停停不停」。- 双路径:经典级联和端到端不要并行抢同一路音频;入口就互斥
- 打断:用轮次标记作废过期的生成/播报/字幕;只停麦不够
- 别堵事件循环:阻塞初始化、重计算丢线程池;记忆预热之类不挡首轮
- 扩展往外挂:新协议、新工具走独立处理与可替换模块,连接核心保持瘦
3. 权限与工具:能进后台 ≠ 能看全部;能说话 ≠ 能办事
多团队共用控制台时,只做登录和菜单,列表接口仍可能扫到不该看的数据。所以:- 系统层管「能不能进功能」
- 团队层管「碰得到哪些资源」;写必须带团队上下文
- 查询侧强制范围;前端团队头显式传递
坏了的时候长什么样
上线前更在意失败形态,而不是只演示 Happy Path。
更多短复盘见 经验总结。
技术栈
相关阅读
配置与控制面
双模式、按设备配置、契约与鉴权分层。
会话与打断
走读加深、双路径、轮次作废与线程边界。
经验总结
跨主题短复盘。
项目概览
项目区入口。