diff --git a/RETRIEX_PATCH_53_CONFIG_LAYOUT_CLEANUP_README.md b/RETRIEX_PATCH_53_CONFIG_LAYOUT_CLEANUP_README.md new file mode 100644 index 0000000..0e9b0ce --- /dev/null +++ b/RETRIEX_PATCH_53_CONFIG_LAYOUT_CLEANUP_README.md @@ -0,0 +1,81 @@ +# RetrieX Patch p53 - Config Layout Cleanup / Shop Runtime Grouping + +## Ziel + +Dieser Patch gruppiert bestehende Shop-Runtime-Konfiguration in `config/retriex/agent.yaml` unter `shop_runtime`, ohne fachliche Logik zu ändern. + +Der Patch ist rein mechanisch: + +- keine neue Profil-/Layer-Architektur +- keine neuen Defaults +- keine neuen Token-/Stringlisten +- keine Ranking-, Retrieval- oder Shopware-Kriterienänderung +- keine LLM-Verhaltensänderung + +## Geänderte Dateien + +- `config/retriex/agent.yaml` +- `config/retriex/vocabulary.yaml` +- `src/Config/AgentRunnerConfig.php` +- `src/Config/RetriexEffectiveConfigProvider.php` + +## Neue Gruppierung + +Die bisherigen Runtime-Blöcke wurden wie folgt verschoben: + +| Alter Pfad | Neuer Pfad | +| --- | --- | +| `direct_shop_result_answer` | `shop_runtime.direct_answer` | +| `shop_prompt.current_input_preservation` | `shop_runtime.query_cleanup.current_input_preservation` | +| `shop_prompt.query_stopword_cleanup` | `shop_runtime.query_cleanup.stopword_cleanup` | +| `shop_prompt.product_attribute_query_cleanup` | `shop_runtime.attribute_cleanup` | +| `shop_prompt.context_usage` | `shop_runtime.context_resolution.context_usage` | +| `shop_prompt.context_anchor_enrichment` | `shop_runtime.context_resolution.history_anchor_enrichment` | +| `shop_prompt.meta_query_guard` | `shop_runtime.context_resolution.meta_query_guard` | +| `shop_prompt.rag_anchor_enrichment` | `shop_runtime.context_resolution.rag_anchor_enrichment` | +| `shop_prompt.direct_result_guard` | `shop_runtime.result_identity` | +| `shop_prompt.length_sort` | `shop_runtime.answer_constraints.length_sort` | +| `shop_prompt.length_filter` | `shop_runtime.answer_constraints.length_filter` | + +`final_answer_guard` bleibt bewusst außerhalb von `shop_runtime`, weil er allgemeiner Runtime-/LLM-Ausgabeschutz ist. + +`shop_prompt` enthält nach dem Cleanup nur noch den eigentlichen Shop-Query-Optimizer-Prompt und dessen Sprach-/Prompt-Regeln. + +## Vocabulary Views + +Die Vocabulary-View-Pfade wurden passend zur neuen Layout-Struktur umbenannt, ohne die zugrunde liegenden Listen zu verändern: + +- `agent.shop_prompt.current_input_preservation_terms` -> `agent.shop_runtime.query_cleanup.current_input_preservation_terms` +- `agent.shop_prompt.context_anchor_enrichment.trigger_terms` -> `agent.shop_runtime.context_resolution.history_anchor_enrichment.trigger_terms` + +Die referenzierten Term-Gruppen bleiben unverändert. + +## Lokale Prüfungen + +Ausgeführt im entpackten Patch-Arbeitsstand: + +```bash +php -l src/Config/AgentRunnerConfig.php +php -l src/Config/RetriexEffectiveConfigProvider.php +python3 - <<'PY' +import yaml +for f in ['config/retriex/agent.yaml', 'config/retriex/vocabulary.yaml']: + yaml.safe_load(open(f, encoding='utf-8')) +PY +``` + +Zusätzlich wurde per YAML-Strukturvergleich geprüft, dass die verschobenen Runtime-Blöcke ihre bisherigen Werte behalten. Nur die zwei Vocabulary-View-Pfadnamen wurden mechanisch an das neue Layout angepasst. + +## Nicht lokal ausführbar + +Die Symfony-Console-Checks konnten in dieser Umgebung nicht vollständig ausgeführt werden, weil der ZIP-Stand kein `vendor/` enthält. + +## Empfohlene Projektchecks nach Einspielen + +```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/config/retriex/agent.yaml b/config/retriex/agent.yaml index 0bbd7fa..8498f5e 100644 --- a/config/retriex/agent.yaml +++ b/config/retriex/agent.yaml @@ -167,6 +167,260 @@ parameters: - '/^\s*(?:produkt(?:-|\s)?nummer|artikel(?:-|\s)?nummer|preis|verfügbar|verfuegbar|url|quellen?)\b/iu' - '/^\s*(?:status|query|intent|datenbasis|shop(?:-|\s)?suche)\b/iu' + shop_runtime: + query_cleanup: + current_input_preservation: + enabled: true + # Terms that must be preserved from the current user input in the final + # Shopware search query. This prevents short domain terms from being + # dropped by query optimization or generic cleanup. Adapt vocabulary view + # defaults or add a local `terms` override instead of changing PHP code. + vocabulary_views: + terms: agent.shop_runtime.query_cleanup.current_input_preservation_terms + + stopword_cleanup: + enabled: true + min_query_tokens_after_cleanup: 2 + # Plain Shopware text search should contain product-relevant terms only. + # These terms are UI, instruction, presentation or sorting words and are + # removed after LLM query optimization. Keep this list simple and local. + terms: + - zeige + - zeig + - suche + - such + - finde + - find + - gib + - gebe + - nenne + - mir + - bitte + - ich + - wir + - im + - in + - shop + - für + - fuer + - nach + - mit + - ohne + - von + - zum + - zur + - der + - die + - das + - ein + - eine + - einen + - ordne + - sortiere + - sortiert + - sortierung + - liste + - tabelle + - übersicht + - uebersicht + - auflistung + - meter + - metern + + + attribute_cleanup: + enabled: true + # For direct product/accessory lookups with comparative attribute + # constraints, keep the concrete product type and application terms but + # do not send range words/numeric thresholds to the plain text shop + # search. Example: "Anschlusskabel pH/Redox länger 20m" becomes + # "anschlusskabel redox" so the shop can return 25m/50m/100m cables. + min_query_tokens_after_cleanup: 2 + # Direct product/accessory cleanup terms are resolved from + # config/retriex/vocabulary.yaml. Local lists may still be added here + # as project-specific overrides, but the default source is vocabulary. + vocabulary_views: + product_type_terms: search_repair.direct_product_type_terms + stop_terms: search_repair.direct_product_attribute_stop_terms + comparative_constraint_patterns: + - '/\b(?:länger|laenger|kürzer|kuerzer|größer|groesser|kleiner|über|ueber|unter|mindestens|maximal|maximum|minimum|ab|bis|mehr\s+als|weniger\s+als)\s+(?P\d+(?:[,.]\d+)?\s*[\p{L}µ°%]*)\b/iu' + + + context_resolution: + context_usage: + referential_terms: + - der + - die + - das + - den + - dem + - dazu + - davon + - dafür + - dafuer + - dieser + - diese + - dieses + - obige + - obigen + - oben + - vorher + - zuvor + - gleiche + - gleichen + - selbe + - selben + history_anchor_enrichment: + enabled: true + max_query_terms: 2 + template: '{anchor} {query}' + vocabulary_views: + trigger_terms: agent.shop_runtime.context_resolution.history_anchor_enrichment.trigger_terms + anchor_patterns: + - '/\b(?:indikator(?:typ)?|indicator(?:\s+type)?|reagenz(?:satz|typ)?|reagent(?:\s+set|\s+type)?|typ|type)\s+[A-Za-zÄÖÜäöüß]{0,8}\s*\d{1,5}(?:\s*[A-ZÄÖÜ]{1,4})?(?:\s*%)?\b/iu' + meta_query_guard: + enabled: true + cleanup_profile: shop_context_fallback + context_fallback_enabled: true + context_fallback_question_limit: 12 + context_fallback_history_budget_chars: 20000 + context_fallback_use_full_history: true + context_fallback_max_terms: 6 + # Legacy/domain override list. Generic stopwords, user-instruction + # phrases and presentation terms are provided by profile + # `shop_context_fallback`. Keep shop/price/domain terms here. + context_fallback_filter_terms: + - preis + - preise + - preisen + - kosten + - kostet + - grenzwert + - grenzwerte + - grenzwerten + - welche + - gut + - geeignet + - messen + - gemessen + meta_only_terms: + - shop + - preis + - preise + - preisen + - kosten + - kostet + - shopsuche + - shop-suche + rag_anchor_enrichment: + enabled: true + # Enriches overly narrow numeric shop queries with a product anchor from + # retrieved RAG records when the same record explicitly contains the + # requested numeric value/unit. This prevents queries such as "0,02" + # when RAG already identified a concrete product such as a device model. + min_score: 120 + max_query_terms: 6 + early_chunk_bonus_max: 10 + template: '{anchor} {query} {subject}' + scores: + exact_value_with_unit: 120 + exact_value_only: 40 + anchor_bonus: 50 + numeric_focus_patterns: + - '/(?P\d+(?:[,.]\d+)?)\s*(?P°?\s*d\s*h|dh|dH)/iu' + product_title_patterns: + - '/#\s*Produkt\s+Titel:\s*`(?P[^`]+)`/iu' + - '/\*\*Produktname:\*\*\s*(?P<title>[^\r\n]+)/iu' + anchor_bonus_patterns: + - '/\b[\p{L}][\p{L}\p{N}®+._-]*(?:\s+|[-_])\d{2,5}\b/u' + subject_terms: + - resthärte + - resthaerte + - wasserhärte + - wasserhaerte + - gesamthärte + - gesamthaerte + - härte + - haerte + - grenzwert + - messung + + + result_identity: + enabled: true + # Direct product-list answers should only list products whose primary + # identity (name/URL) matches the requested product type. This prevents + # devices from being listed as a requested consumable merely because the + # description mentions such consumables as accessories. + prefer_primary_identity_matches: true + compound_prefix_match: + enabled: true + # Some Shopware product names combine the requested product type with + # a noun suffix, for example "pH-Pufferlösung". Keep this list small + # and explicit so broad direct-result filtering remains safe. + terms: + - puffer + - kalibrierpuffer + primary_identity_repair: + enabled: true + min_query_tokens_after_cleanup: 2 + # Only used for a retry query when the direct-result guard would + # otherwise suppress all shop results. Keep product words and context + # such as brand/pH/Redox, but remove target-device wording that can + # push Shopware ranking toward devices instead of the requested + # accessory/consumable. + stop_terms: + - messgerät + - messgeraet + - messgeräte + - messgeraete + - messgeräten + - messgeraeten + - gerät + - geraet + - geräte + - geraete + - geräten + - geraeten + - handmessgerät + - handmessgeraet + - handmessgeräte + - handmessgeraete + - messkoffer + - koffer + + + answer_constraints: + length_sort: + enabled: true + trigger_patterns: + - '/\b(?:ordne|sortiere|sortiert|sortierung)\b.{0,80}\b(?:meter|metern|m)\b/iu' + - '/\bnach\s+(?:meter|metern|m)\b/iu' + value_patterns: + - '/(?P<value>\d+(?:[,.]\d+)?)\s*(?:m|meter|metern)\b/iu' + + length_filter: + enabled: true + min_patterns: + - '/\b(?:ab|mindestens|minimum|min\.?|>=|größer\s+gleich|groesser\s+gleich)\s*(?P<value>\d+(?:[,.]\d+)?)\s*(?:m|meter|metern)\b/iu' + - '/\b(?:länger|laenger|größer|groesser|mehr)\s+(?:als\s+)?(?P<value>\d+(?:[,.]\d+)?)\s*(?:m|meter|metern)\b/iu' + - '/(?P<value>\d+(?:[,.]\d+)?)\s*(?:m|meter|metern)\s*(?:oder\s+)?(?:länger|laenger|mehr)\b/iu' + max_patterns: + - '/\b(?:bis|maximal|maximum|max\.?|höchstens|hoechstens|<=|kleiner\s+gleich)\s*(?P<value>\d+(?:[,.]\d+)?)\s*(?:m|meter|metern)\b/iu' + - '/\b(?:kürzer|kuerzer|kleiner|weniger)\s+(?:als\s+)?(?P<value>\d+(?:[,.]\d+)?)\s*(?:m|meter|metern)\b/iu' + - '/(?P<value>\d+(?:[,.]\d+)?)\s*(?:m|meter|metern)\s*(?:oder\s+)?(?:kürzer|kuerzer|weniger)\b/iu' + + + direct_answer: + enabled: true + max_results: 10 + intro: 'Aus den Shopdaten ergeben sich folgende passende Treffer:' + no_results: 'Ich finde in den Shopdaten keine passenden Treffer für die angefragte Produktsuche. Ich liste deshalb keine fachfremden Ersatzprodukte auf.' + sorted_by_length_note: 'Sortierung: aufsteigend nach erkannter Kabellänge.' + min_length_filter_note: 'Filter: nur Treffer ab {value} m.' + max_length_filter_note: 'Filter: nur Treffer bis {value} m.' + + rag_evidence_guard: cleanup_profile: rag_evidence # Legacy/domain override list. Generic German stopwords are provided by @@ -212,15 +466,6 @@ parameters: vocabulary_maps: synonyms: agent.rag_evidence_guard.synonyms - direct_shop_result_answer: - enabled: true - max_results: 10 - intro: 'Aus den Shopdaten ergeben sich folgende passende Treffer:' - no_results: 'Ich finde in den Shopdaten keine passenden Treffer für die angefragte Produktsuche. Ich liste deshalb keine fachfremden Ersatzprodukte auf.' - sorted_by_length_note: 'Sortierung: aufsteigend nach erkannter Kabellänge.' - min_length_filter_note: 'Filter: nur Treffer ab {value} m.' - max_length_filter_note: 'Filter: nur Treffer bis {value} m.' - no_llm_fallback: max_shop_results: 5 messages: @@ -423,241 +668,6 @@ parameters: - '- Do not revive older products unless the current user input clearly refers to them.' - '- If the current input starts a new topic, ignore older product context.' - '- Prefer the most recent product reference over older ones.' - current_input_preservation: - enabled: true - # Terms that must be preserved from the current user input in the final - # Shopware search query. This prevents short domain terms from being - # dropped by query optimization or generic cleanup. Adapt vocabulary view - # defaults or add a local `terms` override instead of changing PHP code. - vocabulary_views: - terms: agent.shop_prompt.current_input_preservation_terms - - product_attribute_query_cleanup: - enabled: true - # For direct product/accessory lookups with comparative attribute - # constraints, keep the concrete product type and application terms but - # do not send range words/numeric thresholds to the plain text shop - # search. Example: "Anschlusskabel pH/Redox länger 20m" becomes - # "anschlusskabel redox" so the shop can return 25m/50m/100m cables. - min_query_tokens_after_cleanup: 2 - # Direct product/accessory cleanup terms are resolved from - # config/retriex/vocabulary.yaml. Local lists may still be added here - # as project-specific overrides, but the default source is vocabulary. - vocabulary_views: - product_type_terms: search_repair.direct_product_type_terms - stop_terms: search_repair.direct_product_attribute_stop_terms - comparative_constraint_patterns: - - '/\b(?:länger|laenger|kürzer|kuerzer|größer|groesser|kleiner|über|ueber|unter|mindestens|maximal|maximum|minimum|ab|bis|mehr\s+als|weniger\s+als)\s+(?P<value>\d+(?:[,.]\d+)?\s*[\p{L}µ°%]*)\b/iu' - - query_stopword_cleanup: - enabled: true - min_query_tokens_after_cleanup: 2 - # Plain Shopware text search should contain product-relevant terms only. - # These terms are UI, instruction, presentation or sorting words and are - # removed after LLM query optimization. Keep this list simple and local. - terms: - - zeige - - zeig - - suche - - such - - finde - - find - - gib - - gebe - - nenne - - mir - - bitte - - ich - - wir - - im - - in - - shop - - für - - fuer - - nach - - mit - - ohne - - von - - zum - - zur - - der - - die - - das - - ein - - eine - - einen - - ordne - - sortiere - - sortiert - - sortierung - - liste - - tabelle - - übersicht - - uebersicht - - auflistung - - meter - - metern - - direct_result_guard: - enabled: true - # Direct product-list answers should only list products whose primary - # identity (name/URL) matches the requested product type. This prevents - # devices from being listed as a requested consumable merely because the - # description mentions such consumables as accessories. - prefer_primary_identity_matches: true - compound_prefix_match: - enabled: true - # Some Shopware product names combine the requested product type with - # a noun suffix, for example "pH-Pufferlösung". Keep this list small - # and explicit so broad direct-result filtering remains safe. - terms: - - puffer - - kalibrierpuffer - primary_identity_repair: - enabled: true - min_query_tokens_after_cleanup: 2 - # Only used for a retry query when the direct-result guard would - # otherwise suppress all shop results. Keep product words and context - # such as brand/pH/Redox, but remove target-device wording that can - # push Shopware ranking toward devices instead of the requested - # accessory/consumable. - stop_terms: - - messgerät - - messgeraet - - messgeräte - - messgeraete - - messgeräten - - messgeraeten - - gerät - - geraet - - geräte - - geraete - - geräten - - geraeten - - handmessgerät - - handmessgeraet - - handmessgeräte - - handmessgeraete - - messkoffer - - koffer - - length_sort: - enabled: true - trigger_patterns: - - '/\b(?:ordne|sortiere|sortiert|sortierung)\b.{0,80}\b(?:meter|metern|m)\b/iu' - - '/\bnach\s+(?:meter|metern|m)\b/iu' - value_patterns: - - '/(?P<value>\d+(?:[,.]\d+)?)\s*(?:m|meter|metern)\b/iu' - - length_filter: - enabled: true - min_patterns: - - '/\b(?:ab|mindestens|minimum|min\.?|>=|größer\s+gleich|groesser\s+gleich)\s*(?P<value>\d+(?:[,.]\d+)?)\s*(?:m|meter|metern)\b/iu' - - '/\b(?:länger|laenger|größer|groesser|mehr)\s+(?:als\s+)?(?P<value>\d+(?:[,.]\d+)?)\s*(?:m|meter|metern)\b/iu' - - '/(?P<value>\d+(?:[,.]\d+)?)\s*(?:m|meter|metern)\s*(?:oder\s+)?(?:länger|laenger|mehr)\b/iu' - max_patterns: - - '/\b(?:bis|maximal|maximum|max\.?|höchstens|hoechstens|<=|kleiner\s+gleich)\s*(?P<value>\d+(?:[,.]\d+)?)\s*(?:m|meter|metern)\b/iu' - - '/\b(?:kürzer|kuerzer|kleiner|weniger)\s+(?:als\s+)?(?P<value>\d+(?:[,.]\d+)?)\s*(?:m|meter|metern)\b/iu' - - '/(?P<value>\d+(?:[,.]\d+)?)\s*(?:m|meter|metern)\s*(?:oder\s+)?(?:kürzer|kuerzer|weniger)\b/iu' - - context_usage: - referential_terms: - - der - - die - - das - - den - - dem - - dazu - - davon - - dafür - - dafuer - - dieser - - diese - - dieses - - obige - - obigen - - oben - - vorher - - zuvor - - gleiche - - gleichen - - selbe - - selben - context_anchor_enrichment: - enabled: true - max_query_terms: 2 - template: '{anchor} {query}' - vocabulary_views: - trigger_terms: agent.shop_prompt.context_anchor_enrichment.trigger_terms - anchor_patterns: - - '/\b(?:indikator(?:typ)?|indicator(?:\s+type)?|reagenz(?:satz|typ)?|reagent(?:\s+set|\s+type)?|typ|type)\s+[A-Za-zÄÖÜäöüß]{0,8}\s*\d{1,5}(?:\s*[A-ZÄÖÜ]{1,4})?(?:\s*%)?\b/iu' - meta_query_guard: - enabled: true - cleanup_profile: shop_context_fallback - context_fallback_enabled: true - context_fallback_question_limit: 12 - context_fallback_history_budget_chars: 20000 - context_fallback_use_full_history: true - context_fallback_max_terms: 6 - # Legacy/domain override list. Generic stopwords, user-instruction - # phrases and presentation terms are provided by profile - # `shop_context_fallback`. Keep shop/price/domain terms here. - context_fallback_filter_terms: - - preis - - preise - - preisen - - kosten - - kostet - - grenzwert - - grenzwerte - - grenzwerten - - welche - - gut - - geeignet - - messen - - gemessen - meta_only_terms: - - shop - - preis - - preise - - preisen - - kosten - - kostet - - shopsuche - - shop-suche - rag_anchor_enrichment: - enabled: true - # Enriches overly narrow numeric shop queries with a product anchor from - # retrieved RAG records when the same record explicitly contains the - # requested numeric value/unit. This prevents queries such as "0,02" - # when RAG already identified a concrete product such as a device model. - min_score: 120 - max_query_terms: 6 - early_chunk_bonus_max: 10 - template: '{anchor} {query} {subject}' - scores: - exact_value_with_unit: 120 - exact_value_only: 40 - anchor_bonus: 50 - numeric_focus_patterns: - - '/(?P<value>\d+(?:[,.]\d+)?)\s*(?P<unit>°?\s*d\s*h|dh|dH)/iu' - product_title_patterns: - - '/#\s*Produkt\s+Titel:\s*`(?P<title>[^`]+)`/iu' - - '/\*\*Produktname:\*\*\s*(?P<title>[^\r\n]+)/iu' - anchor_bonus_patterns: - - '/\b[\p{L}][\p{L}\p{N}®+._-]*(?:\s+|[-_])\d{2,5}\b/u' - subject_terms: - - resthärte - - resthaerte - - wasserhärte - - wasserhaerte - - gesamthärte - - gesamthaerte - - härte - - haerte - - grenzwert - - messung - language_preservation: enabled: true language_markers: diff --git a/config/retriex/vocabulary.yaml b/config/retriex/vocabulary.yaml index 9f1034e..3e958fe 100644 --- a/config/retriex/vocabulary.yaml +++ b/config/retriex/vocabulary.yaml @@ -898,14 +898,16 @@ parameters: accessory_product_keywords: include: - agent_no_llm_accessory_product_keywords - shop_prompt: - current_input_preservation_terms: - include: - - agent_shop_current_input_preservation_terms - context_anchor_enrichment: - trigger_terms: + shop_runtime: + query_cleanup: + current_input_preservation_terms: include: - - agent_shop_context_anchor_trigger_terms + - agent_shop_current_input_preservation_terms + context_resolution: + history_anchor_enrichment: + trigger_terms: + include: + - agent_shop_context_anchor_trigger_terms maps: prompt: measurement_evidence_guard: diff --git a/src/Config/AgentRunnerConfig.php b/src/Config/AgentRunnerConfig.php index df7e294..f262936 100644 --- a/src/Config/AgentRunnerConfig.php +++ b/src/Config/AgentRunnerConfig.php @@ -749,37 +749,37 @@ final class AgentRunnerConfig public function isDirectShopResultAnswerEnabled(): bool { - return $this->getRequiredBool('direct_shop_result_answer.enabled'); + return $this->getRequiredBool('shop_runtime.direct_answer.enabled'); } public function getDirectShopResultAnswerMaxResults(): int { - return $this->getRequiredInt('direct_shop_result_answer.max_results'); + return $this->getRequiredInt('shop_runtime.direct_answer.max_results'); } public function getDirectShopResultAnswerIntro(): string { - return $this->getRequiredString('direct_shop_result_answer.intro'); + return $this->getRequiredString('shop_runtime.direct_answer.intro'); } public function getDirectShopResultAnswerNoResultsMessage(): string { - return $this->getRequiredString('direct_shop_result_answer.no_results'); + return $this->getRequiredString('shop_runtime.direct_answer.no_results'); } public function getDirectShopResultAnswerSortedByLengthNote(): string { - return $this->getRequiredString('direct_shop_result_answer.sorted_by_length_note'); + return $this->getRequiredString('shop_runtime.direct_answer.sorted_by_length_note'); } public function getDirectShopResultAnswerMinLengthFilterNote(): string { - return $this->getRequiredString('direct_shop_result_answer.min_length_filter_note'); + return $this->getRequiredString('shop_runtime.direct_answer.min_length_filter_note'); } public function getDirectShopResultAnswerMaxLengthFilterNote(): string { - return $this->getRequiredString('direct_shop_result_answer.max_length_filter_note'); + return $this->getRequiredString('shop_runtime.direct_answer.max_length_filter_note'); } public function getNoLlmFallbackMaxShopResults(): int @@ -1073,12 +1073,12 @@ final class AgentRunnerConfig */ public function getShopQueryContextUsageReferentialTerms(): array { - return $this->getRequiredStringList('shop_prompt.context_usage.referential_terms'); + return $this->getRequiredStringList('shop_runtime.context_resolution.context_usage.referential_terms'); } public function isShopQueryCurrentInputPreservationEnabled(): bool { - return $this->getOptionalBool('shop_prompt.current_input_preservation.enabled', true); + return $this->getOptionalBool('shop_runtime.query_cleanup.current_input_preservation.enabled', true); } /** @@ -1087,19 +1087,19 @@ final class AgentRunnerConfig public function getShopQueryCurrentInputPreservationTerms(): array { return $this->getConfiguredStringListOrVocabularyView( - 'shop_prompt.current_input_preservation.terms', - 'shop_prompt.current_input_preservation.vocabulary_views.terms' + 'shop_runtime.query_cleanup.current_input_preservation.terms', + 'shop_runtime.query_cleanup.current_input_preservation.vocabulary_views.terms' ); } public function isShopQueryProductAttributeCleanupEnabled(): bool { - return $this->getRequiredBool('shop_prompt.product_attribute_query_cleanup.enabled'); + return $this->getRequiredBool('shop_runtime.attribute_cleanup.enabled'); } public function getShopQueryProductAttributeCleanupMinTokens(): int { - return $this->getRequiredInt('shop_prompt.product_attribute_query_cleanup.min_query_tokens_after_cleanup'); + return $this->getRequiredInt('shop_runtime.attribute_cleanup.min_query_tokens_after_cleanup'); } /** @@ -1108,8 +1108,8 @@ final class AgentRunnerConfig public function getShopQueryProductAttributeCleanupProductTypeTerms(): array { return $this->getConfiguredStringListOrVocabularyView( - 'shop_prompt.product_attribute_query_cleanup.product_type_terms', - 'shop_prompt.product_attribute_query_cleanup.vocabulary_views.product_type_terms' + 'shop_runtime.attribute_cleanup.product_type_terms', + 'shop_runtime.attribute_cleanup.vocabulary_views.product_type_terms' ); } @@ -1119,8 +1119,8 @@ final class AgentRunnerConfig public function getShopQueryProductAttributeCleanupStopTerms(): array { return $this->getConfiguredStringListOrVocabularyView( - 'shop_prompt.product_attribute_query_cleanup.stop_terms', - 'shop_prompt.product_attribute_query_cleanup.vocabulary_views.stop_terms' + 'shop_runtime.attribute_cleanup.stop_terms', + 'shop_runtime.attribute_cleanup.vocabulary_views.stop_terms' ); } @@ -1129,17 +1129,17 @@ final class AgentRunnerConfig */ public function getShopQueryProductAttributeCleanupComparativeConstraintPatterns(): array { - return $this->getRequiredStringList('shop_prompt.product_attribute_query_cleanup.comparative_constraint_patterns'); + return $this->getRequiredStringList('shop_runtime.attribute_cleanup.comparative_constraint_patterns'); } public function isShopQueryStopwordCleanupEnabled(): bool { - return $this->getRequiredBool('shop_prompt.query_stopword_cleanup.enabled'); + return $this->getRequiredBool('shop_runtime.query_cleanup.stopword_cleanup.enabled'); } public function getShopQueryStopwordCleanupMinTokens(): int { - return $this->getRequiredInt('shop_prompt.query_stopword_cleanup.min_query_tokens_after_cleanup'); + return $this->getRequiredInt('shop_runtime.query_cleanup.stopword_cleanup.min_query_tokens_after_cleanup'); } /** @@ -1147,22 +1147,22 @@ final class AgentRunnerConfig */ public function getShopQueryStopwordCleanupTerms(): array { - return $this->getRequiredStringList('shop_prompt.query_stopword_cleanup.terms'); + return $this->getRequiredStringList('shop_runtime.query_cleanup.stopword_cleanup.terms'); } public function isDirectShopResultGuardEnabled(): bool { - return $this->getRequiredBool('shop_prompt.direct_result_guard.enabled'); + return $this->getRequiredBool('shop_runtime.result_identity.enabled'); } public function shouldPreferDirectShopResultGuardPrimaryIdentityMatches(): bool { - return $this->getOptionalBool('shop_prompt.direct_result_guard.prefer_primary_identity_matches', true); + return $this->getOptionalBool('shop_runtime.result_identity.prefer_primary_identity_matches', true); } public function isDirectShopResultGuardCompoundPrefixMatchEnabled(): bool { - return $this->getOptionalBool('shop_prompt.direct_result_guard.compound_prefix_match.enabled', false); + return $this->getOptionalBool('shop_runtime.result_identity.compound_prefix_match.enabled', false); } /** @@ -1170,17 +1170,17 @@ final class AgentRunnerConfig */ public function getDirectShopResultGuardCompoundPrefixTerms(): array { - return $this->getOptionalStringList('shop_prompt.direct_result_guard.compound_prefix_match.terms'); + return $this->getOptionalStringList('shop_runtime.result_identity.compound_prefix_match.terms'); } public function isDirectShopResultGuardPrimaryIdentityRepairEnabled(): bool { - return $this->getOptionalBool('shop_prompt.direct_result_guard.primary_identity_repair.enabled', true); + return $this->getOptionalBool('shop_runtime.result_identity.primary_identity_repair.enabled', true); } public function getDirectShopResultGuardPrimaryIdentityRepairMinQueryTokens(): int { - return $this->getOptionalInt('shop_prompt.direct_result_guard.primary_identity_repair.min_query_tokens_after_cleanup', 2); + return $this->getOptionalInt('shop_runtime.result_identity.primary_identity_repair.min_query_tokens_after_cleanup', 2); } /** @@ -1188,12 +1188,12 @@ final class AgentRunnerConfig */ public function getDirectShopResultGuardPrimaryIdentityRepairStopTerms(): array { - return $this->getOptionalStringList('shop_prompt.direct_result_guard.primary_identity_repair.stop_terms'); + return $this->getOptionalStringList('shop_runtime.result_identity.primary_identity_repair.stop_terms'); } public function isShopResultLengthSortEnabled(): bool { - return $this->getRequiredBool('shop_prompt.length_sort.enabled'); + return $this->getRequiredBool('shop_runtime.answer_constraints.length_sort.enabled'); } /** @@ -1201,7 +1201,7 @@ final class AgentRunnerConfig */ public function getShopResultLengthSortTriggerPatterns(): array { - return $this->getRequiredStringList('shop_prompt.length_sort.trigger_patterns'); + return $this->getRequiredStringList('shop_runtime.answer_constraints.length_sort.trigger_patterns'); } /** @@ -1209,12 +1209,12 @@ final class AgentRunnerConfig */ public function getShopResultLengthSortValuePatterns(): array { - return $this->getRequiredStringList('shop_prompt.length_sort.value_patterns'); + return $this->getRequiredStringList('shop_runtime.answer_constraints.length_sort.value_patterns'); } public function isShopResultLengthFilterEnabled(): bool { - return $this->getRequiredBool('shop_prompt.length_filter.enabled'); + return $this->getRequiredBool('shop_runtime.answer_constraints.length_filter.enabled'); } /** @@ -1222,7 +1222,7 @@ final class AgentRunnerConfig */ public function getShopResultMinLengthFilterPatterns(): array { - return $this->getRequiredStringList('shop_prompt.length_filter.min_patterns'); + return $this->getRequiredStringList('shop_runtime.answer_constraints.length_filter.min_patterns'); } /** @@ -1230,7 +1230,7 @@ final class AgentRunnerConfig */ public function getShopResultMaxLengthFilterPatterns(): array { - return $this->getRequiredStringList('shop_prompt.length_filter.max_patterns'); + return $this->getRequiredStringList('shop_runtime.answer_constraints.length_filter.max_patterns'); } public function getShopPromptIntro(): string @@ -1304,12 +1304,12 @@ final class AgentRunnerConfig public function isShopQueryMetaGuardEnabled(): bool { - return $this->getRequiredBool('shop_prompt.meta_query_guard.enabled'); + return $this->getRequiredBool('shop_runtime.context_resolution.meta_query_guard.enabled'); } public function getShopQueryContextFallbackCleanupProfile(): string { - $value = $this->optionalValue('shop_prompt.meta_query_guard.cleanup_profile'); + $value = $this->optionalValue('shop_runtime.context_resolution.meta_query_guard.cleanup_profile'); if (is_string($value) && trim($value) !== '') { return trim($value); @@ -1323,32 +1323,32 @@ final class AgentRunnerConfig */ public function getShopQueryMetaOnlyTerms(): array { - return $this->getRequiredStringList('shop_prompt.meta_query_guard.meta_only_terms'); + return $this->getRequiredStringList('shop_runtime.context_resolution.meta_query_guard.meta_only_terms'); } public function isShopQueryContextFallbackEnabled(): bool { - return $this->getRequiredBool('shop_prompt.meta_query_guard.context_fallback_enabled'); + return $this->getRequiredBool('shop_runtime.context_resolution.meta_query_guard.context_fallback_enabled'); } public function getShopQueryContextFallbackQuestionLimit(): int { - return $this->getRequiredInt('shop_prompt.meta_query_guard.context_fallback_question_limit'); + return $this->getRequiredInt('shop_runtime.context_resolution.meta_query_guard.context_fallback_question_limit'); } public function getShopQueryContextFallbackHistoryBudgetChars(): int { - return $this->getRequiredInt('shop_prompt.meta_query_guard.context_fallback_history_budget_chars'); + return $this->getRequiredInt('shop_runtime.context_resolution.meta_query_guard.context_fallback_history_budget_chars'); } public function shouldUseFullHistoryForShopQueryContextFallback(): bool { - return $this->getRequiredBool('shop_prompt.meta_query_guard.context_fallback_use_full_history'); + return $this->getRequiredBool('shop_runtime.context_resolution.meta_query_guard.context_fallback_use_full_history'); } public function getShopQueryContextFallbackMaxTerms(): int { - return $this->getRequiredInt('shop_prompt.meta_query_guard.context_fallback_max_terms'); + return $this->getRequiredInt('shop_runtime.context_resolution.meta_query_guard.context_fallback_max_terms'); } /** @@ -1356,17 +1356,17 @@ final class AgentRunnerConfig */ public function getShopQueryContextFallbackFilterTerms(): array { - return $this->getRequiredStringList('shop_prompt.meta_query_guard.context_fallback_filter_terms'); + return $this->getRequiredStringList('shop_runtime.context_resolution.meta_query_guard.context_fallback_filter_terms'); } public function isShopQueryContextAnchorEnrichmentEnabled(): bool { - return $this->getRequiredBool('shop_prompt.context_anchor_enrichment.enabled'); + return $this->getRequiredBool('shop_runtime.context_resolution.history_anchor_enrichment.enabled'); } public function getShopQueryContextAnchorEnrichmentMaxQueryTerms(): int { - return $this->getRequiredInt('shop_prompt.context_anchor_enrichment.max_query_terms'); + return $this->getRequiredInt('shop_runtime.context_resolution.history_anchor_enrichment.max_query_terms'); } /** @@ -1375,8 +1375,8 @@ final class AgentRunnerConfig public function getShopQueryContextAnchorEnrichmentTriggerTerms(): array { return $this->getConfiguredStringListOrVocabularyView( - 'shop_prompt.context_anchor_enrichment.trigger_terms', - 'shop_prompt.context_anchor_enrichment.vocabulary_views.trigger_terms' + 'shop_runtime.context_resolution.history_anchor_enrichment.trigger_terms', + 'shop_runtime.context_resolution.history_anchor_enrichment.vocabulary_views.trigger_terms' ); } @@ -1385,51 +1385,51 @@ final class AgentRunnerConfig */ public function getShopQueryContextAnchorEnrichmentPatterns(): array { - return $this->getRequiredStringList('shop_prompt.context_anchor_enrichment.anchor_patterns'); + return $this->getRequiredStringList('shop_runtime.context_resolution.history_anchor_enrichment.anchor_patterns'); } public function getShopQueryContextAnchorEnrichmentTemplate(): string { - return $this->getRequiredString('shop_prompt.context_anchor_enrichment.template'); + return $this->getRequiredString('shop_runtime.context_resolution.history_anchor_enrichment.template'); } public function isShopQueryRagAnchorEnrichmentEnabled(): bool { - return $this->getRequiredBool('shop_prompt.rag_anchor_enrichment.enabled'); + return $this->getRequiredBool('shop_runtime.context_resolution.rag_anchor_enrichment.enabled'); } public function getShopQueryRagAnchorEnrichmentMinScore(): int { - return $this->getRequiredInt('shop_prompt.rag_anchor_enrichment.min_score'); + return $this->getRequiredInt('shop_runtime.context_resolution.rag_anchor_enrichment.min_score'); } public function getShopQueryRagAnchorEnrichmentMaxQueryTerms(): int { - return $this->getRequiredInt('shop_prompt.rag_anchor_enrichment.max_query_terms'); + return $this->getRequiredInt('shop_runtime.context_resolution.rag_anchor_enrichment.max_query_terms'); } public function getShopQueryRagAnchorEnrichmentEarlyChunkBonusMax(): int { - return $this->getRequiredInt('shop_prompt.rag_anchor_enrichment.early_chunk_bonus_max'); + return $this->getRequiredInt('shop_runtime.context_resolution.rag_anchor_enrichment.early_chunk_bonus_max'); } public function getShopQueryRagAnchorEnrichmentExactValueUnitScore(): int { - return $this->getRequiredInt('shop_prompt.rag_anchor_enrichment.scores.exact_value_with_unit'); + return $this->getRequiredInt('shop_runtime.context_resolution.rag_anchor_enrichment.scores.exact_value_with_unit'); } public function getShopQueryRagAnchorEnrichmentExactValueScore(): int { - return $this->getRequiredInt('shop_prompt.rag_anchor_enrichment.scores.exact_value_only'); + return $this->getRequiredInt('shop_runtime.context_resolution.rag_anchor_enrichment.scores.exact_value_only'); } public function getShopQueryRagAnchorEnrichmentAnchorBonusScore(): int { - return $this->getRequiredInt('shop_prompt.rag_anchor_enrichment.scores.anchor_bonus'); + return $this->getRequiredInt('shop_runtime.context_resolution.rag_anchor_enrichment.scores.anchor_bonus'); } public function getShopQueryRagAnchorEnrichmentTemplate(): string { - return $this->getRequiredString('shop_prompt.rag_anchor_enrichment.template'); + return $this->getRequiredString('shop_runtime.context_resolution.rag_anchor_enrichment.template'); } /** @@ -1437,7 +1437,7 @@ final class AgentRunnerConfig */ public function getShopQueryRagAnchorEnrichmentNumericFocusPatterns(): array { - return $this->getRequiredStringList('shop_prompt.rag_anchor_enrichment.numeric_focus_patterns'); + return $this->getRequiredStringList('shop_runtime.context_resolution.rag_anchor_enrichment.numeric_focus_patterns'); } /** @@ -1445,7 +1445,7 @@ final class AgentRunnerConfig */ public function getShopQueryRagAnchorEnrichmentProductTitlePatterns(): array { - return $this->getRequiredStringList('shop_prompt.rag_anchor_enrichment.product_title_patterns'); + return $this->getRequiredStringList('shop_runtime.context_resolution.rag_anchor_enrichment.product_title_patterns'); } /** @@ -1453,7 +1453,7 @@ final class AgentRunnerConfig */ public function getShopQueryRagAnchorEnrichmentAnchorBonusPatterns(): array { - return $this->getRequiredStringList('shop_prompt.rag_anchor_enrichment.anchor_bonus_patterns'); + return $this->getRequiredStringList('shop_runtime.context_resolution.rag_anchor_enrichment.anchor_bonus_patterns'); } /** @@ -1461,7 +1461,7 @@ final class AgentRunnerConfig */ public function getShopQueryRagAnchorEnrichmentSubjectTerms(): array { - return $this->getRequiredStringList('shop_prompt.rag_anchor_enrichment.subject_terms'); + return $this->getRequiredStringList('shop_runtime.context_resolution.rag_anchor_enrichment.subject_terms'); } public function getShopQueryTranslationReplacements(string $language): array diff --git a/src/Config/RetriexEffectiveConfigProvider.php b/src/Config/RetriexEffectiveConfigProvider.php index d44cb5c..3a53cb9 100644 --- a/src/Config/RetriexEffectiveConfigProvider.php +++ b/src/Config/RetriexEffectiveConfigProvider.php @@ -655,6 +655,99 @@ final readonly class RetriexEffectiveConfigProvider 'ignore_patterns' => $this->agentRunnerConfig->getFinalAnswerRepeatedLineIgnorePatterns(), ], ], + 'shop_runtime' => [ + 'query_cleanup' => [ + 'current_input_preservation' => [ + 'enabled' => $this->agentRunnerConfig->isShopQueryCurrentInputPreservationEnabled(), + 'terms' => $this->agentRunnerConfig->getShopQueryCurrentInputPreservationTerms(), + ], + 'stopword_cleanup' => [ + 'enabled' => $this->agentRunnerConfig->isShopQueryStopwordCleanupEnabled(), + 'min_query_tokens_after_cleanup' => $this->agentRunnerConfig->getShopQueryStopwordCleanupMinTokens(), + 'terms' => $this->agentRunnerConfig->getShopQueryStopwordCleanupTerms(), + ], + ], + 'attribute_cleanup' => [ + 'enabled' => $this->agentRunnerConfig->isShopQueryProductAttributeCleanupEnabled(), + 'min_query_tokens_after_cleanup' => $this->agentRunnerConfig->getShopQueryProductAttributeCleanupMinTokens(), + 'product_type_terms' => $this->agentRunnerConfig->getShopQueryProductAttributeCleanupProductTypeTerms(), + 'stop_terms' => $this->agentRunnerConfig->getShopQueryProductAttributeCleanupStopTerms(), + 'comparative_constraint_patterns' => $this->agentRunnerConfig->getShopQueryProductAttributeCleanupComparativeConstraintPatterns(), + ], + 'context_resolution' => [ + 'context_usage' => [ + 'referential_terms' => $this->agentRunnerConfig->getShopQueryContextUsageReferentialTerms(), + ], + 'history_anchor_enrichment' => [ + 'enabled' => $this->agentRunnerConfig->isShopQueryContextAnchorEnrichmentEnabled(), + 'max_query_terms' => $this->agentRunnerConfig->getShopQueryContextAnchorEnrichmentMaxQueryTerms(), + 'trigger_terms' => $this->agentRunnerConfig->getShopQueryContextAnchorEnrichmentTriggerTerms(), + 'anchor_patterns' => $this->agentRunnerConfig->getShopQueryContextAnchorEnrichmentPatterns(), + 'template' => $this->agentRunnerConfig->getShopQueryContextAnchorEnrichmentTemplate(), + ], + 'meta_query_guard' => [ + 'enabled' => $this->agentRunnerConfig->isShopQueryMetaGuardEnabled(), + 'cleanup_profile' => $this->agentRunnerConfig->getShopQueryContextFallbackCleanupProfile(), + 'context_fallback_use_full_history' => $this->agentRunnerConfig->shouldUseFullHistoryForShopQueryContextFallback(), + 'meta_only_terms' => $this->agentRunnerConfig->getShopQueryMetaOnlyTerms(), + 'context_fallback_enabled' => $this->agentRunnerConfig->isShopQueryContextFallbackEnabled(), + 'context_fallback_question_limit' => $this->agentRunnerConfig->getShopQueryContextFallbackQuestionLimit(), + 'context_fallback_history_budget_chars' => $this->agentRunnerConfig->getShopQueryContextFallbackHistoryBudgetChars(), + 'context_fallback_max_terms' => $this->agentRunnerConfig->getShopQueryContextFallbackMaxTerms(), + 'context_fallback_filter_terms' => $this->agentRunnerConfig->getShopQueryContextFallbackFilterTerms(), + ], + 'rag_anchor_enrichment' => [ + 'enabled' => $this->agentRunnerConfig->isShopQueryRagAnchorEnrichmentEnabled(), + 'min_score' => $this->agentRunnerConfig->getShopQueryRagAnchorEnrichmentMinScore(), + 'max_query_terms' => $this->agentRunnerConfig->getShopQueryRagAnchorEnrichmentMaxQueryTerms(), + 'early_chunk_bonus_max' => $this->agentRunnerConfig->getShopQueryRagAnchorEnrichmentEarlyChunkBonusMax(), + 'template' => $this->agentRunnerConfig->getShopQueryRagAnchorEnrichmentTemplate(), + 'scores' => [ + 'exact_value_with_unit' => $this->agentRunnerConfig->getShopQueryRagAnchorEnrichmentExactValueUnitScore(), + 'exact_value_only' => $this->agentRunnerConfig->getShopQueryRagAnchorEnrichmentExactValueScore(), + 'anchor_bonus' => $this->agentRunnerConfig->getShopQueryRagAnchorEnrichmentAnchorBonusScore(), + ], + 'numeric_focus_patterns' => $this->agentRunnerConfig->getShopQueryRagAnchorEnrichmentNumericFocusPatterns(), + 'product_title_patterns' => $this->agentRunnerConfig->getShopQueryRagAnchorEnrichmentProductTitlePatterns(), + 'anchor_bonus_patterns' => $this->agentRunnerConfig->getShopQueryRagAnchorEnrichmentAnchorBonusPatterns(), + 'subject_terms' => $this->agentRunnerConfig->getShopQueryRagAnchorEnrichmentSubjectTerms(), + ], + ], + 'result_identity' => [ + 'enabled' => $this->agentRunnerConfig->isDirectShopResultGuardEnabled(), + 'prefer_primary_identity_matches' => $this->agentRunnerConfig->shouldPreferDirectShopResultGuardPrimaryIdentityMatches(), + 'compound_prefix_match' => [ + 'enabled' => $this->agentRunnerConfig->isDirectShopResultGuardCompoundPrefixMatchEnabled(), + 'terms' => $this->agentRunnerConfig->getDirectShopResultGuardCompoundPrefixTerms(), + ], + 'primary_identity_repair' => [ + 'enabled' => $this->agentRunnerConfig->isDirectShopResultGuardPrimaryIdentityRepairEnabled(), + 'min_query_tokens_after_cleanup' => $this->agentRunnerConfig->getDirectShopResultGuardPrimaryIdentityRepairMinQueryTokens(), + 'stop_terms' => $this->agentRunnerConfig->getDirectShopResultGuardPrimaryIdentityRepairStopTerms(), + ], + ], + 'answer_constraints' => [ + 'length_sort' => [ + 'enabled' => $this->agentRunnerConfig->isShopResultLengthSortEnabled(), + 'trigger_patterns' => $this->agentRunnerConfig->getShopResultLengthSortTriggerPatterns(), + 'value_patterns' => $this->agentRunnerConfig->getShopResultLengthSortValuePatterns(), + ], + 'length_filter' => [ + 'enabled' => $this->agentRunnerConfig->isShopResultLengthFilterEnabled(), + 'min_patterns' => $this->agentRunnerConfig->getShopResultMinLengthFilterPatterns(), + 'max_patterns' => $this->agentRunnerConfig->getShopResultMaxLengthFilterPatterns(), + ], + ], + 'direct_answer' => [ + 'enabled' => $this->agentRunnerConfig->isDirectShopResultAnswerEnabled(), + 'max_results' => $this->agentRunnerConfig->getDirectShopResultAnswerMaxResults(), + 'intro' => $this->agentRunnerConfig->getDirectShopResultAnswerIntro(), + 'no_results' => $this->agentRunnerConfig->getDirectShopResultAnswerNoResultsMessage(), + 'sorted_by_length_note' => $this->agentRunnerConfig->getDirectShopResultAnswerSortedByLengthNote(), + 'min_length_filter_note' => $this->agentRunnerConfig->getDirectShopResultAnswerMinLengthFilterNote(), + 'max_length_filter_note' => $this->agentRunnerConfig->getDirectShopResultAnswerMaxLengthFilterNote(), + ], + ], 'rag_evidence_guard' => [ 'cleanup_profile' => $this->agentRunnerConfig->getRagEvidenceCleanupProfile(), 'stop_terms' => $this->agentRunnerConfig->getRagEvidenceStopTerms(), @@ -691,31 +784,6 @@ final readonly class RetriexEffectiveConfigProvider 'language_markers' => $this->agentRunnerConfig->getShopQueryLanguageMarkers(), 'translation_replacements_de' => $this->agentRunnerConfig->getShopQueryTranslationReplacements('de'), ], - 'context_usage' => [ - 'referential_terms' => $this->agentRunnerConfig->getShopQueryContextUsageReferentialTerms(), - ], - 'current_input_preservation' => [ - 'enabled' => $this->agentRunnerConfig->isShopQueryCurrentInputPreservationEnabled(), - 'terms' => $this->agentRunnerConfig->getShopQueryCurrentInputPreservationTerms(), - ], - 'context_anchor_enrichment' => [ - 'enabled' => $this->agentRunnerConfig->isShopQueryContextAnchorEnrichmentEnabled(), - 'max_query_terms' => $this->agentRunnerConfig->getShopQueryContextAnchorEnrichmentMaxQueryTerms(), - 'trigger_terms' => $this->agentRunnerConfig->getShopQueryContextAnchorEnrichmentTriggerTerms(), - 'anchor_patterns' => $this->agentRunnerConfig->getShopQueryContextAnchorEnrichmentPatterns(), - 'template' => $this->agentRunnerConfig->getShopQueryContextAnchorEnrichmentTemplate(), - ], - 'meta_query_guard' => [ - 'enabled' => $this->agentRunnerConfig->isShopQueryMetaGuardEnabled(), - 'cleanup_profile' => $this->agentRunnerConfig->getShopQueryContextFallbackCleanupProfile(), - 'context_fallback_use_full_history' => $this->agentRunnerConfig->shouldUseFullHistoryForShopQueryContextFallback(), - 'meta_only_terms' => $this->agentRunnerConfig->getShopQueryMetaOnlyTerms(), - 'context_fallback_enabled' => $this->agentRunnerConfig->isShopQueryContextFallbackEnabled(), - 'context_fallback_question_limit' => $this->agentRunnerConfig->getShopQueryContextFallbackQuestionLimit(), - 'context_fallback_history_budget_chars' => $this->agentRunnerConfig->getShopQueryContextFallbackHistoryBudgetChars(), - 'context_fallback_max_terms' => $this->agentRunnerConfig->getShopQueryContextFallbackMaxTerms(), - 'context_fallback_filter_terms' => $this->agentRunnerConfig->getShopQueryContextFallbackFilterTerms(), - ], ], ]; } @@ -1281,8 +1349,10 @@ final readonly class RetriexEffectiveConfigProvider $this->validateStringList($this->toList($ragEvidence['aggregate_evidence_terms'] ?? []), 'agent.rag_evidence_guard.aggregate_evidence_terms', $errors, $warnings); $this->validateRegexPatternList($ragEvidence['aggregate_answer_evidence_patterns'] ?? [], 'agent.rag_evidence_guard.aggregate_answer_evidence_patterns', $errors); - $shopPrompt = is_array($agent['shop_prompt'] ?? null) ? $agent['shop_prompt'] : []; - $metaQueryGuard = is_array($shopPrompt['meta_query_guard'] ?? null) ? $shopPrompt['meta_query_guard'] : []; + $shopRuntime = is_array($agent['shop_runtime'] ?? null) ? $agent['shop_runtime'] : []; + $queryCleanup = is_array($shopRuntime['query_cleanup'] ?? null) ? $shopRuntime['query_cleanup'] : []; + $contextResolution = is_array($shopRuntime['context_resolution'] ?? null) ? $shopRuntime['context_resolution'] : []; + $metaQueryGuard = is_array($contextResolution['meta_query_guard'] ?? null) ? $contextResolution['meta_query_guard'] : []; $shopContextCleanupProfile = $metaQueryGuard['cleanup_profile'] ?? null; if (!is_string($shopContextCleanupProfile) || trim($shopContextCleanupProfile) === '') { $shopContextCleanupProfile = $this->agentRunnerConfig->getShopQueryContextFallbackCleanupProfile(); @@ -1290,16 +1360,16 @@ final readonly class RetriexEffectiveConfigProvider $shopContextCleanupProfile = trim($shopContextCleanupProfile); } if (!in_array($shopContextCleanupProfile, $this->languageCleanupConfig->getCleanupProfileNames(), true)) { - $errors[] = 'agent.shop_prompt.meta_query_guard.cleanup_profile references unknown language cleanup profile: ' . $shopContextCleanupProfile . '.'; + $errors[] = 'agent.shop_runtime.context_resolution.meta_query_guard.cleanup_profile references unknown language cleanup profile: ' . $shopContextCleanupProfile . '.'; } - $currentInputPreservation = is_array($shopPrompt['current_input_preservation'] ?? null) ? $shopPrompt['current_input_preservation'] : []; + $currentInputPreservation = is_array($queryCleanup['current_input_preservation'] ?? null) ? $queryCleanup['current_input_preservation'] : []; if (array_key_exists('enabled', $currentInputPreservation) && !is_bool($currentInputPreservation['enabled'])) { - $errors[] = 'agent.shop_prompt.current_input_preservation.enabled must be boolean.'; + $errors[] = 'agent.shop_runtime.query_cleanup.current_input_preservation.enabled must be boolean.'; } $this->validateStringList( $this->toList($currentInputPreservation['terms'] ?? []), - 'agent.shop_prompt.current_input_preservation.terms', + 'agent.shop_runtime.query_cleanup.current_input_preservation.terms', $errors, $warnings ); @@ -1359,17 +1429,20 @@ final readonly class RetriexEffectiveConfigProvider $errors[] = 'agent.input_normalization.prompt.current_user_input_label must not be empty.'; } - $contextUsage = $agent['shop_query_optimizer']['context_usage'] ?? []; + $shopRuntime = is_array($agent['shop_runtime'] ?? null) ? $agent['shop_runtime'] : []; + $contextResolution = is_array($shopRuntime['context_resolution'] ?? null) ? $shopRuntime['context_resolution'] : []; + + $contextUsage = $contextResolution['context_usage'] ?? []; if (is_array($contextUsage)) { - $this->validateStringList($this->toList($contextUsage['referential_terms'] ?? []), 'agent.shop_query_optimizer.context_usage.referential_terms', $errors, $warnings); + $this->validateStringList($this->toList($contextUsage['referential_terms'] ?? []), 'agent.shop_runtime.context_resolution.context_usage.referential_terms', $errors, $warnings); } - $anchorEnrichment = $agent['shop_query_optimizer']['context_anchor_enrichment'] ?? []; + $anchorEnrichment = $contextResolution['history_anchor_enrichment'] ?? []; if (is_array($anchorEnrichment)) { - $this->validateStringList($this->toList($anchorEnrichment['trigger_terms'] ?? []), 'agent.shop_query_optimizer.context_anchor_enrichment.trigger_terms', $errors, $warnings); - $this->validateRegexPatternList($anchorEnrichment['anchor_patterns'] ?? [], 'agent.shop_query_optimizer.context_anchor_enrichment.anchor_patterns', $errors); + $this->validateStringList($this->toList($anchorEnrichment['trigger_terms'] ?? []), 'agent.shop_runtime.context_resolution.history_anchor_enrichment.trigger_terms', $errors, $warnings); + $this->validateRegexPatternList($anchorEnrichment['anchor_patterns'] ?? [], 'agent.shop_runtime.context_resolution.history_anchor_enrichment.anchor_patterns', $errors); if (trim((string) ($anchorEnrichment['template'] ?? '')) === '') { - $errors[] = 'agent.shop_query_optimizer.context_anchor_enrichment.template must not be empty.'; + $errors[] = 'agent.shop_runtime.context_resolution.history_anchor_enrichment.template must not be empty.'; } } }