Amazon Connect Customerで通話中にオペレーターが任意のタイミングで通話録音を一時停止・再開できるガイドを作ってみた
はじめに
Amazon Connect Customerでは、コンタクトフローの設定で通話録音のON/OFFを制御できますが、これは通話全体に対する設定であり、通話の途中で一時的に録音だけを止める、という制御はできません。
一方で、クレジットカード番号のようなセンシティブな情報をオペレーターが口頭で聴き取る必要がある場合、その区間だけ録音を止めたいケースがあります。今回は以下の要件を前提にします。
- オペレーターは1名のまま、同じ音声通話を継続する
- センシティブな情報はオペレーターが口頭で聴き取る(DTMFによるセキュア入力は使わない)
- 別オペレーターへのキュー転送は行わない
- 対象の情報を聴取している区間だけ通話録音を停止し、聴取完了後に同じ通話のまま録音を再開する
標準のフローブロックだけではこの制御はできず、SuspendContactRecording / ResumeContactRecording APIを呼び出す必要があります。
今回は、これらのAPIをAWS Lambdaから呼び出すStep-by-stepガイドを作成し、通話中にオペレーターがボタン操作だけで録音の停止・再開を行える構成を試しました。
前提知識
Show viewブロックとStep-by-stepガイド
Amazon Connect Customerには、エージェントワークスペース上にカスタムUIを表示する「Views(ビュー)」という仕組みがあります。フロー内の「ビューを表示(Show view)」ブロックでビューを指定すると、そのビューがエージェントワークスペースに表示され、ボタン操作などの結果がフローの分岐(Action)として扱われます。
SuspendContactRecording / ResumeContactRecording API
SuspendContactRecordingは、フロー設定で有効になっている録音(通話・IVR・画面)を一時停止するAPIです。ContactRecordingTypeにAGENT/IVR/SCREENのいずれかを指定すると対象を絞り込めますが、省略した場合はフロー設定で有効になっている対象すべてが停止・再開の対象になります。停止していた区間は、最終的な録音では無音として扱われます。
前提条件
今回は以下を前提とします。
- Amazon Connect Customerインスタンスを作成済み
- 通話録音、Contact Lensのリアルタイム分析をフローで有効化済み
- 画面録画は使用していない(画面録画を使う場合の注意点は後述します)
Viewの作成
録音中用ビューには、TextコンポーネントとButtonコンポーネントを1つずつ配置します。変更したのはButtonの表示名(ラベル)とActionのみで、それ以外の設定はデフォルトのままです。
- Text: 「ステータス:録音中」
- Button: ラベル「録音停止」、Action
SuspendRecording

録音中用ビューのキャンバス

「録音停止」ボタンのプロパティ。ActionにSuspendRecordingを指定している
停止中用ビューも同様に、TextとButtonを1つずつ配置し、ラベルとActionのみを変更します。
- Text: 「ステータス: 録音停止中」
- Button: ラベル「録音再開」、Action
ResumeRecording

停止中用ビューのキャンバス
ボタンのActionプロパティに指定した値(SuspendRecording / ResumeRecording)が、後述するShow viewブロックの分岐条件(Condition)と一致している必要があります。ボタンのラベル文言はエージェントワークスペースの表示にのみ影響し、フローの分岐には影響しません。
Lambdaの作成
Lambda関数は以下の内容で作成しました。
import boto3
connect_client = boto3.client('connect')
def lambda_handler(event, context):
contact_data = event['Details']['ContactData']
parameters = event['Details'].get('Parameters', {})
# ビュー表示フローは別コンタクト(CHAT)として動作するため RelatedContactId を使用する
instance_id = contact_data['InstanceARN'].split('/')[1]
related_contact_id = contact_data['RelatedContactId']
action = parameters.get('Action')
common_args = {
'InstanceId': instance_id,
'ContactId': related_contact_id,
'InitialContactId': related_contact_id,
'ContactRecordingType': 'AGENT', # 画面録画は未使用のためAGENT明示
}
if action == 'Suspend':
connect_client.suspend_contact_recording(**common_args)
elif action == 'Resume':
connect_client.resume_contact_recording(**common_args)
else:
raise ValueError(f'Unsupported Action: {action}')
return {'statusCode': '200', 'result': action}
処理のポイントは以下です。
「ビューを表示」ブロックを含むガイド用フローは、エージェントとお客様の通話とは別のコンタクトとして実行されます。そのため、Lambda関数に渡されるイベントのContactData.ContactIdをそのまま使うと、対象の音声コンタクトの録音を制御できません。ContactData.RelatedContactIdをContactIdおよびInitialContactIdに指定する必要があります。
今回の検証環境でも、実際に以下の2つのコンタクトが生成されていました。
ガイド側はチャネルがCHATのコンタクトとして生成され、RelatedContactIdに音声コンタクトのIDを保持されるようです。
- 音声通話
- チャネル: VOICE
- 開始方法: INBOUND
- RelatedContactId: なし
- ビューを表示するフロー
- チャネル: CHAT
- 開始方法: API
- RelatedContactId: 音声通話のコンタクトID
そのため、コード上はeventから取得できるContactIdではなくRelatedContactIdを使っています。
また、どちらのアクションかは、Invoke Lambda関数ブロックの「関数入力パラメータ」で渡したActionの値で判定しています。戻り値のstatusCodeは文字列にしています。Invoke Lambda関数ブロック側でResponseValidationにSTRING_MAPを指定しているため、数値など文字列以外の値を返すとレスポンス検証エラーになり、意図した分岐に進めません。
Lambda実行ロールには、AWSLambdaBasicExecutionRoleに加え、以下のインラインポリシーを付与します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"connect:SuspendContactRecording",
"connect:ResumeContactRecording"
],
"Resource": "arn:aws:connect:ap-northeast-1:111111111111:instance/<connect-instance-id>/contact/*"
}
]
}
フローの作成
今回作成したのは以下の2本のフローです。
受電フロー(ベース)
受電フローは、着信した通話に対して録音・分析設定を行った上でキューに転送し、オペレーターに接続するだけのシンプルな構成です。

