From 785c9f9c9c5dfba994868c3aa4fb501fa8b2e370 Mon Sep 17 00:00:00 2001 From: Floke Date: Thu, 18 Sep 2025 12:22:33 +0000 Subject: [PATCH] v1.2.0 - Bugfix & Robuste Branchen-Regel-Erkennung MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Bugfix: Behebt den Fehler, bei dem keine branchenspezifischen Regeln generiert wurden, weil die Schwellenwerte zu restriktiv waren. - Die Schwellenwerte für die minimale Sample-Anzahl und die prozentuale Branchen-Reinheit wurden gelockert und sind nun am Anfang des Skripts konfigurierbar. - Verbessertes Logging: Das Skript gibt nun detailliert Auskunft, warum ein Department als branchenspezifisch eingestuft oder warum es verworfen wurde. --- knowledge_base_builder.py | 48 +++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/knowledge_base_builder.py b/knowledge_base_builder.py index 10d27479..b445618a 100644 --- a/knowledge_base_builder.py +++ b/knowledge_base_builder.py @@ -1,6 +1,6 @@ # knowledge_base_builder.py -__version__ = "v1.1.0" +__version__ = "v1.2.0" import logging import json @@ -17,13 +17,10 @@ EXACT_MATCH_OUTPUT_FILE = "exact_match_map.json" KEYWORD_RULES_OUTPUT_FILE = "keyword_rules.json" DEPARTMENT_PRIORITIES = { - # --- Tier 1: Ultra-spezifische Nischen (höchste Priorität) --- "Fuhrparkmanagement": 1, "Legal": 1, "Baustofflogistik": 1, "Baustoffherstellung": 1, - - # --- Tier 2: Kern-Fachbereiche (sortiert nach Kontakthäufigkeit) --- "Field Service Management / Kundenservice": 2, "IT": 3, "Production Maintenance / Wartung Produktion": 4, @@ -32,21 +29,14 @@ DEPARTMENT_PRIORITIES = { "Supply Chain Management": 7, "Finanzen": 8, "Technik": 8, - - # --- Tier 3: Übergreifende & Allgemeine Funktionen --- "Management / GF / C-Level": 10, "Logistik": 11, "Vertrieb": 12, "Transportwesen": 13, - - # --- Tier 4: Auffang-Kategorien (niedrigste Priorität) --- "Berater": 20, "Undefined": 99 } -# NEU: Definition von Branchen-Gruppen für die kontextsensitive Regelerstellung -# Key: Ein einfaches, normalisiertes Schlüsselwort für die Gruppe -# Value: Eine Liste von d365_branch_detail Werten aus Ihrer config.py BRANCH_GROUP_RULES = { "bau": [ "Baustoffhandel", "Baustoffindustrie", @@ -63,8 +53,12 @@ BRANCH_GROUP_RULES = { "Braune & Weiße Ware", "Fenster / Glas", "Getränke", "Möbel", "Agrar, Pellets" ] } -# Schwellenwert: Wenn >X% der Jobtitel eines Departments in einer Branchengruppe liegen, wird es spezifisch -BRANCH_SPECIFICITY_THRESHOLD = 0.8 + +# --- NEU: Angepasste und konfigurierbare Schwellenwerte --- +# Ein Department muss mindestens so viele Einträge haben, um eine Branchen-Regel zu bekommen. +MIN_SAMPLES_FOR_BRANCH_RULE = 5 +# Wenn >X% der Jobtitel eines Departments in EINER Branchengruppe liegen, gilt es als spezifisch. +BRANCH_SPECIFICITY_THRESHOLD = 0.7 STOP_WORDS = { 'manager', 'leiter', 'head', 'lead', 'senior', 'junior', 'direktor', 'director', @@ -77,12 +71,7 @@ STOP_WORDS = { def build_knowledge_base(): - """ - Hauptfunktion zur Erstellung der Wissensbasis. - Liest Rohdaten, analysiert sie und erstellt JSON-Dateien für exakte und Keyword-basierte Übereinstimmungen. - Erstellt automatisch Regeln für branchenspezifische Departments. - """ - logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') + logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s') logger = logging.getLogger(__name__) logger.info(f"Starte Erstellung der Wissensbasis (Version {__version__})...") @@ -90,7 +79,7 @@ def build_knowledge_base(): df = gsh.get_sheet_as_dataframe(SOURCE_SHEET_NAME) if df is None or df.empty: - logger.critical(f"Konnte keine Daten aus '{SOURCE_SHEET_NAME}' laden oder das Tabellenblatt ist leer. Abbruch.") + logger.critical(f"Konnte keine Daten aus '{SOURCE_SHEET_NAME}' laden. Abbruch.") return df.columns = [col.strip() for col in df.columns] @@ -112,7 +101,7 @@ def build_knowledge_base(): try: with open(EXACT_MATCH_OUTPUT_FILE, 'w', encoding='utf-8') as f: json.dump(exact_match_map, f, indent=4, ensure_ascii=False) - logger.info(f"-> '{EXACT_MATCH_OUTPUT_FILE}' mit {len(exact_match_map)} einzigartigen Jobtiteln erfolgreich erstellt.") + logger.info(f"-> '{EXACT_MATCH_OUTPUT_FILE}' mit {len(exact_match_map)} Titeln erstellt.") except IOError as e: logger.error(f"Fehler beim Schreiben der Datei '{EXACT_MATCH_OUTPUT_FILE}': {e}") return @@ -141,7 +130,8 @@ def build_knowledge_base(): department_branches = branches_by_department.get(department, []) total_titles_in_dept = len(department_branches) - if total_titles_in_dept > 10: # Mindestanzahl an Datenpunkten, um eine Regel zu erstellen + # Angepasste Logik mit transparentem Logging + if total_titles_in_dept >= MIN_SAMPLES_FOR_BRANCH_RULE: branch_group_counts = Counter() for branch_name in department_branches: for group_keyword, d365_names in BRANCH_GROUP_RULES.items(): @@ -150,16 +140,24 @@ def build_knowledge_base(): if branch_group_counts: most_common_group, count = branch_group_counts.most_common(1)[0] - if (count / total_titles_in_dept) > BRANCH_SPECIFICITY_THRESHOLD: - logger.info(f" -> Department '{department}' ist spezifisch für Branche '{most_common_group}' ({count/total_titles_in_dept:.0%}). Regel wird hinzugefügt.") + ratio = count / total_titles_in_dept + if ratio > BRANCH_SPECIFICITY_THRESHOLD: + logger.info(f" -> Department '{department}' ist spezifisch für Branche '{most_common_group}' ({ratio:.0%}). Regel wird hinzugefügt.") rule["required_branch_keywords"] = [most_common_group] + else: + logger.debug(f" -> Department '{department}' nicht spezifisch genug. Dominante Branche '{most_common_group}' nur bei {ratio:.0%}, benötigt >{BRANCH_SPECIFICITY_THRESHOLD:.0%}.") + else: + logger.debug(f" -> Department '{department}' konnte keiner Branchen-Gruppe zugeordnet werden.") + else: + logger.debug(f" -> Department '{department}' hat zu wenige Datenpunkte ({total_titles_in_dept} < {MIN_SAMPLES_FOR_BRANCH_RULE}) für eine Branchen-Regel.") + keyword_rules[department] = rule try: with open(KEYWORD_RULES_OUTPUT_FILE, 'w', encoding='utf-8') as f: json.dump(keyword_rules, f, indent=4, ensure_ascii=False) - logger.info(f"-> '{KEYWORD_RULES_OUTPUT_FILE}' mit Regeln für {len(keyword_rules)} Departments erfolgreich erstellt.") + logger.info(f"-> '{KEYWORD_RULES_OUTPUT_FILE}' mit Regeln für {len(keyword_rules)} Departments erstellt.") except IOError as e: logger.error(f"Fehler beim Schreiben der Datei '{KEYWORD_RULES_OUTPUT_FILE}': {e}") return