This commit is contained in:
team 1
2026-05-10 10:53:05 +02:00
parent 5c09e17f9c
commit b2bd9f89e0
7 changed files with 278 additions and 3 deletions

View File

@@ -239,6 +239,8 @@ parameters:
- label: Im Shop suchen
prompt: Suche im Shop nach {shop_query}.
action_type: shop_search
material_query_template: '{shop_query}'
hide_when_material_query_matches_current: true
shop_results:
- label: Preis anzeigen
prompt: Zeige mir die Preise zu {shop_price_query}.

View File

@@ -1163,6 +1163,9 @@ parameters:
- orp
- select
- sensor
- brauerei
- brauereien
- brauwasser
- 0,02
stopword_cleanup:
origin: genre_native
@@ -1180,6 +1183,20 @@ parameters:
- bitte
- ich
- wir
- möchte
- moechte
- möchten
- moechten
- würde
- wuerde
- will
- brauche
- benötigen
- benoetigen
- benötige
- benoetige
- gern
- gerne
- im
- in
- shop
@@ -1208,6 +1225,13 @@ parameters:
- auflistung
- meter
- metern
- messen
- messe
- messung
- überwachen
- ueberwachen
- kontrollieren
- think
positive_token_filter:
origin: genre_native
enabled: true
@@ -1232,6 +1256,9 @@ parameters:
- chlor
- freies chlor
- gesamtchlor
- brauerei
- brauereien
- brauwasser
- redox
- orp
- ph
@@ -1261,6 +1288,21 @@ parameters:
- welchem
- welche
- dazu
- think
- möchte
- moechte
- möchten
- moechten
- würde
- wuerde
- will
- brauche
- benötigen
- benoetigen
- benötige
- benoetige
- gern
- gerne
- passend
- passende
- passendes

View File

@@ -211,6 +211,8 @@ parameters:
- '- Start the answer by making the fallback clear: "Aus den Shopdaten ergeben sich folgende Treffer; technische Eignung bitte prüfen."'
- '- If the user directly asks for accessories, cables, electrodes, buffers, kits, sets, indicators, reagents, or consumables and matching shop hits are present, do not start with a missing main-device or missing measuring-device sentence; start directly with the accessory shop hits.'
- '- Do not present shop-only matches as verified technical suitability unless the shop text explicitly states that suitability.'
- '- If the user asks for an industry, application, water type, or medium without naming a concrete measurement parameter and only shop results are available, do not infer application suitability from generic shop text. State that the exact parameter is needed and list shop hits only as unverified search hits.'
- '- In shop-only fallback answers, keep product entries short: product name, product number, price if present, availability, URL, and only the exact field that caused the hit. Do not expand into long measurement ranges, application-area lists, advantages, or repeated product profiles unless they are explicit evidence for the asked suitability.'
- '- Do not say that RAG knowledge confirms the result. Say that no belastbares RAG-Fachwissen was available for this selection.'
keine_belastbaren_daten:
- '- State that no reliable information was found in the provided RAG knowledge, URL content, or shop results.'

View File