受電フロー全体。通話録音・分析設定の直後に「イベントフローの設定」ブロックを追加している
このフローの録音・分析設定ブロックの直後に「イベントフローの設定」ブロックを追加しています。このブロックは、コンタクトとのやり取り中に実行するフローを指定するためのもので、今回は「エージェントUIのデフォルトフロー」というイベントに対して後述のガイド用フローを指定しています。
これにより、通話がオペレーターにつながると、エージェントワークスペースにガイドが自動で表示されます。オペレーターは通話を続けながら、ガイド上のボタンで録音を一時停止・再開できます。

「イベントフローの設定」ブロックのプロパティ。EventHooksのDefaultAgentUIにガイド用フローを指定している
ガイド用フロー
ガイド用フローは、録音中用ビューと停止中用ビューの2つを、Lambda呼び出しブロックを挟んでループさせる構成にしています。

ガイド用フロー全体。録音中用ビューと停止中用ビューを、Lambda呼び出しを挟んで相互に遷移させている
- 録音中用ビュー表示中に「録音停止」ボタンが押されたら、Lambdaで
SuspendContactRecordingを呼び出し、停止中用ビューへ遷移する - 停止中用ビュー表示中に「録音再開」ボタンが押されたら、Lambdaで
ResumeContactRecordingを呼び出し、録音中用ビューへ戻る
状態(録音中/停止中)ごとにビューを分けているため、通話中は常に「現在の状態+次に押せる1つのボタン」だけが表示されます。

「ビューを表示」ブロックで録音中用ビューを選択した状態。左側のブロックには、ボタンのActionに対応するSuspendRecordingの分岐が自動表示されている。

AWS Lambda関数を呼び出すブロックのプロパティ。呼び出す関数と、関数入力パラメータでActionの値を渡している
動作確認
実際にテストしてみると、録音停止中に発言した内容については、Contact Lensのリアルタイム文字起こしが生成されないことを確認できました。

文字起こしが録音停止区間で途切れている。あわせて、録音の波形も停止していた区間は白く表示され、その部分だけ再生されない
Contact Lensのリアルタイム分析セグメントは、以下のコマンドで確認できます(voiceデータの保持期間は24時間のため、その間に実行する必要があります)。
aws connect-contact-lens list-realtime-contact-analysis-segments --instance-id <connect-instance-id> --contact-id <contact-id> --region ap-northeast-1 --query 'Segments[].Transcript.{Role:ParticipantRole,Text:Content,BeginMs:BeginOffsetMillis}' --output table
BeginOffsetMillisを確認すると、録音停止操作の前後で20秒以上のセグメントの空白が生じており、その区間だけTranscriptセグメントが生成されていないことが分かりました。録音再開操作の後は、再びセグメントが生成されています。発言内容の詳細はここでは割愛します。
補足
画面録画を使う場合の注意
今回はContactRecordingTypeにAGENTを明示しているため、画面録画が有効な環境であっても画面録画は継続します。SuspendContactRecordingはAPIドキュメント上、ContactRecordingTypeを省略した場合はフロー設定で有効になっている録音対象(通話・IVR・画面)をまとめて停止するとされています。画面録画も含めて停止・再開したい場合は、ContactRecordingTypeのキー自体を渡さない実装にする必要があります。
文字起こし・ログへの影響について
Contact Lensの文字起こしは録音停止に連動して止まるため、追加設定は不要でした。
CTRやコンタクト属性、CloudWatch Logsもフロー・APIを通じて渡した値のみを保持する仕組みのため、対象の情報を渡さない限り記録されません。
まとめ
通話とオペレーターを変えずに、必要な区間だけ録音を一時停止・再開できました。ガイドのボタンからLambda経由でAPIを呼び出し、録音停止中はContact Lensの文字起こしも止まりました。







