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.'
|
- '- Separate terms using spaces only.'
|
||||||
- '- If a relevant product name is present, it must be placed at the beginning of the final search query.'
|
- '- 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.'
|
- '- 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.'
|
- '- 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".'
|
- '- Do not output words that only describe conversation flow, such as "same", "again", "also", or "like above".'
|
||||||
conversation_context_rules:
|
conversation_context_rules:
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ parameters:
|
|||||||
- analysegeraet
|
- analysegeraet
|
||||||
- messgerät
|
- messgerät
|
||||||
- messgeraet
|
- messgeraet
|
||||||
|
- pockettester
|
||||||
|
- pocket tester
|
||||||
|
- handmessgerät
|
||||||
|
- handmessgeraet
|
||||||
- analysator
|
- analysator
|
||||||
- analyzer
|
- analyzer
|
||||||
- puffer
|
- puffer
|
||||||
@@ -40,6 +44,7 @@ parameters:
|
|||||||
- eignet
|
- eignet
|
||||||
- besser
|
- besser
|
||||||
- besten
|
- besten
|
||||||
|
- gut
|
||||||
- gut für
|
- gut für
|
||||||
- gut fuer
|
- gut fuer
|
||||||
- passend für
|
- passend für
|
||||||
|
|||||||
@@ -393,7 +393,7 @@ span.think {
|
|||||||
background-position: 220% 0;
|
background-position: 220% 0;
|
||||||
-webkit-background-clip: text;
|
-webkit-background-clip: text;
|
||||||
background-clip: text;
|
background-clip: text;
|
||||||
animation: thinkScan 2.2s linear infinite;
|
animation: thinkScan 1.4s linear infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes thinkScan {
|
@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',
|
'analysegeraet',
|
||||||
'messgerät',
|
'messgerät',
|
||||||
'messgeraet',
|
'messgeraet',
|
||||||
|
'pockettester',
|
||||||
|
'pocket tester',
|
||||||
|
'handmessgerät',
|
||||||
|
'handmessgeraet',
|
||||||
'analysator',
|
'analysator',
|
||||||
'analyzer',
|
'analyzer',
|
||||||
'puffer',
|
'puffer',
|
||||||
@@ -46,6 +50,7 @@ final class CommerceIntentConfig
|
|||||||
'eignet',
|
'eignet',
|
||||||
'besser',
|
'besser',
|
||||||
'besten',
|
'besten',
|
||||||
|
'gut',
|
||||||
'gut für',
|
'gut für',
|
||||||
'gut fuer',
|
'gut fuer',
|
||||||
'passend für',
|
'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(
|
return $this->buildDetectionResult(
|
||||||
intent: self::NONE,
|
intent: self::NONE,
|
||||||
score: 0,
|
score: 0,
|
||||||
@@ -95,6 +99,81 @@ final class CommerceIntentLite
|
|||||||
return $this->matchesAnyPattern($prompt, $this->config->getExplicitCommerceIntentPatterns());
|
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.
|
* 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
|
private function applyAdvisorySignals(string $prompt, int $score, array $signals): array
|
||||||
{
|
{
|
||||||
foreach ($this->config->getAdvisorySignals() as $signal) {
|
foreach ($this->config->getAdvisorySignals() as $signal) {
|
||||||
if (str_contains($prompt, mb_strtolower($signal))) {
|
if ($this->containsConfiguredPhraseOrToken($prompt, $signal)) {
|
||||||
$score += $this->config->getAdvisorySignalScore();
|
$score += $this->config->getAdvisorySignalScore();
|
||||||
$signals[] = $this->config->getAdvisorySignalPrefix() . $signal;
|
$signals[] = $this->config->getAdvisorySignalPrefix() . $signal;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user