CLAUDE_PLATFORM Systemarchitektur

Serverbasierter KI-Analyse-Agent: REST-API nimmt natürlichsprachliche Anfragen entgegen, ein autonomer Agent plant und führt mehrstufige Web-Analysen aus und liefert strukturierte JSON-Reports.

Anthropic SDK Agentic Loop FastAPI + Redis RQ 11 Tool-Integrationen 12 Job-Typen Claude Sonnet / Opus / Haiku Prompt-Injection-Schutz

1 Makro-Architektur

Alle Systemkomponenten und ihre Verbindungen auf einen Blick.

flowchart TB subgraph CLIENT["🖥 Client-Layer"] direction LR DASH["Dashboard / Chat-UI"] N8N["n8n Automation"] CURL["API Client / curl"] end subgraph API["⚡ API-Layer · FastAPI Port 8000"] direction TB AUTH["Auth\nX-API-Key Validation"] VALID["Input-Validation\nPydantic-Schemas pro Job-Typ"] SSRF["SSRF-Schutz\nwebhook_url Validation"] RATE["Queue-Limit\nMAX_JOBS_IN_QUEUE"] end subgraph QUEUE["📦 Queue-Layer · Redis RQ"] direction LR QH["Queue HIGH\nfull_analysis, chat"] QN["Queue NORMAL\ncompetitor, seo, sentiment"] QL["Queue LOW\nreview, lead, applicant"] end subgraph WORKER["⚙️ Worker-Layer"] direction TB ORPHAN["Orphan-Recovery\nbeim Start"] CANCEL["Cancel-Check\nvor Ausführung"] WRUN["run_job()"] end subgraph AGENT["🤖 Agent-Layer · Anthropic SDK"] direction TB LOAD["load_prompt()\nTemplate + Platzhalter + Tool-Liste"] LOOP["Agentic Loop\nMAX_TURNS=30"] EXTRACT["JSON-Extraktion\nbalanced-brace parser"] BUDGET["Token-Budget\nMAX_TOKENS=500k"] end subgraph MODELS["🧠 Modell-Layer"] direction LR OPUS["Claude Opus 4.6\nfull_analysis"] SONNET["Claude Sonnet 4.6\ncompetitor, seo, chat, ..."] HAIKU["Claude Haiku 4.5\napplicant, review, lead"] end subgraph TOOLS["🔧 Tool-Layer · 11 Tools"] direction LR T1["web_search\nExa API · neural/keyword/news"] T2["fetch_url\nScrapling + httpx"] T3["browser_fetch\nCamoufox/Botasaurus/Zendriver"] T4["DataForSEO\nRank · Keywords · Volume"] T5["serper_search\nGoogle News"] T6["get_reviews\nTrustpilot · HolidayCheck"] T7["gau_urls\nWayback Machine · CommonCrawl"] T8["get_pagespeed\nGoogle PageSpeed API"] end subgraph STORAGE["💾 Storage-Layer"] direction LR REDIS_R["Redis\nJob-Meta + Results\nTTL 7 Tage"] DISK["Disk\nsrc/outputs/\noptional: SAVE_OUTPUTS=true"] end subgraph EXTERN["🌐 Externe APIs"] direction LR EXA["Exa.ai\nneurale Web-Suche"] DFORSEO["DataForSEO\nSEO-Metriken"] SERPER["Serper.dev\nGoogle News"] ANTHROPIC["api.anthropic.com\nClaude LLM"] PSAPI["Google PageSpeed\nCore Web Vitals"] end CLIENT -->|"POST /api/jobs\nX-API-Key"| API API -->|"validated job"| QUEUE API -->|"job_id + status: queued"| CLIENT QUEUE --> WORKER WORKER --> AGENT AGENT <-->|"messages.create\ntool_use / tool_result"| MODELS AGENT --> TOOLS TOOLS --> EXTERN AGENT --> STORAGE CLIENT -->|"GET /api/jobs/{id}"| STORAGE style CLIENT fill:#1a2332,stroke:#264066 style API fill:#1a2b1a,stroke:#2d5c2d style QUEUE fill:#2b1a2b,stroke:#5c2d5c style WORKER fill:#2b2b1a,stroke:#5c5c2d style AGENT fill:#1a2b2b,stroke:#2d5c5c style MODELS fill:#2b1a1a,stroke:#5c2d2d style TOOLS fill:#1a1a2b,stroke:#2d2d5c style STORAGE fill:#2b2018,stroke:#5c4030 style EXTERN fill:#181818,stroke:#303030
Client-Layer
Trigger-Quellen
  • Dashboard Chat-Interface
  • n8n Automation-Flows
  • Direkte API-Calls (curl/Postman)
  • Webhook-Callbacks (Job fertig)
