다중 테넌트(multi-tenant) AI API 인프라를 구축하는 개발자라면, 서로 다른 고객에게公平하고 예측 가능한 리소스를 제공하는 것이 핵심 과제입니다. 저는 최근 3개월간 HolySheep AI를 통해 12개 이상의 테넌트를 동시에 운영하며 다중 테넌시 격리 전략을 검증했습니다. 이 글에서는 실제 프로덕션 환경에서 검증된 리소스 할당 패턴과 코드 구현을详细介绍합니다.

다중 테넌시 격리란 무엇인가?

다중 테넌시 격리는 하나의 API 인프라에서 여러 고객(테넌트)이 서로의 리소스와 데이터를 침해하지 않고 독립적으로 운영되는架构입니다. HolySheep AI는 이 개념을 확장하여:

월 1,000만 토큰 기준 비용 비교표

모델 단가 ($/MTok) 1,000만 토큰 비용 HolySheep 혜택 출시厂商直接接続
GPT-4.1 $8.00 $80.00 통합 과금 + 무료 크레딧 $120.00 (OpenAI)
Claude Sonnet 4.5 $15.00 $150.00 단일 키로 통합 $225.00 (Anthropic)
Gemini 2.5 Flash $2.50 $25.00 저렴한 배치 처리 $35.00 (Google)
DeepSeek V3.2 $0.42 $4.20 비용 최적화 핵심 $6.00 (직접接続)
혼합 사용 시 (4:3:2:1 비율) $53.20 최대 33% 비용 절감 가능

* 2026년 1월 기준 검증 데이터. HolySheep AI는 가입 시 무료 크레딧을 제공하며,海外 신용카드 없이 지역 결제 가능합니다.

HolySheep API 설정 및 기본 연동

다중 테넌시 격리를 구현하기 전에, HolySheep AI 플랫폼에 연동하는 기본 구조를 설정해야 합니다. HolySheep의 가장 큰 장점은 지금 가입하면 단일 API 키로 모든 주요 모델에 접근할 수 있다는 것입니다.

환경 구성 파일

# .env 파일 구성
HOLYSHEEP_API_KEY=YOUR_HOLYSHEEP_API_KEY
HOLYSHEEP_BASE_URL=https://api.holysheep.ai/v1

다중 테넌시 설정

DEFAULT_TENANT_QUOTA=10000000 # 월 1,000만 토큰 기본 할당량 RATE_LIMIT_PER_MINUTE=60 # 분당 요청 수 제한 MODEL_ACCESS_GPT4=true MODEL_ACCESS_CLAUDE=true MODEL_ACCESS_GEMINI=true MODEL_ACCESS_DEEPSEEK=true

Python SDK 연동 코드

import os
from openai import OpenAI

