This commit is contained in:
team 1
2026-05-02 17:27:34 +02:00
parent e4c05d6ad5
commit 0f89c5c0f6
6 changed files with 66 additions and 7 deletions

View File

@@ -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

View File

@@ -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.'

View File

@@ -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<int, string[]>
*/
@@ -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';
}

View File

@@ -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

View File

@@ -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');

View File

@@ -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);