AI 할루시네이션, 프롬프트로는 못 막는다 — 검증 파이프라인 만드는 법 (2026)
“할루시네이션 없애는 프롬프트”류의 글이 많다. 그런데 모델을 만든 회사의 공식 문서를 보면 결론이 다르다. 완화 기법을 쭉 나열한 다음, 마지막에 이렇게 못박는다 — 이 기법들이 할루시네이션을 상당히 줄여주긴 하지만 완전히 제거하지는 못한다. 중요한 정보는 항상 검증하라.
그러니 질문을 바꾸는 게 맞다. “어떻게 없애지”가 아니라 “틀렸을 때 어떻게 걸러내지”다. 이 글은 후자를 다룬다.
아래 공식 정책·기능 설명은 Anthropic 공식 문서를 2026-07-28에 확인한 내용이다. 기능과 요금은 바뀔 수 있으니 도입 직전에 재확인하자.
왜 생기나 — 모델 탓이 아니라 채점 탓
할루시네이션을 “AI가 가끔 미쳐서 헛소리하는 것”으로 이해하면 대응이 안 나온다. 좀 더 정확한 설명은 채점 방식이 추측을 보상한다는 쪽이다.
객관식 시험을 떠올리면 된다. 모르는 문제를 빈칸으로 두면 확정 0점이고, 찍으면 맞을 확률이라도 생긴다. 정답률만으로 채점하는 한 찍는 게 항상 유리하다. 대부분의 LLM 벤치마크가 정확히 이 구조다 — “모르겠습니다”에 부분점수를 주지 않는다. 모델은 이 채점표에 최적화되도록 학습된다.
이게 실무에 주는 함의가 두 개 있다.
- 모델을 바꿔도 근본적으로는 안 사라진다. 더 좋은 모델은 틀리는 빈도가 낮아질 뿐, 틀릴 때의 태도(자신 있게 틀림)는 같다.
- “모르면 모른다고 해도 된다”고 명시적으로 허용하는 것만으로 효과가 있다. 기권에 점수를 주는 셈이기 때문이다. 공교롭게도 이게 Anthropic 공식 완화 기법 목록의 첫 번째 항목이다.
실제로 무슨 일이 벌어지고 있나
추상적인 위험이 아니다. 법정이 가장 빠르게 데이터가 쌓이는 현장이다 — 제출된 문서가 공개 기록으로 남고, 판사가 진위를 따지기 때문이다.
한 연구자가 AI 허위정보가 사법부에 제출된 전 세계 사례를 수집한 결과 1,400건이 넘었고 계속 늘고 있다고 보도됐다. 구체적 사례도 있다.
| 사례 | 내용 | 결과 |
|---|---|---|
| 미국 캘리포니아 (2026-03) | 반려견 양육권 소송에서 AI가 지어낸 ‘가짜 판례’ 인용 | 변호사에게 제재금 5,000달러 |
| 국내 (2026-02 구속 기소) | AI로 만든 예금잔고증명서(9억 원)를 법원에 제출 | 구속영장이 한 차례 기각됨. 실제 잔고는 23원 |
국내 사법부도 TF를 꾸려 대응방안을 발표했다. 허위 판례를 인용하면 소송비용을 부담시키거나 변호사 징계를 의뢰할 수 있고, 허위 사건번호를 확인하는 서비스도 개발했다.
수집된 사례의 정확한 최신 건수는 출처마다 다르게 인용된다(집계가 매주 갱신되기 때문). 이 글에서는 보도로 확인된 “1,400건 이상”만 쓴다. 정확한 현재 수치는 원본 데이터베이스에서 직접 확인하는 게 맞다. [확인 필요]
여기서 중요한 건 숫자 자체가 아니다. AI가 지어낸 내용을 사람이 눈으로 훑어서는 못 걸러낸다는 게 이 사례들의 공통점이다. 판례 인용은 형식이 정형화돼 있어서, 지어낸 것도 진짜처럼 생겼다.
공식 완화 기법 — 그리고 각각이 왜 먹히는지
Anthropic 공식 문서가 권하는 기법들이다. 나열만 하면 의미가 없으니 왜 작동하는지를 같이 붙였다.
| 기법 | 방법 | 왜 먹히나 |
|---|---|---|
| 모른다고 말할 여지 주기 | ”확실하지 않거나 자료에 없으면 ‘판단할 정보가 부족하다’고 답하라”를 프롬프트에 명시 | 기권에 점수를 준다 — 찍기의 기대값을 낮춤 |
| 직접 인용으로 접지 | 작업 전에 관련 구절을 글자 그대로 먼저 뽑게 함 (긴 문서에 특히 권장) | 답변을 기억이 아니라 눈앞의 텍스트에 묶어둠 |
| 인용으로 사후 검증 | 답변 후 주장마다 뒷받침 인용을 찾게 하고, 못 찾으면 그 주장을 삭제시킴 | 생성과 검증을 분리 — 검증 단계에선 지어낼 유인이 없음 |
| 사고 과정 노출 | 결론 전에 단계별 추론을 쓰게 함 | 틀린 전제가 눈에 보이게 됨 |
| N번 돌려 비교 | 같은 프롬프트를 여러 번 실행해 답을 대조 | 실제 지식은 반복돼도 같고, 지어낸 건 흔들림 |
| 외부 지식 차단 | ”제공된 문서에만 근거하고 일반 지식은 쓰지 말라”고 지시 | 학습 데이터에서 끌어오는 경로를 막음 |
공식 문서는 긴 문서 작업에서 직접 인용 방식을 특히 권한다(대략 2만 토큰이 넘는 문서가 기준으로 제시된다).
여기서 실무적으로 제일 쓸모 있는 건 세 번째 — “못 찾으면 삭제” 다. 나머지가 확률을 낮추는 기법이라면, 이건 검증 가능한 산출물을 만들어낸다. 답변에 인용문이 붙어 있으면, 그 인용문이 진짜인지는 사람이 아니라 기계가 판정할 수 있다.
프롬프트만으로 안 되는 이유
문제가 하나 남는다. 인용문을 지어내는 것도 할루시네이션이다. “근거를 인용해줘”라고 시키면 그럴듯한 인용문이 붙어 나오는데, 그 인용문 자체가 원문에 없을 수 있다. 위의 가짜 판례 사건이 정확히 그 케이스다.
그래서 마지막 한 칸은 기계가 채워야 한다. 답변에 붙은 인용문을 원문과 글자 단위로 대조하는 것. 이건 LLM이 아니라 문자열 비교로 하는 일이고, 그래서 100% 확실하다.
직접 만든 인용 검증기 (API 키 불필요)
로컬에서 돌아가는 스크립트를 만들었다. 답변 파일과 원문 파일을 주면, 답변에서 따옴표로 감싼 구절을 뽑아 원문에 실제로 존재하는지 검사한다.
핵심은 찾지 못했을 때 두 경우를 구분한다는 점이다.
- 원문에 비슷한 구절이 있는데 안 맞음 → 숫자나 조건을 슬쩍 바꾼 변형
- 원문에 비슷한 것조차 없음 → 통째로 지어낸 날조
앞의 것이 훨씬 위험하다. 눈으로 읽으면 자연스럽게 넘어가기 때문이다.
인용문 추출과 대조 부분:
const MIN_LEN = 10; // 이보다 짧은 인용은 우연히 일치할 수 있어 건너뛴다
// 공백·줄바꿈만 정규화한다. 조사나 어미를 건드리면 조작을 놓친다.
const normalize = (s) => s.replace(/\s+/g, " ").trim();
function extractQuotes(text) {
const patterns = [
/"([^"]{4,400})"/g, // 곧은 큰따옴표
/[“]([^”]{4,400})[”]/g, // 굽은 큰따옴표
/[「『]([^」』]{4,400})[」』]/g, // 낫표·겹낫표
];
const found = [];
for (const re of patterns) {
for (const m of text.matchAll(re)) {
const q = normalize(m[1]);
if (q.length >= MIN_LEN) found.push(q);
}
}
return [...new Set(found)];
}
공백만 정규화하고 그 외에는 손대지 않는 게 중요하다. “아니한다”와 “아니 한다” 정도는 넘어가야 실용적이지만, 조사나 어미까지 느슨하게 맞추면 바꿔치기한 문장을 통과시킨다.
변형과 날조를 가르는 부분은 3-gram 자카드 유사도다:
function trigrams(s) {
const set = new Set();
for (let i = 0; i + 3 <= s.length; i++) set.add(s.slice(i, i + 3));
return set;
}
function jaccard(a, b) {
const A = trigrams(a), B = trigrams(b);
if (A.size === 0 || B.size === 0) return 0;
let shared = 0;
for (const g of A) if (B.has(g)) shared++;
return shared / (A.size + B.size - shared);
}
// 인용문과 길이가 비슷한 창을 원문 위로 밀면서 가장 닮은 구간을 찾는다.
function nearestWindow(quote, source) {
const win = quote.length;
const step = Math.max(1, Math.floor(win / 4));
let best = { score: 0, text: "" };
for (let i = 0; i + win <= source.length; i += step) {
const score = jaccard(quote, source.slice(i, i + win));
if (score > best.score) best = { score, text: source.slice(i, i + win) };
}
return best;
}
한글은 형태소 단위로 쪼개면 라이브러리가 필요해지는데, 글자 3-gram이면 의존성 없이 충분히 갈린다.
실측 결과
계약서 원문을 하나 만들고, 답변에 정확한 인용 1개 · 숫자만 바꾼 인용 1개 · 통째로 지어낸 인용 1개를 넣어 돌렸다. 픽스처는 저장소에 넣어뒀으니 그대로 재현된다.
node scripts/quote-check.mjs \
scripts/fixtures/quote-check/answer.txt \
scripts/fixtures/quote-check/contract.txt
✅ 서면으로 최고한 후 14일이 경과하면 이 계약을 해지할 수 있다
└ scripts/fixtures/quote-check/contract.txt
❌ 배상액은 직전 6개월간 지급된 대금의 총액을 초과하지 아니한다
└ ⚠️ 원문을 고쳐 쓴 것으로 보인다 (유사도 0.78, scripts/fixtures/quote-check/contract.txt)
원문: 배상액은 직전 12개월간 지급된 대금의 총액을 초과하지 아니한
❌ 천재지변으로 인한 이행지체는 을의 귀책사유로 보지 아니한다
└ 원문에 없다 (최고 유사도 0.07) — 지어낸 인용이거나, 인용이 아닌 따옴표(강조·지칭)다
인용 3개 검사 · 실패 2개
원문의 12개월을 6개월로 바꾼 인용은 유사도 0.78로 잡히면서 원문 구절까지 같이 보여준다. 존재하지 않는 면책 조항은 0.07 — 두 실패가 성격이 다르다는 게 출력에 드러난다. 위험한 쪽은 0.78이다. 읽어서는 티가 안 나기 때문이다.
유사도 값은 원문 문장의 구성에 따라 조금씩 달라진다. 절대값을 외울 게 아니라 0.4 이상이면 변형, 0.1 아래면 날조라는 구간으로 쓰면 된다.
하나라도 실패하면 exit 1이라 훅이나 CI에 그대로 물릴 수 있다.
npm run quote -- answer.txt source.txt
인용이 아예 없는 답변도 실패로 처리한다. 근거 없이 단정하는 답변이야말로 검증이 불가능하기 때문이다.
한계는 분명하다. 따옴표로 인용하지 않은 주장은 검사 대상이 아니다. 그래서 이 스크립트는 앞의 프롬프트 기법(“주장마다 인용을 붙여라”)과 세트로 써야 의미가 있다. 프롬프트가 검사 가능한 형태의 답변을 만들고, 스크립트가 그걸 검사한다.
API로 붙인다면 — Citations
API를 직접 쓴다면 인용을 프롬프트로 부탁할 필요가 없다. Citations 기능이 인용 위치를 API 차원에서 보장한다. 문서마다 플래그 하나면 켜진다.
{
"type": "document",
"source": { "type": "text", "media_type": "text/plain", "data": "..." },
"title": "계약서",
"citations": { "enabled": true }
}
프롬프트로 시키는 방식과 결정적으로 다른 점:
| 항목 | 프롬프트로 인용 요청 | Citations 기능 |
|---|---|---|
| 인용문의 진위 | 모델이 지어낼 수 있음 | 제공한 문서를 가리키는 유효한 포인터 보장 |
| 위치 정보 | 없음(또는 모델이 추측) | 평문은 문자 인덱스, PDF는 페이지 번호 |
| 출력 토큰 | 인용문 길이만큼 과금 | cited_text는 출력 토큰에 미포함 |
| 다음 턴 재전송 | 입력 토큰으로 과금 | cited_text는 입력 토큰에도 미포함 |
즉 인용을 많이 시킬수록 프롬프트 방식이 더 비싸진다. 공식 문서도 인용 품질과 비용 양쪽에서 이 기능이 낫다고 안내한다.
실무에서 걸리는 지점 세 개:
- 전부 아니면 전무다. 한 요청 안의 문서 중 일부만 켤 수 없다.
- 구조화 출력과 같이 못 쓴다.
output_config.format과 동시에 쓰면 400이 떨어진다. JSON 스키마로 강제하는 것과 인용 블록을 끼워넣는 게 충돌하기 때문이다. 인용과 정형 출력이 둘 다 필요하면 호출을 두 번으로 쪼개는 수밖에 없다. - 이미지는 인용 못 한다. PDF에서도 텍스트만 인용 대상이다. 스캔 PDF는 애초에 대상이 아니라는 뜻이다.
프롬프트 캐싱·토큰 카운팅·배치 처리와는 같이 쓸 수 있다. 문서를 캐싱해두면 반복 질의 비용이 크게 줄어드는데, 이 계산은 API 요금 구조 쪽에 캐싱 적용 전후 실측을 정리해뒀다.
검증에 드는 비용
검증은 공짜가 아니다. 대략 이렇게 갈린다.
| 방법 | 추가 비용 | 잡아내는 것 |
|---|---|---|
| ”모르면 모른다고” 프롬프트 | 0 (토큰 몇십 개) | 자신 없는 답변의 단정 |
| 인용 검증 스크립트 | 0 (로컬 실행) | 인용문 변형·날조 |
| Citations | 입력 토큰 소폭 증가, 출력은 오히려 절감 | 인용 위치의 진위 |
| N번 돌려 비교 | N배 | 모델이 흔들리는 지점 |
| 사람 최종 검토 | 가장 비쌈 | 나머지 전부 |
앞의 세 개는 사실상 공짜다. 공짜인 것부터 다 켜고, 그래도 남는 위험에만 돈을 쓰는 순서가 맞다. N번 돌리기는 비용이 선형으로 늘기 때문에 전체 답변이 아니라 핵심 숫자·고유명사에만 적용하는 게 현실적이다.
못 막는 것
정직하게 정리하면 이 파이프라인이 못 잡는 게 남는다.
- 인용 없이 하는 주장 — 검사 대상이 아니다. 프롬프트로 강제하는 수밖에 없다.
- 원문 자체가 틀린 경우 — 인용은 완벽히 일치해도 원문이 오래됐거나 틀렸으면 그대로 통과한다.
- 맥락을 자른 인용 — 문장은 진짜인데 앞뒤를 잘라 뜻을 뒤집는 경우. 문자열 대조로는 안 잡힌다.
- 요약·해석의 왜곡 — 인용은 맞는데 그로부터 끌어낸 결론이 틀린 경우.
그래서 공식 문서의 마지막 문장이 결국 맞다 — 중요한 판단은 사람이 검증해야 한다. 이 파이프라인의 목적은 사람을 없애는 게 아니라, 사람이 봐야 할 곳을 줄여주는 것이다. 인용 20개 중 18개가 기계적으로 통과하면 사람은 나머지 2개를 본다.
정리
| 항목 | 결론 |
|---|---|
| 없앨 수 있나 | 없다. 공식 문서가 그렇게 명시한다 |
| 근본 원인 | 정답률만 채점 → 기권보다 찍기가 유리 |
| 공짜로 할 수 있는 것 | ”모른다고 해도 된다” 명시 · 인용 강제 · 로컬 인용 대조 |
| 돈 드는 것 | N번 돌려 비교, 사람 검토 |
| API 쓸 때 | Citations로 인용 위치 보장 (출력 토큰 절감 덤) |
| 남는 위험 | 맥락 절단, 원문 자체의 오류, 해석 왜곡 |
검증을 매번 손으로 돌릴 거면 오래 못 간다. 커밋 전이나 문서 생성 직후에 자동으로 걸리게 만들어야 하는데, Claude Code에서는 스킬로 절차를 고정하는 방식이 잘 맞는다 — 검증 스크립트 실행까지 절차에 포함시켜두면 매번 지시하지 않아도 된다. 사내 문서나 DB를 근거로 붙이려면 MCP 연결 쪽을 먼저 보는 게 순서다. 아직 유료 결제를 안 했다면 무료 플랜에서 되는 것부터 확인해도 늦지 않다.
덧붙이면, 출처를 자동으로 달아주는 서비스라고 해서 이 문제가 사라지지는 않는다. 링크는 진짜인데 그 페이지에 해당 내용이 없거나, 인용된 페이지 자체가 틀린 2차 자료인 경우가 흔하다 — 실제 사례와 함께 퍼플렉시티 정리에서 다뤘다.
거꾸로 검색을 아예 안 하는 도구는 위험이 더 크다. 확인할 원문이 모델 안에 없으니 그럴듯한 문장만 남는다. 뤼튼은 2026년 3월에 인터넷 검색을 종료했고, 공식 고객센터도 학습에 활용할 경우 전문가나 공인서적으로 재확인할 것을 권하고 있다 — 뤼튼 정리에 종료된 기능 목록과 약관상 결과물 책임 조항을 정리해뒀다.
자주 묻는 질문
프롬프트를 잘 쓰면 할루시네이션을 없앨 수 있나요?
줄일 수는 있지만 없앨 수는 없습니다. Anthropic 공식 문서도 완화 기법들을 소개한 뒤 "이 기법들이 할루시네이션을 상당히 줄여주긴 하지만 완전히 제거하지는 못한다"고 명시하고, 중요한 정보는 항상 따로 검증하라고 권고합니다. 프롬프트는 확률을 낮추는 수단이지 보장 장치가 아닙니다.
AI가 인용한 내용이 진짜인지 어떻게 확인하나요?
사람이 눈으로 대조하는 대신 기계로 대조하는 게 정확합니다. 답변에서 따옴표로 감싼 구절을 뽑아 원문에 글자 그대로 존재하는지 검사하면 됩니다. 이 글에 API 키 없이 로컬에서 돌아가는 스크립트를 실측 결과와 함께 실어뒀습니다. API로 붙인다면 Claude의 Citations 기능이 인용 위치를 API 차원에서 보장해줍니다.
Claude의 Citations 기능을 쓰면 비용이 더 드나요?
입력 토큰은 조금 늘어납니다(시스템 프롬프트 추가와 문서 청킹 때문). 반대로 출력 쪽은 유리합니다 — 인용된 원문(cited_text)은 출력 토큰으로 계산되지 않고, 다음 턴에 다시 넣어도 입력 토큰으로 계산되지 않습니다. 프롬프트로 "원문을 인용해줘"라고 시켜서 모델이 직접 뱉게 하는 방식보다 오히려 저렴할 수 있습니다.
왜 AI는 모르면 모른다고 안 하나요?
그렇게 하도록 훈련·평가되지 않았기 때문입니다. 대부분의 벤치마크가 정답률만 채점하는데, 이러면 모르는 문제를 빈칸으로 두면 무조건 0점이고 찍으면 맞을 확률이라도 생깁니다. 기권에 점수를 주지 않는 채점 방식이 추측을 보상하는 구조라는 게 관련 연구의 지적입니다. 그래서 프롬프트에서 "모르면 모른다고 답해도 된다"고 명시적으로 허용해주는 것만으로도 효과가 있습니다.