From e70b0b89fc721193f52bba517762b542ba526bc2 Mon Sep 17 00:00:00 2001 From: Floke Date: Mon, 28 Jul 2025 09:48:41 +0000 Subject: [PATCH] Implementierung der kontextbasierten Brancheneinstufung MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - FEATURE: Brancheneinstufung 2.0 implementiert; nutzt nun die reichhaltigen Definitionen und Beispiele aus `config.py` für ein hochpräzises, kontextuelles Matching. - REFACTOR: `evaluate_branche_chatgpt` in `helpers.py` komplett neugeschrieben; gibt nun eine detaillierte Begründung für die Zuordnung zurück. - FEATURE: Neuer Batch-Modus `reclassify_branches` in `data_processor.py` hinzugefügt, um eine vollständige Neubewertung aller Accounts zu ermöglichen. --- helpers.py | 151 ++++++++++++++++++++++++++--------------------------- 1 file changed, 74 insertions(+), 77 deletions(-) diff --git a/helpers.py b/helpers.py index f6b647a2..a3d469d5 100644 --- a/helpers.py +++ b/helpers.py @@ -877,94 +877,91 @@ def summarize_wikipedia_article(full_text, company_name): # ============================================================================== @retry_on_failure -def evaluate_branche_chatgpt(crm_branche, beschreibung, wiki_branche, wiki_kategorien, website_summary, schema_data): +def evaluate_branche_chatgpt(company_name, website_summary, wiki_absatz): """ - Bewertet die Branche eines Unternehmens. Stellt die stabile System-Prompt- und Parsing-Logik - aus v1.7.9 wieder her und integriert den intelligenten Fallback. + Führt eine kontextbasierte Brancheneinstufung (v2.0) durch. + Das Unternehmensprofil wird mit dem vollständigen, definierten Branchenschema + abgeglichen, um die bestmögliche Zuordnung zu finden. """ logger = logging.getLogger(__name__) - - if not schema_data or not schema_data.get("allowed_branches"): - return {"branch": "FEHLER - SCHEMA FEHLT", "confidence": "N/A", "consistency": "error_schema_missing", "justification": "Fehler: Schema-Daten fehlen."} - allowed_branches = schema_data["allowed_branches"] - allowed_branches_lookup = {b.lower(): b for b in allowed_branches} + # 1. Baue das Unternehmensprofil zusammen + unternehmens_profil = [f"- Name: {company_name}"] + if website_summary and 'k.a.' not in website_summary.lower(): + unternehmens_profil.append(f"- Website-Zusammenfassung: {website_summary}") + if wiki_absatz and 'k.a.' not in wiki_absatz.lower(): + unternehmens_profil.append(f"- Wikipedia-Auszug: {wiki_absatz}") - system_prompt_content = ( - "Du bist ein Wirtschaftsanalyst, der die Branche eines Unternehmens bewertet.\n" - "Deine Aufgabe ist es, aus den untenstehenden Informationen die am besten passende Branche aus dem 'Ziel-Branchenschema' auszuwählen.\n" - "Bewerte Wikipedia-Daten und externe Branchenbeschreibungen höher als allgemeine Website-Texte.\n" - "Antworte NUR mit den folgenden drei Zeilen im exakten Format:\n" - "Branche: \n" - "Konfidenz: \n" - "Begruendung: " - ) + if len(unternehmens_profil) == 1: + logger.warning(f"Zu wenige Informationen für Brancheneinstufung von '{company_name}'. Breche ab.") + return { + "branch": "FEHLER (Mangelnde Daten)", + "confidence": "N/A", + "justification": "Keine Website- oder Wikipedia-Daten zur Analyse vorhanden." + } - user_prompt_parts = [ - "--- ZIEL-BRANCHENSCHEMA ---", - "\n".join(f"- {b}" for b in allowed_branches), - "\n--- UNTERNEHMENSDATEN ---" + # 2. Baue das Ziel-Branchenschema als Textblock auf + schema_text_parts = [] + for i, (branch, details) in enumerate(Config.BRANCH_GROUP_MAPPING.items()): + schema_text_parts.append(f"{i+1}. Branche: {branch}") + if details.get("definition"): + schema_text_parts.append(f" Definition: {details['definition']}") + if details.get("beispiele"): + schema_text_parts.append(f" Beispiele: {details['beispiele']}") + + ziel_branchenschema_text = "\n".join(schema_text_parts) + + # 3. Baue den Master-Prompt zusammen + prompt_parts = [ + "Du bist ein erfahrener Branchenanalyst bei einer führenden Unternehmensberatung. Deine Aufgabe ist es, ein Unternehmen präzise einer von 54 vordefinierten Branchenkategorien zuzuordnen. Nutze dafür ausschließlich die bereitgestellten Definitionen.", + "\n--- UNTERNEHMENS-PROFIL ---", + "\n".join(unternehmens_profil), + "\n--- ZIEL-BRANCHENSCHEMA (Deine einzig gültige Wissensbasis) ---", + ziel_branchenschema_text, + "\n--- DEINE AUFGABE (DENKPROZESS) ---", + "1. **Analysiere das Unternehmens-Profil:** Was ist die exakte, primäre Geschäftstätigkeit des Unternehmens?", + "2. **Vergleiche diese Tätigkeit mit ALLEN Definitionen** im Ziel-Branchenschema. Achte genau auf die Abgrenzungen.", + "3. **Identifiziere die Definition, die am besten passt.** Nutze die Beispielunternehmen zur Validierung deiner Wahl.", + "4. **Triff eine Entscheidung** und gib das Ergebnis im folgenden JSON-Format aus:", + ''' +{ + "Branche": "", + "Konfidenz": "", + "Begruendung": "" +} + ''' ] - if crm_branche and str(crm_branche).strip() and str(crm_branche).strip().lower() != "k.a.": user_prompt_parts.append(f"- CRM-Branche (Referenz): {str(crm_branche).strip()}") - if wiki_branche and str(wiki_branche).strip() and str(wiki_branche).strip().lower() != "k.a.": - if beschreibung and str(beschreibung).strip() and str(beschreibung).strip().lower() != "k.a.": user_prompt_parts.append(f"- Beschreibung (CRM): {str(beschreibung).strip()[:500]}...") - if website_summary and str(website_summary).strip() and str(website_summary).strip().lower() != "k.a." and not str(website_summary).strip().startswith("k.A. (Fehler"): user_prompt_parts.append(f"- Website-Zusammenfassung: {str(website_summary).strip()[:500]}...") - user_prompt_parts.append(f"- Wikipedia-Branche: {str(wiki_branche).strip()[:300]}...") - if wiki_kategorien and str(wiki_kategorien).strip() and str(wiki_kategorien).strip().lower() != "k.a.": user_prompt_parts.append(f"- Wikipedia-Kategorien: {str(wiki_kategorien).strip()[:500]}...") - else: - if website_summary and str(website_summary).strip() and str(website_summary).strip().lower() != "k.a." and not str(website_summary).strip().startswith("k.A. (Fehler"): user_prompt_parts.append(f"- Website-Zusammenfassung (als Hauptbeschreibung): {str(website_summary).strip()[:800]}...") - elif beschreibung and str(beschreibung).strip() and str(beschreibung).strip().lower() != "k.a.": user_prompt_parts.append(f"- Beschreibung (CRM, als Hauptbeschreibung): {str(beschreibung).strip()[:800]}...") + prompt = "\n".join(prompt_parts) - full_prompt = system_prompt_content + "\n\n" + "\n".join(user_prompt_parts) - + # 4. API-Aufruf try: - chat_response = call_openai_chat(full_prompt, temperature=0.1) - if not chat_response: raise APIError("Keine Antwort von OpenAI erhalten.") + response_str = call_openai_chat(prompt, temperature=0.0, model="gpt-4o", response_format_json=True) + if not response_str: + raise APIError("Keine Antwort von OpenAI erhalten.") + + response_json = json.loads(response_str) + + # Validierung des Ergebnisses + final_branch = response_json.get("Branche") + if final_branch not in Config.BRANCH_GROUP_MAPPING: + logger.warning(f"KI hat eine ungültige Branche zurückgegeben: '{final_branch}'. Markiere als Fehler.") + response_json["Branche"] = "FEHLER (Ungültige Antwort)" + response_json["Begruendung"] = f"Originalantwort: {final_branch}. " + response_json.get("Begruendung", "") + + return { + "branch": response_json.get("Branche", "FEHLER (Parsing)"), + "confidence": response_json.get("Konfidenz", "N/A"), + "justification": response_json.get("Begruendung", "Keine Begründung erhalten.") + } + except Exception as e: - return {"branch": "FEHLER API", "confidence": "N/A", "consistency": "error_api_failed", "justification": f"Fehler API: {str(e)[:100]}"} - - lines = chat_response.strip().split("\n") - result = {"confidence": "N/A", "justification": ""} - suggested_branch = "" - - for line in lines: - if line.lower().strip().startswith("branche:"): - suggested_branch = line.split(":", 1)[1].strip().strip('"\'') - break - if not suggested_branch and lines: - suggested_branch = lines[0].strip().split(":", 1)[-1].strip().strip('"\'') - - for line in lines: - if line.lower().strip().startswith("konfidenz:"): result["confidence"] = line.split(":", 1)[1].strip() - elif line.lower().strip().startswith("begruendung:"): result["justification"] = line.split(":", 1)[1].strip() - - if not suggested_branch: - return {"branch": "FEHLER PARSING", "confidence": "N/A", "consistency": "error_parsing", "justification": f"Antwort unklar: {chat_response[:100]}"} - - final_branch = None - suggested_branch_lower = suggested_branch.lower() - - if suggested_branch_lower in allowed_branches_lookup: - final_branch = allowed_branches_lookup[suggested_branch_lower] - else: - best_suggestion_match = next((val for key, val in allowed_branches_lookup.items() if suggested_branch_lower in key.lower()), None) - if best_suggestion_match: final_branch = best_suggestion_match - - if final_branch: - result["branch"] = final_branch - result["consistency"] = "ok" if final_branch.lower() == crm_branche.strip().lower() else "X" - else: - crm_short_branch_lower = crm_branche.strip().lower() - best_crm_fallback = next((val for key, val in allowed_branches_lookup.items() if crm_short_branch_lower and crm_short_branch_lower in key.lower()), None) - if best_crm_fallback: - result.update({"branch": best_crm_fallback, "consistency": "fallback_crm_substring", "justification": f"Fallback: KI-Vorschlag ungültig. CRM-Branche '{crm_branche}' passt zu '{best_crm_fallback}'.", "confidence": "N/A (Fallback)"}) - else: - result.update({"branch": "FEHLER - UNGUELTIGE ZUWEISUNG", "consistency": "fallback_invalid", "justification": f"Fehler: Weder KI ('{suggested_branch}') noch CRM ('{crm_branche}') passen.", "confidence": "N/A (Fehler)"}) - - logger.debug(f"Finale Branch-Evaluation: {result}") - return result - + logger.error(f"Endgültiger FEHLER beim OpenAI-Aufruf für Brancheneinstufung von {company_name}: {e}") + return { + "branch": "FEHLER (API)", + "confidence": "N/A", + "justification": f"Fehler bei der API-Kommunikation: {str(e)[:100]}" + } @retry_on_failure def verify_wiki_article_chatgpt(company_name, parent_name, website, wiki_title, wiki_summary):