사례 2주차 2026-07-27 · AI 오류 제거 연재

AI가 만든 가짜 출처, 코드로 잡아내는 시스템 만들기

정규식 + 공식 API로 "AI가 지어낸 출처"를 기계적으로 잡아내는 검증 시스템을 만들고, 진짜 자료로 테스트해서 진짜 버그 2개를 찾은 이야기

한 줄 요약

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 맘대로 못 바꾸게 막아두는 원칙.

앞으로의 계획

지금 만든 건 “출처가 존재하는가”만 잡아냅니다. 다음 단계로 생각하고 있는 건 이거예요.

  1. 독립 모델 교차검증 자동화 — 지금은 사람이 수동으로 다시 확인하는데, 서로 다른 AI 두 개에게 같은 내용을 동시에 검토시키고 의견이 갈리는 지점만 자동으로 뽑아내는 스크립트를 만들 계획이에요. 특히 “출처는 진짜인데 내용을 반대로 인용한 경우”는 지금 시스템의 최대 사각지대라, 이 부분을 메우는 게 목표예요.
  2. 반복 검증 루프 — FAIL이 0건 될 때까지 자동으로 원문을 계속 찾아보고, 정말 없으면 스스로 멈추고 사람에게 알리는 자동화를 붙이려고 해요.
  3. 지식창고 축적 — 매번 API를 새로 부르는 대신, 자주 쓰이는 출처는 검증에 실제로 쓰인 것만 골라 누적해두는 구조(Gold-in Gold-out 원칙 그대로)로 발전시킬 예정이에요.
  4. 완료 기준 명확히 하기 — “다 됐다”를 AI 혼자 판단하게 두지 않고, 제출 직전에 “왜 이게 됐다고 확신하는지”를 저 스스로 설명해야 넘어가는 마지막 관문을 만들어두려고 해요. AI에게만 맡기고 안 읽어보고 넘기는 걸 구조적으로 막기 위해서예요.

이 사례는 실제 업무 대신 재구성한 예시로 작성했어요. 방법론과 배운 점은 실제로 겪은 것 그대로입니다.