AI 에이전트수정 2026-08-08

'환각 0'이 실제로 보장하는 것 — groundedness와 그 대가

검증 노드가 재는 것은 사실성이 아니라 문서와의 일치이며, 문서가 틀리면 통과한다. 다섯 가지 한계와 코드에서 센 LLM 호출 횟수, 그리고 실습 코드에 남아 있는 함정 여덟 개를 정리한다.

"환각을 0으로 만들었다"는 문장이 자료 제목에 그대로 등장한다. 코드는 실제로 잘 돌고, 근거 없는 답변이 걸러지는 것도 사실이다. 그런데 이 표현은 틀렸다. 검증 노드가 재는 것은 사실성이 아니기 때문이다.

이 글은 그 구분에서 시작한다. 코드가 실제로 보장하는 것과 보장하지 못하는 것을 분리하고, 그 검증을 붙이는 대가를 LLM 호출 횟수로 세고, 마지막으로 이 시리즈에서 다룬 네 구현에 남아 있는 함정 여덟 개를 목록으로 남긴다. 앞 편까지 네 변형의 구조를 전부 봤다면 여기가 그 결산이다.

용어 정리

앞 세 편의 용어표에서 이 글이 쓰는 행만 추렸다.

약어 / 용어원어
Groundedness근거성답변이 주어진 문서에 근거하는가
Factuality사실성답변이 세상의 사실과 맞는가. groundedness와 다르다
Graderyes/no 정형 라벨을 뱉는 LLM 판정기
Reflection TokenSelf-RAG 원논문에서 모델이 직접 생성하도록 학습시킨 자기평가 특수 토큰
Knowledge RefinementCRAG 원논문의 단계. 문서를 분해했다가 재조합해 노이즈를 제거
recursion_limitLangGraph 그래프가 실행할 수 있는 최대 스텝 수
Send APILangGraph에서 작업을 여러 갈래로 팬아웃하는 API

측정되는 것은 사실성이 아니다

도식을 탭하면 확대해서 볼 수 있습니다

판정 프롬프트 원문이 grounded in / supported by a set of retrieved facts다. 즉 **"검색된 문서와 어긋나지 않는가"**만 본다. 문서가 세상의 사실과 맞는지는 이 구조 어디에서도 확인하지 않는다.

groundedness와 factuality는 다른 것을 재는 지표다. 전자는 답변과 문서의 일치를, 후자는 답변과 사실의 일치를 본다.

이 둘을 구분하지 않으면 검증 루프의 값어치를 과대평가하게 된다. 검증 루프가 하는 일을 정확히 말하면 **"근거 없는 답변이 사용자에게 도달하는 경로를 차단"**하는 것이다. 그 이상을 주장하면 근거를 대야 한다.

함께 따라붙는 다섯 가지 한계

#한계왜 문제인가
L1문서가 틀리면 환각도 통과한다문서가 오래되었거나 오류가 있으면, 그 오류를 충실히 옮긴 답변은 grounded=yes로 통과한다. 검증되는 것은 출처 일치일 뿐
L2판정자도 같은 종류의 LLM이다생성기·판정기 모두 gpt-4o-mini. 생성기가 놓친 오류를 판정기도 같은 편향으로 놓칠 수 있다. 자기채점(self-grading)의 순환 문제
L3재시도 상한이 없다generate → generate 자기 루프에 카운터가 없다. 환각이 반복 판정되면 기본 재귀 한도에 걸릴 때까지 재생성하며 토큰을 태운다
L42진 판정이라 부분 환각을 못 잡는다5문장 중 1문장만 지어낸 답변에 대해 전체 yes/no만 나온다. 어느 문장인지도 알려주지 않아 재생성이 맹목적이다
L5검증 자체의 비용·지연이 크다문서 N개 채점 + 환각 + 적합성 = 최소 N+2회 추가 호출. 사용자 대면 실시간 응답에는 그대로 쓰기 어렵다

다섯 중 L1과 L2가 성격이 다르다. L3·L4·L5는 구현을 고쳐 완화할 수 있지만, L1·L2는 구조에서 온다. 문서 품질을 검증하려면 또 다른 근거가 필요하고, 판정 편향을 없애려면 판정자가 생성자와 다른 계열이어야 한다. 둘 다 이 파이프라인 안에서는 풀리지 않는다.

