Files
Brancheneinstufung2/MIGRATION_PLAN.md

20 KiB

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

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 Robotics-Signale (SPA-Bereich? Intralogistik? Werkschutz?).
Branchen KI-Vorschlag (Freitext) Strict Mode: Mapping auf feste CRM-Liste (z.B. "Hotellerie", "Maschinenbau").
Texterstellung Pain/Gain Matrix (Service) Pain/Gain Matrix (Robotics). "Übersetzung" des alten Wissens auf Roboter.
Analytics Techniker-ML-Modell Deaktiviert. Vorerst keine Relevanz.
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
Signal Detector NEU. Analysiert Website-Text auf Roboter-Potential.
Logik: Wenn Branche = Hotel & Keyword = "Wellness" -> Potential: Reinigungsroboter.
1
Classifier Brancheneinstufung. Strict Mode: Prüft gegen config/allowed_industries.json. 2
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".

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:

    • OpenAI/Gemini Wrapper (Retry Logic).
    • Text Cleaning (clean_text, normalize_string).
    • URL Normalization.
  • Quelle: Andere Gemini Apps (duckdns, gtm-architect, market-intel)

  • Aktion: Wir betrachten diese als Referenz. Nützliche Logik (z.B. die "Grit"-Prompts aus market-intel) wird explizit in die neuen Service-Module kopiert.

4. Datenstruktur (SQLite Schema)

Tabelle companies (Stammdaten)

  • id (PK)
  • name (String)
  • website (String)
  • crm_id (String, nullable - Link zum D365)
  • industry_crm (String - Die "erlaubte" Branche)
  • city (String)
  • country (String - Standard: "DE" oder aus Impressum)
  • status (Enum: NEW, IMPORTED, ENRICHED, QUALIFIED)

Tabelle signals (Roboter-Potential)

  • company_id (FK)
  • signal_type (z.B. "has_spa", "has_large_warehouse", "has_security_needs")
  • confidence (Float)
  • proof_text (Snippet von der Website)

Tabelle contacts (Ansprechpartner)

  • id (PK)
  • account_id (FK -> companies.id)
  • gender (Selection: "männlich", "weiblich")
  • title (Text, z.B. "Dr.")
  • first_name (Text)
  • last_name (Text)
  • email (Email)
  • job_title (Text - Visitenkarte)
  • language (Selection: "De", "En")
  • role (Selection: "Operativer Entscheider", "Infrastruktur-Verantwortlicher", "Wirtschaftlicher Entscheider", "Innovations-Treiber")
  • status (Selection: Siehe Prozess-Status)
  • is_primary (Boolean - Nur einer pro Account)

Tabelle industries (Branchen-Fokus)

  • id (PK)
  • name (String, Unique)
  • description (Text - Abgrenzung/Definition)
  • is_focus (Boolean)
  • primary_category_id (FK -> robotics_categories.id)
  • metric_type (String: Unit_Count, Area_in, Area_out - Art der Metrik zur Größenbestimmung)
  • min_requirement (Float, nullable - Minimaler Schwellenwert für Signal-Relevanz)
  • whale_threshold (Float, nullable - Schwellenwert, ab dem ein Account als "Whale" gilt)
  • proxy_factor (Float, nullable - Multiplikator für die Standardisierungslogik)
  • scraper_keywords (JSON-Array von Strings - Keywords für den Scraper zur Metrik-Erkennung)
  • standardization_logic (String - Formel zur Standardisierung der Metrik, z.B. "wert * 25m²")

Tabelle job_role_mappings (Rollen-Logik)

  • id (PK)
  • pattern (String - Regex oder Text-Pattern für Jobtitles)
  • role (String - Zielrolle im Verkaufsprozess)

Tabelle duplicates_log

  • Speichert Ergebnisse von Listen-Abgleichen ("Upload X enthielt 20 bekannte Firmen").

