baesisi
← 목록으로
·arXiv 2026·논문리뷰

Beyond Document Grounding: Span-Level Hallucination Detection over Code, Tool Output, and Documents

코드·도구 출력·구조화 문서까지 포괄하는 span-level 환각 탐지 통합 벤치마크와 2B 생성형 탐지기로 자연어 전용 모델·LLM judge를 코드 영역에서 크게 앞섬

#논문리뷰#Paper Review#LLM#환각탐지#Hallucination Detection#벤치마크#Benchmark
티스토리에서 원문 보기 →

KR Labs 쪽에서 2026년 7월 arXiv에 올린 span-level hallucination detection 논문을 읽고 정리했다. RAG hallucination detection 연구는 대부분 검색된 문서와 생성된 답변을 비교하는 자연어 설정에서 이뤄졌다. 그런데 코딩 에이전트나 개발자 도구처럼 근거가 코드나 커맨드 출력인 경우는 어떻게 평가할지 궁금했고, 이 논문이 그 부분을 직접 다룬다. 저자들의 이전 작업인 LettuceDetect(Kovács & Recski, 2025)에서 이어지는 연구이고, 이번 논문은 학습 데이터 범위를 코드, 도구 출력, 마크다운 문서까지 넓히고, 기존 인코더 구조와 함께 생성형 탐지기를 새로 학습시켰다.

  • 저자: Ádám Kovács, Bowei He, Xue Liu, István Boros, Szilveszter Tóth, Gábor Recski (KR Labs, MBZUAI, McGill University, TU Wien)
  • 게재: arXiv 프리프린트 2607.00895 (2026)
  • 코드: KRLabsOrg/LettuceDetect, 모델과 데이터셋: huggingface.co/KRLabsOrg

0. 한 줄 요약

코드, 도구 출력, 구조화된 문서, 기존 자연어 RAG 데이터를 하나의 span-level hallucination detection 과제로 묶은 벤치마크를 만들고, 여기에 Qwen3.5-2B를 파인튜닝한 탐지기가 코드 과제에서 기존 자연어 전용 탐지기와 대형 zero-shot LLM judge를 크게 앞섰다는 논문이다.

1. 문제 정의

실제 grounded generation 시스템이 참고하는 근거는 문서 텍스트만이 아니다. 코딩 에이전트는 레포지토리 파일, git 히스토리, 테스트 출력을 보고 일하고, 개발자 어시스턴트는 커맨드 출력이나 tool observation을 요약하며, 연구용 시스템은 마크다운 페이지와 표, 인용을 검색해서 쓴다. 그런데 기존 벤치마크와 탐지기는 거의 자연어 문서 대 자연어 답변 구도에서만 평가됐다. 생성된 코드, 도구 출력, 구조화 문서를 같은 검증 과제로 함께 다루는 span-level 벤치마크가 없었다는 게 저자들의 문제의식이다.

과제 설정은 이렇다. 이미 생성된 답변 aa와 그 답변을 만든 요청 qq, 근거 cc가 주어지면, aa 안에서 qq와 cc로 뒷받침되지 않는 문자 구간을 찾아내야 한다. 답변 전체를 맞다 틀리다로 판정하지 않고 구간을 짚게 한 이유가 설득력 있다. 코드나 도구 출력에서는 잘못된 필드 하나, 없는 메서드 이름 하나, 잘못 옮긴 값 하나가 나머지가 다 맞더라도 프로그램 동작을 바꿔 버린다. 그러니 검증기는 답변을 통째로 거부하기보다 문제가 되는 부분 문자열을 가리켜야 한다는 것이다.

탐지기는 생성 모델의 logit이나 도구 실행 궤적을 보지 않는다. 답변이 나온 뒤에 외부 검사자가 볼 수 있는 입력만 본다. 생성 중간에 개입하는 방법이 아니라 사후 검증이라는 점을 알고 읽으면 된다.

2. 기존 방법들의 한계

논문이 다루는 기존 연구를 나는 세 갈래로 나눠 봤다.