L2의 완화책이 실무에서 가장 자주 빠진다. 생성기와 판정기를 다른 모델 계열로 두는 것만으로도 편향 공유가 줄어든다.

그런데 이 조치가 아래 비용 절과 충돌한다. 판정을 소형 저가 모델로 계층화하는 것이 비용 대책인데, 같은 벤더의 소형 모델은 대형 모델과 편향을 공유할 가능성이 크다. 비용 최적화와 편향 분리가 반대 방향을 가리킨다.

원논문과 구현의 거리

이 시리즈의 구현은 논문의 간이 재현이다. 무엇이 빠졌는지를 알아야 한계도 정확히 말할 수 있다.

원논문이 시리즈의 구현
Self-RAG모델이 reflection token(검색 필요 여부·관련성·근거성·유용성)을 직접 생성하도록 학습시킨다. 판정이 생성 과정에 내재별도 LLM Grader 체인으로 외부에서 근사. 학습 없이 프롬프트만으로 구현
CRAG경량 retrieval evaluator가 Correct / Incorrect / Ambiguous 3단계 신뢰도를 산출하고, knowledge refinement(문서 분해 후 재조합)로 노이즈를 제거binary yes/no + 웹검색 추가만. refinement 단계 없음

근사 구현의 장점은 학습 없이 어떤 LLM으로도 즉시 만들 수 있다는 것이고, 단점은 판정 품질이 프롬프트에 전적으로 의존한다는 것이다.

이 트레이드오프가 실무 선택에서 직접 값을 한다. 판정 품질이 시스템 상한을 결정하는데 그 품질을 올리는 수단이 프롬프트뿐이라면, 판정 프롬프트를 형상관리 대상으로 두고 회귀 테스트를 붙여야 한다. 프롬프트를 버전 관리하는 실제 사례는 관련성 검증 편에 네 번의 개정 기록으로 남아 있다.

정확한 표현으로 바꾸기

부정확한 표현정확한 표현
"환각을 0으로 만들었다""생성 결과를 문서 근거와 대조하는 검증 노드를 넣어, 근거 없는 답변이 사용자에게 나가는 경로를 차단했다"
"Self-RAG를 구현했다""Self-RAG 논문의 아이디어를 LLM Grader 체인으로 근사 구현했다. 원논문의 reflection token 학습 방식과는 다르다"
"환각률을 측정했다"측정 데이터가 없다면 쓰지 않는다

세 행이 같은 규칙의 사례다. 보장 범위를 좁혀 쓰면 그 안에서는 주장이 성립한다. 넓히면 근거가 따라가지 못하고, 근거가 없으면 좁은 주장까지 함께 흔들린다.

비용 — 코드에서 센 LLM 호출 횟수

아래는 코드의 호출 경로를 센 값이며, 검색 문서 수를 N, 재시도 없음을 가정한다. 실측 벤치마크가 아니다.

변형라우팅에이전트문서 채점생성환각적합성합계웹 API
Naive RAG11
Agentic RAG1113
Self-RAGN111N+3
CRAG (폴백 미발동)N1N+10
CRAG (폴백 발동)N1N+2 (재작성 1 포함)1
Adaptive RAG (벡터 경로)1N111N+4
Adaptive RAG (웹 경로)111141

일곱 행 중 마지막이 유일하게 N에 의존하지 않는다. 웹 경로에는 문서 채점 단계가 없기 때문이다. 라우팅이 비용 절감 장치이기도 하다는 사실이 여기서 드러난다 — 인덱스 밖 질문을 벡터 경로에서 빼내면 채점 N회가 통째로 사라진다.

재시도가 발생하면 다음이 더해진다.

재시도 유형추가 호출
not supported 1회당생성 1 + 환각 1 (+ 적합성 1)
not useful 1회당재작성 1 + 검색 + 채점 N + 생성 1 + 환각 1 + 적합성 1

not useful 루프 한 번이 거의 전체 파이프라인 재실행이다. 상한 설정이 왜 필수인지가 여기서 나온다.

문서 채점 N회는 순차 실행이라 지연에 그대로 누적된다. Send API로 문서별 채점을 팬아웃하거나, 여러 문서를 한 프롬프트에 담아 배치 채점하면 N회를 1~2회로 줄일 수 있다.