5. Phasenplan Umsetzung

  1. Housekeeping: Archivierung des Legacy-Codes (_legacy_gsheets_system).
  2. Setup: Init company-explorer (Backend + Frontend Skeleton).
  3. Foundation: DB-Schema + "List Matcher" (Deduplizierung ist Prio A für Operations).
  4. Enrichment: Implementierung des Scrapers + Signal Detector (Robotics).
  5. UI: React Interface für die Daten.
  6. CRM-Features: Contacts Management & Marketing Automation Status.

6. Spezifikation: Contacts & Marketing Status (v0.5.0)

(Hinzugefügt am 15.01.2026)

Konzept: Contacts stehen in 1:n Beziehung zu Accounts. Accounts können einen "Primary Contact" haben.

Datenfelder:

  • Geschlecht: Selection (männlich / weiblich)
  • Vorname: Text
  • Nachname: Text
  • E-Mail: Type: E-Mail
  • Jobtitle: Text (Titel auf der Visitenkarte)
  • Sprache: Selection (De / En)

Rollen (Funktion im Verkaufsprozess):

  • Operativer Entscheider
  • Infrastruktur-Verantwortlicher
  • Wirtschaftlicher Entscheider
  • Innovations-Treiber

Status (Marketing Automation):

  • Manuell:
    • Soft Denied (freundliche Absage)
    • Bounced (E-Mail invalide)
    • Redirect (ist nicht verantwortlich)
    • Interested (ist interessiert)
    • Hard denied (nicht mehr kontaktieren)
  • Automatisch:
    • Init (Kontakt soll in die Automation hineinlaufen)
    • 1st Step (Kontakt hat die erste Nachricht erhalten)
    • 2nd Step (Kontakt hat die zweite Nachricht erhalten)
    • Not replied (Kontakt hat die dritte Nachricht erhalten und nicht geantwortet)

Branchen-Fokus (Settings):

  • Name: Eindeutiger Name der Branche (CRM-Mapping).
  • Beschreibung: Textuelle Abgrenzung, was zu dieser Branche gehört.
  • Is Focus: Markiert Branchen, die prioritär bearbeitet werden.
  • Primäre Produktkategorie: Zuordnung einer Robotics-Kategorie (z.B. Hotel -> Cleaning).

Job-Rollen Mapping (Settings):

  • Pattern: Text-Muster (z.B. "Technischer Leiter", "CTO"), das in Jobtitles gesucht wird.
  • Zugeordnete Rolle: Die funktionale Interpretation (z.B. Operativer Entscheider).

