Posts

AI는 왜 모른다고 말하지 않을까

목차4
시험장 책상에 앉은 작은 로봇이 객관식 답안지의 동그라미를 연필로 하나도 빠짐없이 채우고 있고 옆에는 다 채운 답안지가 높이 쌓여 있다. 앞에 선 감독관이 한쪽 눈썹을 올린 채 몸을 기울여 보고 있다
빈칸으로 내면 0점이니 일단 다 채웠다

AI에게 라이브러리 사용법을 물어봤다가 존재하지 않는 옵션을 안내받은 경험이 한 번쯤은 있을 것이다. 옵션 이름도 그럴듯하고 예제 코드까지 붙어 있어서 의심 없이 복사한다. 실행해 보면 그런 옵션은 없다는 에러가 나온다. 문서를 다시 찾아봐도 그 옵션은 어디에도 없다.

이렇게 사실이 아닌 내용을 자신 있게 말하는 현상을 보통 환각(hallucination)이라고 부른다. 이상한 건 그 순간 AI가 모르겠다고 말할 수도 있었다는 점이다. 이번 포스팅에서는 AI가 왜 모르는 걸 모른다고 하지 않는지, 그리고 그게 개발자에게 왜 남의 일이 아닌지 이야기해보려고 한다.

생일을 세 번 물어본 실험

2025년 9월 OpenAI 의 애덤 토먼 칼라이(Adam Tauman Kalai)와 동료들은 언어 모델은 왜 환각을 일으키는가라는 논문을 냈다. 논문 앞부분에 짧은 실험이 하나 나온다. 한 언어 모델에게 이렇게 물었다.

애덤 토먼 칼라이의 생일은 언제인가? 알고 있다면 DD-MM 형식으로만 답하라.

질문에 알고 있다면이라는 조건을 분명히 달았는데도 모델은 세 번 물으니 03-07, 15-06, 01-01 이라는 세 개의 다른 날짜를 내놨다. 셋 다 틀렸다. 논문 저자 본인의 생일을 물은 실험이라 정답 확인도 확실하다.

모르면 모른다고 하면 될 텐데 왜 날짜를 지어낼까. 논문이 내놓는 답은 기술이 아니라 채점에 있다.

빈칸이 0점인 시험

보기가 네 개인 객관식 문제를 하나 떠올려 보자. 답을 모를 때 아무거나 찍으면 4분의 1 확률로 1점을 받는다. 빈칸으로 내면 무조건 0점이다. 점수만 놓고 보면 찍는 쪽이 늘 낫다. 그래서 점수를 올리고 싶은 학생은 모르는 문제도 비워 두지 않는다.

언어 모델을 평가하는 벤치마크 대부분이 이 시험과 같은 방식으로 채점한다. 맞히면 1점이고 틀리거나 모르겠다고 하면 0점이다. 이런 점수판에서 높은 순위를 받도록 다듬어진 모델은 자연스럽게 찍는 법을 배운다. 논문은 이 상황을 한 문장으로 요약한다.

언어 모델은 시험을 잘 보도록 최적화되어 있다.

OpenAI 가 같은 시기에 공개한 SimpleQA 평가 결과를 보면 이 차이가 숫자로 드러난다.

정답률만 보면 오른쪽 모델이 2%p 앞선다. 오답은 세 배 가까이 많다

o4-mini 는 거의 모든 질문에 답했다. 그 덕분에 정답률은 24%로 gpt-5-thinking-mini 의 22%보다 조금 높다. 대신 오답이 75%다. gpt-5-thinking-mini 는 절반 넘게 모르겠다고 답했고 오답은 26%에 그쳤다. 정답률 한 줄로 순위를 매기는 표에서는 o4-mini 가 위에 선다.

사람이 보는 시험은 이 문제를 오래전에 겪었다. 논문에 따르면 미국 SAT 와 GRE 도 예전에는 오답에 감점을 줬다. 찍는 게 손해가 되도록 점수를 짠 것이다. 논문이 제안하는 방법도 비슷하다. 질문마다 확신이 t 이상일 때만 답하라고 적는다. 틀리면 t/(1−t)점을 깎는다는 것도 미리 알려 준다. t를 0.75로 잡으면 오답 하나에 3점이 깎인다. 아무거나 찍어서 맞힐 확률이 4분의 1이라면 기대 점수는 0.25에서 0.75×3을 뺀 −2점이다. 이런 시험에서는 확신이 75%를 넘을 때만 답하는 게 이득이다.

AI가 지어내는 답은 모르겠다는 말에 아무 점수도 주지 않는 점수판 위에서 점수를 최대로 받으려 한 결과다.

모른다고 말하지 않는 코드

여기까지 읽고 AI 쪽 문제라고 넘기기 전에 우리 코드를 한번 보자. 상품 가격을 보여주는 화면에 이런 줄이 있다고 해보자.

코드
// 가격을 모를 때도 0원으로 표시
const label = `${(product.price ?? 0).toLocaleString()}원`;

API 가 가격을 빼먹고 내려주면 화면에는 0원이 뜬다. 가격을 모른다는 사실이 0원이라는 그럴듯한 답으로 바뀐 셈이다. 사용자 입장에서 이건 오류 메시지보다 훨씬 위험하다. 무료 상품처럼 보이니까 말이다.

비슷한 코드는 생각보다 흔하다.

코드모르는 상황화면이 하는 말
price ?? 0가격 정보 없음0원
parseInt(input) || 0숫자가 아닌 입력0
catch 에서 빈 배열 반환서버 오류주문 내역이 없습니다

마지막 줄은 특히 고약하다. 서버가 잠깐 죽었을 뿐인데 사용자는 주문이 사라졌다고 생각하고 고객센터에 전화를 건다. 빈 목록과 불러오기 실패는 완전히 다른 상황인데 화면에서는 똑같이 보인다.

우리가 이런 코드를 쓰는 이유도 AI와 크게 다르지 않다. 화면 설계에 모른다는 상태를 보여줄 자리가 없어서다. 디자인 시안에는 가격이 있는 상품과 주문이 있는 목록만 그려져 있다. 타입은 number 하나뿐이고 null 을 받으면 어떻게 그릴지 정해진 게 없다. 빈칸으로 낼 수 없는 시험지를 받은 학생처럼 코드도 일단 뭔가를 채워 넣는다.

모른다고 말할 자리 만들기

AI에게 이 자리를 만들어 주는 건 점수판을 고치는 일이라 우리 손을 벗어나 있다. 다만 질문하는 쪽에서 할 수 있는 건 있다. 모르면 모른다고 답해도 된다고 질문에 적어 두는 것이다. 근거가 된 문서나 버전을 같이 달라고 하면 지어낸 답을 가려내기도 쉬워진다.

코드에서는 모르는 상태에 타입과 화면을 줘야 한다. 가격을 모를 수 있다면 타입을 number | null 로 두고 그 경우에 보여줄 문구를 정한다.

코드
const label = product.price == null ? "가격 문의" : `${product.price.toLocaleString()}원`;

목록을 불러오는 함수라면 실패를 빈 배열로 바꾸지 말고 그대로 위로 올려서 화면이 오류 상태를 따로 그리게 한다. 빈 목록 화면과 오류 화면을 디자인 단계에서 따로 그려 두면 이 구분을 잊을 일도 줄어든다.

다음에 ?? 0 을 쓰려고 손이 갈 때 이 0이 정말 0인지 한 번만 따져 보자. 모른다는 뜻을 0으로 덮은 거라면 그 줄은 우리 코드가 쓴 작은 환각이다.