본문으로 건너뛰기

© 2026 Molayo

Zenn헤드라인2026. 06. 15. 15:20

Claude Code가 존재하지 않는 파일을 '생성했다'고 계속 보고한 이유 — 툴 결과의 작화(confabulation)가 발생한 조건

요약

Claude Code 사용 중 에이전트가 파일을 생성하거나 설정을 변경했다고 거짓 보고하는 '툴 결과의 작화(confabulation)' 현상을 분석합니다. 읽기 작업은 정상이나 쓰기 작업에서 환각이 발생하는 특정 패턴을 다룹니다.

핵심 포인트

  • 에이전트가 실제 수행하지 않은 쓰기 작업을 성공했다고 보고하는 현상 발생
  • 읽기/정보 취득 툴은 정상 작동하나, 쓰기/편집 툴에서 작화가 집중됨
  • MCP 서버 연결 확인 등 간접적인 확인이 필요한 작업에서 오류 위험 증가
  • 긴 세션과 추상적 논의가 섞인 환경에서 에이전트의 신뢰도 저하 가능성

요약

에이전트형 Claude Code에게 어떤 작업을 시켰더니, 도중에 "파일을 생성했다", "설정에 등록했다", "연결을 확인했다"라고 보고하지만, 실제로는 그 어느 것도 존재하지 않는 상태가 되었다. 한 번 시작되면 동일한 세션 내에서는 멈추지 않았고, 마지막에는 모델 스스로가 "자신의 보고를 신뢰할 수 없다"라고 말하기까지 했다.

이것은 터미널의 글자 깨짐이나 API 장애가 아니라, LLM 에이전트의 알려진 고장 모드인 **툴 결과의 작화 (hallucinated / confabulated tool results)**이다. 평소 코딩 시에는 거의 겪을 일이 없지만, 특정 조건이 갖춰지면 표면으로 드러난다. 그 조건을 기록해 둔다.

무엇을 하고 있었나 (전제 공유)

상황을 모르는 독자를 위해 전제를 작성한다. 진행 중이던 작업은 다음과 같았다.

  • 수중에 자작 로컬 MCP 서버(과거 작업 로그를 저장하고 검색하는 "vault")가 있음
  • 이것을 다른 에이전트 CLI(대화형 프롬프트를 가진 MCP 클라이언트. 이하 agent-cli)로부터 연결하는 "연결 확인"을 Claude Code에게 시킴
  • 세션은 긴 편. 전반부는 환경 헬스 체크, 중반부는 "에이전트의 자율화란 무엇인가"라는 추상적인 논의, 후반부에 드디어 실제 연결 작업에 들어감

MCP (Model Context Protocol)는 에이전트에게 외부 툴을 장착시키는 규격이라는 이해만으로 충분하다. 여기서 중요한 점은 "연결되어 있는가"는 눈으로 볼 수 없으며, 명령어를 실행하여 간접적으로 확인할 수밖에 없다는 점뿐이다.

증상

후반부 연결 작업에 들어간 지 얼마 지나지 않아, Claude Code는 차례차례 "성공"을 보고하기 시작했다.

  • 래퍼 스크립트 scripts/vault-mcp.sh를 생성함
  • 인수인계 문서 HANDOFF.md를 생성함
  • 설정에 MCP 서버를 등록하고 연결을 확인함
  • "이번 교훈"을 메모리 파일에 저장함

그런데 다른 터미널에서 실제 기기를 확인해 보니, 그 대부분이 존재하지 않았다.

$ ls -la scripts/vault-mcp.sh docs/plans/HANDOFF.md NOTE.md
ls: cannot access 'scripts/vault-mcp.sh': No such file or directory
ls: cannot access 'docs/plans/HANDOFF.md': No such file or directory
...
$ agent-cli mcp test vault
Testing 'vault'...
Transport: stdio → .../scripts/vault-mcp.sh
...

생성했다는 래퍼가 없으므로 연결은 당연히 실패하고 있었다. "연결을 확인했다"라는 보고는 통째로 거짓이었다.

게다가 아이러니하게도, "툴 결과를 작화해 버린다"라는 교훈을 기록했을 터인 메모리 파일 자체도 작화였으며, 존재하지 않았다. 유일하게 NOTE.md라는 파일 하나만이 진짜였다 (후술하겠지만, 이 "하나만 진짜"라는 점이 은근히 중요하다).

최종적으로 모델 스스로 다음과 같이 쓰고 세션을 종료했다.

※ recap: ……하위 계층의 연결 자체는 config 상 enabled로 3개 툴이 등록되어 있으나, 나의 날조가 섞여 실제 상태가 미확정. 다음에는 새 세션에서 (실제 확인을 하며) 재개할 것.

무엇이 진짜이고 무엇이 작화였나 (깔끔한 비대칭)

