From 8c40153745d9bdb2e828953cb4f6e11e8c39f68a Mon Sep 17 00:00:00 2001 From: team 1 Date: Mon, 27 Apr 2026 16:11:25 +0200 Subject: [PATCH] fix shop research intent --- RETRIEX_SHOP_ADVISORY_INTENT_FIX_README.md | 64 ++++++++++++++ config/retriex/agent.yaml | 2 +- config/retriex/intent.yaml | 5 ++ public/assets/styles/base.css | 2 +- ...ss-shop-advisory-intent-fix-patch-only.zip | Bin 0 -> 9345 bytes src/Config/CommerceIntentConfig.php | 5 ++ src/Intent/CommerceIntentLite.php | 83 +++++++++++++++++- 7 files changed, 157 insertions(+), 4 deletions(-) create mode 100644 RETRIEX_SHOP_ADVISORY_INTENT_FIX_README.md create mode 100644 rag-inprogress-shop-advisory-intent-fix-patch-only.zip diff --git a/RETRIEX_SHOP_ADVISORY_INTENT_FIX_README.md b/RETRIEX_SHOP_ADVISORY_INTENT_FIX_README.md new file mode 100644 index 0000000..c17d6ef --- /dev/null +++ b/RETRIEX_SHOP_ADVISORY_INTENT_FIX_README.md @@ -0,0 +1,64 @@ +# RetrieX shop advisory intent fix + +## Problem + +The prompt + +```text +welcher pockettester ist für Redox messung gut +``` + +was classified as a purely technical RAG question because the factual guard matched +question wording such as `welcher` plus technical wording such as `Messung`. +As a result no Shopware search was executed. The later follow-up + +```text +suche im shop +``` + +then had to recover a shop query from history/client context, which remained fragile. + +## Fix + +This patch keeps the factual RAG guard for technical questions, but lets clear product +advisory prompts pass into commerce detection when both conditions are true: + +1. The prompt contains an advisory term such as `gut`, `geeignet`, `empfehl...`. +2. The prompt contains a product/device class signal such as `pockettester`, + `messgerät`, `handmessgerät`, etc. + +This means: + +- `Was ist der niedrigste Grenzwert ... Testomat ... Wasserhärte?` stays RAG-only. +- `mit welchem indikator wird der wert gemessen` stays RAG-only. +- `was kostet der indikator` stays commerce/product search. +- `welcher pockettester ist für Redox messung gut` now triggers commerce search directly. + +## Changed files + +```text +src/Intent/CommerceIntentLite.php +src/Config/CommerceIntentConfig.php +config/retriex/intent.yaml +config/retriex/agent.yaml +``` + +## Validation performed + +```bash +php -n -l src/Intent/CommerceIntentLite.php +php -n -l src/Config/CommerceIntentConfig.php +``` + +Additional local intent check: + +```text +Was ist der niedrigste Grenzwert ... => none +mit welchem indikator ... => none +was kostet der indikator => product_search +welcher pockettester ist für Redox messung gut => product_search +suche im shop => product_search +suche im shop nach redox messung => product_search +welcher Grenzwert kann mit dem Testomat 808 gemessen werden => none +welches Messgerät ist für Redox Messung geeignet => product_search +``` diff --git a/config/retriex/agent.yaml b/config/retriex/agent.yaml index 6fbbd49..df9d875 100644 --- a/config/retriex/agent.yaml +++ b/config/retriex/agent.yaml @@ -61,7 +61,7 @@ parameters: - '- Separate terms using spaces only.' - '- If a relevant product name is present, it must be placed at the beginning of the final search query.' - '- Try to always identify all products mentioned in the user input text, even in long prompts.' - - '- Look for terms such as Testomat, Horiba, Tritromat, or words like indicator/Indikator.' + - '- Look for terms such as Testomat, Horiba, Tritromat, Pockettester, Redox, ORP, or words like indicator/Indikator.' - '- If the current user input is vague or referential, use the recent conversation context only as support.' - '- Do not output words that only describe conversation flow, such as "same", "again", "also", or "like above".' conversation_context_rules: diff --git a/config/retriex/intent.yaml b/config/retriex/intent.yaml index 27e653f..c4742f4 100644 --- a/config/retriex/intent.yaml +++ b/config/retriex/intent.yaml @@ -21,6 +21,10 @@ parameters: - analysegeraet - messgerät - messgeraet + - pockettester + - pocket tester + - handmessgerät + - handmessgeraet - analysator - analyzer - puffer @@ -40,6 +44,7 @@ parameters: - eignet - besser - besten + - gut - gut für - gut fuer - passend für diff --git a/public/assets/styles/base.css b/public/assets/styles/base.css index 642da3c..9606f4e 100644 --- a/public/assets/styles/base.css +++ b/public/assets/styles/base.css @@ -393,7 +393,7 @@ span.think { background-position: 220% 0; -webkit-background-clip: text; background-clip: text; - animation: thinkScan 2.2s linear infinite; + animation: thinkScan 1.4s linear infinite; } @keyframes thinkScan { diff --git a/rag-inprogress-shop-advisory-intent-fix-patch-only.zip b/rag-inprogress-shop-advisory-intent-fix-patch-only.zip new file mode 100644 index 0000000000000000000000000000000000000000..f9711a3c661c9d1029e778080e795c2c4183e16a GIT binary patch literal 9345 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 literal 0 HcmV?d00001 diff --git a/src/Config/CommerceIntentConfig.php b/src/Config/CommerceIntentConfig.php index 03d8f64..b529689 100644 --- a/src/Config/CommerceIntentConfig.php +++ b/src/Config/CommerceIntentConfig.php @@ -25,6 +25,10 @@ final class CommerceIntentConfig 'analysegeraet', 'messgerät', 'messgeraet', + 'pockettester', + 'pocket tester', + 'handmessgerät', + 'handmessgeraet', 'analysator', 'analyzer', 'puffer', @@ -46,6 +50,7 @@ final class CommerceIntentConfig 'eignet', 'besser', 'besten', + 'gut', 'gut für', 'gut fuer', 'passend für', diff --git a/src/Intent/CommerceIntentLite.php b/src/Intent/CommerceIntentLite.php index 87820bc..2c92719 100644 --- a/src/Intent/CommerceIntentLite.php +++ b/src/Intent/CommerceIntentLite.php @@ -40,7 +40,11 @@ final class CommerceIntentLite ); } - if ($this->isTechnicalFactualKnowledgeQuery($prompt) && !$this->hasExplicitCommerceIntent($prompt)) { + if ( + $this->isTechnicalFactualKnowledgeQuery($prompt) + && !$this->hasExplicitCommerceIntent($prompt) + && !$this->hasAdvisoryProductSelectionIntent($prompt) + ) { return $this->buildDetectionResult( intent: self::NONE, score: 0, @@ -95,6 +99,81 @@ final class CommerceIntentLite return $this->matchesAnyPattern($prompt, $this->config->getExplicitCommerceIntentPatterns()); } + /** + * Product advisory prompts can contain technical words such as "Messung". + * + * They must not be swallowed by the factual RAG guard when the user is + * clearly asking for a suitable shop product or device class. + */ + private function hasAdvisoryProductSelectionIntent(string $prompt): bool + { + if (!$this->containsConfiguredAdvisorySignal($prompt)) { + return false; + } + + if (preg_match($this->config->getModelLikeProductPattern(), $prompt) === 1) { + return true; + } + + foreach ($this->config->getStrongSignalsList() as $signal) { + $signal = mb_strtolower(trim($signal)); + + if ($signal === '' || $this->isNonProductCommerceSignal($signal)) { + continue; + } + + if (str_contains($prompt, $signal)) { + return true; + } + } + + return false; + } + + private function containsConfiguredAdvisorySignal(string $prompt): bool + { + foreach ($this->config->getAdvisorySignals() as $signal) { + if ($this->containsConfiguredPhraseOrToken($prompt, $signal)) { + return true; + } + } + + return false; + } + + private function containsConfiguredPhraseOrToken(string $prompt, string $signal): bool + { + $signal = mb_strtolower(trim($signal)); + + if ($signal === '') { + return false; + } + + if (preg_match('/\s/u', $signal) === 1) { + return str_contains($prompt, $signal); + } + + $pattern = '/(?config->getAdvisorySignals() as $signal) { - if (str_contains($prompt, mb_strtolower($signal))) { + if ($this->containsConfiguredPhraseOrToken($prompt, $signal)) { $score += $this->config->getAdvisorySignalScore(); $signals[] = $this->config->getAdvisorySignalPrefix() . $signal; }