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

如何同步Twilio通话流程与recordingStatusCallback的录音处理?

解决Twilio通话流程中录音处理与通话同步的问题

问题概述

需要实现的通话流程:

  • 发起通话
  • 分析录制的用户输入
  • 根据用户输入生成播放提示语
  • 向用户播放响应内容

当前遇到的问题:使用recordingStatusCallback处理录音时,通话流程会在录音下载完成前继续执行,导致无法将录音处理结果用于后续通话环节。需要调整流程,确保通话等待录音处理完成后再继续。

现有代码

from fastapi import FastAPI, Request, Response
from twilio.twiml.voice_response import VoiceResponse
from twilio.rest import Client
import urllib.parse
from urllib.parse import urljoin
import time
import logging

logger = logging.getLogger(__name__)

# 假设的全局会话存储和配置
session_store = {}
TWILIO_ACCOUNT_SID = "your_account_sid"
TWILIO_AUTH_CODE = "your_auth_code"
TWILIO_PHONE_NUMBER = "your_twilio_number"

def GetTextReplyToAudioQuery(recording_url):
    # 模拟录音处理逻辑
    return "根据你的输入生成的响应"

@app.get("/make_call")
async def make_call(request: Request, to_number: str, txt_to_play: str):
    try:
        base_url = f"{request.url.scheme}://{request.url.netloc}"
        relative_url = "/start_call"
        encoded_txt = urllib.parse.quote(txt_to_play)
        relative_url += "?txt_to_play=" + encoded_txt
        complete_url = urljoin(base_url, relative_url)
        logger.info(complete_url)

        logger.info(f"Account SID : {TWILIO_ACCOUNT_SID}")
        logger.info(f"Auth Code : {TWILIO_AUTH_CODE}")
        client = Client(TWILIO_ACCOUNT_SID, TWILIO_AUTH_CODE)

        if 'Next_Prompt_To_Play' in session_store:
            del session_store['Next_Prompt_To_Play']

        call = client.calls.create(
            to=to_number,
            from_=TWILIO_PHONE_NUMBER,
            url=complete_url
        )
        return {"message": f"Call initiated to {to_number}", "call_sid": call.sid}
    except Exception as e:
        return {"error": f"error in call initiation : {e}"}

@app.post("/start_call")
async def start_call(request: Request):
    try:
        form = await request.form()
        txt_to_play = request.query_params.get('txt_to_play')

        response = VoiceResponse()
        response.say(txt_to_play)
        response.record(
            max_length=30,
            action="/handle_call",
            recordingStatusCallback="/handle_recording",
            recordingStatusCallbackEvent="completed",
            method="POST"
        )
        return Response(content=str(response), media_type="application/xml")
    except Exception as e:
        return {"error": f"error in starting the call: {e}"}

event_occurred = False

@app.post("/handle_recording")
async def handle_recording(request: Request):
    try:
        form = await request.form()
        recording_url = form['RecordingUrl']
        recording_duration = form['RecordingDuration']
        print(f"Recording URL: {recording_url}")
        print(f"Recording Duration: {recording_duration} seconds")

        query_response = GetTextReplyToAudioQuery(recording_url)
        session_store['Next_Prompt_To_Play'] = [query_response]

        global event_occurred
        if 'Next_Prompt_To_Play' in session_store and session_store['Next_Prompt_To_Play']:
            event_occurred = True
            print("Event fired")

        return Response(content=str(VoiceResponse()), media_type="application/xml")
    except Exception as e:
        return {"error": f"error in call recording: {e}"}

@app.post("/handle_call")
async def handle_callflow(request: Request):
    try:
        response = VoiceResponse()
        response.redirect("/handle_call_further")
        return Response(content=str(response), media_type="application/xml")
    except Exception as e:
        return {"error": f"error in call recording: {e}"}

@app.post("/handle_call_further")
async def handle_call_further_function(request: Request):
    try:
        txt_to_play = "Lets try this."
        global event_occurred
        wait_cnt = 5
        cnt = 0
        while cnt < wait_cnt:
            if event_occurred:
                if 'Next_Prompt_To_Play' in session_store and session_store['Next_Prompt_To_Play']:
                    txt_to_play = session_store['Next_Prompt_To_Play'][0]
                event_occurred = False
                print("Handled the event")
                break
            cnt += 1
            print(f"Waiting for the text : {cnt}")
            time.sleep(1)

        response = VoiceResponse()
        response.say(txt_to_play)
        return Response(content=str(response), media_type="application/xml")
    except Exception as e:
        return {"error": f"error in call recording: {e}"}

解决方案

核心思路是移除异步的recordingStatusCallback,将录音处理逻辑直接放到<Record>组件的action端点中。Twilio在用户停止录音后会立即调用action指定的接口,此时录音URL已经可用,无需异步等待,从而保证流程顺序同步。

修改后的关键代码

  1. 调整/start_call端点,移除recordingStatusCallback相关配置,将action指向新的处理端点:
@app.post("/start_call")
async def start_call(request: Request):
    try:
        form = await request.form()
        txt_to_play = request.query_params.get('txt_to_play')

        response = VoiceResponse()
        response.say(txt_to_play)
        # 直接将录音完成后的处理逻辑放到action端点
        response.record(
            max_length=30,
            action="/handle_recording_and_respond",  # 修改为新的处理端点
            method="POST"
        )
        return Response(content=str(response), media_type="application/xml")
    except Exception as e:
        return {"error": f"error in starting the call: {e}"}
  1. 新增/handle_recording_and_respond端点,在该端点中完成录音处理并生成响应:
@app.post("/handle_recording_and_respond")
async def handle_recording_and_respond(request: Request):
    try:
        form = await request.form()
        recording_url = form['RecordingUrl']
        recording_duration = form['RecordingDuration']
        
        # 处理录音,生成响应内容
        query_response = GetTextReplyToAudioQuery(recording_url)
        
        # 直接向用户播放生成的响应
        response = VoiceResponse()
        response.say(query_response)
        return Response(content=str(response), media_type="application/xml")
    except Exception as e:
        return {"error": f"error in processing recording and responding: {e}"}
  1. 删除原有的/handle_recording、/handle_call、/handle_call_further端点及全局变量event_occurred,简化流程。

流程说明

修改后的执行顺序完全符合需求:

  1. make_call:发起通话
  2. start_call:播放初始提示语并开始录音
  3. 用户停止录音后,Twilio立即调用/handle_recording_and_respond
  4. 在该端点中处理录音、生成响应提示语
  5. 向用户播放响应内容

这种方案避免了异步回调的顺序问题,同时去掉了不可靠的全局变量轮询,提升了代码的可靠性和性能。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 05:15:02