Einstellungen

Sprache

Erstellen Sie einen KI-Chatbot mit nur einem API-Schlüssel: Von Null bis zur Produktion in 30 Minuten

L
LemonData
·26. Februar 2026·25 Aufrufe
#Chatbot#Tutorial#Python#FastAPI#Streaming
Erstellen Sie einen KI-Chatbot mit nur einem API-Schlüssel: Von Null bis zur Produktion in 30 Minuten

Erstellen Sie einen KI-Chatbot mit nur einem API-Schlüssel: Von Null bis zur Produktion in 30 Minuten

Dieses Tutorial zeigt, wie man ein produktionsbereites KI-Chatbot-Backend mit Streaming-Antworten, Gesprächshistorie, Modellwechsel und ordentlicher Fehlerbehandlung erstellt. Wir verwenden Python, FastAPI und das OpenAI SDK, das auf einen API-Aggregator zeigt, sodass Sie jedes Modell nutzen können.

Voraussetzungen

pip install fastapi uvicorn openai

Schritt 1: Basis-Chat-Endpunkt

from fastapi import FastAPI
from fastapi.responses import StreamingResponse
from openai import OpenAI
from pydantic import BaseModel

app = FastAPI()

client = OpenAI(
    api_key="sk-lemon-xxx",
    base_url="https://api.lemondata.cc/v1"
)

class ChatRequest(BaseModel):
    message: str
    model: str = "gpt-4.1-mini"
    conversation_id: str | None = None

@app.post("/chat")
async def chat(req: ChatRequest):
    response = client.chat.completions.create(
        model=req.model,
        messages=[{"role": "user", "content": req.message}]
    )
    return {"reply": response.choices[0].message.content}

Das funktioniert, bietet aber kein Streaming, keine Historie und keine Fehlerbehandlung. Das beheben wir jetzt.

Schritt 2: Streaming hinzufügen

Streaming sendet Tokens, sobald sie generiert werden, anstatt auf die vollständige Antwort zu warten. Nutzer sehen die Antwort in Echtzeit entstehen.

@app.post("/chat/stream")
async def chat_stream(req: ChatRequest):
    def generate():
        stream = client.chat.completions.create(
            model=req.model,
            messages=[{"role": "user", "content": req.message}],
            stream=True
        )
        for chunk in stream:
            delta = chunk.choices[0].delta
            if delta.content:
                yield f"data: {delta.content}\n\n"
        yield "data: [DONE]\n\n"

    return StreamingResponse(
        generate(),
        media_type="text/event-stream"
    )

Schritt 3: Gesprächshistorie

Speichern Sie die Gesprächshistorie im Speicher (in der Produktion durch Redis oder eine Datenbank ersetzen).

from collections import defaultdict
import uuid

conversations: dict[str, list] = defaultdict(list)

SYSTEM_PROMPT = "Du bist ein hilfreicher Assistent. Sei prägnant und direkt."

@app.post("/chat/stream")
async def chat_stream(req: ChatRequest):
    conv_id = req.conversation_id or str(uuid.uuid4())

    # Gesprächshistorie aufbauen
    messages = [{"role": "system", "content": SYSTEM_PROMPT}]
    messages.extend(conversations[conv_id])
    messages.append({"role": "user", "content": req.message})

    # Benutzer-Nachricht speichern
    conversations[conv_id].append(
        {"role": "user", "content": req.message}
    )

    def generate():
        full_response = []
        stream = client.chat.completions.create(
            model=req.model,
            messages=messages,
            stream=True
        )
        for chunk in stream:
            delta = chunk.choices[0].delta
            if delta.content:
                full_response.append(delta.content)
                yield f"data: {delta.content}\n\n"

        # Assistenten-Antwort speichern
        conversations[conv_id].append(
            {"role": "assistant", "content": "".join(full_response)}
        )
        yield f"data: [DONE]\n\n"

    return StreamingResponse(
        generate(),
        media_type="text/event-stream",
        headers={"X-Conversation-ID": conv_id}
    )

Schritt 4: Fehlerbehandlung

Aufrufe der KI-API können aus verschiedenen Gründen fehlschlagen: Rate Limits, unzureichendes Guthaben, Modell nicht verfügbar. Behandeln Sie jeden Fall:

from openai import (
    APIError,
    RateLimitError,
    APIConnectionError
)

