🔰

電話で自動応答するプログラムを書く

2024/09/18に公開

こんにちは。KDDI ウェブコミュニケーションズ(KWC)の小原です。

IVR とは

IVR(Interactive Voice Response)とは、電話で入力された数字や音声を認識し、自動で応答するサービスです。

たとえば次のようなものです。

  1. 電話で『7 を押すか「おみくじ」といってください』と案内し、数字か音声の入力を待つ
  2. 「7」もしくは「おみくじ」を受け取ったら、「大吉」などをランダムに返す。「7」や「おみくじ」以外なら 1. に戻す

この IVR であれば、Vonage(ボネージ)を利用することで JSON と簡単なプログラムで実現できます。

Vonage とは

Vonage はエリクソン傘下の CPaaS で、電話、SMS、ビデオなどを REST API や JSON で利用できるサービスを提供しています。弊社(KWC)は再販と日本語サポートを提供しています。

この記事では Vonage を利用して「おみくじ IVR」を作ります。Vonage アカウントと電話番号の取得がまだの場合は下記手順が必要です。

※050/0120/0800 の取得には本人確認(KYC)が必要です。そのため取得に時間がかかります。アメリカの電話番号であれば KYC が不要なため、すぐに IVR を利用可能です。ただし通話料金が国際料金になるためご注意ください。

IVR の流れ

ユーザが Vonage で取得した 050 に電話をかけると、Vonage は案内と入力待ちをします。ユーザが入力したらおみくじの結果を返します。

そのためには電話番号とプログラムの紐づけが必要になります。

NCCO はテキストの読み上げや、数字・音声を取得する Vonage 特有の JSON です。NCCO はのちほど紹介します。

サーバの用意

プログラムの保存

おみくじ IVR の全ソースコードは下記です。app.rb として保存してください。

# frozen_string_literal: true

require 'sinatra'

IVR_URL = 'https://your-sub-domain.ngrok-free.app/ivr'

# 1.電話を着信
get '/answer' do
  content_type :json
  [
    # 案内
    {
      action: 'talk',
      text: '7 を押すか「おみくじ」といってください',
      language: 'ja-JP',
      style: rand(6),
      bargeIn: true
    },
    # 数字(dtmf)もしくは音声(speech)を入力待ちし、入力値を eventUrl に渡す
    {
      action: 'input',
      type: %w[dtmf speech],
      eventUrl: [IVR_URL],
      dtmf: {
        maxDigits: 1
      },
      speech: {
        language: 'ja-JP',
        context: %w[おみくじ くじ くじびき]
      }
    }
  ].to_json
end

# 2. 電話に入力された値を Vonage から受け取る
post '/ivr' do
  params = JSON.parse(request.body.read)
  # 7 か「くじ」なら、おみくじを返す
  if params['dtmf']['digits'] == '7' ||
     params['speech']['results']&.any? { |result| result['text'].include?('くじ') }
    content_type :json
    [
      {
        action: 'talk',
        text: %w[大吉 吉 中吉 小吉 末吉 凶 大凶].sample,
        language: 'ja-JP',
        style: rand(6)
      }
    ].to_json
  # それ以外なら 1. に戻す
  else
    redirect '/answer'
  end
end

# 電話の着信時や終了時に呼ばれる
post '/event' do
  ''
end

今回は Ruby の Sinatra を使用しますが、Vonage は NCCO (JSON) のやり取りができれば問題ないため、ほかの言語でも実装可能です(Vonage 公式の各種言語のサンプルはこちらです)。

Ruby のインストールはバージョン管理のできる mise がおすすめです。

ライブラリのインストール

必要なライブラリをインストールします。

bundle init
bundle add sinatra
bundle add rackup
bundle add rerun

サーバの起動

app.rb を起動します。

$ bundle exec rerun ruby app.rb
[2024-08-26 18:31:50] INFO  WEBrick 1.8.1
[2024-08-26 18:31:50] INFO  ruby 3.3.4 (2024-07-09) [x86_64-linux]
== Sinatra (v4.0.0) has taken the stage on 4567 for development with backup from WEBrick