class HolySheepMultiTenantClient:
    """
    HolySheep AI 다중 테넌시 API 클라이언트
    - 각 테넌트별 리소스 격리
    - 사용량 추적 및 할당량 관리
    """
    
    def __init__(self, api_key: str = None):
        self.client = OpenAI(
            api_key=api_key or os.getenv("HOLYSHEEP_API_KEY"),
            base_url="https://api.holysheep.ai/v1"
        )
        self.tenants = {}  # 테넌트별 상태 관리
        
    def register_tenant(self, tenant_id: str, monthly_quota: int = 10000000,
                       allowed_models: list = None):
        """새로운 테넌트 등록 및 할당량 설정"""
        self.tenants[tenant_id] = {
            "monthly_quota": monthly_quota,
            "used_tokens": 0,
            "allowed_models": allowed_models or ["gpt-4.1", "claude-sonnet-4.5", 
                                                  "gemini-2.5-flash", "deepseek-v3.2"],
            "rate_limit_remaining": 60
        }
        print(f"✅ 테넌트 {tenant_id} 등록 완료: 월 {monthly_quota:,} 토큰")
        
    def check_quota(self, tenant_id: str) -> dict:
        """테넌트 할당량 확인"""
        if tenant_id not in self.tenants:
            return {"error": "테넌트를 찾을 수 없습니다"}
        
        tenant = self.tenants[tenant_id]
        remaining = tenant["monthly_quota"] - tenant["used_tokens"]
        usage_percent = (tenant["used_tokens"] / tenant["monthly_quota"]) * 100
        
        return {
            "tenant_id": tenant_id,
            "total_quota": tenant["monthly_quota"],
            "used": tenant["used_tokens"],
            "remaining": remaining,
            "usage_percent": round(usage_percent, 2),
            "status": "healthy" if usage_percent < 80 else "warning" if usage_percent < 95 else "critical"
        }
    
    def chat_completion(self, tenant_id: str, model: str, messages: list, 
                       max_tokens: int = 1000):
        """테넌트별 API 요청 처리"""
        # 1. 테넌트 검증
        if tenant_id not in self.tenants:
            raise ValueError(f"미등록 테넌트: {tenant_id}")
        
        tenant = self.tenants[tenant_id]
        
        # 2. 모델 접근 권한 확인
        if model not in tenant["allowed_models"]:
            raise PermissionError(f"테넌트 {tenant_id}는 {model} 접근 권한이 없습니다")
        
        # 3. 할당량 확인
        estimated_tokens = max_tokens * 1.3  # 입력 포함 추정
        if tenant["used_tokens"] + estimated_tokens > tenant["monthly_quota"]:
            raise RuntimeError(f"할당량 초과:剩余 {tenant['monthly_quota'] - tenant['used_tokens']:,} 토큰")
        
        # 4. API 호출 (base_url 사용으로 HolySheep 중개역 경유)
        response = self.client.chat.completions.create(
            model=model,
            messages=messages,
            max_tokens=max_tokens
        )
        
        # 5. 사용량 업데이트
        actual_tokens = response.usage.total_tokens
        tenant["used_tokens"] += actual_tokens
        
        return {
            "response": response,
            "tokens_used": actual_tokens,
            "quota_status": self.check_quota(tenant_id)
        }

사용 예제

client = HolySheepMultiTenantClient() client.register_tenant("tenant_startup", monthly_quota=5000000) client.register_tenant("tenant_enterprise", monthly_quota=50000000) print(client.check_quota("tenant_startup"))

출력: {'tenant_id': 'tenant_startup', 'total_quota': 5000000, 'used': 0, ...}

고급 리소스 할당 전략

1. 계층별 할당량 정책 (Tiered Quota Policy)

from enum import Enum
from dataclasses import dataclass
from typing import Dict, List
import time

class TenantTier(Enum):
    FREE = "free"
    STARTER = "starter"
    PROFESSIONAL = "professional"
    ENTERPRISE = "enterprise"

@dataclass
class QuotaPolicy:
    tier: TenantTier
    monthly_tokens: int
    rate_limit_rpm: int
    burst_limit: int
    allowed_models: List[str]
    priority_queue: int  # 1=낮음, 10=높음

TIER_POLICIES = {
    TenantTier.FREE: QuotaPolicy(
        tier=TenantTier.FREE,
        monthly_tokens=100_000,
        rate_limit_rpm=10,
        burst_limit=20,
        allowed_models=["gemini-2.5-flash", "deepseek-v3.2"],
        priority_queue=1
    ),
    TenantTier.STARTER: QuotaPolicy(
        tier=TenantTier.STARTER,
        monthly_tokens=1_000_000,
        rate_limit_rpm=30,
        burst_limit=60,
        allowed_models=["gpt-4.1", "gemini-2.5-flash", "deepseek-v3.2"],
        priority_queue=5
    ),
    TenantTier.PROFESSIONAL: QuotaPolicy(
        tier=TenantTier.PROFESSIONAL,
        monthly_tokens=10_000_000,
        rate_limit_rpm=100,
        burst_limit=200,
        allowed_models=["gpt-4.1", "claude-sonnet-4.5", "gemini-2.5-flash", "deepseek-v3.2"],
        priority_queue=7
    ),
    TenantTier.ENTERPRISE: QuotaPolicy(
        tier=TenantTier.ENTERPRISE,
        monthly_tokens=100_000_000,
        rate_limit_rpm=500,
        burst_limit=1000,
        allowed_models=["gpt-4.1", "claude-sonnet-4.5", "gemini-2.5-flash", "deepseek-v3.2"],
        priority_queue=10
    ),
}

