diff --git a/config/retriex/chat-messages.yaml b/config/retriex/chat-messages.yaml index 972258d..6ac55cb 100644 --- a/config/retriex/chat-messages.yaml +++ b/config/retriex/chat-messages.yaml @@ -243,14 +243,20 @@ parameters: - label: Preis anzeigen prompt: Zeige mir die Preise zu {shop_query}. action_type: price_details + hide_when_answer_matches_any: + - '/\bkeine?\s+(?:passende[nrs]?\s+)?(?:produktbezeichnung|shop-?treffer|treffer|produkte?)\b/iu' - label: Nur Zubehör anzeigen prompt: Suche im Shop nach {shop_query} und zeige daraus nur Zubehör. action_type: role_filter target_role: accessory_or_consumable + hide_when_answer_matches_any: + - '/\bkeine?\s+(?:passende[nrs]?\s+)?(?:produktbezeichnung|shop-?treffer|treffer|produkte?)\b/iu' - label: Nur Geräte anzeigen prompt: Suche im Shop nach {shop_query} und zeige daraus nur Geräte. action_type: role_filter target_role: main_device + hide_when_answer_matches_any: + - '/\bkeine?\s+(?:passende[nrs]?\s+)?(?:produktbezeichnung|shop-?treffer|treffer|produkte?)\b/iu' knowledge: - label: Technische Details anzeigen prompt: Zeige nur zusätzliche technische Details zu {answer_anchor}. diff --git a/patch_history/RETRIEX_PATCH_74_ACCESSORY_IDENTITY_SHOP_RESULT_GUARD_README.md b/patch_history/RETRIEX_PATCH_74_ACCESSORY_IDENTITY_SHOP_RESULT_GUARD_README.md new file mode 100644 index 0000000..72d370a --- /dev/null +++ b/patch_history/RETRIEX_PATCH_74_ACCESSORY_IDENTITY_SHOP_RESULT_GUARD_README.md @@ -0,0 +1,56 @@ +# RetrieX Patch p74 - Accessory Identity Shop Result Guard + +## Ziel + +p73 lädt bei generischen Zubehör-Suchanfragen zusätzliche Shopdaten nach, zum Beispiel für: + +```text +Suche im Shop nach Testomat Resthärte Indikator +``` + +Im Test wurden danach zwar Shop-Treffer gefunden, die Antwort blieb aber zu defensiv und sagte sinngemäß, dass keine passende Produktbezeichnung gefunden wurde. Gleichzeitig wurde weiterhin die Folgeaktion `Preis anzeigen` angeboten. Das ist für den Nutzer verwirrend: Wenn das System keine passende sichtbare Produktauswahl nennt, darf keine Preisaktion erscheinen; wenn Shop-Treffer mit passender Zubehör-Identität vorhanden sind, sollen diese bevorzugt als Produktliste genutzt werden. + +## Änderung + +- `AgentRunnerConfig::getDirectShopResultProductIdentityTerms()` ergänzt eine generische Produktidentitäts-Terminologie für direkte Shop-Ergebnisguards. +- Diese Terminologie bleibt vollständig YAML-/Genre-basiert: + - bestehende Direct-Attribute-Produkttypen + - `product_roles.accessory_product_terms.terms` + - `product_roles.requested_accessory_code_terms.terms` +- `AgentRunner::extractRequestedDirectProductTerms()` nutzt diese erweiterte Identitäts-Terminologie. +- Direkte Zubehör-/Produkt-Suchen wie `... Indikator` können dadurch Shop-Treffer mit passender Primary Identity bevorzugen und deterministisch als Shop-Ergebnisliste ausgeben. +- Gemischte Anfragen nach Geräten **und** Zubehör werden bewusst nicht auf Zubehör verengt. +- Follow-up-Actions der Shop-Ergebnisgruppe werden per YAML ausgeblendet, wenn die Antwort selbst einen No-Match-Zustand formuliert, zum Beispiel `keine Produktbezeichnung`, `keine Treffer`, `keine Produkte`. + +## Warum generisch? + +Der Patch enthält keine Testomat-, Resthärte- oder Indikator-300-Sonderlogik. Er erweitert nur die bestehende direkte Produktidentitätsprüfung um vorhandene Genre-/YAML-Begriffe für Zubehör und Zubehör-Codes. + +## Erwartete Wirkung + +Für: + +```text +Suche im Shop nach Testomat Resthärte Indikator +``` + +soll die Antwort nicht mehr primär Geräte empfehlen oder eine No-Match-Aussage mit Preisaktion erzeugen. Wenn passende Indikator-/Reagenz-Shoptreffer vorhanden sind, sollen diese direkt mit Shopfeldern wie Produktnummer, Preis, Verfügbarkeit und URL gelistet werden. + +## Checks + +Lokal geprüft: + +```bash +php -l src/Agent/AgentRunner.php +php -l src/Config/AgentRunnerConfig.php +python3 YAML parse für config/retriex/chat-messages.yaml, agent.yaml, genre.yaml +python3 Smoke: `Indikator` wird als direkte Produktidentität erkannt; No-Match-Action-Pattern matched +``` + +In der Zielumgebung zusätzlich ausführen: + +```bash +bin/console mto:agent:config:validate +bin/console mto:agent:regression:test +bin/console mto:agent:config:audit-source --details +``` diff --git a/src/Agent/AgentRunner.php b/src/Agent/AgentRunner.php index 9e1d74d..ad18a17 100644 --- a/src/Agent/AgentRunner.php +++ b/src/Agent/AgentRunner.php @@ -3321,6 +3321,10 @@ final readonly class AgentRunner return $shopResults; } + if ($this->isMixedDeviceAndAccessoryProductRequest($prompt, $shopSearchQuery)) { + return $shopResults; + } + $primaryMatches = []; $corpusMatches = []; @@ -3379,6 +3383,10 @@ final readonly class AgentRunner return $emptyResult; } + if ($this->isMixedDeviceAndAccessoryProductRequest($prompt, $shopSearchQuery)) { + return $emptyResult; + } + $repairQuery = $this->buildDirectProductPrimaryIdentityRepairQuery( shopSearchQuery: $shopSearchQuery, requestedTerms: $requestedTerms @@ -4062,7 +4070,7 @@ final readonly class AgentRunner } $terms = []; - foreach ($this->agentRunnerConfig->getShopQueryProductAttributeCleanupProductTypeTerms() as $term) { + foreach ($this->agentRunnerConfig->getDirectShopResultProductIdentityTerms() as $term) { if ($this->containsAllShopQueryTokens($combined, $term)) { $terms[] = $term; } @@ -4071,6 +4079,17 @@ final readonly class AgentRunner return array_values(array_unique($terms)); } + private function isMixedDeviceAndAccessoryProductRequest(string $prompt, string $shopSearchQuery): bool + { + $combined = mb_strtolower($this->normalizeOneLine($prompt . ' ' . $shopSearchQuery), 'UTF-8'); + if ($combined === '') { + return false; + } + + return $this->containsAnyConfiguredTerm($combined, $this->agentRunnerConfig->getNoLlmMainDeviceRequestRoleKeywords()) + && $this->containsAnyConfiguredTerm($combined, $this->agentRunnerConfig->getNoLlmAccessoryProductRoleKeywords()); + } + private function containsAllShopQueryTokens(string $text, string $term): bool { $tokens = array_fill_keys($this->tokenizeShopQueryCandidate($text), true); @@ -4216,6 +4235,7 @@ final readonly class AgentRunner || !$shopSearchAttempted || $shopSearchHadSystemFailure || $this->extractRequestedDirectProductTerms($prompt, $shopSearchQuery) === [] + || $this->isMixedDeviceAndAccessoryProductRequest($prompt, $shopSearchQuery) ) { return ''; } diff --git a/src/Config/AgentRunnerConfig.php b/src/Config/AgentRunnerConfig.php index febfff6..e0a55e1 100644 --- a/src/Config/AgentRunnerConfig.php +++ b/src/Config/AgentRunnerConfig.php @@ -1256,6 +1256,18 @@ final class AgentRunnerConfig ); } + /** + * @return string[] + */ + public function getDirectShopResultProductIdentityTerms(): array + { + return array_values(array_unique(array_merge( + $this->getShopQueryProductAttributeCleanupProductTypeTerms(), + $this->genreStringList('product_roles.accessory_product_terms.terms'), + $this->genreStringList('product_roles.requested_accessory_code_terms.terms') + ))); + } + /** * @return string[] */