AI 맞춤법, 60문항 돌려본 결과에서 쓴 문항 전체와 실행 스크립트입니다. 본문에는 지면상 일부만 실었기 때문에 여기에 전부 공개합니다. 그대로 복사해 같은 실험을 다시 돌리실 수 있습니다.
채점 방식은 단순합니다. 문항마다 오류 표현과 정답 표현을 미리 붙여 두고, 모델 출력에서 오류가 사라지고 정답이 들어갔는지만 봅니다. 사람이 눈으로 판정하지 않습니다.
1차 — 흔한 오류 30문항 + 정상 문장 10문항
31~40번은 고칠 것이 없는 문장입니다. 모델이 멀쩡한 문장을 건드리는지(오검출) 보려고 섞었습니다.
| # | 유형 | 입력(오류 포함) | 정답 |
|---|---|---|---|
| 1 | 되/돼 | 이렇게 하면 안 되요. | 이렇게 하면 안 돼요. |
| 2 | 안/않 | 숙제를 하지 안았다. | 숙제를 하지 않았다. |
| 3 | 왠/웬 | 오늘은 웬지 기분이 좋다. | 오늘은 왠지 기분이 좋다. |
| 4 | 왠/웬 | 네가 여기 왠일이야? | 네가 여기 웬일이야? |
| 5 | 어휘 | 오늘이 몇 일이지? | 오늘이 며칠이지? |
| 6 | 어휘 | 정말 오랫만에 만났다. | 정말 오랜만에 만났다. |
| 7 | 어휘 | 소문이 금새 퍼졌다. | 소문이 금세 퍼졌다. |
| 8 | 사이시옷 | 물건의 갯수를 세었다. | 물건의 개수를 세었다. |
| 9 | 사이시옷 | 등교길이 막힌다. | 등굣길이 막힌다. |
| 10 | 사이시옷 | 최대값을 구하시오. | 최댓값을 구하시오. |
| 11 | 띄어쓰기 | 나는 할수 있다. | 나는 할 수 있다. |
| 12 | 띄어쓰기 | 서울에 온지 삼 년이 됐다. | 서울에 온 지 삼 년이 됐다. |
| 13 | 띄어쓰기 | 노력한만큼 결과가 나온다. | 노력한 만큼 결과가 나온다. |
| 14 | 띄어쓰기 | 그저 웃을뿐이었다. | 그저 웃을 뿐이었다. |
| 15 | 띄어쓰기 | 이번 일은 그가 한것이 아니다. | 이번 일은 그가 한 것이 아니다. |
| 16 | 로서/로써 | 학생으로써 본분을 다하자. | 학생으로서 본분을 다하자. |
| 17 | 로서/로써 | 대화로서 문제를 풀자. | 대화로써 문제를 풀자. |
| 18 | 어떻게/어떡해 | 이 문제를 어떡해 풀어요? | 이 문제를 어떻게 풀어요? |
| 19 | 어휘 | 경쟁에서 뒤쳐지지 않으려고 애썼다. | 경쟁에서 뒤처지지 않으려고 애썼다. |
| 20 | 어휘 | 있다가 다시 연락할게. | 이따가 다시 연락할게. |
| 21 | 사물존칭 | 주문하신 커피 나오셨습니다. | 주문하신 커피 나왔습니다. |
| 22 | 사물존칭 | 이 상품은 품절이십니다. | 이 상품은 품절입니다. |
| 23 | 외래어 | 초콜렛을 선물했다. | 초콜릿을 선물했다. |
| 24 | 외래어 | 케잌에 초를 꽂았다. | 케이크에 초를 꽂았다. |
| 25 | 외래어 | 슈퍼마켙에 다녀왔다. | 슈퍼마켓에 다녀왔다. |
| 26 | 던/든 | 어렸을 때 얼마나 울었든지 모른다. | 어렸을 때 얼마나 울었던지 모른다. |
| 27 | 던/든 | 가던지 말던지 마음대로 해. | 가든지 말든지 마음대로 해. |
| 28 | 어휘 | 윗어른께 인사드렸다. | 웃어른께 인사드렸다. |
| 29 | 활용 | 알맞는 답을 고르시오. | 알맞은 답을 고르시오. |
| 30 | 활용 | 마음이 설레인다. | 마음이 설렌다. |
| 31 | 정상 | 회의는 오후 세 시에 시작합니다. | 고칠 것 없음(오검출 대조군) |
| 32 | 정상 | 그는 밤새 자료를 정리했다. | 고칠 것 없음(오검출 대조군) |
| 33 | 정상 | 이번 주 안에 결과를 정리하겠습니다. | 고칠 것 없음(오검출 대조군) |
| 34 | 정상 | 비가 와서 우산을 챙겼다. | 고칠 것 없음(오검출 대조군) |
| 35 | 정상 | 이 계약서는 검토가 끝나는 대로 보내겠습니다. | 고칠 것 없음(오검출 대조군) |
| 36 | 정상 | 설명이 부족했다면 다시 알려 주세요. | 고칠 것 없음(오검출 대조군) |
| 37 | 정상 | 예산을 초과하지 않도록 주의하고 있습니다. | 고칠 것 없음(오검출 대조군) |
| 38 | 정상 | 담당자가 자리를 비워 연락이 늦었습니다. | 고칠 것 없음(오검출 대조군) |
| 39 | 정상 | 자료를 첨부했으니 확인 부탁드립니다. | 고칠 것 없음(오검출 대조군) |
| 40 | 정상 | 다음 회의는 금요일로 미루기로 했습니다. | 고칠 것 없음(오검출 대조군) |
2차 — 어려운 15문항 + 오류 3개씩 심은 문단 5개
1차가 천장에 닿아 문맥으로만 갈리는 것들로 다시 만들었습니다. 201~205번은 한 문단에 오류를 세 개씩 넣었습니다.
| # | 유형 | 입력(오류 포함) | 정답 |
|---|---|---|---|
| 101 | 동음이의 | 대금을 카드로 결재했다. | 대금을 카드로 결제했다. |
| 102 | 동음이의 | 부장님께 서류를 결제받았다. | 부장님께 서류를 결재받았다. |
| 103 | 동음이의 | 폭력적인 표현은 지향해야 한다. | 폭력적인 표현은 지양해야 한다. |
| 104 | 동음이의 | 품질을 재고하기 위해 노력했다. | 품질을 제고하기 위해 노력했다. |
| 105 | 의미혼동 | 내 생각은 너와 틀리다. | 내 생각은 너와 다르다. |
| 106 | 의미혼동 | 우체국에서 편지를 붙였다. | 우체국에서 편지를 부쳤다. |
| 107 | 의미혼동 | 바짓단을 늘렸다. | 바짓단을 늘였다. |
| 108 | 불규칙활용 | 어머니가 김치를 담궜다. | 어머니가 김치를 담갔다. |
| 109 | 불규칙활용 | 나가면서 문을 잠궜다. | 나가면서 문을 잠갔다. |
| 110 | 불규칙활용 | 어제 시험을 치뤘다. | 어제 시험을 치렀다. |
| 111 | 어법 | 실내에서는 흡연을 삼가해 주세요. | 실내에서는 흡연을 삼가 주세요. |
| 112 | 사이시옷예외 | 회의 회수를 줄이기로 했다. | 회의 횟수를 줄이기로 했다. |
| 113 | 사이시옷예외 | 사진의 촛점이 흐리다. | 사진의 초점이 흐리다. |
| 114 | 어법 | 저희 나라 경제가 성장하고 있다. | 우리나라 경제가 성장하고 있다. |
| 115 | 이중피동 | 잘 쓰여진 문장이다. | 잘 쓰인 문장이다. |
| 201 | 복합 | 지난 번 회의에서 논의된 내용을 정리해서 보내 드립니다. 검토하신 후 회신 주십시요. 일정이 촉박해서 죄송할 따름 입니다. | 지난 번 → 지난번 주십시요 → 주십시오 따름 입니다 → 따름입니다 |
| 202 | 복합 | 이번 프로젝트는 예산이 초과되지 안도록 관리하겠습니다. 담당자가 바뀌였으니 참고 바랍니다. 자세한 내용은 첨부 파일을 참고 하시기 바랍니다. | 초과되지 안도록 → 초과되지 않도록 바뀌였으니 → 바뀌었으니 참고 하시기 → 참고하시기 |
| 203 | 복합 | 고객님께서 주문하신 상품이 품절되셨습니다. 대체 상품을 안내해 드릴수 있으니 연락 주시면 감사드리겠읍니다. | 품절되셨습니다 → 품절되었습니다 드릴수 → 드릴 수 감사드리겠읍니다 → 감사드리겠습니다 |
| 204 | 복합 | 회사에 입사한지 벌써 삼 년이 되었습니다. 그동안 많은 것을 배웠고 앞으로도 최선을 다 하겠습니다. 좋은 결과로 보답할 수 있도록 노력하겠읍니다. | 입사한지 → 입사한 지 다 하겠습니다 → 다하겠습니다 노력하겠읍니다 → 노력하겠습니다 |
| 205 | 복합 | 예산안은 다음 주 까지 제출해야 되요. 각 부서에서 취합한 자료를 빠짐 없이 보내 주시기 바랍니다. | 다음 주 까지 → 다음 주까지 제출해야 되요 → 제출해야 돼요 빠짐 없이 → 빠짐없이 |
실행 스크립트
위 문항을 testset.json으로 저장한 뒤 python run.py <OPENAI_API_KEY>로 돌립니다. 결과와 채점이 results.json에 저장됩니다.
"""한국어 맞춤법 교정 정확도 실측.
40문항(오류 30 + 정상 10) × 모델 3종 × 프롬프트 2종 = 240회 호출.
정상 문장 10개를 섞은 이유: 재현율보다 **오검출**이 실무에서 더 아프기 때문이다.
멀쩡한 문장을 건드리면 결국 전부 다시 읽어야 한다.
"""
import json, re, sys, time, urllib.request, urllib.error
from concurrent.futures import ThreadPoolExecutor
from pathlib import Path
BASE = Path(__file__).parent
KEY = sys.argv[1]
MODELS = ["gpt-5.6-luna", "gpt-5.6-terra", "gpt-5.6-sol"]
PROMPTS = {
"느슨": "다음 문장의 맞춤법을 고쳐 주세요. 고친 문장만 출력하세요.\n\n{s}",
"엄격": ("다음 문장에서 한국어 맞춤법과 띄어쓰기 오류만 고치세요.\n"
"오류가 없으면 원문을 그대로 출력하세요.\n"
"문체·어투·낱말 선택은 절대 바꾸지 마세요.\n"
"설명 없이 결과 문장 한 줄만 출력하세요.\n\n{s}"),
}
def norm(s):
s = re.sub(r"\s+", " ", (s or "").strip())
return s.strip('"\'「」“”‘’ ')
def call(model, prompt):
body = json.dumps({"model": model,
"messages": [{"role": "user", "content": prompt}],
"max_completion_tokens": 2000}).encode()
req = urllib.request.Request("https://api.openai.com/v1/chat/completions", data=body,
headers={"Authorization": f"Bearer {KEY}", "Content-Type": "application/json"})
for attempt in range(3):
try:
r = json.loads(urllib.request.urlopen(req, timeout=180).read())
return r["choices"][0]["message"]["content"], r["usage"]
except urllib.error.HTTPError as e:
if attempt == 2:
return f"__ERR__ {e.code} {e.read().decode()[:120]}", None
time.sleep(3 * (attempt + 1))
except Exception as e:
if attempt == 2:
return f"__ERR__ {e}", None
time.sleep(3 * (attempt + 1))
items = json.loads((BASE / "testset.json").read_text(encoding="utf-8"))
jobs = [(m, pk, it) for m in MODELS for pk in PROMPTS for it in items]
print(f"호출 {len(jobs)}건 시작", flush=True)
def work(job):
model, pk, it = job
out, usage = call(model, PROMPTS[pk].format(s=it["wrong"]))
o = norm(out)
rec = {"model": model, "prompt": pk, **{k: it[k] for k in ("id", "cat", "wrong", "right", "err", "fix")},
"out": o, "err_api": o.startswith("__ERR__")}
if it["cat"] == "정상":
rec["untouched"] = (o == norm(it["wrong"]))
else:
rec["missed"] = it["err"] in o
rec["fixed"] = (it["fix"] in o) and (it["err"] not in o)
rec["wrong_fix"] = not rec["fixed"] and not rec["missed"]
rec["exact"] = (o == norm(it["right"]))
return rec
with ThreadPoolExecutor(8) as ex:
res = list(ex.map(work, jobs))
(BASE / "results.json").write_text(json.dumps(res, ensure_ascii=False, indent=1), encoding="utf-8")
errs = [r for r in res if r["err_api"]]
print(f"완료. API 실패 {len(errs)}건")
for e in errs[:3]:
print(" ", e["model"], e["out"][:110])
알려진 한계
- 26번(
울었든지)의 정답 키가 좁았습니다. 모델이 고른울었는지도 성립하는 문장이라 실패로 세면 안 됩니다. - OpenAI 계열만 측정했습니다. 클로드·제미나이, 전용 맞춤법 검사기와의 비교는 없습니다.
- 60문항은 표본이 작고 문항 선택에 제 편향이 있습니다.
문항에 오류가 있거나 정답 키가 틀렸다면 문의하기로 알려 주세요. 수정 정책에 따라 고치고 그 사실을 남깁니다.