最近用 Gemini 3.1 Live 做了一个 iPhone 实时语音聊天 App。 一开始我以为难点是模型。 后来才发现,真正危险的不是 Gemini 不够聪明,而是: 一个 AI 长对话 App 里,几乎每条链路都在制造“无限流”。 麦克风不断进音频、WebSocket 不断吐事件、模型不断返回音频 chunk、转写文本不断 append、数据库不断写、SwiftUI 不断刷新、播放队列不断 schedule、历史消息不断变长、 单独看,每一件都不复杂。 放在一起,就是一个非常典型的移动端事故现场: 你以为自己在做 AI 应用,其实在管理一组随时可能失控的实时系统。
我这次踩到的第一个坑是 不能让任何队列无限增长。
最开始,音频和事件都按到达速度堆积。 服务端一突发,播放器队列就被填满。 事件流如果 UI 或上层逻辑没及时消费,也会在内存里继续长。 所以第一步不是优化体验,而是先保命: 所有关键队列都必须有上限。 ChatSession 的事件流要有 bounded buffer。 音频发送队列要有上限、warmup 队列也要有上限, 超过上限时,宁可丢掉一部分,也不能让 App 被无限流撑爆。 这听起来不优雅,但能稳定使用的 APP 永远比每个包都不能丢的洁癖更重要。
第二个坑是 APP 很卡
实时 Live 音频接口的返回数据不是均匀的。 它可能一阵子吐很多 chunk,然后又短暂停一下。 但你播放的速度不是这样工作的,你需要的是让用户觉得平稳、音频质量极高的对话聊天。 如果服务端每来一个 chunk,就直接 schedule 到 AVAudioPlayerNode,就很容易出现一种怪现象: 前面堆太多,后面突然没了。听起来就是语音断断续续,像被切碎了一样。 所以后来加了一层业务播放缓冲。 模型音频先进入 PCM16RingBuffer。 内存缓冲不够时,再进入 file spool。 播放器不再被服务端 chunk 节奏牵着走,而是按自己的目标按顺序播放。 这一步的代价是首包到播放会多几十到几百毫秒,但结果会好很多: 播放稳定了, 内存边界清楚了, 突发流量也可以很好处理了。
第三个坑是:音频播放和转写入库不能走同一条等待链。
实时语音里,音频播放是热路径。 转写、数据库、UI 更新都是慢路径。 如果一个音频 chunk 要等转写处理、消息合并、数据库写入之后才能播放,那这个 App 必然卡。 所以现在的处理方式是: 模型音频一到,直接交给 AudioPlayback。 转写文本则走另一条路径,先进入 pending buffer,再按窗口批量写库。 turn complete、中断、停止时再强制 flush。 这不是为了代码好看,而是为了尊重现实: 音频是硬实时,转写落库只是准实时,它们不能共用一个阻塞点。
第四个坑是:长对话不只会拖垮音频,也会拖垮 UI 和字符串
很多时候 AI App 时会盯着模型和网络,但真正让 App 越聊越重的,往往是这些小东西: 一条 transcription 不断 append,字符串反复分配。 SwiftUI 列表每个 token 都触发 diff。 每次更新都自动滚到底。 消息数组越来越长,主线程越来越抖。 这些问题单次看都不严重。 但长对话里,它们会叠加成一种慢性死亡。 所以我后来给文本和 UI 都加了安全阀。 转写有最大长度、pending buffer 有上限、输出转写太长就切成新的 message。 MessageListView 的滚动判断只看尾部消息 ID、状态、完成状态和长度,不把完整 display text 放进去参与判断。 连续 append 时,也不反复强制滚动。 这一步的本质是: 用户可以看到连续对话,但内部对象不能无限连续。 展示可以平铺直叙下去。但数据结构必须是一段一段的。
第五个坑,实时会话和长期记忆必须彻底分层。
现在很多 AI App 容易犯一个错误: 把聊天历史、实时连接、音频播放、转写缓存、长期记忆、UI 数据源,都绑在同一个会话对象上。 短对话没问题。 长对话一定越来越重。 更好的架构应该是两层: 实时层只保留最近几轮、当前播放缓冲、未完成转写和连接状态。 长期层异步接收完成后的 turn,做摘要、索引、归档和检索。 重连时,也不应该恢复完整历史,而是恢复少量 recent turns 加一段可验证摘要。 UI 也不应该直接绑定全量消息数组,而应该用分页窗口加增量投影。 这样,App 才不会因为用户越聊越久,就把自己越拖越沉。
第一次做 APP 最大的感受是: AI 语音 App 的难点,调用模型并不复杂,复杂的是这是一个移动端 APP 的实时系统工程。 模型只是其中一个节点。 真正决定产品能不能长期使用的,是这些更底层的东西: 队列有没有边界。 突发能不能被平滑。 热路径和慢路径有没有分开。 UI 有没有安全阀。 历史数据有没有冷热分层。 很多 Demo 看起来很酷,是因为它只跑 3 分钟。 真正的产品要面对的是: 用户连续聊 30 分钟。 网络抖动。 模型突发输出。 音频不能断。 UI 不能卡。 内存不能涨。 历史还要能恢复。
所以我现在越来越相信一件事: AI 产品化最容易被低估的,不是模型能力。 而是把模型输出接进真实设备、真实网络、真实用户行为之后,那一整套“防失控工程”。 接 API 很简单,难的是让它一直活着。
讨论
登录后可评论、点赞、引用回复。