fix retrieve final technical questions v4

This commit is contained in:
team 1
2026-04-24 11:49:02 +02:00
parent 8a7cb89c5d
commit 66f09e83ca
4 changed files with 356 additions and 13 deletions

View File

@@ -54,6 +54,8 @@ final readonly class AgentRunner
$optimizedShopQuery = '';
$shopSearchQuery = '';
$commerceIntent = CommerceIntentLite::NONE;
$knowledgeRetrievalPrompt = $prompt;
$usedFollowUpRetrievalContext = false;
$commerceHistoryContext = '';
$attemptedShopRepair = false;
$usedShopRepair = false;
@@ -77,14 +79,30 @@ final readonly class AgentRunner
$this->addSource($sources, $this->agentRunnerConfig->getExternalUrlSourceLabel());
}
$commerceIntent = $this->detectCommerceIntent($prompt);
yield $this->systemMsg($this->agentRunnerConfig->getRetrieveKnowledgeMessage(), 'think');
$knowledgeChunks = $this->retriever->retrieve($prompt);
$knowledgeRetrievalPrompt = $this->buildKnowledgeRetrievalPrompt(
prompt: $prompt,
userId: $userId,
commerceIntent: $commerceIntent
);
$usedFollowUpRetrievalContext = $knowledgeRetrievalPrompt !== $prompt;
$knowledgeChunks = $this->retriever->retrieve($knowledgeRetrievalPrompt);
if ($knowledgeChunks !== []) {
$this->addSource($sources, $this->agentRunnerConfig->getRagKnowledgeSourceLabel());
}
$commerceIntent = $this->detectCommerceIntent($prompt);
if ($usedFollowUpRetrievalContext) {
$this->agentLogger->info('Knowledge retrieval used follow-up context', [
'userId' => $userId,
'prompt' => $prompt,
'knowledgeRetrievalPrompt' => $knowledgeRetrievalPrompt,
'commerceIntent' => $commerceIntent,
]);
}
if ($this->isCommerceIntent($commerceIntent)) {
yield $this->systemMsg($this->agentRunnerConfig->getOptimizeSearchMessage(), 'think');
@@ -171,6 +189,8 @@ final readonly class AgentRunner
'finalPrompt' => $finalPrompt,
'optimizedShopQuery' => $optimizedShopQuery,
'shopSearchQuery' => $shopSearchQuery,
'knowledgeRetrievalPrompt' => $knowledgeRetrievalPrompt,
'usedFollowUpRetrievalContext' => $usedFollowUpRetrievalContext,
'primaryShopResultsCount' => count($primaryShopResults),
'shopResultsCount' => count($shopResults),
'attemptedShopRepair' => $attemptedShopRepair,
@@ -228,6 +248,8 @@ final readonly class AgentRunner
'usedShopRepair' => $usedShopRepair,
'shopRepairQueries' => $shopRepairQueries,
'knowledgeChunkCount' => count($knowledgeChunks),
'knowledgeRetrievalPrompt' => $knowledgeRetrievalPrompt,
'usedFollowUpRetrievalContext' => $usedFollowUpRetrievalContext,
'hasUrlContent' => $urlContent !== '',
'usedOptimizedShopQuery' => $optimizedShopQuery !== '',
'optimizedShopQuery' => $optimizedShopQuery,
@@ -258,6 +280,159 @@ final readonly class AgentRunner
|| $commerceIntent === CommerceIntentLite::ADVISORY_PRODUCT_SEARCH;
}
private function buildKnowledgeRetrievalPrompt(
string $prompt,
string $userId,
string $commerceIntent
): string {
if (!$this->shouldUseFollowUpContextForKnowledgeRetrieval($prompt, $commerceIntent)) {
return $prompt;
}
$history = $this->contextService->buildUserContextWithinBudget($userId, 3000);
$previousQuestions = $this->extractRecentUserQuestions($history, 2);
if ($previousQuestions === []) {
return $prompt;
}
$lines = [];
foreach ($previousQuestions as $question) {
$lines[] = 'Vorherige Nutzerfrage: ' . $question;
}
$lines[] = 'Aktuelle Folgefrage: ' . $prompt;
return implode("\n", $lines);
}
private function shouldUseFollowUpContextForKnowledgeRetrieval(string $prompt, string $commerceIntent): bool
{
if ($this->isCommerceIntent($commerceIntent)) {
return false;
}
$normalized = $this->normalizeFollowUpText($prompt);
if ($normalized === '') {
return false;
}
if ($this->containsExplicitCommercialFollowUpSignal($normalized)) {
return false;
}
if (mb_strlen($normalized, 'UTF-8') > 180 && !$this->containsStrongFollowUpReference($normalized)) {
return false;
}
return $this->containsStrongFollowUpReference($normalized);
}
private function containsStrongFollowUpReference(string $normalized): bool
{
$patterns = [
'/\bder\s+wert\b/u',
'/\bdieser\s+wert\b/u',
'/\bdiesen\s+wert\b/u',
'/\bdem\s+wert\b/u',
'/\bmit\s+welche(?:m|n|r)?\b/u',
'/\bwomit\b/u',
'/\bdamit\b/u',
'/\bdafuer\b/u',
'/\bdafür\b/u',
'/\bdazu\b/u',
'/\bdaraus\b/u',
'/\bwelche(?:r|s|m|n)?\s+indikator\b/u',
'/\bwelche(?:r|s|m|n)?\s+indikatortyp\b/u',
'/\bindikator\s+(?:dafuer|dafür|dazu|hierfuer|hierfür)\b/u',
'/\bwelche(?:r|s|m|n)?\s+bereich\b/u',
'/\bwelche(?:r|s|m|n)?\s+messbereich\b/u',
'/\bwelche(?:r|s|m|n)?\s+grenzwert\b/u',
];
foreach ($patterns as $pattern) {
if (preg_match($pattern, $normalized) === 1) {
return true;
}
}
return false;
}
private function containsExplicitCommercialFollowUpSignal(string $normalized): bool
{
$commercialSignals = [
'shop', 'preis', 'preise', 'kostet', 'kosten', 'kaufen', 'bestellen',
'warenkorb', 'lieferzeit', 'verfuegbar', 'verfügbar', 'lager', 'url',
'link', 'artikelnummer', 'sku', 'produktnummer',
];
foreach ($commercialSignals as $signal) {
if (str_contains($normalized, $signal)) {
return true;
}
}
return false;
}
/**
* @return string[]
*/
private function extractRecentUserQuestions(string $history, int $limit): array
{
$history = trim($history);
if ($history === '' || $limit <= 0) {
return [];
}
if (preg_match_all('/^Question:\s*(.+)$/mi', $history, $matches) !== 1) {
return [];
}
$questions = array_values(array_filter(
array_map(
fn(string $question): string => $this->sanitizeHistoryQuestion($question),
$matches[1] ?? []
),
static fn(string $question): bool => $question !== ''
));
if ($questions === []) {
return [];
}
return array_slice($questions, -$limit);
}
private function sanitizeHistoryQuestion(string $question): string
{
$question = trim((string) preg_replace('/\s+/u', ' ', $question));
if ($question === '') {
return '';
}
if (mb_strlen($question, 'UTF-8') <= 500) {
return $question;
}
return rtrim(mb_substr($question, 0, 497, 'UTF-8')) . '...';
}
private function normalizeFollowUpText(string $value): string
{
$value = mb_strtolower(trim($value), 'UTF-8');
$value = str_replace(['-', '/', '_'], ' ', $value);
$value = preg_replace('/[^\p{L}\p{N}\s]+/u', ' ', $value) ?? $value;
$value = preg_replace('/\s+/u', ' ', $value) ?? $value;
return trim($value);
}
private function buildOptimizedShopQuery(
string $prompt,
string $userId,