작성 과정 탐지
Potato의 키스트로크 로그를 붙여넣거나 옮겨 적거나 기계가 생성한 자유 텍스트 답변에 대한 감사 가능한 플래그로 바꾸되, 임계값은 직접 읽고 덮어쓸 수 있습니다.
작성 과정 탐지는 키스트로크 로깅이 수집한 타이핑 동역학을 읽어, 근거를 붙인 이름 있는 플래그를 만들어 냅니다. 각 플래그가 어떤 특징 값 때문에 발생했는지 확인할 수 있으므로 동의하지 않는 것은 버릴 수 있습니다.
Potato 2.7.2 이상이 필요하고, 키스트로크 로깅이 이미 켜져 있는 곳에서만 동작합니다. keystroke_logging.enabled의 기본값이 false이므로, 이 기능을 켠 적이 없는 프로젝트에는 탐지할 대상 자체가 없습니다.
경고: 플래그는 사람이 검토할 근거입니다. 부정행위의 증거가 아니며, 자동 반려나 대금 지급 보류, 참가자 차단에 절대 연결해서는 안 됩니다. 빠르고 유창한 타자수가 깨끗한 초고를 쓰면 실제로 옮겨 적기와 비슷해 보입니다. 휴대전화로 작업하는 어노테이터는 실제로 붙여넣는 사람과 비슷해 보입니다. 아래 규칙들은 그런 충돌을 줄이도록 만들어졌지만, 충돌은 여전히 일어납니다. 오탐 절은 면책 문구가 아니라 이 기능의 일부입니다.
세 단계
| 단계 | 무엇인가 | 레이블 데이터가 필요한가 | 기본값 |
|---|---|---|---|
| 1. 규칙 | 임계값이 명시되고 근거가 보이는 6개의 이름 있는 플래그 | 아니요 | 켜짐 |
| 2. 보정 | 프로젝트의 실제 어노테이터에 맞춰 다시 맞춘 임계값 | 아니요 | 꺼짐 |
| 3. 지도 학습 | 자체 레이블로 직접 학습시키는 분류기 | 예 | 꺼짐 |
Potato는 사전 학습된 모델을 함께 배포하지 않습니다. 저장소에 레이블이 달린 코퍼스가 없으므로, 우리가 계수를 배포한다면 그것은 우리가 지어낸 검증 수치와 함께 오는 셈입니다. 대신 직접 읽고 반박할 수 있는 여섯 개의 규칙을 제공합니다. 실제 레이블이 있다면, 문헌에 보고된 정확도를 재현하는 길은 3단계입니다.
1단계: 규칙
모든 플래그는 그것을 발생시킨 특징 값을 함께 돌려주므로, 개별 플래그를 두고 근거를 대거나 버릴 수 있습니다.
paste_dominant
심각도: suspect. pasted_fraction >= 0.5일 때, 즉 최종 텍스트의 절반 이상이 붙여넣기로 들어왔을 때 발생합니다.
모든 붙여넣기가 self(자기 초안을 다시 배치)나 instance_text(어노테이션 대상 지문 인용)로 분류되면 억제됩니다.
silent_insertion
심각도: suspect. external_insert_ratio >= 0.3일 때, 즉 삽입된 문자의 3분의 1에 가까운 양이 대응하는 키 입력 없이 나타났을 때 발생합니다.
단일 신호로는 가치가 가장 높습니다. 붙여넣기, 자동 완성, 받아쓰기, 프로그램에 의한 주입이 모두 여기에 나타나며, 페이지가 붙여넣기 이벤트 자체를 억제한 경우에도 마찬가지입니다. 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. 페이지를 10초 이상 벗어난 직후에 외부에서 온 큰 삽입(80자 이상)이 이어질 때 발생합니다. "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 et al. 2019), 혼합 분포를 따르는 양에 고정 임계값을 쓰면 편향이 생깁니다(Roeser et al. 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 | 결합 규칙, suspect가 아닌 review, 보정 |
| 아주 짧은 답변 | 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 and Van Waes(Inputlog), 버스트 개념은 Chenoweth and Hayes, 키스트로크 특징의 과제 의존성은 Conijn, Roeser, van Zaanen, 고정 멈춤 임계값이 편향되는 이유는 Roeser, De Maeyer, Leijten, Van Waes를 참고하세요.
References
- 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
더 읽어보기
- 키스트로크 로깅 - 무엇이 어떻게 수집되는가
- 키스트로크 로깅 윤리 - IRB, 동의, 참가자 권리
- 품질 관리 - 주의 확인과 골드 스탠다드
- 관리자 대시보드 - 작성 과정 패널
구현 세부 사항은 원본 문서를 참고하세요.