You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

AWS Polly(boto3)闲置超时无音频返回:Twilio媒体流集成问题

问题:Twilio VoiceBot闲置2小时后AWS Polly无响应,呼叫卡住

基于Twilio Media Streams构建VoiceBot,流程为Speech-to-Text(STT)→LLM→Text-to-Speech(TTS),采用AWS Polly作为TTS组件,将AWS类声明为Twilio服务器启动时的全局变量。初始运行正常,但闲置约2小时后,新呼入进入语音合成步骤时会卡住——Polly无音频返回,呼叫挂起等待响应。已尝试设置Config(tcp_keepalive=True)但无效果,相关代码如下:

import os
import boto3
from botocore.config import Config
import audioop
import logging

logger = logging.getLogger(__name__)

class AWS:
    def __init__(self) -> None:
        self.sample_rate = "8000"  # Ensure sample rate is a string
        self.session = boto3.Session(
            aws_access_key_id=os.environ['AWS_ACCESS_KEY_ID'],
            aws_secret_access_key=os.environ['AWS_SECRET_ACCESS_KEY'],
            region_name=os.environ['AWS_DEFAULT_REGION']
        )
        self.polly: boto3.client = self.session.client("polly",config=Config(tcp_keepalive=True))

    def aws_polly_synthesize_speech(self, text: str, voice_name: str):
        try:
            response = self.polly.synthesize_speech(
                OutputFormat="pcm",
                SampleRate=self.sample_rate,
                Text=text,
                VoiceId=voice_name,
                Engine='neural',
                TextType='text'
            )

            if "AudioStream" in response:
                audio_stream = response['AudioStream']
                raw_pcm_data = audio_stream.read()
                mulaw_audio = audioop.lin2ulaw(raw_pcm_data, 2)
                return mulaw_audio  # Return raw PCM data directly

            logger.error("No AudioStream in the response from AWS Polly")
        except Exception as e:
            logger.error(f"Error during AWS Polly request: {e}")

原因分析

  • boto3的客户端采用长连接复用机制,全局实例闲置2小时后,底层TCP连接可能被AWS负载均衡或中间网络设备主动断开,但客户端未检测到连接失效,后续请求会卡在尝试使用无效连接的状态。
  • tcp_keepalive=True仅开启TCP保活,但AWS服务端或中间防火墙/代理可能设置了更短的闲置超时(比如默认1小时),保活包无法阻止连接被切断。
  • 全局Polly客户端没有连接失效后的自动重建机制,一旦连接中断,后续请求会持续挂起等待无效响应。

解决方法

方法1:添加客户端异常捕获与重建逻辑

修改AWS类,在请求失败时捕获连接相关异常,重建Polly客户端并重试请求:

class AWS:
    def __init__(self) -> None:
        self.sample_rate = "8000"
        self.session = boto3.Session(
            aws_access_key_id=os.environ['AWS_ACCESS_KEY_ID'],
            aws_secret_access_key=os.environ['AWS_SECRET_ACCESS_KEY'],
            region_name=os.environ['AWS_DEFAULT_REGION']
        )
        self._init_polly_client()

    def _init_polly_client(self):
        self.polly: boto3.client = self.session.client(
            "polly",
            config=Config(
                tcp_keepalive=True,
                # 缩短超时时间,避免长时间挂起
                connect_timeout=10,
                read_timeout=15
            )
        )

    def aws_polly_synthesize_speech(self, text: str, voice_name: str):
        try:
            response = self.polly.synthesize_speech(
                OutputFormat="pcm",
                SampleRate=self.sample_rate,
                Text=text,
                VoiceId=voice_name,
                Engine='neural',
                TextType='text'
            )

            if "AudioStream" in response:
                audio_stream = response['AudioStream']
                raw_pcm_data = audio_stream.read()
                mulaw_audio = audioop.lin2ulaw(raw_pcm_data, 2)
                return mulaw_audio

            logger.error("No AudioStream in the response from AWS Polly")
        except (botocore.exceptions.ClientError, ConnectionError) as e:
            # 捕获连接类异常,重建客户端后重试
            logger.error(f"Polly request failed, rebuilding client: {e}")
            self._init_polly_client()
            # 重试一次请求
            try:
                response = self.polly.synthesize_speech(
                    OutputFormat="pcm",
                    SampleRate=self.sample_rate,
                    Text=text,
                    VoiceId=voice_name,
                    Engine='neural',
                    TextType='text'
                )
                if "AudioStream" in response:
                    audio_stream = response['AudioStream']
                    raw_pcm_data = audio_stream.read()
                    mulaw_audio = audioop.lin2ulaw(raw_pcm_data, 2)
                    return mulaw_audio
                logger.error("No AudioStream in retry response from AWS Polly")
            except Exception as retry_e:
                logger.error(f"Polly request failed after retry: {retry_e}")
        except Exception as e:
            logger.error(f"Unexpected error during Polly request: {e}")

方法2:每次请求创建临时Polly客户端

如果并发量不高,可放弃全局客户端,每次调用合成接口时创建新的Polly客户端,彻底避免长连接闲置失效问题:

class AWS:
    def __init__(self) -> None:
        self.sample_rate = "8000"
        self.session = boto3.Session(
            aws_access_key_id=os.environ['AWS_ACCESS_KEY_ID'],
            aws_secret_access_key=os.environ['AWS_SECRET_ACCESS_KEY'],
            region_name=os.environ['AWS_DEFAULT_REGION']
        )

    def aws_polly_synthesize_speech(self, text: str, voice_name: str):
        # 每次请求创建新客户端
        polly = self.session.client(
            "polly",
            config=Config(tcp_keepalive=True)
        )
        try:
            response = polly.synthesize_speech(
                OutputFormat="pcm",
                SampleRate=self.sample_rate,
                Text=text,
                VoiceId=voice_name,
                Engine='neural',
                TextType='text'
            )

            if "AudioStream" in response:
                audio_stream = response['AudioStream']
                raw_pcm_data = audio_stream.read()
                mulaw_audio = audioop.lin2ulaw(raw_pcm_data, 2)
                return mulaw_audio

            logger.error("No AudioStream in the response from AWS Polly")
        except Exception as e:
            logger.error(f"Error during AWS Polly request: {e}")

方法3:配置连接池超时参数

通过调整boto3连接池设置,让客户端自动回收闲置过久的连接:

def _init_polly_client(self):
    self.polly: boto3.client = self.session.client(
        "polly",
        config=Config(
            tcp_keepalive=True,
            connect_timeout=10,
            read_timeout=15,
            # 设置连接池最大闲置时间(30分钟,小于AWS默认超时)
            pool_connections=10,
            pool_maxsize=10,
            pool_timeout=1800
        )
    )

内容的提问来源于stack exchange,提问作者J7er

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 16:12:25