central config part 2

This commit is contained in:
team2
2026-04-26 08:34:45 +02:00
parent c72b9c2e2b
commit 00ebe2fd73
8 changed files with 782 additions and 288 deletions

View File

@@ -6,82 +6,165 @@ namespace App\Config;
final class CommerceIntentConfig
{
private const STRONG_SIGNALS = [
'shop',
'alle',
'preis',
'kunde',
'online',
'produkt',
'artikel',
'sku',
'kaufen',
'kostet',
'suche',
'such',
'finde',
'finden',
'analysegerät',
'analysegeraet',
'messgerät',
'messgeraet',
'analysator',
'analyzer',
'puffer',
'kalibrierpuffer',
'kalibrierlösung',
'kalibrierloesung',
'kalibrierung',
'chemie',
'reagenz',
'reagenzien',
'verbrauchsmaterial',
'zubehör',
'zubehoer',
'ersatzteil',
];
private const ADVISORY_SIGNALS = [
'passt',
'eignet',
'besser',
'besten',
'gut für',
'gut fuer',
'passend für',
'passend fuer',
'geeignet',
'geeigent',
'empfiehl',
'empfehl',
];
private const PRICE_TERMS = [
'euro',
'€',
'eur',
'teuer',
'preis',
'kosten',
'kostet',
];
private const COLOR_TERMS = [
'schwarz',
'weiß',
'weis',
'blau',
'grau',
'beige',
'rosa',
'pink',
'gruen',
'orange',
'braun',
];
private const SIZE_TOKEN_TERMS = [
'xs',
's',
'm',
'l',
'xl',
'xxl',
'xxxxl',
];
private const SIZE_TERMS = [
'größe',
'groesse',
'grösse',
];
private const SUPPORT_DIAGNOSTIC_PATTERNS = [
'/\bfehler\b/u',
'/\bfehlercode\b/u',
'/\berror\b/u',
'/\bstörung\b/u',
'/\bstoerung\b/u',
'/\balarm\b/u',
'/\bstörungsmeldung\b/u',
'/\bstoerungsmeldung\b/u',
'/\bmeldung\b/u',
'/\bwarnung\b/u',
'/\bwarncode\b/u',
'/\bcode\b/u',
'/\bwas bedeutet\b/u',
'/\bwarum\b/u',
'/\bblinkt\b/u',
'/\bzeigt\b/u',
'/\bzeigt an\b/u',
'/\bursache\b/u',
'/\bdiagnose\b/u',
'/\bservicefall\b/u',
'/\bproblem\b/u',
'/\bstörung beheben\b/u',
'/\bstoerung beheben\b/u',
'/\be\d{1,3}\b/u',
];
private const EXPLICIT_COMMERCE_INTENT_PATTERNS = [
'/\bshop\b/u',
'/\bpreis\b/u',
'/\bkosten\b/u',
'/\bkostet\b/u',
'/\bkaufen\b/u',
'/\bbestellen\b/u',
'/\bprodukt\b/u',
'/\bartikel\b/u',
'/\bsku\b/u',
'/\bonline\b/u',
'/\bchemie\b/u',
'/\breagenz(?:ien)?\b/u',
'/\bverbrauchsmaterial(?:ien)?\b/u',
'/\bzubehör\b/u',
'/\bzubehoer\b/u',
'/\bersatzteil(?:e)?\b/u',
];
/**
* @return string[]
* @param array<string, mixed> $config
*/
public function __construct(private readonly array $config = [])
{
}
/** @return string[] */
public function getStrongSignalsList(): array
{
return [
'shop',
'alle',
'preis',
'kunde',
'online',
'produkt',
'artikel',
'sku',
'kaufen',
'kostet',
'suche',
'such',
'finde',
'finden',
'analysegerät',
'analysegeraet',
'messgerät',
'messgeraet',
'analysator',
'analyzer',
'puffer',
'kalibrierpuffer',
'kalibrierlösung',
'kalibrierloesung',
'kalibrierung',
'chemie',
'reagenz',
'reagenzien',
'verbrauchsmaterial',
'zubehör',
'zubehoer',
'ersatzteil',
];
return $this->stringList('strong_signals', self::STRONG_SIGNALS);
}
/**
* @return string[]
*/
/** @return string[] */
public function getAdvisorySignals(): array
{
return [
'passt',
'eignet',
'besser',
'besten',
'gut für',
'gut fuer',
'passend für',
'passend fuer',
'geeignet',
'geeigent',
'empfiehl',
'empfehl',
];
return $this->stringList('advisory_signals', self::ADVISORY_SIGNALS);
}
/**
* @return string[]
*/
/** @return string[] */
public function getPriceTerms(): array
{
return [
'euro',
'€',
'eur',
'teuer',
'preis',
'kosten',
'kostet',
];
return $this->stringList('price_terms', self::PRICE_TERMS);
}
public function getPricePattern(): string
@@ -89,24 +172,10 @@ final class CommerceIntentConfig
return implode('|', $this->getPriceTerms());
}
/**
* @return string[]
*/
/** @return string[] */
public function getColorTerms(): array
{
return [
'schwarz',
'weiß',
'weis',
'blau',
'grau',
'beige',
'rosa',
'pink',
'gruen',
'orange',
'braun',
];
return $this->stringList('color_terms', self::COLOR_TERMS);
}
public function getColorPattern(): string
@@ -114,20 +183,10 @@ final class CommerceIntentConfig
return implode('|', $this->getColorTerms());
}
/**
* @return string[]
*/
/** @return string[] */
public function getSizeTokenTerms(): array
{
return [
'xs',
's',
'm',
'l',
'xl',
'xxl',
'xxxxl',
];
return $this->stringList('size_token_terms', self::SIZE_TOKEN_TERMS);
}
public function getSizeTokenPattern(): string
@@ -135,16 +194,10 @@ final class CommerceIntentConfig
return implode('|', $this->getSizeTokenTerms());
}
/**
* @return string[]
*/
/** @return string[] */
public function getSizeTerms(): array
{
return [
'größe',
'groesse',
'grösse',
];
return $this->stringList('size_terms', self::SIZE_TERMS);
}
public function getSizePattern(): string
@@ -157,62 +210,16 @@ final class CommerceIntentConfig
return '/\b(?:' . $this->getSizePattern() . ')\s*([a-z0-9.-]+)\b/u';
}
/**
* @return string[]
*/
/** @return string[] */
public function getSupportDiagnosticPatterns(): array
{
return [
'/\bfehler\b/u',
'/\bfehlercode\b/u',
'/\berror\b/u',
'/\bstörung\b/u',
'/\bstoerung\b/u',
'/\balarm\b/u',
'/\bstörungsmeldung\b/u',
'/\bstoerungsmeldung\b/u',
'/\bmeldung\b/u',
'/\bwarnung\b/u',
'/\bwarncode\b/u',
'/\bcode\b/u',
'/\bwas bedeutet\b/u',
'/\bwarum\b/u',
'/\bblinkt\b/u',
'/\bzeigt\b/u',
'/\bzeigt an\b/u',
'/\bursache\b/u',
'/\bdiagnose\b/u',
'/\bservicefall\b/u',
'/\bproblem\b/u',
'/\bstörung beheben\b/u',
'/\bstoerung beheben\b/u',
'/\be\d{1,3}\b/u',
];
return $this->stringList('support_diagnostic_patterns', self::SUPPORT_DIAGNOSTIC_PATTERNS);
}
/**
* @return string[]
*/
/** @return string[] */
public function getExplicitCommerceIntentPatterns(): array
{
return [
'/\bshop\b/u',
'/\bpreis\b/u',
'/\bkosten\b/u',
'/\bkostet\b/u',
'/\bkaufen\b/u',
'/\bbestellen\b/u',
'/\bprodukt\b/u',
'/\bartikel\b/u',
'/\bsku\b/u',
'/\bonline\b/u',
'/\bchemie\b/u',
'/\breagenz(?:ien)?\b/u',
'/\bverbrauchsmaterial(?:ien)?\b/u',
'/\bzubehör\b/u',
'/\bzubehoer\b/u',
'/\bersatzteil(?:e)?\b/u',
];
return $this->stringList('explicit_commerce_intent_patterns', self::EXPLICIT_COMMERCE_INTENT_PATTERNS);
}
public function getSkuLikePattern(): string
@@ -334,4 +341,29 @@ final class CommerceIntentConfig
{
return 3;
}
}
/** @return string[] */
private function stringList(string $key, array $default): array
{
$value = $this->config[$key] ?? $default;
if (!is_array($value)) {
return $default;
}
$out = [];
foreach ($value as $item) {
if (!is_scalar($item)) {
continue;
}
$item = trim((string) $item);
if ($item === '' || in_array($item, $out, true)) {
continue;
}
$out[] = $item;
}
return $out !== [] ? $out : $default;
}
}

