diff --git a/config/retriex/genre.yaml b/config/retriex/genre.yaml index 20a1789..b03a90c 100644 --- a/config/retriex/genre.yaml +++ b/config/retriex/genre.yaml @@ -1831,8 +1831,9 @@ parameters: - /\b(?:länger|laenger|kürzer|kuerzer|größer|groesser|kleiner|über|ueber|unter|mindestens|maximal|maximum|minimum|ab|bis|mehr\s+als|weniger\s+als)\s+(?P\d+(?:[,.]\d+)?\s*[\p{L}µ°%]*)\b/iu requested_accessory_code_terms: origin: genre_native - terms: - - requested_accessory_code_terms + vocabulary_views: + terms: search_repair.requested_accessory_code_terms + terms: [] candidate_patterns: origin: genre_native specific_model_candidate_patterns: diff --git a/patch_history/RETRIEX_PATCH_92_EXACT_ACCESSORY_CODE_VOCABULARY_GUARD_README.md b/patch_history/RETRIEX_PATCH_92_EXACT_ACCESSORY_CODE_VOCABULARY_GUARD_README.md new file mode 100644 index 0000000..67d8ad3 --- /dev/null +++ b/patch_history/RETRIEX_PATCH_92_EXACT_ACCESSORY_CODE_VOCABULARY_GUARD_README.md @@ -0,0 +1,74 @@ +# RetrieX Patch p92 - Exact Accessory Code Vocabulary Guard + +## Ziel + +Stabilisiert die Preis-/Shop-Antworten für referenzielle Zubehörfragen mit exakt genanntem Code, z. B. den Flow: + +1. `Was ist der niedrigste Grenzwert fuer die Wasserhaerte, welcher mit einem Testomaten ueberwacht werden kann?` +2. `mit welchem indikator` +3. `was kostet der indikator` + +Nach p91 ist die Shopquery korrekt bereinigt (`testomat 808 300 indikator`). p92 sorgt nun dafuer, dass der bestehende Exact-Code-Guard wieder den konkreten Zubehoer-/Indikatorcode `300` erkennt und Shop-Ergebnisse wie `300 S`, `301`, `302` usw. nicht als gleichwertige Preisantworten durchgereicht werden. + +## Ursache + +Die p72-Guard-Logik war noch vorhanden, bekam aber ueber `AgentRunnerConfig::getRequestedAccessoryCodeTerms()` seit der Vocabulary-/View-Zentralisierung nicht mehr die effektiven Begriffe wie `indikator`, `indikatortyp`, `reagenz`, sondern den View-Namen `requested_accessory_code_terms` als Literal aus `genre.yaml`. + +Zusaetzlich konnte die Code-Erkennung bei kombinierten Prompt-/Query-Texten die generische Prompt-Referenz `indikator` mit der Geraetenummer `808` aus der materialisierten Shopquery verbinden. Dadurch wurden alle `Testomat 808 ...`-Treffer als passend betrachtet. + +## Umsetzung + +- `config/retriex/genre.yaml` + - `search_repair.requested_accessory_code_terms` verweist nun explizit ueber `vocabulary_views.terms` auf `search_repair.requested_accessory_code_terms`. + - Die alte Literal-Liste wird als leerer Fallback gehalten. + +- `src/Config/AgentRunnerConfig.php` + - Neuer Helper `getGenreStringListOrVocabularyView()` fuer genre-seitige Werte, die eine Vocabulary-View referenzieren. + - `getRequestedAccessoryCodeTerms()` nutzt jetzt die View und faellt nur noch bei Bedarf auf direkte Genre-Terme zurueck. + +- `src/Agent/AgentRunner.php` + - Exact-Code-Erkennung bevorzugt die bereits bereinigte/materialisierte Shopquery. + - Erst wenn daraus kein Code ermittelt werden kann, wird auf den kombinierten Prompt-/Query-Text zurueckgefallen. + - Damit wird `testomat 808 300 indikator` zu exakt angefordertem Code `300`, nicht zu `808,300`. + +## Lokale Checks + +Ausgefuehrt im Patch-Arbeitsverzeichnis: + +```bash +php -l src/Agent/AgentRunner.php +php -l src/Config/AgentRunnerConfig.php +python3 - <<'PY' +import yaml +for path in ['config/retriex/genre.yaml','config/retriex/vocabulary.yaml']: + yaml.safe_load(open(path, encoding='utf-8')) + print('[OK] yaml', path) +PY +``` + +Zusaetzlicher Smoke-Test per Reflection mit lokalem mbstring-Polyfill: + +- Effektive Requested-Accessory-Code-Terms: `indikatortyp, indikator, indicator, reagenz, reagent` +- Extrahierter Code aus Prompt `was kostet der indikator` + Query `testomat 808 300 indikator`: `300` +- `Testomat 808 Indikator 300 500 ml`: keep +- `Testomat 808 Indikator 300 S 500 ml`: drop +- `Testomat 808 Indikator 301 500 ml`: drop + +## Erwarteter manueller Test + +Der Flow aus dem Nutzerbeispiel soll nach p91 + p92 liefern: + +- Gesendete Suchquery: `testomat 808 300 indikator` +- Antwortpreise nur fuer exakte `Testomat 808 Indikator 300`-Produkte, z. B.: + - `Testomat 808 Indikator 300 500 ml` + - `Testomat 808 Indikator 300 2 x 100 ml` +- Keine gleichwertige Ausgabe von `300 S`, `301`, `302`, `303`, `310`, `320`, `330`, `350` fuer die konkrete Frage nach `Indikatortyp 300`. + +## Nach dem Einspielen in der Zielumgebung ausfuehren + +```bash +bin/console mto:agent:config:validate +bin/console mto:agent:regression:test +bin/console mto:agent:config:audit-source --details +bin/console mto:agent:config:audit-patterns --details +``` diff --git a/src/Agent/AgentRunner.php b/src/Agent/AgentRunner.php index 97df760..f153df8 100644 --- a/src/Agent/AgentRunner.php +++ b/src/Agent/AgentRunner.php @@ -5019,13 +5019,34 @@ final readonly class AgentRunner */ private function extractExactRequestedAccessoryCodes(string $prompt, string $shopSearchQuery): array { - $text = $this->normalizeOneLine(trim($prompt . ' ' . $shopSearchQuery)); - if ($text === '') { + $codeTerms = $this->agentRunnerConfig->getRequestedAccessoryCodeTerms(); + if ($codeTerms === []) { return []; } - $codeTerms = $this->agentRunnerConfig->getRequestedAccessoryCodeTerms(); - if ($codeTerms === []) { + // Prefer the already materialized Shopware query. It is a single, cleaned + // product-intent string and avoids accidentally joining a generic prompt + // reference such as "the indicator" with a device model number from the + // query context. + $codes = $this->extractExactRequestedAccessoryCodesFromText($shopSearchQuery, $codeTerms); + if ($codes !== []) { + return $codes; + } + + return $this->extractExactRequestedAccessoryCodesFromText( + trim($prompt . ' ' . $shopSearchQuery), + $codeTerms + ); + } + + /** + * @param string[] $codeTerms + * @return string[] + */ + private function extractExactRequestedAccessoryCodesFromText(string $text, array $codeTerms): array + { + $text = $this->normalizeOneLine($text); + if ($text === '') { return []; } diff --git a/src/Config/AgentRunnerConfig.php b/src/Config/AgentRunnerConfig.php index 2c9008b..6587ffe 100644 --- a/src/Config/AgentRunnerConfig.php +++ b/src/Config/AgentRunnerConfig.php @@ -548,6 +548,22 @@ final class AgentRunnerConfig return $terms; } + /** + * @return string[] + */ + private function getGenreStringListOrVocabularyView(string $configPath, string $viewPathConfigPath): array + { + $viewPath = $this->genreString($viewPathConfigPath); + if ($viewPath !== '' && $this->vocabulary !== null) { + $terms = $this->vocabulary->view($viewPath, []); + if ($terms !== []) { + return $terms; + } + } + + return $this->genreStringList($configPath); + } + /** * @return array */ @@ -1531,8 +1547,10 @@ final class AgentRunnerConfig */ public function getRequestedAccessoryCodeTerms(): array { - return $this->genreStringList('search_repair.requested_accessory_code_terms.terms') - ?: $this->genreStringList('product_roles.requested_accessory_code_terms.terms'); + return $this->getGenreStringListOrVocabularyView( + 'search_repair.requested_accessory_code_terms.terms', + 'search_repair.requested_accessory_code_terms.vocabulary_views.terms' + ) ?: $this->genreStringList('product_roles.requested_accessory_code_terms.terms'); } public function isDirectShopResultGuardEnabled(): bool