# Migrations-Plan: Legacy GSheets -> Company Explorer (Robotics Edition v0.7.4) **Kontext:** Neuanfang für die Branche **Robotik & Facility Management**. **Ziel:** Ablösung von Google Sheets/CLI durch eine Web-App ("Company Explorer") mit SQLite-Backend. ## 1. Strategische Neuausrichtung | Bereich | Alt (Legacy) | Neu (Robotics Edition) | | :--- | :--- | :--- | | **Daten-Basis** | Google Sheets | **SQLite** (Lokal, performant, filterbar). | | **Ziel-Daten** | Allgemein / Kundenservice | **Quantifizierbares Potenzial** (z.B. 4500m² Fläche, 120 Betten). | | **Branchen** | KI-Vorschlag (Freitext) | **Strict Mode:** Mapping auf definierte Notion-Liste (z.B. "Hotellerie", "Automotive"). | | **Bewertung** | 0-100 Score (Vage) | **Data-Driven:** Rohwert (Scraper/Search) -> Standardisierung (Formel) -> Potenzial. | | **Analytics** | Techniker-ML-Modell | **Deaktiviert**. Fokus auf harte Fakten. | | **Operations** | D365 Sync (Broken) | **Excel-Import & Deduplizierung**. Fokus auf Matching externer Listen gegen Bestand. | ## 2. Architektur & Komponenten-Mapping Das System wird in `company-explorer/` neu aufgebaut. Wir lösen Abhängigkeiten zur Root `helpers.py` auf. ### A. Core Backend (`backend/`) | Komponente | Aufgabe & Neue Logik | Prio | | :--- | :--- | :--- | | **Database** | Ersetzt `GoogleSheetHandler`. Speichert Firmen & "Enrichment Blobs". | 1 | | **Importer** | Ersetzt `SyncManager`. Importiert Excel-Dumps (CRM) und Event-Listen. | 1 | | **Deduplicator** | Ersetzt `company_deduplicator.py`. **Kern-Feature:** Checkt Event-Listen gegen DB. Muss "intelligent" matchen (Name + Ort + Web). | 1 | | **Scraper (Base)** | Extrahiert Text von Websites. Basis für alle Analysen. | 1 | | **Classification Service** | **NEU (v0.7.0).** Zweistufige Logik:
1. Strict Industry Classification.
2. Metric Extraction Cascade (Web -> Wiki -> SerpAPI). | 1 | | **Marketing Engine** | Ersetzt `generate_marketing_text.py`. Nutzt neue `marketing_wissen_robotics.yaml`. | 3 | **Identifizierte Hauptdatei:** `company-explorer/backend/app.py` ### B. Frontend (`frontend/`) - React * **View 1: Der "Explorer":** DataGrid aller Firmen. Filterbar nach "Roboter-Potential" und Status. * **View 2: Der "Inspector":** Detailansicht einer Firma. Zeigt gefundene Signale ("Hat SPA Bereich"). Manuelle Korrektur-Möglichkeit. * **Identifizierte Komponente:** `company-explorer/frontend/src/components/Inspector.tsx` * **View 3: "List Matcher":** Upload einer Excel-Liste -> Anzeige von Duplikaten -> Button "Neue importieren". * **View 4: "Settings":** Konfiguration von Branchen, Rollen und Robotik-Logik. * **Frontend "Settings" Komponente:** `company-explorer/frontend/src/components/RoboticsSettings.tsx` ### C. Architekturmuster für die Client-Integration Um externen Diensten (wie der `lead-engine`) eine einfache und robuste Anbindung an den `company-explorer` zu ermöglichen, wurde ein standardisiertes Client-Connector-Muster implementiert. | Komponente | Aufgabe & Neue Logik | | :--- | :--- | | **`company_explorer_connector.py`** | **NEU:** Ein zentrales Python-Skript, das als "offizieller" Client-Wrapper für die API des Company Explorers dient. Es kapselt die Komplexität der asynchronen Enrichment-Prozesse. | | **`handle_company_workflow()`** | Die Kernfunktion des Connectors. Sie implementiert den vollständigen "Find-or-Create-and-Enrich"-Workflow:
1. **Prüfen:** Stellt fest, ob ein Unternehmen bereits existiert.
2. **Erstellen:** Legt das Unternehmen an, falls es neu ist.
3. **Anstoßen:** Startet den asynchronen `discover`-Prozess.
4. **Warten (Polling):** Überwacht den Status des Unternehmens, bis eine Website gefunden wurde.
5. **Analysieren:** Startet den asynchronen `analyze`-Prozess.
**Vorteil:** Bietet dem aufrufenden Dienst eine einfache, quasi-synchrone Schnittstelle und stellt sicher, dass die Prozessschritte in der korrekten Reihenfolge ausgeführt werden. | ## 3. Umgang mit Shared Code (`helpers.py` & Co.) Wir kapseln das neue Projekt vollständig ab ("Fork & Clean"). * **Quelle:** `helpers.py` (Root) * **Ziel:** `company-explorer/backend/lib/core_utils.py` * **Aktion:** Wir kopieren nur relevante Teile und ergänzen sie (z.B. `safe_eval_math`, `run_serp_search`). ## 4. Datenstruktur (SQLite Schema) ### Tabelle `companies` (Stammdaten & Analyse) * `id` (PK) * `name` (String) * `website` (String) * `crm_id` (String, nullable - Link zum D365) * `industry_crm` (String - Die "erlaubte" Branche aus Notion) * `city` (String) * `country` (String - Standard: "DE" oder aus Impressum) * `status` (Enum: NEW, IMPORTED, ENRICHED, QUALIFIED) * **NEU (v0.7.0):** * `calculated_metric_name` (String - z.B. "Anzahl Betten") * `calculated_metric_value` (Float - z.B. 180) * `calculated_metric_unit` (String - z.B. "Betten") * `standardized_metric_value` (Float - z.B. 4500) * `standardized_metric_unit` (String - z.B. "m²") * `metric_source` (String - "website", "wikipedia", "serpapi") ### Tabelle `signals` (Deprecated) * *Veraltet ab v0.7.0. Wird durch quantitative Metriken in `companies` ersetzt.* ### Tabelle `contacts` (Ansprechpartner) * `id` (PK) * `account_id` (FK -> companies.id) * `gender`, `title`, `first_name`, `last_name`, `email` * `job_title` (Visitenkarte) * `role` (Standardisierte Rolle: "Operativer Entscheider", etc.) * `status` (Marketing Status) ### Tabelle `industries` (Branchen-Fokus - Synced from Notion) * `id` (PK) * `notion_id` (String, Unique) * `name` (String - "Vertical" in Notion) * `description` (Text - "Definition" in Notion) * `metric_type` (String - "Metric Type") * `min_requirement` (Float - "Min. Requirement") * `whale_threshold` (Float - "Whale Threshold") * `proxy_factor` (Float - "Proxy Factor") * `scraper_search_term` (String - "Scraper Search Term") * `scraper_keywords` (Text - "Scraper Keywords") * `standardization_logic` (String - "Standardization Logic") ### Tabelle `job_role_mappings` (Rollen-Logik) * `id` (PK) * `pattern` (String - Regex für Jobtitles) * `role` (String - Zielrolle) ## 7. Historie & Fixes (Jan 2026) * **[CRITICAL] v0.7.4: Service Restoration & Logic Fix (Jan 24, 2026)** * **Summary:** Identified and resolved a critical issue where `ClassificationService` contained empty placeholder methods (`pass`), leading to "Others" classification and missing metrics. * **Fixes Implemented:** * **Service Restoration:** Completely re-implemented `classify_company_potential`, `_run_llm_classification_prompt`, and `_run_llm_metric_extraction_prompt` to restore AI functionality using `call_gemini_flash`. * **Standardization Logic:** Connected the `standardization_logic` formula parser (e.g., "Values * 100m²") into the metric extraction cascade. It now correctly computes `standardized_metric_value` (e.g., 352 beds -> 35,200 m²). * **Verification:** Confirmed end-to-end flow from "New Company" -> "Healthcare - Hospital" -> "352 Betten" -> "35.200 m²" via the UI "Play" button. * **[STABILITY] v0.7.3: Hardening Metric Parser & Regression Testing (Jan 23, 2026)** * **Summary:** A series of critical fixes were applied to the `MetricParser` to handle complex real-world scenarios, and a regression test suite was created to prevent future issues. * **Specific Bug Fixes:** * **Wolfra Bug ("802020"):** Logic to detect and remove trailing years from concatenated numbers (e.g., "Mitarbeiter: 802020" -> "80"). * **Erding Bug ("Year Prefix"):** Logic to ignore year-like prefixes appearing before the actual metric (e.g., "Seit 2022 ... 200.000 Besucher"). * **Greilmeier Bug ("Truncation"):** Removed aggressive sentence splitting on hyphens that was truncating text and causing the parser to miss numbers at the end of a phrase. * **Expected Value Cleaning:** The parser now aggressively strips units (like "m²") from the LLM's `expected_value` to ensure it can find the correct numeric target even if the source text contains multiple numbers. * **Regression Test Suite:** Created `/backend/tests/test_metric_parser.py` to lock in these fixes. * **[STABILITY] v0.7.2: Robust Metric Parsing (Jan 23, 2026)** * **Legacy Logic Restored:** Re-implemented the robust, regex-based number parsing logic (formerly in legacy helpers) as `MetricParser`. * **German Formats:** Correctly handles "1.000" (thousands) vs "1,5" (decimal) and mixed formats. * **Citation Cleaning:** Filters out Wikipedia citations like `[3]` and years in parentheses (e.g. "80 (2020)" -> 80). * **Hybrid Extraction:** The ClassificationService now asks the LLM for the *text segment* and parses the number deterministically, fixing "LLM Hallucinations" (e.g. "1.005" -> 1). * **[STABILITY] v0.7.1: AI Robustness & UI Fixes (Jan 21, 2026)** * **SDK Stabilität:** Umstellung auf `gemini-2.0-flash` im Legacy-SDK zur Behebung von `404 Not Found` Fehlern. * **API-Key Management:** Robustes Laden des Keys aus `/app/gemini_api_key.txt`. * **Classification Prompt:** Schärfung auf "Best-Fit"-Entscheidungen (kein vorzeitiges "Others"). * **Scraping:** Wechsel auf `BeautifulSoup` nach Problemen mit `trafilatura`. * **[MAJOR] v0.7.0: Quantitative Potential Analysis (Jan 20, 2026)** * **Zweistufige Analyse:** 1. **Strict Classification:** Ordnet Firmen einer Notion-Branche zu (oder "Others"). 2. **Metric Cascade:** Sucht gezielt nach der branchenspezifischen Metrik ("Scraper Search Term"). * **Fallback-Kaskade:** Website -> Wikipedia -> SerpAPI (Google Search). * **Standardisierung:** Berechnet vergleichbare Werte (z.B. m²) aus Rohdaten mit der `Standardization Logic`. * **Datenbank:** Erweiterung der `companies`-Tabelle um Metrik-Felder. * **[UPGRADE] v0.6.x: Notion Integration & UI Improvements** * **Notion SSoT:** Umstellung der Branchenverwaltung (`Industries`) auf Notion. * **Sync Automation:** `backend/scripts/sync_notion_industries.py`. * **Contacts Management:** Globale Kontaktliste, Bulk-Import, Marketing-Status. * **UI Overhaul:** Light/Dark Mode, Grid View, Responsive Design. ## 8. Eingesetzte Prompts (Account-Analyse v0.7.4) ### 8.1 Strict Industry Classification Ordnet das Unternehmen einer definierten Branche zu. ```python prompt = f""" Act as a strict B2B Industry Classifier. Company: {company_name} Context: {website_text[:3000]} Available Industries: {json.dumps(industry_definitions, indent=2)} Task: Select the ONE industry that best matches the company. If the company is a Hospital/Klinik, select 'Healthcare - Hospital'. If none match well, select 'Others'. Return ONLY the exact name of the industry. """ ``` ### 8.2 Metric Extraction Extrahiert den spezifischen Zahlenwert ("Scraper Search Term") und liefert JSON für den `MetricParser`. ```python prompt = f""" Extract the following metric for the company in industry '{industry_name}': Target Metric: "{search_term}" Source Text: {text_content[:6000]} Return a JSON object with: - "raw_value": The number found (e.g. 352 or 352.0). If text says "352 Betten", extract 352. If not found, null. - "raw_unit": The unit found (e.g. "Betten", "m²"). - "proof_text": A short quote from the text proving this value. JSON ONLY. """ ``` ## 9. Notion Integration (Single Source of Truth) Das System nutzt Notion als zentrales Steuerungselement für strategische Definitionen. ### 9.1 Datenfluss 1. **Definition:** Branchen und Robotik-Kategorien werden in Notion gepflegt (Whale Thresholds, Keywords, Definitionen). 2. **Synchronisation:** Das Skript `sync_notion_industries.py` zieht die Daten via API und führt einen Upsert in die lokale SQLite-Datenbank aus. 3. **App-Nutzung:** Das Web-Interface zeigt diese Daten schreibgeschützt an. Der `ClassificationService` nutzt sie als "System-Anweisung" für das LLM. ### 9.2 Technische Details * **Notion Token:** Muss in `/app/notion_token.txt` (Container-Pfad) hinterlegt sein. * **DB-Mapping:** Die Zuordnung erfolgt primär über die `notion_id`, sekundär über den Namen, um Dubletten bei der Migration zu vermeiden. ## 10. Database Migration Wenn die `industries`-Tabelle in einer bestehenden Datenbank aktualisiert werden muss (z.B. um neue Felder aus Notion zu unterstützen), darf die Datenbankdatei **nicht** gelöscht werden. Stattdessen muss das Migrations-Skript ausgeführt werden. **Prozess:** 1. **Sicherstellen, dass die Zieldatenbank vorhanden ist:** Die `companies_v3_fixed_2.db` muss im `company-explorer`-Verzeichnis liegen (bzw. via Volume gemountet sein). 2. **Migration ausführen:** Dieser Befehl fügt die fehlenden Spalten hinzu, ohne Daten zu löschen. ```bash docker exec -it company-explorer python3 backend/scripts/migrate_db.py ``` 3. **Container neu starten:** Damit der Server das neue Schema erkennt. ```bash docker-compose restart company-explorer ``` 4. **Notion-Sync ausführen:** Um die neuen Spalten mit Daten zu befüllen. ```bash docker exec -it company-explorer python3 backend/scripts/sync_notion_industries.py ``` ## 11. Lessons Learned (Retrospektive Jan 24, 2026) 1. **API-Routing-Reihenfolge (FastAPI):** Ein spezifischer Endpunkt (z.B. `/api/companies/export`) muss **vor** einem dynamischen Endpunkt (z.B. `/api/companies/{company_id}`) deklariert werden. Andernfalls interpretiert FastAPI "export" als eine `company_id`, was zu einem `422 Unprocessable Entity` Fehler führt. 2. **Nginx `proxy_pass` Trailing Slash:** Das Vorhandensein oder Fehlen eines `/` am Ende der `proxy_pass`-URL in Nginx ist kritisch. Für Dienste wie FastAPI, die mit einem `root_path` (z.B. `/ce`) laufen, darf **kein** Trailing Slash verwendet werden (`proxy_pass http://company-explorer:8000;`), damit der `root_path` in der an das Backend weitergeleiteten Anfrage erhalten bleibt. 3. **Docker-Datenbank-Persistenz:** Das Fehlen eines expliziten Volume-Mappings für die Datenbankdatei in `docker-compose.yml` führt dazu, dass der Container eine interne, ephemere Kopie der Datenbank verwendet. Alle Änderungen, die außerhalb des Containers an der "Host"-DB vorgenommen werden, sind für die Anwendung unsichtbar. Es ist zwingend erforderlich, ein Mapping wie `./companies_v3_fixed_2.db:/app/companies_v3_fixed_2.db` zu definieren. 4. **Code-Integrität & Platzhalter:** Es ist kritisch, bei Datei-Operationen sicherzustellen, dass keine Platzhalter (wie `pass` oder `# omitted`) in den produktiven Code gelangen. Eine "Zombie"-Datei, die äußerlich korrekt aussieht aber innerlich leer ist, kann schwer zu debuggende Logikfehler verursachen. 5. **Formel-Robustheit:** Formeln aus externen Quellen müssen vor der Auswertung bereinigt werden (Entfernung von Einheiten, Kommentaren), um Syntax-Fehler zu vermeiden. ## 12. Deployment & Access Notes ## 13. Task [2f388f42]: Report mistakes ### Aufgabenbeschreibung: When a user notices an error on an account, such as a wrong value he should have the option to mark this mistake (specify whihch value is wrong) and add a link to the source and a quote option as well as a comment why the user prefers the information above the information found by the llm in the first place. These corrections should be collected in a database for later review. The database will be displayed in the settings. The review shall happen by a sepcific checker-Process which should process the information and include the information into the research process to improve the search quality over time. ### Detaillierter Plan: **Phase 1: Backend & Datenbank** 1. **Neue Datenbank-Tabelle:** Ich werde eine neue Tabelle `reported_mistakes` in der SQLite-Datenbank erstellen. Sie wird Spalten für die `company_id` (FK), den `field_name` (String), den `wrong_value` (Text), den `corrected_value` (Text), die `source_url` (String), das `quote` (Text), den `user_comment` (Text) und einen `status` (Enum: `PENDING`, `APPROVED`, `REJECTED`, Standard: `PENDING`) sowie `created_at` (Timestamp) und `updated_at` (Timestamp) enthalten. 2. **API-Endpunkt zum Melden (POST):** Ich erstelle einen neuen `POST /api/companies/{company_id}/report-mistake` Endpunkt in `company-explorer/backend/app.py`, der die gemeldeten Fehler entgegennimmt und in der neuen Tabelle speichert. 3. **API-Endpunkt zum Anzeigen (GET):** Ich füge einen `GET /api/mistakes` Endpunkt in `company-explorer/backend/app.py` hinzu, der alle gemeldeten Fehler (oder gefilterte nach Status) für die Anzeige auf der Einstellungsseite abruft. 4. **API-Endpunkt zum Aktualisieren (PUT):** Ich füge einen `PUT /api/mistakes/{mistake_id}` Endpunkt in `company-explorer/backend/app.py` hinzu, um den Status eines gemeldeten Fehlers (z.B. `APPROVED`, `REJECTED`) zu aktualisieren. **Phase 2: Frontend (React)** 5. **Benutzeroberfläche zum Melden:** In der `Inspector.tsx` Komponente (`company-explorer/frontend/src/components/Inspector.tsx`) werde ich neben den wichtigsten Datenfeldern ein kleines "Fehler melden"-Icon hinzufügen. Ein Klick darauf öffnet ein Modalfenster/Formular, in das der Benutzer die Korrekturinformationen (Feldname, falscher Wert, korrigierter Wert, URL, Zitat, Kommentar) eingeben kann. 6. **Anzeige in den Einstellungen:** Im Einstellungsbereich, wahrscheinlich in `RoboticsSettings.tsx` (`company-explorer/frontend/src/components/RoboticsSettings.tsx`), wird ein neuer Tab "Gemeldete Fehler" oder eine neue Sektion hinzugefügt. Dort wird eine Tabelle alle Einträge aus der `reported_mistakes`-Tabelle anzeigen, mit Optionen zum Filtern nach Status und zur Interaktion (z.B. Genehmigen/Ablehnen). **Phase 3: Prüfprozess & Ausblick** 7. **Manueller Prüf-Workflow:** Die Tabelle in den Einstellungen wird um "Genehmigen"- und "Ablehnen"-Buttons erweitert. Ein Prüfer kann damit den Status jeder Meldung aktualisieren. Dies wird über den `PUT /api/mistakes/{mistake_id}` Endpunkt umgesetzt. 8. **Konzept für die Zukunft:** Die gesammelten und genehmigten Korrekturen bilden die Grundlage für eine spätere, automatisierte Verbesserung. Dies könnte beinhalten: * **LLM Fine-Tuning/Prompt-Verbesserung:** Genehmigte Korrekturen können als Beispiele für das Training oder die Kontextualisierung von LLM-Prompts verwendet werden, um die Genauigkeit der Datenextraktion zu verbessern. * **Scraping-Regel-Anpassung:** Systematische Fehler, die durch gemeldete Fehler identifiziert werden, könnten zur Anpassung von Scraping-Regeln oder Parser-Logik führen. * **Automatisierte Datenkorrektur:** Bei einer hohen Konfidenz könnten genehmigte Korrekturen direkt in die `companies`-Tabelle zurückgeschrieben werden. ### Wichtige Erkenntnisse zur Umsetzung: * **Backend-Hauptdatei:** `company-explorer/backend/app.py` * **Frontend "Inspector" Komponente:** `company-explorer/frontend/src/components/Inspector.tsx` * **Frontend "Settings" Komponente:** `company-explorer/frontend/src/components/RoboticsSettings.tsx` --- **Wichtiger Hinweis zum Deployment-Setup:** Dieses Projekt läuft in einer Docker-Compose-Umgebung, typischerweise auf einer Synology Diskstation. Der Zugriff auf die einzelnen Microservices erfolgt über einen zentralen Nginx-Reverse-Proxy (`proxy`-Service), der auf Port `8090` des Host-Systems lauscht. **Zugriffs-URLs für `company-explorer`:** * **Intern (im Docker-Netzwerk):** `http://company-explorer:8000` * **Extern (über Proxy):** `https://floke-ai.duckdns.org/ce/` (bzw. lokal `http://192.168.x.x:8090/ce/`) **Datenbank-Persistenz:** * Die SQLite-Datenbankdatei (`companies_v3_fixed_2.db`) muss mittels Docker-Volume-Mapping vom Host-Dateisystem in den `company-explorer`-Container gemountet werden (`./companies_v3_fixed_2.db:/app/companies_v3_fixed_2.db`). Dies stellt sicher, dass Datenänderungen persistent sind und nicht verloren gehen, wenn der Container neu gestartet oder neu erstellt wird.