API-Layer
FastAPI · Port 8000
  • Job-Submission + Validation
  • Status-Abfrage + Job-Liste
  • Job-Abbruch (Soft-Cancel)
  • Health-Check (Redis-Ping)
Queue-Layer
Redis RQ · 3 Queues
  • HIGH: full_analysis, chat
  • NORMAL: competitor, seo, sentiment
  • LOW: review, lead, applicant
Agent-Layer
Anthropic SDK Loop
  • Prompt-Template laden + füllen
  • Tool-Calls ausführen (bis 30 Turns)
  • Token-Budget überwachen
  • JSON aus Response extrahieren

2 Job-Lifecycle — Vollständiger Datenpfad

Von der API-Anfrage bis zum fertigen JSON-Report: jeder Schritt, jede Entscheidung.

sequenceDiagram participant C as Client participant API as FastAPI
server.py participant R as Redis participant W as Worker
worker.py participant A as Agent
agent.py participant CL as Claude API
Sonnet/Opus/Haiku participant T as Tools
(11 Integrationen) C->>API: POST /api/jobs
{type, input, priority} Note over API: ① Auth: X-API-Key prüfen
② Input: Pydantic-Schema per Job-Typ
③ SSRF: webhook_url validieren
④ Rate-Limit: Queue < MAX_JOBS_IN_QUEUE API->>R: job:{id} speichern (TTL 7d)
Status: queued API->>R: Job in Queue einreihen
(high/normal/low) API-->>C: {job_id, status: queued,
estimated_duration_seconds} loop Worker pollt Queue (1s) W->>R: Queue leer? end W->>R: Cancel-Check: Status == cancelled? alt Job abgebrochen W-->>W: Skip (früher Abbruch) else Job läuft W->>R: Status → running W->>A: run_analysis(job_type, input) Note over A: load_prompt()
Template + Platzhalter + Tool-Liste
+ user_data Tags loop Agentic Loop (max 30 Turns) A->>CL: messages.create
{model, system, tools, messages} CL-->>A: stop_reason: tool_use
{tool_name, tool_input} Note over A: Token-Budget-Check
Abbruch wenn > 500k Tokens A->>T: execute_tool(name, input) T-->>A: Tool-Ergebnis (JSON/Text) A->>CL: tool_result zurück CL-->>A: stop_reason: end_turn
+ JSON-Output end Note over A: JSON-Extraktion
balanced-brace parser
(kein Regex!) A-->>W: {success, output, tokens_used} W->>R: Status → completed
result gespeichert (TTL 7d) opt SAVE_OUTPUTS=true W->>W: src/outputs/{domain}/{date}/{type}.json end opt webhook_url gesetzt W->>C: POST webhook mit Result end end C->>API: GET /api/jobs/{job_id} API->>R: job:{id} lesen API-->>C: {status: completed,
result: {...}, tokens_used: N}

3 Agentic Loop — Mikroebene

Was innerhalb eines einzigen Agent-Runs passiert: von der Prompt-Konstruktion bis zur JSON-Ausgabe.