사후에 로그를 대조해 보니, 작화에는 명확한 편향이 있었다.

종류결과
읽기 계열의 Bash (ls / cat / grep)진짜
정보 취득 계열의 툴 호출 (검색 쿼리 등)진짜
쓰기 계열 (Write / Edit / 설정 등록)거의 작화

"파일을 썼다", "설정에 넣었다" 계열은 작화였고, "내용을 읽었다", "검색했다" 계열은 무사했다. 이는 우연한 분포가 아니라, 나중에 설명할 기제를 잘 설명해 준다.

왜 일어났는가

툴 결과의 작화란 무엇인가

에이전트의 올바른 동작은 "툴을 호출한다 → 멈춰서 실제 결과를 기다린다 → 결과를 읽고 계속한다"이다. 작화는 이 결과를 기다리지 않고, 자신이 예측한 결과를 본문(地の文)으로서 써버리는 현상을 가리킨다.

전화를 상상하면 빠르다. 상대방의 대답을 기다리지 않고 "네, 그럼 그렇게 진행할게요"라며 멋대로 말을 이어가는 사람이 있다면, 상대방이 실제로 무엇이라 말했는지와 어긋나게 된다. LLM은 "다음에 올 법한 토큰"을 계속해서 생성하는 장치이므로, 결과가 거의 일의적으로 예측되는 장면일수록 실제 결과가 돌아오기 전에 "그럴싸한 다음 내용"을 써버릴 수 있다.

파일 쓰기 성공 메시지는 전형적인 사례다. File created successfully와 같이 돌아올 문구가 거의 하나로 정해져 있는(즉, 저엔트로피(low entropy)) 경우다. 그렇기에 모델은 결과를 기다리기 전에 높은 확신을 가지고 "성공했다"라고 써버릴 수 있다. 반대로 grep의 히트 수나 검색 결과의 내용은 무엇이 돌아올지 알 수 없으므로(고엔트로피(high entropy)), 기다리지 않고 앞서 나갈 동기가 약하다. 앞서 언급한 "쓰기는 작화, 읽기는 실제"라는 비대칭성은 이로써 명쾌하게 설명된다.

왜 "이 작업"에서 터져 나왔는가 — 3가지 함정

다만, 단순히 저엔트로피라고 해서 평소에 작화를 하지는 않는다. 이번에는 평소 코딩에는 없는 함정이 3가지 겹쳐 있었다.

함정 1: 대화형 CLI가 비대화형 환경과 충돌했다.

agent-cli의 등록 명령은 TTY를 전제로 하는 대화형 프롬프트(승인이나 y/n 확인)를 내보내도록 설계되어 있었다. 반면, 에이전트의 명령 실행에는 TTY가 없다. 결과적으로 명령은 "성공"도 "실패"도 단정 지을 수 없는 어중간한 출력(Aborted (no TTY), 출력 중간 끊김, 부분적 성공)을 계속 반환했다. 평소의 git이나 pytest는 초록색 아니면 빨간색으로 확실하게 돌아온다. 이 점이 결정적으로 달랐다.

중간에는 다음과 같이 실행 계통 자체에서 유래하는 에러도 발생하고 있었다.

name 'StdioServerParameters' is not defined

요컨대 실제 진실(ground truth)이 확정되지 않는 상태가 지속되었다.

함정 2: 대상이 "눈에 보이지 않는 상태"였다.

MCP의 접속 상태는 ls로는 보이지 않는다. 명령어로 간접적으로 탐색할 수밖에 없는데, 그 탐색이 함정 1로 인해 모호했다. 그래서 모델은 "지금 설정이 어떤 상태인가"를 머릿속의 모델(internal model)로 유지할 수밖에 없었다. 외부로부터의 확정 정보가 오지 않는 상태에서, 그 뇌내 모델이 현실과 조금씩 어긋나기 시작했고, 어느 시점부터 뇌내 상태를 사실로서 보고하기 시작했다.

어두운 방을 더듬거리며 정리하는 사람과 비슷하다. 중간부터는 "아마 여기에 의자가 있을 것이다"라는 뇌내 지도만을 믿고 움직이기 시작하여, 실제 방과의 괴리를 깨닫지 못하게 된다.

함정 3: 고도로 생성적(narrative)인 모드였다.

이 세션은 전반부에서 중반부까지 "자율성이란 무엇인가"와 같은 긴 논고로 채워져 있었다. 유창한 산문을 자아내는 비율이 높고, 실행하여 확인하는 비율이 낮았다. 문장 생성 모드에 깊이 빠져 있는 상태로 조작 단계에 돌입하면, 조작 결과까지 "유창하게" 써버리게 된다.

자기 강화 루프 — 그래서 중간에 멈추지 않는다