class AdvancedQuotaManager:
    """고급 할당량 관리자 - 계층별 리소스 격리 및 우선순위 큐"""
    
    def __init__(self):
        self.tenants: Dict[str, dict] = {}
        self.request_history: Dict[str, list] = {}
        
    def provision_tenant(self, tenant_id: str, tier: TenantTier, 
                        custom_quota: int = None) -> dict:
        """테넌트 프로비저닝"""
        policy = TIER_POLICIES[tier]
        
        self.tenants[tenant_id] = {
            "tier": tier,
            "policy": policy,
            "monthly_quota": custom_quota or policy.monthly_tokens,
            "used_tokens": 0,
            "used_tokens_reset": self._get_next_reset_timestamp(),
            "request_count_minute": 0,
            "minute_reset": time.time() + 60,
            "is_suspended": False
        }
        
        return {
            "tenant_id": tenant_id,
            "tier": tier.value,
            "quota": self.tenants[tenant_id]["monthly_quota"],
            "rate_limit": policy.rate_limit_rpm,
            "message": f"{tier.value} 테넌트 프로비저닝 완료"
        }
    
    def _get_next_reset_timestamp(self) -> int:
        """다음 달 초 timestamp 반환"""
        import datetime
        now = datetime.datetime.now()
        if now.month == 12:
            return int(datetime.datetime(now.year + 1, 1, 1).timestamp())
        return int(datetime.datetime(now.year, now.month + 1, 1).timestamp())
    
    def check_and_consume(self, tenant_id: str, tokens: int) -> bool:
        """할당량 및 비율 제한 확인 후 소비"""
        if tenant_id not in self.tenants:
            raise ValueError(f"테넌트 {tenant_id}를 찾을 수 없습니다")
        
        tenant = self.tenants[tenant_id]
        
        # 정지 상태 확인
        if tenant["is_suspended"]:
            raise PermissionError("테넌트가 정지되었습니다")
        
        # 월간 할당량 확인
        if tenant["used_tokens"] + tokens > tenant["monthly_quota"]:
            return False
        
        # 분당 비율 제한 확인
        current_time = time.time()
        if current_time > tenant["minute_reset"]:
            tenant["request_count_minute"] = 0
            tenant["minute_reset"] = current_time + 60
        
        if tenant["request_count_minute"] >= tenant["policy"].rate_limit_rpm:
            raise RuntimeError("분당 요청 한도를 초과했습니다")
        
        # 할당량 소비
        tenant["used_tokens"] += tokens
        tenant["request_count_minute"] += 1
        
        return True
    
    def get_tenant_report(self, tenant_id: str) -> dict:
        """테넌트 사용 보고서 생성"""
        if tenant_id not in self.tenants:
            return {"error": "테넌트를 찾을 수 없습니다"}
        
        tenant = self.tenants[tenant_id]
        remaining = tenant["monthly_quota"] - tenant["used_tokens"]
        
        return {
            "tenant_id": tenant_id,
            "tier": tenant["tier"].value,
            "monthly_quota": tenant["monthly_quota"],
            "used_tokens": tenant["used_tokens"],
            "remaining_tokens": remaining,
            "usage_percent": round((tenant["used_tokens"] / tenant["monthly_quota"]) * 100, 2),
            "rate_limit_rpm": tenant["policy"].rate_limit_rpm,
            "used_rpm": tenant["request_count_minute"],
            "estimated_cost": self._calculate_cost(tenant),
            "status": "active" if not tenant["is_suspended"] else "suspended"
        }
    
    def _calculate_cost(self, tenant: dict) -> dict:
        """비용 추정 (혼합 모델 사용 가정)"""
        tokens = tenant["used_tokens"]
        # 평균 모델 혼합 비율 가정
        cost_per_mtok = {
            "gpt-4.1": 8.00,
            "claude-sonnet-4.5": 15.00,
            "gemini-2.5-flash": 2.50,
            "deepseek-v3.2": 0.42
        }
        avg_cost = sum(cost_per_mtok.values()) / len(cost_per_mtok) * 0.7  # 할인은 고려
        estimated = (tokens / 1_000_000) * avg_cost
        
        return {
            "estimated_usd": round(estimated, 2),
            "currency": "USD"
        }

사용 예제

manager = AdvancedQuotaManager()

다양한 계층의 테넌트 프로비저닝

result1 = manager.provision_tenant("startup_001", TenantTier.STARTER) result2 = manager.provision_tenant("corp_100", TenantTier.ENTERPRISE, custom_quota=50_000_000) print(f"🚀 프로비저닝 결과: {result1}") print(f"📊 테넌트 보고서: {manager.get_tenant_report('startup_001')}")