flowchart TD START([Job empfangen\njob_type + input_data]) --> LOADP LOADP["load_prompt()\nTemplate aus prompts/{job_type}.md laden"] LOADP --> SUBST["Platzhalter ersetzen\n{domain} → 'example.com'\n{analysis_date} → heute"] SUBST --> PCHECK{"Ungefüllte\nPlatzhalter?"} PCHECK -->|"Ja → WARNING\nLog: missing keys"| APPEND PCHECK -->|"Nein → OK"| APPEND APPEND["Tool-Anweisungen anhängen\n+ Tool-Definitionen (11 Tools)"] APPEND --> UMSG["User-Message konstruieren\n-Tags für Injection-Schutz\nchat: natürliche Nachricht direkt\nothers: Parameter-Block"] UMSG --> T1[Turn 1: messages.create\nSystem-Prompt + User-Message] T1 --> RESP{stop_reason?} RESP -->|"tool_use"| TCHECK{"Token-Budget\n> 500k?"} TCHECK -->|"Ja → ABORT"| FAIL2([Fehler:\nToken-Budget\nüberschritten]) TCHECK -->|"Nein → OK"| EXEC["execute_tool(name, input)\nDispatch zu: web_search /\nfetch_url / browser_fetch /\nget_domain_rank / ..."] EXEC --> TEXTERN["Externer API-Call\nExa · DataForSEO ·\nScrapling · Serper · ..."] TEXTERN --> TRES["Tool-Result\nan Claude zurück"] TRES --> TNEXT["Turn N+1:\nmessages.create\n+ tool_results"] TNEXT --> MAXT{"Turn ≥ 30?"} MAXT -->|"Ja"| FAIL1([Fehler:\nMAX_TURNS\nerreicht]) MAXT -->|"Nein"| RESP RESP -->|"end_turn"| EXTRACT["JSON-Extraktion\nbalanced-brace parser\n_find_outermost_json()"] EXTRACT --> JCHECK{"Valides\nJSON?"} JCHECK -->|"Nein"| RAW["Fallback: raw_text\nin output speichern"] JCHECK -->|"Ja"| SAVE["Ergebnis in Redis\n{success, output,\ntokens_used, turns, model}"] RAW --> SAVE SAVE --> DISK{"SAVE_OUTPUTS\n= true?"} DISK -->|"Ja"| FILE["src/outputs/{slug}/{date}/{type}.json"] DISK -->|"Nein"| DONE([Fertig]) FILE --> DONE RESP -->|"unexpected"| FAIL3([Fehler:\nunbekannter\nstop_reason]) style START fill:#1a3a1a,stroke:#3fb950 style DONE fill:#1a3a1a,stroke:#3fb950 style FAIL1 fill:#3a1a1a,stroke:#f85149 style FAIL2 fill:#3a1a1a,stroke:#f85149 style FAIL3 fill:#3a1a1a,stroke:#f85149 style TEXTERN fill:#1a1a3a,stroke:#58a6ff
1

Prompt-Konstruktion

Das Prompt-Template (prompts/{job_type}.md) wird geladen, alle Platzhalter wie {domain} oder {region} werden durch echte Werte ersetzt. Ein Placeholder-Check warnt wenn Platzhalter unreplaced bleiben — das würde Claude mit dem Literal-String {domain} arbeiten lassen.

load_prompt("competitor_analysis", {"domain": "example.com", "keyword": "bio shampoo", "region": "AT"})
2

Tool-Auswahl durch Claude

Claude liest den System-Prompt mit den Schrittanweisungen und wählt autonom welches Tool als nächstes aufgerufen wird. Bei competitor_analysis: DataForSEO zuerst (Pflicht), dann web_search, dann fetch_url für Detailseiten.

→ Claude: tool_use { name: "get_domain_rank", input: { domain: "example.com", location_code: 2276 } }
3

