Skip to content

Think-Aloud 模式

标注者一边干活一边说话,Potato 把逐字转写稿存下来当作理由。语音转文字完全在本地跑,用的是 faster-whisper,音频不出本机,流程里没有云端 API,也没有 LLM。

v2.7.0 新增

在 LLM 出现之前,理解判断过程的黄金方法是出声思考法,而它从来没能真正进到标注工具里。Think-Aloud 模式让标注者干活时直接说出来就行。

逐字转写稿会被原样存成理由,刻意不做摘要,因为对出声思考的记录做转述,等于污染了你本来想采集的那份材料。标签可以用语音提交,靠的是一套基于规则的解析器识别固定说法,整条流程里没有任何 LLM。

语音转文字完全在本地通过 faster-whisper 运行,39 MB 的 tiny.en 模型在 CPU 上就能实时跑。音频不会离开本机,没有云端 API 要调,也没有按 token 计费的账单要付。

Note: Think-Aloud 转写的是你的标注者。如果你想标注的是已有的转写稿,来自 Whisper、云端 ASR API 或下载来的字幕,那是另一个功能:见转写格式。这两者常被混淆,因为都用到了 Whisper。

一份逐字的口头推理转写稿,带识别出的语音标签:人的思维链,用来和模型的思维链对照。

记录人的思维链

重点不只是收集理由。Think-Aloud 捕捉的是一个人究竟怎么推到那个标签上,于是你可以把这条人的思维链,和模型在同一条目上的思维链摆在一起看。

Potato 的过程奖励标注逐步拆解模型怎么推理,Think-Aloud 捕捉的是怎么推理。两者都是采集界面,把它们在同一个条目上并排放着,有意思的分歧就是在那里冒出来的。

工作方式

  1. 标注者点一下 🎤 出声思考,然后随便说。
  2. 音频按完整的六秒分块采集,并在本地转写。
  3. 要用语音提交标签,就用下面这些被接受的说法之一:
    • "I label this Polite" / "I'd call it neutral"
    • "My answer is impolite"
    • "Final answer: polite" / "I go with neutral"
  4. 识别到之后会自动在界面里选中对应选项,常规的保存流程随之触发,小标签给出确认: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(测试和开发用),或 auto,它会选 faster-whisper,缺失时给出有用的报错。
modeltiny.en任意 faster-whisper 模型 id。
chunk_seconds6每个分块都是一个完整的音频文件。
stems内置被接受说法的正则词干;每条都会捕获其后跟着的词。
fillersum, uh, hmm, …犹豫计数器所用的词表。
require_spoken_labeltrue什么都没提交时,在下一条按钮上给一次性提示。

你能得到什么

  • 逐字的理由文本流,与每个 (标注者, 实例) 对齐,标签那句话被单独拆出来。转写稿减去提交那句,就是理由。
  • 确定性的犹豫信号:静音分块计数,以及基于可配置词表的填充词计数,都用算术算出来,不靠模型。
  • 一个审阅页面 /thinkaloud/review(管理员),列出每个会话的转写稿、语音提交的标签、置信度和犹豫统计。
  • 免手操作的标注,算是顺带的好处,其中包括实打实的无障碍支持和对重复性劳损的缓解。

数据与 API

转写稿持久化到 {output_annotation_dir}/thinkaloud/transcripts.jsonl(只追加,每个分块一条记录)。

端点方法鉴权用途
/thinkaloud/api/chunkPOSTsessionMultipart 音频分块 → 转写 + 识别
/thinkaloud/api/textPOSTsession文本分块(无音频路径)
/thinkaloud/api/stateGETsession某个实例的会话聚合数据
/thinkaloud/reviewGETadmin转写稿审阅页面
/thinkaloud/api/exportGETadmin所有会话的 JSON

设计说明

  • 解析器跑在最近两个分块的滑动窗口上,因此跨分块边界的说法依然能识别到。
  • 标签匹配先精确匹配,再前缀匹配,最后用 difflib 在 0.8 阈值上匹配,并优先取精确匹配。"polite" 绝不会模糊撞上 "Impolite"。
  • 要接入新的 STT 后端,就在 potato/thinkaloud/stt.py 里继承 STTBackend,并在 create_stt 中注册。

延伸阅读