- 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)
187 lines
10 KiB
Markdown
187 lines
10 KiB
Markdown
# 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: <br> 1. Strict Industry Classification. <br> 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.
|
|
|
|
```python
|
|
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").
|
|
|
|
```python
|
|
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`. |