같은 실행을 보고 한 표에는 317,935, 다른 설명에는 623이 적혀 있었다. 둘 중 하나가 틀렸다는 뜻은 아니다. 서로 다른 집계 단위를 전체라는 이름으로 묶으면 틀린 설명이 된다. 실제 작업 기록에서 이 두 숫자가 어떻게 만들어졌는지부터 다시 읽었다.

입력의 일부인 캐시를 다시 더하지 않는다

당시 입력은 317,540이고 그중 캐시 입력이 317,312였다. 캐시는 입력 옆에 붙는 추가 사용량이 아니라 그 안의 부분집계다. 출력 395를 입력에 더하면 전체 317,935가 된다. 여기에 캐시를 한 번 더 더하면 같은 입력 부분을 이중으로 센다.

추론 토큰도 이 계약에서는 출력 안의 부분집계다. 추론이 별도 필드로 보인다는 이유로 출력에 또 더하지 않는다. 소스의 measuredUsage는 유효한 비음수 정수만 받고 캐시가 입력보다 크거나 추론이 출력보다 크면 그 상세를 null로 둔다. 잘못된 수치를 정상 0으로 바꿔 표를 매끈하게 만들지 않는다.

623은 비캐시 입력과 출력을 더한 값이다

입력에서 캐시를 빼면 228이고 여기에 출력 395를 더하면 623이다. 따라서 비캐시 입력+출력이라는 이름이 맞다. 전체 317,935가 사라진 것이 아니다. 캐시가 많이 잡힌 실행에서 작은 수치만 강조하면 독자는 전체 대화가 그만큼만 전달됐다고 오해할 수 있다.

이 두 지표는 서로 다른 판단에 쓸 수 있지만 한쪽을 다른 쪽으로 이름 바꾸면 비교가 깨진다. 보고서도 캐시 포함 전체와 비캐시 입력+출력을 나란히 남겼다. 나는 표 제목에 단위를 쓰고 계산식이 따라오게 하는 쪽을 택했다. 어느 쪽이 더 좋은 숫자인지보다 독자가 분모를 알 수 있는지가 중요했다.

반환되지 않은 값은 0과 다르다

원래 스키마는 inputTokens 같은 상세를 null로 표현한다. totalUsage가 알려진 값을 더할 때에도 unknownCalls와 unknownCacheCalls를 함께 센다. 알려진 합계가 있어도 누락 호출이 있으면 전체를 다 안다고 말할 수 없다. 과거 합계만 남은 작업에 없는 상세를 소급해서 만들어 넣지도 않는다.

새 합성 원장은 지표마다 known_sum·unknown_calls·complete를 붙여 이 차이를 더 직접 보여 준다. 입력·출력이 없지만 보고된 전체는 있는 경우와 전체를 입력+출력에서 계산한 경우도 구분한다. 한 열의 합계가 완전해도 다른 열의 누락이 사라지는 것은 아니다. 원래 구현을 완전히 복제한 코드는 아니며 독자가 집계의 빈칸을 확인하기 쉽게 만든 독립 예제다.

실패한 호출도 실제 사용량이 있으면 남긴다

응답을 파싱하지 못했다고 모델이 아무 토큰도 쓰지 않은 것은 아니다. 기존 단위시험은 알려진 사용량을 가진 파싱 실패를 기록하고 같은 측정 실패 객체가 다시 전달돼도 한 번만 더하는지 본다. 네트워크 오류처럼 사용량이 돌아오지 않은 실패는 unknown으로 남긴다.

취소와 작업 전환도 집계의 소속을 바꾸면 안 된다. 늦게 온 옛 작업 응답을 새 작업에 더하지 않는 시험이 있다. 이 기록들은 제거된 내부 모델 경로의 역사적 설계와 시험이며 현재 앱이 그 CLI를 다시 실행한다는 뜻은 아니다. 어떤 상태의 호출을 어느 작업에 더했는지까지가 사용량 설명의 일부다.

토큰 표를 청구액이나 구독 잔여량으로 바꾸지 않는다

이번 글은 남아 있는 소스·시험과 실제 과거 측정값의 산술 대조다. 새 모델 요청을 보내거나 현재 계정의 청구를 조회하지 않았다. 구독의 캐시 처리와 요금표를 가정해 623을 결제 토큰이라고 부르지 않는다. 사용량과 금액을 연결하려면 별도의 현재 계약과 근거가 필요하다.

공개 원장은 합성 값을 직접 바꿔 포함 관계와 unknown 처리, 중복 영수증을 시험할 수 있다. 제공자 인증과 금액 계산 필드는 항상 False다. 나는 큰 숫자를 감추거나 작은 숫자를 전체로 바꾸는 대신 두 수치를 각자의 이름으로 남겼다. 같은 실행을 여러 관점에서 읽어도 계산의 의미가 바뀌지 않도록 하기 위해서다.