첫째는 자연어 RAG용 탐지기다. RAGTruth, Luna, MiniCheck, LettuceDetect 모두 생성된 텍스트를 검색된 문서와 비교하도록 만들어졌다. 이 계열은 저자들이 직접 실험으로도 확인했다. 자연어 RAG 데이터로 학습한 LettuceDetect-large를 코드 테스트셋에 돌리면 span-F1이 0.17밖에 나오지 않는다(§6.4).

둘째는 코드 hallucination 연구다. CodeHalu와 CodeMirage는 생성된 코드 스니펫 자체의 결함을 보고, Collu-Bench는 생성 도중 토큰 확률과 실행 피드백으로 hallucination을 예측하며, AgentHallu는 에이전트 궤적 전체에서 실패 원인을 추적한다. 저자들이 가장 비슷하다고 꼽는 Delulu도 실행으로 검증한 fill-in-the-middle 벤치마크라 라벨이 accept/reject 이진값이다. 이 논문이 하려는 생성 후, 레포지토리 근거 기반, span 단위 검증과는 문제 설정이 다르다. 다만 이 계열과의 비교는 서술로만 이뤄지고 실험 비교는 없다.

셋째는 FAVA처럼 오류 유형을 세밀하게 나눈 분류 체계다. 유용하지만 이것도 주로 텍스트 문서를 근거로 한 자연어 답변을 염두에 두고 설계됐다.

3. 방법

저자들은 기여를 span-level 과제 정의, 통합 벤치마크, 탐지기 두 계열의 세 가지로 적는다. 개인적으로는 이 논문의 핵심이 코드, 도구 출력, 구조화 문서, 자연어 문서를 하나의 span-level 과제로 묶은 벤치마크라고 본다. 오류 분류 체계, edit-based injection, 코드 테스트셋 검토는 이 벤치마크를 만들기 위한 부품으로 읽었다.

오류 분류는 상위 범주 3개로 시작한다. 근거와 충돌하는 contradiction, 요청받지도 근거에 나오지도 않는 내용을 덧붙이는 unsupported addition, 없는 메서드나 속성, 키워드 인자, 섹션, 식별자를 지어내는 fabricated reference다. 여기에 entity, temporal, numerical, value, relational, identifier, section, attribute, claim, behavior, elaboration, subjective, unspecified라는 하위 유형 13개가 붙는다. RAGTruth, FAVA, CodeHalu의 구분을 합치고 코드와 도구 출력에 필요한 라벨을 더해 만들었다고 한다(§3). 코드에서는 앞의 두 범주를 주로 요청에 비춰 판단하고, fabricated reference는 레포지토리와 라이브러리 근거에 비춰 판단한다.

데이터 구축은 정답에서 출발한다. 먼저 소스별로 근거, 요청, 정답이 확실한 답변을 준비한다. 코드는 SWE-bench의 골드 패치를 그대로 정답으로 쓰고, 도구 출력과 마크다운 소스는 LLM에게 근거에만 기반해 답하라고 시켜 정답을 만든다. 그다음 injector LLM에게 답변을 직접 고쳐 쓰게 하지 않고, "original → hallucinated" 쌍으로 된 국소 교체 편집만 JSON으로 내놓게 한다. 파이프라인이 이 편집을 기계적으로 적용하니까 환각 구간의 정확한 문자 오프셋이 자동으로 나온다. 논문 Figure 1의 예시로 보면 torch.cuda.set_device(gpu)의 set_device를 set_active_device로 바꾸고, 라벨은 set_active_device에만 붙는다. 주변의 맞는 호출 구조까지 라벨에 들어가지 않으니 구간이 좁게 유지된다. injector는 코드에 Gemma 4 31B, 나머지 소스에 Qwen 3.6 35B A3B를 썼다.

이 방식은 생성 수율을 포기하고 라벨 품질을 얻는 쪽을 택한 것이다. original 문자열이 답변에서 한 번만 나오지 않거나, 편집이 아무것도 바꾸지 않거나, 환각 구간이 답변의 너무 많은 부분을 덮거나, 지어낸 이름이 사실 근거에 이미 있으면 버린다. 코드 주입 시도 중 자동 점검을 통과하는 건 대략 절반이라고 한다. "incorrect" 같은 힌트 단어가 들어간 경우도 걸러내는데, 탐지기가 검증 대신 생성 과정의 흔적을 배우는 걸 막으려는 장치다.

