録画型の非同期面接(AI面接)を導入する企業が増える一方で、「本番と同じ形式で話す練習」ができる環境はまだ少ないです。自分は就活支援AIツール( https://aijpjobs.com/ja )を運営しており、この春夏で「録画して自分の回答を見直す練習機能」を実装しました。この記事は、その実装で得たWeb Speech API(SpeechRecognition)の日本語まわりの癖と、MediaRecorder との並行稼働でつまずいた点のメモです。同じように面接練習・発話分析・録画レビュー系の機能を作る人の時間を節約できれば嬉しいです。
全体構成
機能要件を整理すると次の3本立てになります。
| 要素 | 使うAPI | 役割 |
|---|---|---|
| 録画 |
getUserMedia + MediaRecorder
|
WebMで面接回答を録画し、再生レビューに使う |
| 文字起こし | SpeechRecognition |
回答をリアルタイムでテキスト化し、後から分析できるようにする |
| 制御 | タイマー + UI状態管理 | 制限時間のカウントダウン、開始/終了、再試行 |
コードの見通しとしては、「録画系」と「認識系」を完全に分けておくのが重要です。両者は独立して失敗する(マイク権限は通るが認識だけ対応ブラウザ外、など)ので、片方が落ちても録画だけは残る設計にします。
日本語音声認識で効いた設定
SpeechRecognition の基本形は次のようになります。
const rec = new (window.SpeechRecognition || window.webkitSpeechRecognition)();
rec.lang = "ja-JP";
rec.continuous = true;
rec.interimResults = true;
ここで実装感覚として効いたのは次の3点です。
-
interimResults: trueは必須。falseだと確定文だけが飛んできて、話している最中のフィードバックが真っ白になります。練習UIとしては「話している文字が目に見える」こと自体に意味があるので、暫定結果をそのまま描画します。 -
無音で自動停止する。数秒〜十数秒の沈黙で
onendが発火します。面接の回答では「考えて黙る」時間が普通に出るので、onendで自動再開するループを書かないと、途中から文字起こしが空になります。 - フィラー語は素通しで保存する。「えー」「あのー」は認識結果にそのまま出てきます。後段でフィラー率を計算するなら、ここで消してはいけません。むしろ生の文字起こしをそのまま保存して、分析側で数えるのが正解でした。
自動再開ループの実装
onend での再開はシンプルですが、再開の連打でエラーになることがあります。実装では、停止フラグを持ってから再開する形に落ち着きました。
let stopped = false;
rec.onend = () => {
if (stopped) return;
try { rec.start(); } catch (e) { /* InvalidStateError は無視して次のonendで再試行 */ }
};
rec.onerror = (ev) => {
// "no-speech" と "aborted" は日常範囲。 "not-allowed" だけ致命的(権限)
if (ev.error === "not-allowed") stopped = true;
};
ポイントは no-speech エラーを異常扱いしないことです。黙って考える時間が長いユーザーほど頻発するので、これをエラーUIにすると「壊れた」と誤解されます。
MediaRecorder との並行稼働
録画と文字起こしは同じgetUserMediaストリームを共有できます。ストリームを分けて取得すると、ブラウザによってはマイクの排他で後から取った方が失敗します。
const stream = await navigator.mediaDevices.getUserMedia({
video: { width: 640, height: 480 },
audio: true,
});
const chunks = [];
const mr = new MediaRecorder(stream, { mimeType: "video/webm" });
mr.ondataavailable = (e) => chunks.push(e.data);
mr.start(1000); // 1秒ごとに分割保存しておくと、途中落ちしても前まで残る
つまずきどころは2つでした。
-
mimeType の対応表を持つ。
video/webm;codecs=vp9が通る環境とそうでない環境があるので、MediaRecorder.isTypeSupportedで候補を順に試すのが安全です。 -
iOS Safari では SpeechRecognition の連続動作が実質期待できない。
webkitSpeechRecognitionは存在しますが、長時間の連続認識が安定しません。現実解は「モバイルでは録画+再生レビューのみ、文字起こしはデスクトップのみ」と機能を分けることでした。
文字起こしから簡易指標を作る
保存した文字起こしは、そのまま3つの指標に落とせます。
| 指標 | 計算 | 目安 |
|---|---|---|
| 話速 | 総文字数 ÷ 発話秒数 | 300〜400字/分あたりが聞き取りやすい |
| フィラー率 | (えー・あのー・なんか の出現数)÷ 総文字数 | 高いほど「言葉に詰まっている」印象 |
| 構成 | 冒頭100字に結論があるか | 結論ファーストの判定に使える |
ここまで自動で出せると、「なんとなく話してしまった」が数値で残るので、練習の反復効果が上がります。面接対策の文面を機械に評価させると違和感が出る、という話を前回の記事でも書きましたが、測定はする、判定は人間がするという線引きは同じです。
まとめ
- Web Speech API の日本語認識は、
onend自動再開と暫定結果表示を押さえれば実用になる - 録画と認識は同じストリームを共有し、失敗ドメインを分けて設計する
- 文字起こしは生のまま保存し、分析(話速・フィラー率・構成)は後段に分離する
面接練習機能そのものは運営中のaijpjobsで実際に試せます。録画型AI面接がさらに普及すると、「本番前のリハーサル環境」をどう作るかは、ツールを作る側・使う側どちらでも重要になっていくはずです。