@app.post("/chat/stream")
async def chat_stream(req: ChatRequest):
    conv_id = req.conversation_id or str(uuid.uuid4())
    messages = build_messages(conv_id, req.message)

    def generate():
        try:
            full_response = []
            stream = client.chat.completions.create(
                model=req.model,
                messages=messages,
                stream=True
            )
            for chunk in stream:
                delta = chunk.choices[0].delta
                if delta.content:
                    full_response.append(delta.content)
                    yield f"data: {delta.content}\n\n"

            conversations[conv_id].append(
                {"role": "assistant", "content": "".join(full_response)}
            )

        except RateLimitError as e:
            yield f"data: [ERROR] Rate limit erreicht. Bitte warten Sie einen Moment.\n\n"
        except APIConnectionError:
            yield f"data: [ERROR] Verbindung fehlgeschlagen. Versuche es erneut...\n\n"
        except APIError as e:
            yield f"data: [ERROR] {e.message}\n\n"

        yield "data: [DONE]\n\n"

    return StreamingResponse(generate(), media_type="text/event-stream")

def build_messages(conv_id: str, user_msg: str) -> list:
    messages = [{"role": "system", "content": SYSTEM_PROMPT}]
    # Behalte die letzten 10 Runden, um die Kontextlänge zu verwalten
    history = conversations[conv_id][-20:]
    messages.extend(history)
    messages.append({"role": "user", "content": user_msg})
    conversations[conv_id].append({"role": "user", "content": user_msg})
    return messages

Schritt 5: Modellwechsel

Ermöglichen Sie den Nutzern, Modelle mitten im Gespräch zu wechseln. Verschiedene Modelle für unterschiedliche Anforderungen:

AVAILABLE_MODELS = {
    "fast": "gpt-4.1-mini",
    "smart": "claude-sonnet-4-6",
    "reasoning": "o3",
    "budget": "deepseek-chat",
    "creative": "claude-sonnet-4-6",
}

@app.get("/models")
async def list_models():
    return {"models": AVAILABLE_MODELS}

Das Frontend kann diese als Optionen darstellen. Da alle Modelle über den Aggregator das gleiche OpenAI-kompatible Format verwenden, ist der Wechsel einfach ein Ändern des model-Parameters.

Schritt 6: Verwaltung des Kontextfensters

Lange Gespräche überschreiten die Kontextgrenzen des Modells. Implementieren Sie ein gleitendes Fenster:

def trim_history(messages: list, max_tokens: int = 8000) -> list:
    """Behalte System-Prompt + aktuelle Nachrichten innerhalb des Token-Budgets."""
    # Grobe Schätzung: 1 Token ≈ 4 Zeichen
    system = messages[0]  # System-Prompt immer behalten
    history = messages[1:]

    total_chars = len(system["content"])
    trimmed = []

    for msg in reversed(history):
        msg_chars = len(msg["content"])
        if total_chars + msg_chars > max_tokens * 4:
            break
        trimmed.insert(0, msg)
        total_chars += msg_chars

    return [system] + trimmed

Komplette Anwendung

# Starten mit: uvicorn main:app --reload --port 8000
# Testen: curl -N -X POST http://localhost:8000/chat/stream \
#   -H "Content-Type: application/json" \
#   -d '{"message": "Hallo!", "model": "gpt-4.1-mini"}'

Der gesamte Code umfasst weniger als 100 Zeilen. Von hier aus können Sie hinzufügen:

  • Authentifizierung (API-Schlüssel oder JWT)
  • Persistente Speicherung (PostgreSQL oder Redis für Gespräche)
  • Rate Limiting pro Nutzer
  • Nutzungsüberwachung und Abrechnung
  • WebSocket-Unterstützung für bidirektionales Streaming
  • Frontend (React, Vue oder reines JS mit EventSource)

Kostenschätzung

Für einen Chatbot, der 1.000 Gespräche/Tag mit durchschnittlich 5 Runden pro Gespräch verarbeitet:

Modell Tägliche Kosten Monatliche Kosten
GPT-4.1-mini ~2,40 $ ~72 $
GPT-4.1 ~12,00 $ ~360 $
Claude Sonnet 4.6 ~18,00 $ ~540 $
DeepSeek V3 ~1,68 $ ~50 $

Die Nutzung von GPT-4.1-mini für die meisten Gespräche und ein Upgrade auf Claude Sonnet 4.6 nur auf Nutzerwunsch hält die Kosten für die meisten Anwendungen unter 100 $/Monat.


Holen Sie sich Ihren API-Schlüssel: lemondata.cc bietet über einen Endpunkt 300+ Modelle. 1 $ Gratisguthaben zum Start.

Share: