rm CachedRetriever.php
add second shopsearch
This commit is contained in:
@@ -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));
|
||||
|
||||
Reference in New Issue
Block a user