Files
Brancheneinstufung2/MIGRATION_PLAN.md
Floke e43e129771 feat: robust metric extraction with confidence score and proof snippets
- fixed Year-Prefix Bug in MetricParser
- added metric_confidence and metric_proof_text to database
- added Entity-Check and Annual-Priority to LLM prompt
- improved UI: added confidence traffic light and mouse-over proof tooltip
- restored missing API endpoints (create, bulk, wiki-override)
2026-01-23 21:16:07 +00:00

10 KiB

Migrations-Plan: Legacy GSheets -> Company Explorer (Robotics Edition v0.7.0)

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

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.
  • View 3: "List Matcher": Upload einer Excel-Liste -> Anzeige von Duplikaten -> Button "Neue importieren".
  • View 4: "Settings": Konfiguration von Branchen, Rollen und Robotik-Logik.

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)

*   **[STABILITY] v0.7.2: Robust Metric Parsing (Jan 23, 2026) [RESOLVED]**
    *   **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 & Year Cleaning:** Filters out Wikipedia citations like `[3]` and years in parentheses.
    *   **Wolfra Fix:** Specifically detects and fixes the "802020" bug by stripping concatenated years from the end of numeric strings.
    *   **Hybrid Extraction:** The ClassificationService now asks the LLM for the *text segment* and parses the number deterministically, fixing the LLM hallucinations.

*   **[SUCCESS] v0.6.4: Wolfra Metric Extraction Bug (Jan 23, 2026)**
    *   **Problem:** Mitarbeiterzahl für "Wolfra" wurde fälschlicherweise als "802020" anstatt "80" ausgelesen.
    *   **Gelöst durch:** Hybrid-Extraktion (LLM Segment + Python Cleanup) und Wiederherstellung der `MetricParser` Logik.
    *   **Status:** Problem **vollständig gelöst**. Alle Core-API Endpunkte (Import, Override, Create) wurden ebenfalls wiederhergestellt.

*   **[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 bei `1.5-flash-latest`.
    *   **API-Key Management:** Implementierung eines robusten Ladevorgangs für den Google API Key (Fallback von Environment-Variable auf lokale Datei `/app/gemini_api_key.txt`).
    *   **Classification Prompt:** Schärfung des Prompts auf "Best-Fit"-Entscheidungen, um zu konservative "Others"-Einstufungen bei klaren Kandidaten (z.B. Thermen) zu vermeiden.
    *   **Frontend Rendering:** Fix eines UI-Crashs im Inspector. Metriken werden jetzt auch angezeigt, wenn nur der standardisierte Wert (Fläche) vorhanden ist. Null-Safety für `.toLocaleString()` hinzugefügt.
    *   **Scraping:** Wiederherstellung der Stabilität durch Entfernung fehlerhafter `trafilatura` Abhängigkeiten; Nutzung von `BeautifulSoup` als robustem Standard.

*   **[MAJOR] v0.7.0: Quantitative Potential Analysis (Jan 20, 2026)**

... ...

11. Lessons Learned (Retrospektive Jan 21, 2026)

  1. KI statt Regex für Zahlen: Anstatt komplexe Python-Funktionen für deutsche Zahlenformate ("1,7 Mio.") zu schreiben, ist es stabiler, das LLM anzuweisen, den Wert direkt als Integer (1700000) zu liefern.

  2. Abhängigkeiten isolieren: Änderungen an zentralen core_utils.py führen schnell zu Import-Fehlern in anderen Modulen. Spezifische Logik (wie Metrik-Parsing) sollte lokal im Service bleiben.

  3. UI Null-Safety: Quantitative Daten sind oft unvollständig (z.B. Fläche vorhanden, aber Besucherzahl nicht). Das Frontend muss robust gegen null-Werte in den Metrik-Feldern sein, um den Render-Prozess nicht zu unterbrechen.

  4. SDK-Versionen: Die Google-API ist in stetigem Wandel. Der explizite Rückgriff auf stabile Modelle wie gemini-2.0-flash ist im Legacy-SDK sicherer als die Nutzung von -latest Tags.

    • 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, Deprecation der signals-Tabelle.
    • Backend Fixes (Finalized Jan 20, 2026):
      • Behebung diverser NameError und ImportError in backend/lib/core_utils.py, backend/services/scraping.py und backend/services/classification.py.
      • Korrektur der ClassificationService Initialisierung und des Aufrufs in backend/app.py zur Behebung des TypeError: missing 1 required positional argument: 'db'.
      • Wiederherstellung fehlender API-Endpunkte in backend/app.py (z.B. /api/companies/bulk) nach zu aggressivem Refactoring.
  • [UPGRADE] v0.6.1: Notion Sync Fixes

    • Mapping: Korrektur des Mappings für Metric Type und Scraper Search Term (Notion Select Fields).
    • Truncate-and-Reload: Sync-Skript löscht alte Daten vor dem Import (für industries), behält aber robotics_categories bei (Upsert), um FK-Constraints zu schützen.
    • Frontend: Korrektur der Einheiten-Anzeige ("Unit") im Settings-Dialog.
  • [UPGRADE] v0.6.0: Notion Single Source of Truth

    • Synchronisation von Branchen und Kategorien direkt aus Notion.
  • [UPGRADE] v0.5.1: Robustness

    • Logging, Wikipedia-Optimierung, UI-Fixes.

8. Eingesetzte Prompts (Account-Analyse v0.7.0)

8.1 Strict Industry Classification

Ordnet das Unternehmen einer definierten Branche zu.

prompt = r"""
Du bist ein präziser Branchen-Klassifizierer.
...
--- ZU VERWENDENDE BRANCHEN-DEFINITIONEN (STRIKT) ---
{industry_definitions_json}
...
Wähle EINE der folgenden Branchen... Wenn keine zutrifft, wähle "Others".
"""

8.2 Metric Extraction

Extrahiert den spezifischen Zahlenwert ("Scraper Search Term").

prompt = r"""
Analysiere den folgenden Text...
--- KONTEXT ---
Branche: {industry_name}
Gesuchter Wert: '{search_term}'
...
Gib NUR ein JSON-Objekt zurück:
'raw_value', 'raw_unit', 'area_value' (falls explizit m² genannt).
"""

9. Notion Integration

Das System nutzt Notion als SSoT für Industries und RoboticsCategories. Sync-Skript: backend/scripts/sync_notion_industries.py.

10. Database Migration

Bei Schema-Änderungen ohne Datenverlust: backend/scripts/migrate_db.py.