7. Historie & Fixes (Jan 2026)

  • [UPGRADE] v0.6.1: Notion Single Source of Truth (Jan 20, 2026)

    • Notion SSoT: Umstellung der Branchenverwaltung (Industries) und Robotik-Kategorien auf Notion. Lokale Änderungen im Web-Interface sind für synchronisierte Felder deaktiviert, um die Datenintegrität zu wahren.
    • Dynamische Klassifizierung: Der ClassificationService lädt die allowed_industries nun direkt aus der Datenbank, die wiederum via Sync-Skript aus Notion befüllt wird.
    • Erweiterte Datenmodelle: Die Datenbank wurde um Felder wie whale_threshold, min_requirement, scraper_keywords und industry_group erweitert.
    • Sync-Automation: Bereitstellung von backend/scripts/sync_notion_industries.py zur manuellen oder automatisierten Aktualisierung des lokalen Datenbestands.
  • [UPGRADE] v0.5.1: Robustness, UI Fixes & Wikipedia Hardening

    • [FIX] Critical DB Schema Mismatch (Jan 15, 2026):
      • Problem: Die Anwendung stürzte beim Zugriff auf Firmendetails mit OperationalError: no such column: wiki_verified_empty ab.
      • Ursache: Eine nicht committete Code-Änderung hatte das DB-Modell in database.py erweitert, die physische Datenbank-Datei (companies_v3_final.db) war jedoch nicht migriert worden und dazu komplett leer/korrupt.
      • Lösung: Um die Anwendung schnell wieder lauffähig zu bekommen, wurde in config.py der DATABASE_URL auf einen neuen Dateinamen (companies_v3_fixed_2.db) geändert. Dies zwang die App, beim Start eine neue, leere Datenbank mit dem korrekten, aktuellen Schema zu erstellen. Auf eine Datenmigration aus der alten, leeren Datei wurde verzichtet.
    • Standort-Fix (4B AG): Die Backend-Logik wurde an entscheidenden Stellen (run_analysis_task, override_impressum_url) mit detailliertem Logging versehen und korrigiert, um sicherzustellen, dass city und country aus Impressums-Daten zuverlässig in die Haupt-Firmentabelle (companies) übernommen werden. Dies löst das Problem, dass Standorte im Inspector, aber nicht in der Übersicht angezeigt wurden.
    • Wikipedia "Verified Empty":
      • Backend: Implementierung einer wiki_verified_empty Flag in der Datenbank, um Firmen ohne Wikipedia-Eintrag dauerhaft zu markieren. Der DiscoveryService überspringt diese Einträge nun.
      • Frontend: Ein neuer Button im Inspector erlaubt das manuelle Setzen dieses Status.
    • Robuste Wikipedia-Suche: Die Namens-Normalisierungslogik aus dem Legacy-System wurde vollständig in den DiscoveryService reintegriert. Dies ermöglicht eine deutlich höhere Trefferquote bei Firmennamen mit unterschiedlichen Rechtsformen (z.B. "Therme Erding Service GmbH" -> "Therme Erding").
    • UI-Fix (Sort & View): Die Frontend-Tabellen (CompanyTable, ContactsTable) wurden grundlegend überarbeitet, um die zuvor fehlenden Sortier-Dropdowns und Grid/List-View-Toggles korrekt und zuverlässig anzuzeigen. Die Standard-Sortierung ist nun "Alphabetisch".
  • [UPGRADE] v0.5.0: Contacts, Settings & UI Overhaul

    • Contacts Management:
      • Implementierung einer globalen Kontakt-Liste (ContactsTable) mit Such- und Filterfunktionen.
      • Detail-Bearbeitung von Kontakten direkt im Inspector (Click-to-Edit).
      • Bulk-Import-Funktion für Kontakte (CSV-basiert) mit automatischer Firmen-Erstellung und Dubletten-Prüfung (E-Mail).
      • Erweiterte Felder: Akademischer Titel, differenzierte Rollen (Operativ, Strategisch etc.) und Marketing-Status.
    • UI Modernisierung:
      • Light Mode: Vollständige Unterstützung für Hell/Dunkel-Modus mit Toggle im Header.
      • Grid View: Umstellung der Firmen-Liste auf eine kartenbasierte Ansicht (analog zu Kontakten).
      • Responsive Design: Optimierung des Inspectors und der Navigation für mobile Endgeräte.
    • Erweiterte Settings:
      • Neue Konfigurations-Tabs für Branchen (Industries) und Job-Rollen.
      • CRUD-Operationen für Branchen (inkl. Auto-Increment bei Namensgleichheit).
    • Bugfixes:
      • Korrektur des API-Pfads für manuelle Impressum-Updates.
      • Stabilisierung der Datenbank-Logik bei Unique-Constraints.
      • Optimierung der Anzeige von "Unknown, DE" in der Firmenliste (wird nun ausgeblendet, solange keine Stadt bekannt ist).
  • [UPGRADE] v0.4.0: Export & Manual Impressum

    • JSON Export: Erweiterung der Detailansicht um einen "Export JSON"-Button, der alle Unternehmensdaten (inkl. Anreicherungen und Signale) herunterlädt.
    • Zeitstempel: Anzeige des Erstellungsdatums für jeden Anreicherungsdatensatz (Wikipedia, AI Dossier, Impressum) in der Detailansicht.
    • Manuelle Impressum-URL: Möglichkeit zur manuellen Eingabe einer Impressum-URL in der Detailansicht, um die Extraktion von Firmendaten zu erzwingen.
    • Frontend-Fix: Behebung eines Build-Fehlers (Unexpected token) in Inspector.tsx durch Entfernung eines duplizierten JSX-Blocks.
  • [UPGRADE] v2.6.2: Report Completeness & Edit Mode

    • Edit Hard Facts: Neue Funktion in Phase 1 ("Edit Raw Data") erlaubt die manuelle Korrektur der extrahierten technischen JSON-Daten.
    • Report-Update: Phase 5 Prompt wurde angepasst, um explizit die Ergebnisse aus Phase 2 (ICPs & Data Proxies) im finalen Report aufzuführen.
    • Backend-Fix: Korrektur eines Fehlers beim Speichern von JSON-Daten, der auftrat, wenn Datenbank-Inhalte als Strings vorlagen.
  • [UPGRADE] v2.6.1: Stability & UI Improvements

    • White Screen Fix: Robuste Absicherung des Frontends gegen undefined-Werte beim Laden älterer Sitzungen (optional chaining).
    • Session Browser: Komplettes Redesign der Sitzungsübersicht zu einer übersichtlichen Listenansicht mit Icons (Reinigung/Service/Transport/Security).
    • URL-Anzeige: Die Quell-URL wird nun als dedizierter Link angezeigt und das Projekt automatisch basierend auf dem erkannten Produktnamen umbenannt.
  • [UPGRADE] v2.6: Rich Session Browser

    • Neues UI: Die textbasierte Liste für "Letzte Sitzungen" wurde durch eine dedizierte, kartenbasierte UI (SessionBrowser.tsx) ersetzt.
    • Angereicherte Daten: Jede Sitzungskarte zeigt nun den Produktnamen, die Produktkategorie (mit Icon), eine Kurzbeschreibung und einen Thumbnail-Platzhalter an.
    • Backend-Anpassung: Die Datenbankabfrage (gtm_db_manager.py) wurde erweitert, um diese Metadaten direkt aus der JSON-Spalte zu extrahieren und an das Frontend zu liefern.
    • Verbesserte UX: Deutlich verbesserte Übersichtlichkeit und schnellere Identifikation von vergangenen Analysen.
  • [UPGRADE] v2.5: Hard Fact Extraction

    • Phase 1 Erweiterung: Implementierung eines sekundären Extraktions-Schritts für "Hard Facts" (Specs).
    • Strukturiertes Daten-Schema: Integration von templates/json_struktur_roboplanet.txt.
    • Normalisierung: Automatische Standardisierung von Einheiten (Minuten, cm, kg, m²/h).
    • Frontend Update: Neue UI-Komponente zur Anzeige der technischen Daten (Core Data, Layer, Extended Features).
    • Sidebar & Header: Update auf "ROBOPLANET v2.5".
  • [UPGRADE] v2.4:

    • Dokumentation der Kern-Engine (helpers.py) mit Dual SDK & Hybrid Image Generation.
    • Aktualisierung der Architektur-Übersicht und Komponenten-Beschreibungen.
    • Versionierung an den aktuellen Code-Stand (v2.4.0) angepasst.
  • [UPGRADE] v2.3:

    • Einführung der Session History (Datenbank-basiert).
    • Implementierung von Markdown-Cleaning (Stripping von Code-Blocks).
    • Prompt-Optimierung für tabellarische Markdown-Ausgaben in Phase 5.
    • Markdown-File Import Feature.

