Zurück zur Startseite

Wir haben eine Routing-Schicht entwickelt, um unsere KI-Kosten zu senken. Sie hat das Produkt kaputt gemacht.

Ein Team baute eine benutzerdefinierte KI-Routing-Schicht, um API-Kosten zu senken, aber dies führte zu Latenz, Fehlern und unvorhersehbarem Verhalten, das die Benutzererfahrung verschlechterte und letztendlich das Produkt beeinträchtigte.

Vorlesen ist in diesem Browser nicht verfügbar
Wir haben eine Routing-Schicht entwickelt, um unsere KI-Kosten zu senken. Sie hat das Produkt kaputt gemacht.

Tags

Kurze Zusammenfassung

Ein Team baute eine benutzerdefinierte KI-Routing-Schicht, um API-Kosten zu senken, aber dies führte zu Latenz, Fehlern und unvorhersehbarem Verhalten, das die Benutzererfahrung verschlechterte und letztendlich das Produkt beeinträchtigte.

Wir haben eine Routing-Schicht gebaut, um unsere KI-Kosten zu senken. Sie hat das Produkt kaputt gemacht.

Kostenoptimierung bei KI ist ein zweischneidiges Schwert. Jedes Ingenieurteam, das ich kenne, stand unter dem gleichen Druck: API-Rechnungen senken, ohne die Qualität zu opfern. Wir dachten, wir hätten die perfekte Lösung gefunden – eine intelligente Routing-Schicht, die dynamisch das günstigste Modell für jede Anfrage auswählt. Im Test lief es hervorragend. Dann hat es das Produkt kaputt gemacht.

Dies ist die Geschichte, was schiefgelaufen ist, die technischen Details der Routing-Schicht, die wir gebaut haben, und die harten Lektionen, die wir über die versteckten Kosten der Optimierung gelernt haben.

Das Problem, das wir lösen wollten

Unser KI-gestütztes Produkt basierte auf mehreren großen Sprachmodellen (LLMs) von Anbietern wie OpenAI und Google. Wir nutzten GPT-4 für komplexe Überlegungen, GPT-3.5 für einfache Aufgaben und Googles PaLM 2 für spezifische Bereichsanfragen. Unsere monatliche Rechnung steuerte auf sechsstellige Beträge zu.

Die offensichtliche Lösung: Jede Anfrage an das günstigste Modell weiterleiten, das sie bewältigen kann. Wir wollten ein System, das jeden Prompt analysiert, seine Komplexität einschätzt und an das kosteneffizienteste Modell sendet. Wenn ein Nutzer fragte "Wie ist das Wetter?", leiteten wir an GPT-3.5-mini weiter. Bei "Schreiben Sie eine detaillierte rechtliche Analyse" ging es an GPT-4.

Anforderungen

Bevor wir bauten, definierten wir, was die Routing-Schicht leisten musste:

  • **Kostenoptimierung**: Durchschnittliche Kosten pro Anfrage um mindestens 40 % senken.
  • **Latenzkontrolle**: Antwortzeiten unter 2 Sekunden für 95 % der Anfragen halten.
  • **Qualitätserhaltung**: Nutzerzufriedenheitswerte innerhalb von 5 % des Ausgangswerts halten.
  • **Fallback-Logik**: Automatisch auf leistungsfähigere Modelle hochstufen, wenn das günstige versagt.
  • **Beobachtbarkeit**: Jede Routing-Entscheidung mit Modell, Kosten und Latenz protokollieren.

Schritt-für-Schritt-Installation

Wir bauten die Routing-Schicht als Python-Middleware-Dienst mit FastAPI und Redis. So haben wir sie eingerichtet.

1. Abhängigkeiten installieren

Erstellen Sie zunächst eine virtuelle Umgebung und installieren Sie die erforderlichen Pakete.

python -m venv routing-env
source routing-env/bin/activate
pip install fastapi uvicorn redis openai google-generativeai pydantic python-dotenv

2. Umgebungskonfiguration

Erstellen Sie eine `.env`-Datei mit Ihren API-Schlüsseln und Modellpreisen.

# .env
OPENAI_API_KEY=sk-your-key-here
GOOGLE_API_KEY=your-google-key
REDIS_URL=redis://localhost:6379

# Modellpreise pro 1K Tokens (USD)
GPT4_PRICE=0.03
GPT35_PRICE=0.0015
PALM2_PRICE=0.0005

3. Kern-Routing-Logik

Das Herzstück des Systems ist ein Komplexitätsschätzer, der Prompts auf einer Skala von 1–10 bewertet.

# router.py
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import openai
import google.generativeai as palm
import redis
import os
from dotenv import load_dotenv

load_dotenv()

app = FastAPI()
r = redis.from_url(os.getenv("REDIS_URL"))

class Request(BaseModel):
    prompt: str
    user_id: str

class Response(BaseModel):
    model: str
    content: str
    cost: float
    latency_ms: int

