1. はじめに
音声AIプラットフォーム(Retell AIなど)を使って「店舗の予約受付AI電話」を構築する場合、一般的には Function Calling(関数呼び出し) を使用して、通話中にLLMが直接予約システムのAPI(Google Calendarなど)を叩き、空き状況をリアルタイムで照会・確保する構成を設計します。
しかし、初期検証(PoC)やプロトタイプ開発の段階において、Function Callingの実装やリアルタイムAPIの同期処理は、以下のような点で開発コストが高くなります。
- APIのレスポンス遅延により、通話中に数秒間の無言状態(レイテンシー)が発生しやすい。
- 認証エラーや例外処理のハンドリングが複雑になり、AIが通話中にフリーズするリスクがある。
本記事では、**「通話中のFunction Callingを一切使わず、通話完了後のWebフック処理によって、最速かつ極めて安定した予約登録システムを実装するアプローチ」**を紹介します。
2. アーキテクチャの比較
パターンA:通話中の Function Calling (リアルタイム同期)
LLMが会話の中で必要な情報(日時、名前)を聞き取った瞬間、APIを呼び出してカレンダーに登録します。
【ユーザー】 ➔ 【音声AI (LLM)】 ➔ (通話中APIリクエスト) ➔ 【Google Calendar】
パターンB:通話終了後の Webフック処理 (非同期登録)
通話中はAIが必要な項目(日時・人数・名前)をヒアリングすることだけに集中し、通話が切断された直後にWebフックでカレンダーへ自動登録します。今回はこちらを採用します。
【ユーザー】 ➔ 【音声AI (LLM)】 (ヒアリングに専念し、通話終了)
↓ Webフック (会話書き起こしテキストを送信)
【中継サーバー (Webhook handler)】 ➔ 【Google Calendar】
3. 実装のステップ
ステップ1:プロンプトで「ヒアリング情報の構造化出力」を指示する
LLM(音声エージェント)のシステムプロンプトに対し、会話の最後に聞き取った情報を復唱確認したうえで、通話を終えるように設計します。
# 役割
あなたはサロンの予約受付AIです。
# ヒアリング対象項目
- 予約日時(例: 7月13日の14時)
- 人数(例: 2名)
- お名前(例: 山田太郎)
# 会話フロー
1. お客様から「予約日時」「人数」「お名前」を聞き取ってください。
2. すべて聞き取れたら、必ず「〇月〇日の〇時から、〇名様、山田太郎様でご予約を承ります。こちらで間違いございませんか?」と確認してください。
3. お客様が「はい」と答えたら、「ありがとうございます。それではご予約を確定いたします。ご来店をお待ちしております。失礼いたします。」と発話して、通話を切断(Hangup)してください。
ステップ2:Retell AI で Webhook (Post-Call Webhook) を設定する
Retell AIの管理画面において、通話終了後にイベントを受け取るWebフックURL(例: AWS Lambda、Cloudflare Workers、または自社FastAPIサーバーのURL)を設定します。
通話が終了すると、Retell AIから以下のようなJSONデータが自動的に送信されます。
{
"event": "call_analyzed",
"call": {
"call_id": "call_1234567890",
"transcript": "AI: お電話ありがとうございます... 顧客: 明日の14時から予約したいです... AI: 明日7月13日14時からですね...",
"user_sentiment": "Positive"
},
"analysis": {
"summary": "7月13日14時から1名で山田太郎様の予約受付完了。",
"custom_variables": {
"booking_date": "2026-07-13T14:00:00+09:00",
"number_of_people": 1,
"guest_name": "山田太郎"
}
}
}
※Retell AIには、会話ログからパラメータを自動で抽出する「AI定性抽出(Custom Variables)」機能があるため、複雑な正規表現でのテキストパースなしで正確に値を取得できます。
ステップ3:Webhookレシーバーでの自動カレンダー登録 (Node.js例)
送られてきたデータをもとに、サーバーサイドでGoogle Calendar APIやCRMシステムに非同期で登録する簡易的なコード例です。
const express = require('express');
const { google } = require('googleapis');
const app = express();
app.use(express.json());
// Google Calendar クライアントの初期化
const oauth2Client = new google.auth.OAuth2(
process.env.CLIENT_ID,
process.env.CLIENT_SECRET,
process.env.REDIRECT_URL
);
oauth2Client.setCredentials({ refresh_token: process.env.REFRESH_TOKEN });
const calendar = google.calendar({ version: 'v3', auth: oauth2Client });
app.post('/webhook/retell', async (req, res) => {
const { event, analysis } = req.body;
if (event === 'call_analyzed') {
const { booking_date, guest_name, number_of_people } = analysis.custom_variables;
if (booking_date && guest_name) {
try {
// Google Calendar に予定を追加
await calendar.events.insert({
calendarId: 'primary',
requestBody: {
summary: `【AI予約】${guest_name} 様 (${number_of_people}名)`,
description: `AI電話受付による自動登録\n要約: ${analysis.summary}`,
start: { dateTime: booking_date },
end: { dateTime: new Date(new Date(booking_date).getTime() + 60 * 60 * 1000).toISOString() }, // 1時間枠
},
});
console.log(`Successfully registered booking for ${guest_name}`);
} catch (err) {
console.error('Failed to insert calendar event:', err);
}
}
}
res.status(200).send('OK');
});
app.listen(3000, () => console.log('Webhook server is running on port 3000'));
4. このアプローチのメリットと注意点
メリット
- 通話遅延(レイテンシー)がゼロ: リアルタイムで外部APIとやり取りしないため、会話のテンポが非常にスムーズになります。
- 堅牢性が高い: カレンダー登録処理に万が一失敗しても、通話自体はすでに成功しており、ログが残っているため、管理者が後から手動でリカバリー可能です(通話中にAIがフリーズして切断される最悪の事態を防げます)。
- 実装が極めてシンプル: 複雑なOAuth2処理やAPIハンドラーを通話エンジン(音声AI)に組み込む必要がなくなります。
注意点
- ダブルブッキングの防止: この構成は「空き枠をその場で確認・ブロックする」処理を行いません。そのため、「予約可能枠が非常に潤沢にある店舗」や「営業時間外のヒアリングのみを目的とする」場合、または「AIが仮予約として登録し、店舗のスタッフが承認メールを送る」という運用モデルに最適です。
5. まとめ
Function Callingは強力ですが、デモ開発や初期検証のスピードを最大化し、システム全体の耐障害性を高めたい場合は、**「通話はヒアリングと復唱確認に専念させ、登録処理は切断後のWebhook経由でバックグラウンドで行う」**設計が非常に有効です。
6. 技術的なサポート・構築代行について
「予約システムやスプレッドシートへの自動Webhook連携部分を作ってほしい」
「Function Callingを使った高度なリアルタイム空き状況確認システムを本格構築したい」
といった企業・開発者様向けに、接続・構築の専門代行サービスを提供しています。お持ちの回線が接続可能かの「無料診断」も実施しておりますので、詳細やご相談は下記ページからお気軽にお問い合わせください。
👉 [SIPBridge 日本番号対応AI電話構築代行] (https://www.sipbridge.dev/agency)