Claude Code의 "Extended Thinking"은 감사 로그가 아니다 — 600자 signature의 정체
며칠 전 Claude Code 세션 로그를 감사 추적용으로 긁어 모으는 작업을 하다가 좀 황당한 걸 발견했다. thinking block에 분명히 추론 내용이 들어 있을 줄 알았는데, 까보니 사람이 읽을 수 없는 600자짜리 base64스러운 문자열만 덩그러니 박혀 있더라. "ctrl+o로 보던 그 사고 과정은 어디 갔지?" 싶었는데, 알고 보니 그건 진짜 추론이 아니라 요약이었다.
이게 단순한 트리비아가 아닌 이유는, AI 코딩 도구를 컴플라이언스 환경에서 굴리는 사람한테는 "에이전트가 왜 이 코드를 짰는지 재현 가능한가?"라는 질문에 직결되기 때문이다. 오늘은 그 얘기를 실무 관점에서 풀어본다.
1. 왜 지금 이게 화제인가
Claude Code는 세션을 디스크에 JSON 형태로 차곡차곡 기록한다. 그래서 많은 사람들이 자연스럽게 "이걸 감사 로그로 쓰면 되겠네"라고 생각한다. 코드 리뷰에서 "이 변경 왜 했어?"라는 질문이 나왔을 때, 에이전트의 사고 과정을 그대로 보여주면 깔끔하니까.
그런데 발단이 된 글(patrickmccanna.net)의 저자가 로컬 로그를 직접 까봤더니, thinking block 안에 있던 건 실제 추론 텍스트가 아니라 600자 길이의 signature였다. Anthropic 공식 문서를 따라가 보면 구조가 이렇다:
- Claude는 추론을 signature 안에 암호화한다.
- 복호화 키는 Anthropic이 보유한다.
- 사용자 기기는 키를 받지 못한다.
- API가 돌려주는 건 추론 원문이 아니라 reasoning summary(요약)다.
- 전체 thinking output을 받으려면 enterprise agreement가 필요하다.
즉, ctrl+o로 보던 그 예쁜 사고 과정은 모델이 실제로 행동을 구동한 추론 그 자체가 아니라, 그걸 압축·요약한 결과물이다. 그리고 압축 과정에서 정보 손실이 일어난다.
2. 동작 원리 — 직접 까보자
백문이 불여일견이니 실제 세션 로그를 한번 들여다보자. Claude Code 세션은 보통 홈 디렉터리 아래 프로젝트별로 떨어진다.
# 세션 로그 위치 확인 (환경에 따라 경로 다를 수 있음, 확인 필요)
$ ls -la ~/.claude/projects/
drwxr-xr-x 3 user staff 96 Feb 10 14:22 -Users-user-work-myapp
해당 디렉터리의 jsonl 파일에서 thinking block만 추려보자. jq로 type이 thinking인 것만 필터링한다.
# thinking 타입 메시지만 추출해서 signature 길이 확인
$ cat ~/.claude/projects/-Users-user-work-myapp/*.jsonl \
| jq -r 'select(.message.content[]?.type == "thinking")
| .message.content[]
| select(.type == "thinking")
| {has_thinking: (.thinking | length), sig_len: (.signature | length)}'
{
"has_thinking": 142,
"sig_len": 612
}
여기서 핵심은 thinking 필드에 든 142자는 요약된 사고 과정이고, signature 필드의 612자가 실제 암호화된 원문이라는 점이다. signature를 그대로 출력해보면 이런 식이다:
$ cat *.jsonl | jq -r 'select(.message.content[]?.type=="thinking")
| .message.content[] | select(.type=="thinking") | .signature' | head -c 200
EuYBCkYIBxgCKkBxJ9... (이하 사람이 읽을 수 없는 인코딩 문자열)
이걸 base64로 디코딩해봐야 의미 있는 텍스트가 안 나온다. Anthropic의 키로 암호화돼 있으니 당연한 결과다.
jpeg 비유는 사실 거꾸로다
원문에서는 이 과정을 "jpeg를 bmp로 저장했다가 다시 jpeg로 내보내는 것 같은 데이터 손실"에 비유했는데, HN 댓글에서 정확히 지적했듯 비유 방향이 반대다. bmp는 무손실, jpeg가 손실 포맷이니까. 핵심만 가져가자면 "요약 과정에서 비가역적인 정보 손실이 일어난다"는 것이다. 요약본을 아무리 들여다봐도 원본 추론을 복원할 수 없다.
그리고 이건 Anthropic만의 문제가 아니다. HN 댓글에서 여러 사람이 짚었듯 OpenAI, Google도 동일하게 원시 추론(raw CoT)을 숨긴다. 이유는 대체로 두 가지로 정리된다: (1) 증류(distillation) 방지 — 경쟁사가 사고 사슬을 학습 데이터로 가져가 모델을 복제하는 걸 막으려는 것, (2) 정렬되지 않은 중간 출력 노출 방지. 한 댓글은 "1~2월쯤 변경됐고 이유가 명시적으로 증류 방지였다"고 회고했다(버전·시점은 댓글 회고라 공식 확인 필요).
3. 실무 관점 — 도입 전에 반드시 짚을 것
감사 로그로 쓸 거면 전제부터 바꿔라
가장 중요한 결론부터. Claude Code 세션 파일만으로는 에이전트의 실제 논리를 재현할 수 없다. 감사 추적이 필요한 환경(금융, 의료, 공공)에서 "AI가 이 결정을 내린 근거"를 제출해야 한다면, 로컬 로그에 있는 thinking 요약은 법적/규제적으로 "근거"가 되기 어렵다. 실제 추론은 암호화돼 있고 키는 당신에게 없으니까.
그래서 감사가 필요하면 추론 자체가 아니라 관찰 가능한 행동을 별도로 로깅해야 한다. 입력, 출력, 실제 파일 변경, 실행된 명령어 같은 것들. 예를 들어 git을 활용해 에이전트가 만든 모든 변경을 강제로 추적하는 식이다:
# 에이전트 작업 전후를 별도 브랜치/커밋으로 강제 스냅샷
$ git add -A && git commit -m "pre-agent snapshot" --allow-empty
# Claude Code 실행 후
$ git diff HEAD~1 --stat
src/auth/login.ts | 23 +++++++++++++----------
src/config/db.ts | 8 ++++----
2 files changed, 18 insertions(+), 13 deletions(-)
추론은 못 가져와도 "무엇을 바꿨는가"는 이렇게 100% 재현 가능하게 남길 수 있다. 감사 관점에서는 사실 이게 추론 텍스트보다 더 확실한 증거다.
흔한 함정 — signature 재사용 에러
API를 직접 다루면서 thinking block을 멀티턴 대화에 그대로 끼워 넣을 때 자주 만나는 함정이 있다. signature 검증에 실패하는 케이스다. 모델 버전이 바뀌었거나, thinking 텍스트를 임의로 잘라서 보내거나, signature를 누락하면 이런 에러가 난다:
{
"type": "error",
"error": {
"type": "invalid_request_error",
"message": "messages.0.content.0.thinking: Input tag 'thinking' found using 'type'
does not match any of the expected signatures, or the thinking block
signature could not be verified."
}
}
이게 뜨는 전형적인 이유:
- thinking block의
text만 보내고signature필드를 빼먹은 경우. 둘은 세트라 같이 보내야 한다. - thinking 내용을 후처리(트리밍, 마스킹)해서 보내면 signature와 불일치해 검증 실패.
- 이전 턴을 다른 모델 버전으로 생성한 뒤 현재 턴에서 다른 버전으로 이어붙인 경우.
핵심 교훈: thinking block은 당신이 편집할 수 있는 데이터가 아니다. 받은 그대로 보존해서 다음 턴에 돌려줘야만 한다. 로그를 가공·익명화하려다 이 에러를 만나는 경우가 의외로 많다.
보안 관점 — 숨겨진 추론의 리스크
HN 댓글에서 나온 우려 중 실무적으로 새겨들을 만한 게 있다. 프롬프트 주입으로 추론 사슬에 비밀 목표를 심으면, 그게 요약과 최종 출력에서는 가려질 수 있다는 점이다. 사용자에게 보이는 요약은 멀쩡한데 내부 추론에선 다른 의도가 작동할 가능성을 검증할 방법이 없다.
다만 같은 댓글 스레드에서 반론도 나왔다. "도구 호출(tool call)은 어차피 클라이언트가 실행해야 하니 숨겨질 수 없다"는 것. 즉 추론은 가려져도 실제 실행되는 명령은 클라이언트에 노출된다. 그래서 도구 호출 레벨에서 화이트리스트/감사를 거는 게 현실적인 방어선이다. 추론을 못 보는 대신, 실행 권한을 좁히는 쪽으로 가는 게 맞다.
대안 — 신뢰 모델이 다른 선택지
- 요약 제한이 없는 모델 선택: 원문 댓글에 따르면 Sonnet 계열은 이 제한이 없다는 언급이 있다(공식 확인 필요). 워크로드에 따라 모델을 나눠 쓰는 것도 방법이다.
- 명시적 CoT 프롬프트로 우회: thinking 기능을 끄고 일반 프롬프트 안에 "답하기 전에 단계별로 생각해라"를 넣으면 GPT-3 시절처럼 사고 과정이 본문에 그대로 노출된다는 회고가 있다. 추론을 본문 텍스트로 받으니 로깅·감사가 쉬워진다. 대신 모델 품질과 토큰 비용은 트레이드오프다.
- 오픈 가중치 모델: DeepSeek, GLM 등은 사고 사슬이 그대로 노출된다(읽으면 "이게 무슨 소리야" 싶은 경우도 많지만). 데이터 주권이 절대 우선인 온프레미스 환경이면 고려 대상이다.
- enterprise agreement: 전체 thinking output이 정말 필요하면 결국 이 길이다. 비용·계약 협상이 필요하다.
4. 정리
한 줄 요약: Claude Code의 thinking block에 보이는 건 암호화된 추론의 "요약본"일 뿐, 모델 행동을 실제로 구동한 추론 원문이 아니다. 키는 Anthropic에 있고 당신에겐 없다.
누가 언제 신경 써야 하나:
- 컴플라이언스/감사가 필요한 팀: 추론 텍스트를 증거로 쓸 생각 버리고, git diff·명령 실행 로그 같은 관찰 가능한 행동을 별도로 강제 로깅하라.
- API를 직접 통합하는 백엔드: thinking block은 절대 가공하지 말고 받은 그대로 보존·재전송하라. 안 그러면 signature 검증 에러를 만난다.
- 온프레미스/데이터 주권 우선 환경: 추론이 외부 키로 암호화돼 재현 불가라는 점을 도입 결정 전에 명확히 인지하고, 필요하면 오픈 가중치 모델을 저울질하라.
일반적인 사이드 프로젝트나 사내 도구 수준이면 솔직히 크게 문제될 일은 아니다. 하지만 "AI가 왜 그렇게 했는지를 증명해야 하는" 순간이 오는 조직이라면, 도입 전에 이 신뢰 모델을 팀과 공유해두는 게 나중에 곤란해지지 않는 길이다.
참고 자료
- Claude Code의 "Extended Thinking" 출력 텍스트는 실제 추론이 아님 | GeekNews
- Fooling around with encrypted reasoning blobs (Matt Green, Cryptography Engineering)
- Anthropic 공식 문서 — Extended Thinking (구조·signature 동작 확인용)
※ 본문의 경로, 버전, 시점 관련 일부 내용은 원문 댓글 회고나 환경별 차이가 있을 수 있어 실제 적용 전 공식 문서 확인을 권장한다.