코드 소스는 신경 쓴 부분이 더 있다. 골드 패치는 함수 전체, 바뀐 hunk만, "파일 X에서 Y를 Z로 바꿔라" 같은 편집 지시문의 세 가지 형태로 렌더링한다. SWE-bench 이슈 원문은 재현 로그나 긴 토론을 걷어내고 짧은 개발자 요청으로 다시 쓴다. unsupported addition은 문법상 문제없고 실제 레포지토리 심볼만 쓰더라도 요청에 없는 동작이면 환각이기 때문에 요청이 입력에 꼭 들어가야 한다.

개인적으로 가장 잘 설계했다고 느낀 건 reference grounding이다. 정답 코드는 같은 클래스의 다른 메서드, import한 헬퍼, 서드파티 API를 자주 부르는데, 잘린 컨텍스트에 그 정의가 없으면 검증기 입장에서는 맞는 참조와 지어낸 참조를 구분할 수 없다. 그래서 수정된 파일, import된 레포지토리 모듈, 변경된 파일이 import하는 모듈에서 참조된 정의를 찾아 붙인다. 이때 현재 코드가 아니라 base commit 시점 기준으로 찾는다. 서드파티 API는 Context7이라는 라이브러리 문서 인덱스에서 시그니처를 받아 붙인다(Figure 3). 이렇게 해야 실제로 있지만 낯선 라이브러리 API가 지어낸 것으로 처리되지 않는다. 다만 실제로 쓰려면 답변이 참조하는 정의를 base commit 기준으로 찾아 붙여 주는 단계가 따로 있어야 한다.

논문 Figure 2의 예시가 이 과제가 왜 어려운지 잘 보여준다. matplotlib에서 plt.xticks()에 minor 키워드를 추가해 달라는 요청에, 답변 코드는 ax.set_xticks(ticks, minor=not minor)와 ax.get_yticks(minor=not minor)처럼 minor의 의미를 뒤집어 놓았다. 문법도 맞고 실제 존재하는 메서드만 썼는데 의도와 반대로 동작한다.

탐지기는 두 종류다. 주력인 LettuceDetect-Qwen-2B는 Qwen3.5-2B를 LoRA로 파인튜닝해서 범주와 하위 유형이 달린 span을 JSON으로 출력하게 만든 생성형 모델이다. rank 32, α=64, learning rate 2e-4, 2 epoch, effective batch 32, 최대 시퀀스 길이 32,768토큰이다. 출력한 문자열은 답변에서 다시 찾아 문자 오프셋으로 바꾼다. 비교용 LettuceDetect-mmBERT-base는 307M 파라미터 mmBERT-base 인코더로 토큰 분류를 하고, 최대 길이는 8,192토큰이다. 학습셋은 새로 만든 74,285개에 RAGTruth와 PsiloQA를 변환해 합쳐서 train/validation/test가 145,250 / 6,171 / 10,698개다.

4. 실험 결과

새로 만든 소스는 코드(SWE-bench, 18,524개), 도구 출력(Squeez, 11,365개), ACL 논문 청크(5,355개), README(13,803개), Wikipedia(25,238개)이고 각각 절반 정도가 환각 예제다. 코드 소스는 53개 레포지토리에서 왔고, 레포지토리 단위로 35/6/12개씩 겹치지 않게 나눠서 16,319 / 190 / 2,015개로 분할했다. 테스트셋에는 학습 때 보지 못한 레포지토리만 들어간다.

주 결과인 Table 2를 보면 LettuceDetect-Qwen-2B의 전체 span-F1은 0.689, example-F1은 0.921이다. 소스별로는 README 0.866, Wikipedia 0.817이 가장 높고, 새로 만든 소스 중에서는 코드가 0.602로 가장 낮다. 저자들은 컨텍스트가 길고, 답변에 새 코드가 많이 들어가고, 단순한 사실 충돌이 아니라 의도를 잘못 구현한 오류가 섞여 있어서라고 설명한다. mmBERT 인코더(전체 0.642, 코드 0.508)는 모든 소스에서 Qwen-2B보다 낮았다.

