# 方法細節

（全文方法節底稿。演算法在執行前寫定；任何修訂記入
`decision-log.md`。定義檔全文見 `prompts/`，執行紀錄見
`runs/`。）

## 自然編碼管線總覽

三個步驟：步驟 1 自由標註（兩次執行進池）→ 步驟 2 詞彙表
建構（詞向量分群，確定性）→ 步驟 3 全量編碼（三次執行＋
多數決）。歌詞只出現在步驟 1 與步驟 3；步驟 2 完全不接觸
歌詞，也不呼叫 LLM。設計原則見 `research-plan.md`；本檔
記載可重現的演算法細節。

編號的所指為**研究程序的工序**，不是定義檔：步驟 1 與
步驟 3 有定義檔（`prompts/`），步驟 2 沒有——它是單一
確定性計算，由 `cluster-keywords` 一個子命令完成。有無
定義檔的區別即「該步是否為 LLM 判斷」，由 `prompts/` 是
否存在同號檔案直接可見。

## 步驟 2 詞彙表建構——詞向量分群

詞彙表由確定性程序產生，不經 LLM。完整分割（每個關鍵字
恰屬一組、不遺漏、不新增）由演算法結構保證，無須事後
驗證。

### 進池

兩次標註執行的全部關鍵字取聯集、逐字串精確去重、字典序
排列。失敗與拒答的記錄跳過（其歌曲不貢獻關鍵字）；解析
時偵測重複鍵，違規即失敗。

### 分群

- **嵌入**：`sentence-transformers/all-mpnet-base-v2`
  （釘定 revision），關鍵字的連字號先還原為空格再編碼，
  輸出 768 維向量並 L2 正規化。
- **分群**：階層式聚合分群（Ward linkage），k=100。
  向量既已正規化，歐氏距離與餘弦相似度單調對應；三種
  linkage 實測比較，Ward 於各個 k 的組內一致性均最高
  （average 與 complete 皆產生吞噬半數語料的巨大異質
  組）。
- **組數的取捨**：k 太小則壓縮比過高，樹上層被迫併入
  不相干的詞，組雖大而無主題（k=30 最大組 491 詞、
  組內一致性 0.41，成員橫跨籃球、海灘、外星人綁架）；
  k 太大則人工難以通覽。定於 100，理由是實測顯示雜物櫃
  組於此始裂解為有主題的組，且編碼實測未見碼數過多的
  副作用——全量三次執行下 101 個碼全數用到；模型另行
  造出的碼共 13 筆，佔 44,149 筆標籤指派的 0.03%。
- **組名**：取 medoid——與該組中心（成員向量均值後
  正規化）餘弦相似度最高的成員詞。組名因此必為模型
  自己產出過的關鍵字，非任何人事後撰寫。已知限制：
  組越大越異質時，medoid 只是折衷詞，可能代表不了組內
  內容（實測 `mutual-individuality` 組內一致 0.70 而
  編碼從未使用）；此類碼於結果中呈現為零使用，據實
  報告，不事後改名。
- **取捨紀錄**：曾以 LLM 單發收斂（merge／cap 兩步）
  實作本步，四種模型六次執行全部無法維持完整分割，
  已棄用（詳見 `decision-log.md` 2026-08-05；棄用的
  定義檔止於 git 歷史，見 `git log -- prompts/`）。
- **產物**：五份，前綴分別標示來源與結果。
  `source-keywords.txt`（進池後的關鍵字，一行一個）
  記錄進來的是什麼；`result-keywords.txt`（組名，一行
  一個）與 `groups.csv`（欄位 Group、Keyword，一列一個
  成員）記錄算出來的分割；`keywords-to-merge.json`
  （`{"keywords": [...]}`）是實際交給模型的碼，即組名
  加上先驗主題詞——五份中只有這一份含研究者的介入。
  `meta.json` 記錄執行本身：進池的兩份執行歸檔與其有效
  筆數、嵌入模型與釘定 revision、分群參數與組數、外加
  的先驗詞、關鍵字總數，以及產生數字的套件版本。凡命令
  列上的選擇與環境事實皆在此，不記時間戳與輸入雜湊
  ——前者使同環境重跑逐位元組可再生，後者只會重述 git
  已保證的事。
