Files
MtoRagSystem/src/Config/AgentRunnerConfig.php
2026-04-25 21:34:55 +02:00

276 lines
8.3 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Config;
final class AgentRunnerConfig
{
/**
* @param array<string, mixed> $config
*/
public function __construct(
private readonly array $config = [],
) {
}
public function getCommerceHistoryBudgetChars(): int
{
return $this->getInt('commerce_history_budget_chars', 1000);
}
public function getProductSearchKnowledgeChunkLimit(): int
{
return $this->getInt('product_search_knowledge_chunk_limit', 6);
}
public function getAdvisoryProductSearchKnowledgeChunkLimit(): int
{
return $this->getInt('advisory_product_search_knowledge_chunk_limit', 9);
}
public function getOptimizedShopQueryPrefixPattern(): string
{
return $this->getString('optimized_shop_query_prefix_pattern', '/^(?:keywords?|suchquery|search\\s*query|query)\\s*:\\s*/iu');
}
public function getOptimizedShopQueryTrimCharacters(): string
{
return " \t\n\r\0\x0B\"'`";
}
private function getInt(string $key, int $default): int
{
$value = $this->config[$key] ?? $default;
return is_numeric($value) ? (int) $value : $default;
}
private function getString(string $key, string $default): string
{
$value = $this->config[$key] ?? $default;
return is_string($value) && $value !== '' ? $value : $default;
}
public function getEmptyPromptMessage(): string
{
return '❌ Empty prompt.';
}
public function getAnalyzeRequestMessage(): string
{
return 'Ich analysiere deine Anfrage...';
}
public function getCheckInternetSourcesMessage(): string
{
return 'Ich prüfe auf Internetquellen...';
}
public function getRetrieveKnowledgeMessage(): string
{
return 'Ich hole relevante Daten aus meinem RAG-Wissen...';
}
public function getOptimizeSearchMessage(): string
{
return 'Ich optimiere die Recherche...';
}
public function getFetchSearchDataMessageTemplate(): string
{
return 'Ich rufe Recherchedaten ab (type: %s)';
}
public function getAnalyzeAllInformationMessage(): string
{
return 'Ich analysiere alle Informationen...';
}
public function getThinkingWhileStreamingMessage(): string
{
return 'Denke nach...';
}
public function getNoLlmDataReceivedMessage(): string
{
return '❌ Es wurden keine Daten vom LLM empfangen.';
}
public function getGenericInternalErrorMessage(): string
{
return '❌ Bei der Verarbeitung der Anfrage ist ein interner Fehler aufgetreten.';
}
public function getDebugInternalErrorPrefix(): string
{
return '❌ Interner Fehler: ';
}
public function getExternalUrlSourceLabel(): string
{
return 'Externe URL';
}
public function getRagKnowledgeSourceLabel(): string
{
return 'RAG Wissen';
}
public function getConversationHistorySourceLabel(): string
{
return 'Chatverlauf';
}
public function getShopSystemSourceLabel(): string
{
return 'Shopsystem';
}
public function getExtendedShopSearchSourceLabel(): string
{
return 'Erweiterte Shopsuche';
}
public function getUsedSourcesPrefix(): string
{
return 'Genutzte Quellen: ';
}
public function getSourcesPrefix(): string
{
return 'Quellen: ';
}
public function getSourceBadgeHtmlTemplate(): string
{
return '<span class="badge bg-info text-black">%s</span>';
}
public function getErrorHtmlTemplate(): string
{
return '<div class="retriex-alert retriex-alert--error"><div class="retriex-alert__icon">❌</div><div class="retriex-alert__content"><div class="retriex-alert__title">Hinweis</div><div class="retriex-alert__text">%s</div></div></div>' . "\n";
}
public function getThinkHtmlTemplate(): string
{
return '<span class="text-info think">%s</span>' . "\n";
}
public function getInfoHtmlTemplate(): string
{
return "\n\n" . '<span class="text-info fw-bolder">%s</span>' . "\n";
}
public function getDebugHtmlTemplate(): string
{
return "\n\nDEBUG: <code>%s</code>\n";
}
public function getShopPrompt(string $prompt, string $commerceHistoryContext = ''): string
{
$historyBlock = '';
if (trim($commerceHistoryContext) !== '') {
$historyBlock = $this->buildHistoryBlock($commerceHistoryContext);
}
return $this->implodePromptBlocks([
$this->getShopPromptIntro(),
$this->buildRulesBlock($this->getShopPromptRules()),
$this->getShopPromptOutputFormatBlock(),
$historyBlock,
$this->getCurrentUserInputLabel() . ':',
trim($prompt),
]);
}
private function buildHistoryBlock(string $commerceHistoryContext): string
{
return $this->implodePromptBlocks([
$this->getRecentConversationContextLabel() . ':',
trim($commerceHistoryContext),
$this->buildRulesBlock($this->getConversationContextRules(), 'Additional rules for conversation context:'),
]);
}
/**
* @return string[]
*/
public function getShopPromptRules(): array
{
return [
'- Output only the final search query.',
'- Always convert relevant search terms to their singular form.',
'- No introduction, no explanation, no quotation marks.',
'- Use only shop-relevant search terms from the user input for a shop search.',
'- Maximum 6 search terms, preferably fewer.',
'- Remove filler words, polite phrases, and irrelevant words.',
'- Preserve product names, brands, model numbers, and compound terms exactly if they are relevant.',
'- Numbers that belong to a product name or model must be preserved (e.g. Indikator 300, Testomat 808, Testomat 2000).',
'- Separate terms using spaces only.',
'- If a relevant product name is present, it must be placed at the beginning of the final search query.',
'- Try to always identify all products mentioned in the user input text, even in long prompts.',
'- Look for terms such as Testomat, Horiba, Tritromat, or words like indicator.',
'- If the current user input is vague or referential, use the recent conversation context only as support.',
'- Do not output words that only describe conversation flow, such as "same", "again", "also", or "like above".',
];
}
/**
* @return string[]
*/
public function getConversationContextRules(): array
{
return [
'- The current user input has highest priority.',
'- Use the recent conversation context only to resolve omitted references.',
'- Use it only for product carry-over, brand carry-over, model carry-over, or variant follow-ups.',
'- Do not revive older products unless the current user input clearly refers to them.',
'- If the current input starts a new topic, ignore older product context.',
'- Prefer the most recent product reference over older ones.',
];
}
public function getShopPromptIntro(): string
{
return 'Generate a short search query for Shopware 6 from the following user input text.';
}
public function getShopPromptOutputFormatBlock(): string
{
return "Output format:\nKeyword1 Keyword2 Keyword3";
}
public function getRecentConversationContextLabel(): string
{
return 'RECENT CONVERSATION CONTEXT';
}
public function getCurrentUserInputLabel(): string
{
return 'CURRENT USER INPUT';
}
private function buildRulesBlock(array $rules, string $headline = 'Rules:'): string
{
return $headline . "\n" . implode("\n", $rules);
}
/**
* @param string[] $blocks
*/
private function implodePromptBlocks(array $blocks): string
{
$normalized = array_values(array_filter(
array_map(
static fn(string $block): string => trim($block),
$blocks
),
static fn(string $block): bool => $block !== ''
));
return implode("\n\n", $normalized);
}
}