View File

@@ -1,50 +1,90 @@
<?php
declare(strict_types=1);
namespace App\Config;
class IntentLightConfig
{
public const LIST_THRESHOLD = 4;
public function getQuantityWords(): array
private const QUANTITY_WORDS = [
'alle',
'sämtliche',
'saemtliche',
'mehrere',
'verschiedene',
'einige',
'viele',
'optionen',
'möglichkeiten',
'moeglichkeiten',
'varianten',
'arten',
'modelle',
'funktionen',
'punkte',
'schritte',
'kategorien',
'übersicht',
'uebersicht',
];
private const STRONG_PATTERNS = [
'/\bliste(n)?\b/u',
'/\bauflisten\b/u',
'/\baufz(a|ä)hl(en)?\b/u',
'/\bnenn(e)?\b/u',
'/\bzeig(e)?\b/u',
'/\bwelche\s+sind\b/u',
'/\bwelche\s+gibt\s+es\b/u',
'/\bwas\s+sind\b/u',
'/\bwie\s+viele\b/u',
'/\branking\b/u',
'/\btop\s*\d+\b/u',
];
/**
* @param array<string, mixed> $config
*/
public function __construct(private readonly array $config = [])
{
return [
'alle',
'sämtliche',
'saemtliche',
'mehrere',
'verschiedene',
'einige',
'viele',
'optionen',
'möglichkeiten',
'moeglichkeiten',
'varianten',
'arten',
'modelle',
'funktionen',
'punkte',
'schritte',
'kategorien',
'übersicht',
'uebersicht',
];
}
/** @return string[] */
public function getQuantityWords(): array
{
return $this->stringList('quantity_words', self::QUANTITY_WORDS);
}
/** @return string[] */
public function getStrongPatterns(): array
{
return [
'/\bliste(n)?\b/u',
'/\bauflisten\b/u',
'/\baufz(a|ä)hl(en)?\b/u',
'/\bnenn(e)?\b/u',
'/\bzeig(e)?\b/u',
'/\bwelche\s+sind\b/u',
'/\bwelche\s+gibt\s+es\b/u',
'/\bwas\s+sind\b/u',
'/\bwie\s+viele\b/u',
'/\branking\b/u',
'/\btop\s*\d+\b/u',
];
return $this->stringList('strong_patterns', self::STRONG_PATTERNS);
}
}
/** @return string[] */
private function stringList(string $key, array $default): array
{
$value = $this->config[$key] ?? $default;
if (!is_array($value)) {
return $default;
}
$out = [];
foreach ($value as $item) {
if (!is_scalar($item)) {
continue;
}
$item = trim((string) $item);
if ($item === '' || in_array($item, $out, true)) {
continue;
}
$out[] = $item;
}
return $out !== [] ? $out : $default;
}
}

