fix shop research intent
This commit is contained in:
64
RETRIEX_SHOP_ADVISORY_INTENT_FIX_README.md
Normal file
64
RETRIEX_SHOP_ADVISORY_INTENT_FIX_README.md
Normal file
@@ -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
|
||||
```
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
BIN
rag-inprogress-shop-advisory-intent-fix-patch-only.zip
Normal file
BIN
rag-inprogress-shop-advisory-intent-fix-patch-only.zip
Normal file
Binary file not shown.
@@ -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',
|
||||
|
||||
@@ -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 = '/(?<![\p{L}\p{N}])' . preg_quote($signal, '/') . '(?![\p{L}\p{N}])/u';
|
||||
|
||||
return preg_match($pattern, $prompt) === 1;
|
||||
}
|
||||
|
||||
private function isNonProductCommerceSignal(string $signal): bool
|
||||
{
|
||||
return in_array($signal, [
|
||||
'shop',
|
||||
'alle',
|
||||
'kunde',
|
||||
'online',
|
||||
'kaufen',
|
||||
'kostet',
|
||||
'suche',
|
||||
'such',
|
||||
'finde',
|
||||
'finden',
|
||||
], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects factual technical knowledge questions that must stay in RAG retrieval.
|
||||
*
|
||||
@@ -255,7 +334,7 @@ final class CommerceIntentLite
|
||||
private function applyAdvisorySignals(string $prompt, int $score, array $signals): array
|
||||
{
|
||||
foreach ($this->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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user