電話自動応答システムのQAのための自動応答システムを作った話
はじめに
こんにちは。電話AI SaaS IVRyのAIエンジニアの町田です。
IVRyは従来のプッシュ型の自動応答システムに留まらず、LLMを積極的に活用したAI音声対話システムを開発しています。2023年初頭にAI対話システムの開発を開始してから1年半、多くの企業に実際に導入され、ほぼ毎週新機能や改善のリリースを行うまでに成長しました。
しかし、実際の音声対話をベースとしたAI電話アプリケーションの開発は、従来のWebアプリケーション開発とは異なる独自の課題を抱えています。毎週の新機能や改修を安定的にリリースすることは、決して容易なタスクではありません。
本記事では、高速かつ安全なリリースを実現するためのQAの工夫についてご紹介します。
QAの重要性
プロダクト開発において、品質保証(QA)プロセスは極めて重要な役割を果たします。特に、高速なリリースサイクルを目指すソフトウェア開発において、その重要性はますます高まっています。
AI電話アプリケーションのテストにおいて、Webアプリケーションとの最大の違いは「実際に電話をかけてのテストが必要」という点です。
電話自動応答サービスにおいて、電話が繋がらないことは致命的です。
音声のクオリティや自然な会話の流れなど、実際の通話でしか検証できない要素が多数存在します。
例えば:
- 読み上げテキストの設定は正しくてもAIが読み方を間違って読み上げてしまう
- 主語を省略して発話すると認識されない
- ノイズによる認識精度に問題はないか
など、バリエーションは様々です。
また、IVRyでは担当者によるルール設定機能や、通話履歴もありますから、以下の点も確認が必要です:
- 設定されたルール通りのシナリオが実行されているか
- 通話履歴や着信通知といった電話以外の部分(主にWebアプリケーション)の動作に問題がないか
このように電話挙動とWebアプリケーションの両方を常に気にかけながら開発をする必要があります。
既存のQA手法の限界
通常のWebアプリケーションであれば自動でQAを行うツールは色々とありそうです。ですが実際に電話をかける場合はスタンダードなソリューションを見つけることはできませんでした。
まず、クラウドソーシングなどを利用することで安価にテストを実行できないか検討しましたが、以下の理由から断念しました:
- 実際に通話するために個人の電話を利用しなければならない
- QAのために細かい指示を毎回作成する必要がある(その通りやってくれる保証もない)
- 終了までの時間とコストが見込めず、気軽にテストが実行できない
次に、システムで自動化できないか考えました。アイデアとして、プッシュをベースとしたIVRの場合は、実際にDTMF信号を発信することで自動化ができそうです。(本記事では紹介しませんが、こちらの自動化ツールも作成しました)
しかし、音声認識をベースにしたシステムの場合はいくつかのハードルを乗り越えなければなりません。
- システム側の発話の状況を認識
- 実際に発話を行う
- その結果を評価する
いくつか考えたものの、なかなかこのハードルをすべてクリアできず、結局人間が頑張ることで担保していました。しかしサービス規模が大きくなるにつれていよいよ限界が来たため、本腰をいれて検討を進めました。結果、以下のやり方に行き着きました。
音声認識AIに対する架電シミュレーション機能
基本的なアイデア
SpeechToText(以下STT)を利用し、IVRSystemの発話内容を認識し、TextToSpeech(以下TTS)を利用してそのレスポンス音声を生成します。
リアルタイムに音声内容を取得し、発話の切れ目(IVRSystem側の発話終了)を見つけるため
TwilioのMediaStream機能を利用します。このとき、Stream動詞で track="inbound"
を指定しておくとIVRSystemからの発話のみを取得することができます。
処理の流れ
- テストケースの会話スクリプトを用意
- システムでIVRSystemに実際に架電
- IVRSystem側の発話を認識し、レスポンス発話が切れたかどうかを判定。
- シナリオに応じてTTS音声を再生
- IVRSystem側が発話に応じて次の発話をおこなう
- (4に戻り発話終了までループ)
- 完了後、通話履歴や関連アクションが期待通りであるかを比較
実際の自動テスト通話
実際の通話音声をお聞きください。
実際の通話音声
サンプルではTTS音声を利用しているため、若干の不自然さはありますが、実際に電話ができているのか、期待通り動いているのか、という最低限守りたいラインは自動で検証できそうです。
ちなみに、本記事ではTTSをベースに紹介していますが、実際の録音音声を流すこともできるので、そうするとよりリアリティある対話で検証が可能です。
検証システム概要
概要図
- Caller
- テスト用の架電を行う。処理としては電話をかけ、その時にTwilioの単方向ストリーム機能を利用してテストシステム用のwebsocketに向けてinbound接続を構築します。これにより、テスト対象システムからの自動応答発話を音声ストリームとして取得できます。
- DialogHandler
- 取得した音声ストリームを元に会話を制御する。音声が生データをして取得できるので、その内容を元にテストシステムの次の行動を決定・トリガーできます。
- 今回は「IVRSystemの発話が終わったら」「シナリオに沿って応答をする」ことを行います。適当な手法で終話を検知したら、シナリオの次の発話に進むようにすると、擬似的に電話を進めることができます。
- Evaluator
- 自動で実行された発話の履歴を取得し、意図通りに動いたかを評価します。
- TwilioベースであればTwilioからログが取得できますし、アプリケーション側で保存したログの内容をもとに評価することも可能です。(テストが十分できるようにログを出しておく必要があります)
- 日付認識など時間帯によって変化するものがある場合、編集距離による評価をするなどの工夫も必要です。
※図中のcall.create
などはtwilioの処理をイメージしています。
テストシナリオ例
"scenarios": [
{
"title": "Test Case 1",
"push_actions": [
{
"send_digit": "2"
},
{
"send_digit": "1"
}
],
"speech_actions": [
{
"say": "アイブリーの町田です。"
},
{
"say": "田中さん"
},
{
"say": "ゼロ、ハチ、ゼロ、イチ、ニ、サン、ヨン、ゴ、ロク、シチ、ハチ、です"
},
{
"say": "はい"
},
{
"say": "通話のテストをしています"
}
],
"expect": [
{
"action": "notification",
"notification_text": "以下の内容でお問い合わせを承りました。\n\nお客様の会社名とお名前: アイブリーの町田\n担当者: 田中さん\n電話番号: 08012345678\nお問い合わせ内容: 通話のテストをしています。\n※音声認識結果は不正確な可能性があります"
},
{
"action": "complete"
}
]
},
擬似コード
擬似コードではありますが、以下のような内容で実現できます。
class Caller:
function initiate_call():
twiml = create_twiml_with_stream()
Twilio.calls.create(twiml, to, from)
function create_twiml_with_stream():
twiml = new VoiceResponse()
twiml.append(Pause(length=5))
start = Start()
stream = Stream(
name="TestStream",
url="wss://your-websocket-url.com/ws",
track="inbound"
)
stream.parameter(name="TestCaseId", value=scenario_id)
start.append(stream)
twiml.append(start)
twiml.append(Pause(length=60))
twiml.append(Hangup())
return twiml
class DialogHandler:
initialize WebSocket connection
function process_dialog():
receive media stream from Twilio via WebSocket
payload = process_audio(media_stream)
if is_IVRsystem_speaking:
manage_IVRsystem_turn(payload)
else:
manage_system_turn(payload)
function manage_IVRsystem_turn(payload):
if detect_end_turn(payload):
end_IVRsystem_turn()
start_system_turn()
else:
reset IVRsystem_silence_duration
function manage_system_turn(payload):
if not system_speaking:
start_system_speaking()
elif detect_IVRsystem_speech(payload):
if detect_end_turn(payload):
end_system_turn()
start_IVRsystem_turn()
class Evaluator:
function evaluate_results():
retrieve call logs from DB
compare actual responses with expected responses
calculate performance metrics
generate evaluation report
導入効果
この自動テスト戦略の導入により、観点にして100以上見るために30回程度の架電が必要だったところが自動化できました。人力で1時間割いていた作業が自動化できたことは非常に大きいです。
実際に架電するQAは、変更を入れた箇所に特に限定することができるようになったので効率も良くなり、品質担保だけでなく、リリース頻度の向上にも繋がりました。
今後の取り組み
現在の手法は事前に作成したシナリオと決められた文言によるQAが中心です。
今後はシナリオベースではなく、AIにEnd-to-Endで対話させて品質を評価したり、読み上げのイントネーションの評価や、ノイズ化での対話状況の再現など、自動化できるところをどんどん増やしていこうと考えています。
まとめ
AI対話プロダクトが数多く発表されていますが、プロダクトとして提供するには品質担保が重要です。IVRyでは実際に架電をするところまで自動化し、効率的な開発と品質担保に取り組んでいます。
とはいえ、実際に発話するという評価は電話アプリケーションではどうしても必要です。
自前の電話QAの仕組みを作っているところ、まだ少ないのではないでしょうか。
ご興味ある方、是非IVRyで新しいQAを開拓してみてください!!
Discussion