Skip to content

Think-Aloud モード

アノテーターが作業しながら話し、Potatoはその逐語の文字起こしを根拠として保存します。音声認識はfaster-whisperで完全にローカルに動くので、音声は端末の外に出ず、クラウドAPIもLLMも経路に入りません。

v2.7.0の新機能

LLM以前、判断を理解するための最良の方法はthink-aloudプロトコルでした。そしてそれは、アノテーションツールの世界には根づきませんでした。Think-Aloud モードは、アノテーターが作業しながらただ話せるようにします。

逐語の文字起こしはそのまま根拠として保存されます。要約はあえてしません。think-aloudプロトコルを言い換えてしまうと、集めようとしている資料そのものが汚染されるからです。ラベルは決まった言い回しを使って声で確定でき、その検出はルールベースのパーサーが行います。経路のどこにもLLMはいません。

音声認識はfaster-whisperによって完全にローカルで動きます。39 MBの tiny.en モデルならCPUでリアルタイムです。音声は端末の外に出ず、呼び出すクラウドAPIもトークン単位の請求もありません。

Note: Think-Aloudが文字起こしするのはアノテーター自身です。Whisperやクラウドの音声認識API、ダウンロードした字幕など、すでに手元にある文字起こしをアノテーションしたい場合は別の機能になります。文字起こし形式を参照してください。どちらもWhisperが絡むので混同されがちです。

検出された音声ラベルとともに表示される、発話そのままの推論の文字起こし。人間の思考の連鎖を、モデルのそれと比べるために。

人間の思考の連鎖を記録する

目的は根拠を集めることだけではありません。Think-Aloudが捉えるのは人が実際にどうラベルへたどり着くかであり、同じアイテムに対するモデルの思考の連鎖と、その人間の思考の連鎖を突き合わせられます。

Potatoのプロセス報酬アノテーションモデルの推論をステップごとに切り分けます。Think-Aloudはの推論を捉えます。どちらも記録のための面であり、同じアイテムの上に並べたときにこそ、興味深い食い違いが見えてきます。

仕組み

  1. アノテーターが 🎤 声に出して考える をタップして、自由に話します。
  2. 音声は6秒ずつの完結したチャンクとして取り込まれ、ローカルで文字起こしされます。
  3. 声でラベルを確定するには、受け付けられる言い回しのいずれかを使います。
    • "I label this Polite" / "I'd call it neutral"
    • "My answer is impolite"
    • "Final answer: polite" / "I go with neutral"
  4. 検出されるとUI上で該当の選択肢が自動的に選ばれ、通常の保存処理が走り、ピルが確認を出します:Heard: Impolite ✓。あとから新しい言い回しを口にすればラベルは変わり、最後の確定が勝ちます。
  5. require_spoken_label: true のとき、ラベルを確定しないまま次へを押すと、期待される言い回しを示す注意が一度だけ出ます。もう一度次へを押せばそのまま進み、ラベルのクリックはいつでも有効です。

考えているあいだに口にしたことはすべて無視されます。"This seems polite, but…" は何も確定しません。確定するのは決まった言い回しだけで、だからこそルールベースの解析で足ります。聞き間違いはラベルのあいまい一致が吸収するので、"in polite" は Impolite に解決されます。

セットアップ

bash
pip install faster-whisper   # local STT; first recording downloads the model (~39 MB)

ブラウザにはマイクの許可が必要です。localhost はセキュアなコンテキストとして扱われます。

設定

yaml
thinkaloud:
  enabled: true
  schema: politeness          # scheme whose labels can be spoken (default: first radio)
  stt: auto                   # faster_whisper | mock | auto
  model: tiny.en              # tiny.en is CPU real-time; base.en is sturdier
  chunk_seconds: 6            # recording chunk length
  require_spoken_label: true  # nudge on Next without a committed label
  # stems:                    # override accepted phrasing regexes (advanced)
  # fillers: [um, uh, hmm, i guess, maybe]
  # language: en
オプションデフォルト説明
sttautofaster_whisper(ローカル)、mock(テストと開発用)、または autoauto はfaster-whisperを選び、入っていなければ分かりやすいエラーを出します。
modeltiny.enfaster-whisperの任意のモデルID。
chunk_seconds6各チャンクは完結した音声ファイルです。
stems組み込み受け付ける言い回しの正規表現の語幹。それぞれが後続の語を取り込みます。
fillersum, uh, hmm, …ためらいのカウンターが使う語彙。
require_spoken_labeltrue何も確定していないときに、次へのボタンで一度だけ出す注意。

得られるもの

  • 逐語の根拠のストリームが (アノテーター, インスタンス) ごとに揃い、ラベルの言い回しは切り分けられます。文字起こしから確定の言い回しを除いたものが根拠です。
  • 決定的なためらいのシグナル:無音チャンクの数と、設定可能な語彙に基づくフィラー語の数。モデルではなく算術で計算します。
  • レビューページ/thinkaloud/review(管理者)にあり、各セッションの文字起こし、声で確定したラベル、確信度、ためらいの統計が並びます。
  • 副産物としてのハンズフリーのアノテーション。アクセシビリティやRSIの緩和という実利もあります。

データとAPI

文字起こしは {output_annotation_dir}/thinkaloud/transcripts.jsonl に保存されます(追記のみ、チャンク1つにつき1レコード)。

エンドポイントメソッド認証目的
/thinkaloud/api/chunkPOSTセッションマルチパートの音声チャンク → 文字起こし + 検出
/thinkaloud/api/textPOSTセッションテキストのチャンク(音声を使わない経路)
/thinkaloud/api/stateGETセッションインスタンスに対するセッションの集計
/thinkaloud/reviewGET管理者文字起こしのレビューページ
/thinkaloud/api/exportGET管理者全セッションをJSONで

設計上のメモ

  • パーサーは直近2チャンクの移動ウィンドウ上で動くので、チャンクの境界をまたぐ言い回しも検出されます。
  • ラベルの照合は完全一致、次に前方一致、次に difflib のしきい値0.8の順で、完全一致を優先します。"polite" が "Impolite" にあいまい一致で衝突することはありません。
  • 音声認識のバックエンドを追加するには、potato/thinkaloud/stt.pySTTBackend を継承し、create_stt に登録します。

参考資料