[2f988f42] fix(company-explorer): Implement robust quantitative potential and atomic opener generation\n\n- Refactored ClassificationService for two-stage metric extraction (direct area and proxy).- Enhanced MetricParser for targeted value matching and robust number parsing.- Implemented persona-specific 'Atomic Opener' generation using segmented pains.- Fixed logging configuration and Pydantic response models.- Added dedicated debugging script and updated documentation (GEMINI.md, MIGRATION_PLAN.md).

This commit is contained in:
2026-02-21 08:01:07 +00:00
parent 26644f0e54
commit cf7cac2819
13 changed files with 666 additions and 534 deletions

View File

@@ -159,3 +159,98 @@ Anweisungen für den "Bautrupp" (Gemini CLI).
* **Pfad:** `/volume1/homes/Floke/python/brancheneinstufung/company-explorer`
* **DB:** `/app/companies_v3_fixed_2.db`
* **Sync:** `docker exec -it company-explorer python backend/scripts/sync_notion_to_ce_enhanced.py`
---
## 17. Analyse-Logik v3.0 (Feb 2026): Quantitative Potenzialanalyse & "Atomic Opener"
Nach mehreren instabilen Iterationen wurde die Kernlogik des `ClassificationService` finalisiert. Dieser Abschnitt dient als "Single Source of Truth", um zukünftige Fehlentwicklungen zu vermeiden.
### 17.1 Das Gesamtbild: Vom Content zur fertigen Analyse
Der Prozess ist streng sequenziell und baut aufeinander auf.
```
1. Branchen-Klassifizierung
|
-> Erkannte Branche: "Healthcare - Hospital"
|
2. Quantitative Potenzialanalyse (Zweistufige Kaskade)
|
--> 2a. Stufe 1: Direkte Flächensuche ("Fläche in m²")
| |
| --> Ergebnis: FEHLSCHLAG
|
--> 2b. Stufe 2: Branchenspezifische Proxy-Suche
|
--> Suchbegriff (aus Branche): "Anzahl Betten"
--> Formel (aus Branche): "wert * 100"
|
-> Ergebnis: 250 Betten -> 25000 m²
|
3. "Atomic Opener" Generierung (Zwei getrennte Personas)
|
--> 3a. Opener 1 (Primär): Fokus auf Infrastruktur-Entscheider
| |
| --> Produkt-Kontext: Nassreinigungsroboter (Primärprodukt)
| --> Pain-Kontext: Hygiene-Audits, Keimbelastung
|
--> 3b. Opener 2 (Sekundär): Fokus auf Operativen Entscheider
|
--> Produkt-Kontext: Serviceroboter (Sekundärprodukt, da "ops_focus_secondary" aktiv)
|
--> Pain-Kontext: Personalmangel, Entlastung der Pflegekräfte
|
4. FINALES COMMIT
```
### 17.2 Quantitative Potenzialanalyse im Detail
**Ziel:** Für jedes Unternehmen einen `standardized_metric_value` in `m²` zu ermitteln.
* **Stufe 1: Direkte Flächensuche (Direct Hit)**
* Das System sucht **immer** zuerst nach direkten Flächenangaben (Keywords: "Fläche", "m²", "Quadratmeter").
* Findet der `MetricParser` einen plausiblen Wert, wird dieser direkt in `standardized_metric_value` geschrieben und der Prozess ist für diese Stufe beendet. `calculated_metric_value` ist in diesem Fall identisch.
* **Stufe 2: Proxy-Metrik-Suche (Fallback)**
* **Nur wenn Stufe 1 fehlschlägt**, wird die branchenspezifische Logik aus den `industries`-Settings angewendet.
* **Suchbegriff:** `scraper_search_term` (z.B. "Anzahl Betten", "Anzahl Passagiere").
* **Extraktion:** Der `MetricParser` extrahiert den Rohwert (z.B. `250`). Dieser wird in `calculated_metric_value` gespeichert.
* **Standardisierung:** Die Formel aus `standardization_logic` (z.B. `wert * 100`) wird auf den Rohwert angewendet. Das Ergebnis wird in `standardized_metric_value` geschrieben.
### 17.3 "Atomic Opener" Generierung im Detail
**Ziel:** Zwei hoch-personalisierte, schlagkräftige Einleitungssätze (1-2 Sätze) zu generieren, die eine operative Herausforderung implizieren, ohne die Lösung zu nennen.
* **Zwei getrennte Kontexte:** Es werden zwei Sätze für zwei Personas generiert:
1. **`ai_opener` (Primär):** Zielt auf den **Infrastruktur-Entscheider** (z.B. Facility Manager, Technischer Leiter).
2. **`ai_opener_secondary` (Sekundär):** Zielt auf den **Operativen Entscheider** (z.B. Produktionsleiter, Pflegedienstleitung).
* **Persona-spezifische Produktauswahl:**
* Der primäre Opener (Infrastruktur) bezieht sich **immer** auf das `primary_category` der Branche.
* Der sekundäre Opener (Operativ) bezieht sich:
* Standardmäßig ebenfalls auf das `primary_category`.
* **Ausnahme:** Wenn in der Branche `ops_focus_secondary = True` gesetzt ist, bezieht er sich auf das `secondary_category`.
* **Der "1komma5°"-Prompt:**
* Die Generierung nutzt einen bewährten Prompt, der das Sprachmodell anweist, das Geschäftsmodell des Unternehmens zu analysieren und eine wertschätzende Beobachtung zu formulieren.
* **"Munition":** Der Prompt wird dynamisch mit den hoch-spezifischen, vordefinierten `pains` und `gains` aus der jeweiligen Branche angereichert.
* **Regel:** Das Produkt selbst wird **nicht** im Opener genannt. Der Satz fokussiert sich rein auf die Formulierung der Herausforderung. Die Auflösung erfolgt in den nachfolgenden, persona-spezifischen Textbausteinen.
### 17.4 Debugging & Lessons Learned (Feb 21, 2026)
Die Implementierung der v3.0-Logik war von mehreren hartnäckigen Problemen geprägt, deren Behebung wichtige Erkenntnisse für die zukünftige Entwicklung lieferte.
1. **"Phantom" `NameError` für `joinedload`:**
* **Problem:** Trotz korrekter `import`-Anweisung wurde ein `NameError` ausgelöst.
* **Lösung:** Ein erzwungener Neustart des Containers (`--force-recreate`) ist nach kritischen Code-Änderungen (besonders Imports) unerlässlich.
2. **Die "Krankenhaus-Schlacht" (Proxy-Metriken & Parser-Interferenz):**
* **Problem:** Bei Kliniken wurde oft der Wert "100" extrahiert (aus "100%ige Trägerschaft"), anstatt der korrekten Bettenanzahl. Zudem scheiterte die Standardisierung an Resten von Einheiten in der Formel (z.B. `wert * 100 (m²)`).
* **Lösung 1 (Targeted Matching):** Der `MetricParser` wurde so umgebaut, dass er einen "Hint" (erwarteter Wert vom LLM) priorisiert. Er sucht nun im Volltext exakt nach der Ziffernfolge, die das LLM identifiziert hat, und ignoriert alle anderen plausiblen Zahlen.
* **Lösung 2 (Aggressive Formula Cleaning):** Die `_parse_standardization_logic` entfernt nun konsequent alles in Klammern und alle Nicht-Rechenzeichen, bevor sie `safe_eval_math` aufruft. Dies verhindert `SyntaxError` durch Datenbank-Reste.
3. **Persona-spezifische Pains:**
* **Erkenntnis:** Damit die Opener wirklich zwischen Infrastruktur und Betrieb unterscheiden, müssen die `pains` in der Datenbank mit Markern wie `[Primary Product]` und `[Secondary Product]` versehen werden. Die Logik wurde entsprechend angepasst, um diese Segmente gezielt zu extrahieren.
Diese Punkte unterstreichen die Notwendigkeit von robusten Deployment-Prozessen, aggressiver Datenbereinigung und der Schaffung von dedizierten Test-Tools zur Isolierung komplexer Anwendungslogik.