
AWS演示通过SageMaker AI上的Kokoro让智能体更早开始语音输出
AWS的一篇教程展示了如何把智能体已经生成完整的句子持续发送给Kokoro-82M,让语音合成在整段回答生成完毕之前开始。这个拥有八千二百万参数的模型运行在自定义容器中,通过SageMaker AI实时端点提供服务,使用的GPU实例类型为ml.g5.xlarge。文中的实现经验和性能数字均由教程作者报告。
这套方案关注的是首段语音何时能够开始产生。智能体每完成一句话,就可以把这句话送入合成环节,不必把后续尚未完成的回答一起等待。理解这一处理顺序,也有助于区分教程所说的提前启动,与容器内部是否同时合成多句话这两个问题。
同一条连接持续处理多个句子
教程区分了两种调用方式。POST /invocations针对一次完整输入流式返回输出;WS /invocations-bidirectional-stream则允许客户端通过同一条双向连接发送多条消息。因此,一段回答中的多个句子可以依次使用已有会话,而不需要每句话都重新建立连接。
容器完成一句话的音频发送后,还会发送synthesis_complete控制帧,然后继续等待下一条消息。这个控制帧表示本次合成完成,并不意味着整个会话结束。只有客户端明确发送关闭消息,容器才会关闭连接;句子的完成与连接的结束在这里有不同含义。
不过,每个处理器仍然按顺序完成合成和发送。在上一句话的音频传输期间,它不会同时生成下一句话的音频。作者所描述的延迟收益来自更早启动第一句话,以及避免逐句重新连接,不能把这种实现理解成同一处理器对相邻句子的音频合成与发送进行了重叠处理。
负责切分句子的组件既可以接收完整字符串,也可以接收陆续到达的文本片段。feed()会释放新完成的句子,同时保留末尾尚未完成的文字;当输入结束时,flush()再释放剩下的不完整句子。这样,流式输入中暂时不够组成一句话的尾部不会被立即丢弃。
作者还报告,在同一个真实会话中测试了三条句子输入,每次返回的音频块都与相应输入文字一致。这个检查针对的是连接能否持续处理多个输入,以及每次输入与输出能否对应,与后文衡量首个音频到达时间的性能测试属于不同的验证内容。
二进制消息与端点状态
教程指出,SageMaker AI的双向传输会以二进制WebSocket帧把客户端消息交给容器。如果Starlette处理器假定收到的是文本帧,并直接读取text字段,就可能在每次请求中触发KeyError。即便出现这种请求处理错误,端点仍可能保持InService状态。
因此,仅观察端点是否处于服务中,并不能覆盖教程指出的这一帧类型问题。示例处理器同时接受文本帧和二进制帧,并把二进制输入按UTF-8解码。作者也建议使用二进制帧进行测试,使验证覆盖实际传输交给容器的消息形式。
首个音频的测量范围
为了单独观察语音合成传输的时间,作者使用了一段包含四句话、总计二百六十七个字符的文字。在真实端点上,他们分别测试发送完整文字与只发送第一句话,两种方式各进行五次试验。这个对照把输入范围作为比较条件,而不是直接拿不同回答进行比较。
作者报告,发送第一句话后,首个音频字节的平均等待时间从三百二十九点三毫秒降至一百五十六点七毫秒,相对减少百分之五十二点四。这些数字对应上述固定文本和试验安排;它们说明的是该次比较中的首字节时间变化,不能替代对完整智能体链路的单独测量。
端到端示例使用由Amazon Bedrock支持的Strands智能体,并把它托管在AgentCore中。整条路径包含彼此分开的连接:面向浏览器的SSE连接、用于Bedrock文字生成的连接,以及用于Kokoro语音合成的双向连接。模型一侧采用双向传输,并不意味着浏览器一侧也使用相同协议。
在bidi模式中,生成的文字和合成后的音频通过共享事件队列被并发接收;batch模式则等待完整回答后才开始合成。这里的并发描述的是示例对文字流和音频流的消费方式,与前面所说的单个合成处理器按顺序处理消息并不矛盾,两者处在不同环节。
作者使用相同提示词、同一个真实Bedrock模型和同一端点比较这两种模式,报告本地容器中的首个音频到达速度为原来的二点四倍,部署到AgentCore后为三点五倍。复现测试的计时定义是:以agent_start为起点,记录第一个audio_chunk事件出现的时间。
两种模式面向调用方的音频都仍然采用base64编码。原因是BedrockAgentCoreApp入口返回的是可序列化为JSON的字典,随后由其SDK转换成SSE帧。因此,教程中模型连接可以避免base64的做法,并没有让这个端到端示例的所有连接都取消这种编码。
编码开销与音频分块边界
作者针对二十四段音频考察传输成本,比较了MP3与AAC,并报告使用Amazon Transcribe确认不同编码之间没有质量差异。按照他们的测量,base64会让载荷体积增加约百分之三十三。这是教程讨论原始二进制音频传输时列出的具体开销之一。
音频格式的逻辑边界也不能直接等同于网络交付边界。在一次测量的响应中,四个逻辑MP3帧被分成六个传输块到达,其中两个传输块在音频帧中间结束。接收端因此需要缓存并重新组装数据,不能把每个刚收到的传输块都当成一个完整音频单元。
模型一侧的另一种方案是在现有双向会话中复用原始二进制音频和JSON控制帧,从而在这一段链路避免base64以及SSE封装。它的判断方式是检查整个数据块能否被解码为完整JSON对象;不能完整解码为JSON对象的块则按音频处理。
这种完整检查避免仅凭第一个字节就作判断。PCM音频的数据也可能以左花括号对应的字节开头,不能因为这一开头就把整块音频误认为JSON。控制消息与音频共用连接时,教程强调的是对完整数据块的识别,而不是只检查一个看似具有文本含义的起始符号。
作者并未对所有前端给出同一种迁移建议。对于已经使用SSE的前端,他们建议继续保留通过SSE传输base64音频的方式;对于新的语音路径,或者把延迟放在优先位置的场景,则可以考虑统一传输。选择需要对应具体链路,不能把模型侧的替代方案直接视为示例前端已经采用的实现。
部署条件、清理与版本警告
运行示例需要SageMaker AI、AgentCore、IAM和ECR相关权限,以及已经完成引导初始化的AWS CDK v2。开发环境还需要Python 3.12或更新版本、uv、Docker,支持invoke_endpoint_with_bidirectional_stream的Boto3,以及对WebSocket和ASGI的了解,并使用教程引用的部署代码。
Python版本要求还有底层依赖的原因:所用HTTP/2客户端只为Python 3.12及更新版本提供预构建wheel包。此外,端点容器必须声明com.amazonaws.sagemaker.capabilities.bidirectional-streaming=true。环境准备、客户端支持和容器能力声明,都是教程列出的部署前提。
部署先执行speech-infra deploy,创建端点基础设施、IAM角色、存放模型权重缓存的S3资源、容器镜像、模型及端点配置。随后执行agent-infra deploy,创建承载Strands智能体的AgentCore运行时。两个部署步骤分别对应语音端点与智能体运行环境,清理时则按相反顺序进行。
清理命令还有资源保留行为需要留意:destroy不会删除S3缓存存储桶和ECR仓库。要停止这些保留资源产生的存储费用,仍需分别删除它们。因此,完成两个部署栈的销毁,并不等于教程涉及的缓存和镜像存储资源已经全部移除。
最后,原文明确提醒,示例容器固定使用的一版较旧PyTorch受到PYSEC-2026-2286影响,并把torch 2.10.0列为修复版本。作者要求,在超出价值验证范围使用示例之前,先检查并升级这一版本固定配置。准备复用部署代码时,这项原文警告与连接处理和性能结果同样需要纳入考虑。