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.
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-dotenv2. 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.00053. 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) / 10004. Dienst starten
Führen Sie die Routing-Schicht auf Port 8000 aus.
uvicorn router:app --host 0.0.0.0 --port 8000 --reloadNutzungsbeispiele
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 resultFazit
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.