또 하나는 모델 계층화다. 채점은 소형·저가 모델, 생성은 고성능 모델로 분리한다. 다만 앞서 L2에서 짚었듯 같은 계열 소형 모델은 편향을 공유하므로, 비용만 보고 고르면 판정 신뢰도를 함께 깎는다.

코드 함정 여덟 가지

실습을 재현할 때 걸리는 지점들이다.

#위치함정대응
C1Agentic RAG grade_documents반환 타입 Literal[...] 애노테이션을 지우면 조건 엣지 분기가 성립하지 않는다 (경로 맵 인자를 안 넘기므로)애노테이션 유지 또는 경로 맵 명시
C2Self-RAG·Adaptive RAG grade_generation_v_documents_and_questionelse 분기에서 pprint(...) 호출. Adaptive RAG 쪽은 pprint끝까지 import하지 않아 환각 판정 시 NameErrorprint로 교체하거나 import 추가
C3Adaptive RAG web_searchdocuments에 리스트가 아닌 Document 단일 객체를 넣는다. 현재는 곧장 generate로 가서 터지지 않지만, grade_documents를 거치게 바꾸면 순회에서 깨진다[web_results]로 감싸기
C4Self-RAG·CRAGretriever.get_relevant_documents(...)는 폐기 예정 APIretriever.invoke(...)
C5Agentic RAG·Self-RAG·CRAGlangchain_core.pydantic_v1은 폐기 경로from pydantic import BaseModel, Field
C6CRAG web_searchdocuments.append(...)로 State 안의 리스트를 제자리 변경. 리듀서 없는 TypedDict라 동작하지만 부작용이 있는 패턴새 리스트를 만들어 반환
C7전 구현Self-RAG·Adaptive RAG 그래프에 재시도 카운터가 없다State에 retry_count 추가 후 조건 분기에서 상한 체크
C8Agentic RAGgenerate 노드에서 format_docs를 정의만 하고 쓰지 않는다 (문자열을 그대로 전달)데드코드로 인지

여덟 항목이 세 무리로 갈린다.

무리항목성격
지금 터진다C2특정 분기에서 즉시 예외
나중에 터진다C1, C3, C6지금은 우연히 동작. 코드를 고치면 깨진다
안 터지고 새어 나간다C4, C5, C7, C8폐기 API·비용·데드코드

가운데 무리가 가장 위험하다. 지금 동작한다는 사실이 설계가 옳다는 증거로 읽히기 때문이다.

C3가 교과서적이다. Document 단일 객체를 넣어도 지금 흐름에서는 곧장 generate로 가므로 아무 일도 일어나지 않는다. 그런데 나중에 "웹 결과도 채점하자"는 자연스러운 개선을 하는 순간 순회에서 깨진다. 타입 계약을 지금 지키는 비용보다, 나중에 왜 깨졌는지 찾는 비용이 훨씬 크다.

C7은 이 시리즈 전체를 관통하는 결함이다. 1편의 실패 분류표에서 F5(무한 재시도)에 "어느 변형도 못 막는다"고 적은 근거가 이 항목이고, Agentic RAG가 recursion_limit=10을 건 것도 그래프 안이 아니라 실행 시점 옵션이었다. 상한을 그래프 안에 넣으려면 State에 카운터 채널을 하나 두고 조건 분기에서 읽으면 된다 — 앞 시리즈에서 본 리듀서 없는 채널이 정확히 이 용도에 맞는다.


여기까지가 판정기를 꽂아 경로를 바꾸는 RAG다. 판정 하나에서 넷까지 늘렸고, 그 대가가 호출 횟수의 선형 증가라는 것까지 봤다.

그런데 지금까지의 그래프는 전부 에이전트 하나였다. 노드가 여럿이어도 판단하는 주체는 하나이고, 도구와 판정기가 그 하나에 매달려 있는 구조다. 도구가 여섯 개를 넘어가고 역할이 서로 충돌하기 시작하면 무엇을 해야 하는가. 쪼개고 나면 누가 다음 순서를 정하는가. 이어지는 시리즈에서 단일 에이전트의 경계와 멀티에이전트 토폴로지를 다룬다.

"환각 0"이라는 표현이 왜 위험한지, 판정기가 틀릴 때 무엇을 할 수 있는지, 개선을 무엇으로 측정하는지는 운영 Q&A에 이어져 있다.