- Restored missing method implementations in ClassificationService (classify, extract_metrics) - Fixed Standardization Logic not being applied in metric cascade - Bumped version to v0.7.4 in config.py - Removed duplicate API endpoint in app.py - Updated MIGRATION_PLAN.md
15 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
companiesersetzt.
Tabelle contacts (Ansprechpartner)
id(PK)account_id(FK -> companies.id)gender,title,first_name,last_name,emailjob_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, 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.
* **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" -> "35200 m²" via the UI "Play" button.
* **[STABILITY] v0.7.3: Hardening Metric Parser & Regression Testing (Jan 23, 2026) [RESOLVED]**
* **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.
* **Fixes Implemented:**
* **Greilmeier Bug:** 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.
* **Wolfra Bug:** Logic to detect and remove trailing years from concatenated numbers (e.g., "802020" -> "80") was confirmed and added to tests.
* **Erding Bug:** Logic to ignore year-like prefixes (e.g., "2022 ... 200.000") was confirmed and added to tests.
* **Regression Test Suite (`/backend/tests/test_metric_parser.py`):**
* Created to lock in the fixes for the three critical bugs (Wolfra, Erding, Greilmeier) and ensure long-term stability of the number extraction logic.
* **Status:** The parser is now significantly more robust. All known major bugs are resolved and protected by automated tests. Core API endpoints (Import, Override, Create) were also restored during this process.
* **[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)
-
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.
-
Abhängigkeiten isolieren: Änderungen an zentralen
core_utils.pyführen schnell zu Import-Fehlern in anderen Modulen. Spezifische Logik (wie Metrik-Parsing) sollte lokal im Service bleiben. -
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. -
SDK-Versionen: Die Google-API ist in stetigem Wandel. Der explizite Rückgriff auf stabile Modelle wie
gemini-2.0-flashist im Legacy-SDK sicherer als die Nutzung von-latestTags.- Zweistufige Analyse:
- Strict Classification: Ordnet Firmen einer Notion-Branche zu (oder "Others").
- 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 dersignals-Tabelle. - Backend Fixes (Finalized Jan 20, 2026):
- Behebung diverser
NameErrorundImportErrorinbackend/lib/core_utils.py,backend/services/scraping.pyundbackend/services/classification.py. - Korrektur der
ClassificationServiceInitialisierung und des Aufrufs inbackend/app.pyzur Behebung desTypeError: missing 1 required positional argument: 'db'. - Wiederherstellung fehlender API-Endpunkte in
backend/app.py(z.B./api/companies/bulk) nach zu aggressivem Refactoring.
- Behebung diverser
- Zweistufige Analyse:
-
[UPGRADE] v0.6.1: Notion Sync Fixes
- Mapping: Korrektur des Mappings für
Metric TypeundScraper Search Term(Notion Select Fields). - Truncate-and-Reload: Sync-Skript löscht alte Daten vor dem Import (für
industries), behält aberrobotics_categoriesbei (Upsert), um FK-Constraints zu schützen. - Frontend: Korrektur der Einheiten-Anzeige ("Unit") im Settings-Dialog.
- Mapping: Korrektur des Mappings für
-
[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.
11.1 Lessons Learned (Retrospektive Jan 24, 2026)
- 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 einecompany_id, was zu einem422 Unprocessable EntityFehler führt. - Nginx
proxy_passTrailing Slash: Das Vorhandensein oder Fehlen eines/am Ende derproxy_pass-URL in Nginx ist kritisch. Für Dienste wie FastAPI, die mit einemroot_path(z.B./ce) laufen, darf kein Trailing Slash verwendet werden (proxy_pass http://company-explorer:8000;), damit derroot_pathin der an das Backend weitergeleiteten Anfrage erhalten bleibt. - Docker-Datenbank-Persistenz: Das Fehlen eines expliziten Volume-Mappings für die Datenbankdatei in
docker-compose.ymlfü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./database.db:/app/database.dbzu definieren. - Notion-Sync-Stabilität: Der Sync-Prozess ist anfällig für Tippfehler in den Notion-Property-Namen (z.B. "Stanardization" statt "Standardization"). Dies führt zu stillen Fehlern, bei denen Felder einfach
Nonesind. Bei fehlenden Daten muss dieses Skript zuerst überprüft werden. - Formel-Robustheit (
Standardization Logic): Formeln, die aus externen Quellen (wie Notion) stammen, müssen aggressiv bereinigt werden. Kommentare in Klammern (z.B.(Fläche pro Patient...)) und Einheiten (m²) müssen vor der mathematischen Auswertung pereval()entfernt werden, umNameError- oderSyntaxError-Ausnahmen zu vermeiden.
12. Deployment & Access Notes (Diskstation / Docker Compose)
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): Der
company-explorer-Service lauscht intern auf Port8000. Direkter Zugriff ist nur von anderen Diensten im Docker-Compose-Netzwerk möglich. -
Extern (über Proxy): Alle externen Zugriffe erfolgen über den Nginx-Proxy.
-
Lokales Netzwerk (Beispiel):
http://192.168.178.6:8090/ce/ -
Extern (über DuckDNS/HTTPS, Beispiel):
https://floke-ai.duckdns.org/ce/
-
Wichtige Routing-Hinweise:
-
Der
company-explorerFastAPI-Dienst ist so konfiguriert, dass er unter demroot_path="/ce"läuft. Alle API-Endpunkte (z.B./api/companies,/api/companies/export) sind daher unter/ce/api/...zu erreichen, wenn sie über den Proxy aufgerufen werden. -
Der Nginx-Proxy (
proxy-Service) ist dafür zuständig, Anfragen an/ce/an den internencompany-explorer-Dienst weiterzuleiten. Stellen Sie sicher, dass dienginx-proxy.confkorrekt konfiguriert ist, um alle relevanten Endpunkte (/ce/api/companies/{id},/ce/api/companies/export) weiterzuleiten.
Datenbank-Persistenz:
- Die SQLite-Datenbankdatei (
companies_v3_fixed_2.db) muss mittels Docker-Volume-Mapping vom Host-Dateisystem in dencompany-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.