Tool-Execution (execute_tool)

Der Worker führt das Tool aus — alle 11 Tools sind Python-Funktionen die echte externe APIs aufrufen. Das Ergebnis (bis zu 50k Zeichen) wird als tool_result in den nächsten Turn zurückgegeben.

execute_tool("get_domain_rank", {"domain": "example.com"}) → {"rank": 42, "traffic": 12000, ...}
4

Synthesis + JSON-Output

Nach allen Tool-Calls schreibt Claude stop_reason: end_turn und gibt das finale JSON-Objekt aus. Der balanced-brace parser extrahiert es zuverlässig — auch wenn Claude Text drum herum schreibt oder es in Code-Fences verpackt.

_find_outermost_json(text) → {"competitors": [...], "recommendations": [...], "data_sources": [...]}

4 Tool-Inventar — 11 Integrationen

Alle verfügbaren Tools, ihre API-Basis, Trigger-Bedingungen und Verfügbarkeit.

Tool API / Technologie Hauptparameter Wann Claude es nutzt Key-Pflicht
web_search Exa.ai Neural Search search_depth neural/keyword
category company/news/...
max_age_hours, livecrawl
include_domains
Fast immer — semantische Suche, aktuelle Daten, Branchenrecherche EXA_API_KEY
fetch_url Scrapling + httpx fallback url, extract_text Seiteninhalt lesen, Meta-Tags, Strukturdaten extrahieren kein Key
browser_fetch Camoufox / Botasaurus / Zendriver mode: camoufox/botasaurus/zendriver
wait_for_selector
JS-gerenderte Seiten (SPAs, Preiskalkulatoren, Cloudflare-geschützt) kein Key
get_domain_rank DataForSEO Labs API domain, location_code Domain-Authority, Traffic-Schätzung — PFLICHT als Schritt 0 in SEO-Analysen DATAFORSEO_LOGIN
DATAFORSEO_PASSWORD
get_ranked_keywords DataForSEO Labs API domain, location_code, limit Welche Keywords rankt ein Wettbewerber? Keyword-Lücken identifizieren. DATAFORSEO_*
get_keyword_volume DataForSEO Labs API keywords: list, location_code Monatliches Suchvolumen — füllt monthly_searches in Keyword-Gap-Reports DATAFORSEO_*
serper_search Serper.dev Google API query, gl (Land), type: news/web Google News Dual-Source — parallel zu web_search in press_monitoring SERPER_API_KEY
get_reviews Scrapling (Trustpilot / HolidayCheck / App Store) url, platform sentiment_analysis, review_analysis — Kundenstimmen ohne API-Key kein Key
get_pagespeed Google PageSpeed Insights API url, strategy: mobile/desktop Core Web Vitals — technische SEO-Analyse optional
PAGESPEED_API_KEY
gau_urls GAU Binary (Wayback Machine + CommonCrawl + OTX) domain, providers Alle historischen URLs einer Domain — gelöschte Seiten als Content-Lücken kein Key
(Binary nötig)
tavily_search Tavily API query, max_results Fallback-Suche / alternative Perspektive zu web_search TAVILY_API_KEY

5 Modell-Routing & Job-Typen

Jeder Job-Typ bekommt das kostenoptimale Modell. MODEL_OVERRIDE in .env überschreibt das Routing für Tests.

