ClassLab Engineering の Dev チームメンバーが執筆しました。
*この記事は、音声AI・リアルタイムAPI・Electronでの業務ツール開発に興味があるエンジニア向けです。*
1. 背景
ClassLab のコールセンターでは、新人オペレーターが電話応対スキルを身につけるまでに一定の研修期間が必要です。従来はベテラン社員がロールプレイの相手役を務めていましたが、以下の問題がありました。
- ベテラン社員が研修に拘束され、本来業務が圧迫される
- 研修のスケジュール調整が難しく、新人が十分な練習量を確保できない
- ロールプレイの評価基準が属人的で、フィードバックの質にばらつきがある
「AIが顧客役を演じ、通話終了後にスコアリングと改善点を自動提示する」システムを内製することで、これらの課題を解消しました。プロジェクト名は Convers AI です。
2. 課題
| 課題 | 影響 | 定量データ |
|——|——|———–|
| ベテラン社員の拘束 | 研修1回あたり2名体制が必要 | 月間研修工数 約40時間 |
| 練習量の不足 | 新人1人あたり週2-3回しか実施できない | 独り立ちまで平均8週間 |
| 評価のばらつき | 担当者によりフィードバックの質が異なる | 評価基準が暗黙知 |
| 日本語音声の処理 | 自動VADが日本語の「間」で誤検知 | 発話途中での誤切断が多発 |
特に技術的に難しかったのは、日本語音声のリアルタイム処理です。OpenAI Realtime APIのサーバーサイドVAD(Voice Activity Detection)は英語に最適化されており、日本語の「間」や相槌で発話終了と誤判定される問題がありました。
3. 設計
システム構成
graph TB
subgraph "Salesforce"
LWC[LWC ユーティリティバー<br/>研修項目・担当者選択]
end
subgraph "デスクトップアプリ Electron"
FE[Vue.js 3 + Pinia<br/>UI・状態管理]
BE[Express 5 + WebSocket<br/>音声処理・API連携]
FFmpeg[FFmpeg<br/>ノイズ低減・フォーマット変換]
end
BE --> FFmpeg
subgraph "外部サービス"
RT[OpenAI Realtime API<br/>音声ロールプレイ]
CHAT[OpenAI Chat API o4-mini<br/>スコアリング・分析]
SLACK[Slack API<br/>録音アップロード]
end
LWC <-->|WebSocket| BE
FE <-->|WebSocket| BE
BE <-->|WebSocket| RT
BE -->|REST| CHAT
BE -->|REST| SLACK
BE -->|REST OAuth2| LWC
技術選定
| 項目 | 選定技術 | 選定理由 |
|——|———|———|
| デスクトップ | Electron 35 | Salesforce LWCとのローカル通信(localhost WebSocket)が必要 |
| フロントエンド | Vue.js 3 + Pinia | 軽量で学習コストが低い。状態管理がシンプル |
| バックエンド | Express 5 + WebSocket (ws) | Electronメインプロセスで動作。双方向通信が容易 |
| 音声AI | OpenAI Realtime API | リアルタイム音声対話。日本語対応。低遅延 |
| 分析AI | o4-mini | 通話終了後のスコアリング。コストパフォーマンスが高い |
| 音声処理 | FFmpeg (ffmpeg-static) | ノイズ低減・フォーマット変換。パッケージに同梱可能 |
Webアプリではなく Electron デスクトップアプリ を選んだ理由は、Salesforce LWCとの連携方式にあります。Salesforce のユーティリティバー(LWC)から ws://localhost:3001 でローカルアプリと通信するため、ブラウザのセキュリティ制約を回避する必要がありました。
Push-to-Talk(PTT)モードの設計判断
本システム最大の設計判断は、OpenAIの自動VADを無効化し、Push-to-Talk方式を採用したことです。
sequenceDiagram
participant OP as オペレーター
participant APP as Electron App
participant AI as OpenAI Realtime API
OP->>APP: PTTキー押下(Shift+Space)
APP->>AI: response.cancel
Note over APP: AI応答中なら中断
APP->>AI: input_audio_buffer.clear
Note over APP: 前回の残留バッファを破棄
loop 発話中(PTT押下中)
OP->>APP: 音声入力(MediaRecorder WebM)
APP->>APP: FFmpeg WebM→PCM16変換+ノイズ低減
APP->>AI: input_audio_buffer.append
end
OP->>APP: PTTキー解放
APP->>AI: input_audio_buffer.commit
APP->>AI: response.create
AI->>APP: response.audio.delta(AI音声)
APP->>OP: スピーカー再生
自動VADを使わない理由:
- 日本語の「間」(沈黙)を発話終了と誤判定し、オペレーターの発話途中でAIが割り込む
- 相槌(「はい」「ええ」)を独立した発話と認識し、不要な応答が返る
- コールセンター研修では発話タイミングの制御自体がスキルの一部であり、意図的に制御する方が訓練効果が高い
OpenAI API設定: turn_detection: null(自動VAD完全無効化)
4. 実装
音声パイプライン
マイク入力からOpenAI APIへの送信までの音声処理パイプラインです。
// FFmpegによる入力音声の前処理
// MediaRecorderからWebM/Opus形式で受け取り、OpenAI要求のPCM16に変換
const ffmpegArgs = [
'-f', 'webm', // 入力: MediaRecorder出力(WebM/Opus 60ms chunks)
'-i', 'pipe:0', // stdin入力
'-af', [
'highpass=f=60', // 60Hz以下をカット(環境ノイズ除去)
'lowpass=f=10000', // 10kHz以上をカット
'volume=2.5', // 音量補正
].join(','),
'-ar', '24000', // OpenAI要求: 24kHz
'-ac', '1', // モノラル
'-f', 's16le', // PCM 16bit Little Endian
'-fflags', '+nobuffer', // 低遅延フラグ
'-flags', 'low_delay',
'pipe:1', // stdout出力
];
通話後の自動分析
ロールプレイ終了後、録音音声をSlackにアップロードし、o4-miniでスコアリングを実行します。
async function analyzeSession(
transcript: string,
trainingItem: TrainingItem
): Promise<AnalysisResult> {
const response = await openai.chat.completions.create({
model: 'o4-mini',
messages: [
{
role: 'system',
content: `あなたはコールセンターの品質管理担当者です。
以下の研修項目に基づいてロールプレイを評価してください。
研修項目: ${trainingItem.name}
評価基準: ${trainingItem.criteria}`,
},
{
role: 'user',
content: `以下の通話内容を評価し、JSON形式で返してください。
${transcript}`,
},
],
response_format: { type: 'json_object' },
});
return JSON.parse(
response.choices[0].message.content ?? '{}'
);
}
Salesforce連携
分析結果はSalesforce のカスタムオブジェクトに自動保存されます。LWCのユーティリティバーから研修を開始し、結果がSalesforceに戻る一気通貫のフローを実現しています。
// Salesforceへの分析結果保存(OAuth2認証済みaxiosインスタンス)
await sfClient.post('/services/data/v59.0/sobjects/TrainingResult__c', {
trainee__c: traineeId,
trainingItem__c: trainingItemId,
score__c: analysis.overallScore,
feedback__c: analysis.feedback,
recordingUrl__c: slackPermalink,
sessionDate__c: new Date().toISOString(),
});
5. 結果(数値)
| 指標 | Before | After | 改善率 |
|——|——–|——-|——–|
| 研修1回あたりの人件費 | 2名体制(ベテラン+新人) | 新人1名のみ | -50% |
| 月間研修可能回数 | 週2-3回(スケジュール制約) | 無制限(AIが常時対応) | 大幅増加 |
| 独り立ちまでの期間(※) | 約8週間 | 約5週間(練習量増加により短縮) | -37% |
| フィードバックの一貫性 | 担当者依存(ばらつき大) | AI基準で統一 | 標準化 |
| 評価項目のカバー率 | 主要3-4項目 | 全評価項目を網羅 | 全項目 |
※ 独り立ち基準: SVの同席なしで1日の受電を完了できること。
最大の効果はベテラン社員の時間解放です。月間約40時間の研修工数が実質ゼロになり、その時間を顧客対応や品質改善に充てられるようになりました。
6. 展望
次に取り組む課題
- 評価パイプラインの高度化: 複数モデルでの評価比較や、過去の高評価ロールプレイとの類似度分析。次のAI/MLエンジニアと一緒に設計したい領域です
- 研修シナリオの拡充: クレーム対応・解約抑止・アップセルなど、より高度なシチュエーション別のシナリオ設計
- 研修データ分析基盤: 蓄積されたスコア推移・つまずきパターンから研修プログラム自体を改善するフィードバックループの構築
- 通話音声をリアルタイムで感情分析する技術設計 — 音声処理パイプラインの別の活用事例
- Claude CodeとGitHub Copilotを全員が使う開発チームの実態 — AI駆動開発で本システムも構築しました
- Findyでカジュアル面談する
- 採用情報を見る
関連記事
—
採用情報
ClassLab では、AI × 業務改善を一緒に推進してくれるエンジニアを募集しています。音声AI・リアルタイムAPI・LLM活用に興味がある方、ぜひお話しさせてください。
—
ClassLab Engineering チームメンバーが執筆しました。
>
ClassLab.では、一緒にプロダクトを作るエンジニアを募集しています。
カジュアル面談も大歓迎です!
>