From 0f89c5c0f6cdc672d7275cd1be2fad5fd6205d59 Mon Sep 17 00:00:00 2001 From: team 1 Date: Sat, 2 May 2026 17:27:34 +0200 Subject: [PATCH] patch 18 --- config/retriex/agent.yaml | 5 ++ config/retriex/prompt.yaml | 5 ++ src/Agent/AgentRunner.php | 46 ++++++++++++++++--- src/Agent/PromptBuilder.php | 7 ++- src/Config/AgentRunnerConfig.php | 8 ++++ src/Config/RetriexEffectiveConfigProvider.php | 2 + 6 files changed, 66 insertions(+), 7 deletions(-) diff --git a/config/retriex/agent.yaml b/config/retriex/agent.yaml index c17e232..499c63b 100644 --- a/config/retriex/agent.yaml +++ b/config/retriex/agent.yaml @@ -123,6 +123,11 @@ parameters: - bestände - bestaende - lieferprogramm + aggregate_answer_evidence_patterns: + - '/\b(?:anzahl|gesamtzahl|stückzahl|stueckzahl|count)\b.{0,80}\b\d+\b/u' + - '/\b\d+\s+(?:[a-z0-9+\-]+\s+){0,3}(?:produkte|artikel|geräte|geraete|messgeräte|messgeraete)\b/u' + - '/\b(?:insgesamt|gesamt|total)\b.{0,80}\b\d+\b/u' + - '/\b(?:sortiment|portfolio|lieferprogramm)\b.{0,120}\b(?:umfasst|enthält|enthaelt|besteht\s+aus|beinhaltet)\b.{0,80}\b\d+\b/u' synonyms: salinität: - salinität diff --git a/config/retriex/prompt.yaml b/config/retriex/prompt.yaml index 796091a..6958552 100644 --- a/config/retriex/prompt.yaml +++ b/config/retriex/prompt.yaml @@ -404,6 +404,11 @@ parameters: - '- State that no reliable information was found in the provided RAG knowledge, URL content, or shop results.' - '- Do not answer with "gibt es nicht". Use narrow wording such as "Ich finde dazu keine belastbaren Daten in den vorliegenden Quellen."' - '- Ask one focused clarification question if a parameter, product family, accessory type, or application context would make the search answerable.' + aggregatfrage_keine_belastbare_zaehlinformation: + - '- The user asks for a count or aggregate number, but the retrieved sources do not contain an explicit count/aggregate answer.' + - '- Do not present nearby product-family or portfolio mentions as proof of a concrete count.' + - '- Say narrowly: "Ich habe passende Quellen geprüft, finde darin aber keine belastbare Zählinformation für die angefragte Anzahl."' + - '- If helpful, explain that individual product mentions are not the same as a maintained aggregate count.' semantische_rag_treffer_kein_direkter_fachbeleg: - '- Retrieved RAG records are semantic nearest-neighbor hits only; they are not a direct factual match for the essential user term or configured synonym.' - '- Do not present these RAG hits as fachlich belegt. Say narrowly that the RAG knowledge does not contain a direct Fachbeleg for the requested term.' diff --git a/src/Agent/AgentRunner.php b/src/Agent/AgentRunner.php index 8ee16a5..2883733 100644 --- a/src/Agent/AgentRunner.php +++ b/src/Agent/AgentRunner.php @@ -354,7 +354,8 @@ final readonly class AgentRunner isCommerceIntent: true, shopSearchAttempted: $shopSearchAttempted, hasShopResults: $shopResults !== [], - shopSearchHadSystemFailure: $primaryShopSearchHadSystemFailure + shopSearchHadSystemFailure: $primaryShopSearchHadSystemFailure, + knowledgeEvidenceState: $knowledgeEvidenceState ) ), 'meta' @@ -419,7 +420,8 @@ final readonly class AgentRunner isCommerceIntent: $this->isCommerceIntent($commerceIntent), shopSearchAttempted: $shopSearchAttempted, hasShopResults: $shopResults !== [], - shopSearchHadSystemFailure: $primaryShopSearchHadSystemFailure + shopSearchHadSystemFailure: $primaryShopSearchHadSystemFailure, + knowledgeEvidenceState: $knowledgeEvidenceState ) ), 'meta' @@ -458,7 +460,8 @@ final readonly class AgentRunner isCommerceIntent: $this->isCommerceIntent($commerceIntent), shopSearchAttempted: $shopSearchAttempted, hasShopResults: $shopResults !== [], - shopSearchHadSystemFailure: $primaryShopSearchHadSystemFailure + shopSearchHadSystemFailure: $primaryShopSearchHadSystemFailure, + knowledgeEvidenceState: $knowledgeEvidenceState ), completed: true ), @@ -1652,12 +1655,20 @@ final readonly class AgentRunner } $haystack = $this->normalizeRagEvidenceText(implode("\n\n", array_map('strval', $knowledgeChunks))); + $isAggregateQuery = $this->isAggregateRagEvidenceQuery($prompt); if ( - $this->isAggregateRagEvidenceQuery($prompt) + $isAggregateQuery + && !$this->containsAnyRagEvidencePattern($haystack, $this->agentRunnerConfig->getRagEvidenceAggregateAnswerEvidencePatterns()) + ) { + return 'aggregate_missing'; + } + + if ( + $isAggregateQuery && !$this->containsAnyRagEvidenceTerm($haystack, $this->agentRunnerConfig->getRagEvidenceAggregateEvidenceTerms()) ) { - return 'weak'; + return 'aggregate_missing'; } foreach ($needles as $needleGroup) { @@ -1680,6 +1691,7 @@ final readonly class AgentRunner { return match ($knowledgeEvidenceState) { 'direct' => 'fachlich belegt', + 'aggregate_missing' => 'geprüfte Quellen, keine passende Zählinformation', 'weak' => 'RAG-Näherungstreffer, kein direkter Fachbeleg', default => 'noch keine belastbaren Treffer', }; @@ -1689,6 +1701,7 @@ final readonly class AgentRunner { return match ($knowledgeEvidenceState) { 'direct' => 'fachlich belegt; Shopdaten werden geprüft', + 'aggregate_missing' => 'geprüfte Quellen ohne Zählinformation; Shopdaten werden geprüft', 'weak' => 'RAG-Näherungstreffer; Shopdaten werden geprüft', default => 'Shopdaten werden geprüft', }; @@ -1725,6 +1738,20 @@ final readonly class AgentRunner return false; } + /** + * @param string[] $patterns + */ + private function containsAnyRagEvidencePattern(string $haystack, array $patterns): bool + { + foreach ($patterns as $pattern) { + if (@preg_match($pattern, $haystack) === 1) { + return true; + } + } + + return false; + } + /** * @return array */ @@ -1968,8 +1995,15 @@ final readonly class AgentRunner bool $isCommerceIntent, bool $shopSearchAttempted, bool $hasShopResults, - bool $shopSearchHadSystemFailure + bool $shopSearchHadSystemFailure, + string $knowledgeEvidenceState = 'unknown' ): string { + if ($knowledgeEvidenceState === 'aggregate_missing' && !$hasShopResults) { + return $shopSearchHadSystemFailure + ? 'geprüfte Quellen ohne Zählinformation; Shopdaten nicht verfügbar' + : 'geprüfte Quellen, keine passende Zählinformation'; + } + if ($shopSearchHadSystemFailure) { return $hasKnowledge ? 'fachlich belegt; Shopdaten nicht verfügbar' : 'Shopdaten nicht verfügbar'; } diff --git a/src/Agent/PromptBuilder.php b/src/Agent/PromptBuilder.php index 5768172..c6d0e5f 100644 --- a/src/Agent/PromptBuilder.php +++ b/src/Agent/PromptBuilder.php @@ -281,7 +281,12 @@ final readonly class PromptBuilder string $knowledgeEvidenceState = 'unknown' ): string { $hasDirectKnowledgeEvidence = $knowledgeEvidenceState === 'direct' || $knowledgeEvidenceState === 'unknown' && $hasKnowledge; - $hasWeakKnowledgeEvidence = $knowledgeEvidenceState === 'weak'; + $hasAggregateMissingEvidence = $knowledgeEvidenceState === 'aggregate_missing'; + $hasWeakKnowledgeEvidence = $knowledgeEvidenceState === 'weak' || $hasAggregateMissingEvidence; + + if ($hasAggregateMissingEvidence && !$hasShopResults && !$shopSearchHadSystemFailure) { + return 'aggregatfrage_keine_belastbare_zaehlinformation'; + } if ($shopSearchHadSystemFailure && !$hasDirectKnowledgeEvidence) { return $hasWeakKnowledgeEvidence diff --git a/src/Config/AgentRunnerConfig.php b/src/Config/AgentRunnerConfig.php index 5d45c34..6196b6b 100644 --- a/src/Config/AgentRunnerConfig.php +++ b/src/Config/AgentRunnerConfig.php @@ -355,6 +355,14 @@ final class AgentRunnerConfig return $this->getRequiredStringList('rag_evidence_guard.aggregate_evidence_terms'); } + /** + * @return string[] + */ + public function getRagEvidenceAggregateAnswerEvidencePatterns(): array + { + return $this->getRequiredStringList('rag_evidence_guard.aggregate_answer_evidence_patterns'); + } + public function getNoLlmFallbackShopOnlyMessage(): string { return $this->getRequiredString('no_llm_fallback.messages.shop_only'); diff --git a/src/Config/RetriexEffectiveConfigProvider.php b/src/Config/RetriexEffectiveConfigProvider.php index aeecfde..5d9a740 100644 --- a/src/Config/RetriexEffectiveConfigProvider.php +++ b/src/Config/RetriexEffectiveConfigProvider.php @@ -460,6 +460,7 @@ final readonly class RetriexEffectiveConfigProvider 'synonyms' => $this->agentRunnerConfig->getRagEvidenceSynonyms(), 'aggregate_query_patterns' => $this->agentRunnerConfig->getRagEvidenceAggregateQueryPatterns(), 'aggregate_evidence_terms' => $this->agentRunnerConfig->getRagEvidenceAggregateEvidenceTerms(), + 'aggregate_answer_evidence_patterns' => $this->agentRunnerConfig->getRagEvidenceAggregateAnswerEvidencePatterns(), ], 'source_labels' => [ 'external_url' => $this->agentRunnerConfig->getExternalUrlSourceLabel(), @@ -1021,6 +1022,7 @@ final readonly class RetriexEffectiveConfigProvider $this->validateStringListMap($ragEvidence['synonyms'] ?? [], 'agent.rag_evidence_guard.synonyms', $errors, $warnings); $this->validateRegexPatternList($ragEvidence['aggregate_query_patterns'] ?? [], 'agent.rag_evidence_guard.aggregate_query_patterns', $errors); $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); $this->validateStringListMap($agent['shop_query_optimizer'] ?? [], 'agent.shop_query_optimizer', $errors, $warnings); $this->validateRegexPattern($agent['optimized_shop_query_prefix_pattern'] ?? null, 'agent.optimized_shop_query_prefix_pattern', $errors);