Claude Opus 4.6
Höchste Komplexität · Umfangreichste Analyse
full_analysis
~$3–8 / Run · Alle 5 Dimensionen + SWOT
Claude Sonnet 4.6
Standard · Multi-Tool · Web-Recherche
chat competitor_analysis seo_analysis sentiment_analysis content_audit press_monitoring price_monitoring custom
~$0.20–0.80 / Run
Claude Haiku 4.5
Einfach · Schnell · Kostengünstig
applicant_analysis review_analysis lead_qualification
~$0.05–0.15 / Run · Textanalyse ohne Web-Tool
flowchart LR INPUT["API-Anfrage\ntype + input"] --> ROUTER{MODEL_OVERRIDE\ngesetzt?} ROUTER -->|"Ja"| OVERRIDE["Direkt: z.B. glm-5\nfür Z.AI-Tests"] ROUTER -->|"Nein"| ROUTE{job_type?} ROUTE -->|"full_analysis"| OPUS["claude-opus-4-6\nMax. Qualität"] ROUTE -->|"chat / competitor\nseo / sentiment\ncontent / press\nprice / custom"| SONNET["claude-sonnet-4-6\nOptimale Balance"] ROUTE -->|"applicant\nreview / lead"| HAIKU["claude-haiku-4-5\nKostengünstig"] ROUTE -->|"unknown"| FALLBACK["claude-sonnet-4-6\nDefault-Fallback"] style OPUS fill:#3a2010,stroke:#ffa657 style SONNET fill:#10203a,stroke:#58a6ff style HAIKU fill:#103a10,stroke:#3fb950 style FALLBACK fill:#2a2a2a,stroke:#555

6 Chat-Modus — Kontext-intelligente Anfragen

Freie natürlichsprachliche Anfragen: der Agent interpretiert, plant, führt aus — ohne vorstrukturierten Input.

flowchart TD MSG["User-Nachricht\n'Was können wir heute\nfür costakreuzfahrten.de machen?'"] MSG --> INTERP["Interpretieren\nWelche Domain? Welches Thema?\nWelchen Zeitraum? Welchen Markt?"] INTERP --> PLAN["Planen\nWelche Analyse-Schritte?\nWelche Tools in welcher Reihenfolge?"] PLAN --> DEC{Erkannte Signale} DEC -->|"Domain erkannt"| D1["get_domain_rank\nget_ranked_keywords"] DEC -->|"'heute' / 'aktuell'"| D2["max_age_hours=48\nlivecrawl=True"] DEC -->|"'Wettbewerber'"| D3["web_search competitors\n+ DataForSEO Top-3"] DEC -->|"'News' / 'Presse'"| D4["web_search news\nsearch_type=news"] DEC -->|"'was tun' / 'nächste Schritte'"| D5["Vollanalyse\n→ Action-Plan"] D1 & D2 & D3 & D4 & D5 --> EXEC["Ausführen\nTool-Calls bis Datenlage vollständig"] EXEC --> SYNTH["Synthetisieren\nAlle Ergebnisse zu kohärentem Report"] SYNTH --> OUT["JSON-Output\n{intent, plan_executed, report (Markdown),\nkey_findings, next_actions, data}"] style MSG fill:#1a2b1a,stroke:#3fb950 style OUT fill:#1a2030,stroke:#58a6ff
Beispiel: API-Anfrage Chat-Modus
POST /api/jobs
{
  "type": "chat",
  "input": {
    "message": "Was sind die wichtigsten SEO-Chancen für costakreuzfahrten.de?",
    "context": "Kreuzfahrt-Branche, Markt DE/AT"
  },
  "priority": "high"
}

7 Security-Architektur

Alle Schutzmaßnahmen auf API-, Daten- und Infrastruktur-Ebene.

API-Authentifizierung

X-API-Key Header — jeder Request validiert. Ziel v2: JWT mit tenant_id für Multi-Tenant-Isolation.

Input-Validation

Pydantic-Schema pro Job-Typ. Fehlende Pflichtfelder → 422-Fehler bevor der Job startet. Verhindert Prompt-Bruch durch leere Platzhalter.

SSRF-Schutz

webhook_url wird gegen private IPs (10.x, 192.168.x, 172.16-31.x), localhost und HTTP geblockt. Nur https:// zu öffentlichen Adressen erlaubt.

Prompt-Injection-Schutz