サーバは Vonage からアクセスできれば AWS や Cloudflare など、どこでも構いません。今回はお手軽な ngrok を利用します。

$ ngrok http 4567
Account         kwcplus (Plan: Free)
Version         3.14.1
Region          Japan (jp)
Latency         4ms
Web Interface   http://127.0.0.1:4040
Forwarding      https://your-sub-domain.ngrok-free.app -> http://localhost:4567

ngrok の出力にある Forwarding の https://your-sub-domain.ngrok-free.app はのちほど Vonage のダッシュボードにも設定するためメモしておいてください。

まずは app.rb の 5 行目にある IVR_URL を Forwarding の URL に変更します。rerun を利用しているため、ファイルが更新されると自動でサーバが再起動します。

IVR_URL = 'https://your-sub-domain.ngrok-free.app/ivr'

プログラムや JSON はのちほど説明します。

アプリケーションの作成

プログラムと Vonage を紐づけるために、Vonage のダッシュボードにログインし、アプリケーションを作成します。

アプリケーション

  1. 画面左のメニューから「アプリケーション」を選択
  2. 「新しいアプリケーションを作成する」ボタンを押す

アプリケーションとプログラムの紐づけ

アプリケーション設定

「機能」にある「音声」のチェックボックスを ON にします。

名前と回答 URL、イベント URL を設定し、最後に「新しいアプリケーションの生成」を押します。

項目 設定
名前 おみくじ IVR(任意)
回答 URL HTTP GET にして https://your-sub-domain.ngrok-free.app/answer
イベント URL HTTP POST にして https://your-sub-domain.ngrok-free.app/event

「回答 URL」は電話を着信すると呼び出される Webhook です。

「イベント URL」は今回利用しませんが、必須項目になります。イベント URL には電話の着信時や終了時のイベントが届きます。のちほど電話をした際に ngrok の Web Interface で確認してみてください。

アプリケーションと電話番号の紐づけ

アプリケーションと電話番号の紐づけ

取得した電話番号の「リンク」ボタンを押します。アプリケーションに紐づくと「リンク解除」に変わります。

これにより、電話を着信すると、アプリケーションに設定された回答 URL が呼ばれます。

これで IVR の完成です。取得した番号に電話してみてください。

「7」を押すか、「おみくじ」と言うことで、おみくじの結果が返ってきましたか?

参考資料:Getting Started with Voice API | Vonage API Documentation

NCCO

テキストの読み上げや、IVR の入力に JSON を書きました。この JSON を Vonage は NCCO (Nexcmo Call Control Object) と呼びます。

NCCO は着信した電話の振る舞いを定義します。

talk

talk は text の内容を読み上げます。

   {
    "action": "talk",
    "text": "7 を押すか「おみくじ」といってください",
    "language": "ja-JP",
    "style": 0,
    "bargeIn": true
  }
オプション 概要
text テキスト読み上げ内容を指定。SSML も無料で利用可能
bargeIn true だとテキスト読み上げ中も IVR を受付可能
language 言語の種類。日本語であれば ja-JP
premium true を指定すると読み上げが流暢になります。デフォルト falsefalse は無料ですが、true の場合は 100 文字あたり 0.4 円
style 声色や性別。日本語であれば 05 を指定可能。language や premium 対応有無、音声サンプルはこちら

参考資料:Talk

input

input は IVR の入力待ちをし、入力されたら eventUrl に入力された値を送ります。

  {
    "action": "input",
    "eventUrl": [
      "https://you-sub-domain.ngrok-free.app/ivr"
    ],
    "type": [
      "dtmf",
      "speech"
    ],
    "dtmf": {
      "maxDigits": 1
    },
    "speech": {
      "language": "ja-JP",
      "context": [
        "おみくじ",
        "くじ",
        "くじびき"
      ]
    }
  }
オプション 概要
type 数字だけ(["dtmf"])、音声だけ(["speech"])、両方(["dtmf", "speech"])を指定可能
eventUrl 入力された内容を送る URL

参考資料:Input

