실행 결과를 짧게 돌려주는 프로그램을 만들고 나니 얼마가 줄었는지 설명하는 일도 중요해졌다. 전체 토큰과 비캐시 입력, 전달 바이트와 개발 중 사용량을 한 숫자로 묶으면 다른 질문의 답이 섞인다. 그 구분을 직접 시험할 수 있도록 작은 사용량 원장을 독립 코드로 만들었다.
이 원장은 사용량을 측정하는 연결기가 아니다
입력은 호출자가 선언한 합성 기록이다. 모델 호출 ID, 개발 또는 정상 실행 범위, 성공·실패·취소 상태와 토큰·바이트 값을 받는다. 계정이나 세션을 읽지 않고 모델을 부르지 않는다. 기록이 실제 제공자 응답과 연결됐는지 인증하는 기능도 없으므로 provider_authenticated는 항상 False다.
코드를 읽은 원래 제품의 내부 CLI는 이미 제거된 역사적 구현이다. 남은 사용량 스키마와 시험을 현재 모델 실행기로 복원하지 않았다. 이 예제의 엄격한 입력 계약은 비교 단위를 이해하기 위한 별도 선택이며 모든 서비스의 응답 형식이 이렇다는 뜻도 아니다. 금액이나 구독 잔여량을 계산하지 않는 범위부터 명시했다.
부분집계는 전체에 한 번 더 더하지 않는다
이 계약에서 cached는 input의 부분집계이고 reasoning은 output의 부분집계다. 전체가 알려져 있으면 input과 output의 합과 같아야 한다. 캐시나 추론을 다시 더한 모순 입력은 거절한다. 전체를 직접 받지 않았어도 입력과 출력이 모두 있으면 derived로 계산하고, 제공자가 보고한 값과 출처를 구별한다.
비캐시 입력은 input에서 cached를 뺀 값이다. 여기에 output을 더한 지표도 별도 이름으로 둔다. 이것을 전체라고 부르지 않는다. 캐시를 모르면 비캐시 입력도 unknown이며 나머지 값을 대입해 0으로 꾸미지 않는다. 계산이 가능한 조건과 지표 이름이 함께 있어야 같은 표를 읽는 사람이 같은 수치를 이해할 수 있다.
unknown이 있는 합계는 부분 합계로 표시한다
각 지표에는 known_sum과 unknown_calls, complete가 함께 나온다. 알려진 호출의 값을 더할 수는 있지만 누락된 호출이 있으면 완전한 합계가 아니다. 입력·출력은 없고 전체만 보고된 경우도 따로 다룬다. 전체 지표가 완전하다고 입력과 캐시의 상세까지 아는 것은 아니다.
명시적인 0은 알려진 값으로 유지한다. 실패나 취소 호출도 원장에 남으며 실제 사용량이 알려지지 않았으면 그 상태를 보존한다. 전송 바이트는 독립 지표로 더하고 토큰으로 환산하지 않는다. 숫자가 없는 행을 빼서 더 좋아 보이는 성공 실행 표를 만드는 대신 어느 부분을 아직 모르는지 드러낸다.
코드와 구현 살펴보기
독립 합성 전체 구현
"""Strict synthetic usage ledger. Counts are caller declarations, not billing evidence."""
from copy import deepcopy
TOKEN_FIELDS = ('input', 'cached', 'output', 'reasoning', 'total')
BYTE_FIELDS = ('request_bytes', 'response_bytes')
FIELDS = {'id', 'bucket', 'status', *TOKEN_FIELDS, *BYTE_FIELDS}
def count(value):
if value is not None and (type(value) is not int or value < 0 or value > 9007199254740991):
raise ValueError('NONNEGATIVE_INTEGER_OR_UNKNOWN_REQUIRED')
return value
def normalize(row):
if type(row) is not dict or set(row) != FIELDS:
raise ValueError('EXACT_RECORD_FIELDS_REQUIRED')
if type(row['id']) is not str or not row['id'].strip() or len(row['id']) > 120:
raise ValueError('CALL_ID_REQUIRED')
if row['bucket'] not in ('development', 'normal_run') or row['status'] not in ('completed', 'failed', 'cancelled'):
raise ValueError('RECORD_CATEGORY_INVALID')
for key in TOKEN_FIELDS + BYTE_FIELDS:
count(row[key])
i, c, o, r, t = (row[k] for k in TOKEN_FIELDS)
if i is not None and c is not None and c > i:
raise ValueError('CACHE_IS_A_SUBSET_OF_INPUT')
if o is not None and r is not None and r > o:
raise ValueError('REASONING_IS_A_SUBSET_OF_OUTPUT')
if t is not None:
if (i is not None and o is not None and t != i + o) or t < (i or 0) + (o or 0):
raise ValueError('TOTAL_INCONSISTENT_WITH_INPUT_OUTPUT')
return deepcopy(row)
def derived(row):
i, c, o, r, t = (row[k] for k in TOKEN_FIELDS)
uncached = None if i is None or c is None else i - c
total = t if t is not None else None if i is None or o is None else i + o
return {**{k: row[k] for k in TOKEN_FIELDS + BYTE_FIELDS},
'effective_total': total,
'uncached_input': uncached,
'uncached_input_plus_output': None if uncached is None or o is None else uncached + o,
'total_source': 'reported' if t is not None else 'derived' if total is not None else 'unknown'}
def aggregate(rows):
values = [derived(row) for row in rows]
keys = TOKEN_FIELDS + BYTE_FIELDS + ('effective_total', 'uncached_input', 'uncached_input_plus_output')
result = {'calls': len(rows), 'status_counts': {s: sum(r['status'] == s for r in rows) for s in ('completed', 'failed', 'cancelled')},
'metrics': {}, 'total_sources': {s: sum(v['total_source'] == s for v in values) for s in ('reported', 'derived', 'unknown')},
'provider_authenticated': False, 'currency_cost_calculated': False}
for key in keys:
known = [v[key] for v in values if v[key] is not None]
result['metrics'][key] = {'known_sum': sum(known), 'unknown_calls': len(rows) - len(known), 'complete': len(rows) == len(known)}
return result
class UsageLedger:
def __init__(self):
self._rows = {}
def add(self, row):
clean = normalize(row)
old = self._rows.get(clean['id'])
if old is not None:
if old != clean:
raise ValueError('CALL_ID_CONFLICT')
return False
self._rows[clean['id']] = clean
return True
def rows(self):
return deepcopy(list(self._rows.values()))
def summary(self, bucket=None):
if bucket is not None and bucket not in ('development', 'normal_run'):
raise ValueError('BUCKET_INVALID')
return aggregate([r for r in self._rows.values() if bucket is None or r['bucket'] == bucket])
def compare_samples(self, before_ids, after_ids):
for ids in (before_ids, after_ids):
if type(ids) is not list or not ids or any(type(x) is not str for x in ids) or len(ids) != len(set(ids)):
raise ValueError('DISTINCT_NONEMPTY_CALL_LIST_REQUIRED')
if any(x not in self._rows for x in ids):
raise ValueError('CALL_NOT_FOUND')
if set(before_ids) & set(after_ids):
raise ValueError('OVERLAPPING_SAMPLES')
rows = [self._rows[x] for x in before_ids + after_ids]
if any(r['bucket'] != 'normal_run' for r in rows):
raise ValueError('COMPARE_NORMAL_RUNS_ONLY')
before, after = (aggregate([self._rows[x] for x in ids]) for ids in (before_ids, after_ids))
changes = {}
for key in ('effective_total', 'uncached_input_plus_output'):
a, b = before['metrics'][key], after['metrics'][key]
complete = a['complete'] and b['complete']
changes[key] = {'before': a, 'after': b,
'difference': b['known_sum'] - a['known_sum'] if complete else None,
'reduction_percent': round(100 * (a['known_sum'] - b['known_sum']) / a['known_sum'], 4) if complete and a['known_sum'] else None}
return {'sample_changes': changes, 'controlled_experiment_verified': False,
'general_savings_guaranteed': False, 'currency_cost_calculated': False}
같은 영수증의 재전달과 새 호출을 나눈다
같은 ID와 같은 내용이 다시 들어오면 두 번째 호출로 합산하지 않는다. 같은 ID에 다른 사용량이 들어오면 충돌로 거절해 앞의 기록을 조용히 바꾸지 않는다. 다만 ID는 호출자가 제공한 것이며 실제 제공자 요청 ID임을 인증하지는 않는다. 인증된 원자료 연결은 운영에서 별도로 해야 한다.
development와 normal_run도 나눠 집계한다. 실패를 포함해 개선에 쓴 양과 정상 반복 한 번의 양은 다른 범위다. 비교 함수에는 정상 실행만 넣게 했다. 개발에 많은 토큰을 썼다는 사실을 분모로 삼아 정상 실행이 대폭 절감됐다고 계산하는 길을 예제의 계약에서 막은 것이다.
비교는 두 표본의 차이까지만 계산한다
비교할 호출 집합은 비어 있지 않고 서로 겹치지 않아야 한다. 지표가 양쪽 모두 완전할 때 차이와 감소율을 계산하며 기준이 0이면 비율은 만들지 않는다. 정상 실행이라는 같은 이름이 붙어 있어도 작업·모델·캐시·대화 길이·실행 순서가 같은지는 이 코드가 확인하지 않는다.
그래서 controlled_experiment_verified와 general_savings_guaranteed는 항상 False다. 출력의 감소율은 제공된 두 표본의 산술 결과다. 한 번의 실제 성공이나 역사적 비교를 모든 고객의 고정 절감률로 바꾸지 않는다. 원래 작업 기록을 읽을 때도 이 구분이 필요했기에 공유 예제에 필드로 남겼다.
실행 기록과 시험도 같은 단위로 남긴다
독립 원본과 추출 ZIP에서 각각 32개 시험을 실행한다. 캐시·추론의 이중 합산, unknown과 0, 누락된 입력 상세, 실패·취소 유지, ID 충돌, 개발 비교 차단과 기준 0인 비율을 확인한다. 다섯 기록은 로컬 함수 호출로 만들었고 아래 선택기는 그 결과만 읽는다.
코드·시험·기록 생성기와 설명은 수정하고 재사용할 수 있다. 영속 파일 저장, 동시 작성, 자동 계측, 가격표 연결은 구현하지 않았다. 필요한 운영 기능을 덧붙이더라도 먼저 어떤 단위를 언제부터 언제까지 측정했는지 남겨야 한다. 절감이라는 말보다 비교 가능한 수치를 만들 수 있는 계약을 공개하고 싶었다.