feat: v1.1 분류 체계 + 소셜 프리뷰 썸네일

v1.1 (ai-tell-taxonomy.md):
- 실전 1호(AI 전략 칼럼) 자기 재감사 결과 재현 2회+ 신규 패턴 7건 승격
- A-15: 추상 주어 + 만능 동사 ("X가 Y를 보여준다")
- C-7: 문단 문두 "먼저·반면·결국" 3단 공식
- C-8: 대칭 대구 "A인가, B인가" 반복
- D-5: 의인화된 추상 주어 ("두 지능의 충돌")
- D-6: 완결 공식형 결말 "~할 때입니다"
- F-5: "~적 N" 복합 추상어 체인
- H-4: 재정의 접속사 "즉" 남발

썸네일:
- assets/social-preview.png — 1280x640 소셜 프리뷰 이미지
- scripts/make_thumbnail.py — Pretendard 기반 PIL 생성 스크립트
- README.md 상단에 미리보기 추가
This commit is contained in:
epoko77-ai 2026-04-24 22:45:48 +09:00
commit f25ee641ab
4 changed files with 226 additions and 12 deletions

View file

@ -1,4 +1,4 @@
# AI 한글 티 분류 체계 v1 (Korean AI-Tell Taxonomy)
# AI 한글 티 분류 체계 v1.1 (Korean AI-Tell Taxonomy)
LLM(ChatGPT·Claude·Gemini 등)이 생성한 한글 글에서 반복적으로 관찰되는 "AI 티" 패턴을 10개 대분류 × 서브 패턴으로 정리한다. 탐지기·윤문가·리뷰어가 공유하는 단일 진실 원천(SSOT). 각 패턴마다 (1) 정의, (2) 시그니처 예문, (3) 심각도(S1 결정적 / S2 강함 / S3 약함), (4) 윤문 처방을 제공한다.
@ -91,6 +91,11 @@ J. 시각 장식 남용
- 예: "그는 보고했다. **그리고** 자리에 앉았다." → "그는 보고하고 자리에 앉았다."
- 처방: "-고", "-며", "-면서" 등 연결어미로 압축.
### A-15. 추상 주어 + 만능 동사 [S2] · v1.1 신규
- 패턴: 영어 `The X shows / provides / brings Y` 직역. 주어가 사건·현상이고 술어가 "보여준다·제공한다·가져온다·시사한다"
- 예: "DeepSeek-V4**의 등장은** ~을 **보여줍니다**" / "이 전략**은** 지형**을 흔들고 있습니다**" / "X**는** Y**를 제공합니다**"
- 처방: 주어를 행위자(사람·팀·회사)로 돌리거나, 주어·동사 자체를 없애고 직접 서술. "DeepSeek는 ~ 원칙을 이렇게 증명했다" 식.
---
## B. 영어 인용·용어 과다 — S2
@ -141,6 +146,16 @@ J. 시각 장식 남용
- 패턴: 모든 섹션 헤딩 직후 "이 섹션에서는 ~를 다룬다" 같은 안내문.
- 처방: 삭제. 본문이 바로 들어가야 한국어 글답다.
### C-7. 문단 간 기계적 "먼저·반면·결국" 3단 공식 [S2] · v1.1 신규
- 패턴: 문단 문두가 순서대로 "먼저 ~ / 반면 ~ / 결국 ~" 또는 "첫째 ~ / 둘째 ~ / 마지막으로 ~"로 고정. 한국 필자도 가끔 쓰나, 3연속 이상이면 AI 특유.
- 예: 본 문서 v1 초안 (문단 2 "먼저", 문단 3 "반면", 문단 6 "결국")
- 처방: 3개 중 2개 삭제. 순서 의미는 문단 자체 흐름으로 전달. 문두 접속사 없는 문단도 섞음.
### C-8. 대칭 대구 공식 "A인가, B인가" 반복 [S2] · v1.1 신규
- 패턴: 동일 문서에서 이항 대립이 3회 이상 평행구로 반복. 영어 수사학 직역.
- 예: "독점**인가**, 확산**인가**" / "전략가에게는 ~, 입안자에게는 ~" / "누가 더 ~, 누가 더 ~"
- 처방: 3개 중 2개는 비대칭으로 재배치. 한쪽만 질문형·다른 쪽은 서술형으로 섞거나, 한쪽을 더 길게 풀어쓰고 다른 쪽은 짧게.
---
## D. AI 특유의 관용구 (Signature Phrases) — S1
@ -171,6 +186,16 @@ J. 시각 장식 남용
- "~의 새로운 장을 열다"
- "~시대가 도래했다"
### D-5. 의인화된 추상 주어 [S2] · v1.1 신규
- 패턴: 사건·기술·개념을 주어로 삼아 인간 행위처럼 서술. AI가 글을 "무게감 있게" 보이게 하려는 기본 동작.
- 예: "**두 지능의 충돌**이 질문을 **던집니다**" / "**AI 대전**이 **끝나지 않습니다**" / "**지능의 가성비**가 **증명합니다**"
- 처방: 실제 행위자로 주어 교체("두 회사의 경쟁은", "엔지니어들은"), 또는 의인화 동사 약화("던집니다"→"남습니다"·"생깁니다"). 단, 상징적 제목·요약 1회 정도는 허용.
### D-6. 완결 공식형 결말 "~할 때입니다 / 시점입니다" [S2] · v1.1 신규
- 패턴: 칼럼·리포트 마지막 문장이 "~해야 할 때입니다", "~로 나아갈 시점입니다", "~할 순간입니다" 공식.
- 예: "에이전트 정부의 시대로 **나아가야 할 때입니다**"
- 처방: 동일 의미를 구체 동사 단언으로. "에이전트 정부 단계로 **넘어갈 때입니다**" 정도까지는 허용(덜 과장). 한 문서에 한 번만.
**처방:** 대부분 **삭제**. 의미가 필요하면 구체 명사·동사로 치환("중요하다"→"핵심이다" 혹은 구체 근거로).
---
@ -214,6 +239,11 @@ J. 시각 장식 남용
- "~성(性)", "~화(化)" 남발
- 예: "**근본적 관점**에서 **구조적 변화**가 **필연적**이다" → "구조가 근본부터 바뀐다"
### F-5. "~적 N" 복합 추상어 체인 [S2] · v1.1 신규
- 패턴: 명사 앞 "~적 N" 형태가 한 문서에 3회 이상 반복. F-4와 달리 추상 관형("적 측면/관점")이 아닌 **구체 명사 앞 "~적 N"** 체인. 원문의 지적 권위를 AI가 흉내 낼 때 빈출.
- 예: "에이전트**적** 자율성", "기술**적** 안정성", "경제**적** 자립", "기술**적** 토대", "시스템**적** 접근", "구조**적** 변화"
- 처방: 해당 "~적 N"을 (a) "~로서의 N" ("에이전트로서의 자율성"), (b) 동사구 ("기술이 얼마나 안정적인가"), (c) 구체 명사 ("토대") 중 하나로 해체. 문서 전체에서 "~적 N" 밀도를 절반 이하로.
---
## G. 과도한 Hedging (완곡) — S2
@ -246,6 +276,11 @@ J. 시각 장식 남용
- "이는 ~을 의미한다"
- 처방: 앞 문장과 붙이거나 구체 서술로 치환.
### H-4. 재정의 접속사 "즉" 남발 [S2] · v1.1 신규
- 패턴: 영어 `i.e.` / `that is` 직역. 보충 설명이 필요할 때마다 "즉"을 앞에 붙임.
- 예: "AI 민주화, **즉** 경제성 측면에서"
- 처방: "곧", "말하자면", "다시 말해", "바꿔 말하면"으로 어휘 변주. 또는 아예 생략하고 앞뒤를 쉼표로만 연결. 한 문서에 "즉" 2회 이하로 제한.
---
## I. 형식명사·의존명사 과다 — S2
@ -270,6 +305,11 @@ J. 시각 장식 남용
- "혁신이 필요하다", "변화가 필요하다"
- 처방: 누가 무엇을 해야 하는지 주어·동사로 구체화.
### I-6. "~능력" 추상명사 연쇄 [S2] · v1.1 신규
- 패턴: "N 능력"이 한 문서에 3회 이상 반복되며 동사 대신 명사구로 능력을 서술. 영어 `ability to X / X capability` 직역 감성.
- 예: "사고 **능력**", "워크플로우 수행 **능력**", "장기 문맥 유지 **능력**", "추론 **능력**"
- 처방: 동사형으로 풀기. "사고 능력은 뛰어나다" → "잘 사고한다" / "사고의 수준이 높다". "워크플로우 수행 능력" → "워크플로우를 얼마나 잘 처리하는지". 한 문서에 "~능력" 2회 이하로 제한.
---
## J. 시각 장식 남용 — S2~S3
@ -330,5 +370,13 @@ J. 시각 장식 남용
## 버전 관리
- v1 (2026-04-24): 초기 10대분류 × 40+ 서브 패턴 정의.
- **v1** (2026-04-24): 초기 10대분류 × 40+ 서브 패턴 정의.
- **v1.1** (2026-04-24): 실전 1호(AI 전략 칼럼 윤문) 자기 재감사 결과, **재현 2회+ 패턴 7건 승격**:
- `A-15` 추상 주어 + 만능 동사 (`X가 Y를 보여준다/제공한다`)
- `C-7` 문단 문두 "먼저·반면·결국" 3단 공식
- `C-8` 대칭 대구 공식 "A인가, B인가" 반복
- `D-5` 의인화된 추상 주어 ("두 지능의 충돌", "AI 대전")
- `D-6` 완결 공식형 결말 "~할 때입니다 / 시점입니다"
- `F-5` "~적 N" 복합 추상어 체인 (에이전트적 자율성·기술적 토대)
- `H-4` 재정의 접속사 "즉" 남발
- 확장 원칙: 실전 입력에서 재현 2회 이상 + 인간 필자가 거의 안 쓰는 패턴만 서브 항목 추가.

