AI Engineering Academy · Lektion

Fallback-udbydere og circuit breakers

Opbyg en udbyderkaskade, der automatisk skifter fra OpenAI til Anthropic og derefter til en lokal model, når den primære udbyder er langsom eller utilgængelig, ved hjælp af circuit breaker-mønsteret.

Lektion 3 af 413 trin

Fallback-udbydere og circuit breakers er en gratis AI Engineering Academy-lektion på CoddyKit. Dette er lektion 3 af 4. Du kan læse hele lektionen gratis nedenfor — og derefter øve dig praktisk i browseren med en indbygget kodeeditor og en AI-vejleder, der er tilgængelig døgnet rundt. Den er en del af læringsforløbet i AI Engineering Academy, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. AI Engineering Academy-kurset indeholder 4 lektioner i alt.

Risikoen ved én udbyder

Hvis du er afhængig af én LLM-udbyder, opstår der et enkelt fejlpunkt. OpenAI har oplevet driftsafbrydelser, som tog minutter til timer at løse. Hvis hele din applikation afhænger af, at GPT-4o er tilgængelig, bliver enhver hændelse hos udbyderen straks til nedetid, som brugerne mærker. En strategi med en reserveudbyder opretholder tjenestens kontinuitet ved at sende trafikken til alternative udbydere, når den primære udbyder svigter.

Definition af en udbyderkæde

En udbyderkæde er en ordnet liste over udbydere og modeller, som afprøves i rækkefølge. Når den primære udbyder svigter eller får timeout, prøver systemet automatisk den næste udbyder. En typisk kæde kan være: OpenAI GPT-4o → Anthropic Claude 3.5 Sonnet → en lokalt implementeret Llama-model. Hvert niveau er en reserve, hvor den lokale model fungerer som sidste udvej, der ikke kan gå ned.

from dataclasses import dataclass
from typing import Optional

@dataclass
class Provider:
    name: str
    base_url: Optional[str]
    api_key_env: str
    model: str
    priority: int  # lower = higher priority

CASCADE = [
    Provider('openai',    None,                              'OPENAI_API_KEY',    'gpt-4o',              1),
    Provider('anthropic', 'https://api.anthropic.com/v1',   'ANTHROPIC_API_KEY', 'claude-3-5-sonnet',   2),
    Provider('local',     'http://localhost:8000/v1',        'LOCAL_KEY',         'llama-3.1-8b-inst',   3),
]

Implementering af reserveløkken

Implementér reserveløkken som en simpel try/except, der går gennem kæden. Fang midlertidige fejl (timeouts, 500-fejl, 503-fejl), og gå videre til den næste udbyder. Fang ikke godkendelsesfejl (401) eller ugyldige anmodningsfejl (400) – det er programmeringsfejl, som skal vises med det samme i stedet for at udløse skift til en anden udbyder.

import openai
import os

TRANSIENT_ERRORS = (openai.APITimeoutError, openai.InternalServerError, openai.APIConnectionError)

async def call_with_fallback(messages: list, **kwargs) -> str:
    for provider in CASCADE:
        try:
            client = openai.AsyncOpenAI(
                api_key=os.environ[provider.api_key_env],
                base_url=provider.base_url
            )
            resp = await client.chat.completions.create(
                model=provider.model,
                messages=messages,
                timeout=10.0,
                **kwargs
            )
            return resp.choices[0].message.content
        except TRANSIENT_ERRORS as e:
            print(f'Provider {provider.name} failed: {e}, trying next...')
    raise RuntimeError('All providers failed')

Hvad er en kredsløbsafbryder?

En kredsløbsafbryder forhindrer, at en fejlramt tjeneste bombarderes med anmodninger under en driftsafbrydelse. Den er opkaldt efter elektriske kredsløbsafbrydere og har tre tilstande: Closed (anmodninger går normalt igennem), Open (anmodninger afvises med det samme) og Half-Open (én testanmodning får lov til at gå igennem for at kontrollere, om tjenesten er kommet sig). Det beskytter både den underliggende tjeneste og din egen applikation under hændelser.

# Circuit breaker state machine:
#
# CLOSED --> (failure_count >= threshold) --> OPEN
#    ^                                          |
#    |     (test_request succeeds)              | (timeout expires)
#    +------------ HALF_OPEN <-----------------+
#
# In OPEN state: immediately return fallback/error
# In HALF_OPEN: allow one request through to test recovery
# In CLOSED: normal operation, count failures

Implementering af en kredsløbsafbryder