dtmf オプション

電話で数字入力する方式を DTMF といいます。DTMF 専用のオプション。

オプション 概要
timeOut 入力を待つ秒数。デフォルト 3。最大 10
maxDigits 入力文字数。指定した文字数を入力すると eventUrl を呼ぶ。デフォルト 4。最大 20
submitOnHash true を指定すると # を押すと eventUrl を呼ぶ。デフォルトは false

speech オプション

音声認識専用のオプション。

オプション 概要
endOnSilence 話をしたあとに、指定した秒数の無音で eventUrl を呼ぶ。デフォルト 2.00.410.0 を指定可能
language 認識する言語。日本語であれば ja-JP
context 配列で音声認識の精度を上げるヒントを指定
startTime ユーザが話し始めるまで待つ秒数。デフォルト 10160 を指定可能
maxDuration 音声認識する秒数。デフォルトは 60160 を指定可能。15 秒あたり 2.5 円
saveAudio true を指定すると録音。録音データのある URL は eventUrl に recording_url として送信。無料ですが 30 日後に自動削除。デフォルトは false

eventUrl に送信されるサンプル

eventUrl には、Vonage から JSON で送られます。

発信元(from)が渡されるため、CRM と連携をすれば、誰が電話をしたか判別可能です。

dtmf サンプル

入力された数字は文字列として渡されます。

{
    "speech": {
        "results": []
    },
    "dtmf": {
        "digits": "7",
        "timed_out": false
    },
    "from": "8180xxxxyyyy",
    "to": "8150xxxxyyyy",
    "uuid": "xxxx6ed87e66bd731aba84cca626xxxx",
    "conversation_uuid": "CON-a6077a9f-xxxx-xxxx-xxxx-02f43861d28f",
    "timestamp": "2024-08-22T14:51:38.554Z"
}

参考資料:DTMF Capturing Results

speech サンプル

音声認識結果は複数渡されます。このサンプルでは 2 件ですが、8 件くらい渡される場合もあります。

confidence の値が大きいほど確度が高いとされます。

{
    "speech": {
        "timeout_reason": "end_on_silence_timeout",
        "results": [
            {
                "confidence": "0.9158283",
                "text": "くじ"
            },
            {
                "confidence": "0.9121674",
                "text": "おみくじ"
            }
        ]
    },
    "dtmf": {
        "digits": null,
        "timed_out": false
    },
    "from": "8180xxxxyyyy",
    "to": "8150xxxxyyyy",
    "uuid": "xxxx4246e5dd8199616dc8e136a5xxxx",
    "conversation_uuid": "CON-d46c6c3c-xxxx-xxxx-xxxx-d49dbd1fa4bf",
    "timestamp": "2024-08-22T14:53:59.677Z"
}
項目 概要
timeout_reason end_on_silence_timeoutmax_durationstart_timeout
error エラーがあった場合はエラーメッセージが入ります

参考資料:Speech Recognition Results

未入力サンプル

何も入力せず、発声もしない場合は、speech は []、dtmf は "" が渡されます。

{
    "speech": {
        "timeout_reason": "start_timeout",
        "results": []
    },
    "dtmf": {
        "digits": "",
        "timed_out": true
    },
    "from": "8180xxxxyyyy",
    "to": "8150xxxxyyyy",
    "uuid": "xxxx4246e5dd8199616dc8e136a5xxxx",
    "conversation_uuid": "CON-d46c6c3c-xxxx-xxxx-xxxx-d49dbd1fa4bf",
    "timestamp": "2024-08-22T14:53:55.288Z"
}

まとめ

  • Vonage は、電話や SMS といったコミュニケーションをプログラムできる CPaaS です
  • NCCO と呼ばれる JSON で、電話がかかってきたときの処理を設定できます。たとえば、「7 を押してください」と案内したり、入力された言葉を認識したりと、さまざまな挙動を制御できます
  • Web サーバーには、ユーザーが入力した数字や言葉が送られ、それに応じた結果(おみくじの結果など)を返すことで IVR を実現します

参考資料

KWCPLUS

Discussion