Fokusbranchen-Logik in Branchenevaluation integriert
Wiedereinführung und Verbesserung der Funktionalität zur Priorisierung von Fokusbranchen bei der automatisierten Branchenklassifizierung durch ChatGPT.
**Änderungen:**
- **Fokusbranchen aus CSV:**
- Die Definition von Fokusbranchen erfolgt nun über eine zusätzliche Spalte in der `ziel_Branchenschema.csv`-Datei. Dies ermöglicht eine flexible Konfiguration ohne Code-Änderungen.
- Die Funktion `load_target_schema` wurde erweitert, um diese Fokus-Markierungen einzulesen und eine separate Liste `FOCUS_TARGET_BRANCHES` sowie einen spezifischen Prompt-Teil `FOCUS_BRANCHES_PROMPT_PART` zu generieren.
- **Angepasste `evaluate_branche_chatgpt` Funktion:**
- Nutzt nun die global geladenen Listen `ALLOWED_TARGET_BRANCHES` und `FOCUS_TARGET_BRANCHES` sowie die Prompt-Teile `TARGET_SCHEMA_STRING` und `FOCUS_BRANCHES_PROMPT_PART`.
- Der an ChatGPT gesendete Prompt wurde um einen expliziten Hinweis erweitert, Fokusbranchen bei der Klassifizierung zu priorisieren, falls mehrere Branchen plausibel erscheinen.
- Die Fallback-Logik für die zu verwendende Beschreibungsquelle (CRM-Beschreibung vs. Website-Zusammenfassung bei fehlenden Wiki-Daten) wurde aus der früheren Funktionsversion übernommen und verfeinert.
- Die API-Key-Handhabung wurde entfernt, da diese nun global über `Config.API_KEYS` und `call_openai_chat` erfolgt.
- `debug_print` Aufrufe wurden durch Standard-Logging (`logger.debug`) ersetzt.
- **Globale Variablen:** Neue globale Variablen für Fokusbranchen und deren Prompt-Teil wurden eingeführt.
**Ziel:**
- Erhöhung der Genauigkeit der Branchenklassifizierung, indem vordefinierte, strategisch wichtige Branchen bei der KI-gestützten Bewertung bevorzugt werden.
- Verbesserung der Flexibilität und Wartbarkeit der Fokusbranchen-Definition durch Auslagerung in die zentrale CSV-Datei.
This commit is contained in:
@@ -191,6 +191,9 @@ COLUMN_MAP = {
|
|||||||
BRANCH_MAPPING = {}
|
BRANCH_MAPPING = {}
|
||||||
TARGET_SCHEMA_STRING = "Ziel-Branchenschema nicht verfuegbar."
|
TARGET_SCHEMA_STRING = "Ziel-Branchenschema nicht verfuegbar."
|
||||||
ALLOWED_TARGET_BRANCHES = []
|
ALLOWED_TARGET_BRANCHES = []
|
||||||
|
FOCUS_TARGET_BRANCHES = [] # NEU
|
||||||
|
TARGET_SCHEMA_STRING = "Ziel-Branchenschema nicht verfuegbar."
|
||||||
|
FOCUS_BRANCHES_PROMPT_PART = "" # NEU: Für den Prompt-Teil der Fokusbranchen
|
||||||
|
|
||||||
# Marker für URLs, die erneut per SERP gesucht werden sollen
|
# Marker für URLs, die erneut per SERP gesucht werden sollen
|
||||||
URL_CHECK_MARKER = "URL_CHECK_NEEDED" # <<< NEU HINZUFÜGEN
|
URL_CHECK_MARKER = "URL_CHECK_NEEDED" # <<< NEU HINZUFÜGEN
|
||||||
@@ -975,91 +978,83 @@ ALLOWED_TARGET_BRANCHES = [] # Liste der erlaubten Kurzformen
|
|||||||
|
|
||||||
def load_target_schema(csv_filepath=BRANCH_MAPPING_FILE):
|
def load_target_schema(csv_filepath=BRANCH_MAPPING_FILE):
|
||||||
"""
|
"""
|
||||||
Laedt Liste erlaubter Ziel-Branchen (Kurzformen) aus Spalte A der CSV-Datei.
|
Laedt Liste erlaubter Ziel-Branchen und Fokus-Branchen aus der CSV-Datei.
|
||||||
Befuellt die globalen Variablen ALLOWED_TARGET_BRANCHES und TARGET_SCHEMA_STRING.
|
Befuellt die globalen Variablen ALLOWED_TARGET_BRANCHES, FOCUS_TARGET_BRANCHES,
|
||||||
|
TARGET_SCHEMA_STRING und FOCUS_BRANCHES_PROMPT_PART.
|
||||||
Args:
|
|
||||||
csv_filepath (str, optional): Pfad zur CSV-Datei mit dem Branchenschema.
|
|
||||||
Defaults to the global BRANCH_MAPPING_FILE.
|
|
||||||
"""
|
"""
|
||||||
logger = logging.getLogger(__name__) # <<< DIESE ZEILE HINZUFÜGEN
|
logger = logging.getLogger(__name__)
|
||||||
# Zugriff auf die globalen Variablen
|
global ALLOWED_TARGET_BRANCHES, FOCUS_TARGET_BRANCHES, TARGET_SCHEMA_STRING, FOCUS_BRANCHES_PROMPT_PART
|
||||||
global BRANCH_MAPPING, TARGET_SCHEMA_STRING, ALLOWED_TARGET_BRANCHES
|
|
||||||
|
|
||||||
# Setzen Sie BRANCH_MAPPING zurueck, da es in dieser Version nicht primaer genutzt wird
|
ALLOWED_TARGET_BRANCHES = []
|
||||||
BRANCH_MAPPING = {}
|
FOCUS_TARGET_BRANCHES = []
|
||||||
|
allowed_branches_set = set()
|
||||||
allowed_branches_set = set() # Nutzt ein Set, um Duplikate automatisch zu behandeln
|
focus_branches_set = set() # Für Fokusbranchen
|
||||||
line_count = 0
|
line_count = 0
|
||||||
|
|
||||||
logger.info(f"Lade Ziel-Schema (Kurzformen) aus '{csv_filepath}' Spalte A...") # Jetzt sollte logger definiert sein
|
logger.info(f"Lade Ziel-Schema und Fokus-Branchen aus '{csv_filepath}'...")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Versuche, die Datei mit UTF-8-BOM-Signatur oder normalem UTF-8 zu oeffnen
|
|
||||||
# Verwenden Sie "r" fuer Textmodus und geben Sie das Encoding an
|
|
||||||
with open(csv_filepath, "r", encoding="utf-8-sig") as f:
|
with open(csv_filepath, "r", encoding="utf-8-sig") as f:
|
||||||
# Verwenden Sie den CSV-Reader, um Zeilen zu lesen
|
|
||||||
reader = csv.reader(f)
|
reader = csv.reader(f)
|
||||||
# Versuche, die erste Zeile als Header zu ueberspringen (Heuristik)
|
|
||||||
try:
|
try:
|
||||||
header_row = next(reader)
|
header_row = next(reader) # Überspringe Header
|
||||||
# logger.debug(f"Ueberspringe Header-Zeile: {header_row}") # Zu viel Laerm im Debug
|
logger.debug(f"Ueberspringe Header-Zeile im Schema: {header_row}")
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
# Wenn die Datei leer ist
|
logger.warning(f"Schema-Datei '{csv_filepath}' ist leer oder hat keinen Header.")
|
||||||
logger.warning(f"Schema-Datei '{csv_filepath}' ist leer.")
|
TARGET_SCHEMA_STRING = "Ziel-Branchenschema nicht verfuegbar (Datei leer)."
|
||||||
ALLOWED_TARGET_BRANCHES = [] # Setze die Liste auf leer
|
FOCUS_BRANCHES_PROMPT_PART = ""
|
||||||
TARGET_SCHEMA_STRING = "Ziel-Branchenschema nicht verfuegbar (Datei leer)." # Setze Fehler-String
|
return
|
||||||
return # Beende die Funktion, da nichts zu tun ist
|
|
||||||
|
|
||||||
# Iteriere ueber die verbleibenden Zeilen
|
for row_num, row in enumerate(reader, 1): # Starte Zählung bei 1 für Zeilennummern nach Header
|
||||||
for row in reader:
|
line_count = row_num
|
||||||
line_count += 1
|
if not row: # Leere Zeile überspringen
|
||||||
# logger.debug(f"Schema-Laden: Lese Zeile {line_count}: {row}") # Zu viel Laerm im Debug
|
continue
|
||||||
|
|
||||||
# Pruefe, ob die Zeile mindestens eine Spalte hat (Spalte A ist Index 0)
|
|
||||||
if len(row) >= 1:
|
if len(row) >= 1:
|
||||||
target = row[0].strip() # Hole den Wert aus Spalte A und entferne Whitespace
|
target_branch = row[0].strip()
|
||||||
if target: # Fuege den Wert zum Set hinzu, wenn er nicht leer ist
|
if target_branch:
|
||||||
allowed_branches_set.add(target)
|
allowed_branches_set.add(target_branch)
|
||||||
# logger.debug(f" -> '{target}' zum Set hinzugefuegt.") # Zu viel Laerm im Debug
|
# Prüfe Spalte B (Index 1) für Fokus-Markierung
|
||||||
|
if len(row) >= 2 and row[1].strip().upper() in ["X", "FOKUS", "JA", "TRUE", "1"]:
|
||||||
|
focus_branches_set.add(target_branch)
|
||||||
|
logger.debug(f" -> Fokusbranche gefunden: '{target_branch}'")
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
# Wenn die Schema-Datei nicht gefunden wird
|
|
||||||
logger.critical(f"FEHLER: Schema-Datei '{csv_filepath}' nicht gefunden.")
|
logger.critical(f"FEHLER: Schema-Datei '{csv_filepath}' nicht gefunden.")
|
||||||
ALLOWED_TARGET_BRANCHES = [] # Setze die Liste auf leer
|
TARGET_SCHEMA_STRING = "Ziel-Branchenschema nicht verfuegbar (Datei nicht gefunden)."
|
||||||
TARGET_SCHEMA_STRING = "Ziel-Branchenschema nicht verfuegbar (Datei nicht gefunden)." # Setze Fehler-String
|
FOCUS_BRANCHES_PROMPT_PART = ""
|
||||||
return # Beende die Funktion, da die Datei fehlt
|
return
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# Fange andere unerwartete Fehler beim Lesen der Datei ab
|
logger.critical(f"FEHLER beim Laden des Ziel-Schemas aus '{csv_filepath}' (Zeile {line_count}): {e}")
|
||||||
logger.critical(f"FEHLER beim Laden des Ziel-Schemas aus '{csv_filepath}' (Zeile {line_count if line_count > 0 else 'vor erster Zeile'}): {e}")
|
TARGET_SCHEMA_STRING = "Ziel-Branchenschema nicht verfuegbar (Fehler beim Lesen)."
|
||||||
ALLOWED_TARGET_BRANCHES = [] # Setze die Liste auf leer
|
FOCUS_BRANCHES_PROMPT_PART = ""
|
||||||
TARGET_SCHEMA_STRING = "Ziel-Branchenschema nicht verfuegbar (Fehler beim Lesen)." # Setze Fehler-String
|
return
|
||||||
return # Beende die Funktion, da ein Fehler aufgetreten ist
|
|
||||||
|
|
||||||
|
|
||||||
# Konvertiere das Set in eine sortierte Liste
|
|
||||||
ALLOWED_TARGET_BRANCHES = sorted(list(allowed_branches_set), key=str.lower)
|
ALLOWED_TARGET_BRANCHES = sorted(list(allowed_branches_set), key=str.lower)
|
||||||
logger.info(f"Ziel-Schema geladen. {len(ALLOWED_TARGET_BRANCHES)} eindeutige Zielbranchen gefunden.")
|
FOCUS_TARGET_BRANCHES = sorted(list(focus_branches_set), key=str.lower)
|
||||||
|
|
||||||
|
logger.info(f"Ziel-Schema geladen: {len(ALLOWED_TARGET_BRANCHES)} eindeutige Zielbranchen, davon {len(FOCUS_TARGET_BRANCHES)} Fokusbranchen.")
|
||||||
|
|
||||||
# Erstelle den Prompt-String fuer ChatGPT, wenn gueltige Branchen gefunden wurden
|
|
||||||
if ALLOWED_TARGET_BRANCHES:
|
if ALLOWED_TARGET_BRANCHES:
|
||||||
# logger.debug(f"Erste 10 geladene Zielbranchen: {ALLOWED_TARGET_BRANCHES[:10]}") # Zu viel Laerm im Debug
|
|
||||||
schema_lines = ["Ziel-Branchenschema: Folgende Branchenbereiche sind gueltig (Kurzformen):"]
|
schema_lines = ["Ziel-Branchenschema: Folgende Branchenbereiche sind gueltig (Kurzformen):"]
|
||||||
# Fuege jede erlaubte Branche als Listeneintrag hinzu
|
|
||||||
schema_lines.extend(f"- {branch}" for branch in ALLOWED_TARGET_BRANCHES)
|
schema_lines.extend(f"- {branch}" for branch in ALLOWED_TARGET_BRANCHES)
|
||||||
# Fuege strenge Anweisungen fuer das Antwortformat hinzu
|
# Anweisungen für das Antwortformat (unverändert)
|
||||||
schema_lines.append("\nBitte ordne das Unternehmen ausschliesslich in einen dieser Bereiche ein. Gib NUR den exakten Kurznamen der Branche zurueck (keine Praefixe oder zusaetzliche Erklaerungen ausser im 'Begruendung'-Feld).") # Verwende Umlaute nicht, um Encoding-Probleme im Prompt zu vermeiden
|
schema_lines.append("\nBitte ordne das Unternehmen ausschliesslich in einen dieser Bereiche ein. Gib NUR den exakten Kurznamen der Branche zurueck (keine Praefixe oder zusaetzliche Erklaerungen ausser im 'Begruendung'-Feld).")
|
||||||
schema_lines.append("Antworte ausschliesslich im folgenden Format (keine Einleitung, kein Schlusssatz):")
|
schema_lines.append("Antworte ausschliesslich im folgenden Format (keine Einleitung, kein Schlusssatz):")
|
||||||
schema_lines.append("Branche: <Exakter Kurzname der Branche aus der Liste>")
|
schema_lines.append("Branche: <Exakter Kurzname der Branche aus der Liste>")
|
||||||
schema_lines.append("Uebereinstimmung: <ok oder X (Vergleich deines Vorschlags mit der extrahierten Kurzform der CRM-Referenz)>") # Verwende Umlaute nicht
|
schema_lines.append("Uebereinstimmung: <ok oder X (Vergleich deines Vorschlags mit der extrahierten Kurzform der CRM-Referenz)>")
|
||||||
schema_lines.append("Begruendung: <Sehr kurze Begruendung fuer deinen Branchenvorschlag>") # Verwende Umlaute nicht
|
schema_lines.append("Begruendung: <Sehr kurze Begruendung fuer deinen Branchenvorschlag>")
|
||||||
|
|
||||||
# Verbinde die Zeilen zum finalen Prompt-String
|
|
||||||
TARGET_SCHEMA_STRING = "\n".join(schema_lines)
|
TARGET_SCHEMA_STRING = "\n".join(schema_lines)
|
||||||
# logger.debug(f"Generierter TARGET_SCHEMA_STRING:\n{TARGET_SCHEMA_STRING}") # Zu viel Laerm im Debug
|
|
||||||
|
if FOCUS_TARGET_BRANCHES:
|
||||||
|
focus_prompt_lines = ["\nZusätzlicher Hinweis: Wenn die Wahl zwischen mehreren passenden Branchen besteht, priorisiere bitte, wenn möglich, eine der folgenden Fokusbranchen:"]
|
||||||
|
focus_prompt_lines.extend(f"- {branch}" for branch in FOCUS_TARGET_BRANCHES)
|
||||||
|
FOCUS_BRANCHES_PROMPT_PART = "\n".join(focus_prompt_lines)
|
||||||
|
else:
|
||||||
|
FOCUS_BRANCHES_PROMPT_PART = ""
|
||||||
|
logger.info("Keine Fokusbranchen im Schema definiert.")
|
||||||
else:
|
else:
|
||||||
# Wenn keine gueltigen Branchen gefunden wurden
|
|
||||||
TARGET_SCHEMA_STRING = "Ziel-Branchenschema nicht verfuegbar (Keine gueltigen Branchen in Datei gefunden)."
|
TARGET_SCHEMA_STRING = "Ziel-Branchenschema nicht verfuegbar (Keine gueltigen Branchen in Datei gefunden)."
|
||||||
|
FOCUS_BRANCHES_PROMPT_PART = ""
|
||||||
logger.warning("Keine gueltigen Zielbranchen im Schema gefunden. Branchenbewertung ist nicht moeglich.")
|
logger.warning("Keine gueltigen Zielbranchen im Schema gefunden. Branchenbewertung ist nicht moeglich.")
|
||||||
|
|
||||||
|
|
||||||
@@ -1404,201 +1399,156 @@ def summarize_batch_openai(tasks_data):
|
|||||||
# Funktion zur Branchenbewertung mittels OpenAI.
|
# Funktion zur Branchenbewertung mittels OpenAI.
|
||||||
# Nutzt globale Helfer: ALLOWED_TARGET_BRANCHES, TARGET_SCHEMA_STRING,
|
# Nutzt globale Helfer: ALLOWED_TARGET_BRANCHES, TARGET_SCHEMA_STRING,
|
||||||
# call_openai_chat, logger, re, retry_on_failure.
|
# call_openai_chat, logger, re, retry_on_failure.
|
||||||
@retry_on_failure # Wende den Decorator auf diese Funktion an, da sie call_openai_chat aufruft
|
@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):
|
||||||
"""
|
logger = logging.getLogger(__name__)
|
||||||
Ordnet das Unternehmen basierend auf den angegebenen Informationen exakt einer Branche
|
# Zugriff auf die globalen, durch load_target_schema() befüllten Variablen
|
||||||
aus dem Ziel-Branchenschema (nur Kurzformen) zu. Validiert den ChatGPT-Vorschlag
|
global ALLOWED_TARGET_BRANCHES, TARGET_SCHEMA_STRING, FOCUS_BRANCHES_PROMPT_PART
|
||||||
strikt gegen die erlaubten Kurzformen und fuehrt einen Fallback auf die (extrahierte)
|
|
||||||
CRM-Kurzform durch, falls der Vorschlag ungueltig ist.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
crm_branche (str): Branche laut CRM (kann noch Praefix enthalten).
|
|
||||||
beschreibung (str): Unternehmensbeschreibung (CRM).
|
|
||||||
wiki_branche (str): Branche aus Wikipedia (falls vorhanden).
|
|
||||||
wiki_kategorien (str): Wikipedia-Kategorien.
|
|
||||||
website_summary (str): Zusammenfassung des Website-Inhalts.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
dict: Enthaehlt "branch" (die finale, gueltige Kurzform oder Fehler),
|
|
||||||
"consistency" ('ok', 'X', 'fallback_crm_valid', 'fallback_invalid', 'error_...'),
|
|
||||||
"justification" (Begruendung von ChatGPT oder Fallback-Info).
|
|
||||||
Wirft Exception bei API-Fehlern nach Retries (von call_openai_chat).
|
|
||||||
"""
|
|
||||||
# Verwenden Sie logger, da das Logging jetzt konfiguriert ist
|
|
||||||
logger = logging.getLogger(__name__) # <<< DIESE ZEILE HINZUFÜGEN
|
|
||||||
# Zugriff auf globale Variablen (befuellt von load_target_schema im Block 6)
|
|
||||||
global ALLOWED_TARGET_BRANCHES, TARGET_SCHEMA_STRING
|
|
||||||
|
|
||||||
# Grundlegende Pruefung: Ist das Schema ueberhaupt geladen?
|
|
||||||
if not ALLOWED_TARGET_BRANCHES:
|
if not ALLOWED_TARGET_BRANCHES:
|
||||||
logger.critical("FEHLER in evaluate_branche_chatgpt: Ziel-Branchenschema (ALLOWED_TARGET_BRANCHES) ist leer. Kann Branchen nicht validieren.")
|
logger.critical("FEHLER in evaluate_branche_chatgpt: Ziel-Branchenschema nicht geladen.")
|
||||||
# Geben Sie ein Fehlerergebnis zurueck
|
|
||||||
return {"branch": "FEHLER - SCHEMA FEHLT", "consistency": "error_schema_missing", "justification": "Fehler: Ziel-Schema nicht geladen"}
|
return {"branch": "FEHLER - SCHEMA FEHLT", "consistency": "error_schema_missing", "justification": "Fehler: Ziel-Schema nicht geladen"}
|
||||||
|
|
||||||
# Erstelle Lookup fuer erlaubte Branches (case-insensitive)
|
|
||||||
allowed_branches_lookup = {b.lower(): b for b in ALLOWED_TARGET_BRANCHES}
|
allowed_branches_lookup = {b.lower(): b for b in ALLOWED_TARGET_BRANCHES}
|
||||||
|
|
||||||
# --- Prompt fuer ChatGPT erstellen ---
|
# --- Prompt für ChatGPT erstellen ---
|
||||||
# Beginne mit den Regeln und der Liste der gueltigen Kurzformen
|
# Beginnt mit den Regeln und der Liste der gueltigen Kurzformen (TARGET_SCHEMA_STRING)
|
||||||
prompt_parts = [TARGET_SCHEMA_STRING] # Enthält bereits die Liste und Anweisungen
|
# Fügt dann den Hinweis auf Fokusbranchen hinzu (FOCUS_BRANCHES_PROMPT_PART)
|
||||||
|
prompt_parts = [TARGET_SCHEMA_STRING, FOCUS_BRANCHES_PROMPT_PART] # <<< HIER FOCUS_BRANCHES_PROMPT_PART EINFÜGEN
|
||||||
prompt_parts.append("\nOrdne das Unternehmen anhand folgender Angaben exakt einer Branche des Ziel-Branchenschemas (Kurzformen) zu:")
|
prompt_parts.append("\nOrdne das Unternehmen anhand folgender Angaben exakt einer Branche des Ziel-Branchenschemas (Kurzformen) zu:")
|
||||||
|
|
||||||
# Fuege nur vorhandene Informationen hinzu und kuerze sie ggf.
|
# Informationen hinzufügen (unverändert)
|
||||||
# Stellen Sie sicher, dass die Werte keine None-Typen sind
|
|
||||||
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 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 beschreibung and str(beschreibung).strip() and str(beschreibung).strip().lower() != "k.a.": prompt_parts.append(f"- Beschreibung: {str(beschreibung).strip()[:500]}...") # Kuerzen
|
# Fallback-Logik für beschreibung vs. website_summary (basierend auf Ihrer alten Funktion)
|
||||||
if wiki_branche and str(wiki_branche).strip() and str(wiki_branche).strip().lower() != "k.a.": prompt_parts.append(f"- Wikipedia-Branche: {str(wiki_branche).strip()[:300]}...") # Kuerzen
|
if wiki_branche and str(wiki_branche).strip() and str(wiki_branche).strip().lower() != "k.a.":
|
||||||
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]}...") # Kuerzen
|
# Wenn Wiki-Daten vorhanden sind, nutzen wir die CRM-Beschreibung als Hauptbeschreibung
|
||||||
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"): # Pruefe auch auf Website Summary Fehlerwerte
|
if beschreibung and str(beschreibung).strip() and str(beschreibung).strip().lower() != "k.a.":
|
||||||
prompt_parts.append(f"- Website-Zusammenfassung: {str(website_summary).strip()[:500]}...") # Kuerzen
|
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"):
|
||||||
|
prompt_parts.append(f"- Website-Zusammenfassung: {str(website_summary).strip()[:500]}...")
|
||||||
|
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: # Keine validen Wiki-Daten
|
||||||
|
logger.debug("evaluate_branche_chatgpt: Keine validen Wiki-Daten, nutze Website-Zusammenfassung als primäre Beschreibung (falls vorhanden).")
|
||||||
|
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]}...") # Ggf. länger, da Hauptinfo
|
||||||
|
elif beschreibung and str(beschreibung).strip() and str(beschreibung).strip().lower() != "k.a.": # Fallback auf CRM Beschreibung
|
||||||
|
prompt_parts.append(f"- Beschreibung (CRM, als Hauptbeschreibung): {str(beschreibung).strip()[:800]}...")
|
||||||
|
|
||||||
|
|
||||||
# Fallback, wenn zu wenige Infos da sind (mindestens 2 relevante Zeilen im Prompt neben dem Schema)
|
if len(prompt_parts) < (3 + (1 if FOCUS_BRANCHES_PROMPT_PART else 0) )): # Mind. TARGET_SCHEMA + FOCUS_PART (falls vorhanden) + Instruktion + 1 Infoquelle
|
||||||
# Der Prompt hat immer mindestens 1 Zeile (Schema) + 1 Zeile (Instruktion "Ordne zu...").
|
logger.warning("Warnung in evaluate_branche_chatgpt: Zu wenige Informationen für Branchenevaluierung.")
|
||||||
# Pruefen wir, ob mindestens 2 Info-Zeilen hinzugefuegt wurden.
|
crm_short_branch_for_fallback = "k.A."
|
||||||
if len(prompt_parts) < 3: # 1 (Schema) + 1 (Instruktion) + <2 (Infos)
|
if crm_branche and isinstance(crm_branche, str):
|
||||||
logger.warning("Warnung in evaluate_branche_chatgpt: Zu wenige Informationen (<2 Quellen) fuer Branchenevaluierung.")
|
parts = crm_branche.split(">", 1)
|
||||||
# Geben Sie ein Fehlerergebnis zurueck, verwenden Sie die CRM-Branche als Fallback
|
crm_short_branch_for_fallback = parts[1].strip() if len(parts) > 1 else crm_branche.strip()
|
||||||
return {"branch": crm_branche, "consistency": "error_no_info", "justification": "Fehler: Zu wenige Informationen fuer eine Einschaetzung"}
|
return {"branch": crm_short_branch_for_fallback if crm_short_branch_for_fallback.lower() != "k.a." else "FEHLER",
|
||||||
|
"consistency": "error_no_info",
|
||||||
|
"justification": "Fehler: Zu wenige Informationen fuer eine Einschaetzung"}
|
||||||
|
|
||||||
# Prompt fuer das Antwortformat ist bereits in TARGET_SCHEMA_STRING enthalten.
|
prompt = "\n".join(filter(None, prompt_parts)) # filter(None, ...) um leere Strings aus FOCUS_BRANCHES_PROMPT_PART zu entfernen
|
||||||
|
# logger.debug(f"Erstellter Prompt fuer Branchenevaluierung:\n---\n{prompt}\n---")
|
||||||
|
|
||||||
prompt = "\n".join(prompt_parts)
|
# --- ChatGPT aufrufen (Rest der Funktion bleibt wie in Block 10/20) ---
|
||||||
# logger.debug(f"Erstellter Prompt fuer Branchenevaluierung:\n---\n{prompt}\n---") # Zu viel Laerm im Debug
|
# ... (ChatGPT Call, Parsing, Validierung, Fallback-Logik) ...
|
||||||
|
# Wichtig: Die API-Key Ladung und der openai-Import sollten jetzt global (Block 1) erfolgen
|
||||||
|
# und nicht mehr innerhalb dieser Funktion.
|
||||||
|
# Ihre alte Funktion hat api_key.txt gelesen, das ist jetzt Teil von Config.load_api_keys().
|
||||||
|
|
||||||
# --- ChatGPT aufrufen ---
|
|
||||||
# call_openai_chat nutzt den retry_on_failure Decorator und wirft bei endgueltigem Fehler eine Exception
|
|
||||||
chat_response = None
|
chat_response = None
|
||||||
try:
|
try:
|
||||||
chat_response = call_openai_chat(prompt, temperature=0.0) # Niedrige Temperatur fuer konsistente Zuordnung
|
chat_response = call_openai_chat(prompt, temperature=0.0) # Nutzt globale Funktion
|
||||||
|
|
||||||
if not chat_response:
|
if not chat_response:
|
||||||
# Dieser Fall sollte nach der Aenderung in call_openai_chat nicht mehr auftreten (wuerde Exception werfen)
|
logger.error("call_openai_chat gab unerwarteterweise None/leer zurueck fuer Branchenevaluation.")
|
||||||
logger.error("call_openai_chat gab unerwarteterweise None zurueck fuer Branchenevaluation.")
|
raise APIError("Keine Antwort von OpenAI erhalten fuer Branchenevaluation.")
|
||||||
raise openai.error.APIError("Keine Antwort von OpenAI erhalten fuer Branchenevaluation.") # Wirf eine Exception
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# Wenn call_openai_chat nach Retries eine Exception wirft
|
|
||||||
# Der Fehler wird bereits vom retry_on_failure Decorator geloggt.
|
|
||||||
logger.error(f"Endgueltiger FEHLER beim OpenAI-Aufruf fuer Branchenevaluation: {e}")
|
logger.error(f"Endgueltiger FEHLER beim OpenAI-Aufruf fuer Branchenevaluation: {e}")
|
||||||
# Geben Sie ein Fehlerergebnis zurueck, verwenden Sie die CRM-Branche als Fallback
|
crm_short_branch_for_fallback = "k.A."
|
||||||
# Haengen Sie die Fehlermeldung an die Begruendung an.
|
if crm_branche and isinstance(crm_branche, str):
|
||||||
return {"branch": crm_branche, "consistency": "error_api_failed", "justification": f"Fehler API: {str(e)[:100]}"}
|
parts = crm_branche.split(">", 1)
|
||||||
|
crm_short_branch_for_fallback = parts[1].strip() if len(parts) > 1 else crm_branche.strip()
|
||||||
|
return {"branch": crm_short_branch_for_fallback if crm_short_branch_for_fallback.lower() != "k.a." else "FEHLER API",
|
||||||
|
"consistency": "error_api_failed",
|
||||||
|
"justification": f"Fehler API: {str(e)[:100]}"}
|
||||||
|
|
||||||
|
# --- Antwort parsen (wie gehabt) ---
|
||||||
# --- Antwort parsen ---
|
|
||||||
lines = chat_response.strip().split("\n")
|
lines = chat_response.strip().split("\n")
|
||||||
# Initialisiere Ergebnisdict mit Fallback-Werten oder leeren Strings
|
|
||||||
result = {"branch": None, "consistency": None, "justification": ""}
|
result = {"branch": None, "consistency": None, "justification": ""}
|
||||||
suggested_branch = ""
|
suggested_branch = ""
|
||||||
parsed_branch = False
|
parsed_branch = False
|
||||||
for line in lines:
|
for line in lines:
|
||||||
line_lower = line.lower(); line_stripped = line.strip()
|
line_lower = line.lower(); line_stripped = line.strip()
|
||||||
if line_lower.startswith("branche:"):
|
if line_lower.startswith("branche:"):
|
||||||
# Extrahiere die vorgeschlagene Branche, bereinige Leerzeichen und Anfuehrungszeichen
|
|
||||||
suggested_branch = line_stripped.split(":", 1)[1].strip().strip('"\'')
|
suggested_branch = line_stripped.split(":", 1)[1].strip().strip('"\'')
|
||||||
parsed_branch = True
|
parsed_branch = True
|
||||||
elif line_lower.startswith("uebereinstimmung:") or line_lower.startswith("ubereinstimmung:"): # Beruecksichtige Umlaute und keine Umlaute
|
elif line_lower.startswith("uebereinstimmung:") or line_lower.startswith("ubereinstimmung:"):
|
||||||
# Wir ueberschreiben die Konsistenz spaeter basierend auf unserer Logik, ignorieren Sie die KI-Antwort hier
|
|
||||||
pass
|
pass
|
||||||
elif line_lower.startswith("begruendung:") or line_lower.startswith("begruendung:"): # Beruecksichtige Umlaute und keine Umlaute
|
elif line_lower.startswith("begruendung:") or line_lower.startswith("begruendung:"):
|
||||||
# Erfasse die Begruendung. Wenn es mehrere Begruendungszeilen gibt, haenge sie an.
|
|
||||||
justification_text = line_stripped.split(":", 1)[1].strip()
|
justification_text = line_stripped.split(":", 1)[1].strip()
|
||||||
if result["justification"]: result["justification"] += " " + justification_text
|
if result["justification"]: result["justification"] += " " + justification_text
|
||||||
else: result["justification"] = justification_text
|
else: result["justification"] = justification_text
|
||||||
# Behandle andere moegliche unerwartete Zeilen (optional)
|
|
||||||
# elif line_lower.startswith(("resultat", "eintrag", "antwort")):
|
|
||||||
# logger.warning(f"Unerwartete Zeile im Branchen-Prompt gefunden: {line[:100]}...")
|
|
||||||
|
|
||||||
|
if not parsed_branch or not suggested_branch or suggested_branch.lower() in ["k.a.", "n/a"]:
|
||||||
|
logger.error(f"Fehler in evaluate_branche_chatgpt: Konnte 'Branche:' nicht oder nur leer/k.A. aus Antwort parsen: {chat_response[:500]}...")
|
||||||
|
crm_short_branch_for_fallback = "k.A."
|
||||||
|
if crm_branche and isinstance(crm_branche, str):
|
||||||
|
parts = crm_branche.split(">", 1)
|
||||||
|
crm_short_branch_for_fallback = parts[1].strip() if len(parts) > 1 else crm_branche.strip()
|
||||||
|
return {"branch": crm_short_branch_for_fallback if crm_short_branch_for_fallback.lower() != "k.a." else "FEHLER PARSING",
|
||||||
|
"consistency": "error_parsing",
|
||||||
|
"justification": f"Fehler Parsing: Antwortformat unerwartet."}
|
||||||
|
|
||||||
# Pruefe, ob Branch geparst wurde UND nicht leer ist
|
# --- Validierung und Fallback (wie gehabt) ---
|
||||||
if not parsed_branch or not suggested_branch or suggested_branch.lower() in ["k.a.", "n/a"]: # Fuege "k.a." zur Pruefung hinzu
|
|
||||||
logger.error(f"Fehler in evaluate_branche_chatgpt: Konnte 'Branche:' nicht oder nur leer/k.A. aus Antwort parsen: {chat_response[:500]}...") # Logge Anfang der Antwort
|
|
||||||
# Geben Sie ein Fehlerergebnis zurueck, verwenden Sie die CRM-Branche als Fallback
|
|
||||||
return {"branch": crm_branche, "consistency": "error_parsing", "justification": f"Fehler Parsing: Antwortformat unerwartet."}
|
|
||||||
|
|
||||||
|
|
||||||
# --- Validierung des ChatGPT-Vorschlags ---
|
|
||||||
final_branch = None
|
final_branch = None
|
||||||
suggested_branch_lower = suggested_branch.lower()
|
suggested_branch_lower = suggested_branch.lower()
|
||||||
|
|
||||||
# 1. Ist der vorgeschlagene Branch EXAKT im Ziel-Schema enthalten?
|
|
||||||
if suggested_branch_lower in allowed_branches_lookup:
|
if suggested_branch_lower in allowed_branches_lookup:
|
||||||
final_branch = allowed_branches_lookup[suggested_branch_lower] # Nimm die korrekte Schreibweise aus der Liste
|
final_branch = allowed_branches_lookup[suggested_branch_lower]
|
||||||
logger.debug(f"ChatGPT-Branchenvorschlag '{suggested_branch}' ist gueltig ('{final_branch}').")
|
logger.debug(f"ChatGPT-Branchenvorschlag '{suggested_branch}' ist gueltig ('{final_branch}').")
|
||||||
result["consistency"] = "pending_comparison" # Temporaer Status vor Vergleich mit CRM
|
result["consistency"] = "pending_comparison"
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# --- Fallback-Logik, wenn Vorschlag ungueltig ist ---
|
logger.debug(f"ChatGPT-Branchenvorschlag '{suggested_branch}' ist NICHT im Ziel-Schema. Starte Fallback...")
|
||||||
logger.debug(f"ChatGPT-Branchenvorschlag '{suggested_branch}' ist NICHT im Ziel-Schema ({len(ALLOWED_TARGET_BRANCHES)} Eintraege). Starte Fallback...")
|
crm_short_branch = "k.A."
|
||||||
|
if crm_branche and isinstance(crm_branche, str):
|
||||||
|
parts = crm_branche.split(">", 1)
|
||||||
|
crm_short_branch = parts[1].strip() if len(parts) > 1 else crm_branche.strip()
|
||||||
|
|
||||||
# Versuche Kurzform aus CRM-Branche zu extrahieren
|
|
||||||
crm_short_branch = "k.A." # Default
|
|
||||||
# Stellen Sie sicher, dass crm_branche ein String ist
|
|
||||||
if crm_branche and isinstance(crm_branche, str) and ">" in crm_branche:
|
|
||||||
crm_short_branch = crm_branche.split(">", 1)[1].strip()
|
|
||||||
# Wenn CRM schon Kurzform sein koennte (nicht leer/k.A. und kein Praefix > enthalten)
|
|
||||||
elif crm_branche and isinstance(crm_branche, str) and crm_branche.strip() and crm_branche.strip().lower() != "k.a.":
|
|
||||||
crm_short_branch = crm_branche.strip()
|
|
||||||
|
|
||||||
logger.debug(f" Fallback: Pruefe extrahierte CRM-Kurzform: '{crm_short_branch}'")
|
|
||||||
crm_short_branch_lower = crm_short_branch.lower()
|
crm_short_branch_lower = crm_short_branch.lower()
|
||||||
|
|
||||||
# 2. Ist die extrahierte CRM-Kurzform EXAKT im Ziel-Schema enthalten?
|
|
||||||
if crm_short_branch != "k.A." and crm_short_branch_lower in allowed_branches_lookup:
|
if crm_short_branch != "k.A." and crm_short_branch_lower in allowed_branches_lookup:
|
||||||
final_branch = allowed_branches_lookup[crm_short_branch_lower] # Nimm korrekte Schreibweise
|
final_branch = allowed_branches_lookup[crm_short_branch_lower]
|
||||||
result["consistency"] = "fallback_crm_valid" # Setze Fallback-Status
|
result["consistency"] = "fallback_crm_valid"
|
||||||
# Kombiniere ChatGPT Begruendung (falls vorhanden) mit Fallback-Info
|
|
||||||
fallback_reason = f"Fallback: Ungueltiger ChatGPT-Vorschlag ('{suggested_branch}'). Gueltige CRM-Kurzform '{final_branch}' verwendet."
|
fallback_reason = f"Fallback: Ungueltiger ChatGPT-Vorschlag ('{suggested_branch}'). Gueltige CRM-Kurzform '{final_branch}' verwendet."
|
||||||
result["justification"] = f"{fallback_reason} (ChatGPT Begruendung war: {result.get('justification', 'Keine')})"
|
result["justification"] = f"{fallback_reason} (ChatGPT Begruendung war: {result.get('justification', 'Keine')})"
|
||||||
logger.info(f"Fallback auf gueltige CRM-Kurzform erfolgreich: '{final_branch}'")
|
logger.info(f"Fallback auf gueltige CRM-Kurzform erfolgreich: '{final_branch}'")
|
||||||
else:
|
else:
|
||||||
# 3. Wenn auch CRM-Kurzform ungueltig
|
final_branch = suggested_branch # Behalte ungueltigen Vorschlag für Info, wird aber als Fehler markiert
|
||||||
final_branch = suggested_branch # Behalte ungueltigen Vorschlag
|
result["consistency"] = "fallback_invalid"
|
||||||
result["consistency"] = "fallback_invalid" # Setze Fehler-Fallback-Status
|
error_reason = f"Fehler: Ungueltiger ChatGPT-Vorschlag ('{suggested_branch}') und keine gueltige CRM-Kurzform ('{crm_short_branch}') als Fallback."
|
||||||
error_reason = f"Fehler: Ungueltiger ChatGPT-Vorschlag ('{suggested_branch}') und keine gueltige CRM-Kurzform ('{crm_short_branch}') als Fallback verfuegbar."
|
|
||||||
result["justification"] = f"{error_reason} (ChatGPT Begruendung war: {result.get('justification', 'Keine')})"
|
result["justification"] = f"{error_reason} (ChatGPT Begruendung war: {result.get('justification', 'Keine')})"
|
||||||
logger.warning(f"Fallback fehlgeschlagen. Ungueltiger Vorschlag: '{final_branch}', Ungueltige CRM-Kurzform: '{crm_short_branch}'")
|
logger.warning(f"Fallback fehlgeschlagen. Ungueltiger Vorschlag: '{final_branch}', Ungueltige CRM-Kurzform: '{crm_short_branch}'")
|
||||||
# Alternativ: Setze final_branch auf einen expliziten Fehlerwert, um es im Sheet hervorzuheben
|
final_branch = "FEHLER - UNGUELTIGE ZUWEISUNG" # Expliziter Fehlerwert
|
||||||
# final_branch = "FEHLER - UNGUELTIGE ZUWEISUNG" # Optional
|
|
||||||
|
|
||||||
# Setze den finalen Branch im Ergebnis-Dictionary
|
|
||||||
# Verwenden Sie einen Standard-Fehlerwert, falls final_branch aus irgendeinem Grund immer noch None ist
|
|
||||||
result["branch"] = final_branch if final_branch else "FEHLER"
|
result["branch"] = final_branch if final_branch else "FEHLER"
|
||||||
|
|
||||||
# --- Konsistenzpruefung (Finale Bewertung des final_branch vs. CRM-Kurzform) ---
|
|
||||||
# Extrahiere CRM-Kurzform fuer den Vergleich (erneut oder Variable von oben)
|
|
||||||
crm_short_to_compare = "k.A."
|
crm_short_to_compare = "k.A."
|
||||||
if crm_branche and isinstance(crm_branche, str) and ">" in crm_branche:
|
if crm_branche and isinstance(crm_branche, str):
|
||||||
crm_short_to_compare = crm_branche.split(">", 1)[1].strip()
|
parts = crm_branche.split(">", 1)
|
||||||
elif crm_branche and isinstance(crm_branche, str) and crm_branche.strip() and crm_branche.strip().lower() != "k.a.":
|
crm_short_to_compare = parts[1].strip() if len(parts) > 1 else crm_branche.strip()
|
||||||
crm_short_to_compare = crm_branche.strip()
|
|
||||||
|
|
||||||
|
if result["consistency"] == "pending_comparison" and result["branch"] != "FEHLER" and not result["branch"].startswith("FEHLER"):
|
||||||
# Vergleiche finalen Branch (falls nicht FEHLER) mit CRM-Kurzform (case-insensitive)
|
|
||||||
# Aktualisiere den Consistency-Status, WENN er noch 'pending_comparison' ist.
|
|
||||||
# Fallback-Status ('fallback_crm_valid', 'fallback_invalid') sollen erhalten bleiben.
|
|
||||||
if result["consistency"] == "pending_comparison" and result["branch"] != "FEHLER":
|
|
||||||
if result["branch"].lower() == crm_short_to_compare.lower():
|
if result["branch"].lower() == crm_short_to_compare.lower():
|
||||||
result["consistency"] = "ok" # Uebereinstimmung mit CRM
|
result["consistency"] = "ok"
|
||||||
else:
|
else:
|
||||||
result["consistency"] = "X" # Keine Uebereinstimmung mit CRM
|
result["consistency"] = "X"
|
||||||
|
|
||||||
|
|
||||||
# Entferne den temporaeren Status, falls er noch da ist (sollte nicht passieren)
|
|
||||||
if result["consistency"] == "pending_comparison":
|
if result["consistency"] == "pending_comparison":
|
||||||
logger.warning("Konsistenzpruefung blieb im Status 'pending_comparison', setze auf 'error_comparison_failed'.")
|
|
||||||
result["consistency"] = "error_comparison_failed"
|
result["consistency"] = "error_comparison_failed"
|
||||||
elif result["consistency"] is None: # Sollte nicht passieren
|
elif result["consistency"] is None:
|
||||||
logger.error("Konsistenz blieb unerwartet None, setze auf 'error_unknown_state'.")
|
|
||||||
result["consistency"] = "error_unknown_state"
|
result["consistency"] = "error_unknown_state"
|
||||||
|
|
||||||
|
|
||||||
# Debug-Ausgabe des finalen Ergebnisses vor Rueckgabe
|
|
||||||
logger.debug(f"Finale Branch-Evaluation Ergebnis: Branch='{result.get('branch')}', Consistency='{result.get('consistency')}', Justification='{result.get('justification', '')[:100]}...'")
|
logger.debug(f"Finale Branch-Evaluation Ergebnis: Branch='{result.get('branch')}', Consistency='{result.get('consistency')}', Justification='{result.get('justification', '')[:100]}...'")
|
||||||
|
return result
|
||||||
return result # Rueckgabe des Ergebnis-Dictionarys
|
|
||||||
|
|
||||||
|
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
|
|||||||
Reference in New Issue
Block a user