Eine saubere FastAPI-Anwendung für LLM-basierte Chatbots, Workflows und Agents.
/app
├── __init__.py
├── main.py ← FastAPI App Initialisierung
├── /api ← FastAPI Routen
│ ├── __init__.py
│ └── chat.py ← Chat Completion Endpoints
├── /logic ← Agenten, Tools, KI-Logik
│ ├── __init__.py
│ └── agent_controller.py ← Plan-Reason-Respond Agent
├── /models ← Pydantic Models
│ ├── __init__.py
│ └── chat.py ← Chat-bezogene Models
├── /services ← Externe Service-Integrationen
│ ├── __init__.py
│ ├── memory_qdrant.py ← Qdrant Vector Database Service
│ └── /llms ← LLM Provider (neue Struktur)
│ ├── __init__.py
│ ├── base_llm_provider.py ← Abstrakte Basisklasse
│ ├── azure_openai_provider.py ← Azure OpenAI Provider
│ ├── claude_provider.py ← Anthropic Claude Provider
│ └── echo_provider.py ← Echo Provider (Demo)
└── /utils ← Hilfsfunktionen
├── __init__.py
└── logging.py ← Logging-Konfiguration
Der agent-mode implementiert ein fortgeschrittenes "plan – reason – respond"-Paradigma:
- 🎯 Plan: Bestimmt das Ziel basierend auf der Benutzereingabe
- 🧠 Reason: Generiert Chain-of-Thought-Reasoning
- 💬 Respond: Erstellt finale Antwort basierend auf Planung und Überlegung
agent-mode- Fortgeschrittener Agent mit plan-reason-respond Paradigmagpt4.1-chat- Azure OpenAI GPT-4 (falls konfiguriert)claude-4-sonnet- Anthropic Claude (falls konfiguriert)echo-model- Einfaches Echo-Modell zum Testen
conda create -n twinly python=3.13conda activate twinlypip install -r requirements.txt# Azure OpenAI (optional)
export AZURE_OPENAI_ENDPOINT="your_endpoint"
export AZURE_OPENAI_API_KEY="your_key"
export AZURE_OPENAI_API_VERSION="2024-02-15-preview"
export AZURE_OPENAI_DEPLOYMENT_NAME="gpt-4"
# Claude/Anthropic (optional)
export CLAUDE_API_KEY="your_claude_key"
# Logging Level
export LOG_LEVEL="INFO"# WICHTIG: Immer vom Projekt-Root-Verzeichnis ausführen!
cd /pfad/zu/twinly
conda activate twinly
uvicorn app.main:app --reload- Führe uvicorn NIEMALS aus dem
/appVerzeichnis aus - Das führt zu
ModuleNotFoundError: No module named 'app'
✅ Korrekte Vorgehensweise:
# 1. Zum Projekt-Root navigieren
cd /path/to/twinly
# 2. Conda Environment aktivieren
conda activate twinly
# 3. Server starten (WICHTIG: app.main:app als Modul-Pfad verwenden)
uvicorn app.main:app --reloadWarum dieser Ansatz?
- Die Anwendung verwendet absolute Imports (
from app.api.chat import ...) - Python muss das
appPackage vom Root-Verzeichnis aus finden können - Der Modul-Pfad
app.main:appzeigt auf die FastAPI-Instanz inapp/main.py
Entwicklung Workflow:
- Terminal 1: Server starten mit obigem Befehl
- Terminal 2: Tests ausführen, Dependencies installieren, etc.
- Code ändern: Auto-reload funktioniert automatisch
- API testen: http://127.0.0.1:8000 oder http://127.0.0.1:8000/docs
Debugging:
- Server läuft auf: http://127.0.0.1:8000
- API Dokumentation: http://127.0.0.1:8000/docs
- Health Check: http://127.0.0.1:8000/health
- Logs werden in der Konsole angezeigt
# Alle Tests
python -m pytest -v
# Nur Main-API Tests
python -m pytest test_main.py -v
# Nur Agent Tests
python -m pytest test_agent.py -vListe verfügbare Modelle auf
curl -X GET "http://127.0.0.1:8000/v1/models"Erstelle Chat Completions (OpenAI kompatibel)
curl -X POST "http://127.0.0.1:8000/v1/chat/completions" \
-H "Content-Type: application/json" \
-d '{
"model": "agent-mode",
"messages": [
{"role": "user", "content": "Wie lerne ich Python?"}
],
"stream": false
}'curl -X POST "http://127.0.0.1:8000/v1/chat/completions" \
-H "Content-Type: application/json" \
-d '{
"model": "echo-model",
"messages": [
{"role": "user", "content": "Test streaming"}
],
"stream": true
}'Hinweis: Streaming wird für agent-mode nicht unterstützt.
Health Check Endpoint
Informative Homepage mit API-Dokumentation
Automatische Swagger UI Dokumentation
Die Anwendung verwendet strukturiertes Logging:
from app.utils.logging import get_logger
logger = get_logger(__name__)
logger.info("Your log message")Log-Level kann über die LOG_LEVEL Environment-Variable gesteuert werden:
DEBUGINFO(Standard)WARNINGERRORCRITICAL
- Erstelle eine neue Datei in
app/logic/ - Implementiere deine Agent-Logik
- Registriere den Agent in
app/api/chat.py - Füge das Modell zur Models-Liste hinzu
- Erstelle eine neue Service-Klasse in
app/services/ - Implementiere die erforderlichen Methoden
- Integriere den Service in den entsprechenden Router
- Erstelle eine neue Provider-Klasse in
app/services/llms/die vonBaseLLMProvidererbt - Implementiere alle abstrakten Methoden (
is_available,generate_response,generate_streaming_response) - Erstelle eine globale Instanz am Ende der Provider-Datei
- Aktualisiere das
MODEL_MAPPINGinapp/api/chat.py - Füge das Modell zur Models-Liste im
/v1/modelsEndpoint hinzu - Schreibe Contract- und Mock-Tests für den Provider in
tests/
- Definiere Pydantic Models in
app/models/ - Verwende Type Hints für alle Funktionen
- Validiere Ein- und Ausgaben
- Async/Await für alle I/O-Operationen
- Strukturierte Logging für besseres Debugging
- Modulare Architektur für einfache Wartung
- OpenAI-kompatible API für nahtlose Integration
- Keine sensiblen Daten in Code committen
- Environment-Variablen für API-Schlüssel verwenden
- Input-Validation über Pydantic Models
- Error Handling mit detailliertem Logging
- Verwende nur
FastAPIunduvicorn - Verwende NICHT die lokale Python-Umgebung, sondern immer
conda activate twinly - Halte alles so minimal wie möglich
- Verwende keine unnötigen Dependencies
- Schreibe klaren, gut kommentierten Code
- API-Antworten sind immer im JSON-Format
- Schreibe Tests für alles vor der Implementierung neuer Features
- Verwende Type Hints für alle Funktions-Signaturen
- Verwende
pydanticModels für Request- und Response-Validation - Verwende
pytestfür Tests
- Keine komplexen Features wie Authentifizierung oder Datenbanken
- Keine externen Tools oder Frameworks außer den genannten
Alle LLM-Provider nutzen jetzt eine einheitliche Architektur basierend auf der abstrakten BaseLLMProvider-Klasse:
- Provider-Klasse erstellen: Erstelle eine neue Datei in
app/services/llms/my_provider.py - Von BaseLLMProvider erben: Implementiere alle abstrakten Methoden:
is_available() -> boolasync generate_response(req: ChatCompletionRequest) -> Dict[str, Any]async generate_streaming_response(req: ChatCompletionRequest) -> AsyncGenerator[str, None]
- Globale Instanz: Erstelle eine globale Instanz am Ende der Datei:
my_provider = MyProvider() - Export hinzufügen: Füge den Provider zu
app/services/llms/__init__.pyhinzu - Model Mapping: Aktualisiere
MODEL_MAPPINGinapp/api/chat.py - Models Endpoint: Füge das Modell zum
/v1/modelsEndpoint hinzu - Tests schreiben: Erstelle Contract- und Mock-Tests in
tests/ - Linting & Type Checks: Stelle sicher, dass
ruff,blackundmypybestehen - Dokumentation: Dokumentiere erforderliche Environment-Variablen
# app/services/llms/my_provider.py
from .base_llm_provider import BaseLLMProvider
class MyProvider(BaseLLMProvider):
def is_available(self) -> bool:
return True # Check API key, etc.
async def generate_response(self, req: ChatCompletionRequest):
# Implementation here
pass
async def generate_streaming_response(self, req: ChatCompletionRequest):
# Implementation here
yield "data: chunk\n\n"
# Global instance
my_provider = MyProvider()- Einheitliche API: Alle Provider implementieren dieselben Methoden
- Austauschbarkeit: Provider können einfach ausgetauscht werden
- Testbarkeit: Jeder Provider kann isoliert getestet werden
- Erweiterbarkeit: Neue Provider sind einfach hinzuzufügen
- Factory Pattern:
MODEL_MAPPINGermöglicht dynamische Provider-Auswahl