View File

@@ -1,65 +1,142 @@
<?php
declare(strict_types=1);
namespace App\Config;
class SalesIntentConfig
{
// Minimum gap between Top 1 and Top 2 so that an intent is truly dominant.
public const DOMINANCE_DELTA = 2;
// Minimum score required for any non-discovery intent to be accepted.
public const MIN_SCORE_THRESHOLD = 3;
private const SALES_SIGNALS = [
'preis',
'preise',
'kosten',
'lizenz',
'lizenzmodell',
'tarif',
'tarife',
'gebuehr',
'gebühr',
'monatlich',
'jaehrlich',
'jährlich',
'abo',
'subscription',
];
private const COMPARISON_SIGNALS = [
'/\bvergleich(en)?\b/u',
'/\bvs\b/u',
'/\bgegenueber\b/u',
'/\balternative(n)?\b/u',
'/\bunterschied(e)?\b/u',
'/\bbesser\b/u',
];
private const OBJECTION_SIGNALS = [
'problem',
'risiko',
'nachteil',
'datenschutz',
'dsgvo',
'sicherheit',
'compliance',
'kritik',
'zweifel',
'unsicher',
];
private const IMPLEMENTATION_SIGNALS = [
'implementierung',
'implementieren',
'integration',
'integrieren',
'einführung',
'einfuehrung',
'aufwand',
'setup',
'rollout',
'migration',
'installation',
'api',
'schnittstelle',
];
private const ROI_SIGNALS = [
'roi',
'rentabilitaet',
'rentabilität',
'business case',
'einsparung',
'kosten senken',
'umsatz steigern',
'effizienz steigern',
];
/**
* @param array<string, mixed> $config
*/
public function __construct(private readonly array $config = [])
{
}
/** @return string[] */
public function getSalesSignals(): array
{
return [
'preis', 'preise', 'kosten', 'lizenz', 'lizenzmodell',
'tarif', 'tarife', 'gebuehr', 'gebühr',
'monatlich', 'jaehrlich', 'jährlich', 'abo', 'subscription'
];
return $this->stringList('sales_signals', self::SALES_SIGNALS);
}
/** @return string[] */
public function getComparisonSignals(): array
{
return [
'/\bvergleich(en)?\b/u',
'/\bvs\b/u',
'/\bgegenueber\b/u',
'/\balternative(n)?\b/u',
'/\bunterschied(e)?\b/u',
'/\bbesser\b/u'
];
return $this->stringList('comparison_signals', self::COMPARISON_SIGNALS);
}
/** @return string[] */
public function getObjectionSignals(): array
{
return [
'problem', 'risiko', 'nachteil', 'datenschutz',
'dsgvo', 'sicherheit', 'compliance',
'kritik', 'zweifel', 'unsicher'
];
}
public function getImplementationSignals(): array
{
return [
'implementierung', 'implementieren',
'integration', 'integrieren',
'einführung', 'einfuehrung',
'aufwand', 'setup', 'rollout',
'migration', 'installation',
'api', 'schnittstelle'
];
}
public function getRoiSignals(): array
{
return [
'roi', 'rentabilitaet', 'rentabilität',
'business case', 'einsparung',
'kosten senken', 'umsatz steigern',
'effizienz steigern'
];
return $this->stringList('objection_signals', self::OBJECTION_SIGNALS);
}
}
/** @return string[] */
public function getImplementationSignals(): array
{
return $this->stringList('implementation_signals', self::IMPLEMENTATION_SIGNALS);
}
/** @return string[] */
public function getRoiSignals(): array
{
return $this->stringList('roi_signals', self::ROI_SIGNALS);
}
/** @return string[] */
private function stringList(string $key, array $default): array
{
$value = $this->config[$key] ?? $default;
if (!is_array($value)) {
return $default;
}
$out = [];
foreach ($value as $item) {
if (!is_scalar($item)) {
continue;
}
$item = trim((string) $item);
if ($item === '' || in_array($item, $out, true)) {
continue;
}
$out[] = $item;
}
return $out !== [] ? $out : $default;
}
}

