-
AI가 '지어낸다'는 말을 정확히 나누기 — 할루시네이션의 분류학IT 2026. 7. 7. 22:00
"지어낸다"는 말은 너무 뭉뚱그려져 있다
LLM(대규모 언어 모델, Large Language Model)을 써 본 사람이라면 한 번쯤 이런 경험을 한다. 그럴듯한 문장인데, 막상 따져 보면 틀렸다. 사람들은 이걸 뭉뚱그려 할루시네이션(hallucination, 환각)이라고 부른다. 모델이 사실이 아닌 내용을 마치 사실처럼 자신 있게 말하는 현상이다.
그런데 "지어냈다"는 한마디는 너무 거칠다는 게 내가 공부하면서 든 첫 생각이었다. 비유하자면, 의사에게 "어디가 아파요?"라고 물었더니 "그냥 몸이 안 좋아요"라고만 답하는 것과 같다. 머리가 아픈지, 배가 아픈지, 열이 나는지를 구분하지 않으면 처방을 내릴 수 없다. 할루시네이션도 마찬가지다. 어떤 종류의 "지어냄"인지를 나눠 봐야, 그것을 잡아낼 방법도 달라진다.
이 글은 할루시네이션 연구의 표준 분류를 정리한 논문(Ji et al., "Survey of Hallucination in Natural Language Generation", ACM Computing Surveys 55(12), Article 248, 2023, DOI 10.1145/3571730)을 바탕으로, 내가 "지어냄"을 두 개의 축으로 정확히 나눠 본 정리다.
한 덩어리로 보면 잡을 수 없다
LLM에게 어떤 문서를 주고 "요약해 줘"라고 시켰다고 하자. 모델이 내놓은 요약문에 원문에 없던 문장이 끼어 있다. 이걸 그냥 "틀렸다"고만 하면, 정확히 무엇이 잘못됐는지 알 수 없다.
여기엔 사실 서로 다른 두 가지 질문이 숨어 있다. 첫째, 주어진 문서(원천, source)와 비교했을 때 어긋났는가? 둘째, 세상의 실제 사실과 비교했을 때 어긋났는가?
이 둘은 같아 보이지만 다른 질문이다. 원문이 잘못된 정보를 담고 있을 수도 있고, 반대로 모델이 원문에는 없지만 세상 사실로는 맞는 내용을 덧붙일 수도 있다. 그래서 "틀림"을 한 덩어리로 보면, 무엇을 어떻게 검증해야 할지 결정할 수 없다.
다이어그램 설명. 하나의 출력 문장을 두고 우리가 던질 수 있는 두 갈래 질문을 보여준다. 왼쪽 갈래는 "내가 준 자료에 충실한가"를 묻고, 오른쪽 갈래는 "세상 사실에 맞는가"를 묻는다. 같은 문장이라도 어느 잣대로 재느냐에 따라 합격일 수도, 불합격일 수도 있다는 점이 핵심이다. 두 질문을 섞으면 "무엇을 검증할지"가 흐려지는 게 함정이다.
두 개의 축으로 나눠 보기
첫 번째 축 — 충실성 vs 사실성
이 둘은 "무엇과 비교하느냐"의 차이다.
- 충실성(faithfulness): 출력이 주어진 원천(source)과 일관되는가. 쉽게 말해 "내가 건넨 자료에 적힌 대로 답했는가"이다. 시험으로 치면 "지문에 적힌 대로 답했는가"에 해당한다.
- 사실성(factuality): 출력이 세상의 실제 사실(world facts)과 일치하는가. 쉽게 말해 "자료와 상관없이 진짜로 맞는 말인가"이다.
일상 비유로, 독후감을 떠올려 보자. "이 책 내용을 충실히 요약했는가"는 충실성이고, "그 책에 적힌 주장이 실제로 옳은가"는 사실성이다. 둘은 별개다. 엉터리 책을 충실하게 요약할 수도 있다.
두 번째 축 — 내재적 vs 외재적
이 둘은 원천과 비교했을 때 "어긋남의 종류"가 다르다.
- 내재적(intrinsic) 할루시네이션: 출력이 주어진 원천과 정면으로 모순되는 것. 쉽게 말해 자료에 적힌 내용을 거꾸로 뒤집은 경우다. 원천에 "키가 180cm"라고 적혀 있는데 모델이 "170cm"라고 말하는 식이다.
- 외재적(extrinsic) 할루시네이션: 출력이 원천만으로는 검증할 수 없는 것. 쉽게 말해 자료 어디에도 근거가 없어서 맞다고도 틀리다고도 할 수 없는, 모델이 새로 끌어온 내용이다.
비유하자면 시험 답안에서, 지문과 정반대로 적은 답(내재적)과 지문에 나오지도 않은 이야기를 적은 답(외재적)의 차이다. 둘 다 채점에서 문제지만, 잡아내는 방식이 다르다. 모순은 원천과 대조하면 바로 드러나고, 근거 없음은 "원천에서 찾을 수 없다"는 부재를 확인해야 한다.
다이어그램 설명. 두 개의 독립된 축을 한눈에 정리한 그림이다. 위쪽 갈래(무엇과 비교하나)는 비교 대상이 "내가 준 원천"이냐 "세상 사실"이냐를 가르고, 아래쪽 갈래(어긋남의 종류)는 그 어긋남이 "모순"이냐 "근거 없음"이냐를 가른다. 두 축이 서로 독립이라는 게 요점이라, 둘을 한 줄로 합쳐 생각하면 분류가 무너진다.
한 문장이 세 갈래로 갈리는 순간
같은 문장이라도 원천과 견주면 셋 중 하나로 떨어진다. 원천이 그 문장을 지지하면 정상이고, 정면으로 부딪치면 내재적 할루시네이션, 원천에서 찾을 수 없으면 외재적 할루시네이션이다.
다이어그램 설명. 출력 문장 하나가 원천과 대조될 때 갈라지는 세 갈래를 보여준다. 핵심은 "근거가 없음"과 "정면으로 틀림"을 같은 오류로 뭉치지 않는다는 점이다. 근거 없음은 원천을 넘어선 내용이고, 모순은 원천을 정면으로 거스른 내용이라 대응이 달라진다. 둘을 같은 버킷에 넣으면 처방이 엇나가는 게 함정이다.
분류가 곧 처방이다
이렇게 나누면 무엇이 좋은가. 작업의 성격에 따라 어느 잣대가 중요한지가 분명해진다.
요약, 번역, 문서화처럼 "주어진 원천"이 분명한 작업에서는 보통 충실성이 핵심이다. 우리가 모델에게 기대하는 건 "이 자료에 충실하라"이지, "세상 모든 진실을 알아서 채워 넣어라"가 아니기 때문이다. 반대로 원천 없이 열린 질문에 답하는 작업에서는 사실성이 전면에 나선다.
다만 한계도 있다. 충실성을 완벽히 지켜도 원천 자체가 틀렸다면 결과도 틀린다. 분류는 "무엇을 검증해야 하는지"를 알려줄 뿐, 원천의 품질까지 보장하지는 못한다. 또한 내재적과 외재적의 경계가 항상 칼같이 나뉘지는 않아서, 실제 판정에는 사람이나 별도 검증기의 판단이 끼어든다.
다이어그램 설명. 검증의 두 경로를 위아래로 대비한 그림이다. 사실성 경로는 세상 전체의 지식을 끌어와야 해서 무겁고 "무엇이 진실인가"를 정의하기부터 어렵다. 반면 충실성 경로는 비교 기준이 손에 쥔 원천 하나로 고정되어 훨씬 다루기 쉽다. 원천이 분명한 작업이라면 충실성 경로만으로 충분하다는 점이 실무적으로 중요한 대목이다.
코드 위키 자동 생성에 왜 핵심인가
나는 이 분류가 코드를 읽어 설명 문서를 자동 생성하는 작업에 딱 들어맞는다는 걸 깨달았다. 이런 작업에서는 코드 그 자체가 원천(source)이 된다. 그러면 검증 축이 놀랍도록 단순해진다. "코드와 모순되는 서술"은 내재적 할루시네이션이고, "코드에서 확인되지 않는 서술"은 외재적 할루시네이션이다.
이 도메인에서는 사실성 검증, 즉 세상 사실과의 대조가 사실상 무의미해진다. 코드가 곧 진실의 기준(ground truth)이므로, 세상 사실과 대조할 필요 없이 "코드 대비 일관성", 즉 충실성만 따지면 된다. 결과적으로 평가 축이 내재적 충실성 하나로 깔끔하게 수렴한다.
모든 서술이 코드의 실제 줄을 가리키도록 강제하면, 외재적 할루시네이션(근거 없는 서술)은 애초에 통과하지 못한다. 실제로 내가 만들고 있는 코드 위키 자동 생성 시스템에서는 이 원칙을 그대로 규칙으로 박아 넣었다. 가리킬 코드 근거(앵커)가 없는 서술은 모델에게 물어볼 것도 없이 곧장 '근거 없음'으로 처리한다. 인용 없는 주장은 정의상 검증이 불가능하기 때문이다. 그렇게 외재적 할루시네이션을 걸러내고 나면, 남는 일은 "코드와 모순되는가"만 검사하는 단일하고 자동화 가능한 게이트뿐이다.
"지어낸다"를 두 축으로 쪼갠 덕분에, 검증해야 할 대상이 막막한 "세상 모든 사실"에서 손에 쥔 코드 한 덩어리로 좁혀졌다. 나에게는 분류학이 곧 설계 도구가 되는 순간이었다.
이 글은 생성형 AI의 도움을 받아 작성되었습니다. 원본 자료를 기반으로 AI가 초안을 생성하고, 작성자가 검토·편집하였습니다.
'IT' 카테고리의 다른 글
에이전트가 방금 내 시스템에서 뭘 했지? — OpenClaw가 Activity 탭으로 답한 방법 (0) 2026.07.09 에이전트가 지금 무슨 도구를 쓰는지 안 보인다 — OpenClaw 웹 채팅이 WebSocket 스트리밍으로 푼 방법 (0) 2026.07.09 에이전트가 지금 살아 있나 — OpenClaw가 흩어진 상태를 한 화면(Overview)에 모은 방법 (0) 2026.07.09 근거가 이 문장을 정말 뒷받침하나 — 로컬 NLI로 매일 $0 사실 검증 (0) 2026.07.08 문장을 잘게 쪼개 검증한다 — Claim 분해와 앵커 없는 주장 걸러내기 (0) 2026.07.07 모듈이 바깥에 한 약속 — 공개 인터페이스를 코드에서 뽑아 표로 만들기 (0) 2026.07.07 이 이름이 가리키는 진짜 그곳 — 코드 심볼을 구문에서 의미로 풀어내기 (0) 2026.07.06 이 함수를 누가 부르는가 — jedi로 호출 관계를 의미 수준에서 풀기 (0) 2026.07.06 언어가 하나 늘 때 코드를 고치지 않으려면 — 표준 tags.scm 쿼리로 다언어 추출하기 (0) 2026.07.05 코드를 글자가 아니라 구조로 읽기 — tree-sitter로 함수와 모듈을 뽑아내기 (0) 2026.07.05