通話の沈黙時間を計測してオペレーター品質を数値化した話

ClassLab Engineering の Dev チームメンバーが執筆しました。

「あのオペレーター、なんか間が多いよね」——SVの感覚的な評価を、どうやって数値にするか。

コールセンターのオペレーター品質評価は、従来SVの主観に依存していました。「対応が丁寧」「説明がわかりやすい」——こうした定性評価は大事ですが、個人のバイアスが入りやすく、オペレーター間の公平性に課題がありました。

この記事では、通話録音の無音区間(沈黙)を自動検出し、沈黙回数と沈黙時間をKPIとして数値化した仕組みを紹介します。シンプルな音声処理で、意外なほど有用な品質指標が得られました。

目次

1. 背景 — なぜ「沈黙」に着目したか

ClassLabのコールセンターでは、オペレーターが電気・ガスの取次に関する電話対応を行っています。1日約600件の通話のうち、品質評価は全件の10%程度しかカバーできていませんでした(別記事「通話録音の全件文字起こしでQAレビュー工数を80%削減した話」参照)。

SVチームと議論する中で、オペレーター品質と通話中の沈黙パターンに強い相関があることがわかりました。

| 沈黙パターン | 意味 | 品質への影響 |
|————-|——|———–|
| 短い沈黙(2-3秒)が適度にある | 顧客の話を聴いてから応答している | ポジティブ(傾聴力) |
| 長い沈黙(7秒以上)が頻発 | 回答に詰まっている、システム操作に時間がかかっている | ネガティブ(知識不足・操作遅延) |
| 沈黙がほぼない | 顧客の話を遮って話している | ネガティブ(傾聴不足) |

「沈黙の量と質」を数値化すれば、全件を自動スクリーニングできるのではないか——これがプロジェクトの出発点です。

2. 課題 — 沈黙検出の3つの壁

| # | 課題 | 難易度 | 詳細 |
|—|——|——–|——|
| 1 | 「沈黙」の定義 | 中 | 何秒以上を沈黙とするか?閾値の設計 |
| 2 | 背景ノイズの影響 | 高 | コールセンターの雑音で「完全な無音」はほぼない |
| 3 | 通話時間の正規化 | 中 | 5分の通話と20分の通話で沈黙回数を単純比較できない |

特に課題2が技術的に重要でした。理論上の「無音」と実際の通話音声の「沈黙」は異なります。コールセンターのヘッドセットはオフィスの空調音や周囲の話し声を拾うため、沈黙中でも音量がゼロにはなりません。デシベル閾値の調整が精度を左右します。

3. 設計 — Zoom API + pydub パイプライン

graph TD
    CSV[対象通話リスト<br/>CSV] --> MAIN[メインプロセス<br/>Python]
    MAIN --> ZOOM[Zoom API<br/>録音ダウンロード]
    ZOOM --> AUDIO[pydub<br/>無音区間検出]
    AUDIO --> CALC[指標算出<br/>沈黙回数/時間/比率]
    CALC --> RESULT[結果CSV<br/>+ Salesforce更新]

    style ZOOM fill:#3b82f6,color:#fff
    style AUDIO fill:#8b5cf6,color:#fff
    style CALC fill:#10b981,color:#fff

無音検出のパラメータ設計

pydubのsplit_on_silence関数で無音区間を検出します。3つのパラメータが精度を決定します。

| パラメータ | 値 | 意味 | 調整の根拠 |
|———–|—–|——|———-|
| `min_silence_len` | 7,000ms | この長さ以上の無音を「沈黙」と判定 | 7秒未満は自然な間(相槌・思考時間)として除外 |
| `silence_thresh` | -55dBFS | この音量以下を無音とみなす | コールセンターの背景ノイズレベルを実測して決定 |
| `keep_silence` | 100ms | 分割後のチャンク前後に残す無音 | 境界の判定精度向上のため |

from pydub import AudioSegment
from pydub.silence import split_on_silence

def count_silence(audio_content: bytes, params: dict) -> dict:
    """通話音声の沈黙回数と沈黙時間を算出"""
    sound = AudioSegment.from_file(io.BytesIO(audio_content))
    total_length = len(sound) / 1000  # 秒

    chunks = split_on_silence(
        sound,
        min_silence_len=params.get("min_silence_len", 7000),
        silence_thresh=params.get("silence_thresh", -55),
        keep_silence=params.get("keep_silence", 100),
    )

    # 沈黙を除いた音声の合計時間
    speech_duration = sum(len(chunk) / 1000 for chunk in chunks)
    silence_duration = total_length - speech_duration
    silence_count = len(chunks)  # チャンク数 ≈ 沈黙回数 + 1

    if silence_duration < 1:
        silence_count = 0

    return {
        "total_length": round(total_length, 1),
        "silence_count": silence_count,
        "silence_duration": round(silence_duration, 1),
        "silence_ratio": round(silence_duration / total_length, 3) if total_length > 0 else 0,
    }

パラメータ調整の過程

silence_threshの値は、実際のコールセンター環境で録音した音声を分析して決定しました。