def estimate_complexity(prompt: str) -> int:
    """Bewertet die Prompt-Komplexität von 1–10 basierend auf Länge, Schlüsselwörtern und Struktur."""
    score = 1
    if len(prompt) > 200:
        score += 2
    if len(prompt) > 500:
        score += 2
    if any(word in prompt.lower() for word in ["legal", "medical", "code", "math"]):
        score += 3
    if "?" not in prompt:
        score += 1
    return min(score, 10)

def select_model(complexity: int) -> str:
    """Leitet an das günstigste Modell weiter, das die Komplexität bewältigen kann."""
    if complexity <= 3:
        return "palm-2"  # $0.0005
    elif complexity <= 6:
        return "gpt-3.5-turbo"  # $0.0015
    else:
        return "gpt-4"  # $0.03

@app.post("/route")
async def route_request(request: Request):
    complexity = estimate_complexity(request.prompt)
    model = select_model(complexity)
    
    # Entscheidung für Überwachung in Redis protokollieren
    r.rpush("routing_log", f"{model}:{complexity}:{request.user_id}")
    
    # Das ausgewählte Modell aufrufen
    import time
    start = time.time()
    
    try:
        if model == "gpt-4" or model == "gpt-3.5-turbo":
            openai.api_key = os.getenv("OPENAI_API_KEY")
            response = openai.ChatCompletion.create(
                model=model,
                messages=[{"role": "user", "content": request.prompt}]
            )
            content = response.choices[0].message.content
        elif model == "palm-2":
            palm.configure(api_key=os.getenv("GOOGLE_API_KEY"))
            response = palm.generate_text(prompt=request.prompt)
            content = response.result
        
        latency = int((time.time() - start) * 1000)
        cost = calculate_cost(model, len(request.prompt), len(content))
        
        return Response(model=model, content=content, cost=cost, latency_ms=latency)
    
    except Exception as e:
        # Bei Fehler auf GPT-4 zurückfallen
        return await fallback(request.prompt)

def calculate_cost(model: str, input_tokens: int, output_tokens: int) -> float:
    prices = {
        "gpt-4": 0.03,
        "gpt-3.5-turbo": 0.0015,
        "palm-2": 0.0005
    }
    return prices[model] * (input_tokens + output_tokens) / 1000

4. Dienst starten

Führen Sie die Routing-Schicht auf Port 8000 aus.

uvicorn router:app --host 0.0.0.0 --port 8000 --reload

Nutzungsbeispiele

Sobald der Dienst läuft, können Sie ihn mit curl testen.

Einfache Abfrage (Wird an PaLM 2 weitergeleitet)

curl -X POST http://localhost:8000/route \
  -H "Content-Type: application/json" \
  -d '{"prompt": "Was ist die Hauptstadt von Frankreich?", "user_id": "test1"}'

Erwartete Antwort:

{
  "model": "palm-2",
  "content": "Die Hauptstadt von Frankreich ist Paris.",
  "cost": 0.0005,
  "latency_ms": 340
}

Komplexe Abfrage (Wird an GPT-4 weitergeleitet)

curl -X POST http://localhost:8000/route \
  -H "Content-Type: application/json" \
  -d '{"prompt": "Schreiben Sie eine detaillierte rechtliche Analyse der Auswirkungen des Vierten Verfassungszusatzes auf die Handyortung ohne Durchsuchungsbefehl durch Strafverfolgungsbehörden, einschließlich relevanter Präzedenzfälle des Obersten Gerichtshofs und abweichender Meinungen.", "user_id": "test2"}'

Erwartete Antwort:

{
  "model": "gpt-4",
  "content": "...",
  "cost": 0.12,
  "latency_ms": 2100
}

Was das Produkt kaputt gemacht hat

Die Routing-Schicht funktionierte isoliert einwandfrei. Unsere Kosten pro Anfrage sanken in der ersten Woche um 52 %. Aber Nutzer begannen sich über inkonsistente Qualität, zufällige Fehler und seltsames Verhalten zu beschweren.

Die Zerbrechlichkeit der Komplexitätsschätzung

Unsere `estimate_complexity`-Funktion war zu simpel. Ein Nutzer, der fragte "Was ist der Unterschied zwischen einer Katze und einem Hund?", bekam eine Bewertung von 1 (einfach) und wurde an PaLM 2 weitergeleitet. PaLM 2 gab eine kurze, technisch korrekte Antwort. Aber der Nutzer erwartete eine detaillierte, gesprächige Antwort, wie GPT-4 sie gegeben hätte. Die Routing-Schicht optimierte auf Kosten, nicht auf Nutzererfahrung.

Schlimmer noch: Mehrdeutige Prompts wie "Hilf mir, diesen Code zu verstehen" erhielten aufgrund des Wortes "Code" eine hohe Bewertung und wurden unnötig an GPT-4 weitergeleitet. Wir verbrannten Geld für einfache Anfragen.

Der Fallback-Albtraum

Wenn PaLM 2 bei einer Anfrage versagte (was aufgrund von Ratenbegrenzungen in etwa 8 % der Fälle geschah), funktionierte unser Fallback auf GPT-4 – aber es verdoppelte die Latenz. Nutzer sahen Ladekreisel für über 4 Sekunden. Unser 95. Perzentil der Latenz stieg von 1,2 s auf 4,8 s.