- **可重現性**：同一輸入、同一釘定模型、同一參數逐次
  重現。不同 CPU／BLAS 實作的浮點尾數差異可能使邊界
  詞的歸屬翻動，屬已揭露的限制；論文所用碼表逐字
  commit，引用單位為該份定案檔案。

### women-power 的注入

定案詞彙表為 100 個分群組名再加上 `women-power` 一詞，
共 101 個碼。`women-power` 是研究者任意決定的先驗主題（即本
論文的主題本身），不由資料產生，屬揭露的儀器介入。該詞
於執行時以 `--extra-keyword` 明示加入，不寫死在程式裏
——研究者的介入因此每次都出現在重現命令上，而非無聲
發生；分群結果的兩份產物不含它，只有交給模型的碼表含
它。

注入而非另設篩選軌的理由：讓研究者的主題詞與模型自己
收斂出的類別（分群已自行長出 `female-empowerment` 等組）
在同一份提示詞、同一個判斷體制下受檢，避免單目標提問
把該主題的顯著性人為抬高。兩者的落點差異本身即可報告
的結果。

## 步驟 3 編碼的三次執行與多數決

- **三次執行**：同一份定義檔、同一份輸入檔，獨立執行
  三次，三份歸檔並列（`runs/03-code/run1`、`run2`、
  `run3`），彼此無先後主從之別。
- **多數決**：一首歌的一個標籤，三次執行中至少兩次標出
  即收入定案編碼。三票不平手，裁決規則因此無例外條款，
  計票由確定性子命令完成（見交接契約）。
- **第三票取全量**：只對前兩次分歧的標籤補問第三票，
  計票結果相同；仍採全量執行——全部歌曲、全部關鍵字
  ——使三票在同一條件下取得。
- **對邊緣標籤的作用**：兩次執行只分得出「兩次皆標」與
  「僅一次標」；三次執行還分得出 3-0 與 2-1，故「定案
  編碼中有多少比例僅以一票之差成立」成為可報告的量。
  至於判定本身，兩次執行相左的標籤在何種協定下都由第三
  個判斷定奪，票數不使不確定性消失：某標籤於單次執行被
  標出的傾向若恰為一半，任何票數皆為擲幣。三票之效在
  傾向偏離一半處——多數決將判定推向該傾向本身（單次
  0.7 者為 0.78，0.9 者為 0.97），程序重跑的一致性因而
  高於單次執行，唯獨恰半處無從改善。

## 女性力量候選集

候選集為兩類歌曲的合集：定案編碼含 `women-power` 者，
以及定案編碼含研究者指認之女性力量概念域分群組者。
指認於詞彙表定案後、黃金標準編碼開始前完成，指認清單
與理由記入決策日誌。

## 軌跡對映（診斷用）

沿收斂軌跡的機械對映：原始關鍵字 →（兩份標註執行歸檔的
`output.jsonl`）歌曲、原始關鍵字 →（分群）組，純程式查表，
決定性。以其結果與步驟 3 直接編碼的差異率作為「收斂軌跡
扭曲」的診斷量，不作主結果。

## 全管線的交接契約

每一步的輸出如何變成下一步的輸入，皆為確定性程序，規則
明定如下：

- **歌詞輸入檔（步驟 1）**：`export-llm-input` 自工作
  儲存產出，每筆 `{"id": "song-<ID>", "content": <歌詞>}`，
  依歌曲 ID 升序。步驟 3 的輸入由同一子命令、同一工作
  儲存產出（見下），兩步的語料同一性由此成立；各步
  輸入檔的 SHA-256 記入該步 meta。
- **步驟 1 → 2**：`cluster-keywords` 讀兩份執行歸檔的
  `output.jsonl`（一律以換行字元 `\n` 切行——歌詞含
  U+0085 等控制字元時，`str.splitlines()` 類的通用切行
  會截斷 JSON 字串，實測踩中），進池後直接分群，一次
  產出上列五份檔案。
