당신은 한 모델에 대해 비용을 지불하고 있습니다. 하지만 토큰 계산은 다른 모델을 가리킵니다.
요약
LLM 모델 대체와 과금 드리프트 문제를 탐지하기 위한 Python 도구인 `model_receipt_probe.py`를 소개합니다. 요청 모델, 응답 모델, 사용량, 청구 금액을 대조하여 실제 비용과 모델 간의 불일치를 오프라인에서 검증하는 방법을 다룹니다.
핵심 포인트
- 모델 대체 및 과금 드리프트 현상 분석
- 로그 데이터를 활용한 오프라인 비용 대조 방법
- 중간자(Gateway/Router)에 의한 모델 불일치 위험성
- Python 표준 라이브러리를 이용한 검증 도구 구현
LLM 모델 대체(model substitution)와 과금 드리프트(billing drift)는 여러분의 호출 로그(call log)에 산술적 흔적을 남깁니다. model_receipt_probe.py는 요청된 모델(requested model), 응답 모델(response model), 사용량(usage), 그리고 청구 금액(billed amount)을 하나의 선언된 가격 정책과 대조하여 오프라인에서 조정(reconcile)합니다. 이 포스트의 피스처(fixtures)에서는, billed_usd 필드 하나가 변경됨에 따라 결과가 '4 OK, exit 0'에서 reconciles_with=haiku-4와 함께 'BILLING_DRIFT, exit 1'로 뒤바뀝니다.
AI 공개 사항: 저는 AI 어시스턴트와 함께
model_receipt_probe.py를 작성했으며, 게시하기 전에 오프라인에서 직접 실행했습니다. 아래 출력 블록의 모든 숫자는 Python 3.13.5, 표준 라이브러리만 사용하고 네트워크 연결 없이 로컬에서 실제로 실행한 결과에서 복사한 것입니다. 저는 종료 코드(0 / 1 / 2)를 확인했고, 모든 피스처의 전체 STDOUT을 두 번 해싱하여 바이트 단위로 결정론적(deterministic)임을 확인했으며, 모든 줄을 직접 편집했습니다. 제가 인용하는 외부 수치(Kaspersky의 보고서에 있는 CISPA 테스트, arXiv 대체 감사)는 제 것이 아니라 해당 기관의 수치입니다. 저는 원본 소스에 링크를 제공하며 제 결과에는 그들의 수치를 포함하지 않았습니다.
요약하자면:
- 이제 당신과 모델 사이에는 게이트웨이(gateway), 라우터(router), 릴레이(relay)와 같은 중간자가 서 있는 경우가 많습니다. 응답의
model필드에 도착하는 것은 무엇이든 그 중간자가 그곳에 쓰기로 선택한 것입니다. - 당신의 로그에는 이미 영수증이 있습니다. 요청된 모델, 응답 모델, 토큰 사용량, 청구 금액: 이 네 가지 필드는 하나의 선언된 가격 정책에 대해 오프라인에서 대조(reconcile)가 가능합니다. 키(key)도, 트래픽도 필요 없습니다.
- 중요한 데모: 두 개의 로그가 있으며, 하나의
billed_usd값을 제외하고는 바이트 단위로 동일합니다. 두 로그 모두 모델 이름은opus-4라고 되어 있습니다. 하지만 그중 하나는 비용이haiku-4요율과만 일치합니다. 종료 코드 0이 종료 코드 1이 되며, 프로브(probe)는reconciles_with=haiku-4를 출력합니다. - "영수증 없음(No receipt)"은 OK 상태가 아니라 하나의 상태입니다. 사용 가능한 모델이나 사용량 필드가 없는 응답은 WARN이며,
--strict모드에서는 FAIL입니다. - 이것은 부주의한 대체와 드리프트된 장부를 잡아내는 것이지, 정교한 위조를 잡아내는 것이 아닙니다. 플래그(flag)의 부재가 진위 여부의 증거는 아니며, 글을 마치기 전에 이 점을 다시 한번 말씀드립니다.
중간자가 들어왔고, 영수증은 나갔습니다
6월 30일, Daniel Nwaneri는 재판매되는 AI 액세스 시장의 암시장(gray market)에 관한 Dev.to 게시물을 게시했으며, 그 안에는 제가 읽어본 것 중 이 문제에 대한 가장 명확한 진술이 담겨 있습니다: "당신은 opus에 대해 비용을 지불하지만, haiku를 받게 되고, 때로는 glm을 받게 됩니다. 어떤 모델이 답변했는지 확인할 수 없습니다." 그의 말입니다. 해당 게시물은 제가 확인하지 않았고 반복하지도 않을 주장들까지 포함하여 훨씬 더 넓은 범위를 다루고 있지만, 그 두 문장만으로도 충분히 의미가 있습니다. 댓글에서 kenielzep97이라는 이름으로 글을 쓴 한 독자는 이를 한 단계 더 밀어붙였습니다: "그것은 단순한 절도가 아니라 무결성(integrity)의 파괴입니다. 당신의 로그는 무언가가 실행되었다고 말하지만 실제로는 다른 것이 실행되었으며, 그 프록시(proxy)를 통과하고 살아남는 영수증은 없습니다."
프록시를 통과하고 살아남는 영수증은 없습니다. 저는 이 문구를 계속 곱씹게 되는데, 왜냐하면 이 문구는 절반은 맞고, 틀린 절반이 오히려 유용한 부분이기 때문입니다. 프록시는 당신에게 영수증을 주지 않습니다. 하지만 프록시가 당신이 영수증을 다시 만드는 것을 막을 수도 없습니다. 왜냐하면 네 가지 영수증 필드가 이미 요청 시점에 당신의 클라이언트(client)에 의해 작성되어 당신의 호출 로그(call log)에 남아 있기 때문입니다.
그리고 중간 계층(middle layer)은 더 이상 비주류가 아닙니다. 이번 주 MCP 클라우드인 Manufact, YC S25의 Launch HN은 104점과 62개의 댓글을 기록했습니다. 게이트웨이(gateways), 라우터(routers), 그리고 릴레이(relays)는 이제 점점 늘어나는 프로덕션 호출(production calls)과 실제로 답변을 제공하는 모델 사이에 자리 잡고 있습니다. 어떤 것들은 훌륭합니다. 하지만 그들 모두는 한 가지 공통된 속성을 공유합니다: 응답(response)에 포함된 model 문자열을 작성할 권한이 그들에게 있다는 점입니다. 영수증이 없는 게이트웨이는 인프라로 위장한 신뢰(trust)일 뿐입니다.
따라서 여러분이 반박할 수 있도록 논지를 다음과 같이 정리하겠습니다. 호출 로그(call log)와 선언된 가격 정책(price policy)이 주어지면, 모든 기록의 판결은 오프라인에서 결정론적(deterministically)으로 계산 가능합니다. 요청된 모델(requested model)과 응답 모델(response model)은 별칭 맵(alias map)을 거친 후 일치해야 합니다. 청구된 금액은 선언된 허용 오차(tolerance) 범위 내에서 사용량에 선언된 모델의 요율을 곱한 값과 일치해야 합니다. 산술적 계산은 일치하는데 탐지(probe)가 드리프트(drift)를 감지하거나, 반대로 계산은 일치하지 않는데 탐지가 조용한 기록을 하나라도 보여준다면, 이 도구와 이 글은 틀린 것입니다.
여러분의 로그에서 모델 영수증은 어떤 모습인가요?
호출당 네 가지 필드가 포함됩니다: 요청한 모델, 응답이 주장하는 모델, 토큰 사용량(token usage), 그리고 청구된 금액입니다. 이를 유지하는 JSONL 로그는 다음과 같으며, 한 줄당 하나의 호출을 나타냅니다:
{"id": "call-001", "requested_model": "opus-4", "base_url": "https://api.example-provider.com/v1/messages", "response": {"model": "opus-4", "usage": {"input_tokens": 12000, "output_tokens": 3000}}, "billed_usd": 0.405}
{"id": "call-002", "requested_model": "opus-4", "base_url": "https://api.example-provider.com/v1/messages", "response": {"model": "opus-4-20260115", "usage": {"input_tokens": 52000, "output_tokens": 8200}}, "billed_usd": 1.401}
{"id": "call-003", "requested_model": "sonnet-4", "base_url": "https://api.example-provider.com/v1/messages", "response": {"model": "sonnet-4", "usage": {"input_tokens": 230000, "output_tokens": 41000}}, "billed_usd": 1.305}
...
다른 입력값은 여러분이 선언하는 가격 정책입니다. 로컬에 존재하며 절대 외부에서 가져오지 않는 하나의 JSON 파일입니다:
{
"prices_per_mtok": {
"opus-4": {"in": 15.0, "out": 75.0},
...
이 파일에 대해 세 가지를 말씀드리겠습니다. 각각은 결정의 결과물이기 때문입니다. 요율(rates)은 **고정 요율 (fixture rates)**입니다. 이는 특정 업체의 실시간 가격표가 아니라, 3단계 라인업에 대해 그럴듯하게 설정된 mtok당 수치이며, 모델 이름 또한 위의 인용문에서 가져온 고정된 컨텍스트입니다. aliases 맵은 핵심적인 역할을 합니다. 제공업체들은 실제로 과거 시점의 스냅샷 이름을 반환하곤 하는데, 요청된 opus-4에 대해 opus-4-20260115를 대조하여 플래그를 표시하는 프로브(probe)를 사용한다면 점심시간이 되기도 전에 수많은 오탐(false alarms)에 파묻히게 될 것입니다. 그리고 tolerance_pct는 기본값이 아닌 필수 항목입니다. 제 첫 번째 초안에서는 이를 5로 기본 설정했으나 삭제했습니다. 어느 정도의 과금 오차(billing slack)를 허용할지는 정책적 결정 사항이며, 도구가 사용자 몰래 정책을 결정하게 되면 편차(drift)가 정상적인 것으로 굳어지기 때문입니다.
60초 안에 실행하기
키(keys)도 필요 없습니다. 네트워크도 필요 없습니다. Python 외에 설치할 것도 없습니다. 도구 전체가 단 하나의 파일로 구성되어 있으며, 표준 라이브러리만 사용합니다:
#!/usr/bin/env python3
"""
model_receipt_probe.py -- LLM API 호출을 위한 오프라인 영수증 대조 도구,
...
기준점: 4개의 영수증, 4번의 대조
$ python3 model_receipt_probe.py fixtures/clean.jsonl fixtures/prices.json
MODEL-RECEIPT-PROBE REPORT
price policy: 3 models priced, 3 aliases, tolerance
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기