View File

@@ -6,10 +6,67 @@ namespace App\Config;
final class SearchRepairConfig
{
private const GENERIC_CANDIDATE_TOKENS = [
'wasser',
'messgerät',
'messgeraet',
'produkt',
'geräte',
'geraete',
'gerät',
'geraet',
'resthärte',
'resthaerte',
'preis',
'infos',
'wissen',
];
private const ACCESSORY_CANDIDATE_TERMS = [
'indikator',
'indicator',
'reagenz',
'reagent',
'kit',
'set',
];
private const ACCESSORY_OR_BUNDLE_TERMS = [
'passend',
'passende',
'zubehor',
'zubehör',
'dazu',
'zusatz',
'erganzung',
'ergänzung',
'indikator',
'reagenz',
'kit',
'set',
'auch\s+das',
'mit\s+preis\s+und\s+allen\s+infos',
];
private const SPECIFICITY_BOOST_TERMS = [
'indikator',
'indicator',
'testomat',
'tritromat',
'titromat',
'reagenz',
'reagent',
];
/**
* @param array<string, mixed> $config
*/
public function __construct(
private readonly bool $enabled = true,
private readonly int $maxRepairQueries = 3,
private readonly int $minPrimaryResultsWithoutRepair = 2,
private readonly array $config = [],
private readonly ?DomainVocabularyConfig $vocabulary = null,
) {
}
@@ -58,26 +115,13 @@ final class SearchRepairConfig
return '/\b(?:' . implode('|', $this->getSpecificityBoostTerms()) . ')\b/iu';
}
/**
* @return string[]
*/
/** @return string[] */
public function getGenericCandidateTokens(): array
{
return [
'wasser',
'messgerät',
'messgeraet',
'produkt',
'geräte',
'geraete',
'gerät',
'geraet',
'resthärte',
'resthaerte',
'preis',
'infos',
'wissen',
];
return $this->stringList(
'generic_candidate_tokens',
$this->vocabularyView('search_repair.generic_candidate_tokens', self::GENERIC_CANDIDATE_TOKENS)
);
}
public function getSanitizeTrimCharacters(): string
@@ -155,57 +199,61 @@ final class SearchRepairConfig
return 4;
}
/**
* @return string[]
*/
/** @return string[] */
public function getAccessoryCandidateTerms(): array
{
return [
'indikator',
'indicator',
'reagenz',
'reagent',
'kit',
'set',
];
return $this->stringList(
'accessory_candidate_terms',
$this->vocabularyView('search_repair.accessory_candidate_terms', self::ACCESSORY_CANDIDATE_TERMS)
);
}
/**
* @return string[]
*/
/** @return string[] */
public function getAccessoryOrBundleTerms(): array
{
return [
'passend',
'passende',
'zubehor',
'zubehör',
'dazu',
'zusatz',
'erganzung',
'ergänzung',
'indikator',
'reagenz',
'kit',
'set',
'auch\s+das',
'mit\s+preis\s+und\s+allen\s+infos',
];
return $this->stringList(
'accessory_or_bundle_terms',
$this->vocabularyView('search_repair.accessory_or_bundle_terms', self::ACCESSORY_OR_BUNDLE_TERMS)
);
}
/**
* @return string[]
*/
/** @return string[] */
public function getSpecificityBoostTerms(): array
{
return [
'indikator',
'indicator',
'testomat',
'tritromat',
'titromat',
'reagenz',
'reagent',
];
return $this->stringList(
'specificity_boost_terms',
$this->vocabularyView('search_repair.specificity_boost_terms', self::SPECIFICITY_BOOST_TERMS)
);
}
}
/** @return string[] */
private function vocabularyView(string $path, array $fallback): array
{
return $this->vocabulary?->view($path, $fallback) ?? $fallback;
}
/** @return string[] */
private function stringList(string $key, array $default): array
{
$value = $this->config[$key] ?? $default;
if (!is_array($value)) {
return $default;
}
$out = [];
foreach ($value as $item) {
if (!is_scalar($item)) {
continue;
}
$item = trim((string) $item);
if ($item === '' || in_array($item, $out, true)) {
continue;
}
$out[] = $item;
}
return $out !== [] ? $out : $default;
}
}