高度なIVR/音声ボットの構築方法

このガイドでは、Vonage Voice APIとOpenAIを使って音声ベースのAIエージェントを構築する方法を説明します。このガイドでは 音声ボット 自動音声認識(ASR)を使ってユーザーの質問を聞き、LLMによって生成されたインテリジェントな回答で応答する。

前提条件

始める前に、あなたが持っていることを確認してください:

ローカル環境のセットアップ

プロジェクト用に新しいディレクトリを作成し、必要な依存関係をインストールします:

mkdir vonage-voice-bot cd vonage-voice-bot npm init -y npm install express openai

ローカルサーバーの公開

Vonage needs to send webhooks to your local machine. Use ngrok to expose your server:

ngrok http 3000

Note: Keep this terminal open and copy your ngrok URL. You'll need it in the next steps.

Vonageリソースのプロビジョニング

にログインする。 Vonageダッシュボード を開始する。

音声アプリケーションの作成

  1. に移動する。 Applications > 新規アプリケーションの作成.
  2. 名前をつける(例:Voice AI Bot)。
  3. クリック 公開鍵と秘密鍵の生成.を保存する。 private.key この基本的なASRフローでは使用しませんが、アプリの作成には必要です)。
  4. アンダー 能力有効にする .
  5. の中で 回答URL フィールドに ngrok Base URL を入力し、その後に /webhooks/answer (例 https://{random-id}.ngrok.app/webhooks/answer).メソッドを
    GET
    に設定します。
  6. の中で イベントURL フィールドに ngrok Base URL を入力し、その後に /webhooks/events (例 https://{random-id}.ngrok.app/webhooks/events).メソッドを
    POST
    に設定します。
  7. クリック 新規アプリケーションの作成 一番下にある。

Numbersをリンクする

  1. こちらへ 電話番号 > ナンバーズを買う をクリックし、音声対応番号を購入する。
  2. こちらへ Applicationsをクリックし、ボットアプリケーションを選択します。 編集.
  3. の下にある。 Numbers タブをクリックします。 リンク 新しく購入したNumbersの横。

音声ボットを作る

という名前のファイルを作成する。 index.js そして以下のコードを追加する。置き換える YOUR_OPENAI_API_KEY を実際のキーで入力します。

注:ngrokでローカルに実行する場合、 req.protocol/req.get('host') は、あなたの公開トンネルURLと一致しないかもしれません。ウェブフックに失敗した場合は、configでトンネルのベースURLを設定し(例えばenv var)、ビルドしてください。 eventUrl その代わりだ。

const express = require('express');
const { OpenAI } = require('openai');

const app = express();
app.use(express.json());

const openai = new OpenAI({ apiKey: 'YOUR_OPENAI_API_KEY' });

// 1. Handle the initial call
app.get('/webhooks/answer', (req, res) => {
  const ncco = [
    {
      action: 'talk',
      text: 'Hi, I am your AI assistant. How can I help you today?'
    },
    {
      action: 'input',
      eventUrl: [`${req.protocol}://${req.get('host')}/webhooks/asr`],
      type: ['speech'],
      speech: {
        language: 'en-us',
        endOnSilence: 1
      }
    }
  ];
  res.json(ncco);
});

// 2. Process the Speech-to-Text result and query OpenAI
app.post('/webhooks/asr', async (req, res) => {
  const speechResults = req.body.speech?.results;

  if (!speechResults || speechResults.length === 0) {
    return res.json([{ action: 'talk', text: 'I am sorry, I didn\'t catch that. Goodbye.' }]);
  }

  const userText = speechResults[0].text;
  console.log(`User said: ${userText}`);

  try {
    // Request a completion from OpenAI
    const completion = await openai.chat.completions.create({
      model: "gpt-4o",
      messages: [
        { role: "system", content: "You are a helpful assistant on a phone call. Keep answers concise." },
        { role: "user", content: userText }
      ],
    });

    const aiResponse = completion.choices[0].message.content;

    // Respond back to the user
    res.json([{ action: 'talk', text: aiResponse }]);
    
  } catch (error) {
    console.error("OpenAI Error:", error);
    res.json([{ action: 'talk', text: 'I encountered an error processing your request.' }]);
  }
});

// 3. Log call events
app.post('/webhooks/events', (req, res) => {
  console.log('Event:', req.body.status);
  res.sendStatus(200);
});

app.listen(3000, () => console.log('Server running on port 3000'));

アプリケーションのテスト

  1. サーバーを起動してください:

    node index.js
  2. お使いの電話からVonage番号にダイヤルしてください。

  3. プロンプトが表示されたら、質問をする(例. なぜ空は青いのか? または ジョークを言ってくれ).

  4. ボットはあなたの発話をキャプチャし、それをOpenAIに送信し、以下の方法で応答を読み返します。 音声合成.

文脈に沿った会話を可能にする

会話を自然なものにするためには、アプリが以前のやりとりを記憶し、ユーザーに再度入力を促すように修正しなければならない。

注:ngrokでローカルに実行する場合、 req.get('host') はあなたのパブリック・トンネル・ホストと一致しないかもしれない。ウェブフックに失敗した場合は eventUrl リクエストホストの代わりに、パブリックトンネルのベースURL(例えばconfig/env)を使用します。

更新 index.js このステートフルなロジックで:

// 1. Add a Map to store conversation history by Call UUID
const sessions = new Map();

// Helper to generate a NCCO that "loops" back to ASR
const getConversationalNCCO = (text, host) => [
  { action: 'talk', text: text },
  {
    action: 'input',
    eventUrl: [`https://${host}/webhooks/asr`],
    type: ['speech'],
    speech: { language: 'en-us', endOnSilence: 1 }
  }
];

app.get('/webhooks/answer', (req, res) => {
  const uuid = req.query.uuid;
  // Initialize history for this specific caller
  sessions.set(uuid, [{ role: "system", content: "You are a helpful, concise assistant." }]);
  
  res.json(getConversationalNCCO('Hello! What is on your mind?', req.get('host')));
});

app.post('/webhooks/asr', async (req, res) => {
  const { uuid, speech } = req.body;
  const userText = speech?.results?.[0]?.text;

  if (!userText) {
    sessions.delete(uuid);
    return res.json([{ action: 'talk', text: 'Goodbye!' }]);
  }

  // Retrieve history and append the new question
  let history = sessions.get(uuid) || [];
  history.push({ role: "user", content: userText });

  const completion = await openai.chat.completions.create({
    model: "gpt-4o",
    messages: history,
  });

  const aiResponse = completion.choices[0].message.content;
  history.push({ role: "assistant", content: aiResponse });
  sessions.set(uuid, history);

  // Return the AI response AND listen for the next question
  res.json(getConversationalNCCO(aiResponse, req.get('host')));
});

// Clean up memory when the call ends
app.post('/webhooks/events', (req, res) => {
  if (req.body.status === 'completed') sessions.delete(req.body.uuid);
  res.sendStatus(200);
});

何が変わったのか

  • セッション・マップ私たちは uuid 異なる発信者の履歴を別々に管理するため。
  • 再帰的NCCO:単純な talk アクションを返します。 talk 続いて input アクション。これで回線は開いたままだ。
  • メモリー:メモリ全体を渡すことで history OpenAIの配列では、ボットは次のようなフォローアップの質問を理解するようになりました。 それについて詳しく教えてください。

サーバーを再起動し、アプリケーションにリンクされているVonage番号にダイヤルして、更新されたアプリケーションをお試しください。 アプリケーションのテスト ステップ

人とつながる」ツールを追加

このステップでは、ツール定義を更新し、ASRロジックにVonageの connect アクションだ。

更新 index.js

新しいツール定義を追加し asr ウェブフックで転送を処理する:

// Define the transfer tool
const tools = [
  {
    type: "function",
    function: {
      name: "connect_to_human",
      description: "Call this when the user wants to speak to a real person or a human agent.",
      parameters: { type: "object", properties: {} } // No arguments needed
    }
  }
];

const HUMAN_AGENT_NUMBER = '15551234567'; // Replace with your phone number

app.post('/webhooks/asr', async (req, res) => {
  const { uuid, speech } = req.body;
  const userText = speech?.results?.[0]?.text;

  if (!userText) return res.json([{ action: 'talk', text: 'Goodbye.' }]);

  let history = sessions.get(uuid) || [];
  history.push({ role: "user", content: userText });

  const response = await openai.chat.completions.create({
    model: "gpt-4o",
    messages: history,
    tools: tools // Provide the tool to the LLM
  });

  const message = response.choices[0].message;

  // Check if the AI wants to transfer the call
  if (message.tool_calls && message.tool_calls[0].function.name === 'connect_to_human') {
    console.log(`Transferring call ${uuid} to human agent...`);
    
    // Clean up session since the AI is leaving the call
    sessions.delete(uuid);

    // Return the "connect" NCCO
    return res.json([
      { 
        action: 'talk', 
        text: 'Please hold while I connect you to a human representative.' 
      },
      {
        action: 'connect',
        from: 'YOUR_VONAGE_NUMBER', // Your linked Vonage number
        endpoint: [{ type: 'phone', number: HUMAN_AGENT_NUMBER }]
      }
    ]);
  }

  // Regular conversational flow
  const aiResponse = message.content;
  history.push({ role: "assistant", content: aiResponse });
  sessions.set(uuid, history);

  res.json(getConversationalNCCO(aiResponse, req.get('host')));
});

仕組み

  • 意図:ユーザーが マネージャーと話したい または 助けてください。 LLMは、その意図を認識し、それを発動する。 connect_to_human 関数である。
  • ハンドオフ:サーバーはASRのループを停止し connect アクションをVonageへ。
  • 接続:Vonageは新しいアウトバウンドレッグを作成します。 HUMAN_AGENT_NUMBER そして2つの通話をブリッジする。接続が行われると、AIはもはや "リッスン "していない。

サーバーを再起動し、アプリケーションのVonage番号に電話をかけます。ボットに人間と話すように頼むと、次のように言うはずです。 担当者におつなぎしますので、少々お待ちください。に設定した電話番号に接続します。 HUMAN_AGENT_NUMBER.

次のステップ

  • カスタムボイス: 音声名の変更 での talk アクションで、よりブランディングされた体験を提供する。
  • WebSocketストリーミング:低レイテンシーを実現するには ウェブソケット オーディオをリアルタイムでストリーミングする。
  • エンドポイントPBXまたはコンタクトセンターへの接続 SIP経由 を使用して、人間のエージェントのための独自のウェブインターフェースを構築することもできます。 Client SDK.
  • .NETバージョン:.NETで実装された同じIVR/ボイスボットのシナリオをご覧ください。 ブログ記事.