
Gemini 3.6 Flash의 usage 계약을 검사하는 Python
요약
Gemini 3.6 Flash 도입 시 발생하는 API 응답 형식(snake_case vs camelCase) 차이로 인한 비용 집계 오류를 방지하기 위한 Python 계약 테스트 구현 방법을 소개합니다. 모델 전환 시 데이터 정규화와 예외 처리를 통해 운영 비용 관측성을 유지하는 가이드를 제공합니다.
핵심 포인트
- Gemini 3.6 Flash의 Interactions API는 snake_case 형식을 사용함
- 기존 API의 camelCase 형식을 그대로 사용하면 비용 데이터가 0으로 집계될 위험이 있음
- 두 형식을 하나의 Usage 클래스로 정규화하는 계약 테스트 구현 권장
- 모델 전환 시 정답률뿐만 아니라 토큰 사용량(Usage)의 일관성 검증 필수
Gemini 3.6 Flash로 전환한다면, 모델의 답변뿐만 아니라 이용량 로그의 키(key)도 먼저 검사하는 것이 좋다. 새로운 Interactions API는 usage.total_input_tokens와 같은 snake_case를 반환한다. 반면, 기존의 GenerateContent API 로그는 usageMetadata.promptTokenCount와 같은 camelCase이다. 집계 측이 전자만을, 혹은 후자만을 전제로 하고 있다면 비용 대시보드가 조용히 0이 되어버린다.
이번에는 두 JSON을 하나의 형식으로 통일하고, 알 수 없는 형태는 예외로 처리하는 작은 계약 테스트(contract test)를 만든다.
7월 21일에 출시된 Gemini 3.6 Flash는 Google의 발표에 따르면 3.5 Flash보다 출력 토큰을 17% 줄였으며, 입력 100만 토큰당 $1.50, 출력 100만 토큰당 $7.50이다. 이 점은 매력적이지만, 앱 측에서 확인해야 할 수치는 모델명이나 청구 단가만이 아니다. 실제로 입력, 출력, thinking, 캐시(cache), 도구(tool) 이용이 얼마나 발생하는가이다. 공식 토큰 설명에서도 응답의 usage로부터 입력, 출력, thinking, 캐시, 도구 이용, 합계를 가져올 수 있다고 되어 있다.
나는 이행(migration)용 로그를 살펴보던 중, 구형 형식인 promptTokenCount를 읽는 집계가 남아 있다는 것을 발견했다. 에러는 발생하지 않는다. 딕셔너리의 get()이 0을 반환할 뿐이라서, 그래프만 묘하게 깨끗하게 나올 뿐이다. 이런 종류의 고장은 발견하기 가장 어렵다.
Google은 현재 최신 기능과 모델에는 Interactions API를 권장하고 있다. 동일한 공식 문서의 Python 예제에서도 gemini-3.6-flash에 대해 interaction.usage를 읽는 형태로 되어 있다. 구형 API의 이력을 즉시 삭제할 수 없는 팀에서는, 두 가지 형태를 수용하는 경계(boundary)를 한 곳에 두는 것이 다루기 쉽다.
다음 코드는 API를 호출하지 않는다. 저장된 응답 JSON을 받아 집계에 전달할 수 있는 Usage로 정규화(normalize)한다. total_tokens를 각 항목의 합계로 재계산하지 않는 것도 의도적이다. 캐시된 토큰은 입력 토큰에 포함되는 것으로 취급되는 경우가 있기 때문에, 무턱대고 더하면 이중 계산이 될 수 있다. API가 반환한 합계를 그대로 기록한다.
from dataclasses import dataclass
@dataclass(frozen=True)
class Usage:
...
가지고 있는 Python 3.9.6에서 실행한 출력은 다음과 같았다.
Usage(input_tokens=1200, output_tokens=280, thought_tokens=200, cached_tokens=900, tool_use_tokens=0, total_tokens=1680)
raw.get(key, 0)는 임의의 항목이 생략된 응답을 허용하기 위해 사용하고 있다. 다만 필수 항목으로 만들고 싶다면 이 부분을 raw[key]로 바꾸면 된다. 중요한 것은 키 이름의 변경을 집계 SQL이나 시각화 코드까지 누락 없이 전달하는 것이다. 이 과정을 통과하지 못하는 응답은 예외로 처리하여, 모델 전환 PR(Pull Request) 단계에서 차단한다.
이것만으로는 부족하다. 이것은 이용량 계약만을 지키는 테스트다. 다음으로는 실제 서비스의 대표 요청을 구형 모델과 3.6 Flash에 동일한 조건으로 보내고, 정답률, p95 레이턴시(latency), Usage를 동일한 행에 저장한다. 공식 발표의 17%는 비교의 출발점일 뿐이며, 자신의 프롬프트나 도구 호출 횟수까지 보장하지는 않는다.
모델 업데이트 시에는 '답변이 돌아왔다'를 합격 기준으로 삼기 쉽지만, 운영 비용 관측이 망가진 상태에서는 비교 자체가 불가능하다. 응답 경계를 하나 만들어 두면, 다음 API 이행 시에도 수정할 위치가 명확해진다. 작은 코드이지만, 비용 최적화는 여기서부터 시작하는 것이 확실하다고 생각한다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기