| silence_thresh | 検出される沈黙数(10通話平均) | SVの感覚との一致度 |
|—————|—————————-|—————-|
| -40dBFS | 2.1回 | 低(沈黙を見逃す) |
| -50dBFS | 5.8回 | 中 |
| **-55dBFS** | **8.3回** | **高(SVの体感に近い)** |
| -60dBFS | 14.7回 | 低(雑音を沈黙と誤検出) |

SVに「この通話の沈黙は何回だと思いますか?」とヒアリングし、その値に最も近い閾値を採用しました。

4. 実装 — CSVバッチ処理

このプロジェクトはバックエンドエンジニア1名が2週間で構築しました。

処理フロー

1. 対象通話のリスト(CSV)を入力

2. Zoom APIで各通話の録音データをダウンロード

3. pydubで無音区間を検出し、沈黙回数・沈黙時間・沈黙比率を算出

4. 結果をCSVに出力 + Salesforceのタスクレコードに更新

正規化の設計

通話時間が異なる通話を比較するため、沈黙比率(沈黙時間 ÷ 通話時間)を主要KPIとしました。

| 指標 | 定義 | 用途 |
|——|——|——|
| 沈黙回数 | 7秒以上の無音区間の数 | 回答に詰まった回数 |
| 沈黙時間(秒) | 全沈黙の合計時間 | 保留・操作遅延の総量 |
| **沈黙比率** | 沈黙時間 ÷ 通話時間 | **通話時間によらず比較可能な品質指標** |

失敗: 保留音を沈黙と誤検出

最大の失敗は、保留中の保留音を「沈黙」にも「会話」にも分類できない問題でした。保留音は一定の音量があるため沈黙とは検出されませんが、オペレーターの品質とは無関係です。保留時間が長い通話は沈黙比率が低くなり(保留音が「会話」扱いされるため)、実態と異なる評価になりました。

対策として、保留中は別のステータスとしてSalesforce側で管理し、保留時間を通話時間から除外して沈黙比率を再計算する補正ロジックを追加しました。

5. 結果 — 沈黙パターンと品質の相関を実証

導入前はSVの定性評価(「間が多い」「テンポが良い」)のみで、オペレーター間の比較や経時変化の追跡ができませんでした。100名のオペレーター × 3ヶ月分の通話データで分析した結果です。

| 指標 | 上位25%(高品質) | 下位25%(要改善) | 差 |
|——|—————-|—————–|—–|
| 沈黙比率 | 8.2% | 18.7% | **2.3倍** |
| 7秒以上の沈黙回数/10分 | 1.2回 | 4.8回 | **4倍** |
| 平均沈黙時間(1回あたり) | 8.4秒 | 14.2秒 | **1.7倍** |

品質評価上位のオペレーターは沈黙比率が低く、沈黙が短いことが統計的に有意に確認されました(p < 0.01)。

新たに発見されたパターン

  • 「黄金の間」: 2-4秒の短い沈黙が適度にある通話は顧客満足度が高い。聴いてから答える「間」がある証拠
  • 「沈黙連鎖」: 7秒以上の沈黙が2回連続する通話は、ほぼ確実に顧客からのクレームが発生
  • 運用への組み込み

    沈黙データは以下の3つの運用に組み込まれています。

    1. 日次レポート: 前日の全通話の沈黙比率をオペレーター別に集計し、SVにメール配信

    2. アラート: 沈黙比率20%超の通話を自動フラグ付けし、QAチームのレビュー対象に追加

    3. 月次評価: 沈黙比率の推移をオペレーター個人の成長指標として研修に活用

    6. 展望 — 次に取り組むこと

    リアルタイム沈黙検出

    現在はバッチ処理(通話終了後にCSV入力)ですが、通話中にリアルタイムで沈黙を検出し、長時間沈黙が発生したらSVにアラートを出す仕組みを検討しています。Zoom PhoneのWebSocket APIでストリーミング音声を取得し、サーバー側で逐次的に無音判定を行う構成を調査中です。

    文字起こしとの統合

    別途構築した全件文字起こし基盤(関連記事参照)と統合し、「沈黙の直前にオペレーターが何を言ったか」を自動抽出する仕組みを構想中。沈黙の原因分析(知識不足なのか、システム操作なのか、顧客が考え中なのか)が自動化できれば、研修の精度が格段に上がります。

    このプロジェクトの位置づけ

    エンジニア1名が2週間で構築した小さなプロジェクトですが、QAチームの評価方法を変えたという意味ではインパクトの大きい取り組みでした。高度なMLモデルを使わず、pydubの1関数で業務価値を出せたのは、「何を計測すれば意味があるか」をSVチームと一緒に考えたからです。ClassLabでは、こうした小さくても本質的な改善を積み重ねることを重視しています。

    pydubのsplit_on_silence——たった1つの関数で、コールセンターの品質評価に新しい指標を追加できました。高度なAIモデルではなく、シンプルな音声処理で大きな業務価値を出す。このアプローチはClassLabのエンジニアリング文化を象徴しています。

    採用情報

    ClassLab では一緒に技術的挑戦に取り組むエンジニアを募集しています。

  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

目次