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

如何通过Twilio Voice实现通话录音转写及正确外呼?

解决方案:单通外呼+录音转写+避免双重计费

一、录音转写的正确配置

不要在client.calls.create里单独设置record:true,全程录音转写需要通过<Dial>标签的参数实现,而非单独的<Record>标签(后者仅用于录制通话片段)。

直接在创建通话时传入包含转写配置的TwiML,示例如下:

<Response>
  <Dial record="record-from-answer" transcribe="true" transcribeCallback="/api/transcribe-result">
    <Number>{客户号码}</Number>
  </Dial>
</Response>
  • record="record-from-answer":从通话接通开始全程录音
  • transcribe="true":启用自动转写
  • transcribeCallback:指定接收转写结果的API端点(需公开可访问)

二、消除双重通话/计费问题

你遇到的两次通话是因为现有逻辑让Twilio先呼叫你的应用端,再由应用端触发呼叫客户,形成两条链路。解决方法是让Twilio直接从你的号码外呼客户:

NextJS API端点示例代码:

import twilio from 'twilio';

export default async function handler(req, res) {
  const client = twilio(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN);
  
  const call = await client.calls.create({
    from: process.env.TWILIO_PHONE_NUMBER, // 你的Twilio号码
    to: req.body.customerNumber, // 客户号码
    twiml: `<Response><Dial record="record-from-answer" transcribe="true" transcribeCallback="/api/transcribe-result"><Number>${req.body.customerNumber}</Number></Dial></Response>`
  });
  
  res.status(200).json({ callSid: call.sid });
}

此逻辑仅生成一次从Twilio号码到客户的通话,避免双重计费。

三、前端直连外呼(@twilio/voice-sdk)实现

1. 后端生成AccessToken

创建/api/get-token端点,生成包含外呼权限的Token:

import twilio from 'twilio';
import { AccessToken } from 'twilio/lib/jwt/AccessToken';
import { VoiceGrant } from 'twilio/lib/jwt/AccessToken';

export default function handler(req, res) {
  const accountSid = process.env.TWILIO_ACCOUNT_SID;
  const authToken = process.env.TWILIO_AUTH_TOKEN;
  const appSid = process.env.TWILIO_TWIML_APP_SID; // 关联你的TwiML App
  const identity = 'user-' + Date.now(); // 唯一用户标识

  const voiceGrant = new VoiceGrant({
    outgoingApplicationSid: appSid,
    incomingAllow: false // 关闭呼入,仅允许外呼
  });

  const token = new AccessToken(accountSid, process.env.TWILIO_API_KEY, process.env.TWILIO_API_SECRET);
  token.addGrant(voiceGrant);
  token.identity = identity;

  res.status(200).json({ token: token.toJwt() });
}

2. 前端初始化Device并发起呼叫

import { Device } from '@twilio/voice-sdk';

let device;

// 初始化设备
const initDevice = async () => {
  const res = await fetch('/api/get-token');
  const { token } = await res.json();
  
  device = new Device(token, {
    audioConstraints: { audio: true }
  });
  
  device.on('ready', () => console.log('设备就绪'));
  device.on('error', (err) => console.error('设备错误:', err));
};

// 发起外呼按钮点击事件
const makeOutboundCall = (customerNumber) => {
  if (!device) return;
  
  const call = device.connect({
    params: {
      To: customerNumber
    }
  });
  
  call.on('connect', () => console.log('通话接通'));
  call.on('disconnect', () => console.log('通话结束'));
};

// 页面加载时初始化设备
initDevice();

3. TwiML App的语音端点配置

创建/api/voice端点,根据前端传入的To参数返回Dial指令:

export default function handler(req, res) {
  const to = req.query.To;
  const twiml = `
    <Response>
      <Dial record="record-from-answer" transcribe="true" transcribeCallback="/api/transcribe-result">
        <Number>${to}</Number>
      </Dial>
    </Response>
  `;
  res.setHeader('Content-Type', 'text/xml');
  res.status(200).send(twiml);
}

关键注意事项

  • 确保Twilio账号已开启语音转写功能(控制台>语音>设置)
  • transcribeCallback端点需公开可访问,开发环境可使用ngrok做隧道转发
  • 不要同时在client.calls.create和TwiML中设置录音参数,避免重复录音

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 20:34:57