가장 인상적인 건 코드 테스트셋만 따로 본 Table 4다. 자연어 RAG로 학습된 LettuceDetect-large가 0.172, Nemotron-3-Ultra-550B가 일반 프롬프트로 0.186, 과제에 맞춘 프롬프트로 0.216, gpt-oss-120b가 과제에 맞춘 프롬프트로 0.212였다(일반 프롬프트로는 0.177, Table 2). Qwen-2B의 0.602는 LettuceDetect-large의 약 3.5배다. zero-shot judge들은 recall은 0.50.67로 꽤 되는데 precision이 0.110.14에 머문다. 진짜 오류 구간도 어느 정도 잡지만 그보다 훨씬 많이 찍는다는 뜻이다. 반면 Qwen-2B는 precision 0.596, recall 0.609로 균형이 맞는다.

HHEM-2.1-Open, Lynx-8B, Granite-Guardian-4.1-8B, MiniCheck-7B 같은 답변 단위 탐지기도 같은 경향을 보였다. MiniCheck-7B는 precision 0.50, recall 1.00이다. 코드 테스트셋의 환각 비율이 50.3%라는 걸 같이 생각하면, 내 계산으로는 거의 모든 답변을 환각이라고 찍은 것과 비슷하다. 저자들은 이 모델들이 새로 작성된 정상 패치 코드를 근거 없는 내용으로 판정하는 경우가 많다고 분석한다. 내 해석으로는, 근거 문서에 없는 문장을 의심하도록 학습된 모델에게는 원래 코드에 없던 줄을 새로 쓰는 패치가 모두 근거 없는 추가로 보였을 것 같다.

자연어 벤치마크와의 비교(Table 3)도 나쁘지 않다. RAGTruth example-F1은 81.8로 RAG-HAT 8B의 83.9보다는 낮지만 LettuceDetect-large(79.2), 파인튜닝한 Llama-2-13B(78.7)보다 높고 GPT-4(63.4)보다는 18.4포인트 높다. PsiloQA 영어 IoU는 0.724로 PsiloQA 저자들의 mmBERT-base(0.707)보다 높고, 14개 언어 평균 IoU도 0.689로 0.623보다 높다. 코드까지 같이 학습한 모델이 자연어 벤치마크에서도 특화 시스템과 견줄 만한 수치를 냈다는 점이 의미 있다고 생각한다. 다만 저자들도 Table 3은 발표된 숫자와 자기들 평가를 섞은 참고용이지 같은 조건의 리더보드가 아니라고 적어 두었다.

span과 함께 유형까지 맞혀야 점수를 주면 성능이 떨어진다(§6.5). 범주까지 맞혀야 하는 category-gated span-F1은 0.585, 하위 유형까지 맞혀야 하는 subcategory-gated는 0.468로, 이진 span-F1 0.689보다 낮다. 자연어 예제에서는 claim, elaboration, value, relational이 서로 겹쳐서 특히 헷갈린다고 한다. 인코더로 span을 찾고 별도 head로 유형을 붙이는 cascade도 실험했는데, 정답 span을 주면 범주 정확도 0.82, 하위 유형 0.64가 나오지만 end-to-end로는 0.461 / 0.315로 생성형 모델보다 낮았다.

오류 분석(§6.6)에서 코드에 남은 어려운 사례는 요청 기준의 의도 오류와 넓은 범위의 생성 편집이다. 잘못된 필드명이나 조건문이 실제 레포지토리 심볼을 그대로 쓰면 구조상으로는 멀쩡해 보인다. 도구 출력에서도 매끄러운 답변이 버전, 개수, 파일명, 상태 중 하나만 잘못 옮기는 패턴이 반복됐다. zero-shot judge 중 Nemotron-3-Ultra는 과제에 맞춘 프롬프트로 precision이 0.11에서 0.13으로만 올랐다. 작은 규모의 reasoning judge 진단에서는 잘린 컨텍스트를 지어낸 참조의 증거로 보거나, 편집 지시문 형태의 답변에서 원문과 다른 문자열을 span으로 내놓는 오류가 있었다.

5. 읽으면서 든 의문

