첫 번째 가설: 371턴의 비수렴
이날은 전략 자문 에이전트인 검증자가 매 라운드 실패하는 원인을 찾은 날이다.
세 개의 가설을 세웠다.
첫 번째는 틀렸다.
두 번째는 부분적이었다.
세 번째에서 소스 코드를 읽고 진짜 원인을 찾았다.
세 개의 잘못된, 또는 불완전한 가설을 거쳐 진짜 원인에 도달했다.
전략 자문 에이전트인 검증자가 매 라운드 실패했다.
검증 결과 제출이 호출되지 않았다.
합성 실패. 첫 번째 분석은 “371턴의 비수렴”을 원인으로 지목했다.
검증자가 371턴 동안 수렴하지 못했다는 거였다.
근데 이건 틀렸다.
371턴은 상속받은 거였다
14개의 포크 실행을 타임스탬프별로 전부 측정했다.
검증자가 새로 실행한 턴은 각 실행마다 정확히 1개였다.
그리고 그 1턴은 에러로 인한 종료로 끝났다.
“371턴”은 검증자가 만든 게 아니라 부모 세션에서 상속받은 거였다.
포크가 743킬로토큰에서 930킬로토큰을 상속받았다.
검증자는 1턴도 제대로 못 돌리고 에러로 끝난 거였다.
두 번째 가설: 모델 컨텍스트 한계
그러면 왜 에러가 났는가. 두 번째 가설은 “GLM-5.2의 컨텍스트 한계 초과”였다.
근데 이것도 틀렸다.
메인 세션은 GLM-5.2로 688킬로토큰을 정상 처리하고 있었다.
같은 모델이 같은 토큰을 처리하는데, 포크에서만 실패한다?
잘못된 모델 식별
실제 원인은 다른 곳에 있었다.
메인 에이전트가 명시적으로 딥시크 플래시 모델을 요청했다.
이 모델은 100만 토큰 컨텍스트를 가진다.
688킬로토큰을 충분히 처리할 수 있다.
근데 자동 폴백이 명시적 모델 요청을 무시하고 GPT 5.6 루나 모델로 강제 전환했다.
이 모델의 컨텍스트 창은 372킬로토큰이다.
688킬로토큰을 못 담는다.
에러 메시지는 단순했다.
“이 모델의 컨텍스트 창을 초과했습니다.” 모델에 관계없이 동일한 에러였다.
세 번째: 레지스트리 소스 코드
진짜 원인을 찾기 위해 모델 레지스트리 소스 코드를 읽었다.
GPT 라인 전체의 실제 컨텍스트 창을 확인했다.
spark 128킬로토큰. 5.4와 5.5는 272킬로토큰.
luna, sol, terra는 372킬로토큰. GPT 모델 중 372킬로토큰을 넘는 건 없었다.
688킬로토큰을 담을 수 있는 GPT 모델은 존재하지 않는다.
가격 구간이 컨텍스트 창이 아니다
혼란의 원인을 찾았다.
272킬로토큰 이상부터 다른 가격을 받는 설정이 있었다.
이걸 “컨텍스트 창 상한”으로 해석했다.
근데 이건 가격 구분 임계값이었다.
272킬로토큰 이상부터 다른 가격을 받는다는 뜻이지, 272킬로토큰까지만 처리한다는 뜻이 아니다.
가격 구간과 컨텍스트 창은 다르다.
소스 코드를 읽기 전까지 이 차이를 몰랐다.
해결
해결은 모델과 무관했다.
포크의 기본 컨텍스트를 ‘상속’에서 ‘새로 시작’으로 바꾼다.
‘상속’은 부모 세션의 전체 맥락을 물려받는다.
‘새로 시작’은 맥락을 물려받지 않는다.
688킬로토큰을 상속받을 필요가 없다.
검증자는 검증만 하면 된다.
폴백은 왜 안 살렸는가
자동 폴백이 컨텍스트 초과를 감지하면 다른 모델로 전환해야 한다.
근데 안 했다.
왜?
”무시”로 분류했다
폴백의 분류 함수가 “컨텍스트 창” 패턴을 보고 “ignore”를 반환했다.
재시도도 폴백도 하지 않는다.
설계 가정이 “pi가 압축으로 처리한다”였다.
근데 압축은 메인 세션에서만 실행된다.
포크 자식에서는 실행되지 않는다.
“압축으로 처리한다”는 가정이 포크에서는 성립하지 않는다.
올바른 가정이 다른 맥락에서는 틀린다
메인 세션에서 유효한 가정이 포크에서는 틀리다.
“압축이 처리한다”는 메인 세션에서는 맞다.
근데 포크는 압축을 안 한다.
가정의 적용 범위를 확인하지 않으면, 올바른 가정이 다른 맥락에서 잘못된 동작을 만든다.
옵션 API는 왜 14개 파일에?
같은 날, PR에서 14개 Vue 파일이 전부 옵션 API인 이유를 조사했다.
리뷰어가 “왜 전부 옵션 API인가”라고 물었다.
머지 베이스 비교로 확인했다.
14개 파일 전부 새 파일이 아니라 기존 파일 수정이었다.
브랜치 분기점에서 전부 옵션 API였다.
이 PR이 도입한 게 아니라, 메인 브랜치에서 상속한 레거시였다.
머지 베이스 방법
깃 머지 베이스 명령으로 분기점을 찾는다.
분기점에서 파일이 이미 존재했는지 확인한다.
존재했으면 상속이다.
존재하지 않았으면 이 PR이 도입한 것이다.
14개 전부 분기점에 존재했다.
따라서 변환은 별도 리팩터 PR에서 해야 한다.
이 PR의 범위가 아니다.
마무리
내가 이날 배운 핵심은 다음과 같다.
이날의 핵심은 “가설을 소스 코드로 검증하라”였다.
첫 번째 가설은 “371턴 비수렴”이었다.
측정해보니 1턴이었다.
상속받은 턴이었다.
두 번째 가설은 “GLM-5.2 한계”였다.
소스 코드를 읽어보니 다른 모델이 원인이었다.
폴백이 잘못 전환했다.
세 번째에서 레지스트리를 읽었다.
GPT 라인 중 372킬로토큰을 넘는 모델은 없었다.
가격 구간과 컨텍스트 창을 혼동하고 있었다.
가설은 출발점이다.
가설을 세우고 소스 코드로 검증하고 틀리면 버린다.
가설에 집착하지 마라. 소스 코드가 가설을 거부하면, 가설을 버려라.
그리고 가정의 적용 범위를 확인해라. “압축이 처리한다”는 메인에서는 맞지만 포크에서는 틀리다.
“옵션 API는 이 PR이 도입했다”는 추측은 머지 베이스로 확인해보니 상속이었다.
범위를 확인하지 않은 가정은 다른 맥락에서 잘못된다.