
기존 모델이 몰래 교체되었다. 언급된 실질적 피해를 26,520건 조사했으나 0건이었다
요약
Claude Opus 모델의 기본값이 변경되면서 발생한 모델 교체 현상과 그 영향을 실측한 보고서입니다. 모델 지정 없이 호출할 경우 자동으로 새로운 모델이 적용되며, 특히 '사고(Thinking)' 기능이 기본값으로 포함됨에 따라 발생할 수 있는 잠재적 위험을 분석합니다.
핵심 포인트
- 모델 명시를 하지 않을 경우 배포처의 기본값 변경에 따라 모델이 자동 교체됨
- Opus 5부터는 '사고(Thinking)' 기능이 기본 활성화되어 토큰 사용 방식이 변화함
- max_tokens 설정이 타이트할 경우 사고 토큰 점유로 인해 응답이 끊길 위험이 있음
- 작성자의 실측 결과 26,520건의 응답 중 실제 끊김 현상은 발생하지 않음
7월 26일, 비용 기록에 낯선 이름이 나타났다. claude-opus-5.
발견한 것은 우리 측 CC다. 일별 비용을 임의로 확인하고 보고해 왔다. 나는 요청한 적이 없다.
나는 모델 지정(Model specification)을 변경하지 않았다. 그런데도 다음 날인 27일에는, 내 환경 응답의 97.1%가 새로운 모델로부터 반환되고 있었다.
지정을 변경하지 않았음에도, 사용하는 모델이 이틀 만에 교체된 셈이다.
배포처의 이행 가이드(Migration guide)에는 이 교체가 무엇을 초래하는지가 적혀 있다. 가장 까다로워 보이는 것은 "응답이 도중에 끊긴다"라는 증상이다.
그래서 자신의 로그를 세어 보았다. 26,520건의 응답 중, 상한(Limit)에서 끊긴 것은 0건이었다.
이 기사는 교체가 실제로 일어난 것에 대한 실측과, 언급되는 실질적 피해를 내 환경에서 계산한 결과, 그리고 당신이 동일한 확인을 자신의 환경에서 수행하는 절차를 정리한 것이다.
배포처가 공개하고 있는 변경 기록을 직접 열어 확인했다. 2.1.219 항목에 새로운 모델이 추가되었으며, 그것이 Opus 계열의 기본값(Default)이 되었다고 명시되어 있다.
"기본값"이라는 것은, 모델을 명시하지 않은 기동은 이날을 기점으로 다른 모델로 동작하기 시작한다는 뜻이다. 내 환경은 자동으로 재기동되는 메커니즘을 가지고 있으며, 그 기동 명령에 모델 지정을 넣지 않는다. 그래서 기록의 내용이 내 조작 없이 교체되었다.
자신의 세션 기록에서 Opus 계열의 응답을 일별로 세어 보니 다음과 같았다.
| 날짜 | Opus 4.8 | Opus 5 | 새로운 쪽의 비율 |
|---|---|---|---|
| 7월 26일 | 85건 | 1,846건 | 95.6% |
| 7월 27일 | 169건 | 5,651건 | 97.1% |
이전 모델은 사라지지 않았다. 3% 정도 남아 있다. 나는 처음에 이것을 "사라졌다"라고 썼다가, 기록을 재검토하고 수정했다. 사라진 것이 아니라, 기본값이 아닌 것은 호출되지 않게 될 뿐이다. 지정을 명시하고 있는 경로만이 남는다.
여기서 안심하게 되는 부분이 있다. 새로운 기본 모델의 단가는 이전의 기본값과 동일하다.
| 모델 | 입력 단가 | 출력 단가 |
|---|---|---|
| Claude Opus 5 | 100만 토큰당 5.00달러 | 100만 토큰당 25.00달러 |
| Claude Opus 4.8 | 100만 토큰당 5.00달러 | 100만 토큰당 25.00달러 |
동일하다. 한 번에 읽어들일 수 있는 양도 둘 다 100만 토큰이며, 긴 문맥(Long context)에 대한 추가 요금도 없다.
그래서 단가 표를 본 사람은 "가격이 같다면 영향은 없다"라고 결론 내린다. 나도 처음에는 그렇게 생각했다.
배포처의 이행 가이드에 파괴적인 변경(Breaking changes)으로 적혀 있는 항목이 있다.
【CC 보충】 사고(Thinking)의 기본값이 반전되었다
이행 가이드에 파괴적인 변경으로 명기되어 있다. Opus 4.8과 4.7에서는 thinking을 생략하면 사고하지 않았지만, Opus 5에서는 생략 시 사고한다. 즉, 설정을 글자 하나 바꾸지 않은 호출이 이날을 기점으로 사고를 위한 토큰을 추가로 사용하기 시작한다.
【CC 보충】 상한은 사고와 본문의 합계에 적용된다
max_tokens는 응답 본문에만 적용되는 것이 아니라, 사고와 본문의 합계에 대해 적용된다. 사고하지 않는 것을 전제로 상한을 타이트하게 설정해 두었던 설정은, 사고가 기본값으로 들어간 만큼 본문에 사용할 수 있는 여지가 줄어든다. 결과적으로, 그때까지 끝까지 나왔던 응답이 도중에 끊기게 된다.
답변의 길이에 딱 맞춰 상한을 조여 놓았던 사람일수록 손해를 본다는 이야기가 된다. 낭비 없는 여유를 두지 않으려는 정중한 설정이었을 것이, 어느 날부터 답변을 잘라버리기 시작하는 것이다.
여기까지는 전부 배포처의 문서에 적혀 있는 내용이다. 내가 측정한 것이 아니다.
끊긴 응답은 겉보기에는 얼핏 그럴싸하게 끝난 것처럼 보일 때가 있다. 그래서 눈으로 보는 것이 아니라, 종료 이유(Finish reason)를 세었다.
| 기간 | 응답 총수 | 상한에서 끊긴 수 |
|---|---|---|
| 7월 17일~22일 (교체 전 · Opus 4.8) | 18,781건 | 0건 |
| 7월 26일~27일 (교체 후 · 주로 Opus 5) | 7,739건 | 0건 |
합계 26,520건 중 0건. 교체 전도 후도 단 한 번도 일어나지 않았다.
여기서 잠시 멈춰 섰다. 0건은 "정말로 0"인 것인가, 아니면 "내 계산 방식이 틀린 것"인가.
동일한 방식으로, 반드시 존재하는 값을 세어 대조를 맞추었다.
| 종료 이유 | 건수 |
|---|---|
tool_use | |
| 71,409 | |
end_turn | |
| 4,202 | |
stop_sequence | |
| 100 | |
null | |
| 84 | |
max_tokens | |
| 0 |
동일한 형식으로 end_turn이 4,202건 나왔으므로, 0은 형식의 오류가 아니다.
기록 전체 75,795건을 통틀어, 상한선(limit)에서 잘린 응답은 단 한 번도 나타나지 않았다.
0이 나왔을 때는 먼저 자신의 계산 방식을 의심하는 것이 좋다. 반드시 존재하는 값으로 동일한 검사를 수행하면, 도구가 고장 난 것인지 정말로 없는 것인지가 갈린다.
이 부분을 모호하게 하면 거짓말이 되므로, 조건을 작성한다.
내 환경에서 0건이었던 이유는 내가 상한선을 타이트하게 설정하지 않았기 때문이다. 상한선에 여유가 있는 설정에서는 사고(thinking) 분량이 늘어나더라도 본문의 여지는 여전히 남는다. 그래서 잘리지 않는다.
역설적으로 말하면, 상한선을 정답의 길이에 딱 맞춰 설정한 사람에게는 발생할 수 있는 일이다. 이행 가이드(migration guide)에 적혀 있는 기제(mechanism) 그 자체는 실재한다. 나의 0건은 "이 증상은 일어나지 않는다"는 증거가 아니라, "내 설정에서는 일어나지 않았다"는 증거다.
따라서 결론은 "걱정할 필요 없다"가 아니다. "자신의 환경에서 세어보라"가 된다.
또 하나, 측정하려고 했으나 측정할 수 없었던 것을 적어둔다.
사고(thinking)가 기본값(default)으로 포함된다면 사용량이 늘어날 것이다. 늘어났는지 여부를 모델별 출력 토큰(output token)의 중앙값으로 비교했다. 두 가지 방식으로 측정했더니, 정반대의 결과가 나왔다.
| 비교 방식 | Opus 4.8 | Opus 5 | 겉보기 결론 |
|---|---|---|---|
| 전체 기간을 합산 | 1,245 | 799 | 새로운 모델이 더 가볍다 |
| 두 모델이 모두 작동한 날만 | 457 / 444 | 942 / 757 | 새로운 모델이 더 무겁다 |
원인은 알고 있다. 모델의 할당이 무작위가 아니기 때문이다. 같은 날이라도, 구형 모델은 남은 작은 작업들을, 신형 모델은 주류인 무거운 작업들을 담당하고 있다. 날짜를 맞춰도 작업의 종류가 맞지 않는다.
따라서 평균 비교로는 모델의 차이인지 작업의 차이인지를 구분할 수 없다. 일별 비용의 합계도 마찬가지로 작업량과 연동되어 버린다.
측정할 수 없는 것을 측정했다고 쓰고 싶지 않으므로, 여기서는 "알 수 없었다"로 남겨둔다.
【CC 보충】왜 날짜를 맞춰도 부족한가
비교에서 교란 변수(confounding variable)를 제거하려면, 비교하는 두 집단이 "조사하려는 요인 이외에는 동일"해야 한다. 여기서 다른 점은 날짜가 아니라 어떤 작업이 어떤 모델에 할당되었는가이며, 이는 실험자(이 경우에는 운영 체계)가 결정한다. 무작위가 아닌 할당 하에서는 집단의 평균 차이가 요인의 효과와 할당의 편향(bias)의 합계가 되어, 한쪽만을 추출해낼 수 없다. 이를 구분하려면 동일한 입력을 양쪽 모델에 통과시켜 비교하는(대응 표본 비교, paired comparison) 과정이 필요하다.
일별 기록을 남기고 있다면 사용된 모델 이름의 변화를 살펴라. 우리 팀은 이것을 통해 알아냈다.
자동으로 구동되는 메커니즘을 가진 사람은, 그 구동 명령에 모델 지정이 포함되어 있는지 확인해 보길 바란다. 나의 경우는 다음과 같았다.
claude --dangerously-skip-permissions
모델 지정이 없다. 그래서 기본값이 바뀐 날, 사용하는 모델도 바뀌었다.
눈으로 보지 마라. 종료 이유를 세어라. 자신의 로그에서 센다면 이것으로 충분하다.
grep -h '"stop_reason":"max_tokens"' ~/.claude/projects/*/*.jsonl | wc -l
0 이외의 숫자가 나온다면 상한선에 걸린 것이다. 직접 API를 호출하는 구조라면 응답마다 이 값을 확인하라.
if response.stop_reason == "max_tokens":
print("상한선에서 잘림", response.usage)
【CC 보충】무효화에는 깊이의 제약이 있다
이행 가이드에 따르면, 사고를 무효화할 수 있는 것은 effort가 high 이하일 때뿐이며, 더 깊은 설정과 무효화를 조합한 호출은 400 에러로 거부된다. 게다가 이 검사는 호출마다 이루어지기 때문에, 대화 도중에 깊이를 높이면 그때까지 통과되던 동일한 조합이 그 시점에서 거부된다. 전반부가 성공했다는 것이 후반부의 성공을 보장하지 않는다.
자신이 아무것도 바꾸지 않았는데 전제 조건이 바뀌어 버리는 형태의 문제는 이번이 처음이 아니다. 설정 파일이 덮어씌워지거나, 버전이 올라가며 기본값이 바뀌거나, 의존하는 요소가 멋대로 업데이트되는 경우 말이다. 모두 자신의 조작 기록을 추적해도 원인이 나오지 않는다.
이번 사례에 특유한 점은, 청구 단가가 바뀌지 않기 때문에 금액만으로는 알아챌 수 없다는 것이다.
그리고 이번에 가장 크게 배운 점은, 경고를 읽었다면 자신의 로그를 직접 세어보는 것이 더 빠르다는 것이었다. 세어보기 전에는 '끊겼을지도 모른다'라고 생각했다. 하지만 직접 세어보니 0건이었고, 대신 '왜 0건인가'라는 조건에 대해 더 잘 이해하게 되었다. 조건을 알게 되면 어떤 설정의 사용자가 위험한지도 알 수 있다.
자동으로 작동하는 시스템일수록, 무엇을 지정하지 않았는지 한 번쯤 세어볼 가치가 있다.
마찬가지로 '금액으로는 알아챌 수 없는' 형태로 토큰이 사라지는 경로를 800시간의 운영 기록으로부터 수집하여, 토큰 소비를 절반으로 줄이는 책(¥2,500)으로 정리해 두었다. 제2장까지는 무료로 읽을 수 있다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기