feat(profile): P14+P15+P16 — B2B-Heuristik + Doc-URL-Dedup + Homepage-Profile
P14 — _detect_no_direct_sales erweitert um 3 Cluster: A) OEM-Konfigurator (BMW/Audi/Mercedes/VW/Porsche-Markennamen + Vertragshaendler-Pattern) B) B2B-Dienstleister (CE-Zertifizierung, Compliance-Beratung, Schulungen, Auditierung, TISAX, ISO-Normen, Arbeitssicherheit, ...) C) NGO/Verein/Public (Spendenkonto, Vereinsregister, gemeinnuetzig, ...) Schwelle: pos >= 2 pro Cluster UND pos > neg. Bisher: nur OEM. P15 — Doc-URL-Dedup im Worker: wenn mehrere Doc-Types DASSELBE Dokument referenzieren (Safetykon-Pattern: User gibt /datenschutz fuer dse, cookie UND widerruf), wird nur dem primaeren Doc-Type (Priority: dse > impressum > cookie > widerruf > agb > nutzungsbedingungen) der Text gegeben. Andere landen als "Nicht separat vorhanden — wird im Dokument 'X' mit-geprueft." Eliminiert die 8+8 systematischen widerruf/cookie False Positives. P16 — Profile-Detection auch Homepage-Text: Homepage-HTML wird mit kurzem Fetch (8s timeout) gezogen, getrippt und zum profile_input gemerged. Vor- her wirkte P14 nur wenn B2B-Indikatoren im DSE/Impressum-Pflichttext standen — bei Safetykon stehen sie nur im Homepage-Menue. Plus Bonus: TDM-Override-Submit-Button wird deaktiviert wenn Reason < 10 Zeichen — verhindert dass User wie heute in den Bug rein klickt. Smoke-Test Safetykon (B2B Compliance-Dienstleister): dse geprueft (kein err) impressum geprueft (kein err) cookie "Nicht separat vorhanden — wird in DSE mit-geprueft" agb "Nicht anwendbar — kein Direkt-Kaufvertrag" widerruf "Nicht anwendbar — kein Direkt-Kaufvertrag" nutzungsbedingungen "Nicht anwendbar — kein Direkt-Kaufvertrag" Vorher: 16 False Positives. Jetzt: 0. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -335,9 +335,10 @@ async def detect_business_profile(documents: dict[str, str]) -> BusinessProfile:
|
||||
return profile
|
||||
|
||||
|
||||
# Indikatoren: Site verweist primaer auf Vertragshaendler/Niederlassungen
|
||||
# statt einen eigenen Checkout-Vertragsabschluss zu bieten.
|
||||
_NO_DIRECT_SALES_POSITIVE = [
|
||||
# P14: drei Cluster die jeweils unabhaengig no_direct_sales=True triggern.
|
||||
|
||||
# Cluster A: OEM-Konfigurator-Pattern (Auto-Hersteller mit Vertragshaendler-Netz)
|
||||
_OEM_POSITIVE = [
|
||||
"vertragshaendler", "vertragshändler", "vertragspartner",
|
||||
"vertragswerkstatt", "haendlersuche", "händlersuche",
|
||||
"niederlassung", "vertretung", "autorisierter haendler",
|
||||
@@ -347,27 +348,80 @@ _NO_DIRECT_SALES_POSITIVE = [
|
||||
"anfrage an haendler", "anfrage an händler",
|
||||
"konfigurator", "fahrzeug konfigurieren",
|
||||
"ihre individuelle anfrage",
|
||||
# OEM-Markennamen — sind Hersteller-Marken die ueblicherweise via
|
||||
# Haendler vertreiben.
|
||||
"bmw vertriebs", "audi vertriebs", "mercedes-benz vertriebs",
|
||||
"volkswagen vertriebs", "porsche zentrum",
|
||||
# OEM-Markennamen im Pflichttext (Datenschutz erwaehnt Hersteller)
|
||||
"bmw ag", "audi ag", "mercedes-benz ag", "volkswagen ag",
|
||||
"porsche ag", "opel automobile gmbh",
|
||||
]
|
||||
|
||||
# Cluster B: B2B-Dienstleister (Beratung / Compliance / Schulung / CE)
|
||||
_B2B_SERVICE_POSITIVE = [
|
||||
"ce-zertifizierung", "ce zertifizierung",
|
||||
"ce-konformitaet", "ce-konformität",
|
||||
"ce-kennzeichnung", "ce kennzeichnung",
|
||||
"compliance-beratung", "compliance beratung",
|
||||
"arbeitssicherheit", "product compliance",
|
||||
"produktsicherheit", "produkthaftung",
|
||||
"auditierung", "auditor", "auditierungen",
|
||||
"schulungen", "workshops", "akademie",
|
||||
"beratungsleistungen", "consultingleistungen",
|
||||
"consulting services", "managementsystem",
|
||||
"datenschutzbeauftragter (extern)",
|
||||
"externer datenschutzbeauftragter",
|
||||
"datenschutz-audit", "tisax", "iso 27001",
|
||||
"iso 9001", "iso 14001", "iso 45001",
|
||||
"gefaehrdungsbeurteilung", "gefährdungsbeurteilung",
|
||||
"betriebsbeauftragter", "fachkraft fuer arbeitssicherheit",
|
||||
"fachkraft für arbeitssicherheit",
|
||||
]
|
||||
|
||||
# Cluster C: NGO / Verein / oeffentliche Verwaltung
|
||||
_NONPROFIT_PUBLIC_POSITIVE = [
|
||||
"spendenkonto", "vereinsregister", "gemeinnuetzig",
|
||||
"gemeinnützig", "ehrenamtlich", "foerderverein",
|
||||
"förderverein", "stiftung", "buergeramt", "bürgeramt",
|
||||
"landratsamt", "kommunalverwaltung",
|
||||
]
|
||||
|
||||
# Backwards-compat
|
||||
_NO_DIRECT_SALES_POSITIVE = (
|
||||
_OEM_POSITIVE + _B2B_SERVICE_POSITIVE + _NONPROFIT_PUBLIC_POSITIVE
|
||||
)
|
||||
|
||||
# Indikatoren GEGEN no_direct_sales: echte Online-Shop-Funktionen.
|
||||
_DIRECT_SALES_NEGATIVE = [
|
||||
"in den warenkorb", "warenkorb hinzu", "zur kasse",
|
||||
"jetzt kaufen", "kostenpflichtig bestellen",
|
||||
"zahlungspflichtig bestellen", "sofort-kauf",
|
||||
"online bestellen", "lieferadresse", "rechnungsadresse",
|
||||
"versandkosten", "lieferzeit", "lieferbedingungen",
|
||||
"checkout", "stueckpreis", "stückpreis",
|
||||
]
|
||||
|
||||
|
||||
def _detect_no_direct_sales(full_text: str) -> bool:
|
||||
"""Heuristik: erkennt OEM-Konfigurator-Sites die nicht direkt verkaufen."""
|
||||
"""Heuristik: True wenn Site keinen Direkt-Vertrieb mit B2C-Kunden hat.
|
||||
|
||||
Trifft fuer 3 Cluster zu (jeweils mind. 2 Treffer im Cluster):
|
||||
A) OEM-Konfigurator (Auto-Hersteller)
|
||||
B) B2B-Dienstleister (Beratung/Compliance/Schulung)
|
||||
C) NGO / oeffentliche Verwaltung
|
||||
|
||||
Negativ-Signale (echte Shop-Funktionen) zaehlen gegen den Cluster:
|
||||
nur True wenn pos > neg.
|
||||
"""
|
||||
text = full_text.lower()
|
||||
pos = sum(1 for k in _NO_DIRECT_SALES_POSITIVE if k in text)
|
||||
oem = sum(1 for k in _OEM_POSITIVE if k in text)
|
||||
b2b = sum(1 for k in _B2B_SERVICE_POSITIVE if k in text)
|
||||
npg = sum(1 for k in _NONPROFIT_PUBLIC_POSITIVE if k in text)
|
||||
neg = sum(1 for k in _DIRECT_SALES_NEGATIVE if k in text)
|
||||
# Mindestens 3 Haendler-Indikatoren UND weniger Shop-Indikatoren als
|
||||
# Haendler-Indikatoren. Vermeidet false-positive fuer Shops die
|
||||
# zusaetzlich "Haendlersuche" als Filiale-Finder anbieten.
|
||||
return pos >= 3 and pos > neg
|
||||
# Jeder Cluster ist eigenstaendig: 2 Treffer + weniger Negativ-Signale
|
||||
# als Cluster-Treffer.
|
||||
if oem >= 2 and oem > neg:
|
||||
return True
|
||||
if b2b >= 2 and b2b > neg:
|
||||
return True
|
||||
if npg >= 2 and npg > neg:
|
||||
return True
|
||||
return False
|
||||
|
||||
Reference in New Issue
Block a user