Abweichendes Modellverhalten

Jedes Modell hat eine andere Persönlichkeit. GPT-4 ist ausführlich und vorsichtig. GPT-3.5 ist prägnant und direkt. PaLM 2 neigt zu mehr Kreativität. Nutzer bemerkten, dass derselbe Prompt je nach Routing-Entscheidung völlig unterschiedliche Töne annahm. Unser Produkt verlor seine konsistente Stimme.

Was wir gelernt haben

1. Kostenoptimierung muss die Nutzererfahrung einschließen

Wir maßen Kosten pro Anfrage, ignorierten aber Kosten pro zufriedenem Nutzer. Eine billige Antwort, die einen Nutzer vertreibt, ist unendlich teurer als eine Premium-Antwort, die ihn hält.

2. Routing benötigt Personalisierung pro Nutzer

Manche Nutzer bevorzugen kurze Antworten. Andere wollen tiefgehende Analysen. Unsere Routing-Schicht hätte Nutzerpräferenzen lernen und Schwellenwerte entsprechend anpassen sollen.

3. Fallbacks brauchen Zeitbudgets

Statt blind auf GPT-4 zurückzufallen, hätten wir ein maximales Latenzbudget festlegen sollen. Wenn das günstige Modell länger als 500 ms braucht, sofort eskalieren – nicht auf seinen Fehler warten.

4. Qualität überwachen, nicht nur Kosten

Wir verfolgten Kosten pro Anfrage und Latenz. Wir hätten verfolgen sollen:

  • Nutzerzufriedenheitswerte pro Modell
  • Aufgabenerfüllungsraten pro Modell
  • Sitzungsabbruchraten

Die verbesserte Routing-Schicht

Nach dem Vorfall bauten wir die Routing-Schicht mit diesen Erkenntnissen neu auf. Hier ist die verbesserte Version.

# improved_router.py
import asyncio
from typing import Optional

class AdaptiveRouter:
    def __init__(self, user_preferences: dict = None):
        self.user_prefs = user_preferences or {}
        self.latency_budget = 2000  # maximal 2 Sekunden insgesamt
    
    async def route_with_timeout(self, prompt: str, user_id: str):
        complexity = self.estimate_complexity(prompt)
        preferred_style = self.user_prefs.get(user_id, "balanced")
        
        # Schwellenwert basierend auf Nutzerpräferenz anpassen
        if preferred_style == "concise":
            threshold = 4
        elif preferred_style == "detailed":
            threshold = 6
        else:
            threshold = 5
        
        # Zuerst günstigstes Modell mit Timeout versuchen
        model = self.select_model(complexity, threshold)
        try:
            result = await asyncio.wait_for(
                self.call_model(model, prompt),
                timeout=self.latency_budget / 1000
            )
            return result
        except asyncio.TimeoutError:
            # Sofort eskalieren, wenn das günstige Modell langsam ist
            result = await self.call_model("gpt-4", prompt)
            return result

Fazit

Der Bau einer Routing-Schicht zur Senkung der KI-Kosten ist verlockend. Die Mathematik sieht auf dem Papier großartig aus: 70 % der Anfragen an günstige Modelle weiterleiten, 50 % bei den API-Rechnungen sparen. Aber die versteckten Kosten – inkonsistente Nutzererfahrung, erhöhte Latenz und Qualitätsverlust – können Ihr Produkt zerstören.

Unsere Erfahrung hat uns gelehrt, dass KI-Kostenoptimierung nicht nur darin besteht, das günstigste Modell auszuwählen. Es geht darum, Ihre Nutzer zu verstehen, die richtigen Metriken zu messen und adaptive Systeme zu bauen, die Kosten mit Qualität in Einklang bringen. Die beste Routing-Schicht ist für Nutzer unsichtbar. Unsere war alles andere als das.

Wenn Sie ein ähnliches System bauen, beginnen Sie mit einer einfachen Regel: Lassen Sie niemals zu, dass ein Nutzer sieht, dass Sie das Modell gewechselt haben. Wenn sie es bemerken, haben Sie das Produkt bereits kaputt gemacht.

Quellen

FAQ

Worum geht es in diesem Artikel?

Dieser Artikel behandelt „Wir haben eine Routing-Schicht entwickelt, um unsere KI-Kosten zu senken. Sie hat das Produkt kaputt gemacht.“ in der Kategorie KI-Tools. Ein Team baute eine benutzerdefinierte KI-Routing-Schicht, um API-Kosten zu senken, aber dies führte zu Latenz, Fehlern und unvorhersehbarem Verhalten, das die Benutzererfahrung verschlechterte und letztendlich das Produkt beeinträchtigte.

Für wen ist dieser Artikel nützlich?

Er ist nützlich für Leserinnen und Leser, die KI-Tools und KI-Anwendungen praktisch verstehen möchten.

Was ist der nächste Schritt?

Lesen Sie den Artikel, prüfen Sie die angegebenen Quellen und testen Sie passende Ideen in Ihrem Kontext.