View file

@ -1,19 +1,23 @@
<p align="center">
<img src="assets/social-preview.png" alt="im-not-ai — 한글 AI 티 제거기" width="820">
</p>
# Humanize KR — AI 한글 티 제거 하네스
AI(ChatGPT · Claude · Gemini 등)가 쓴 한글 글을 **내용은 한 글자도 건드리지 않고** 문체 · 리듬 · 표현만 자연스러운 한국어로 되돌리는 Claude Code 하네스.
AI(ChatGPT · Claude · Gemini 등)가 쓴 한글 글을 **내용은 한 글자도 건드리지 않고** 문체 · 리듬 · 표현만 자연스러운 한국어로 되돌리는 Claude Code 하네스 입니다.
번역투, 과도한 영어 인용, 기계적 병렬 ("첫째 · 둘째 · 셋째"), "결론적으로 / 시사하는 바가 크다" 같은 AI 특유 관용구, 피동태 남용, 문두 접속사 남발, 이모지·불릿 남용 등 **10대 카테고리 × 40+ 서브 패턴**을 심각도(S1/S2/S3)로 분류해 스팬 단위로 탐지한 뒤, 수술적으로 윤문한다.
번역투, 과도한 영어 인용, 기계적 병렬 ("첫째 · 둘째 · 셋째"), "결론적으로 / 시사하는 바가 크다" 같은 AI 특유 관용구, 피동태 남용, 문두 접속사 남발, 이모지·불릿 남용 등 **10대 카테고리 × 40+ 서브 패턴**을 심각도(S1/S2/S3)로 분류해 스팬 단위로 탐지한 뒤, 윤문합니다.
## 왜 한글 특화인가
영어권 humanizer(QuillBot · Hix · Undetectable AI)는 한국어에 약하다. 한글 AI 글의 티는 대부분 **영어 번역투**에서 온다:
영어권 humanizer(QuillBot · Hix · Undetectable AI)는 한국어에 약합니다. 한글 AI 글의 티는 대부분 **영어 번역투**에서 나옵니다.
- "AI 기술을 **통해** 효율을 높**일 수 있다**" → "AI로 효율을 높인다"
- "이에 **있어서** 중요한 **점은**" → "여기서 중요한 건"
- "~**에 의해** 생성된" → "~가 만든"
- "**결론적으로**, 이는 **시사하는 바가 크다**" → (삭제)
이 하네스는 그 한글 고유 패턴을 SSOT로 정리하고, 탐지·윤문·내용 감사·자연스러움 검증을 분리된 에이전트로 수행다.
이 하네스는 그 한글 고유 패턴을 SSOT로 정리하고, 탐지·윤문·내용 감사·자연스러움 검증을 분리된 에이전트로 수행합니다.
## 4대 철칙
@ -85,7 +89,7 @@ AI(ChatGPT · Claude · Gemini 등)가 쓴 한글 글을 **내용은 한 글자
## 사용법 (Claude Code 환경)
이 리포지토리는 [Claude Code](https://claude.com/claude-code) 하네스로 설계됐다. 프로젝트 루트를 Claude Code 작업 디렉토리로 지정하면 오케스트레이터 스킬이 자동 트리거다.
이 리포지토리는 https://github.com/revfactory/harness 기반으로 만들었습니다, 프로젝트 루트를 Claude Code 작업 디렉토리로 지정하면 오케스트레이터 스킬이 자동 트리거됩니다.
```
이 AI 글 자연스럽게 윤문해줘:
@ -119,14 +123,14 @@ AI(ChatGPT · Claude · Gemini 등)가 쓴 한글 글을 **내용은 한 글자
## 라이선스 & 윤리
- 본 도구는 "AI 탐지기 우회(Undetectable AI)"가 아니라 **한글 글쓰기 품질 개선**을 목적으로 다.
- 학술 제출·저널리즘 진실성 보증 도구가 아니다.
- 분류 체계(`ai-tell-taxonomy.md`)는 연구·교육 목적 자유 이용 가능.
- 본 도구는 "AI 탐지기 우회(Undetectable AI)"가 아니라 **한글 글쓰기 품질 개선**을 목적으로 합니다.
- 학술 제출·저널리즘 진실성 보증 도구가 아니다
- 분류 체계(`ai-tell-taxonomy.md`)는 연구·교육 목적 자유 이용 가능합니다.
## 기여
새로운 AI 티 패턴을 발견했다면 `references/ai-tell-taxonomy.md` 하단 버전 섹션에 후보로 제안해 주세요. 실증 사례 2건 이상을 첨부하면 분류학자 에이전트가 v1 → v2 승격을 심사한다.
새로운 AI 티 패턴을 발견했다면 `references/ai-tell-taxonomy.md` 하단 버전 섹션에 후보로 제안해 주세요. 실증 사례 2건 이상을 첨부하면 분류학자 에이전트가 v1 → v2 승격시킵니다.
---
Built with [Claude Code](https://claude.com/claude-code) · 하네스 아키텍처 기반 프로젝트.
Built with [Claude Code](https://claude.com/claude-code) + https://github.com/revfactory/harness 하네스 아키텍처 기반 프로젝트.

BIN
assets/social-preview.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

162
scripts/make_thumbnail.py Normal file
View file

@ -0,0 +1,162 @@
"""Generate social preview thumbnail for im-not-ai.
Size 1280x640 (GitHub social preview recommended).
Warm cream background, typographic minimalism with before/after exemplar.
"""
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont
HOME = Path.home()
FONT_DIR = HOME / "Library/Fonts"
OUT = Path(__file__).resolve().parent.parent / "assets" / "social-preview.png"
OUT.parent.mkdir(parents=True, exist_ok=True)
def F(weight, size):
names = {
"black": "Pretendard-Black.otf",
"xbold": "Pretendard-ExtraBold.otf",
"bold": "Pretendard-Bold.otf",
"semi": "Pretendard-SemiBold.otf",
"med": "Pretendard-Medium.otf",
"reg": "Pretendard-Regular.otf",
"light": "Pretendard-Light.otf",
}
return ImageFont.truetype(str(FONT_DIR / names[weight]), size)
W, H = 1280, 640
BG = (245, 241, 232)
INK = (26, 26, 26)
MUTED = (110, 104, 98)
DIVIDER = (218, 208, 192)
STRIKE = (196, 78, 60)
AFTER = (45, 96, 74)
BADGE_BG = (228, 218, 202)
img = Image.new("RGB", (W, H), BG)
d = ImageDraw.Draw(img)
PAD = 72
# ---------- Header ----------
d.text((PAD, 56), "im-not-ai", font=F("black", 82), fill=INK)
subtitle = "한글 AI 티 제거기"
sub_f = F("med", 26)
sw = d.textlength(subtitle, font=sub_f)
d.text((W - PAD - sw, 94), subtitle, font=sub_f, fill=MUTED)
# divider
d.line([(PAD, 170), (W - PAD, 170)], fill=DIVIDER, width=2)
# ---------- Before / After exemplar ----------
def strike_text(x, y, text, font_, ink=INK, strike_color=STRIKE):
"""Draw text and overlay a strike line through the middle."""
d.text((x, y), text, font=font_, fill=ink)
bb = d.textbbox((x, y), text, font=font_)
mid = (bb[1] + bb[3]) // 2 + 2
d.line([(bb[0] - 2, mid), (bb[2] + 2, mid)], fill=strike_color, width=3)
return bb[2]
def plain(x, y, text, font_, color=INK):
d.text((x, y), text, font=font_, fill=color)
return d.textbbox((x, y), text, font=font_)[2]
body_f = F("semi", 36)
label_f = F("bold", 16)
# Labels
label_y = 218
d.text((PAD, label_y), "BEFORE (AI)", font=label_f, fill=STRIKE)
d.text((W // 2 + 40, label_y), "AFTER (사람)", font=label_f, fill=AFTER)
# Row 1
y1 = 260
# BEFORE line
x = PAD
x = plain(x, y1, "AI 기술을 ", body_f)
x = strike_text(x, y1, "통해", body_f)
x = plain(x, y1, " 효율을 높", body_f)
x = strike_text(x, y1, "일 수 있다", body_f)
# arrow in column gap
arrow_x = W // 2 - 30
d.text((arrow_x, y1 - 2), "", font=F("bold", 40), fill=MUTED)
# AFTER line
plain(W // 2 + 40, y1, "AI 기술로 효율을 높인다", body_f, color=AFTER)
# Row 2
y2 = 332
x = PAD
x = strike_text(x, y2, "결론적으로", body_f)
x = plain(x, y2, ", 이는 ", body_f)
x = strike_text(x, y2, "시사하는 바가 크다", body_f)
d.text((arrow_x, y2 - 2), "", font=F("bold", 40), fill=MUTED)
plain(W // 2 + 40, y2, "의미는 분명하다", body_f, color=AFTER)
# Row 3
y3 = 404
x = PAD
x = strike_text(x, y3, "~에 있어서", body_f)
x = plain(x, y3, " ", body_f)
x = strike_text(x, y3, "판단되어진다", body_f)
d.text((arrow_x, y3 - 2), "", font=F("bold", 40), fill=MUTED)
plain(W // 2 + 40, y3, "~에서, 판단한다", body_f, color=AFTER)
# ---------- Stats row ----------
d.line([(PAD, 480), (W - PAD, 480)], fill=DIVIDER, width=1)
stat_y = 510
# Score badge
score_f = F("xbold", 44)
score_label_f = F("semi", 14)
d.text((PAD, stat_y - 4), "74.5", font=score_f, fill=STRIKE)
arrow_gap = d.textlength("74.5", font=score_f)
d.text((PAD + arrow_gap + 14, stat_y + 10), "", font=F("med", 32), fill=MUTED)
d.text((PAD + arrow_gap + 62, stat_y - 4), "9.5", font=score_f, fill=AFTER)
after_x = PAD + arrow_gap + 62 + d.textlength("9.5", font=score_f)
d.text((PAD, stat_y + 52), "AI-tell score", font=score_label_f, fill=MUTED)
# A+ badge
badge_x = after_x + 40
badge_w, badge_h = 88, 44
d.rounded_rectangle(
[(badge_x, stat_y + 2), (badge_x + badge_w, stat_y + 2 + badge_h)],
radius=22, fill=AFTER
)
grade = "A+"
gf = F("xbold", 26)
gw = d.textlength(grade, font=gf)
d.text(
(badge_x + (badge_w - gw) // 2, stat_y + 8),
grade, font=gf, fill=(255, 255, 255)
)
# Categories · patterns
meta = "10 categories · 40+ patterns · v1.1"
meta_f = F("med", 22)
mw = d.textlength(meta, font=meta_f)
d.text((badge_x + badge_w + 32, stat_y + 12), meta, font=meta_f, fill=MUTED)
# ---------- Footer: URL ----------
url = "github.com/epoko77-ai/im-not-ai"
url_f = F("semi", 22)
uw = d.textlength(url, font=url_f)
d.text((W - PAD - uw, H - 50), url, font=url_f, fill=INK)
img.save(OUT, "PNG", optimize=True)
print(f"saved: {OUT} ({OUT.stat().st_size // 1024} KB)")