Her er en minimal implementering af en kredsløbsafbryder. Spor antallet af fejl og tidspunktet, hvor kredsløbet blev åbnet. Når antallet af fejl overskrider tærsklen, skal du åbne kredsløbet. Efter en konfigurerbar nulstillingstimeout skal du tillade én prøbeanmodning. Hvis prøven lykkes, skal du lukke kredsløbet. Hvis den mislykkes, skal du holde kredsløbet åbent og nulstille timeouten.

import time
from enum import Enum

class State(Enum):
    CLOSED = 'closed'
    OPEN = 'open'
    HALF_OPEN = 'half_open'

class CircuitBreaker:
    def __init__(self, failure_threshold=5, reset_timeout=60):
        self.state = State.CLOSED
        self.failure_count = 0
        self.failure_threshold = failure_threshold
        self.reset_timeout = reset_timeout
        self.opened_at = None

    def record_success(self):
        self.failure_count = 0
        self.state = State.CLOSED

    def record_failure(self):
        self.failure_count += 1
        if self.failure_count >= self.failure_threshold:
            self.state = State.OPEN
            self.opened_at = time.time()

    def can_attempt(self) -> bool:
        if self.state == State.CLOSED:
            return True
        if self.state == State.OPEN:
            if time.time() - self.opened_at > self.reset_timeout:
                self.state = State.HALF_OPEN
                return True  # allow one probe
            return False
        return True  # HALF_OPEN: allow probe

Integration af kredsløbsafbrydere med udbydere

Vedligehold én kredsløbsafbryder pr. udbyder. Kontrollér, om udbyderens kredsløbsafbryder tillader forsøget, før du kalder udbyderen. Registrér succes eller fejl efter hvert kald. Når en udbyders kredsløb åbnes, springer reserveløkken naturligt udbyderen over og prøver den næste udbyder i kæden uden at vente på en timeout ved hvert enkelt kald.

breakers = {p.name: CircuitBreaker(failure_threshold=5, reset_timeout=60) for p in CASCADE}

async def call_with_circuit_breaker(messages: list) -> str:
    for provider in CASCADE:
        breaker = breakers[provider.name]
        if not breaker.can_attempt():
            continue  # skip this provider, circuit is open
        try:
            result = await call_provider(provider, messages)
            breaker.record_success()
            return result
        except TRANSIENT_ERRORS as e:
            breaker.record_failure()
            print(f'{provider.name} failed ({breaker.failure_count}/{breaker.failure_threshold})')
    raise RuntimeError('All providers exhausted')

Registrering af langsomme kald som fejl

En udbyder, der svarer efter 30 sekunder, er næsten lige så problematisk som en udbyder, der er helt nede, set fra brugeroplevelsens perspektiv. Konfigurér en kort timeout for hvert kald til udbyderen, og behandl timeout-undtagelser som fejl i kredsløbsafbryderen. En timeout på 10 sekunder betyder, at reserven aktiveres hurtigt nok til, at brugeren kun oplever en kort forsinkelse og ikke en fastfrosset skærm.

async def call_provider(provider: Provider, messages: list) -> str:
    client = openai.AsyncOpenAI(
        api_key=os.environ[provider.api_key_env],
        base_url=provider.base_url
    )
    try:
        resp = await asyncio.wait_for(
            client.chat.completions.create(model=provider.model, messages=messages),
            timeout=10.0  # fail fast, let circuit breaker count it
        )
        return resp.choices[0].message.content
    except asyncio.TimeoutError:
        raise openai.APITimeoutError('Provider timed out')

Dashboard for udbydernes tilstand

Eksponér et /health/providers-endepunkt, der viser den aktuelle tilstand for hver udbyders kredsløbsafbryder, herunder antal fejl, tilstand (lukket/åben/halvåben) og tid til nulstilling. Det gør det nemt at se, hvilke udbydere der er sunde under en hændelse, og hjælper dig med at afgøre, om du manuelt skal tvinge en nulstilling igennem eller vente på automatisk genoprettelse.

from fastapi import FastAPI

app = FastAPI()

@app.get('/health/providers')
def provider_health():
    return {
        name: {
            'state': cb.state.value,
            'failure_count': cb.failure_count,
            'seconds_until_reset': (
                max(0, cb.reset_timeout - (time.time() - cb.opened_at))
                if cb.state == State.OPEN else None
            )
        }
        for name, cb in breakers.items()
    }

Tilpasning af udbydernes output

Forskellige udbydere har forskellige svarformater, sikkerhedsfiltre og funktioner. Når du skifter fra GPT-4o til Claude, kan modellen afvise visse anmodninger, som GPT-4o ville besvare. Vedligehold udbyderspecifikke promptomslag, der tilpasser dine prompts til hver udbyders konventioner. Test hver reserveudbyder uafhængigt for at sikre, at den leverer acceptabelt output til din anvendelse.