- **步驟 2 → 3 輸入檔**：`export-llm-input --extras
  <定案碼表>` 自工作儲存產出步驟 3 的輸入，每筆
  `{"id": "song-<ID>", "content": <字串>}`，`content` 為
  固定鍵序序列化的 `{"lyrics": …, "keywords": [...]}`，
  依歌曲 ID 升序。碼表以參數傳入而非填進定義檔——定義
  檔只規定任務形狀，換詞彙表、換演算法都不必改它。
- **步驟 3 定案**：`tally-codings <執行歸檔 1> <執行歸檔
  2> <執行歸檔 3> <輸出 CSV> --corrections <更正表>
  --valid-keywords <合法碼清單>` 讀三份執行歸檔的
  `output.jsonl`，依序套用更正表、驗證所有標籤皆在合法碼
  清單之內、計票。
- **更正表**：`data/manual/coding-corrections.csv`，研究者
  逐列校定的人工著作，欄位 `Song ID`、`Run`、`Type`、
  `To Be Replaced`、`Correct Term`。`Type` 為 `keyword`
  或 `evidence`，分別更正標籤與引述；`Correct Term` 為
  替代字串，或 `**REMOVE**` 表示刪去該筆標籤指派（`keyword`）
  或該句引述（`evidence`）。一筆 `evidence` 更正套用於該
  首歌該次執行的所有出現處。兩個文字欄以兩字元 `\n` 表示
  換行，故一列一行，純文字工具可逐列處理。表中任一列若
  在資料中找不到對應者，即中止；校定的判準記於
  `decision-log.md`。
- **合法碼清單**：純文字、一行一個碼，自詞彙表產出：
  `{ cat runs/02-cluster/result-keywords.txt; echo
  women-power; } | sort`。
- **定案表**：`results/codings.csv`，欄位 `Song`、
  `Artist Credit`、`Keyword`、`Quote`，一列一個標籤。歌名
  與演出者名銜逐首查工作儲存取得，故本子命令須在
  `build-db` 之後執行。`Quote` 為該標籤在計票中各份執行
  所引的歌詞行：各份的引述串接後逐字去重，按 Unicode
  碼位排序，以單一 `|` 相接（三份執行彼此無先後主從之
  別，引述之序取決於引述本身）；`Quote` 欄以兩字元 `\n`
  表示換行，故一列一行，還原 `\n` 後即為該首歌歌詞的逐字
  片段。列序依印出的前三欄依序排：歌名、演出者名銜、
  標籤，一律以 Unicode 碼位比較，換行為 CRLF（同專案
  其他 CSV）。
- **序列化通則**：所有中間檔為 UTF-8，欄序、鍵序與元素
  序皆依上列規則明定，無時間戳、無隨機成分；JSON 解析
  一律偵測重複鍵，違規即失敗。人讀為主的產物採純文字或
  CSV（CSV 依 RFC 4180，標題列字首大寫），機器交接檔採
  JSON。給定相同的 LLM 執行輸出，全部交接產物逐位元組
  可再生。

## 執行與稽核

- LLM 步驟以 `run-llm <定義檔> <輸入檔> <歸檔目錄>`
  執行；一步的 N 次執行＝重現命令清單上的 N 行命令，
  各自歸檔（`runs/<步驟>/run1`、`run2`，編碼步驟另有
  `run3`）。
- 確定性步驟（進池、分群、計票、對映）為子命令，其
  輸入輸出檔同隨 `runs/` 歸檔；因無執行變異，歸檔目錄
  下不分 `run<N>` 層。
- Batch API 的每筆請求自含全部脈絡且互不可見（平台
  契約），歌與歌之間的獨立性由此成立；各次執行的獨立
  性由「一次呼叫、一個批次、一份歸檔」的執行結構自明。
- 每次 `run-llm` 執行的 token 用量與費用記入
  `run-costs.md`，被取代的執行一併保留供總支出核算。

## 映射分析方法

（依 2026-07-30 決策，於看到結果前寫定；待黃金標準
編碼展開前補入。）