8. Eingesetzte Prompts (Account-Analyse)

Dieser Abschnitt dokumentiert die Prompts, die im Backend des Company Explorers zur automatisierten Analyse von Unternehmensdaten eingesetzt werden.

8.1 Impressum Extraktion (aus services/scraping.py)

Dient der Extraktion strukturierter Stammdaten aus dem rohen Text der Impressums-Seite.

Prompt:

prompt = f"""
Extract the official company details from this German 'Impressum' text.
Return JSON ONLY. Keys: 'legal_name', 'street', 'zip', 'city', 'country_code', 'email', 'phone', 'ceo_name', 'vat_id'.
'country_code' should be the two-letter ISO code (e.g., "DE", "CH", "AT").
If a field is missing, use null.

Text:
{raw_text}
"""

Variablen:

  • raw_text: Der bereinigte HTML-Text der gefundenen Impressums-URL (max. 10.000 Zeichen).

8.2 Robotics Potential Analyse (aus services/classification.py)

Der Kern-Prompt zur Bewertung des Automatisierungspotenzials. Er fasst das Geschäftsmodell zusammen, prüft auf physische Infrastruktur und bewertet spezifische Robotik-Anwendungsfälle.

Prompt:

prompt = f"""
You are a Senior B2B Market Analyst for 'Roboplanet', a specialized robotics distributor.
Your task is to analyze the target company based on their website text and create a concise **Dossier**.

--- TARGET COMPANY ---
Name: {company_name}
Website Content (Excerpt):
{website_text[:20000]} 

--- ALLOWED INDUSTRIES (STRICT) ---
You MUST assign the company to exactly ONE of these industries. If unsure, choose the closest match or "Sonstige".
{json.dumps(self.allowed_industries, ensure_ascii=False)}

--- ANALYSIS PART 1: BUSINESS MODEL ---
1. Identify the core products/services.
2. Summarize in 2-3 German sentences: What do they do and for whom? (Target: "business_model")

--- ANALYSIS PART 2: INFRASTRUCTURE & POTENTIAL (Chain of Thought) ---
1. **Infrastructure Scan:** Look for evidence of physical assets like *Factories, Large Warehouses, Production Lines, Campuses, Hospitals*.
2. **Provider vs. User Check:** 
   - Does the company USE this infrastructure (Potential Customer)?
   - Or do they SELL products for it (Competitor/Partner)? 
   - *Example:* "Cleaning" -> Do they sell soap (Provider) or do they have a 50,000sqm factory (User)?
3. **Evidence Extraction:** Extract 1-2 key sentences from the text proving this infrastructure. (Target: "infrastructure_evidence")

--- ANALYSIS PART 3: SCORING (0-100) ---
Based on the identified infrastructure, score the potential for these categories:

{category_guidance}

--- OUTPUT FORMAT (JSON ONLY) ---
{{
    "industry": "String (from list)",
    "business_model": "2-3 sentences summary (German)",
    "infrastructure_evidence": "1-2 key sentences proving physical assets (German)",
    "potentials": {{
        "cleaning": {{ "score": 0-100, "reason": "Reasoning based on infrastructure." }},\
        "transport": {{ "score": 0-100, "reason": "Reasoning based on logistics volume." }},\
        "security": {{ "score": 0-100, "reason": "Reasoning based on perimeter/assets." }},\
        "service": {{ "score": 0-100, "reason": "Reasoning based on guest interaction." }}\
    }}\
}}
"""

Variablen:

  • company_name: Offizieller Name des Zielunternehmens zur korrekten Identifikation im Dossier.
  • website_text: Der gescrapte Text der Hauptseite (max. 20.000 Zeichen), der als primäre Informationsquelle dient.
  • allowed_industries: Eine JSON-Liste der gültigen Branchen. Diese wird dynamisch aus der Datenbanktabelle industries geladen (synchronisiert aus Notion). Erzwingt ein sauberes CRM-Mapping.
  • category_guidance: Dynamisch generierte Definitionen und Scoring-Anweisungen für die Robotik-Kategorien. Ermöglicht die Anpassung der KI-Logik über Notion/Settings ohne Code-Änderung.

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 (v0.6.1 -> v0.6.2)

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.
  2. Migration ausführen: Dieser Befehl fügt die fehlenden Spalten hinzu, ohne Daten zu löschen.
    docker exec -it company-explorer python3 backend/scripts/migrate_db.py
    
  3. Container neu starten: Damit der Server das neue Schema erkennt.
    docker-compose restart company-explorer
    
  4. Notion-Sync ausführen: Um die neuen Spalten mit Daten zu befüllen.
    docker exec -it company-explorer python3 backend/scripts/sync_notion_industries.py