From b2bd9f89e0165063996c058c01a6904b03f6b487 Mon Sep 17 00:00:00 2001 From: team 1 Date: Sun, 10 May 2026 10:53:05 +0200 Subject: [PATCH] p81+81 --- config/retriex/chat-messages.yaml | 2 + config/retriex/genre.yaml | 42 +++++++ config/retriex/prompt.yaml | 2 + ...SE_AND_BRAUWASSER_FALLBACK_GUARD_README.md | 116 ++++++++++++++++++ ..._FOLLOWUP_ACTION_SELF_LOOP_GUARD_README.md | 76 ++++++++++++ src/Agent/AgentRunner.php | 35 ++++++ src/Config/ChatMessagesConfig.php | 8 +- 7 files changed, 278 insertions(+), 3 deletions(-) create mode 100644 patch_history/RETRIEX_PATCH_81_SHOP_QUERY_NOISE_AND_BRAUWASSER_FALLBACK_GUARD_README.md create mode 100644 patch_history/RETRIEX_PATCH_82_FOLLOWUP_ACTION_SELF_LOOP_GUARD_README.md diff --git a/config/retriex/chat-messages.yaml b/config/retriex/chat-messages.yaml index cd66a21..76dce64 100644 --- a/config/retriex/chat-messages.yaml +++ b/config/retriex/chat-messages.yaml @@ -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}. diff --git a/config/retriex/genre.yaml b/config/retriex/genre.yaml index 30100e1..7c37773 100644 --- a/config/retriex/genre.yaml +++ b/config/retriex/genre.yaml @@ -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 diff --git a/config/retriex/prompt.yaml b/config/retriex/prompt.yaml index 7911823..9cd43a2 100644 --- a/config/retriex/prompt.yaml +++ b/config/retriex/prompt.yaml @@ -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.' diff --git a/patch_history/RETRIEX_PATCH_81_SHOP_QUERY_NOISE_AND_BRAUWASSER_FALLBACK_GUARD_README.md b/patch_history/RETRIEX_PATCH_81_SHOP_QUERY_NOISE_AND_BRAUWASSER_FALLBACK_GUARD_README.md new file mode 100644 index 0000000..1494b5c --- /dev/null +++ b/patch_history/RETRIEX_PATCH_81_SHOP_QUERY_NOISE_AND_BRAUWASSER_FALLBACK_GUARD_README.md @@ -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 diff --git a/patch_history/RETRIEX_PATCH_82_FOLLOWUP_ACTION_SELF_LOOP_GUARD_README.md b/patch_history/RETRIEX_PATCH_82_FOLLOWUP_ACTION_SELF_LOOP_GUARD_README.md new file mode 100644 index 0000000..5a6bdfa --- /dev/null +++ b/patch_history/RETRIEX_PATCH_82_FOLLOWUP_ACTION_SELF_LOOP_GUARD_README.md @@ -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 +``` diff --git a/src/Agent/AgentRunner.php b/src/Agent/AgentRunner.php index 1a0b3bf..fc0e33e 100644 --- a/src/Agent/AgentRunner.php +++ b/src/Agent/AgentRunner.php @@ -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 $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} $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)) { diff --git a/src/Config/ChatMessagesConfig.php b/src/Config/ChatMessagesConfig.php index df240b1..d33a278 100644 --- a/src/Config/ChatMessagesConfig.php +++ b/src/Config/ChatMessagesConfig.php @@ -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'])) {