はじめに
ありがたいことに、2026 Japan AWS Jr. Champions に選出されてから、登壇する機会が結構増えました。回数を重ねるにつれて、自分がどんなふうに話しているのか、聞いている側からどう見えているのかが気になってきました。ただ、自分の発表を自分で振り返ると、どうしても甘くなります。一度、客観的な物差しで評価してもらいたいと思っていました。
ちょうどこの間、LT を録画していただく機会がありました。その録画(mp4)を S3 に置くだけで AWS 上で自動採点し、HTML のレポートを出すシステムを作ってみました。
数えられるものは Lambda、数えられないものは Bedrock
設計の軸は1つだけです。すべてを LLM に任せず、数えられるものは Lambda で機械的に計算し、数えられないものだけを Bedrock に任せる。
話す速さやフィラー(「えー」「あの」のような言葉のつなぎ)の回数、発表時間は、文字起こしがあれば数えられます。一方で「冒頭で聴衆を引き込めているか」「伝えたいことが1つに絞れているか」は数えられないので、ここを Bedrock に採点させます。こうしておくと、点数が低かったときに「なぜ低いのか」を数字で説明できます。
構成
入り口は Claude Code のスキルにしました。VS Code で /lt-score <mp4のパス> 8 と入力すると、スキルが録画を S3 にアップロードします。最後の数字は規定時間(何分の枠か)で、S3 オブジェクトのメタデータ time-limit-min として一緒に渡します。アップロードをきっかけに、EventBridge 経由で Step Functions が起動します。
Step Functions は Amazon Transcribe(文字起こし)と Amazon Rekognition Video(顔検出)を並行して実行し、その結果を4つの Lambda で採点していきます。
- 顔検出が終わったら、視線判定の Lambda が顔の向きを分類し、聴衆側を見ていた時間の割合を出す
- 文字起こしと視線判定の両方が終わったら、定量評価の Lambda が話す速さやフィラーなどを数えて、50点満点で採点する
- 定性評価の Lambda が、Bedrock(Claude Sonnet 4.6)に内容と構成を50点満点で3回採点させ、平均を取る
- 集計の Lambda が、2つの評価を合算して結果を S3 に保存する
最後に、同じスキルがパイプラインの完了を待って結果を取得し、HTML レポートを作ってブラウザで開きます。Lambda は Python 3.12(arm64)、Bedrock は Converse API で呼び、インフラは SAM テンプレートで書きました。
採点基準は100点満点
定量評価の50点は Lambda が計算するので、同じ録画なら毎回同じ点になります。
| 指標 | 配点 | 計算方法 |
|---|---|---|
| 話す速さ | 10 | 句読点を除いた文字数 ÷ 発話時間(分)。目安は300字/分で、±30字以内なら満点 |
| フィラー | 10 | 「えー」「あの」「まあ」などの回数を1分あたりに換算。1回/分以下で満点 |
| 長い沈黙 | 5 | 単語と単語の間が3秒以上空いた回数。1回ごとに1点減点 |
| 時間配分 | 5 | 発表を3等分し、各区間の文字数の最大/最小の比が1.3倍以内なら満点 |
| 時間厳守 | 10 | 発話時間と規定時間を比較。超過した分だけ減点し、短い分には減点しない |
| 視線 | 10 | 聴衆(カメラ)の方を向いていた時間の割合。60%以上で満点 |
定性評価の50点は Bedrock に採点させ、3回の平均を取ります。
| 観点 | 配点 | 内容 |
|---|---|---|
| 冒頭の掴み | 15 | 最初の30秒で聴衆の興味を引けているか |
| メッセージの明確さ | 15 | 伝えたいことが1つに絞られているか |
| 構成の分かりやすさ | 10 | 話の流れが追いやすいか |
| 締め | 10 | 聴衆が持ち帰れるものがあるか |
プロンプトでは「話し方は機械的に採点済みなので、内容と構成だけを評価し、話し方で減点しない」と指示しています。これを書かないと、早口が定量でも定性でも二重に減点されてしまいます。
Step Functions で Transcribe と Rekognition の完了を待つ
Transcribe の StartTranscriptionJob も、Rekognition Video の StartFaceDetection も、呼ぶとジョブを受け付けた時点ですぐに返ってきます。文字起こしや顔検出が実際に終わるのは数分後です。
Step Functions には、ジョブの完了まで待ってくれる .sync 統合(Resource の末尾に .sync を付けると、完了まで Step Functions が待ってくれる仕組み)があります。ただ、対応しているサービスは一部だけで、Transcribe と Rekognition は対応していません。そのまま呼ぶと、ジョブを開始しただけで次のステートに進んでしまいます。
そこで、Wait(30秒)→ Get 系の API で状態を確認 → Choice(完了したか判定)のポーリングループを自分で組みました。完了なら先に進み、失敗なら失敗として終わり、それ以外なら Wait に戻ってもう一度待ちます。
Transcribe 側の判定部分(抜粋)はこんな形です。
"IsTranscriptionDone": {
"Type": "Choice",
"Choices": [
{"Variable": "$.transcription.status", "StringEquals": "COMPLETED", "Next": "TranscriptionDone"},
{"Variable": "$.transcription.status", "StringEquals": "FAILED", "Next": "TranscriptionFailed"}
],
"Default": "WaitForTranscription"
}
文字起こしと顔検出はお互いに依存しないので、Parallel ステートで並べて待ち時間を短くしています。
ハマったところ
フィラーが1文字の単語として出てくる
Transcribe(ja-JP)は、フィラーを省略せずに文字起こししてくれます。ところが「まあ」は「ま」、「えー」は「え」という1文字の単語として出力されていました。最初に用意した辞書は「まあ」「えー」だったので、1回も拾えていませんでした。
出力を眺めてようやく気づき、「ま」「え」「あ」を辞書に追加しました。「まで」が「ま」「で」に分かれて誤カウントされることもありますが、影響は小さかったので許容しています。
toolUse で固定しても JSON の形が崩れる
Bedrock の出力は、Converse API の toolConfig で採点結果用のツール submit_review を定義し、toolChoice でそのツールを必ず使わせる形にしました。文章から JSON を抜き出す方式より、形式はかなり安定します。
それでも一度、配列であるべき items が JSON の文字列として返ってきてエラーになりました。対策として、文字列なら JSON として読み直し、観点の過不足や点数の範囲を検証して、不正なら最大3回まで呼び直すようにしています。
このときありがたかったのが、Step Functions の redrive(失敗したステートから実行を再開する機能)です。文字起こしと顔検出をやり直さずに定性評価の Lambda から再開できたので、時間も費用もかかりませんでした。
真横を向いた顔は上下の角度が乱れる
Rekognition Video の顔検出は、検出した顔の向きを時刻付きで返します。視線判定の Lambda では動画を0.5秒ごとに区切り、各区間で一番大きく映っている顔(発表者)の向きを、Yaw(左右の向き)と Pitch(上下の向き)で次のように分類しました。
| 顔の向き | 判定 |
|---|---|
| 左右の向きが -50〜+30° の範囲外 | 横・スクリーン |
| 範囲内で、Pitch が -30° より小さい(うつむいている) | 手元(PC) |
| それ以外 | 聴衆 |
最初は上下を先に判定していました。ところが、真横を向いた顔は Pitch の推定値が大きく乱れ、-170° のような値が出ます。その結果、スクリーンを見ている場面が「手元」に分類されていました。左右を先に判定する順番に変えて解決しています。
閾値は、録画から場面を切り出して目で見て決めました。カメラの位置が変われば合わせ直しが必要です。
費用
費用は1回あたり約2ドルでした。内訳は Rekognition が約1.3ドル、Transcribe が約0.3ドル、Bedrock が数十円程度です。
いずれも手元での目安です。正確な単価は、各サービスの公式の料金ページを確認してください。
自分の LT を採点させてみた
最後に、録画していただいた私の LT(約12分半)を、規定時間8分の発表として採点させてみました。
結果は、100点中37点(定量12点、定性25点)でした。
| 指標 | 得点 | 実測値 |
|---|---|---|
| 話す速さ | 2 / 10 | 404字/分(目安300字/分) |
| フィラー | 0 / 10 | 162回(13.1回/分) |
| 長い沈黙 | 5 / 5 | 0回 |
| 時間配分 | 5 / 5 | 最大/最小 1.08倍 |
| 時間厳守 | 0 / 10 | 12分25秒(4分25秒超過) |
| 視線 | 0 / 10 | カメラ側を向いていた時間 1% |
| 冒頭の掴み | 4 / 15 | |
| メッセージの明確さ | 8 / 15 | |
| 構成の分かりやすさ | 6 / 10 | |
| 締め | 7 / 10 |
フィラー162回のうち、114回が「まぁ」でした。自分の口癖が「まぁ」だという自覚はまったくありませんでした。8分枠で12分半しゃべっているのも、シンプルに時間オーバーしすぎですね...
視線の1%は、さすがに低すぎると感じています。今回の録画は正面からではなく斜めから撮られていたので、聴衆の方を向いていても「横を向いている」と判定された場面が多かったはずです。実際はもう少し聴衆を見て話していたと思います。思いたい(切実に)
AI の講評で一番点が低かったのは「冒頭の掴み(4/15)」で、こんな改善案が付いていました。(原文から抜粋)
『AIに要件を話したら、数時間で申し込みシステムが完成した』『あなたの次のプロジェクト、同じことができますか?』のように、聴衆が自分ごととして引き込まれる一言から入りましょう。自己紹介は1文に圧縮するか、スライドに委ねてください。
挨拶と自己紹介から入るのが LT の定番だと思っていましたが、確かに最初に掴みがあるといいなーと思いました。
おわりに
LLM に全部任せる作りにしていたら、「37点です」と言われても、どこを直せばいいのか分からなかったと思います。数えられるものを Lambda に寄せたおかげで、「まぁ」が114回、と根拠付きで返ってきました。悔しいですが、納得感はあります。
一番ギクッとしたのは視線でした。撮影の角度を差し引いても、自分が思っていたよりずっと、スクリーンや手元を見ながら話していたようです。これからの登壇では、聴衆の方を向いて話すことを意識していきたいと思います。
これからも登壇するたびに採点させて、前回の結果と比べながら少しずつより良い発表ができるようにしようと思います。
自分の話し方を一度客観的に見てみたい人や、Step Functions と Bedrock で何か作ってみたい人の参考になれば嬉しいです。


