harden retrieval logic

bugfixes
This commit is contained in:
team2
2026-04-18 21:49:30 +02:00
parent a2425b68a6
commit 5984091282
7 changed files with 426 additions and 108 deletions

View File

@@ -51,6 +51,9 @@ final readonly class AgentRunner
$shopResults = [];
$sources = [];
$optimizedShopQuery = '';
$shopSearchQuery = '';
$commerceIntent = CommerceIntentLite::NONE;
$commerceHistoryContext = '';
$this->agentLogger->info('Agent run started', [
'userId' => $userId,
@@ -97,7 +100,7 @@ final readonly class AgentRunner
$commerceHistoryContext = $this->buildCommerceHistoryContext($userId);
if($commerceHistoryContext){
if ($commerceHistoryContext !== '') {
$this->addSource($sources, 'Chatverlauf');
}
@@ -109,6 +112,16 @@ final readonly class AgentRunner
$shopSearchQuery = $optimizedShopQuery !== '' ? $optimizedShopQuery : $prompt;
$this->agentLogger->info('Commerce search prepared', [
'userId' => $userId,
'commerceIntent' => $commerceIntent,
'usedOptimizedShopQuery' => $optimizedShopQuery !== '',
'optimizedShopQuery' => $optimizedShopQuery,
'shopSearchQuery' => $shopSearchQuery,
'hasCommerceHistoryContext' => $commerceHistoryContext !== '',
'commerceHistoryContextLength' => mb_strlen($commerceHistoryContext),
]);
yield $this->systemMsg(
'Ich rufe Recherchedaten ab (type: ' . $commerceIntent . ')',
'think'
@@ -126,7 +139,9 @@ final readonly class AgentRunner
}
}
$knowledgeChunks = $this->limitKnowledgeChunks($knowledgeChunks, $commerceIntent);
if ($shopResults !== []) {
$knowledgeChunks = $this->limitKnowledgeChunks($knowledgeChunks, $commerceIntent);
}
yield $this->systemMsg('Ich analysiere alle Informationen...', 'think');
@@ -148,6 +163,7 @@ final readonly class AgentRunner
'userId' => $userId,
'finalPrompt' => $finalPrompt,
'optimizedShopQuery' => $optimizedShopQuery,
'shopSearchQuery' => $shopSearchQuery,
]);
}
@@ -198,6 +214,10 @@ final readonly class AgentRunner
'knowledgeChunkCount' => count($knowledgeChunks),
'hasUrlContent' => $urlContent !== '',
'usedOptimizedShopQuery' => $optimizedShopQuery !== '',
'optimizedShopQuery' => $optimizedShopQuery,
'shopSearchQuery' => $shopSearchQuery,
'hasCommerceHistoryContext' => $commerceHistoryContext !== '',
'commerceHistoryContextLength' => mb_strlen($commerceHistoryContext),
]);
} catch (Throwable $e) {
$this->agentLogger->error('Agent run failed', [
@@ -282,6 +302,8 @@ final readonly class AgentRunner
'userId' => $userId,
'commerceIntent' => $commerceIntent,
'query' => $query,
'hasCommerceHistoryContext' => $commerceHistoryContext !== '',
'commerceHistoryContextLength' => mb_strlen($commerceHistoryContext),
'exception' => $e,
]);

View File

@@ -42,6 +42,13 @@ final readonly class PromptBuilder
*/
private const MIN_PROMPT_BUDGET_TOKENS = 1024;
/**
* Limit how many ranked shop results are passed into the final prompt.
* The shop search may return many candidates, but the LLM should only see
* the most relevant top subset after local reranking.
*/
private const MAX_SHOP_RESULTS_IN_PROMPT = 8;
/**
* Technical product prompts should be answered like documentation,
* not like sales copy.
@@ -84,8 +91,7 @@ final readonly class PromptBuilder
private ContextService $contextService,
private SystemPromptRepository $systemPromptRepository,
private ModelGenerationConfigProvider $modelGenerationConfigProvider,
)
{
) {
}
/**
@@ -222,18 +228,21 @@ final readonly class PromptBuilder
"Source: Shop Search";
}
if ($shopResults === []) {
$normalizedShopResults = array_values(array_filter(
$shopResults,
static fn(mixed $product): bool => $product instanceof ShopProductResult
));
if ($normalizedShopResults === []) {
return $this->implodeBlocks($parts);
}
$isDetailed = count($shopResults) <= 5;
$totalCount = count($normalizedShopResults);
$limitedShopResults = array_slice($normalizedShopResults, 0, self::MAX_SHOP_RESULTS_IN_PROMPT);
$isDetailed = count($limitedShopResults) <= 5;
$lines = [];
foreach ($shopResults as $i => $product) {
if (!$product instanceof ShopProductResult) {
continue;
}
foreach ($limitedShopResults as $i => $product) {
$n = $i + 1;
$entryParts = [
"[{$n}] " . $this->normalizeBlockText($product->name),
@@ -283,13 +292,19 @@ final readonly class PromptBuilder
}
if ($lines !== []) {
$parts[] =
$header =
"LIVE SHOP RESULTS (authoritative for current commercial details):\n" .
"Use these results as the primary source for current price, availability, URL, and current shop-visible product naming.\n" .
"If retrieved documents conflict with shop data on price, availability, URL, or current naming, prefer the shop data.\n" .
"Output real URL values exactly as provided in the shop results. Do not replace them with placeholders, link labels, or product names.\n" .
"Do not infer undocumented technical specifications from shop data.\n\n" .
implode("\n\n", $lines);
"Do not infer undocumented technical specifications from shop data.";
if ($totalCount > count($limitedShopResults)) {
$header .= "\n" .
"Only the top " . count($limitedShopResults) . " ranked shop results are shown here out of {$totalCount} total results.";
}
$parts[] = $header . "\n\n" . implode("\n\n", $lines);
}
return $this->implodeBlocks($parts);