third step
This commit is contained in:
@@ -4,71 +4,72 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Config;
|
||||
|
||||
class IntentLightConfig
|
||||
/**
|
||||
* YAML-backed deterministic list-intent configuration.
|
||||
*
|
||||
* This class intentionally has no PHP fallback values. Missing or invalid
|
||||
* configuration must be fixed in config/retriex/intent.yaml.
|
||||
*/
|
||||
final class IntentLightConfig
|
||||
{
|
||||
public const LIST_THRESHOLD = 4;
|
||||
|
||||
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 = [])
|
||||
public function __construct(private readonly array $config)
|
||||
{
|
||||
}
|
||||
|
||||
public function getListThreshold(): int
|
||||
{
|
||||
return $this->requiredPositiveInt('list_threshold');
|
||||
}
|
||||
|
||||
/** @return string[] */
|
||||
public function getQuantityWords(): array
|
||||
{
|
||||
return $this->stringList('quantity_words', self::QUANTITY_WORDS);
|
||||
return $this->requiredStringList('quantity_words');
|
||||
}
|
||||
|
||||
/** @return string[] */
|
||||
public function getStrongPatterns(): array
|
||||
{
|
||||
return $this->stringList('strong_patterns', self::STRONG_PATTERNS);
|
||||
return $this->requiredStringList('strong_patterns');
|
||||
}
|
||||
|
||||
private function requiredPositiveInt(string $key): int
|
||||
{
|
||||
if (!array_key_exists($key, $this->config)) {
|
||||
throw new \InvalidArgumentException(sprintf('Missing required RetrieX light intent config key "%s".', $key));
|
||||
}
|
||||
|
||||
$value = $this->config[$key];
|
||||
|
||||
if (is_int($value)) {
|
||||
$intValue = $value;
|
||||
} elseif (is_string($value) && preg_match('/^-?\d+$/', trim($value)) === 1) {
|
||||
$intValue = (int) trim($value);
|
||||
} else {
|
||||
throw new \InvalidArgumentException(sprintf('RetrieX light intent config key "%s" must be an integer.', $key));
|
||||
}
|
||||
|
||||
if ($intValue <= 0) {
|
||||
throw new \InvalidArgumentException(sprintf('RetrieX light intent config key "%s" must be greater than 0.', $key));
|
||||
}
|
||||
|
||||
return $intValue;
|
||||
}
|
||||
|
||||
/** @return string[] */
|
||||
private function stringList(string $key, array $default): array
|
||||
private function requiredStringList(string $key): array
|
||||
{
|
||||
$value = $this->config[$key] ?? $default;
|
||||
if (!array_key_exists($key, $this->config)) {
|
||||
throw new \InvalidArgumentException(sprintf('Missing required RetrieX light intent config key "%s".', $key));
|
||||
}
|
||||
|
||||
$value = $this->config[$key];
|
||||
|
||||
if (!is_array($value)) {
|
||||
return $default;
|
||||
throw new \InvalidArgumentException(sprintf('RetrieX light intent config key "%s" must be a list.', $key));
|
||||
}
|
||||
|
||||
$out = [];
|
||||
@@ -78,13 +79,19 @@ class IntentLightConfig
|
||||
}
|
||||
|
||||
$item = trim((string) $item);
|
||||
if ($item === '' || in_array($item, $out, true)) {
|
||||
if ($item === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$out[] = $item;
|
||||
if (!in_array($item, $out, true)) {
|
||||
$out[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
return $out !== [] ? $out : $default;
|
||||
if ($out === []) {
|
||||
throw new \InvalidArgumentException(sprintf('RetrieX light intent config key "%s" must not be empty.', $key));
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -658,13 +658,13 @@ final readonly class RetriexEffectiveConfigProvider
|
||||
],
|
||||
],
|
||||
'light' => [
|
||||
'list_threshold' => IntentLightConfig::LIST_THRESHOLD,
|
||||
'list_threshold' => $this->intentLightConfig->getListThreshold(),
|
||||
'quantity_words' => $this->intentLightConfig->getQuantityWords(),
|
||||
'strong_patterns' => $this->intentLightConfig->getStrongPatterns(),
|
||||
],
|
||||
'sales' => [
|
||||
'dominance_delta' => SalesIntentConfig::DOMINANCE_DELTA,
|
||||
'min_score_threshold' => SalesIntentConfig::MIN_SCORE_THRESHOLD,
|
||||
'dominance_delta' => $this->salesIntentConfig->getDominanceDelta(),
|
||||
'min_score_threshold' => $this->salesIntentConfig->getMinScoreThreshold(),
|
||||
'sales_signals' => $this->salesIntentConfig->getSalesSignals(),
|
||||
'comparison_signals' => $this->salesIntentConfig->getComparisonSignals(),
|
||||
'objection_signals' => $this->salesIntentConfig->getObjectionSignals(),
|
||||
|
||||
@@ -4,123 +4,95 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Config;
|
||||
|
||||
class SalesIntentConfig
|
||||
/**
|
||||
* YAML-backed deterministic sales-intent configuration.
|
||||
*
|
||||
* This class intentionally has no PHP fallback values. Missing or invalid
|
||||
* configuration must be fixed in config/retriex/intent.yaml.
|
||||
*/
|
||||
final 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 = [])
|
||||
public function __construct(private readonly array $config)
|
||||
{
|
||||
}
|
||||
|
||||
public function getDominanceDelta(): int
|
||||
{
|
||||
return $this->requiredNonNegativeInt('dominance_delta');
|
||||
}
|
||||
|
||||
public function getMinScoreThreshold(): int
|
||||
{
|
||||
return $this->requiredNonNegativeInt('min_score_threshold');
|
||||
}
|
||||
|
||||
/** @return string[] */
|
||||
public function getSalesSignals(): array
|
||||
{
|
||||
return $this->stringList('sales_signals', self::SALES_SIGNALS);
|
||||
return $this->requiredStringList('sales_signals');
|
||||
}
|
||||
|
||||
/** @return string[] */
|
||||
public function getComparisonSignals(): array
|
||||
{
|
||||
return $this->stringList('comparison_signals', self::COMPARISON_SIGNALS);
|
||||
return $this->requiredStringList('comparison_signals');
|
||||
}
|
||||
|
||||
/** @return string[] */
|
||||
public function getObjectionSignals(): array
|
||||
{
|
||||
return $this->stringList('objection_signals', self::OBJECTION_SIGNALS);
|
||||
return $this->requiredStringList('objection_signals');
|
||||
}
|
||||
|
||||
/** @return string[] */
|
||||
public function getImplementationSignals(): array
|
||||
{
|
||||
return $this->stringList('implementation_signals', self::IMPLEMENTATION_SIGNALS);
|
||||
return $this->requiredStringList('implementation_signals');
|
||||
}
|
||||
|
||||
/** @return string[] */
|
||||
public function getRoiSignals(): array
|
||||
{
|
||||
return $this->stringList('roi_signals', self::ROI_SIGNALS);
|
||||
return $this->requiredStringList('roi_signals');
|
||||
}
|
||||
|
||||
private function requiredNonNegativeInt(string $key): int
|
||||
{
|
||||
if (!array_key_exists($key, $this->config)) {
|
||||
throw new \InvalidArgumentException(sprintf('Missing required RetrieX sales intent config key "%s".', $key));
|
||||
}
|
||||
|
||||
$value = $this->config[$key];
|
||||
|
||||
if (is_int($value)) {
|
||||
$intValue = $value;
|
||||
} elseif (is_string($value) && preg_match('/^-?\d+$/', trim($value)) === 1) {
|
||||
$intValue = (int) trim($value);
|
||||
} else {
|
||||
throw new \InvalidArgumentException(sprintf('RetrieX sales intent config key "%s" must be an integer.', $key));
|
||||
}
|
||||
|
||||
if ($intValue < 0) {
|
||||
throw new \InvalidArgumentException(sprintf('RetrieX sales intent config key "%s" must be greater than or equal to 0.', $key));
|
||||
}
|
||||
|
||||
return $intValue;
|
||||
}
|
||||
|
||||
/** @return string[] */
|
||||
private function stringList(string $key, array $default): array
|
||||
private function requiredStringList(string $key): array
|
||||
{
|
||||
$value = $this->config[$key] ?? $default;
|
||||
if (!array_key_exists($key, $this->config)) {
|
||||
throw new \InvalidArgumentException(sprintf('Missing required RetrieX sales intent config key "%s".', $key));
|
||||
}
|
||||
|
||||
$value = $this->config[$key];
|
||||
|
||||
if (!is_array($value)) {
|
||||
return $default;
|
||||
throw new \InvalidArgumentException(sprintf('RetrieX sales intent config key "%s" must be a list.', $key));
|
||||
}
|
||||
|
||||
$out = [];
|
||||
@@ -130,13 +102,19 @@ class SalesIntentConfig
|
||||
}
|
||||
|
||||
$item = trim((string) $item);
|
||||
if ($item === '' || in_array($item, $out, true)) {
|
||||
if ($item === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$out[] = $item;
|
||||
if (!in_array($item, $out, true)) {
|
||||
$out[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
return $out !== [] ? $out : $default;
|
||||
if ($out === []) {
|
||||
throw new \InvalidArgumentException(sprintf('RetrieX sales intent config key "%s" must not be empty.', $key));
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user