From acd57301afb861dc2a8e483cb3a8c96fe21e7734 Mon Sep 17 00:00:00 2001 From: Floke Date: Tue, 1 Jul 2025 14:49:04 +0000 Subject: [PATCH] STABLE - Objektorientiertes Schema-Handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - MAJOR REFACTOR: Globale Variablen für Branchenschema komplett entfernt. Das Schema wird nun in der `DataProcessor`-Instanz gehalten. - FIX: Kritischer Prompt-Fehler endgültig behoben. `evaluate_branche_chatgpt` erhält das Schema nun als explizites Argument, was Scope-Probleme beseitigt. - Das Projekt ist nun in einem stabilen, logisch konsistenten und lauffähigen Zustand für die Bestandsanreicherung. Die Grundlage für neue Features ist gelegt. --- helpers.py | 94 ++++++++++++++++++++---------------------------------- 1 file changed, 34 insertions(+), 60 deletions(-) diff --git a/helpers.py b/helpers.py index c0f3586d..cfd3f5fa 100644 --- a/helpers.py +++ b/helpers.py @@ -898,30 +898,23 @@ def summarize_batch_openai(tasks_data): # ============================================================================== @retry_on_failure -def evaluate_branche_chatgpt(crm_branche, beschreibung, wiki_branche, wiki_kategorien, website_summary): +def evaluate_branche_chatgpt(crm_branche, beschreibung, wiki_branche, wiki_kategorien, website_summary, schema_data): """ - Bewertet die Branche eines Unternehmens. - NUTZT DIE BEWÄHRTE PARSING-LOGIK AUS v1.7.9. + Bewertet die Branche eines Unternehmens. Erhält das Schema als Argument. + Verwendet die robuste Parsing-Logik aus v1.7.9. """ logger = logging.getLogger(__name__) - - # HINWEIS: Die globalen Variablen TARGET_SCHEMA_STRING, FOCUS_BRANCHES_PROMPT_PART - # und ALLOWED_TARGET_BRANCHES werden auf Modulebene importiert und von - # initialize_target_schema() modifiziert. Wir dürfen sie hier nicht erneut importieren. - - # Lazy Loading des Branchenschemas - if not ALLOWED_TARGET_BRANCHES: - logger.warning("Branchenschema nicht geladen. Versuche es jetzt zu laden...") - if not initialize_target_schema(): - logger.critical("FEHLER in evaluate_branche_chatgpt: Schema konnte nicht geladen werden.") - return {"branch": "FEHLER - SCHEMA FEHLT", "confidence": "N/A", "consistency": "error_schema_missing", "justification": "Fehler: Schema nicht geladen."} - - allowed_branches_lookup = {b.lower(): b for b in ALLOWED_TARGET_BRANCHES} - prompt_parts = [TARGET_SCHEMA_STRING, FOCUS_BRANCHES_PROMPT_PART] - prompt_parts.append("\nOrdne das Unternehmen anhand folgender Angaben exakt einer Branche des Ziel-Branchenschemas (Kurzformen) zu.") - prompt_parts.append("Gib zusätzlich eine Konfidenz für deine Branchenwahl an (Hoch, Mittel oder Niedrig).") - # --- Prompt-Zusammenstellung (unverändert) --- + if not schema_data or not schema_data.get("allowed_branches"): + logger.critical("FEHLER: Kein gültiges Schema-Datenobjekt an evaluate_branche_chatgpt übergeben.") + return {"branch": "FEHLER - SCHEMA FEHLT", "confidence": "N/A", "consistency": "error_schema_missing", "justification": "Fehler: Schema-Daten fehlen."} + + allowed_branches = schema_data["allowed_branches"] + schema_prompt = schema_data["schema_prompt_string"] + allowed_branches_lookup = {b.lower(): b for b in allowed_branches} + + prompt_parts = [schema_prompt] + # Rest der Prompt-Zusammenstellung if crm_branche and str(crm_branche).strip() and str(crm_branche).strip().lower() != "k.a.": 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.": prompt_parts.append(f"- Beschreibung (CRM): {str(beschreibung).strip()[:500]}...") @@ -929,11 +922,10 @@ def evaluate_branche_chatgpt(crm_branche, beschreibung, wiki_branche, wiki_kateg 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.": prompt_parts.append(f"- Wikipedia-Kategorien: {str(wiki_kategorien).strip()[:500]}...") else: - logger.debug("evaluate_branche_chatgpt: Keine validen Wiki-Daten, nutze Website-Zusammenfassung als primäre Beschreibung.") 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"): 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.": prompt_parts.append(f"- Beschreibung (CRM, als Hauptbeschreibung): {str(beschreibung).strip()[:800]}...") - - prompt = "\n".join(filter(None, prompt_parts)) + + prompt = "\n".join(prompt_parts) try: chat_response = call_openai_chat(prompt, temperature=0.0) @@ -942,72 +934,54 @@ def evaluate_branche_chatgpt(crm_branche, beschreibung, wiki_branche, wiki_kateg logger.error(f"Endgueltiger FEHLER beim OpenAI-Aufruf fuer Branchenevaluation: {e}") return {"branch": "FEHLER API", "confidence": "N/A", "consistency": "error_api_failed", "justification": f"Fehler API: {str(e)[:100]}"} - # --- v1.7.9 PARSING LOGIK WIEDERHERGESTELLT --- + # v1.7.9 Parsing Logic lines = chat_response.strip().split("\n") result = {"confidence": "N/A", "justification": ""} suggested_branch = "" - parsed_branch = False for line in lines: line_lower = line.lower() - if line_lower.startswith("branche:"): - suggested_branch = line.split(":", 1)[1].strip() - parsed_branch = True - break # Wichtig: Schleife beenden, sobald der Treffer da ist - - # Fallback, wenn "Branche:" nicht gefunden wurde (Logik aus v1.7.9) - if not parsed_branch and lines: - suggested_branch = lines[0].strip() # Nimm die erste Zeile als Vorschlag - logger.warning(f"Konnte 'Branche:' nicht finden. Interpretiere erste Zeile als Vorschlag: '{suggested_branch}'") + if "branche:" in line_lower: + 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('"\'') - # Extrahiere Konfidenz und Begründung separat + # Extrahiere Konfidenz und Begründung for line in lines: - line_lower = line.lower() - if line_lower.startswith("konfidenz:"): - result["confidence"] = line.split(":", 1)[1].strip() - elif line_lower.startswith("begruendung:"): - result["justification"] = line.split(":", 1)[1].strip() + if line.lower().startswith("konfidenz:"): result["confidence"] = line.split(":", 1)[1].strip() + elif line.lower().startswith("begruendung:"): result["justification"] = line.split(":", 1)[1].strip() if not suggested_branch: - logger.error(f"Fehler: Konnte keine Branche aus der Antwort extrahieren: {chat_response[:200]}") - return {"branch": "FEHLER PARSING", "confidence": "N/A", "consistency": "error_parsing", "justification": f"Antwort leer oder unklar: {chat_response[:100]}"} + logger.error(f"Fehler: Konnte keine Branche aus Antwort parsen: {chat_response[:200]}") + return {"branch": "FEHLER PARSING", "confidence": "N/A", "consistency": "error_parsing", "justification": f"Antwortformat unerwartet: {chat_response[:100]}"} - # --- Validierung und intelligenter Fallback (leicht angepasst aus v1.9.1) --- + # Validierung und intelligenter Fallback 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] - logger.debug(f"KI-Vorschlag '{suggested_branch}' ist ein exakter Match.") else: - # Substring-Match für KI-Vorschlag - best_suggestion_match = next((val for key, val in allowed_branches_lookup.items() if suggested_branch_lower in key), None) - if best_suggestion_match: - final_branch = best_suggestion_match - logger.info(f"KI-Vorschlag '{suggested_branch}' per Substring zu '{final_branch}' gemappt.") + 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 - # Konsistenzprüfung (ok/X) - if final_branch.lower() == crm_branche.strip().lower(): - result["consistency"] = "ok" - else: - result["consistency"] = "X" + if final_branch.lower() == crm_branche.strip().lower(): result["consistency"] = "ok" + else: result["consistency"] = "X" else: - # Fallback auf CRM-Branche - logger.debug(f"Vorschlag '{suggested_branch}' nicht im Schema. Prüfe Fallback auf CRM-Branche...") 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 in key and crm_short_branch_lower), None) - + 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["branch"] = best_crm_fallback result["consistency"] = "fallback_crm_substring" - result["justification"] = f"Fallback: KI-Vorschlag ungültig. CRM-Branche '{crm_branche}' passt zu Schema '{best_crm_fallback}'." + result["justification"] = f"Fallback: KI-Vorschlag ungültig. CRM-Branche '{crm_branche}' passt zu '{best_crm_fallback}'." result["confidence"] = "N/A (Fallback)" else: result["branch"] = "FEHLER - UNGUELTIGE ZUWEISUNG" result["consistency"] = "fallback_invalid" - result["justification"] = f"Fehler: Weder KI-Vorschlag ('{suggested_branch}') noch CRM-Branche ('{crm_branche}') passen zum Schema." + result["justification"] = f"Fehler: Weder KI ('{suggested_branch}') noch CRM ('{crm_branche}') passen." result["confidence"] = "N/A (Fehler)" logger.debug(f"Finale Branch-Evaluation: {result}")