폭주를 잡 단위로 판정하지 않기로 했다
호출 1회 비용 상한을 만들고, 올리고, 알림으로 바꾸고, 결국 없앴다. 나흘 동안 같은 장치를 네 번 고친 끝에 남은 건 총량 게이트와 타임아웃뿐이다. "알림만 주라고 했지 않아?"
공유 큐는 여러 서비스의 LLM 호출을 한 줄로 세워 돌리는 장치다. 구독 쿼터는 총량으로 정해져 있는데, 잡 하나가 컨텍스트를 여러 번 다시 실으면서 평소의 네 배를 태우는 일이 나왔다. 그 폭주를 호출 1회짜리 비용 상한으로 막아 보려 했다.
하려던 것
공유 큐에서 폭주 잡이 보였다. 정상 잡은 78,291자 입력에 토큰 44,519, 11초다. 폭주 잡은 61,112자 입력인데 입력 토큰 193,395, 출력 59,113, 477초. thinking은 0인데 컨텍스트를 여러 번 다시 실은 것이다. 구조화 출력의 재시도 루프로 추정됐다.
길이는 방아쇠지만 상한은 아니었다. 1만 자 미만 143건은 폭주 0, 4~8만 자 21건 중 8건 폭주, 15만 자가 정상 종료하기도 했다. 타임아웃으로도 못 막는다. 180초에서 600초로 늘리자 죽던 잡이 “성공”하며 더 태웠다.
그래서 9월 2일에 --max-budget-usd 0.15달러를 호출 1회 상한으로 걸었다.
어떻게 시켰나
나는 지금 구독형에서 클로드 쓰는 건데, 저렇게 비용이 계산되는 거야?
구독이라 한계비용은 0이다. 플래그가 보는 값은 CLI가 토큰 수에 API 정가를 곱한 환산치이고, 구독 인증에서도 계산과 차단이 작동한다는 걸 실측했다. 토큰의 단조 함수라 결국 토큰 상한을 달러로 적은 것이다. 문서에 그렇게 남겼다.
같은 밤에 이미 방향을 말했다. “사실상 몇 건 안 되니 되게 하고, 대신 많이 필요한 경우에 텔레그램으로 알림을 주면 어떨까.” 그런데 다음 날 아침 커밋은 차단을 유지한 채 상한만 0.20달러로 올렸다.
어디서 막혔나
상한을 올린 근거는 실측 72건이었다. 정상 잡은 0.0070.191달러, 폭주 잡은 0.1000.489달러. 분포가 겹친다. 0.15에서는 정상 2건이 잘리고 폭주 10건이 막히고 5건이 통과했다. 0.20에서는 정상 0, 폭주 8 차단, 7 통과. 오탐 0인 최소값이라 골랐다. 완전 차단이 아니라 손해의 상한이라고 적었다. 그리고 차단에 텔레그램 알림을 붙였다.
아침 7시 17분에 물었다. “예산은 남아 있는데 왜 예산 초과야?” 4분 뒤에 다시. “내가 알림만 주고 처리하게 하라고 했지 않아?”
0.20달러가 하루에 27건을 성공에서 실패로 바꾸고 있었다. 폭주로 분류된 15건은 전부 완료된 잡이었다. 비쌌을 뿐이다. 주간 쿼터는 31%였다. 커밋 본문에 그대로 적혔다. “요구는 ‘돌게 하고 알림만 줘라’였는데 알림을 차단에 붙였다.”
그 사이 캐시 이야기도 하나 정정됐다. 같은 문서를 앞에 두고 지시문만 바꾸면 캐시 읽기가 0이었다. 완전히 같은 입력만 읽힌다. CLI의 캐시는 메시지 단위라, 로컬 vLLM에서 쓰던 프리픽스 캐싱 전제가 여기선 깨진다. 전날 문서에 적은 “캐시 쓰기 1.25배라 전문 전송이 더 비싸다”도 걷어냈다. 1.25는 API 정가 배수이고 구독에선 달러가 제약이 아니다.
어떻게 풀었나
9월 3일에 차단을 알림으로 바꿨다. 출력 토큰 5,000을 넘으면 알림만 보낸다. 정상 최대 4,433과 폭주 41,000 사이의 빈 구간이다. 비용 상한은 1.0달러로 올려 마지막 방어선으로만 남겼다. 관측 최대가 0.489달러니 평소엔 안 걸린다.
이틀 뒤 그마저 없앴다. “평상시엔 안 걸리는 장치는 걸릴 때 가장 비싼 잡을 골라서 버린다.” 쿼터는 5시간 창과 주간 게이트가 총량으로 지킨다. 부수 효과가 하나 있었다. 실패 처리 경로가 토큰을 기록하지 않아서 차단된 잡의 소모가 게이트에 안 잡히고 있었다. 차단을 뺀 쪽이 계량이 더 정확했다.
같은 날 알림도 껐다. 5,000은 Haiku와 effort low 분포에서 고른 값인데, 잡별로 모델과 effort를 고를 수 있게 열자 정상 잡도 넘을 수 있게 됐다. 정상 잡이 상시로 때리는 알림은 소음이고, 소음이 되는 순간 진짜 폭주도 같이 안 보인다. 판별자가 전제를 잃은 채로 켜 두는 것이 끄는 것보다 나쁘다.
남은 장치는 셋이다. 총량 게이트, 호출 1건을 자르는 유일한 장치인 10분 타임아웃, 사후 관측용 대시보드. 근본 대응은 큐가 아니라 프로듀서 쪽이었다. 공시 요약 서비스가 입력을 2.4만 자로 클램프하자 폭주가 0건이 됐다.
배운 점
- 폭주는 잡 단위로 판정하지 않는다. 정상과 폭주의 분포가 겹치면 어떤 상한도 정상 잡을 자른다. 총량으로 지키고 원인은 프로듀서에서 없앤다.
- “알림만”이라는 요구에 차단을 붙이지 않는다. 하루 27건 실패는 요구를 잘못 옮긴 비용이었다.
- 평상시 안 걸리는 장치는 걸릴 때 최악의 선택을 한다. 가장 비싼 잡, 곧 가장 긴 문서를 골라 버린다.
- 판별 기준의 전제가 바뀌면 판별자를 끈다. 모델 선택이 열리는 순간 5,000이라는 숫자는 근거를 잃었다.
- 구독 CLI의 캐시는 메시지 단위다. 문서 앞, 지시 뒤 구조로는 캐시가 안 맞는다. 로컬 서빙의 전제를 그대로 들고 오지 않는다.