@@ -0,0 +1,116 @@
# RetrieX Patch 81 - Shop Query Noise Cleanup and Brauwasser Fallback Guard
## Ziel
Direkte Shop-Suchen aus natürlich formulierten Nutzeranfragen sollen keine Füllwörter, Mess-Verben oder Meta-/Think-Tokens in die finale Shopware-Suchquery übernehmen.
Beispiel vor dem Patch:
```text
ich möchte für brauerei das brauwasser messen
Gesendete Suchquery: möchte brauerei brauwasser messen think
```
Zusätzlich soll die Antwortqualität bei breiten Anwendungs-/Medium-Fragen mit nur Shopdaten verbessert werden. Shop-only Treffer dürfen nicht als technisch geprüfte Eignung für eine Branche, Anwendung oder ein Medium wirken, wenn kein konkreter Messparameter genannt ist und die Shopdaten die Anwendung nicht explizit belegen.
## Änderung
### `config/retriex/genre.yaml`
Die bestehende genre-native Shopquery-Bereinigung wurde erweitert:
- `shop_query_runtime.current_input_preservation_terms` schützt jetzt auch anwendungsbezogene Shop-Tokens:
- `brauerei`
- `brauereien`
- `brauwasser`
- `shop_query_runtime.stopword_cleanup.terms` entfernt jetzt typische höfliche Formulierungen, Mess-Verben und den Meta-Token `think`, z. B.:
- `möchte`, `moechte`, `würde`, `wuerde`, `brauche`, `gern`, `gerne`
- `messen`, `messe`, `messung`, `überwachen`, `ueberwachen`, `kontrollieren`
- `think`
- `shop_query_runtime.positive_token_filter.allowed_terms` erlaubt `brauerei`, `brauereien`, `brauwasser` als positive Shop-Tokens, damit der Positive-Filter nicht auf die ungefilterte Query zurückfallen muss.
- `shop_query_runtime.positive_token_filter.blocked_terms` blockt zusätzlich höfliche Formulierungen und `think`, falls sie nach einem Optimizer-Lauf noch in der Query stehen.
### `config/retriex/prompt.yaml`
Die Shop-only-Fallback-Regeln wurden geschärft:
- Bei Branchen-/Anwendungs-/Medium-Fragen ohne konkreten Messparameter soll keine technische Eignung aus generischen Shopdaten abgeleitet werden.
- Produktlisten im Shop-only-Fallback sollen kurz bleiben und keine langen Messbereichs-, Einsatzgebiets-, Vorteil- oder Wiederholungsblöcke ausgeben, solange diese Felder nicht der direkte Beleg für die angefragte Eignung sind.
## Erwartetes Verhalten nach dem Patch
```text
ich möchte für brauerei das brauwasser messen
Gesendete Suchquery: brauerei brauwasser
```
Die Antwort sollte außerdem transparenter bleiben, z. B. sinngemäß:
```text
Für Brauwasser/Brauerei finde ich in den Shopdaten Treffer, aber keine belastbar geprüfte technische Eignung ohne konkreten Messparameter. Bitte grenze ein, ob Härte, Carbonathärte, pH, Leitfähigkeit, Chlor/Desinfektion o. Ä. gemessen werden soll.
Shop-Treffer (technische Eignung nicht sicher belegt): ...
```
## Bewusst nicht geändert
- keine PHP-Core-Änderung
- keine neue Retrieval-, Ranking-, Scoring- oder Shop-Matching-Logik
- keine Änderung an Follow-up-Actions
- keine Änderung an bestehenden Produkt-/Variantentoken-Guards aus v1.5.6/p80
Der Patch bleibt damit YAML-/Prompt-seitig und folgt der bestehenden Konfigurationslinie.
## Lokale Prüfungen
Im ZIP ohne `vendor/` wurden ausführbare lokale Prüfungen gemacht:
```bash
python3 -c "import yaml; yaml.safe_load(open('config/retriex/genre.yaml')); yaml.safe_load(open('config/retriex/prompt.yaml'))"
php -l src/Agent/AgentRunner.php
php -l src/Config/AgentRunnerConfig.php
```
Zusätzlich wurde die Query-Pipeline logisch simuliert:
```text
möchte brauerei brauwasser messen think -> brauerei brauwasser
ich möchte für brauerei das brauwasser messen -> brauerei brauwasser
chlor select sensor -> chlor select sensor
```
## In der Zielumgebung ausführen
```bash
bin/console cache:clear
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
```
## Manueller Regressionstest
```text
ich möchte für brauerei das brauwasser messen
```
Erwartung:
- Gesendete Suchquery enthält `brauerei brauwasser`
- `möchte`, `messen` und `think` erscheinen nicht in der gesendeten Shopquery
- Antwort macht transparent, dass ohne konkreten Messparameter keine geprüfte Brauwasser-Eignung abgeleitet werden soll
- Shop-only Ausgabe bleibt kurz und wird nicht durch lange wiederholte Produktprofile abgeschnitten
Zusätzliche kurze Smoke-Checks:
```text
ich suche den chlor select sensor
was kostet der indikator
Testomat 2000 THCL 100276
```
Erwartung:
- `chlor select sensor` bleibt erhalten
- bisherige Zubehör-/Preis-/Variantentoken-Flows aus v1.5.6 bleiben unverändert stabil

View File