def adapt_messages_for_provider(provider: Provider, messages: list) -> list:
    if provider.name == 'anthropic':
        # Claude prefers explicit task descriptions
        system = next((m['content'] for m in messages if m['role'] == 'system'), '')
        if 'JSON' not in system:
            messages = [{'role': 'system', 'content': system + ' Respond in JSON.'}] + [
                m for m in messages if m['role'] != 'system'
            ]
    return messages

Test af reserveadfærd

Skriv en test, der tvinger den primære udbyder til at svigte (ved at angive en ugyldig API-nøgle eller bruge en mock, der kaster fejl), og kontrollér, at reserven aktiveres og returnerer et gyldigt svar. Test også, at kredsløbsafbryderen åbnes korrekt efter det konfigurerede antal fejl, og at den genoprettes efter nulstillingstimeouten. Reservelogik, der aldrig testes, er upålidelig under en reel driftsafbrydelse.

import pytest
from unittest.mock import AsyncMock, patch

@pytest.mark.asyncio
async def test_fallback_on_primary_timeout():
    # Primary provider times out
    with patch('your_module.call_provider', side_effect=[
        openai.APITimeoutError('Timeout'),  # primary fails
        'Claude response'                   # fallback succeeds
    ]):
        result = await call_with_circuit_breaker([{'role': 'user', 'content': 'Hello'}])
    assert result == 'Claude response'

Overvejelser om omkostninger ved leverandørkaskader

Fallback-leverandører har ofte andre priser end din primære leverandør. Anthropic Claude kan koste mere eller mindre end OpenAI GPT-4o afhængigt af modelniveauet. Hold styr på, hvilken leverandør der håndterede hver anmodning, og beregn omkostningsfordelingen separat. Hvis fallback-løsningen konsekvent er dyrere, bør du undersøge, om den primære leverandør har for få ressourcer til rådighed, og om en opgradering til et højere rate-limit-niveau vil være mere omkostningseffektiv end hyppig brug af fallback.

# Approximate costs per 1M tokens (2026):
PROVIDER_COSTS = {
    'openai/gpt-4o':          {'input': 2.50, 'output': 10.00},
    'anthropic/claude-3.5-sonnet': {'input': 3.00, 'output': 15.00},
    'openai/gpt-4o-mini':     {'input': 0.15, 'output': 0.60},
    'local/llama-3.1-8b':     {'input': 0.00, 'output': 0.00},  # infra cost only
}

# If fallback adds $0.50/day and a Tier 2 upgrade costs $100/month:
# Tier 2 pays off if you use fallback > 200 requests/day

Hurtigt tjek

Test din forståelse af circuit breakers og fallback-leverandører.

Opsummering af lektionen

I denne lektion lærte du, at leverandørkaskader definerer en ordnet fallback-sekvens fra primære til sekundære LLM-leverandører, at circuit breakers forhindrer gentagne kald til en leverandør, der fejler, ved hurtigt at afbryde efter en tærskel for antal fejl, og at timeouts pr. leverandør sikrer, at langsomme kald hurtigt udløser fallback i stedet for at blokere brugerne. Næste gang fastsætter vi timeout-budgetter og implementerer gradvis forringelse.

Gratis at komme i gang

Lær Python med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
30
Lektioner
120

Ofte stillede spørgsmål

Er lektionen “Fallback-udbydere og circuit breakers” gratis?

Ja — hele teksten til “Fallback-udbydere og circuit breakers” kan læses gratis her på nettet. Hvis du vil øve dig interaktivt med en indbygget kodeeditor og en AI-vejleder døgnet rundt og få adgang til resten af AI Engineering Academy-kurset, skal du opgradere til CoddyKit PRO. AI Engineering Academy-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “Fallback-udbydere og circuit breakers”?

Opbyg en udbyderkaskade, der automatisk skifter fra OpenAI til Anthropic og derefter til en lokal model, når den primære udbyder er langsom eller utilgængelig, ved hjælp af circuit breaker-mønsteret. Du øver dig i AI Engineering Academy med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på AI Engineering Academy?

Der kræves ingen tidligere erfaring. AI Engineering Academy på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 3 af 4.

Hvor lang tid tager lektionen “Fallback-udbydere og circuit breakers”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne AI Engineering Academy-lektion?

Ja. Alle AI Engineering Academy-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. Måling af LLM-latenstid: TTFT og TPOT
  2. Load balancing og strategier med flere nøgler
  3. Fallback-udbydere og circuit breakers
  4. Timeoutbudgetter og kontrolleret forringelse
← Tilbage til AI Engineering Academy