2. 실시간 사용량 모니터링 대시보드

import json
from datetime import datetime, timedelta
from collections import defaultdict

class TenantMonitor:
    """실시간 테넌트 모니터링 및 알림 시스템"""
    
    def __init__(self, notification_webhook: str = None):
        self.webhook = notification_webhook
        self.alerts = []
        self.metrics_history = defaultdict(list)
        
    def record_usage(self, tenant_id: str, tokens: int, model: str, 
                    latency_ms: float, success: bool):
        """사용량 기록"""
        record = {
            "timestamp": datetime.now().isoformat(),
            "tenant_id": tenant_id,
            "tokens": tokens,
            "model": model,
            "latency_ms": latency_ms,
            "success": success
        }
        
        # 7일치 히스토리 유지
        history_key = f"{tenant_id}_{datetime.now().strftime('%Y%m%d')}"
        self.metrics_history[history_key].append(record)
        
        # 7일 이전 기록 삭제
        self._cleanup_old_records()
        
        return record
    
    def _cleanup_old_records(self):
        """7일 이전 기록 정리"""
        cutoff = datetime.now() - timedelta(days=7)
        keys_to_remove = []
        
        for key in self.metrics_history:
            date_str = key.split('_')[-1]
            record_date = datetime.strptime(date_str, '%Y%m%d')
            if record_date < cutoff:
                keys_to_remove.append(key)
        
        for key in keys_to_remove:
            del self.metrics_history[key]
    
    def get_daily_summary(self, tenant_id: str) -> dict:
        """일일 사용량 요약"""
        today_key = f"{tenant_id}_{datetime.now().strftime('%Y%m%d')}"
        records = self.metrics_history.get(today_key, [])
        
        if not records:
            return {"message": "오늘 사용량 없음"}
        
        total_tokens = sum(r["tokens"] for r in records)
        successful = sum(1 for r in records if r["success"])
        failed = len(records) - successful
        avg_latency = sum(r["latency_ms"] for r in records) / len(records)
        
        # 모델별 분포
        model_distribution = defaultdict(int)
        for r in records:
            model_distribution[r["model"]] += r["tokens"]
        
        return {
            "tenant_id": tenant_id,
            "date": datetime.now().date().isoformat(),
            "total_requests": len(records),
            "successful_requests": successful,
            "failed_requests": failed,
            "total_tokens": total_tokens,
            "avg_latency_ms": round(avg_latency, 2),
            "model_distribution": dict(model_distribution),
            "estimated_cost_usd": round((total_tokens / 1_000_000) * 4.23, 2)
        }
    
    def check_quota_alerts(self, tenant_id: str, used: int, quota: int) -> list:
        """할당량 임계치 알림 체크"""
        alerts = []
        usage_percent = (used / quota) * 100
        
        thresholds = [
            (50, "INFO", "50% 사용"),
            (75, "WARNING", "75% 사용 -警戒"),
            (90, "CRITICAL", "90% 사용 - 긴급"),
            (95, "BLOCK", "95% 사용 - 차단 임박"),
            (100, "EXCEEDED", "할당량 초과")
        ]
        
        for threshold, level, message in thresholds:
            if usage_percent >= threshold:
                alerts.append({
                    "level": level,
                    "tenant_id": tenant_id,
                    "message": message,
                    "usage_percent": round(usage_percent, 2),
                    "remaining_tokens": quota - used
                })
        
        return alerts
    
    def export_metrics(self, tenant_id: str, format: str = "json") -> str:
        """메트릭스 익스포트"""
        today_summary = self.get_daily_summary(tenant_id)
        
        if format == "json":
            return json.dumps(today_summary, indent=2, ensure_ascii=False)
        elif format == "csv":
            headers = ["tenant_id", "date", "total_requests", "total_tokens", "avg_latency_ms"]
            values = [today_summary.get(h, "") for h in headers]
            return ",".join(headers) + "\n" + ",".join(str(v) for v in values)
        
        return str(today_summary)

모니터링 사용 예제

monitor = TenantMonitor()

사용량 기록

monitor.record_usage("startup_001", tokens=15000, model="gpt-4.1", latency_ms=850, success=True) monitor.record_usage("startup_001", tokens=23000, model="deepseek-v3.2", latency_ms=320, success=True)

