한 줄 요약
AI가 만든 글에서 “존재하지 않는 출처”를 사람이 눈으로 대조하지 않고, 코드가 기계적으로 잡아내는 시스템을 직접 만들었어요. 예시 데이터로 테스트했을 땐 멀쩡했는데, 형태를 좀 더 복잡하게 바꿔서 다시 돌려보니 진짜 버그가 2개 나왔습니다.
이런 분께 도움 돼요: AI로 보고서·글을 쓰는데, 인용한 출처(문서번호·규정 조항 같은 것)가 실제로 맞는지 매번 눈으로 확인하기 지치는 분.
Before — 뭐가 불편했나
AI에게 글을 써달라고 하면, 문장은 그럴듯한데 가끔 존재하지 않는 문서번호를 인용해요. 예를 들면 이런 식이에요.
“정책문서 2024-A-1204호에 따르면 ~해야 한다”
문제는 이게 진짜 있는 문서인지, AI가 그냥 그럴듯하게 지어낸 건지 문장만 보고는 구분이 안 된다는 거였어요. 매번 원문을 찾아 눈으로 대조하는 게 시간도 오래 걸리고, 무엇보다 사람이 하다 보면 놓치는 게 생겨요.
어떻게 — 3단계 검증 시스템
AI한테 “이거 맞는지 확인해줘”라고 다시 물어보는 건 별로 효과가 없었어요. 같은 AI가 착각해서 지어낸 걸, 같은 AI에게 다시 검토시키면 또 못 잡는 경우가 많더라고요. 그래서 AI 판단이 아니라 코드가 판정하는 방식으로 설계했어요.
1단계 — 출처 자동 추출 (정규식, AI 없음)
글에서 “문서번호” 패턴을 코드로 뽑아냅니다. AI한테 “뽑아줘”라고 시키지 않는 이유는, 추출 단계부터 AI에게 맡기면 AI가 지어낸 출처를 “잘 뽑았다”고 착각할 위험이 있어서예요.
import re
DOC_ID_PATTERN = re.compile(r"\d{4}-[A-Z]-\d{3,5}호?")
def extract(text):
return [m.group() for m in DOC_ID_PATTERN.finditer(text)]
2단계 — 공식 출처와 원문 대조 (API 연동)
추출된 문서번호로, 그 문서를 관리하는 공식 시스템(사내 문서관리 API 등)에서 원문을 실제로 가져옵니다. 여기서 못 찾으면 “원문 자체가 없다”는 뜻이니 바로 의심 대상이 돼요.
3단계 — 기계적 대조로 PASS/FAIL 판정
def verify(claim_text, source_texts):
target = normalize(claim_text)
for source in source_texts:
if target in normalize(source):
return "PASS"
return "FAIL"
이 판정에는 AI가 전혀 개입하지 않아요. 원문에 있으면 PASS, 없으면 FAIL — 그게 다예요.
막힘 → 해결: 진짜 자료로 테스트하니까 나온 버그 2개
처음엔 간단한 예시 문장 몇 개로 테스트했는데 잘 작동했어요. “됐다!” 싶었는데, 실제로 쓰이는 형태의 복잡한 문서로 다시 테스트해보니 진짜 버그가 튀어나왔습니다.
버그 1 — 번호 표기 방식 차이
원문 문서는 항목 번호를 “①②③” 같은 동그라미 숫자로 쓰는데, 글에서는 “제1항”이라고 풀어서 씁니다. 사람 눈엔 당연히 같은 뜻인데, 코드는 문자 그대로만 비교하니까 다른 걸로 인식해서 진짜 있는 출처인데도 FAIL로 잘못 판정했어요.
# 수정: ①→"제1항" 자동 변환 후 비교
CIRCLED_TO_HANG = {"①": "제1항", "②": "제2항", "③": "제3항"}
def normalize(s):
for circled, spelled in CIRCLED_TO_HANG.items():
s = s.replace(circled, spelled)
return re.sub(r"\s+", "", s)
버그 2 — 엉뚱한 파일에서 우연히 일치
더 심각했던 건 이거예요. 문서 A번을 검증해야 하는데, 전혀 상관없는 문서 B번 안에 우연히 같은 글자 조합이 들어있어서 엉뚱한 문서에서 “찾았다”고 잘못 PASS 판정한 거예요. 존재하지 않는 걸 존재한다고 잘못 통과시키는, 더 위험한 종류의 오류였어요.
# 수정: 같은 종류(문서 유형)의 원문에서만 대조하도록 후보를 좁힘
candidates = [s for s in sources if s.doc_type == claim.doc_type]
두 버그 다 간단한 예시로는 절대 못 찾았을 거예요. 처음에 “테스트 통과했으니 됐다”고 안심했으면 그대로 넘어갔을 문제들이었습니다.
After — 뭐가 달라졌나
- 출처 확인을 사람이 일일이 안 하고, 코드가 PASS/FAIL/확인불가로 자동 분류
- FAIL·확인불가만 골라서 보면 되니, 검토 시간이 크게 줄었어요
- 이 과정 전체를 “스킬”로 저장해둬서, 다음부턴 “검수해줘” 한마디로 3단계가 자동으로 돌아가요
배운 것 / 재사용 자산
- 간단한 예시로 통과했다고 안심하지 말 것. 실제로 쓰는 형태(특수기호, 복잡한 문서 구조)로 다시 테스트해야 진짜 버그가 보여요.
- AI에게 “다시 확인해줘”보다, AI 판단이 아예 안 들어가는 코드 대조가 훨씬 확실해요. 대신 이건 “존재 여부”만 잡을 수 있고, “내용이 맞게 인용됐는지”는 별도로 사람이나 다른 AI가 봐야 해요.
- 위 정규식·대조 코드는 문서번호 패턴만 바꾸면 다른 분야에도 그대로 쓸 수 있어요.
이번에 새로 배운 개념
이 시스템을 설계하면서 처음 마주친 용어들을, 제가 이해한 만큼 쉽게 풀어 정리해봤어요.
| 용어 | 쉬운 설명 |
|---|---|
| 결정론적 게이트 | ”무조건 이 규칙대로만 판정한다”는 뜻. AI가 “대충 괜찮은 것 같은데?”라고 판단하는 게 아니라, 코드가 딱 정해진 기준으로만 통과/불통과를 가르는 것. |
| Gold-in Gold-out | ”좋은 걸 넣어야 좋은 게 나온다.” 아무 자료나 막 쌓아두지 말고, 실제로 검증에 쓰인 것만 골라서 저장해두자는 원칙. |
| 루프의 3요소 (목표·검증자·정지조건) | 뭔가를 “될 때까지 반복시키는” 자동화를 만들 때 꼭 정해야 하는 3가지. ① 뭘 이루고 싶은지 ② 이뤄졌는지 누가/뭐가 확인하는지 ③ 언제 멈출지. 이게 없으면 무한히 돌거나 엉뚱하게 끝남. |
| 독립 모델 교차검증 | AI 한 명한테만 물어보면 그 AI가 착각한 걸 그대로 믿을 수 있으니, 다른 AI한테도 같이 물어봐서 답이 갈리면 의심해보는 방법. |
| 결정성 경계 | ”여기까지는 코드/규칙이 정하고, 여기부터는 AI가 판단해도 된다”는 선을 미리 그어두는 것. 중요한 최종 판정은 AI 맘대로 못 바꾸게 막아두는 원칙. |
앞으로의 계획
지금 만든 건 “출처가 존재하는가”만 잡아냅니다. 다음 단계로 생각하고 있는 건 이거예요.
- 독립 모델 교차검증 자동화 — 지금은 사람이 수동으로 다시 확인하는데, 서로 다른 AI 두 개에게 같은 내용을 동시에 검토시키고 의견이 갈리는 지점만 자동으로 뽑아내는 스크립트를 만들 계획이에요. 특히 “출처는 진짜인데 내용을 반대로 인용한 경우”는 지금 시스템의 최대 사각지대라, 이 부분을 메우는 게 목표예요.
- 반복 검증 루프 — FAIL이 0건 될 때까지 자동으로 원문을 계속 찾아보고, 정말 없으면 스스로 멈추고 사람에게 알리는 자동화를 붙이려고 해요.
- 지식창고 축적 — 매번 API를 새로 부르는 대신, 자주 쓰이는 출처는 검증에 실제로 쓰인 것만 골라 누적해두는 구조(Gold-in Gold-out 원칙 그대로)로 발전시킬 예정이에요.
- 완료 기준 명확히 하기 — “다 됐다”를 AI 혼자 판단하게 두지 않고, 제출 직전에 “왜 이게 됐다고 확신하는지”를 저 스스로 설명해야 넘어가는 마지막 관문을 만들어두려고 해요. AI에게만 맡기고 안 읽어보고 넘기는 걸 구조적으로 막기 위해서예요.
이 사례는 실제 업무 대신 재구성한 예시로 작성했어요. 방법론과 배운 점은 실제로 겪은 것 그대로입니다.