寫作過程檢測
把 Potato 的擊鍵日誌變成可審計的標記,指出哪些自由文本答案是貼上、謄抄或機器生成的,閾值你都能讀懂、也能覆蓋。
寫作過程檢測讀取擊鍵記錄採集到的打字動態,產出附帶證據的具名標記,這樣你可以看到是哪些特徵值觸發了每一個標記,並把你不認同的那些丟掉。
它需要 Potato 2.7.2 或更高版本,而且只在已經開啟擊鍵記錄的地方執行。既然 keystroke_logging.enabled 預設為 false,一個從未主動開啟的項目也就沒有什麼可檢測的。
警告: 標記是供人審查的證據。它們不是不端行為的證明,也絕不能接到自動拒絕、扣款或封禁參與者上。一個打字又快又流暢、一稿乾淨寫完的人,確實很像謄抄。一個用手機標註的人,確實很像在貼上。下面這些規則就是為減少這類撞車而設計的,撞車還是會發生。誤報一節是這個功能的組成部分,不是免責宣告。
三個層次
| 層次 | 是什麼 | 需要帶標籤的資料嗎? | 預設 |
|---|---|---|---|
| 1. 規則 | 六個具名標記,閾值明確、證據可見 | 否 | 開 |
| 2. 校準 | 用你自己項目的標註者重新擬合閾值 | 否 | 關 |
| 3. 有監督 | 你用自己的標籤訓練的分類器 | 是 | 關 |
Potato 不隨附任何預訓練模型。倉庫裡沒有帶標籤的語料,所以我們釋出的任何係數都會帶著一個我們自己編出來的驗證數字。作為替代,你拿到的是六條你能讀懂、也能反駁的規則。如果你有真實標籤,第 3 層才是復現文獻中那些準確率的路徑。
第 1 層:規則
每個標記都會返回觸發它的特徵值,所以任何一條你都可以為之辯護,或者棄之不用。
paste_dominant
嚴重程度:suspect。在 pasted_fraction >= 0.5 時觸發,即最終文本中有一半或以上來自貼上。
當所有貼上都被歸類為 self(重新排布自己的草稿)或 instance_text(引用正在標註的材料)時抑制。
silent_insertion
嚴重程度:suspect。在 external_insert_ratio >= 0.3 時觸發,即插入的字元中將近三分之一在出現時沒有對應的擊鍵。
這是價值最高的單一訊號。貼上、自動填充、聽寫和程式化注入都會在這裡顯現,即便貼上事件本身被頁面攔下也一樣。它直接把 Asher 等人所說的「擊鍵數相對於回答長度異常偏低」操作化了。
在軟鍵盤(virtual_keyboard)上和 IME 組字期間抑制,那些情況下 keydown 不會被可靠地發出,否則每一次插入看起來都是無聲的。這條規則用的是區分來源的比率,所以正當的引用不會被計入。
transcription_rhythm
嚴重程度:review。當以下三條同時成立時觸發:
iki_log_cv <= 0.06,節拍均勻的打位元組奏revision_ratio <= 0.02,基本沒有刪除- 每 100 個字元裡 2 秒及以上的停頓少於 0.5 次
有意做成合取。任何一條單獨看都有無辜的解釋。三條合在一起,才是 Crossley 等人描述的謄抄特徵:線性、成串爆發、低方差。
對 80 字元或 40 次擊鍵以下的回答完全跳過,那裡談不上什麼節奏。
offscreen_composition
嚴重程度:suspect。當一段較大的外部來源插入(80 字元或以上)緊跟在離開頁面 10 秒或更久之後出現時觸發。這就是「切到 ChatGPT,回來,貼上」的模式。
當插入內容的來源是正在標註的材料或標註者自己的文本時抑制,因為走開一會兒再回來引用材料是很正常的行為。
implausible_speed
嚴重程度:review。在整份回答中持續超過每分鐘 900 字元(約 180 wpm)時觸發。
synthetic_input
嚴重程度:suspect。在任何 isTrusted === false 的事件上觸發,也就是瀏覽器報告該輸入由指令碼而非人產生。瀏覽器自動化、注入的指令碼和某些無障礙工具都會產生這種事件。
判定等級
| 等級 | 含義 |
|---|---|
ok | 沒有標記觸發 |
review | 至少有一個 review 級別的標記 |
suspect | 至少有一個 suspect 級別的標記 |
覆蓋閾值
keystroke_logging:
detection:
thresholds:
paste_dominant.pasted_fraction: 0.4
silent_insertion.ratio: 0.25
transcription_rhythm.iki_log_cv: 0.05
transcription_rhythm.revision_ratio: 0.02
transcription_rhythm.pause_2s_per_100_chars: 0.5
offscreen_composition.blur_ms: 15000
offscreen_composition.insert_chars: 100
implausible_speed.chars_per_min: 1000閾值在服務端評估,從不傳送到瀏覽器。公佈它們等於精確地告訴標註者要多慢地貼上才能不被標記。
第 2 層:項目校準
擊鍵特徵會隨寫作任務有相當大的差異(Conijn 等人 2019),而在一個混合分佈的量上用固定閾值是有偏的(Roeser 等人 2021)。對一句話理由說明合適的界限,用在五段的文章上就是錯的。
校準把每個閾值設在你自己項目會話的某個尾部百分位上。
# Inspect the fit without saving it
python -m potato.typing_detect calibrate config.yaml --dry-run
# Save it
python -m potato.typing_detect calibrate config.yamlkeystroke_logging:
detection:
calibrate: true # use the saved fit校準至少需要 30 個可用會話(80 字元或以上,非移動端)。少於這個數量時它返回 insufficient_data,預設值繼續生效。
警告: 百分位界限是一種相對的離群定義。按其構造,即便在一個誰都沒做錯事的人群裡,它也會標出大約
tail_fraction(預設 5%)的會話。它告訴你先看哪裡。它不是證據。校準後的閾值還會被額外限制在內建預設值的 3 倍以內,這樣一個同質化的人群就沒法把界限拖到自己的中位數上,進而開始標記誠實的標註者。
顯式的 thresholds: 覆蓋總是壓過校準值,校準值又總是壓過內建預設值。
第 3 層:有監督分類器
如果你有帶標籤的會話,就訓練一個真正的模型:
from potato import typing_store
from potato.typing_detect import fit_supervised
rows = typing_store.feature_matrix(task_dir, project)
labels = [...] # 1 = non-composed, 0 = composed
result = fit_supervised(rows, labels, model="random_forest")
print(result["cv_accuracy_mean"], result["feature_importances"])需要 scikit-learn,它是延遲匯入的,不是 Potato 的依賴。
不做外部研究也能拿到標籤
Potato 的培訓階段可以替你生成標籤。讓標註者把一段給定的材料抄一遍作為熱身任務。他們的謄抄會話就是真實的謄抄樣本,他們平常的答案就是撰寫樣本,於是你在自己的項目裡、在自己的任務上、用自己的標註者拿到了一個帶標籤的資料集。這與 Crossley 等人構建語料的方式相同,校準示例把這套流程端到端地搭好了。
誤報
這個功能的失敗模式是冤枉一位誠實的標註者。下面這些情況確實會像上面那些模式。
| 情形 | 像哪條規則 | Potato 如何處理 |
|---|---|---|
| 引用正在標註的材料 | paste_dominant、silent_insertion、offscreen_composition | 通過 paste_source: instance_text 抑制 |
| 挪動自己的草稿 | 同上 | 通過 paste_source: self 抑制 |
| 用手機或平板打字 | silent_insertion | 通過 virtual_keyboard 抑制 |
| 通過 IME 輸入非拉丁文字 | silent_insertion | 通過 composition_events 抑制 |
| 打字又快又流暢、一稿乾淨 | transcription_rhythm、implausible_speed | 合取規則;判為 review 而非 suspect;可校準 |
| 非常短的回答 | transcription_rhythm | 80 字元 / 40 次擊鍵以下跳過 |
| 聽寫或語音轉文字 | silent_insertion | 未處理。會被標記。請排除這些標註者或調高閾值。 |
| 螢幕閱讀器和部分輔助技術 | synthetic_input | 未完全處理。某些工具會產生不可信事件。 |
| 移動端自動糾錯 | silent_insertion | 部分處理。insertReplacementText 計為外部來源。 |
| 語法工具之類的瀏覽器擴充套件 | silent_insertion、synthetic_input | 未處理。會被標記。 |
最後四項是真實的侷限,而不是疏漏。如果你的參與者中包含聽寫使用者、輔助技術使用者,或使用寫作擴充套件的人,要麼把這些規則從你的審查標準裡去掉,要麼把每一個標記都當作去問一問標註者的提示,而不是採取行動的依據。
無障礙
synthetic_input 依據 isTrusted 判斷,而某些輔助技術也會觸發它。因為一位殘障標註者使用了自己工作所需的工具就把他標記出來,是不可接受的結果。
那條規則沒有閾值,所以你沒有任何值可以設來讓它永不觸發。如果你的研究對輔助技術使用者開放,要麼把 synthetic_input 從你寫下的審查標準裡排除,要麼徹底關閉檢測,自己分析匯出的特徵:
keystroke_logging:
enabled: true
detection:
enabled: false基於閾值的規則可以通過設一個荒唐的界限來讓它永不觸發,例如 implausible_speed.chars_per_min: 1000000。
即時干預
keystroke_logging:
detection:
on_external_insert: flag # allow | warn | block | flag| 值 | 行為 |
|---|---|
allow | 除記錄之外什麼都不做 |
warn | 外部貼上時給出不阻斷的提示,自引和引用材料時不提示 |
block | 阻止往被記錄的欄位貼上和拖放 |
flag | 預設。靜默記錄;由你稍後決定 |
block 是個鈍器。它同樣會擋掉正當的引用,而且鐵了心的參與者可以改成手打。用 flag 再回頭審查,通常更好。
管理員儀表板
管理員儀表板行為標籤頁下的寫作過程面板,展示每位標註者的中位數、貼上率、無聲插入率,以及每一個被標記的會話及其證據。
它報告 writing_process_risk,即一位使用者的會話中觸發了各個標記的比例,並向那些最難有無辜解釋的訊號加權。這是一個排序輔助,刻意與既有的 suspicion_score 分開,免得兩個數字裡的一個悄悄改變了另一個的含義。
研究依據
下面每一條引用都已對照 Crossref 或 DataCite 註冊記錄核實。
Crossley、Tian、Choi、Holmes 和 Morris(2024)收集了 500 篇論辯文章,讓另一批工作者謄抄這些文章,並用隨機森林以 99% 的準確率把真實寫作與謄抄分開(其他模型為 96-98%)。他們的特徵族正是 Potato 採集的那些:句子和詞語之前的停頓時長、插入和刪除次數、產品-過程比、爆發、修改和過程方差。他們的結論就是這裡的設計目標。真實寫作表現出更長的停頓、更多的插入和刪除,以及更大的方差;謄抄則是線性的、成串爆發的。
Deane、Zhang、Hao 和 Li,以及另外一組 Zhang、Feng、He、Li 和 Zhu,各自獨立證實了謄抄與自然寫作之間的可分性,後者用的是深度學習模型。Asher、Gold、Chen 和 Carvalho 則是專門針對眾包的案例:在 Prolific 上用一個擊鍵工具標出往回答框裡貼上、或擊鍵數相對於回答長度異常偏低的參與者。
關於底層的過程測量,可參考 Leijten 和 Van Waes(Inputlog)的標準日誌測量、Chenoweth 和 Hayes 關於爆發這一構唸的工作、Conijn、Roeser 和 van Zaanen 關於擊鍵特徵的任務依賴性,以及 Roeser、De Maeyer、Leijten 和 Van Waes 關於固定停頓閾值為何有偏的論述。
參考文獻
- Crossley, S., Tian, Y., Choi, J. S., Holmes, L., & Morris, W. (2024). Plagiarism Detection Using Keystroke Logs. EDM 2024 (Short Papers). doi:10.5281/zenodo.12729864
- Deane, P., Zhang, M., Hao, J., & Li, C. Using Keystroke Dynamics to Detect Nonoriginal Text. Journal of Educational Measurement, 63(1). doi:10.1111/jedm.12431
- Asher, M. W., Gold, G., Chen, E., & Carvalho, P. F. (2026). Chatbots Are Undermining Crowdsourced Research in the Behavioral Sciences: Detecting Artificial Intelligence-Assisted Cheating With a Keystroke-Based Tool. Advances in Methods and Practices in Psychological Science, 9(1). doi:10.1177/25152459261424723
- Zhang, M., Feng, L., He, X., Li, C., & Zhu, M. (2026). Disentangling copy typing and natural writing behaviors using keystroke logs and deep learning model. Assessing Writing. doi:10.1016/j.asw.2026.101070
- Leijten, M., & Van Waes, L. (2013). Keystroke Logging in Writing Research: Using Inputlog to Analyze and Visualize Writing Processes. Written Communication, 30(3), 358-392. doi:10.1177/0741088313491692
- Chenoweth, N. A., & Hayes, J. R. (2001). Fluency in Writing: Generating Text in L1 and L2. Written Communication, 18(1), 80-98. doi:10.1177/0741088301018001004
- Conijn, R., Roeser, J., & van Zaanen, M. (2019). Understanding the keystroke log: the effect of writing task on keystroke features. Reading and Writing, 32(9), 2353-2374. doi:10.1007/s11145-019-09953-8
- Roeser, J., De Maeyer, S., Leijten, M., & Van Waes, L. (2021). Modelling typing disfluencies as finite mixture process. Reading and Writing. doi:10.1007/s11145-021-10203-z
- Lee, M., Liang, P., & Yang, Q. (2022). CoAuthor: Designing a Human-AI Collaborative Writing Dataset for Exploring Language Model Capabilities. CHI 2022. doi:10.1145/3491102.3502030
延伸閱讀
有關實現細節,請參閱原始碼文件。