@@ -0,0 +1,76 @@
# RetrieX Patch 82 - Follow-up Action Self-loop Guard
## Ziel
Verhindert Folgeaktions-Loops, bei denen nach einem bereits ausgeführten Shoplauf erneut dieselbe Shop-Suche angeboten wird.
Beispiel vor dem Patch:
1. `brauerei brauwasser`
2. Folgeaktion `Nur Zubehör anzeigen`
3. Shopquery `brauerei brauwasser zubehör` liefert `0` Shop-Treffer
4. Folgeaktion `Im Shop suchen` erzeugt wieder `brauerei brauwasser zubehör`
5. Wiederholung ohne fachliche Änderung
## Änderung
- `config/retriex/chat-messages.yaml`
- Die Commerce-Folgeaktion `Im Shop suchen` erhält eine materialisierte Query-Signatur:
- `material_query_template: '{shop_query}'`
- `hide_when_material_query_matches_current: true`
- `src/Agent/AgentRunner.php`
- Ergänzt einen Guard nach dem Rendern der Folgeaktion.
- Wenn die materialisierte Action-Query nach Normalisierung identisch mit der aktuellen Shopquery ist, wird die Action unterdrückt.
- `src/Config/ChatMessagesConfig.php`
- Reicht die neuen optionalen Action-Felder aus `chat-messages.yaml` an den AgentRunner durch.
## Bewusst nicht geändert
- Kein Retrieval-/Scoring-/Ranking-Fix.
- Keine Änderung an Shop-Matching oder Query-Optimierung.
- Keine fachlichen Tokenlisten im PHP-Core.
- Role-Filter-Actions wie `Nur Zubehör anzeigen` bleiben erlaubt, solange sie eine echte fachliche Eingrenzung darstellen.
## Erwartetes Verhalten
Nach einem ergebnislosen Lauf mit:
```text
brauerei brauwasser zubehör
```
wird nicht erneut angeboten:
```text
Im Shop suchen -> Suche im Shop nach brauerei brauwasser zubehör.
```
Die Action wird nur versteckt, wenn ihre materialisierte Query identisch mit der aktuellen Shopquery ist. Andere Actions mit anderer materialisierter Query können weiterhin angezeigt werden.
## Lokale Checks
```bash
php -l src/Agent/AgentRunner.php
php -l src/Config/ChatMessagesConfig.php
python3 - <<'PY'
import yaml
from pathlib import Path
for rel in [
'config/retriex/chat-messages.yaml',
'config/retriex/genre.yaml',
'config/retriex/prompt.yaml',
]:
yaml.safe_load(Path(rel).read_text())
print('yaml ok', rel)
PY
```
## Empfohlene Checks in Zielumgebung
```bash
bin/console cache:clear
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
```

View File

@@ -5648,6 +5648,10 @@ final readonly class AgentRunner
continue;
}
if (!$this->shouldShowRenderedFollowUpAction($item, $context, $actionPrompt)) {
continue;
}
$key = mb_strtolower($label . "\n" . $actionPrompt, 'UTF-8');
if (isset($seenActionKeys[$key])) {
continue;
@@ -5702,6 +5706,37 @@ final readonly class AgentRunner
return true;
}
/**
* @param array<string, mixed> $item
* @param array{shop_query:string, shop_price_query:string, answer_has_price:bool, answer_text:string, answer_anchor:string, answer_detail_score:int, role_counts:array<string,int>} $context
*/
private function shouldShowRenderedFollowUpAction(array $item, array $context, string $renderedPrompt): bool
{
if (!$this->optionalFollowUpActionBool($item, 'hide_when_material_query_matches_current')) {
return true;
}
$currentQuery = $this->normalizeShopQueryForComparison($context['shop_query']);
if ($currentQuery === '') {
return true;
}
$materialQueryTemplate = isset($item['material_query_template']) && is_scalar($item['material_query_template'])
? trim((string) $item['material_query_template'])
: '';
$materialQuery = $materialQueryTemplate !== ''
? $this->renderFollowUpActionPrompt($materialQueryTemplate, $context)
: $renderedPrompt;
$materialQuery = $this->normalizeShopQueryForComparison($materialQuery);
if ($materialQuery === '') {
return true;
}
return $materialQuery !== $currentQuery;
}
private function optionalFollowUpActionBool(array $item, string $key): bool
{
if (!array_key_exists($key, $item)) {

View File

@@ -543,7 +543,7 @@ final class ChatMessagesConfig
'prompt' => $prompt,
];
foreach (['action_type', 'target_role', 'hide_when_answer_detail_score_at_least'] as $optionalKey) {
foreach (['action_type', 'target_role', 'hide_when_answer_detail_score_at_least', 'material_query_template'] as $optionalKey) {
if (isset($item[$optionalKey]) && is_scalar($item[$optionalKey])) {
$optionalValue = trim((string) $item[$optionalKey]);
if ($optionalValue !== '') {
@@ -552,8 +552,10 @@ final class ChatMessagesConfig
}
}
if (array_key_exists('requires_answer_anchor', $item) && (is_bool($item['requires_answer_anchor']) || is_scalar($item['requires_answer_anchor']))) {
$action['requires_answer_anchor'] = $item['requires_answer_anchor'];
foreach (['requires_answer_anchor', 'hide_when_material_query_matches_current'] as $optionalBoolKey) {
if (array_key_exists($optionalBoolKey, $item) && (is_bool($item[$optionalBoolKey]) || is_scalar($item[$optionalBoolKey]))) {
$action[$optionalBoolKey] = $item[$optionalBoolKey];
}
}
if (isset($item['hide_when_answer_matches_any']) && is_array($item['hide_when_answer_matches_any'])) {