첫 번째는 라벨 대부분이 LLM이 주입한 합성 라벨이라는 점이다. 저자들도 Limitations에서 이걸 먼저 인정한다. 코드 테스트셋만 검토를 거쳤고, 새로 만든 소스의 train/dev와 코드 외 테스트셋은 자동 점검만 통과한 생성 라벨이다(RAGTruth는 원래 사람이 단 라벨이다). 그리고 이 벤치마크를 실제 환경의 hallucination 발생 빈도로 읽으면 안 된다고 명시한다. 개인적으로는 분포 쪽이 더 궁금했다. 코드에서 정상 예제는 사람이 쓴 골드 패치를 렌더링한 답변이고, 환각 예제는 Gemma가 그 답변을 국소적으로 고친 것이다(환각 예제당 span은 평균 2개 미만). 실제 코딩 에이전트가 내는 오류가 이렇게 국소적이고 작은 편집 형태일지, 탐지기가 injector의 편집 습관 같은 걸 배우지는 않았을지는 이 논문만으로는 알기 어렵다. 저자들이 힌트 단어를 거르고 같은 인스턴스의 정상/환각 쌍을 함께 넣지 않는 장치를 둔 건 이 문제를 의식했기 때문일 것이다.

두 번째는 코드 테스트셋 검토 방식이다. 2,038개를 검토해 2,015개를 남겼고, 경계 235개를 조정하고 잘못된 span 23개를 지우는 등 꽤 꼼꼼히 했다. 다만 1차로 문제 후보를 표시한 건 Claude Sonnet 4.6이고, 표시된 사례를 블라인드로 다시 판정한 뒤 저자들이 최종 결정을 내렸다. 저자들도 여러 명이 독립적으로 주석한 것이 아니라 모델 보조 검토라고 밝히고 있다. 저자들은 코드 테스트 샘플을 모두 검토했다고 쓰지만, 사람이 다시 판정한 건 모델이 표시한 사례였다고 읽혀서, 모델이 넘긴 예제를 사람이 어디까지 봤는지는 분명하지 않다. 판정 기록과 루브릭을 전부 공개한 건 좋았다.

세 번째는 통계적 신뢰도다. Table 2~4 모두 한 번 돌린 수치로 보이고 시드, 반복 실행, 신뢰구간이 없다. 학습에 쓴 계산 자원도 보고하지 않았다. Qwen-2B와 zero-shot judge의 차이처럼 0.4포인트가 벌어지는 비교는 결론이 흔들릴 것 같지 않다. 하지만 Qwen-2B와 mmBERT의 PsiloQA 차이(0.732 대 0.714)나 RAGTruth에서 RAG-HAT과의 차이 정도는 실행 간 분산을 모르면 판단하기 어렵다.

네 번째는 Qwen-2B와 mmBERT 비교에서 조건이 여러 개 동시에 다르다는 점이다. 파라미터 수(2B 대 307M), 최대 컨텍스트(32,768 대 8,192토큰), 생성형 대 토큰 분류 구조가 한꺼번에 바뀌어서 성능 차이가 어디서 왔는지 나눠 볼 수 없다. 코드 예제는 평균 답변만 1.8k자 정도이고 reference grounding으로 정의를 붙이면 컨텍스트가 더 길어지니까, 8,192토큰 제한이 코드 성능에 영향을 줬을 수도 있겠다는 생각이 들었다. 내 해석으로는, 같은 프롬프트로 파인튜닝한 LFM2.5-8B-A1B가 코드에서 0.507로 mmBERT와 비슷했으니 파라미터 수만으로 설명되는 차이는 아닌 것 같다. 다만 LFM의 학습 조건은 Qwen만큼 자세히 나와 있지 않아서 확실하게 말하긴 어렵다.

다섯 번째는 Table 4에 지표가 다른 두 종류의 숫자가 같이 있다는 점이다. span 시스템은 문자 단위 span P/R/F1이고, HHEM이나 MiniCheck 같은 답변 단위 시스템은 환각 클래스의 example 단위 P/R/F1이다. 답변 단위 탐지기의 F1 0.61~0.67을 Qwen-2B의 span-F1 0.602와 나란히 놓으면 오히려 답변 단위 쪽이 나아 보일 수 있다. 같은 기준으로 맞춰 보려면 Qwen-2B의 example-F1 0.835와 비교해야 한다. 답변 단위 모델은 원래 span을 내도록 설계된 모델도 아니다. 표 캡션에 설명은 있지만 읽을 때 주의가 필요했다.

