작은 LLM 회로 차단기(Circuit Breaker)를 만들었습니다: 예산이 소진되면 실패하는 대신 로컬 모델로 전환합니다
요약
LLM API 사용 중 예산 초과나 할당량 소진 시, 자동으로 로컬 모델로 전환하여 작업을 지속하게 해주는 'LLM Circuit Breaker' 라이브러리를 소개합니다. 비용 폭탄을 방지하면서도 멀티 에이전트 시스템의 연속성을 보장하는 가벼운 도구입니다.
핵심 포인트
- 설정된 예산 초과 시 유료 API 대신 로컬 모델로 자동 폴백
- JSONL 파일을 활용한 단순하고 가벼운 지출 내역 관리
- 스트리밍 및 비동기(Async) 호출 지원
- 유료 모델에서 오픈 모델, 로컬 모델로 이어지는 단계적 티어 설정 가능
저는 집에서 소규모 멀티 에이전트 시스템(multi-agent system)을 운영하고 있습니다. 노트북에서 주로 재미와 학습을 위해 정찰, 요약, 작성을 수행하는 몇 개의 에이전트를 돌리고 있죠. 대단한 것은 아닙니다. 하지만 저는 계속해서 똑같은 두 가지 벽에 부딪혔습니다:
- 실행 도중 유료 API 할당량(quota) 소진. 한 제공업체에서 429 에러(Too Many Requests)가 발생하면 전체 실행이 중단됩니다.
- 무한 루프에 빠질지 모른다는 막연한 두려움. 잘못된 재시도(retry)나 에이전트가 자기 자신과 대화하는 상황이 발생하면, 순식간에 한 달 예산의 상당 부분을 몇 분 만에 써버릴 수 있습니다.
기존의 LLM 게이트웨이(gateways)들을 살펴보았습니다. 그것들은 훌륭하지만, 대부분은 관측성(observability, 대시보드, 트레이스)이나 라우팅(routing, 가장 저렴하거나 최적의 모델 선택) 문제를 해결합니다. 제가 실제로 원했던 것은 이상할 정도로 구체적이었고, 작은 규모의 도구로는 찾을 수 없었습니다:
예산을 초과하기 직전이라면, 실패하거나 계속 비용을 지불하지 말고 — 무료 로컬 모델(local model)로 넘어가서(fall through) 작업을 계속하세요.
그래서 저는 정확히 그 기능을 수행하는 가장 작은 버전을 작성했습니다. 이 도구는 단 한 가지 일만 합니다:
from llm_circuit_breaker import LLMCircuitBreaker, anthropic_tier, ollama_tier
breaker = LLMCircuitBreaker(
...
오늘의 지출이 5달러를 넘어서면, 그날 남은 시간 동안의 모든 호출은 유료 티어(paid tier)를 건너뛰고 로컬 모델로 바로 연결됩니다 — 코드 변경도 필요 없고, 갑작스러운 청구서도 없습니다. 지출 내역은 단순한 JSONL 파일에 기록됩니다. 데이터베이스도, 호스팅된 프록시(proxy)도, 별도로 구축할 것도 없습니다.
결정 방식
- 설정한 한도(사람이 읽을 수 있는 JSONL 장부)와 비교하여 오늘의 지출 및 누적 지출을 확인합니다.
- 예산 범위 내: 각 티어를 순서대로 시도하고, 첫 번째 성공한 결과를 반환합니다.
- 예산 초과: 모든 유료 티어를 건너뛰고,
is_local=True로 표시된 티어만 시도합니다. - 시도한 모든 방법이 실패하면, 조용히 실패하는 대신 명확한 예외(exception)를 발생시킵니다.
이것이 전체 아이디어입니다. 무료 로컬 컴퓨팅(compute)으로 '실패(failing into)'하는 것이, 요란하게 실패하거나 조용히 요금 폭탄을 맞으며 실행되는 것보다 낫습니다.
실제로 사용하면서 추가한 기능 (v0.2)
- Streaming (스트리밍) —
complete_stream을 통해 동일한 티어(tier)와 예산 폴백(budget fallback)을 유지하면서, 청크(chunk) 단위로 결과물을 생성합니다. - Async (비동기) — 많은 호출을 동시에 처리할 수 있도록
acomplete를 지원합니다. costCLI — 검사할 수 없는 예산 차단기는 반쪽짜리 도구이기 때문입니다:
llm-cb cost # 장부(ledger)에서 직접 오늘 사용량 / 총 사용량 / 티어별 사용량을 확인합니다
사용해 보니 좋았던 점이 하나 더 있습니다. 티어는 단순히 순서가 있는 리스트이기 때문에, 폴백(fallback)이 반드시 "비싼 모델 → 로컬 모델"일 필요는 없습니다. 중간에 **저렴한 프론티어 오픈 모델 (cheap frontier open model)**을 끼워 넣을 수 있습니다. 예를 들어, LongCat-2.0이나 GLM-5 같은 모델을 가리키는 openrouter_tier를 설정하면, 전체 체인이 프론티어 유료 모델 → 저렴한 오픈 모델 → 로컬 모델 순서가 되며, 예산 상한선(budget cap)이 전체 과정을 여전히 제어합니다.
이것이 아닌 것 (What this is NOT)
이것은 LiteLLM이나 Portkey의 대체재가 아닙니다. 엔터프라이즈급 라우팅(routing), 가드레일(guardrails), 팀 예산 관리, 또는 대규모 플릿(fleet) 앞단의 프록시(proxy)가 필요하다면 해당 도구들을 사용하세요. 그것들은 매우 훌륭합니다.
이 프로젝트는 의도적으로 정반대의 트레이드오프(trade-off)를 택했습니다. 한 번에 읽을 수 있는 약 200줄의 코드이며, 여러분의 저장소(repo)에 바로 포함시킬 수 있습니다. 별도의 서비스를 실행하지 않고도 과도한 지출 전에 확실한 중단 지점을 원하는 개인 개발자 및 소규모 에이전트 프로젝트를 위한, 프로세스 내부(in-process)의 단일 목적 도구입니다. MIT 라이선스입니다.
개발하며 배운 두 가지
- 가장 어려운 점은 기능을 거절하는 것이었습니다. 거의 대시보드가 있는 프록시 서버로 만들 뻔했습니다. 하지만 그렇게 하는 순간, 제가 원했던 것—읽을 수 있고 신뢰할 수 있는 아주 작은 파일—이 아니게 되었습니다. 이 도구의 가치는 바로 그 '작음'에 있습니다. 로드맵을 삭제한 것이 곧 기능이었습니다.
- 폴백(Fallback)이 재시도(Retry)보다 낫습니다. 처음에는 유료 API에 대해 더 똑똑한 재시도 로직을 만드는 것이 본능적인 선택이었습니다. 하지만 할당량이 다해가는 상황에서의 재시도는 시간과 돈만 낭비할 뿐입니다. 비록 더 멍청한 모델일지라도 항상 응답하는 무료 모델로 폴백하는 것이 에이전트를 계속 작동하게 만들었습니다. 그것이 새벽 2시에 제가 실제로 필요했던 것이었습니다.
Repo (MIT): https://github.com/qkrehgk1-wq/llm-circuit-breaker
저는 진심으로 피드백을 받고 싶습니다. 특히 핵심 동작에 대해서 말이죠. 예산 소진(budget-exhausted) → 로컬 모델 전환(local) 방식이 여러분이 에이전트(agent)의 실패를 처리하고 싶은 방식과 일치하나요? 그리고 다른 분들도 로컬 모델을 기본(primary)이 아닌 폴백 (fallback) 용도로 라우팅하여 사용하시나요? 마지막 질문은 제가 계속해서 고민하고 있는 부분입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기