# Migration Report: Competitor Analysis Agent

## Status: Jan 11, 2026 - ✅ ROBUSTNESS UPGRADE COMPLETE

Die App ist unter `/ca/` voll funktionsfähig und verfügt nun über eine "Grounded Truth" Engine (Scraping + SerpAPI) sowie eine skalierbare **Map-Reduce Architektur**.

### 🚨 Vollständige Chronik der Fehler & Lösungen

1.  **Problem: 404 auf `/ca/`**
    *   **Ursache:** Der Container startete aufgrund von Syntaxfehlern nicht, was Nginx mit einem 404 (später 502) quittierte.

2.  **Problem: `SyntaxError: unterminated string literal` (Runde 1)**
    *   **Ursache:** Verwendung von `f"""..."""` für komplexe Prompts.
    *   **Lösung:** Umstellung auf `.format()`. **Wichtig:** Docker-Volumes synchronisierten nicht zuverlässig, was zu "Phantom-Fehlern" führte. Ein `build --no-cache` war nötig.

3.  **Problem: `ImportError: cannot import name 'Schema'`**
    *   **Ursache:** Uralte SDK-Version `google-generativeai==0.3.0` in `requirements.txt`.
    *   **Lösung:** Umstellung auf Dictionaries, später komplettes SDK-Upgrade.

4.  **Problem: `404 models/gemini-1.5-pro ... for API version v1beta`**
    *   **Ursache:** Das alte SDK nutzte veraltete Endpunkte.
    *   **Lösung:** Migration auf das moderne **`google-genai`** Paket (v1.x) und Nutzung des neuen `genai.Client`.

5.  **Problem: `TypeError: unexpected keyword argument 'client_options'`**
    *   **Analyse:** Obwohl das SDK in `requirements.txt` aktualisiert wurde, installierte Docker auf der Diskstation hartnäckig eine alte Version.
    *   **Lösung:** Erzwingen der Version `google-genai>=1.2.0`.

6.  **Problem: Das "Grounding Upgrade" & Die Syntax-Hölle (Runde 2)**
    *   **Aufgabe:** Einbau von echtem Web-Scraping und SerpAPI zur Qualitätssteigerung.
    *   **Fehler:** `SyntaxError: f-string: expecting '}'` in komplexen Listen-Generatoren (z.B. `c_sum`).
    *   **Ursache:** Python 3.11 erlaubt keine geschachtelten Anführungszeichen in F-Strings, wenn diese Backslashes oder komplexe Ausdrücke enthalten.
    *   **Lösung:** **RADIKALER VERZICHT auf F-Strings** in allen kritischen Logik-Bereichen. Umstellung auf einfache Schleifen und `.format()`.

7.  **Problem: `unterminated string literal (detected at line 203)`**
    *   **Ursache:** Einfache Anführungszeichen `'` in Kombination mit `\n` wurden im Container-Kontext falsch interpretiert.
    *   **Lösung:** **ULTIMATIVE SYNTAX:** Verwendung von **Triple Raw Quotes (`r"""..."""`)** für jeden einzelnen String, der Variablen oder Sonderzeichen enthält.

8.  **Problem: Analyse stoppt nach 5 Konkurrenten (Token Limit / Lazy LLM)**
    *   **Symptom:** Bei 9 Konkurrenten wurden nur die ersten 5 analysiert, der Rest fehlte.
    *   **Ursache:** Der riesige Prompt ("Analysiere alle 9...") überforderte das Kontext-Fenster oder führte zu Timeouts.
    *   **Lösung:** Umstellung auf **Map-Reduce**: Jeder Konkurrent wird in einem eigenen parallelen Task (`asyncio.gather`) analysiert. Erhöhung von `max_output_tokens` auf 8192.

9.  **Problem: `NameResolutionError` im Container**
    *   **Symptom:** Scraping schlug fehl ("Name or service not known").
    *   **Ursache:** Docker-Container nutzten den (instabilen) Host-DNS.
    *   **Lösung:** Explizites Setzen von Google DNS (`8.8.8.8`, `8.8.4.4`) in `docker-compose.yml`.

10. **Problem: `422 Unprocessable Entity` in Schritt 6 & 8**
    *   **Ursache:** Diskrepanz zwischen Frontend-Request (z.B. sendet `industries`) und Backend-Pydantic-Modell (erwartet `target_industries`).
    *   **Lösung:** Backend-Modelle exakt an die Frontend-Payloads angepasst.

11. **Problem: Leere Matrizen in der Conclusion**
    *   **Ursache:** Das LLM füllte das `availability`-Array nicht korrekt oder erfand eigene Produktnamen als Zeilenbeschriftung.
    *   **Lösung:** Extrem strikter Prompt ("KEINE Produktnamen", "GENAU einen Eintrag pro Kategorie") und detailliertes JSON-Schema.

12. **Problem: Blinde KI in Schritt 8 (Referenzen)**
    *   **Symptom:** Die Referenzanalyse lieferte nur generische, oft erfundene Branchen, anstatt echter Kunden.
    *   **Ursache:** Der Prompt bat die KI, "nach Referenzen zu suchen", ohne ihr eine Datengrundlage zu geben. Die KI hat halluziniert.
    *   **Lösung:** Implementierung einer **"Grounded" Referenz-Suche**.
        1.  Ein neuer Scraper (`discover_and_scrape_references_page`) sucht gezielt nach "Referenzen", "Case Studies" oder "Kunden" auf der Website des Wettbewerbers.
        2.  Der Inhalt DIESER Seiten wird extrahiert.
        3.  Nur dieser "grounded" Text wird an das LLM zur Analyse und Extraktion übergeben.
    *   **Ergebnis:** Die Analyse basiert nun auf Fakten von der Webseite des Wettbewerbers, nicht auf dem allgemeinen Wissen der KI.

### 🛡️ Die finale "Grounded" Architektur

*   **Scraping:** Nutzt `requests` und `BeautifulSoup`, um nicht nur die Homepage, sondern auch Produkt- und Branchen-Unterseiten zu lesen.
*   **Grounded References:** Für die Referenzanalyse (Schritt 8) wird nun gezielt nach "Case Study" oder "Kunden"-Seiten gescraped, um die Extraktion auf echte Daten zu stützen und Halluzinationen zu vermeiden.
*   **Map-Reduce:** Statt eines Riesen-Prompts werden Konkurrenten parallel einzeln analysiert. Das skaliert linear.
*   **Logging:** Ein spezieller `log_debug` Helper schreibt direkt in `/app/Log_from_docker`, um Python-Logging-Probleme zu umgehen.

### Lessons Learned für die Ewigkeit

1.  **F-STRINGS SIND VERBOTEN** für Prompts und komplexe Listen-Operationen.
2.  **TRIPLE RAW QUOTES (`r"""..."""`)** sind der einzige sichere Weg für Strings in Docker-Umgebungen.
3.  **DUAL SDK STRATEGY:** Legacy SDK für Stabilität (`gemini-2.0-flash`), Modern SDK für Spezial-Features.
4.  **MAP-REDUCE:** Bei Listen > 3 Elementen niemals das LLM bitten, "alle auf einmal" zu bearbeiten. Immer zerlegen (Map) und aggregieren (Reduce).
5.  **SCHEMA FIRST:** Frontend (`types.ts`) und Backend (`Pydantic`) müssen *vorher* abgeglichen werden. `422` bedeutet fast immer Schema-Mismatch.
---
*Dokumentation aktualisiert am 11.01.2026 nach erfolgreicher Skalierung auf 9+ Konkurrenten.*