마지막은 실제 배포 환경과의 차이다. 코드 예제는 검증기가 base commit 시점 기준으로 찾은 참조 정의와 서드파티 라이브러리 시그니처까지 붙은 컨텍스트를 받는다는 전제로 만들어졌다. 실제 서비스에서 검증기가 매번 이 정도 컨텍스트를 받을 수 있을지는 다루지 않는다. 오류 분석의 작은 reasoning judge 진단에서 잘린 컨텍스트를 지어낸 참조의 증거로 착각한 사례가 나온 걸 보면, 컨텍스트가 덜 갖춰진 환경에서는 파인튜닝한 탐지기도 비슷한 문제를 겪을 수 있다고 본다. 저자들도 벤치마크가 최종 답변 검증만 다루고 에이전트 궤적 전체는 다루지 않는다고 범위를 밝혀 두었다.

6. 정리

가장 설득력 있었던 근거는 Table 4다. 학습 때 보지 못한 레포지토리로 만든 코드 테스트셋에서, 자연어 전용 탐지기(0.17)와 1,200억~5,500억 파라미터급 zero-shot judge(최대 0.22)를 2B 파인튜닝 모델이 0.60으로 앞섰다. 오류 분석에서도 judge들이 새로 작성된 정상 패치 코드를 근거 없는 내용으로 찍는 방식으로 실패한다는 게 구체적으로 드러난다. 범용 LLM judge가 이 과제에서는 과제에 맞춘 학습을 대신하지 못한다는 저자들의 결론은 이 결과로 충분히 뒷받침된다고 생각한다. 그러면서 RAGTruth와 PsiloQA에서도 기존 특화 시스템과 견줄 만한 성능을 냈다. 다만 자연어 데이터만으로 학습한 같은 모델과 비교하지는 않아서, 코드 데이터를 섞은 영향이 얼마나 되는지는 알 수 없다.

약점도 분명하다. 라벨 대부분이 합성 주입이고, 코드 테스트셋 검토도 모델 보조 방식이며, 반복 실행이나 신뢰구간이 없고, 모델 비교에서 크기와 컨텍스트 길이, 구조를 나눠 보지 않았다. 그래서 새로운 과제를 정의하고 데이터를 공개했다는 기여는 분명하지만, 각 수치를 얼마나 정밀하게 믿을지와 실제 에이전트 출력에도 그대로 통할지는 조심해서 봐야 한다.

이 논문에서 하나 가져간다면 라벨을 만드는 방식이다. 답변을 통째로 다시 쓰게 하지 않고 original/hallucinated 편집 쌍만 받아 기계적으로 적용하니까, 코드와 자연어가 섞인 답변에서도 문자 단위 라벨이 깔끔하게 나온다. 그리고 코드 hallucination을 판정하려면 탐지 모델만큼이나 무엇을 근거로 보여 주느냐가 중요하다는 것도 reference grounding 설계에서 다시 느꼈다.

7. 같이 읽어볼 논문

  • Niu et al. (ACL 2024), RAGTruth: A Hallucination Corpus for Developing Trustworthy Retrieval-Augmented Language Models. 이 논문이 통합한 자연어 RAG 벤치마크이고, 상위 범주를 나눌 때 FAVA와 함께 참고한 분류 체계다.
  • Kovács & Recski (2025), LettuceDetect: A Hallucination Detection Framework for RAG Applications. 같은 저자들의 이전 작업으로, 이번 논문의 mmBERT 탐지기가 이 토큰 분류 구조를 그대로 따른다.
  • Rykov et al. (Findings of EMNLP 2025), When models lie, we learn: Multilingual span-level hallucination detection with PsiloQA. 14개 언어 span-level 벤치마크로, 이 논문 학습 데이터의 다국어 부분이 여기서 왔다.
  • Song et al. (EMNLP 2024 Industry Track), RAG-HAT: A Hallucination-Aware Tuning Pipeline for LLM in Retrieval-Augmented Generation. RAGTruth example-F1에서 이 논문보다 높은 83.9를 기록한 특화 모델이다.
  • Su et al. (2025), Learning to reason for hallucination span detection. span-F1을 강화학습으로 직접 최적화하는 방식이라 SFT만 쓴 이 논문과 비교해 보기 좋다.