User-kontrollierte Inputs (Domain-Inhalte, Nachrichten) werden in <user_data>-Tags gewrappt. Trennt Instruktionen klar von Nutzerdaten.

Token-Budget-Cap

MAX_TOKENS_PER_JOB=500000 (konfigurierbar). Verhindert unkontrollierte Kosten durch fehlerhafte Inputs oder Endlos-Tool-Chains.

Queue-Rate-Limit

MAX_JOBS_IN_QUEUE=50. Bei Überschreitung: HTTP 429 mit klarer Fehlermeldung. Verhindert Kosten-Explosion durch massenhafte Job-Einreichung.

CORS-Kontrolle

Explizite Allowlist via ALLOWED_ORIGINS Env-Var. Kein Wildcard * in Produktion.

Orphan-Recovery

Beim Worker-Start: alle Jobs mit Status running werden auf failed gesetzt. Verhindert ewiges Hängen nach Worker-Crash.

Soft-Cancel

Abgebrochene Jobs (Redis-Status: cancelled) werden vor Ausführung übersprungen. RQ-Queue wird nicht blockiert.

flowchart LR IN["Eingehende\nAPI-Anfrage"] --> A1{"X-API-Key\nvalid?"} A1 -->|Nein| R1["401 Unauthorized"] A1 -->|Ja| A2{"Input-Schema\nvalid?"} A2 -->|Nein| R2["422 Validation Error\n+ Feldname + Grund"] A2 -->|Ja| A3{"webhook_url\nSSRF-sicher?"} A3 -->|Nein| R3["422: SSRF-Risiko\nnur https:// public"] A3 -->|Ja| A4{"Queue-Size\n< MAX?"} A4 -->|Nein| R4["429 Too Many Requests\nWarte auf freie Slots"] A4 -->|Ja| OK["✅ Job akzeptiert\njob_id zurück"] style OK fill:#1a3a1a,stroke:#3fb950 style R1 fill:#3a1a1a,stroke:#f85149 style R2 fill:#3a1a1a,stroke:#f85149 style R3 fill:#3a1a1a,stroke:#f85149 style R4 fill:#3a2a1a,stroke:#ffa657

8 Deployment-Infrastruktur

Aktueller Stand (lokal) und Ziel-Deployment (Production).

flowchart TB subgraph LOCAL["Lokal (macOS) — Phase 1 ✅"] direction LR LC["curl / Test-Script"] --> LF["FastAPI\nlocalhost:8000"] LF --> LR["Redis\nlocalhost:6379"] LR --> LW["SimpleWorker\n(kein Fork — kein SIGSEGV)"] LW --> LA["agent.py\nAnthropics SDK direkt"] end subgraph PROD["Production — Phase 3 (geplant)"] direction TB subgraph DOCKER["Docker Compose"] PC["nginx\nReverse Proxy"] PA["Gunicorn + UvicornWorker\nAPI · mehrere Prozesse"] PR["Redis 7\nPersistenz: AOF"] PW["Worker\n(Standard Worker — Linux Fork OK)"] end PE["Egress-Proxy\nAllowlist: api.anthropic.com\n+ erlaubte Research-APIs"] PHETZ["Hetzner CX22\nUbuntu 22.04"] end subgraph V2["v2 — Phase 4 (Architektur-Ziel)"] direction LR V2A["ARQ async Worker\n20 parallele Jobs / Prozess"] V2B["PostgreSQL 16 + JSONB\nJob-Historie · keine 7d-TTL"] V2C["JWT + Tenant-Isolation\nSchema-per-Tenant + RLS"] V2D["slowapi Rate-Limiting\nTenant-aware Sliding Window"] end LOCAL -.->|"rsync src/ + .env"| PROD PROD -.->|"Migrations"| V2 style LOCAL fill:#1a2b1a,stroke:#3fb950 style PROD fill:#1a2030,stroke:#58a6ff style V2 fill:#2b1a2b,stroke:#d2a8ff