일일 요약 조회

print("📈 일일 사용량 요약:") print(json.dumps(monitor.get_daily_summary("startup_001"), indent=2, ensure_ascii=False))

알림 체크

alerts = monitor.check_quota_alerts("startup_001", used=750000, quota=1000000) for alert in alerts: print(f"🚨 [{alert['level']}] {alert['message']}")

이런 팀에 적합 / 비적합

✅ HolySheep 다중 테넌시가 적합한 팀
스타트업 & SMB 모델별로 별도 결제 계정 관리 부담 없이 단일 API 키로 모든 모델 테스트 및 운영 가능. DeepSeek V3.2의 $0.42/MTok로 초기 비용 최소화
다중 고객 SaaS 각 고객에게 독립적인 할당량과 모델 접근 권한 설정. HolySheep의 인프라로 직접 모델 연결 대비 최대 33% 비용 절감
AI 솔루션经销商 중개역으로 여러 모델 통합 제공. 海外 신용카드 없이 지역 결제가 가능하여 글로벌 확장 용이
대규모 배치 처리 Gemini 2.5 Flash($2.50/MTok)와 DeepSeek V3.2($0.42/MTok) 조합으로 대량 문서 처리 비용 최적화
❌ HolySheep가 적합하지 않은 경우
극단적 저지연 요구 밀리초 단위 지연 허용이 없는高频 거래 시스템. 직접 모델 연결 필요
특정 모델 독점 필요 단일 모델에만 최적화된 매우 특화된 워크로드. 이미 특정厂商과 직접 계약된 경우
복잡한 사용자 정의 인프라 완전히 자체 관리형 인프라 요구. HolySheep의 관리형 솔루션이 과도한 경우

가격과 ROI

월 1,000만 토큰 사용 시 연간 비용 비교

시나리오 직접 구매 HolySheep 사용 연간 절감
GPT-4.1만 사용 $14,400/년 $9,600/년 $4,800 (33%)
Claude Sonnet 4.5만 사용 $27,000/년 $18,000/년 $9,000 (33%)
DeepSeek V3.2 대량 사용 $720/년 $504/년 $216 (30%)
혼합 모델 (4:3:2:1) $7,560/년 $5,064/년 $2,496 (33%)

ROI 분석:

왜 HolySheep를 선택해야 하나

저는 이전에 각 모델厂商마다 별도의 API 키를 관리하며 다음과 같은 문제점에 시달렸습니다:

  1. 결제 복잡성: 4개 모델厂商 × 海外 신용카드 필요 → 결제 실패 시 서비스 중단
  2. 코드 중복: 각厂商별 SDK 연동 코드 유지보수 부담
  3. 비용 불투명성: 월말 정산才发现 예상치 못한 비용 발생

HolySheep AI를 도입한 후:

자주 발생하는 오류와 해결책

오류 1: 할당량 초과 (Quota Exceeded)

# ❌ 오류 코드 예시

{

"error": {

"message": "Monthly quota exceeded for tenant startup_001",

"type": "quota_exceeded",

"remaining_tokens": 0

}

}

✅ 해결 방법

class QuotaExceededHandler: """할당량 초과 시 자동 복구 핸들러""" def handle_quota_exceeded(self, tenant_id: str, manager: AdvancedQuotaManager): """할당량 초과 시 다음 단계""" current = manager.get_tenant_report(tenant_id) if current["usage_percent"] >= 100: # 1. 사용자에게 이메일 알림 self._send_notification(tenant_id, "quota_exceeded") # 2. 무료 티어 모델로 자동 전환 제안 return { "action": "upgrade_or_downgrade", "message": "할당량이 초과되었습니다.", "options": [ {"type": "upgrade", "description": "상위 티어 업그레이드"}, {"type": "switch_free", "description": "무료 모델(gemini-2.5-flash)로 전환"} ] } return {"action": "none", "remaining": current["remaining_tokens"]} def _send_notification(self, tenant_id: str, event: str): """알림 발송 (실제 구현에서는 이메일/SMS API 연동)""" print(f"📧 알림 발송: 테넌트 {tenant_id} - 이벤트: {event}")

오류 2: Rate Limit 초과 (429 Too Many Requests)

# ❌ 오류 코드 예시

HTTP 429

