rm CachedRetriever.php

add second shopsearch
This commit is contained in:
team 1
2026-04-19 14:20:23 +02:00
parent a71426c300
commit 4d944a5113
9 changed files with 1075 additions and 129 deletions

View File

@@ -87,6 +87,22 @@ final readonly class PromptBuilder
'testomat',
];
private const ACCESSORY_REQUEST_KEYWORDS = [
'passend',
'passende',
'passendes',
'zubehör',
'zubehor',
'dazu',
'indikator',
'reagenz',
'kit',
'set',
'zusatz',
'ergänzung',
'ergaenzung',
];
public function __construct(
private ContextService $contextService,
private SystemPromptRepository $systemPromptRepository,
@@ -119,18 +135,20 @@ final readonly class PromptBuilder
$swagFullOutPut = $this->normalizeNullableBlockText($swagFullOutPut);
$hasShopResults = $shopResults !== [];
$isTechnicalProductQuestion = $this->isLikelyTechnicalProductQuestion($prompt);
$systemBlock = $this->buildSystemBlock();
$shopBlock = $this->buildShopBlock($shopResults, $swagFullOutPut);
$outputPriorityBlock = $this->buildOutputPriorityBlock($hasShopResults);
$responseFormatBlock = $this->buildResponseFormatBlock($prompt, $hasShopResults, $isTechnicalProductQuestion);
$knowledgeBlock = $this->buildKnowledgeBlock($knowledgeChunks, $urlContent, $prompt, $hasShopResults);
$userBlock = $this->buildUserBlock($prompt);
// Build fixed blocks first so history only receives the remaining budget.
$fixedPrompt = $this->implodeBlocks([
$systemBlock,
$shopBlock,
$outputPriorityBlock,
$responseFormatBlock,
$knowledgeBlock,
$userBlock,
]);
@@ -145,6 +163,7 @@ final readonly class PromptBuilder
$systemBlock,
$shopBlock,
$outputPriorityBlock,
$responseFormatBlock,
$knowledgeBlock,
$contextBlock,
$userBlock,
@@ -326,6 +345,39 @@ final readonly class PromptBuilder
"Do not let bundles, accessories, or service items override a better technical match unless the user explicitly asks for them.\n";
}
private function buildResponseFormatBlock(
string $prompt,
bool $hasShopResults,
bool $isTechnicalProductQuestion
): string {
$rules = [
"RESPONSE FORMAT RULES:",
"- Keep normal spacing between all words. Never fuse words together.",
"- Use short, clean paragraphs or short labeled sections.",
"- Do not use persuasive or promotional wording.",
"- Do not repeat the same fact in slightly different wording.",
];
if ($hasShopResults) {
$rules[] = "- If a product is identified, prefer this structure per product: product name, product number, price, availability, URL, then only the most relevant technical facts.";
$rules[] = "- Keep price, availability, and URL on separate lines when they are present.";
}
if ($isTechnicalProductQuestion) {
$rules[] = "- Write like technical documentation: precise, neutral, and source-close.";
$rules[] = "- Prefer exact values, ranges, thresholds, compatibility notes, and application areas over general explanation.";
}
if ($this->asksForAccessoryOrBundle($prompt)) {
$rules[] = "- If the user asks for a matching accessory, separate the answer into: main device and matching accessory.";
$rules[] = "- The main device must come first. The accessory must not replace the main device.";
$rules[] = "- Only name an accessory as matching if compatibility is explicitly grounded in the provided sources.";
$rules[] = "- Do not call accessories, indicators, reagents, kits, sets, or consumables a device, measuring device, or main product unless the source explicitly says so.";
}
return implode("\n", $rules);
}
/**
* Build the knowledge block.
*
@@ -451,6 +503,8 @@ final readonly class PromptBuilder
"- Use retrieved knowledge as highest priority for technical matching, thresholds, measurement principles, and technical explanation.",
"- When shop results are present and relevant, include current price and the actual URL if available.",
"- Do not let accessories, bundles, or service items override a technically better product match unless the user explicitly asks for them.",
"- Do not call accessories, indicators, reagents, kits, sets, or consumables a device, measuring device, or main product unless the source explicitly says so.",
"- Do not claim that an accessory is required, necessary, used for calibration, or sets the measurement range unless this is explicitly stated in the provided sources.",
]);
} else {
$rules[] = "- Use retrieved knowledge as authoritative for factual answers.";
@@ -484,10 +538,10 @@ final readonly class PromptBuilder
{
$filtered = array_values(array_filter(
array_map(
fn ($block): string => is_string($block) ? $this->normalizeBlockText($block) : '',
fn($block): string => is_string($block) ? $this->normalizeBlockText($block) : '',
$blocks
),
static fn (string $block): bool => $block !== ''
static fn(string $block): bool => $block !== ''
));
return implode("\n\n", $filtered);
@@ -536,6 +590,19 @@ final readonly class PromptBuilder
return preg_match('/\b[\p{L}]{2,}\s?\d{2,5}\b/u', $prompt) === 1;
}
private function asksForAccessoryOrBundle(string $prompt): bool
{
$normalized = mb_strtolower($prompt, 'UTF-8');
foreach (self::ACCESSORY_REQUEST_KEYWORDS as $keyword) {
if (str_contains($normalized, $keyword)) {
return true;
}
}
return false;
}
private function clamp(int $value, int $min, int $max): int
{
return max($min, min($max, $value));