patina-cli 3.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.patina.default.yaml +211 -0
- package/CHANGELOG.md +265 -0
- package/LICENSE +21 -0
- package/README.md +319 -0
- package/README_JA.md +254 -0
- package/README_KR.md +253 -0
- package/README_ZH.md +254 -0
- package/SKILL-MAX.md +455 -0
- package/SKILL.md +730 -0
- package/assets/brand/patina-icon.svg +9 -0
- package/assets/brand/patina-logo.svg +17 -0
- package/assets/social/patina-before-after.svg +46 -0
- package/assets/social/patina-og.svg +31 -0
- package/bin/patina.js +9 -0
- package/core/scoring.md +657 -0
- package/core/standalone-prompt.md +364 -0
- package/core/stylometry.md +754 -0
- package/core/voice.md +163 -0
- package/docs/AUTHENTICATION.md +105 -0
- package/docs/AUTHENTICATION_KR.md +105 -0
- package/docs/BRANDING.md +37 -0
- package/docs/CLI.md +80 -0
- package/docs/COMPARISON.md +38 -0
- package/docs/COOKBOOK.md +173 -0
- package/docs/DEMO.md +40 -0
- package/docs/ETHICS.md +27 -0
- package/docs/EXAMPLES.md +130 -0
- package/docs/EXAMPLES_KR.md +130 -0
- package/docs/EXIT-CODES.md +25 -0
- package/docs/FAQ.md +67 -0
- package/docs/FAQ_KR.md +65 -0
- package/docs/FLAG-PARITY.md +53 -0
- package/docs/GLOSSARY.md +123 -0
- package/docs/PATTERNS-EN.md +718 -0
- package/docs/PATTERNS-JA.md +706 -0
- package/docs/PATTERNS-KO.md +707 -0
- package/docs/PATTERNS-ZH.md +706 -0
- package/docs/PATTERNS.md +22 -0
- package/docs/ROADMAP.md +315 -0
- package/docs/audits/2026-05-deep-research.md +290 -0
- package/docs/benchmarks/detector-comparison.json +442 -0
- package/docs/benchmarks/detector-comparison.md +65 -0
- package/docs/benchmarks/latest.json +988 -0
- package/docs/benchmarks/latest.md +112 -0
- package/docs/integrations/docker.md +19 -0
- package/docs/integrations/github-action.md +59 -0
- package/docs/integrations/pre-commit.md +77 -0
- package/docs/integrations/release.md +43 -0
- package/docs/internal/HARNESS.md +14 -0
- package/docs/internal/README.md +14 -0
- package/docs/internal/WARP.md +23 -0
- package/docs/research/2025-rebaseline-plan.md +89 -0
- package/docs/research/ai-human-metrics.md +380 -0
- package/docs/social/gstack-cardnews.html +236 -0
- package/docs/social/gstack-cardnews.md +88 -0
- package/docs/social/gstack-thread.md +106 -0
- package/docs/social/patina-launch-copy.md +227 -0
- package/docs/superpowers/specs/2026-04-03-meaning-preservation-design.md +299 -0
- package/lexicon/ai-en.md +162 -0
- package/lexicon/ai-ko.md +159 -0
- package/package.json +100 -0
- package/patina-max/SKILL.md +523 -0
- package/patina-max/composite.py +457 -0
- package/patterns/en-communication.md +89 -0
- package/patterns/en-content.md +133 -0
- package/patterns/en-filler.md +113 -0
- package/patterns/en-language.md +163 -0
- package/patterns/en-structure.md +173 -0
- package/patterns/en-style.md +139 -0
- package/patterns/en-viral-hook.md +211 -0
- package/patterns/ja-communication.md +101 -0
- package/patterns/ja-content.md +153 -0
- package/patterns/ja-filler.md +123 -0
- package/patterns/ja-language.md +190 -0
- package/patterns/ja-structure.md +142 -0
- package/patterns/ja-style.md +147 -0
- package/patterns/ja-viral-hook.md +216 -0
- package/patterns/ko-communication.md +98 -0
- package/patterns/ko-content.md +154 -0
- package/patterns/ko-filler.md +105 -0
- package/patterns/ko-language.md +182 -0
- package/patterns/ko-structure.md +147 -0
- package/patterns/ko-style.md +146 -0
- package/patterns/ko-viral-hook.md +211 -0
- package/patterns/zh-communication.md +101 -0
- package/patterns/zh-content.md +153 -0
- package/patterns/zh-filler.md +118 -0
- package/patterns/zh-language.md +173 -0
- package/patterns/zh-structure.md +145 -0
- package/patterns/zh-style.md +159 -0
- package/patterns/zh-viral-hook.md +216 -0
- package/profiles/academic.md +53 -0
- package/profiles/blog.md +81 -0
- package/profiles/casual-conversation.md +105 -0
- package/profiles/code-comment.md +104 -0
- package/profiles/commit-message.md +99 -0
- package/profiles/default.md +62 -0
- package/profiles/email.md +52 -0
- package/profiles/formal.md +98 -0
- package/profiles/instructional.md +80 -0
- package/profiles/legal.md +57 -0
- package/profiles/marketing.md +56 -0
- package/profiles/medical.md +53 -0
- package/profiles/narrative.md +79 -0
- package/profiles/release-notes.md +98 -0
- package/profiles/social.md +56 -0
- package/profiles/technical.md +53 -0
- package/scripts/benchmark-report.mjs +252 -0
- package/scripts/check-release-metadata.mjs +48 -0
- package/scripts/detector-comparison.mjs +267 -0
- package/scripts/lint.mjs +40 -0
- package/scripts/precommit-score.mjs +31 -0
- package/scripts/prose-score.mjs +186 -0
- package/scripts/update-benchmark-ranges.mjs +108 -0
- package/src/api.js +330 -0
- package/src/auth.js +105 -0
- package/src/backends/claude-cli.js +112 -0
- package/src/backends/codex-cli.js +121 -0
- package/src/backends/contract.js +21 -0
- package/src/backends/gemini-cli.js +135 -0
- package/src/backends/index.js +159 -0
- package/src/cache.js +106 -0
- package/src/cli.js +1280 -0
- package/src/commands/doctor.js +229 -0
- package/src/commands/init.js +208 -0
- package/src/config.js +126 -0
- package/src/errors.js +53 -0
- package/src/features/index.js +96 -0
- package/src/features/lexicon.js +90 -0
- package/src/features/segment.js +49 -0
- package/src/features/stylometry.js +50 -0
- package/src/loader.js +103 -0
- package/src/logger.js +70 -0
- package/src/manifest.js +162 -0
- package/src/max-mode.js +207 -0
- package/src/ouroboros.js +233 -0
- package/src/output.js +480 -0
- package/src/prompt-builder.js +409 -0
- package/src/providers.js +100 -0
- package/src/scoring.js +531 -0
- package/src/security.js +133 -0
- package/tests/fixtures/suspect-zones/en/ai/en-ai-01.md +16 -0
- package/tests/fixtures/suspect-zones/en/ai/en-ai-02.md +16 -0
- package/tests/fixtures/suspect-zones/en/ai/en-ai-03.md +17 -0
- package/tests/fixtures/suspect-zones/en/ai/en-ai-04.md +15 -0
- package/tests/fixtures/suspect-zones/en/ai/en-ai-05.md +16 -0
- package/tests/fixtures/suspect-zones/en/ai/en-ai-06-chat-register.md +16 -0
- package/tests/fixtures/suspect-zones/en/natural/en-nat-01.md +15 -0
- package/tests/fixtures/suspect-zones/en/natural/en-nat-02.md +15 -0
- package/tests/fixtures/suspect-zones/en/natural/en-nat-03.md +15 -0
- package/tests/fixtures/suspect-zones/en/natural/en-nat-04.md +15 -0
- package/tests/fixtures/suspect-zones/en/natural/en-nat-05.md +15 -0
- package/tests/fixtures/suspect-zones/expected-ranges.json +939 -0
- package/tests/fixtures/suspect-zones/ja/ai/ja-ai-01.md +11 -0
- package/tests/fixtures/suspect-zones/ja/ai/ja-ai-02.md +11 -0
- package/tests/fixtures/suspect-zones/ja/ai/ja-ai-03.md +11 -0
- package/tests/fixtures/suspect-zones/ja/natural/ja-nat-01.md +11 -0
- package/tests/fixtures/suspect-zones/ja/natural/ja-nat-02.md +11 -0
- package/tests/fixtures/suspect-zones/ja/natural/ja-nat-03.md +11 -0
- package/tests/fixtures/suspect-zones/ko/ai/ko-ai-01.md +14 -0
- package/tests/fixtures/suspect-zones/ko/ai/ko-ai-02.md +16 -0
- package/tests/fixtures/suspect-zones/ko/ai/ko-ai-03.md +15 -0
- package/tests/fixtures/suspect-zones/ko/ai/ko-ai-04.md +15 -0
- package/tests/fixtures/suspect-zones/ko/ai/ko-ai-05.md +16 -0
- package/tests/fixtures/suspect-zones/ko/ai/ko-ai-06-chat-register.md +16 -0
- package/tests/fixtures/suspect-zones/ko/natural/ko-nat-01.md +15 -0
- package/tests/fixtures/suspect-zones/ko/natural/ko-nat-02.md +15 -0
- package/tests/fixtures/suspect-zones/ko/natural/ko-nat-03.md +15 -0
- package/tests/fixtures/suspect-zones/ko/natural/ko-nat-04.md +14 -0
- package/tests/fixtures/suspect-zones/ko/natural/ko-nat-05.md +15 -0
- package/tests/fixtures/suspect-zones/zh/ai/zh-ai-01.md +11 -0
- package/tests/fixtures/suspect-zones/zh/ai/zh-ai-02.md +11 -0
- package/tests/fixtures/suspect-zones/zh/ai/zh-ai-03.md +11 -0
- package/tests/fixtures/suspect-zones/zh/natural/zh-nat-01.md +11 -0
- package/tests/fixtures/suspect-zones/zh/natural/zh-nat-02.md +11 -0
- package/tests/fixtures/suspect-zones/zh/natural/zh-nat-03.md +11 -0
- package/tests/quality/README.md +121 -0
- package/tests/quality/benchmark.mjs +306 -0
- package/tests/quality/detectors.manual.example.json +31 -0
- package/tests/quality/dogfood.mjs +44 -0
|
@@ -0,0 +1,754 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Stylometric Suspect-Zone Detection
|
|
3
|
+
version: 2.2.0
|
|
4
|
+
description: Deterministic statistical preprocessing that flags suspect paragraphs and sentence groups before pattern scanning, used by SKILL.md Step 4.6 (burstiness + MATTR) and Step 4.7 (AI-lexicon overlap)
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Stylometric Suspect-Zone Detection
|
|
8
|
+
|
|
9
|
+
결정론적 통계 분석으로 패턴 카탈로그가 놓치는 "이름 없는 AI다움"을 표시하는 전처리 단계.
|
|
10
|
+
패턴 스캔 이전에 단락 단위 burstiness와 어휘 다양성(MATTR)을 계산하고, 의심 단락 내부에서
|
|
11
|
+
문장 단위로 zoom-in 한다. 결과는 LLM에게 내부 작업 메모리로 전달되며 사용자 출력에는 노출하지 않는다.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 1. Overview
|
|
16
|
+
|
|
17
|
+
`core/scoring.md`가 패턴 감지 결과를 0-100 점수로 환산하는 채점 알고리즘이라면,
|
|
18
|
+
이 문서는 패턴 스캔 *이전에* 동작하는 통계 전처리 알고리즘을 정의한다.
|
|
19
|
+
|
|
20
|
+
목적:
|
|
21
|
+
- 28개 패턴 카탈로그가 직접 명명하지 못하는 분포적 단서(균질한 문장 길이, 빈약한
|
|
22
|
+
어휘 다양성)를 결정론적 수치로 잡아낸다
|
|
23
|
+
- 4.5단계 의미 앵커(Semantic Anchor)와 같은 "내부 작업 메모리" 패턴을 따라,
|
|
24
|
+
hot 단락/문장 정보를 5a·5b 입력에 주입한다
|
|
25
|
+
- 외부 의존성(형태소 분석기, 외부 detector API) 없이 ko/en은 whitespace 토큰화,
|
|
26
|
+
zh/ja는 deterministic character-token fallback으로 동작한다
|
|
27
|
+
|
|
28
|
+
이 알고리즘은 5a 구조 분석 단계와 5b 문장/어휘 단계에 자연스럽게 결합되도록 설계되었다.
|
|
29
|
+
- 5a는 단락 수준 hot 표시를 우선 검토 대상으로 사용한다
|
|
30
|
+
- 5b는 문장 수준 hot 그룹을 우선 재작성 대상으로 사용한다
|
|
31
|
+
|
|
32
|
+
### 언어 범위
|
|
33
|
+
|
|
34
|
+
| 언어 | 지원 | 비고 |
|
|
35
|
+
|------|---------|------|
|
|
36
|
+
| ko | yes | 어절 단위 토큰화 |
|
|
37
|
+
| en | yes | 단어 단위 토큰화 |
|
|
38
|
+
| zh | yes | Han character-token fallback |
|
|
39
|
+
| ja | yes | Kana/Han character-token fallback; 형태소 분석 없음 |
|
|
40
|
+
|
|
41
|
+
기본 `stylometry.languages`는 `[ko, en, zh, ja]`이다. 사용자가 설정에서 언어를 제거하면
|
|
42
|
+
해당 언어는 4.6단계를 skip 한다.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## 2. Tokenization Policy
|
|
47
|
+
|
|
48
|
+
외부 의존성을 배제하기 위해 형태소 분석기 없이 토큰화한다. ko/en은 whitespace +
|
|
49
|
+
edge-punctuation strip, zh/ja는 Han/Kana 문자와 ASCII run을 token으로 삼는 fallback을
|
|
50
|
+
사용한다. raw token만으로도 burstiness와 MATTR 신호는 충분히 안정적이다.
|
|
51
|
+
|
|
52
|
+
### 절차
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
tokens = []
|
|
56
|
+
IF language is zh or ja:
|
|
57
|
+
FOR each Han/Kana character or ASCII alnum run IN sentence:
|
|
58
|
+
tokens.append(match)
|
|
59
|
+
ELSE:
|
|
60
|
+
FOR each whitespace-separated chunk IN sentence:
|
|
61
|
+
stripped = chunk with leading/trailing `\W` characters removed
|
|
62
|
+
IF stripped is non-empty:
|
|
63
|
+
tokens.append(stripped)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
- `\W` 는 정규식의 비단어 문자 클래스 (Unicode aware: 한글·영문 단어 문자는 보존)
|
|
67
|
+
- 내부 punctuation(예: `don't`, `좋은-도구`)은 token 일부로 유지한다
|
|
68
|
+
- 빈 문자열은 token 으로 인정하지 않는다
|
|
69
|
+
|
|
70
|
+
### 단위
|
|
71
|
+
|
|
72
|
+
| 언어 | 토큰 단위 | 예시 입력 | 토큰 |
|
|
73
|
+
|------|-----------|-----------|------|
|
|
74
|
+
| ko | 어절 (whitespace 기준) | `이 도구는 자동완성처럼 동작한다` | `[이, 도구는, 자동완성처럼, 동작한다]` |
|
|
75
|
+
| en | 단어 (whitespace 기준) | `The tool acts like autocomplete.` | `[The, tool, acts, like, autocomplete]` |
|
|
76
|
+
| zh | 한자 문자 | `工具提升写作` | `[工, 具, 提, 升, 写, 作]` |
|
|
77
|
+
| ja | 가나/한자 문자 | `道具が書く` | `[道, 具, が, 書, く]` |
|
|
78
|
+
|
|
79
|
+
> **주의:** 한국어는 어절 기준이므로 morpheme-level 분석과 다르다. `도구는`과 `도구가`는
|
|
80
|
+
> 서로 다른 토큰으로 취급된다. zh/ja 역시 형태소 단위가 아니라 문자 fallback이다. jieba,
|
|
81
|
+
> sudachi, mecab 같은 형태소 분석기는 배포 의존성과 설치 실패면을 늘리므로 기본 경로에서
|
|
82
|
+
> 제외한다.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## 3. Sentence Splitting
|
|
87
|
+
|
|
88
|
+
문장 분할은 단순 정규식만 사용한다. 약어·소수점 등 false split 가능성은 v1에서 감수한다.
|
|
89
|
+
|
|
90
|
+
### 규칙
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
split text on regex: [.!?。…]+\s+ OR \n+
|
|
94
|
+
trim whitespace
|
|
95
|
+
drop empty fragments
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
종결부호 후보:
|
|
99
|
+
- `.` (마침표)
|
|
100
|
+
- `!` (느낌표)
|
|
101
|
+
- `?` (물음표)
|
|
102
|
+
- `。` (전각 마침표 — 향후 ja/zh 확장 대비)
|
|
103
|
+
- `…` (말줄임표)
|
|
104
|
+
- 줄바꿈 `\n`
|
|
105
|
+
|
|
106
|
+
### 단락 분할
|
|
107
|
+
|
|
108
|
+
- 빈 줄(`\n\s*\n`) 기준으로 단락 분리
|
|
109
|
+
- 단락 내부 줄바꿈은 같은 단락의 문장 경계로 취급
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## 4. Burstiness Metric
|
|
114
|
+
|
|
115
|
+
문장 길이의 변동성을 측정한다. AI 텍스트는 문장 길이가 균질해지는 경향이 있다.
|
|
116
|
+
|
|
117
|
+
### 공식
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
sentence_token_counts = [len(tokens) for sentence in paragraph.sentences]
|
|
121
|
+
mean = sum(sentence_token_counts) / len(sentence_token_counts)
|
|
122
|
+
stddev = sqrt(sum((x - mean)^2) / len(sentence_token_counts)) # population stddev
|
|
123
|
+
burstiness_CV = stddev / mean
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
- `CV` = Coefficient of Variation (변동계수)
|
|
127
|
+
- mean 이 0 이면 정의되지 않음 → skip 처리
|
|
128
|
+
- population stddev 사용 (표본 보정 없음, 단순화)
|
|
129
|
+
|
|
130
|
+
### 밴드
|
|
131
|
+
|
|
132
|
+
| 밴드 | 임계값 | 해석 |
|
|
133
|
+
|------|--------|------|
|
|
134
|
+
| low | CV < 0.30 | 문장 길이가 균질함 — AI 의심 |
|
|
135
|
+
| mid | 0.30 ≤ CV ≤ 0.50 | 자연스러운 변동 |
|
|
136
|
+
| high | CV > 0.50 | 사람다운 큰 변동 |
|
|
137
|
+
|
|
138
|
+
임계값은 `.patina.default.yaml`의 `stylometry.burstiness.bands`에서 조정 가능하다.
|
|
139
|
+
|
|
140
|
+
> **v3.5.1 calibration:** 초기 임계값은 `0.25`였다. 외부 300 단락
|
|
141
|
+
> (HC3 ChatGPT 100 + HC3 human 100 + Wikipedia 100) 측정 후 `0.30`으로 상향 조정.
|
|
142
|
+
> 0.25에서는 AI catch rate 가 57%에 그쳐 v1 목표(70%) 미달이었고,
|
|
143
|
+
> 0.30에서 66%까지 상승. 0.35 이상은 Wikipedia false positive 가 34%+ 로
|
|
144
|
+
> 급증해 채택하지 않음. 자세한 sweep 결과는 §13 참조.
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## 5. TTR via MATTR
|
|
149
|
+
|
|
150
|
+
Type-Token Ratio (TTR)는 어휘 다양성 지표다. 텍스트 길이에 민감하므로 단순 TTR 대신
|
|
151
|
+
MATTR (Moving Average TTR)을 사용해 길이 의존성을 줄인다.
|
|
152
|
+
|
|
153
|
+
### 공식
|
|
154
|
+
|
|
155
|
+
```
|
|
156
|
+
window = 50 # tokens
|
|
157
|
+
lower_tokens = [token.lower() for token in paragraph_tokens] # no stemming, no lemmatization
|
|
158
|
+
IF len(lower_tokens) < window:
|
|
159
|
+
MATTR = len(set(lower_tokens)) / len(lower_tokens) # fall back to simple TTR
|
|
160
|
+
ELSE:
|
|
161
|
+
ratios = []
|
|
162
|
+
FOR i in 0 .. len(lower_tokens) - window:
|
|
163
|
+
slice = lower_tokens[i : i + window]
|
|
164
|
+
ratios.append(len(set(slice)) / window)
|
|
165
|
+
MATTR = sum(ratios) / len(ratios)
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
- token normalization: lowercase 만 적용 (stemming/lemmatization 없음)
|
|
169
|
+
- 한국어 어절 token은 lowercase가 거의 영향 없음 (영문 외래어만 영향)
|
|
170
|
+
- window=50은 문헌 권장값 (Covington & McFall 2010)
|
|
171
|
+
|
|
172
|
+
### 밴드
|
|
173
|
+
|
|
174
|
+
| 밴드 | 임계값 | 해석 |
|
|
175
|
+
|------|--------|------|
|
|
176
|
+
| low | MATTR < 0.55 | 어휘 반복 많음 — AI 의심 |
|
|
177
|
+
| mid | 0.55 ≤ MATTR ≤ 0.70 | 자연스러운 다양성 |
|
|
178
|
+
| high | MATTR > 0.70 | 풍부한 어휘 |
|
|
179
|
+
|
|
180
|
+
임계값은 `.patina.default.yaml`의 `stylometry.ttr.bands`에서 조정 가능하다.
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## 6. Hot Decision Rule
|
|
185
|
+
|
|
186
|
+
단락 수준 SUSPECT 판정은 단순한 OR 규칙으로 결정한다.
|
|
187
|
+
|
|
188
|
+
```
|
|
189
|
+
paragraph is SUSPECT iff
|
|
190
|
+
burstiness_band == "low" OR MATTR_band == "low"
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### 근거
|
|
194
|
+
|
|
195
|
+
- 두 신호 중 하나만 약해도 사람 글에서는 드물게 동시 발생한다
|
|
196
|
+
- AND 조건은 recall 이 너무 낮아 v1 acceptance criteria(AI 시드 7/10)를 만족하기 어렵다
|
|
197
|
+
- false positive 는 `FalsePositiveControl` 평가(자연 시드 ≤2/10)로 견제한다
|
|
198
|
+
|
|
199
|
+
### 임계값 재조정
|
|
200
|
+
|
|
201
|
+
v1 결과에서 false positive 가 한도(자연 시드 2/10)를 넘기면, `.patina.default.yaml`의
|
|
202
|
+
`stylometry.burstiness.bands.low` 또는 `stylometry.ttr.bands.low` 를 보수적으로 낮춰
|
|
203
|
+
재평가한다 (예: 0.30 → 0.27).
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## 7. Sentence Zoom Rule
|
|
208
|
+
|
|
209
|
+
hot 단락 내부에서 문장 수준 sub-flag 을 생성한다. 단락 전체를 재작성 대상으로
|
|
210
|
+
지정하지 않고, 가장 의심스러운 인접 문장 그룹만 가리키는 것이 목적이다.
|
|
211
|
+
|
|
212
|
+
### 규칙
|
|
213
|
+
|
|
214
|
+
```
|
|
215
|
+
similarity_threshold = 0.20 # ±20% token count
|
|
216
|
+
|
|
217
|
+
FOR each pair of adjacent sentences (S_i, S_{i+1}) in hot paragraph:
|
|
218
|
+
smaller = min(len(S_i.tokens), len(S_{i+1}.tokens))
|
|
219
|
+
larger = max(len(S_i.tokens), len(S_{i+1}.tokens))
|
|
220
|
+
IF smaller == 0:
|
|
221
|
+
continue
|
|
222
|
+
IF (larger - smaller) / smaller < similarity_threshold:
|
|
223
|
+
mark (S_i, S_{i+1}) as adjacent-similar pair
|
|
224
|
+
|
|
225
|
+
merge overlapping pairs into contiguous groups
|
|
226
|
+
emit groups of length ≥ 2 as sub-flags
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
- 기준은 토큰 수 차이 < 20% (i.e., 길이 비가 대략 1.0 ~ 1.20 범위)
|
|
230
|
+
- 단일 문장은 sub-flag 으로 emit 하지 않는다 (인접 유사 페어 필요)
|
|
231
|
+
- 임계값은 `stylometry.sentence_zoom.similarity_threshold` 에서 조정 가능
|
|
232
|
+
|
|
233
|
+
### 출력
|
|
234
|
+
|
|
235
|
+
각 sub-flag 은 `P{n}.S{m..k}` 범위로 표기한다. 예: `P2.S1-S3`.
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## 8. Skip Conditions
|
|
240
|
+
|
|
241
|
+
오버헤드 회피를 위해 짧은 텍스트는 통째로 skip 한다.
|
|
242
|
+
|
|
243
|
+
| 조건 | 동작 |
|
|
244
|
+
|------|------|
|
|
245
|
+
| 단락 수 ≤ 2 | 4.6단계 전체 skip — meta block 생략 |
|
|
246
|
+
| 전체 문장 수 ≤ 2 | 4.6단계 전체 skip — meta block 생략 |
|
|
247
|
+
| 단락 내 문장 수 < 2 | 해당 단락은 burstiness 계산 불가 → MATTR 만 평가 |
|
|
248
|
+
| 단락 내 토큰 수 = 0 | 해당 단락 skip |
|
|
249
|
+
| 언어가 `stylometry.languages` 에 없음 | 4.6단계 전체 skip (기본값은 ko/en/zh/ja 포함) |
|
|
250
|
+
|
|
251
|
+
임계값은 4.5단계 의미 앵커 추출의 skip 조건과 동일하다.
|
|
252
|
+
|
|
253
|
+
> **참고:** 4.6단계가 skip 되면 5a·5b 입력에 `<suspect-zones>` meta block이 포함되지 않는다.
|
|
254
|
+
> 이 경우 파이프라인은 정상 진행한다 (suspect-zone 정보 없이).
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## 9. LLM Delivery Format
|
|
259
|
+
|
|
260
|
+
분석 결과는 5a 입력 직전에 텍스트 상단에 삽입되는 meta block 과, 본문 내 단락 prefix
|
|
261
|
+
두 가지 형태로 LLM 에 전달된다.
|
|
262
|
+
|
|
263
|
+
### Meta Block
|
|
264
|
+
|
|
265
|
+
```
|
|
266
|
+
<suspect-zones lang="{ko|en}">
|
|
267
|
+
- P{n}: burstiness={float} ({low|mid|high}), MATTR={float} ({low|mid|high}) — {short reason}
|
|
268
|
+
- P{n}.S{m}: {short reason}
|
|
269
|
+
</suspect-zones>
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
규칙:
|
|
273
|
+
- hot 단락만 entry 로 emit (mid/high 단락은 생략)
|
|
274
|
+
- burstiness 와 MATTR 은 소수 둘째 자리까지 표기
|
|
275
|
+
- short reason 은 한국어 한 줄 (예: `문장 길이 균질`, `어휘 반복 다수`)
|
|
276
|
+
- 문장 sub-flag 은 별도 entry 로 추가
|
|
277
|
+
- meta block 전체는 사용자 출력에 노출하지 않는다 (anchor 와 동일 정책)
|
|
278
|
+
|
|
279
|
+
### Body Prefix
|
|
280
|
+
|
|
281
|
+
hot 단락 본문 첫머리에 토큰 prefix `«P{n} SUSPECT»` 를 삽입한다.
|
|
282
|
+
|
|
283
|
+
```
|
|
284
|
+
«P2 SUSPECT» 이 도구는 단순한 자동완성을 넘어선다. ...
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
- 이 prefix 는 5a/5b 처리 시점에 LLM 이 즉시 인지하도록 신호를 reinforce 한다
|
|
288
|
+
- 5c 자기검수 단계에서 prefix 와 meta block 모두 제거된 최종 출력을 생성한다
|
|
289
|
+
- 사용자 대면 출력에는 prefix 가 절대 남지 않아야 한다
|
|
290
|
+
|
|
291
|
+
### 5c 회귀 체크 (옵션)
|
|
292
|
+
|
|
293
|
+
5c 자기검수 단계에서 hot zone 들이 모두 처리되었는지 확인할 수 있다.
|
|
294
|
+
- 모든 hot 단락이 어떤 형태로든 재작성되었는지 (단락 비교)
|
|
295
|
+
- 처리되지 않은 hot zone 이 있으면 경고 (강제 재처리는 v1 범위 밖)
|
|
296
|
+
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
## 10. Worked Example
|
|
300
|
+
|
|
301
|
+
### 한국어 예시
|
|
302
|
+
|
|
303
|
+
입력 (단락 1개):
|
|
304
|
+
|
|
305
|
+
```
|
|
306
|
+
이 도구는 단순한 자동완성을 넘어선다. 이 도구는 사용자의 의도를 이해한다.
|
|
307
|
+
이 도구는 효율적인 협업을 가능하게 한다. 이 도구는 혁신적인 생산성을 제공한다.
|
|
308
|
+
이 도구는 다양한 언어를 지원한다.
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### Step-by-step
|
|
312
|
+
|
|
313
|
+
**Tokenize (어절 단위)**
|
|
314
|
+
|
|
315
|
+
| 문장 | 토큰 수 |
|
|
316
|
+
|------|---------|
|
|
317
|
+
| S1 | 5 |
|
|
318
|
+
| S2 | 5 |
|
|
319
|
+
| S3 | 5 |
|
|
320
|
+
| S4 | 5 |
|
|
321
|
+
| S5 | 5 |
|
|
322
|
+
|
|
323
|
+
**Burstiness**
|
|
324
|
+
|
|
325
|
+
```
|
|
326
|
+
mean = 5
|
|
327
|
+
stddev = 0
|
|
328
|
+
CV = 0 / 5 = 0.00
|
|
329
|
+
band = low (< 0.30)
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
**MATTR**
|
|
333
|
+
|
|
334
|
+
전체 토큰 수 = 25 < 50 → simple TTR fallback.
|
|
335
|
+
|
|
336
|
+
```
|
|
337
|
+
lower_tokens = [이, 도구는, 단순한, 자동완성을, 넘어선다, 이, 도구는, 사용자의,
|
|
338
|
+
의도를, 이해한다, 이, 도구는, 효율적인, 협업을, 가능하게, 한다,
|
|
339
|
+
이, 도구는, 혁신적인, 생산성을, 제공한다, 이, 도구는, 다양한,
|
|
340
|
+
언어를, 지원한다]
|
|
341
|
+
# 26 tokens
|
|
342
|
+
unique = {이, 도구는, 단순한, 자동완성을, 넘어선다, 사용자의, 의도를, 이해한다,
|
|
343
|
+
효율적인, 협업을, 가능하게, 한다, 혁신적인, 생산성을, 제공한다,
|
|
344
|
+
다양한, 언어를, 지원한다}
|
|
345
|
+
# 18 unique
|
|
346
|
+
TTR = 18 / 26 ≈ 0.69
|
|
347
|
+
band = mid (0.55 ≤ 0.69 ≤ 0.70)
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
**Hot Decision**
|
|
351
|
+
|
|
352
|
+
burstiness=low → **SUSPECT** (OR 조건 충족).
|
|
353
|
+
|
|
354
|
+
**Sentence Zoom**
|
|
355
|
+
|
|
356
|
+
모든 인접 문장이 동일한 토큰 수(5) → 차이 0% < 20% → S1-S5 전체가 한 그룹.
|
|
357
|
+
|
|
358
|
+
**Meta Block 출력**
|
|
359
|
+
|
|
360
|
+
```
|
|
361
|
+
<suspect-zones lang="ko">
|
|
362
|
+
- P1: burstiness=0.00 (low), MATTR=0.69 (mid) — 문장 길이 균질
|
|
363
|
+
- P1.S1-S5: 인접 문장 토큰 수 동일
|
|
364
|
+
</suspect-zones>
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
**Body Prefix**
|
|
368
|
+
|
|
369
|
+
```
|
|
370
|
+
«P1 SUSPECT» 이 도구는 단순한 자동완성을 넘어선다. 이 도구는 사용자의 의도를 이해한다.
|
|
371
|
+
이 도구는 효율적인 협업을 가능하게 한다. 이 도구는 혁신적인 생산성을 제공한다.
|
|
372
|
+
이 도구는 다양한 언어를 지원한다.
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### 영어 예시
|
|
376
|
+
|
|
377
|
+
입력 (단락 1개):
|
|
378
|
+
|
|
379
|
+
```
|
|
380
|
+
The tool is innovative. The tool is efficient. The tool is reliable.
|
|
381
|
+
The tool is scalable. The tool is essential.
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
**Tokenize (단어 단위)**
|
|
385
|
+
|
|
386
|
+
각 문장 4 토큰. 전체 토큰 수 = 20.
|
|
387
|
+
|
|
388
|
+
**Burstiness**
|
|
389
|
+
|
|
390
|
+
```
|
|
391
|
+
mean = 4, stddev = 0, CV = 0.00 → low
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
**MATTR**
|
|
395
|
+
|
|
396
|
+
전체 토큰 수 20 < 50 → simple TTR.
|
|
397
|
+
|
|
398
|
+
```
|
|
399
|
+
lower_tokens = [the, tool, is, innovative, the, tool, is, efficient, the, tool,
|
|
400
|
+
is, reliable, the, tool, is, scalable, the, tool, is, essential]
|
|
401
|
+
unique = {the, tool, is, innovative, efficient, reliable, scalable, essential}
|
|
402
|
+
# 8 unique
|
|
403
|
+
TTR = 8 / 20 = 0.40
|
|
404
|
+
band = low (< 0.55)
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
**Hot Decision**
|
|
408
|
+
|
|
409
|
+
burstiness=low AND MATTR=low → **SUSPECT** (둘 다 충족).
|
|
410
|
+
|
|
411
|
+
**Meta Block 출력**
|
|
412
|
+
|
|
413
|
+
```
|
|
414
|
+
<suspect-zones lang="en">
|
|
415
|
+
- P1: burstiness=0.00 (low), MATTR=0.40 (low) — uniform sentence length, low lexical diversity
|
|
416
|
+
- P1.S1-S5: identical token counts
|
|
417
|
+
</suspect-zones>
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
**Body Prefix**
|
|
421
|
+
|
|
422
|
+
```
|
|
423
|
+
«P1 SUSPECT» The tool is innovative. The tool is efficient. The tool is reliable.
|
|
424
|
+
The tool is scalable. The tool is essential.
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
---
|
|
428
|
+
|
|
429
|
+
## 11. Roadmap (v2+)
|
|
430
|
+
|
|
431
|
+
현재 기본 경로에서 의도적으로 제외했거나 ship 이후 후속 검토만 남은 항목.
|
|
432
|
+
|
|
433
|
+
**zh/ja decision:** 기본 경로는 char-token fallback으로 확정한다. go 조건은 "무의존성,
|
|
434
|
+
Node 18 호환, 문장 길이 CV/MATTR가 공백 없는 zh/ja 문장에서 0-token으로 붕괴하지 않음"이다.
|
|
435
|
+
jieba/sudachi/mecab 같은 형태소 의존성은 no-go다. go 조건을 다시 열려면 (1) pure JS 또는
|
|
436
|
+
optional dependency로 설치 실패가 없는 경로, (2) ko/en/zh/ja benchmark에서 false positive
|
|
437
|
+
증가가 허용 범위 내라는 근거, (3) 패키지 크기/속도 회귀가 문서화되어야 한다.
|
|
438
|
+
|
|
439
|
+
| 항목 | 설명 | 도입 후보 시점 |
|
|
440
|
+
|------|------|----------------|
|
|
441
|
+
| ~~n-gram redundancy~~ | ~~bi/trigram 반복도~~ — **dropped, §15 negative finding** | — |
|
|
442
|
+
| ~~AI-lexicon overlap~~ | ~~28-패턴 외 AI 특유 어구 사전 매칭~~ — **shipped v3.7, §16** | v3.7 |
|
|
443
|
+
| ~~zh/ja character fallback~~ | ~~Han/Kana character-token burstiness/TTR~~ — **shipped for 4.6** | current |
|
|
444
|
+
| Perplexity proxy | small LM 또는 cloze prompt 기반 token-level surprise | v4 후보 |
|
|
445
|
+
| Function-word distribution | 기능어 빈도 분포 차이 (Mosteller & Wallace 류) | v4 후보 |
|
|
446
|
+
| GPTZero / Originality 연동 | 외부 detector API 결과를 hot 신호로 합산 | v4+ 후보 |
|
|
447
|
+
| 형태소 분석 기반 ko 토큰화 | 어절 → 형태소 단위로 정밀화 | v2+ |
|
|
448
|
+
| zh/ja 형태소 분석 통합 | 단어 경계 처리; 위 go/no-go 충족 시에만 재검토 | v4+ 후보 |
|
|
449
|
+
| 사용자 idiolect 학습 | 개인 작성 스타일 학습 후 false positive 억제 | 별도 트랙 |
|
|
450
|
+
| 정량 라벨 데이터셋 | 언어별 50+ 라벨 데이터 기반 임계값 튜닝 | v1 결과 후 검토 |
|
|
451
|
+
|
|
452
|
+
---
|
|
453
|
+
|
|
454
|
+
## 12. Known Limitations
|
|
455
|
+
|
|
456
|
+
- **Korean morphology coarseness**: 어절 단위 토큰화는 morpheme-level 분석과 다르다.
|
|
457
|
+
같은 명사의 조사 변형(`도구는`, `도구가`, `도구를`)을 서로 다른 token 으로 취급하므로
|
|
458
|
+
MATTR 이 실제보다 약간 높게 나올 수 있다.
|
|
459
|
+
- **Short text noise**: 단락 ≤2 또는 문장 ≤2 인 텍스트는 통계적으로 신뢰할 수 없어 skip
|
|
460
|
+
한다. 이 경우 4.6단계 전체가 비활성화된다.
|
|
461
|
+
- **Window=50 fallback**: MATTR window 보다 짧은 단락은 simple TTR 로 fallback 하므로
|
|
462
|
+
길이 의존 편향이 일부 남는다. 단락 비교 시 길이 차가 크면 신중히 해석한다.
|
|
463
|
+
- **LLM non-determinism**: 통계 계산 자체는 결정론적이나, 5a/5b 가 hot 정보를 어떻게
|
|
464
|
+
활용할지는 LLM 의 판단에 달려 있다. acceptance criteria 는 hot 표시 정확도까지만 보장한다.
|
|
465
|
+
- **No external detector**: v1 은 외부 detector API(GPTZero 등)와 통합하지 않는다.
|
|
466
|
+
외부 신호는 v3 후보 로드맵 항목이다.
|
|
467
|
+
- **False split**: 약어(`Mr.`, `e.g.`) 와 소수점(`3.14`) 같은 종결부호 오인식을 v1 에서
|
|
468
|
+
감수한다. 시드 평가에서 false positive 비율이 한도를 넘기면 v1.1 에서 보강한다.
|
|
469
|
+
- **Language scope**: 4.6 stylometry는 ko/en/zh/ja를 지원한다. zh/ja는 형태소가 아니라
|
|
470
|
+
character-token fallback이므로 MATTR 해석은 보수적으로만 사용한다. 4.7 lexicon은
|
|
471
|
+
curated 사전이 있는 en/ko만 기본 지원한다.
|
|
472
|
+
|
|
473
|
+
---
|
|
474
|
+
|
|
475
|
+
## 13. Calibration Appendix (v3.5.1)
|
|
476
|
+
|
|
477
|
+
v3.5.0 자체 시드 평가는 만점이었으나(AI 10/10, 자연 0/10), 외부 데이터에서는 신호가 약했다. 시드 픽스처를 작성한 측과 측정 알고리즘이 같았기 때문이다(자기 일관성). 이 절은 v3.5.1 calibration 의 근거 데이터다.
|
|
478
|
+
|
|
479
|
+
### 외부 측정 (300 단락)
|
|
480
|
+
|
|
481
|
+
| Source | n | hot rate (v3.5.0, 0.25) | hot rate (v3.5.1, 0.30) | CV median |
|
|
482
|
+
|--------|---|--------------------------|--------------------------|-----------|
|
|
483
|
+
| HC3 ChatGPT 답변 (en) | 100 | 57.0% | 66.0% | 0.220 |
|
|
484
|
+
| HC3 human 답변 (en) | 100 | 8.0% | 12.0% | 0.511 |
|
|
485
|
+
| Wikipedia 도입 단락 (en) | 100 | 19.0% | 23.0% | 0.386 |
|
|
486
|
+
|
|
487
|
+
### 임계값 sweep 핵심 결과
|
|
488
|
+
|
|
489
|
+
burstiness × MATTR 35조합을 모두 측정. **AI ≥70% AND max FP ≤20% 동시 만족하는 조합은 0개.** Pareto frontier 상위:
|
|
490
|
+
|
|
491
|
+
| burst | mattr | AI hot | HC3 FP | Wiki FP | utility |
|
|
492
|
+
|-------|-------|--------|--------|---------|---------|
|
|
493
|
+
| 0.25 | 0.55 (v3.5.0) | 57% | 8% | 19% | 38 |
|
|
494
|
+
| **0.30** | **0.55 (v3.5.1)** | **66%** | **12%** | **23%** | **43** |
|
|
495
|
+
| 0.35 | 0.55 | 73% | 22% | 34% | 39 |
|
|
496
|
+
| 0.40 | 0.55 | 84% | 35% | 57% | 27 |
|
|
497
|
+
|
|
498
|
+
v3.5.1 은 utility 최대점. 0.35 이상은 Wikipedia FP 가 급증해 백과사전체 자연 텍스트를 과도하게 hot 으로 표시.
|
|
499
|
+
|
|
500
|
+
### MATTR 의 약한 변별력 — 정직한 한계
|
|
501
|
+
|
|
502
|
+
MATTR 임계값을 `0.55 → 0.78` 로 광범위하게 sweep 해도 AI 와 사람의 발화 빈도 차이가 거의 없었다.
|
|
503
|
+
|
|
504
|
+
| MATTR threshold | AI MATTR-only fires | Human MATTR-only fires |
|
|
505
|
+
|-----------------|---------------------|------------------------|
|
|
506
|
+
| 0.65 | 2/100 | 3/100 |
|
|
507
|
+
| 0.70 | 6/100 | 5/100 |
|
|
508
|
+
| 0.75 | 16/100 | 19/100 |
|
|
509
|
+
|
|
510
|
+
MATTR 0.75 에서는 사람 텍스트가 오히려 더 자주 hot 발화. 자유 산문에서는 어휘 다양성이 AI/사람 변별 신호로 약하다는 의미. v3.5.1 은 **MATTR 임계값을 0.55 로 유지**한다 (실질적으로 거의 발화하지 않는 안전 신호로 보존; 임계값 상향은 false positive 만 늘림). 후속 §15 에서 n-gram 반복도도 같은 한계를 보임 — 단순 redundancy 신호 추가만으로는 Pareto 벽을 못 뚫는다.
|
|
511
|
+
|
|
512
|
+
### Wikipedia 의 register 문제
|
|
513
|
+
|
|
514
|
+
Wikipedia 도입 단락은 백과사전체 register 라 자연스럽게 균질한 문장 길이를 가진다(CV median 0.386). v3.5.1 에서 23% false positive 는 v1 acceptance(≤20%) 를 살짝 위반하나 register issue 에서 비롯된 구조적 한계로 본다. 단순 임계값 조정으로는 해소되지 않으며, §15 에서 확인했듯 n-gram redundancy 도 도움이 되지 않는다 — 백과사전체 균질성과 AI 균질성을 분리하려면 perplexity 또는 AI-lexicon 같은 다른 축의 신호가 필요하다.
|
|
515
|
+
|
|
516
|
+
### 운용상 권고
|
|
517
|
+
|
|
518
|
+
v3.5.1 은 advisory marker 로 사용하라. 패턴 카탈로그가 명명한 28 개 신호의 **보조 입력**이며, 단독 결정 신호로 쓰기엔 catch rate(66%) 가 부족하다. 단락이 hot 표시되면 LLM 이 우선 검토하고, 표시되지 않더라도 패턴 단계가 정상 작동한다.
|
|
519
|
+
|
|
520
|
+
### 외부 검증 재현
|
|
521
|
+
|
|
522
|
+
`.omc/research/eval_external_v2.py` 로 측정, `.omc/research/threshold_sweep.py` 로 sweep 수행. raw 결과는 `.omc/research/external_results_v2.json`. 재실행 시 HuggingFace `Hello-SimpleAI/HC3` + `wikimedia/wikipedia` 20231101.en 다운로드 발생.
|
|
523
|
+
|
|
524
|
+
---
|
|
525
|
+
|
|
526
|
+
## 14. Korean External Validation (post-v3.5.1)
|
|
527
|
+
|
|
528
|
+
v3.5.1 calibration 은 영어 데이터(HC3 + Wikipedia)만 사용했다. 이후 NamuWiki 100 단락 (`heegyu/namuwiki`, CC-BY-NC-SA 2.0 KR) 으로 한국어 자연 텍스트 검증을 수행했다.
|
|
529
|
+
|
|
530
|
+
### 결과
|
|
531
|
+
|
|
532
|
+
| Source | n | CV median | MATTR median | hot rate (v3.5.1) |
|
|
533
|
+
|--------|---|-----------|--------------|-------------------|
|
|
534
|
+
| NamuWiki (ko 자연) | 100 | 0.592 | 0.941 | **11.0%** |
|
|
535
|
+
| HC3 human (en 자연) | 100 | 0.511 | 0.792 | 12.0% |
|
|
536
|
+
| Wikipedia (en 자연) | 100 | 0.386 | 0.770 | 23.0% |
|
|
537
|
+
|
|
538
|
+
NamuWiki 한국어 단락의 burstiness CV 중앙값(0.592)은 영어 자연 텍스트보다 더 높고, hot rate 11% 는 v1 자연 대조 한도(≤20%) 를 여유롭게 통과한다. **v3.5.1 임계값이 한국어에서도 그대로 유효함을 확인.**
|
|
539
|
+
|
|
540
|
+
### MATTR 의 한국어 특이성
|
|
541
|
+
|
|
542
|
+
MATTR median 0.941 — 영어 자연 텍스트(0.77~0.79) 보다 훨씬 높다. 이유: 어절 단위 토큰화가 `도구는`/`도구가`/`도구를` 같은 조사 변형을 별개 토큰으로 취급해 어휘 다양성이 인위적으로 부풀려진다. §12 Known Limitations 에서 명시한 한계가 데이터로 확인됨. 한국어에서 MATTR 신호는 사실상 발화하지 않는다 (NamuWiki 100 단락 중 0건 hot).
|
|
543
|
+
|
|
544
|
+
### 한국어 AI 텍스트 측정 (post-v3.7.0)
|
|
545
|
+
|
|
546
|
+
자유 배포 한국어 AI 코퍼스가 없어 NamuWiki 100 토픽을 시드로 Claude (claude -p, opus) 에 paired 한국어 단락 100건을 자가 생성한 뒤 측정했다 (`.omc/research/ko_ai_robust.py`).
|
|
547
|
+
|
|
548
|
+
| 지표 | ko/AI (Claude 생성, n=100) | ko/human (NamuWiki, n=100) | gap |
|
|
549
|
+
|------|---------------------------|----------------------------|-----|
|
|
550
|
+
| CV median | 0.200 | 0.592 | — |
|
|
551
|
+
| MATTR median | 0.982 | 0.941 | — |
|
|
552
|
+
| lexicon density median | 0.00 | n/a | — |
|
|
553
|
+
| **hot rate (v3.5.1: burst+MATTR)** | **82.0%** | 11.0% | **+71pp** |
|
|
554
|
+
| **hot rate (v3.7.0: 3-signal OR)** | **83.0%** | 13.0% | **+70pp** |
|
|
555
|
+
|
|
556
|
+
**해석:**
|
|
557
|
+
- 한국어 AI catch rate (82~83%) 가 영어 AI catch rate (76%, HC3) 보다 높다. Claude 가 생성한 한국어 백과사전체 산문이 균질한 문장 길이를 더 강하게 보이는 경향.
|
|
558
|
+
- v3.5.1 burstiness 임계값이 한국어에서 그대로 잘 작동 — 별도 calibration 불필요.
|
|
559
|
+
- MATTR 은 한국어에서 0/100 발화 (median 0.982). 어절 토큰화 한계로 사실상 죽은 신호.
|
|
560
|
+
- lexicon 은 +1pp 보조 (9/100 발화). 영어 (+10pp) 보다 약함 — Korean lexicon (90 entries) 의 효과는 제한적이며, 이후 사용자 corpus 측정으로 재조정 여지 있음.
|
|
561
|
+
- ko/human FP (13%) vs ko/AI hot (83%) 의 gap 70pp 는 영어 gap (52pp) 보다도 크다. 한국어에서 stylometric 신호가 강하게 동작.
|
|
562
|
+
|
|
563
|
+
**한계:**
|
|
564
|
+
- AI 측 데이터가 Claude 자가 생성이라 OOM (out-of-our-model) AI 시스템(GPT-4, Gemini, Llama 등) 검증은 별도. v4 에서 멀티모델 paired corpus 로 확장 예정.
|
|
565
|
+
- NamuWiki 토픽이 알파벳 가나다순 첫 100건이라 도메인 편향 가능성 (라이트노벨 캐릭터, 가면라이더 시리즈가 다소 많음). 도메인 stratified sample 은 v4 후보.
|
|
566
|
+
|
|
567
|
+
---
|
|
568
|
+
|
|
569
|
+
## 15. v3.6 n-gram Repetition — Negative Finding
|
|
570
|
+
|
|
571
|
+
§13 Pareto 벽을 뚫기 위한 v2 후보였던 n-gram 반복도 신호를 동일 코퍼스(400 단락 = HC3 AI/human + Wikipedia + NamuWiki) 에 추가 측정했다.
|
|
572
|
+
|
|
573
|
+
### 측정값 분포
|
|
574
|
+
|
|
575
|
+
| Source | bigram redundancy median | trigram redundancy median |
|
|
576
|
+
|--------|--------------------------|---------------------------|
|
|
577
|
+
| HC3 ChatGPT | 0.051 | 0.000 |
|
|
578
|
+
| HC3 human | **0.071** | 0.012 |
|
|
579
|
+
| Wikipedia | 0.046 | 0.000 |
|
|
580
|
+
| NamuWiki | 0.000 | 0.000 |
|
|
581
|
+
|
|
582
|
+
### 핵심 발견
|
|
583
|
+
|
|
584
|
+
**HC3 human 의 bigram redundancy (0.071) 가 HC3 AI (0.051) 보다 오히려 높다.** Q&A 형식의 사람 답변이 ChatGPT 답변보다 구조적으로 더 반복적이라는 의미. n-gram 반복도는 AI/사람 변별 신호로 쓸모가 없다 — 적어도 단순 redundancy 정의(`1 - unique/total`) 로는 그렇다.
|
|
585
|
+
|
|
586
|
+
trigram 임계값 sweep 에서도 AI catch 와 human FP 가 거의 같은 비율로 함께 오르내림 (gap 변동 ±2pp). 신호 추가 효과 없음.
|
|
587
|
+
|
|
588
|
+
### 운용 결정
|
|
589
|
+
|
|
590
|
+
n-gram 반복도를 ship 하지 않는다. v3.5.1 상태 유지. roadmap 에서 n-gram 항목을 제거하고, 다음 후보로 이동:
|
|
591
|
+
|
|
592
|
+
- **Perplexity proxy** (token-level surprise) — 구조적 균질성과 다른 축의 신호. AI 텍스트는 token 별 conditional probability 가 일관되게 높음 (LM 이 다음 토큰을 잘 예측). 구현: small LM (예: GPT-2 small) 또는 cloze prompt 기반 추정. v4 후보.
|
|
593
|
+
- **AI-lexicon overlap** — 28-패턴이 명명하지 못한 AI 특유 어구 사전("transformative", "cutting-edge", "leveraging" 류) 와의 단순 매칭. 패턴 카탈로그의 통계적 보강. v3.7 후보.
|
|
594
|
+
- **Function-word distribution** — AI 와 사람의 of/in/the/는/이/를 등 기능어 빈도 차이. Stylometric authorship attribution 문헌 (Mosteller & Wallace 1964 연속) 의 고전 신호. v4 후보.
|
|
595
|
+
|
|
596
|
+
### 재현
|
|
597
|
+
|
|
598
|
+
`.omc/research/v3_6_ngram_probe.py` 로 측정, raw 결과는 `.omc/research/v3_6_results.json`. HuggingFace `Hello-SimpleAI/HC3` + `wikimedia/wikipedia` + `heegyu/namuwiki` 다운로드 발생.
|
|
599
|
+
|
|
600
|
+
---
|
|
601
|
+
|
|
602
|
+
## 16. AI-Lexicon Overlap Signal (v3.7)
|
|
603
|
+
|
|
604
|
+
§13 calibration 과 §15 n-gram 부정 결과 모두 같은 결론을 가리켰다 — 구조적 균질성(burstiness, MATTR, n-gram redundancy)은 백과사전체와 AI를 충분히 분리하지 못한다. 다른 축의 신호가 필요했다. v3.7은 28-패턴 카탈로그가 명시적으로 잡지 못하는 AI 특유 어구를 평면 사전(flat dictionary)으로 추가 매칭해 어휘 축의 신호를 도입한다.
|
|
605
|
+
|
|
606
|
+
### 알고리즘
|
|
607
|
+
|
|
608
|
+
```
|
|
609
|
+
For paragraph P with tokens T:
|
|
610
|
+
matches = number of lexicon entries that appear in P
|
|
611
|
+
(case-insensitive, whole-word for "Strict matches",
|
|
612
|
+
substring for "Multi-word phrases")
|
|
613
|
+
density = matches / len(T) * 1000 # matches per 1000 tokens
|
|
614
|
+
|
|
615
|
+
hot iff density > threshold
|
|
616
|
+
```
|
|
617
|
+
|
|
618
|
+
기본 threshold = `2.0` (1,000 토큰당 2회). `lexicon.density_threshold`로 설정 가능.
|
|
619
|
+
|
|
620
|
+
### 단락 hot 결정 규칙 (3-signal OR)
|
|
621
|
+
|
|
622
|
+
```
|
|
623
|
+
paragraph is SUSPECT iff
|
|
624
|
+
burstiness_band == "low" OR MATTR_band == "low" OR lexicon_density > threshold
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
§6의 2-signal OR 규칙을 3-signal OR로 확장한다. burstiness/MATTR는 분포적 신호, lexicon은 어휘적 신호 — 다른 축이라 OR 결합 시 둘 다 합산 효과를 낸다.
|
|
628
|
+
|
|
629
|
+
### 사전 파일
|
|
630
|
+
|
|
631
|
+
- `lexicon/ai-en.md` — 영어 50개 strict + 58개 phrase = 108 entries
|
|
632
|
+
- `lexicon/ai-ko.md` — 한국어 41개 strict + 49개 phrase = 90 entries
|
|
633
|
+
|
|
634
|
+
탐색은 `Glob lexicon/ai-{lang}.md`로 자동. zh/ja는 4.6 stylometry만 기본 지원하고
|
|
635
|
+
4.7 lexicon은 curated 사전이 없어 기본 미지원(`lexicon.languages` 기본 `[en, ko]`).
|
|
636
|
+
사용자가 `custom/lexicon/ai-{lang}.md` 를 두면 우선 로드.
|
|
637
|
+
|
|
638
|
+
### 매칭 정책
|
|
639
|
+
|
|
640
|
+
- **Strict matches** (Markdown `## Strict matches` 섹션): 대소문자 무시 whole-word 매칭. 한국어 어절(예: `자리매김`)은 substring으로 근사 — Korean punctuation/space로 분리되므로 false attach 위험이 낮다
|
|
641
|
+
- **Multi-word phrases** (Markdown `## Multi-word phrases` 섹션): 대소문자 무시 substring 매칭. `~` 가 포함된 한국어 phrase(예: `~의 지평을 넓히다`)는 `~` 을 wildcard 로 취급 (`.{0,40}`)
|
|
642
|
+
- 한 paragraph 안에서 같은 entry 가 여러 번 나와도 1로 계산 (entry 단위 카운트)
|
|
643
|
+
|
|
644
|
+
### LLM 전달 형식 확장
|
|
645
|
+
|
|
646
|
+
기존 `<suspect-zones>` meta block을 lexicon hit 정보로 확장한다.
|
|
647
|
+
|
|
648
|
+
```
|
|
649
|
+
<suspect-zones lang="ko">
|
|
650
|
+
- P1: burstiness=0.18 (low) — 문장 길이 균질
|
|
651
|
+
- P2: lexicon_density=4.2/1000 — AI-lexicon hits: "혁신적인 접근, 패러다임 전환, 시너지"
|
|
652
|
+
- P3: burstiness=0.22 (low), lexicon_density=3.1/1000 — 균질 + AI 어휘 다수
|
|
653
|
+
</suspect-zones>
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
규칙:
|
|
657
|
+
- lexicon hit이 있는 단락은 매칭된 entry 중 최대 5개를 짧은 인용으로 표기
|
|
658
|
+
- burstiness/MATTR hot 과 lexicon hot 이 동시 발화하면 한 줄에 합쳐서 표기
|
|
659
|
+
- meta block 전체는 사용자 출력에 노출하지 않는다 (§9 Anchor 정책과 동일)
|
|
660
|
+
|
|
661
|
+
### Body Prefix
|
|
662
|
+
|
|
663
|
+
§9의 `«P{n} SUSPECT»` prefix 는 lexicon-only hot 단락에도 동일하게 적용한다. LLM 입력에는 hot의 발화 사유가 무엇이든 동일한 신호로 도착한다.
|
|
664
|
+
|
|
665
|
+
### v3.7 Calibration (외부 400 단락)
|
|
666
|
+
|
|
667
|
+
같은 코퍼스(HC3 ChatGPT/human + Wikipedia + NamuWiki)로 `density_threshold` 0.5–5.0 범위 sweep.
|
|
668
|
+
|
|
669
|
+
| Source | n | v3.5.1 hot | v3.7 hot (thr=2.0) |
|
|
670
|
+
|--------|---|------------|---------------------|
|
|
671
|
+
| HC3 ChatGPT (en AI) | 100 | **66.0%** | **76.0%** |
|
|
672
|
+
| HC3 human (en) | 100 | 12.0% | 19.0% |
|
|
673
|
+
| Wikipedia (en) | 100 | 23.0% | **25.0%** |
|
|
674
|
+
| NamuWiki (ko) | 100 | 11.0% | 13.0% |
|
|
675
|
+
|
|
676
|
+
Pareto frontier (3-signal OR, threshold sweep):
|
|
677
|
+
|
|
678
|
+
| lex_thr | AI% | HC3% | Wiki% | Namu% | utility |
|
|
679
|
+
|---------|-----|------|-------|-------|---------|
|
|
680
|
+
| 0.5–2.5 (plateau) | 76.0 | 19.0 | 25.0 | 13.0 | +51 |
|
|
681
|
+
| 3.0 | 76.0 | 17.0 | 25.0 | 13.0 | +51 |
|
|
682
|
+
| 5.0 | 76.0 | 15.0 | 25.0 | 13.0 | +51 |
|
|
683
|
+
| v3.5.1 baseline | 66.0 | 12.0 | 23.0 | 11.0 | +43 |
|
|
684
|
+
|
|
685
|
+
### Acceptance criteria — 충족
|
|
686
|
+
|
|
687
|
+
| 기준 | 목표 | 실측 |
|
|
688
|
+
|------|------|------|
|
|
689
|
+
| AI catch (HC3 ChatGPT) | ≥ 75% | **76.0%** ✓ |
|
|
690
|
+
| max human FP | ≤ 25% | **25.0%** ✓ (Wikipedia, 경계) |
|
|
691
|
+
| NamuWiki 회귀 | v3.5.1 +5pp 이내 | 11% → 13% (+2pp) ✓ |
|
|
692
|
+
|
|
693
|
+
3개 기준 모두 충족 → v3.7 ship 결정.
|
|
694
|
+
|
|
695
|
+
### Threshold 선택 근거
|
|
696
|
+
|
|
697
|
+
`density_threshold = 2.0` 채택. 0.5–5.0 plateau 구간 어디에서도 동일한 catch/FP 가 나오므로 사양 기본값(2.0) 을 사용한다. 운용 의미: "1,000 토큰당 AI lexicon entry 가 2개 초과로 나타나면 단락 의심". 이는 사양 §3 Recommendation 과 일치한다.
|
|
698
|
+
|
|
699
|
+
### Calibration drop list
|
|
700
|
+
|
|
701
|
+
초기 lexicon 후보 중 다음 entry 들은 sweep 결과 사람 텍스트(특히 Wikipedia 백과사전체) 발화율이 AI 발화율과 같거나 높아 제외했다.
|
|
702
|
+
|
|
703
|
+
- Strict (drop): `intersection`, `principles`, `mindset`, `iterative`, `responsible`, `methodologies`, `redefine`, `accessible`, `equitable`
|
|
704
|
+
- Phrases (drop): `one of the most`, `in conjunction with`, `the power of`
|
|
705
|
+
|
|
706
|
+
이 entry 들은 학술/전문 prose 에서 자연스럽게 등장하는 단어로, AI 의 promotional 어휘가 아니라 register 의 일부였다. 재추가 시 반드시 `.omc/research/v3_7_lexicon_eval.py` 로 회귀 측정.
|
|
707
|
+
|
|
708
|
+
### 28-패턴 카탈로그와의 분리
|
|
709
|
+
|
|
710
|
+
lexicon 은 다음 카탈로그 항목과 **중복되지 않도록** 큐레이션됐다:
|
|
711
|
+
|
|
712
|
+
- `en-language.md` Pattern 7 (delve, tapestry, multifaceted, leverage 등 30개) — 카탈로그가 이미 다룸
|
|
713
|
+
- `en-content.md` Pattern 1, 4 (groundbreaking, transformative — paradigm shift 등 promotional 형용사) — 카탈로그가 이미 다룸
|
|
714
|
+
- `ko-language.md` Pattern 7, 8 (다양한, 활발한, 혁신적인, ~적 접미사 등) — 카탈로그가 이미 다룸
|
|
715
|
+
- `ko-style.md` Pattern 13, 18 (이를 통해, 도모하다, 본 사업 등) — 카탈로그가 이미 다룸
|
|
716
|
+
|
|
717
|
+
lexicon 의 50+58/41+49 entry 는 위 패턴들이 명시적으로 잡지 않는 영역(modal scaffolding, 추상명사, 의례적 도입/마무리 phrase) 만 추렸다.
|
|
718
|
+
|
|
719
|
+
### 한국어 lexicon 의 검증 결과 + v3.8.0 재큐레이션
|
|
720
|
+
|
|
721
|
+
**v3.7.0 (90 entries) 측정**: ko/AI 100 단락(Claude 자가 생성) 에서 한국어 lexicon 발화는 9/100. burstiness 신호(82/100) 위에 +1 단락만 추가. 영어 lexicon (HC3 ChatGPT 에서 +10pp) 대비 한국어 효과 미미.
|
|
722
|
+
|
|
723
|
+
**v3.8.0 (102 entries) 재큐레이션**:
|
|
724
|
+
|
|
725
|
+
ko/AI 코퍼스 vs NamuWiki human 차별 빈도 마이닝(`.omc/research/v3_8_ko_lexicon_mine.py`) 으로 AI 측 doc-freq ≥4×, ratio ≥4.0 인 phrase 발굴. 도메인 아티팩트(`가면라이더`, `한국`, `시리즈` 등) 제외하고 register marker 12개 추가:
|
|
726
|
+
|
|
727
|
+
- Strict (8개): `평가된다`, `꼽힌다`, `가리킨다`, `사례로`, `다수의`, `알려져`, `일컬어진다`, `평가받다`
|
|
728
|
+
- Phrases (4개): `가운데 하나로`, `자리 잡았다`, `알려져 있다`, `~의 사례로`
|
|
729
|
+
|
|
730
|
+
**v3.8.0 결과** (동일 코퍼스 재측정, `.omc/research/v3_8_remeasure.py`):
|
|
731
|
+
|
|
732
|
+
| Source | n | v3.7.0 hot | v3.8.0 hot | Δ | lex fires |
|
|
733
|
+
|--------|---|-----------:|-----------:|---:|---------:|
|
|
734
|
+
| ko_ai (Claude) | 100 | 83% | **91%** | **+8pp** | 9 → 52 |
|
|
735
|
+
| namu_human FP | 100 | 13% | **13%** | **0pp** | 0 → 2 |
|
|
736
|
+
| hc3_ai (en) | 100 | 76% | 76% | — | (en lex unchanged) |
|
|
737
|
+
| hc3_human FP | 100 | 19% | 19% | — | — |
|
|
738
|
+
| wiki_human FP | 100 | 25% | 25% | — | — |
|
|
739
|
+
|
|
740
|
+
**Clean Pareto 개선** — Korean human FP 0pp regression, AI catch +8pp. 신규 entry 가 AI 측에서 강하게 (52/100 fires) 사람 측에서 약하게 (2/100 fires) 발화. 한국어 catch rate 91% 는 시스템 전체에서 가장 강한 신호.
|
|
741
|
+
|
|
742
|
+
**근본 원인 — 왜 v3.7.0 한국어 lexicon 은 약했는가**:
|
|
743
|
+
|
|
744
|
+
v3.7.0 lexicon 큐레이션은 저자(Track B)의 한국어 AI 직관에 의존했다. 실제 Claude 출력은 그 직관과 어휘 분포가 달랐다(`시너지 효과`, `패러다임의 전환` 같은 비즈니스 외래어를 별로 안 씀; 대신 백과사전체 passive 종결 `평가된다`, `가리킨다`, `꼽힌다` 를 자주 씀). v3.8.0 마이닝은 직관 대신 paired corpus 의 통계적 차별로 발굴 — 더 실증적.
|
|
745
|
+
|
|
746
|
+
**v3.8 운용 권고**: 한국어에서 lexicon 이 burstiness 와 함께 의미있는 보조 신호로 작동하기 시작했다(91%). 영어 측은 v3.7.0 시점의 직관 큐레이션이 그대로 잘 작동(+10pp 기여). 영어 lexicon 도 향후 paired corpus 마이닝으로 재검토 가능 — v4 후보.
|
|
747
|
+
|
|
748
|
+
### 운용상 권고
|
|
749
|
+
|
|
750
|
+
v3.7 도 advisory marker 다. 28-패턴 카탈로그의 보조 입력이며, 단독 결정 신호로 쓰기엔 catch rate(76%) 가 여전히 부족하다. 단락이 hot 표시되면 LLM 이 우선 검토하고, 표시되지 않더라도 패턴 단계가 정상 작동한다. v3.5.1 운용 권고와 동일.
|
|
751
|
+
|
|
752
|
+
### 재현
|
|
753
|
+
|
|
754
|
+
`.omc/research/v3_7_lexicon_eval.py` 로 측정. raw 결과는 `.omc/research/v3_7_results.json` (paragraph-level: text + cv + mattr + lex_density + lex_hits). 재실행 시 HuggingFace `Hello-SimpleAI/HC3` + `wikimedia/wikipedia` 20231101.en + `heegyu/namuwiki` 다운로드 발생.
|