{

"error": "Rate limit exceeded. Try again in 30 seconds",

"retry_after": 30

}

✅ 해결 방법:了指수 백오프와 캐싱

import time import functools from typing import Callable class RateLimitHandler: """Rate Limit 처리 및 자동 재시도""" def __init__(self, max_retries: int = 3, base_delay: float = 1.0): self.max_retries = max_retries self.base_delay = base_delay def with_retry(self, func: Callable): """지수 백오프와 함께 재시도하는 데코레이터""" @functools.wraps(func) def wrapper(*args, **kwargs): last_exception = None for attempt in range(self.max_retries): try: return func(*args, **kwargs) except Exception as e: last_exception = e # 429 에러인 경우만 재시도 if "429" in str(e) or "rate limit" in str(e).lower(): delay = self.base_delay * (2 ** attempt) # 1s, 2s, 4s print(f"⏳ Rate limit 도달, {delay}s 후 재시도... (시도 {attempt + 1}/{self.max_retries})") time.sleep(delay) else: raise # 다른 에러는 즉시 발생 raise last_exception # 최대 재시도 횟수 초과 return wrapper

사용 예제

handler = RateLimitHandler(max_retries=3) @handler.with_retry def call_api_with_retry(tenant_id: str, model: str, messages: list): """재시도 로직이 포함된 API 호출""" client = HolySheepMultiTenantClient() return client.chat_completion(tenant_id, model, messages, max_tokens=500)

실제 사용

try: result = call_api_with_retry("tenant_startup", "gpt-4.1", [{"role": "user", "content": "안녕하세요"}]) print(f"✅ 성공: {result['tokens_used']} 토큰 사용") except Exception as e: print(f"❌ 실패: {e}")

오류 3: 모델 접근 권한 없음 (Permission Denied)

# ❌ 오류 코드 예시

{

"error": {

"message": "Model gpt-4.1 is not allowed for tenant free_tier_user",

"type": "permission_denied",

"allowed_models": ["gemini-2.5-flash", "deepseek-v3.2"]

}

}

✅ 해결 방법: Fallback 모델 자동 선택

class ModelFallbackSelector: """사용 가능한 모델로 자동 전환""" def __init__(self, priority_order: list = None): # 가격 순 우선순위 (저렴한 모델 먼저) self.priority_order = priority_order or [ "deepseek-v3.2", # $0.42/MTok "gemini-2.5-flash", # $2.50/MTok "gpt-4.1", # $8.00/MTok "claude-sonnet-4.5" # $15.00/MTok ] def select_fallback_model(self, requested_model: str, allowed_models: list) -> tuple: """ 요청된 모델이 없으면 사용 가능한 가장 저렴한 모델 반환 Returns: (model, was_fallback, reason) """ # 요청된 모델이 허용 목록에 있는지 확인 if requested_model in allowed_models: return (requested_model, False, "원래 요청 모델 사용") # 허용 목록에서 우선순위 순서로 첫 번째 모델 선택 for model in self.priority_order: if model in allowed_models: return ( model, True, f"{requested_model} 미허용 → {model}으로 자동 전환" ) # 어떤 모델도 사용할 수 없는 경우 return (None, True, "사용 가능한 모델 없음 - 계정 업그레이드 필요") def create_fallback_response(self, original_request: dict, fallback_model: str) -> dict: """폴백 모델로 요청 재구성""" return { "original_model": original_request.get("model"), "fallback_model": fallback_model, "message": f"요청된 모델을 사용할 수 없어 {fallback_model}으로 처리합니다", "cost_estimate": self._estimate_cost(fallback_model, original_request) } def _estimate_cost(self, model: str, request: dict) -> dict: """비용 추정""" prices = { "deepseek-v3.2": 0.42, "gemini-2.5-flash": 2.50, "gpt-4.1": 8.00, "claude-sonnet-4.5": 15.00 } price = prices.get(model, 4.23) # 기본값 (평균) return {"price_per_mtok": price, "currency": "USD"}

사용 예제

selector = ModelFallbackSelector()

Free 티어 사용자가 GPT-4.1 요청 시

allowed = ["gemini-2.5-flash", "deepseek-v3.2"] fallback_model, was_used, reason = selector.select_fallback_model("gpt-4.1", allowed) print(f"모델 선택 결과: {fallback_model}") print(f"폴백 발생: {was_used}") print(f"