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
- Housekeeping: Archivierung des Legacy-Codes (
_legacy_gsheets_system). - Setup: Init
company-explorer(Backend + Frontend Skeleton). - Foundation: DB-Schema + "List Matcher" (Deduplizierung ist Prio A für Operations).
- Enrichment: Implementierung des Scrapers + Signal Detector (Robotics).
- UI: React Interface für die Daten.
- 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
ClassificationServicelädt dieallowed_industriesnun 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_keywordsundindustry_grouperweitert. - Sync-Automation: Bereitstellung von
backend/scripts/sync_notion_industries.pyzur manuellen oder automatisierten Aktualisierung des lokalen Datenbestands.
- Notion SSoT: Umstellung der Branchenverwaltung (
-
[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_emptyab. - Ursache: Eine nicht committete Code-Änderung hatte das DB-Modell in
database.pyerweitert, 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.pyderDATABASE_URLauf 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.
- Problem: Die Anwendung stürzte beim Zugriff auf Firmendetails mit
- 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, dasscityundcountryaus 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_emptyFlag in der Datenbank, um Firmen ohne Wikipedia-Eintrag dauerhaft zu markieren. DerDiscoveryServiceüberspringt diese Einträge nun. - Frontend: Ein neuer Button im Inspector erlaubt das manuelle Setzen dieses Status.
- Backend: Implementierung einer
- Robuste Wikipedia-Suche: Die Namens-Normalisierungslogik aus dem Legacy-System wurde vollständig in den
DiscoveryServicereintegriert. 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".
- [FIX] Critical DB Schema Mismatch (Jan 15, 2026):
-
[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.
- Implementierung einer globalen Kontakt-Liste (
- 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).
- Contacts Management:
-
[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) inInspector.tsxdurch 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.
- White Screen Fix: Robuste Absicherung des Frontends gegen
-
[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.
- Neues UI: Die textbasierte Liste für "Letzte Sitzungen" wurde durch eine dedizierte, kartenbasierte UI (
-
[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.
- Dokumentation der Kern-Engine (
-
[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 Datenbanktabelleindustriesgeladen (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
- Definition: Branchen und Robotik-Kategorien werden in Notion gepflegt (Whale Thresholds, Keywords, Definitionen).
- Synchronisation: Das Skript
sync_notion_industries.pyzieht die Daten via API und führt einen Upsert in die lokale SQLite-Datenbank aus. - App-Nutzung: Das Web-Interface zeigt diese Daten schreibgeschützt an. Der
ClassificationServicenutzt 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:
- Sicherstellen, dass die Zieldatenbank vorhanden ist: Die
companies_v3_fixed_2.dbmuss imcompany-explorer-Verzeichnis liegen. - 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 - Container neu starten: Damit der Server das neue Schema erkennt.
docker-compose restart company-explorer - 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