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は人の推論を捉えます。どちらも記録のための面であり、同じアイテムの上に並べたときにこそ、興味深い食い違いが見えてきます。
仕組み
- アノテーターが 🎤 声に出して考える をタップして、自由に話します。
- 音声は6秒ずつの完結したチャンクとして取り込まれ、ローカルで文字起こしされます。
- 声でラベルを確定するには、受け付けられる言い回しのいずれかを使います。
- "I label this Polite" / "I'd call it neutral"
- "My answer is impolite"
- "Final answer: polite" / "I go with neutral"
- 検出されるとUI上で該当の選択肢が自動的に選ばれ、通常の保存処理が走り、ピルが確認を出します:Heard: Impolite ✓。あとから新しい言い回しを口にすればラベルは変わり、最後の確定が勝ちます。
require_spoken_label: trueのとき、ラベルを確定しないまま次へを押すと、期待される言い回しを示す注意が一度だけ出ます。もう一度次へを押せばそのまま進み、ラベルのクリックはいつでも有効です。
考えているあいだに口にしたことはすべて無視されます。"This seems polite, but…" は何も確定しません。確定するのは決まった言い回しだけで、だからこそルールベースの解析で足ります。聞き間違いはラベルのあいまい一致が吸収するので、"in polite" は Impolite に解決されます。
セットアップ
pip install faster-whisper # local STT; first recording downloads the model (~39 MB)ブラウザにはマイクの許可が必要です。localhost はセキュアなコンテキストとして扱われます。
設定
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| オプション | デフォルト | 説明 |
|---|---|---|
stt | auto | faster_whisper(ローカル)、mock(テストと開発用)、または auto。auto はfaster-whisperを選び、入っていなければ分かりやすいエラーを出します。 |
model | tiny.en | faster-whisperの任意のモデルID。 |
chunk_seconds | 6 | 各チャンクは完結した音声ファイルです。 |
stems | 組み込み | 受け付ける言い回しの正規表現の語幹。それぞれが後続の語を取り込みます。 |
fillers | um, uh, hmm, … | ためらいのカウンターが使う語彙。 |
require_spoken_label | true | 何も確定していないときに、次へのボタンで一度だけ出す注意。 |
得られるもの
- 逐語の根拠のストリームが (アノテーター, インスタンス) ごとに揃い、ラベルの言い回しは切り分けられます。文字起こしから確定の言い回しを除いたものが根拠です。
- 決定的なためらいのシグナル:無音チャンクの数と、設定可能な語彙に基づくフィラー語の数。モデルではなく算術で計算します。
- レビューページが
/thinkaloud/review(管理者)にあり、各セッションの文字起こし、声で確定したラベル、確信度、ためらいの統計が並びます。 - 副産物としてのハンズフリーのアノテーション。アクセシビリティやRSIの緩和という実利もあります。
データとAPI
文字起こしは {output_annotation_dir}/thinkaloud/transcripts.jsonl に保存されます(追記のみ、チャンク1つにつき1レコード)。
| エンドポイント | メソッド | 認証 | 目的 |
|---|---|---|---|
/thinkaloud/api/chunk | POST | セッション | マルチパートの音声チャンク → 文字起こし + 検出 |
/thinkaloud/api/text | POST | セッション | テキストのチャンク(音声を使わない経路) |
/thinkaloud/api/state | GET | セッション | インスタンスに対するセッションの集計 |
/thinkaloud/review | GET | 管理者 | 文字起こしのレビューページ |
/thinkaloud/api/export | GET | 管理者 | 全セッションをJSONで |
設計上のメモ
- パーサーは直近2チャンクの移動ウィンドウ上で動くので、チャンクの境界をまたぐ言い回しも検出されます。
- ラベルの照合は完全一致、次に前方一致、次に
difflibのしきい値0.8の順で、完全一致を優先します。"polite" が "Impolite" にあいまい一致で衝突することはありません。 - 音声認識のバックエンドを追加するには、
potato/thinkaloud/stt.pyのSTTBackendを継承し、create_sttに登録します。
参考資料
- プロセス報酬アノテーション — モデル側の思考の連鎖を扱う面
- 行動トラッキング — 所要時間の分析
- 品質管理
- ソースドキュメント