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

使用Twilio Gather动词在同一通话中获取多语音回复问题排查

问题原因分析

你的问题出在Twilio <Gather> 的工作机制上——当用户完成语音输入后,Twilio会把请求发送到你指定的action地址,默认情况下,这个请求处理完后通话就会结束,不会继续执行后续的Twiml指令。你现在是一次性把三个问题的<Gather>都塞进初始Twiml里,但第一个<Gather>触发后就跳去webhook,后续的指令根本没机会执行,所以通话直接结束,只收到一条回复。

解决方案

要实现多问题的连续调查,你需要通过webhook的状态流转来逐步返回每个问题的Twiml,而不是一次性生成所有内容。具体做法如下:

1. 修改初始呼叫的Twiml生成逻辑

初始发起呼叫时,只播放第一个问题的音频和提示,并在<Gather>的action参数里带上当前问题的索引(用来标记进度):

const makeCalls = async () => {
  let callCount = 0;

  for (const phoneNumber of phoneNumberList) {
    if (callCount >= 40) {
      await new Promise(resolve => setTimeout(resolve, 60000));
      callCount = 0;
    }

    const response = new twilio.twiml.VoiceResponse();
    // 只处理第一个问题
    const firstAudio = audioFiles[0];
    response.play(firstAudio.url);
    response.say(firstAudio.question, { voice: 'alice', language: 'es-MX' });
    // action带上问题索引,方便webhook判断进度
    response.gather({
      input: 'speech',
      language: 'es-MX',
      action: `${webhookEndpoint}?questionIndex=0`,
      speechtimeout: 1,
      method: 'POST',
    });

    const twiml = response.toString();

    await client.calls.create({
      twiml: twiml,
      from: fromPhoneNumber,
      to: `+52${phoneNumber}`,
      record: true,
      transcribe: true,
      language: 'es-MX',
    })
      .then(call => {
        console.log('Call initiated:', call.sid);
        callCount++;
      })
      .catch(error => {
        console.error('Failed to make the call:', error);
      });
  }
};

2. 编写Webhook的处理逻辑

webhook需要接收用户的转录内容(通过SpeechResult参数),保存当前回复,然后根据questionIndex判断是否还有下一个问题:

// 假设用Express实现webhook,示例代码如下
app.post('/webhook', (req, res) => {
  const questionIndex = parseInt(req.query.questionIndex);
  const speechResult = req.body.SpeechResult;
  const callSid = req.body.CallSid;

  // 这里将用户回复保存到数据库/文件,关联通话ID和问题索引
  console.log(`Call ${callSid} - Q${questionIndex+1}: ${speechResult}`);

  const response = new twilio.twiml.VoiceResponse();
  const nextIndex = questionIndex + 1;

  if (nextIndex < audioFiles.length) {
    // 还有下一个问题,生成对应Twiml
    const nextAudio = audioFiles[nextIndex];
    response.play(nextAudio.url);
    response.say(nextAudio.question, { voice: 'alice', language: 'es-MX' });
    // action带上下一个问题的索引
    response.gather({
      input: 'speech',
      language: 'es-MX',
      action: `${webhookEndpoint}?questionIndex=${nextIndex}`,
      speechtimeout: 1,
      method: 'POST',
    });
  } else {
    // 所有问题完成,结束通话
    response.say('感谢你的回答,通话即将结束。', { voice: 'alice', language: 'es-MX' });
    response.hangup();
  }

  res.type('text/xml');
  res.send(response.toString());
});

关键要点

  • 每次<Gather>的action必须带上当前问题索引,让webhook明确处理进度
  • webhook处理完当前回复后,必须返回新的Twiml才能继续通话,否则Twilio会默认结束流程
  • 需自行实现回复存储逻辑,关联通话ID与问题索引,确保每个回复对应正确的问题

内容的提问来源于stack exchange,提问作者Art N Werk Studio

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 10:52:47