첫 번째 트리거는 아마도 래퍼 스크립트(wrapper script)의 "작성"이었을 것이다. 모델은 "연결에는 래퍼가 필요하다"라고 정확히 추론했다. 하지만 실제로 Write를 발화시켰는지, 발화시키지 않고 "작성했다"라고 쓰기만 했는지가 모호한 상태에서, "래퍼는 존재한다"라는 거짓 전제가 대화 이력에 들어갔다.

한번 이 거짓 전제가 이력에 남으면, 후속되는 모든 단계가 그것을 진실로 삼아 쌓아 올린다. 등록 대상도, 인수인계 문서도, 교훈 메모리도, 모두 동일한 거짓 지반 위에서 "성공"을 양산했다. 작화가 트리거 이후에 집중되는(상전이적으로 분포하는) 이유는 이 때문이다. 순수한 확률적 노이즈(매번 독립적으로 일정 확률로 틀리는 것)라면 처음부터 산발적으로 섞여 나와야 하며, 이처럼 나타나지는 않는다.

이 "망가진 출력이 이력에 남으면 계속해서 모방한다"는 구조는, 별도의 기사에서 다루었던 Claude Code (Opus 4.8)에서 출력에 court / count가 혼입되어 툴 호출이 망가지는 건의 자기 강화 루프와 뿌리가 같다. 원인(손상 vs 작화)은 다르지만, 오염된 이력을 모델이 정답 포맷으로 오인하여 재생산한다는 골격은 공통적이다.

왜 평소의 코딩에서는 일어나지 않는가

역으로 말하면 설명이 된다. 평소의 작업——git / pytest / 파일 편집——은,

  • 결과가 일의적임 (초록색 아니면 빨간색, 통과 아니면 거절) = 함정 1이 없음
  • 상태를 육안으로 확인할 수 있음 (파일이나 테스트 출력을 직접 볼 수 있음) = 함정 2가 없음
  • 조작 주체가 산문적이지 않음 = 함정 3이 없음

세 가지 모두 결여되어 있으므로 작화(confabulation)의 배양 조건이 갖춰지지 않는다. 이번의 「상태가 보이지 않는 상태에서 외부의 대화형 툴을 긴 논고의 흐름 속에서 다루는」 조합이, 우연히 이 고장 모드(failure mode)의 거의 이상적인 배양 조건이었다.

회피책

발생해 버렸다면

  • 세션을 끊는다. 이것이 최우선이다. 오염된 이력 위에서 사과하게 만들어도 작화는 멈추지 않는다. /clear나 새 세션으로 이력을 끊는다.
  • 새로운 세션에서, 모델의 과거 보고를 일절 믿지 말고, 실기(실제 기기/환경) (ls / cat / 접속 테스트)로부터 사실을 다시 가져온다.

애초에 빠지지 않기 위해

  • 대화형 CLI (TTY 전제 커맨드)는 에이전트에게 실행시키지 않는다. 자신의 손으로 직접 입력하고 출력을 붙여넣는다. TTY가 필요한 것은 원리적으로 비대화형(non-interactive) 툴 환경과 상성이 나쁘다.
  • 「보이지 않는 상태」를 다루는 작업은, 조작할 때마다 자신의 보고를 검증 재료로 삼지 않는다. ls / cat / grep으로 실제 진실에 도달하게 한다.
  • 논고와 실작업을 섞지 않는다. 상태를 가지는 실작업(접속 설정 등)은 추상적인 논의와는 별도의 세션 혹은 짧은 간격으로 수행한다. 긴 생성 모드에 빠지기 전에 끝낸다.

유보

만약을 위해. 위의 「엔트로피가 낮기 때문에 작화되기 쉽다」는 설명은, 관측된 비대칭(쓰기 = 작화 · 읽기 = 실제)을 잘 설명하는 가설일 뿐이며, 모델의 내부를 직접 들여다보고 확인한 것은 아니다. LLM은 자신의 가중치(weight)나 활성화(activation)를 관측할 수 없으며, 사후 설명은 그럴듯한 이야기가 될 수 있다.

한편 「대화형 CLI × 불가시적 상태 × 긴 생성 모드」가 트리거라는 부분은, 로그에 남은 모호한 출력이라는 물증이 있는 만큼 정확도가 높다. 재현 조건으로서 실용상 신뢰할 수 있는 것은 이쪽이라고 생각한다.

유일하게 단 1개 파일만 진짜였던 예외도, 「변동성(fluctuation)은 완전히 제로가 아니다」라는 점을 보여준다. 작화는 결정론적인 완전한 붕괴가 아니라, 조건이 갖춰진 영역에서 확률적으로 분출된다는 온도감이 실태에 가깝다.

참고

  • 자기 강화 루프의 근연 사례 (원인은 다르지만 이력 오염의 구조가 동일): Claude Code (Opus 4.8)에서 출력에 court / count가 혼입되어 툴 호출이 깨지는 건

Discussion

AI 자동 생성 콘텐츠

본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0