move tokens to config part 2

This commit is contained in:
team 1
2026-04-26 11:21:09 +02:00
parent 38ae9d528f
commit cd70460918
3 changed files with 265 additions and 56 deletions

View File

@@ -151,6 +151,19 @@ final class PromptBuilderConfig
return is_numeric($value) ? (float) $value : $default;
}
private function getString(string $path, string $default): string
{
$value = $this->getValue($path, $default);
if (!is_scalar($value)) {
return $default;
}
$value = (string) $value;
return $value !== '' ? $value : $default;
}
/**
* @return string[]
*/
@@ -204,17 +217,17 @@ final class PromptBuilderConfig
public function getSystemSectionLabel(): string
{
return 'SYSTEM';
return $this->getString('sections.system_label', 'SYSTEM');
}
public function getUserQuestionSectionLabel(): string
{
return 'USER QUESTION';
return $this->getString('sections.user_question_label', 'USER QUESTION');
}
public function getConversationContextSectionLabel(): string
{
return 'CONVERSATION CONTEXT (contextual only)';
return $this->getString('sections.conversation_context_label', 'CONVERSATION CONTEXT (contextual only)');
}
/**
@@ -222,23 +235,23 @@ final class PromptBuilderConfig
*/
public function getConversationContextIntroLines(): array
{
return [
return $this->getStringList('conversation_context.intro_lines', [
'The following messages are previous turns of this conversation.',
'Use them only to resolve references, follow-up questions, and user intent.',
'Previous assistant answers are not a factual source for technical values, product compatibility, indicators, ranges, prices, or availability.',
'All factual claims must come from retrieved factual knowledge, user-provided URL content, or live shop data.',
'Conversation context must not override retrieved factual knowledge or live shop data.',
];
]);
}
public function getShopSearchQuerySectionLabel(): string
{
return 'SHOP SEARCH QUERY';
return $this->getString('sections.shop_search_query_label', 'SHOP SEARCH QUERY');
}
public function getShopSearchQuerySourceLine(): string
{
return 'Source: Shop Search';
return $this->getString('shop_search.source_line', 'Source: Shop Search');
}
/**
@@ -246,7 +259,7 @@ final class PromptBuilderConfig
*/
public function getLiveShopResultsHeaderLines(): array
{
return [
return $this->getStringList('shop_results.header_lines', [
'LIVE SHOP RESULTS (authoritative for current commercial details):',
'Use these results as the primary source for current price, availability, URL, and current shop-visible product naming.',
'If retrieved documents conflict with shop data on price, availability, URL, or current naming, prefer the shop data.',
@@ -256,17 +269,17 @@ final class PromptBuilderConfig
'Do not merge a device identified in retrieved knowledge with price, URL, product number, or availability from a different shop item such as a reagent, accessory, kit, consumable, or service item.',
'If a shop result has no price field, do not state a price for it.',
'Never interpret a missing price or a zero price as free, kostenlos, gratis, or available for 0.00 EUR.',
];
]);
}
public function getLiveShopResultsOverflowNoticeTemplate(): string
{
return 'Only the top %d ranked shop results are shown here out of %d total results.';
return $this->getString('shop_results.overflow_notice_template', 'Only the top %d ranked shop results are shown here out of %d total results.');
}
public function getOutputPrioritySectionLabel(): string
{
return 'OUTPUT PRIORITY';
return $this->getString('sections.output_priority_label', 'OUTPUT PRIORITY');
}
/**
@@ -274,16 +287,16 @@ final class PromptBuilderConfig
*/
public function getOutputPriorityRules(): array
{
return [
return $this->getStringList('output_priority.rules', [
'- Use retrieved knowledge first to determine the technically matching product or answer.',
'- If shop results are present, use them afterwards to add current price, availability, and the actual URL.',
'- Do not let bundles, accessories, or service items override a better technical match unless the user explicitly asks for them.',
];
]);
}
public function getResponseFormatSectionLabel(): string
{
return 'RESPONSE FORMAT RULES';
return $this->getString('sections.response_format_label', 'RESPONSE FORMAT RULES');
}
/**
@@ -291,7 +304,7 @@ final class PromptBuilderConfig
*/
public function getResponseFormatBaseRules(): array
{
return [
return $this->getStringList('response_format.base_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.',
@@ -301,7 +314,7 @@ final class PromptBuilderConfig
'- Do not generate external alternative lists, vendor suggestions, or purchase recommendations unless they are explicitly present in the provided sources.',
'- Do not combine technical identity from one source with commercial fields from a different product.',
'- Product number, price, availability, and URL must belong to the same explicitly grounded product.',
];
]);
}
/**
@@ -309,14 +322,14 @@ final class PromptBuilderConfig
*/
public function getResponseFormatWithShopRules(): array
{
return [
return $this->getStringList('response_format.with_shop_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.',
'- Keep price, availability, and URL on separate lines when they are present.',
'- Only use shop price, URL, product number, or availability for the main product when the shop result clearly matches that same main product.',
'- If the matching shop item appears to be an accessory, reagent, consumable, set, or kit, keep it separate and do not present its commercial fields as the main device.',
'- If the commercial match is uncertain, say that commercial details for the main product are not clearly available in the provided shop results.',
'- If no price is shown for a shop item, omit the price instead of writing 0,00 €, free, kostenlos, or a guessed price.',
];
]);
}
/**
@@ -324,9 +337,9 @@ final class PromptBuilderConfig
*/
public function getResponseFormatWithoutShopRules(): array
{
return [
return $this->getStringList('response_format.without_shop_rules', [
'- If no shop results are present, do not compensate by inventing external products or external manufacturers.',
];
]);
}
/**
@@ -334,11 +347,11 @@ final class PromptBuilderConfig
*/
public function getResponseFormatTechnicalRules(): array
{
return [
return $this->getStringList('response_format.technical_rules', [
'- Write like technical documentation: precise, neutral, and source-close.',
'- Prefer exact values, ranges, thresholds, compatibility notes, and application areas over general explanation.',
'- If the sources only support a negative finding, output only that negative finding and do not add speculative alternatives.',
];
]);
}
/**
@@ -346,17 +359,17 @@ final class PromptBuilderConfig
*/
public function getResponseFormatAccessoryRules(): array
{
return [
return $this->getStringList('response_format.accessory_rules', [
'- If the user asks for a matching accessory, separate the answer into: main device and matching accessory.',
'- The main device must come first. The accessory must not replace the main device.',
'- Only name an accessory as matching if compatibility is explicitly grounded in the provided sources.',
'- Do not call accessories, indicators, reagents, kits, sets, or consumables a device, measuring device, or main product unless the source explicitly says so.',
];
]);
}
public function getLanguageRulesSectionLabel(): string
{
return 'LANGUAGE RULES';
return $this->getString('sections.language_rules_label', 'LANGUAGE RULES');
}
/**
@@ -364,17 +377,17 @@ final class PromptBuilderConfig
*/
public function getLanguageRules(): array
{
return [
return $this->getStringList('language.rules', [
'- Answer only in the same language as the user question.',
'- All headings, labels, notes, and structural elements must be in the same language as the user question.',
'- Do not switch languages unless the user does.',
'- If headings are used, write them in the user\'s language.',
];
]);
}
public function getFactGroundingRulesSectionLabel(): string
{
return 'FACT GROUNDING RULES';
return $this->getString('sections.fact_grounding_rules_label', 'FACT GROUNDING RULES');
}
/**
@@ -382,7 +395,7 @@ final class PromptBuilderConfig
*/
public function getFactGroundingBaseRules(): array
{
return [
return $this->getStringList('fact_grounding.base_rules', [
'- State only facts that are explicitly present in the provided sources.',
'- Extract concrete values exactly when they are present, including units, ranges, model names, indicator names, IP classes, temperatures, pressures, dimensions, counts, relay outputs, current outputs, and error codes.',
'- Do not invent missing values.',
@@ -390,14 +403,14 @@ final class PromptBuilderConfig
'- Do not claim that information is missing if it appears in the provided sources.',
'- Do not compare with other products unless those products are also present in the provided sources.',
'- Prefer source-faithful wording over persuasive wording.',
'- Avoid marketing language such as \'ideal\', \'perfect\', \'unverzichtbar\', \'entscheidend\', \'optimal\', \'kosteneffizient\', \'prozesssicher\', or \'state-of-the-art\'.',
"- Avoid marketing language such as 'ideal', 'perfect', 'unverzichtbar', 'entscheidend', 'optimal', 'kosteneffizient', 'prozesssicher', or 'state-of-the-art'.",
'- Clearly separate explicit facts from inferences.',
'- If a conclusion goes beyond the source wording, label it exactly as \'Inference:\'.',
"- If a conclusion goes beyond the source wording, label it exactly as 'Inference:'.",
'- If a sentence cannot be traced to the provided sources, do not write it.',
'- For follow-up questions, use the conversation only to resolve what the user refers to; do not copy technical facts from previous assistant answers unless the same fact is present in the current retrieved sources.',
'- Never mention external manufacturers, external brands, or external products unless they are explicitly present in the provided sources.',
'- If the sources do not identify a suitable product, do not invent one.',
];
]);
}
/**
@@ -405,7 +418,7 @@ final class PromptBuilderConfig
*/
public function getFactGroundingWithShopRules(): array
{
return [
return $this->getStringList('fact_grounding.with_shop_rules', [
'- Use shop data as highest priority only for current commercial fields: price, availability, URL, and current shop-visible naming.',
'- 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.',
@@ -416,7 +429,7 @@ final class PromptBuilderConfig
'- Do not assign the product number, price, URL, or availability of a reagent, accessory, kit, set, consumable, or service item to a device identified in retrieved knowledge.',
'- Only use commercial fields for the main product when the shop item and the technically identified product clearly refer to the same product identity.',
'- If the shop match is ambiguous, keep the technical identification and commercial details separate.',
];
]);
}
/**
@@ -424,10 +437,10 @@ final class PromptBuilderConfig
*/
public function getFactGroundingWithoutShopRules(): array
{
return [
return $this->getStringList('fact_grounding.without_shop_rules', [
'- Use retrieved knowledge as authoritative for factual answers.',
'- If no shop results are present, do not compensate with external recommendations or external product suggestions.',
];
]);
}
/**
@@ -435,7 +448,7 @@ final class PromptBuilderConfig
*/
public function getFactGroundingTechnicalRules(): array
{
return [
return $this->getStringList('fact_grounding.technical_rules', [
'- For technical product questions, answer primarily with explicitly stated facts.',
'- Behave like a technical documentation assistant, not like a sales advisor.',
'- Keep interpretations minimal and do not generalize application areas beyond the provided sources.',
@@ -461,82 +474,82 @@ final class PromptBuilderConfig
'- If a detail is not explicitly stated in the provided sources, say so plainly.',
'- Prefer short, source-close sentences over explanatory expansion.',
'- If the sources only support that a product family is not suitable, output only that unsuitability and stop there.',
];
]);
}
public function getRetrievedKnowledgeSectionLabel(): string
{
return 'RETRIEVED KNOWLEDGE (primary for technical matching and factual explanation)';
return $this->getString('sections.retrieved_knowledge_label', 'RETRIEVED KNOWLEDGE (primary for technical matching and factual explanation)');
}
public function getRetrievedKnowledgeSourceLine(): string
{
return 'Source: Documents';
return $this->getString('retrieved_knowledge.source_line', 'Source: Documents');
}
public function getUrlContentSectionLabel(): string
{
return 'CONTENT FROM URL (authoritative if user-provided)';
return $this->getString('sections.url_content_label', 'CONTENT FROM URL (authoritative if user-provided)');
}
public function getUrlContentSourceLine(): string
{
return 'Source: URL';
return $this->getString('url_content.source_line', 'Source: URL');
}
public function getShopProductNumberLabel(): string
{
return 'Product number';
return $this->getString('shop_results.fields.product_number_label', 'Product number');
}
public function getShopManufacturerLabel(): string
{
return 'Manufacturer';
return $this->getString('shop_results.fields.manufacturer_label', 'Manufacturer');
}
public function getShopPriceLabel(): string
{
return 'Price';
return $this->getString('shop_results.fields.price_label', 'Price');
}
public function getShopAvailabilityLabel(): string
{
return 'Available';
return $this->getString('shop_results.fields.availability_label', 'Available');
}
public function getShopAvailabilityYesLabel(): string
{
return 'yes';
return $this->getString('shop_results.fields.availability_yes_label', 'yes');
}
public function getShopAvailabilityNoLabel(): string
{
return 'no';
return $this->getString('shop_results.fields.availability_no_label', 'no');
}
public function getShopHighlightPrefix(): string
{
return '- ';
return $this->getString('shop_results.fields.highlight_prefix', '- ');
}
public function getShopUrlLabel(): string
{
return 'URL';
return $this->getString('shop_results.fields.url_label', 'URL');
}
public function getShopProductImageLabel(): string
{
return 'Product image';
return $this->getString('shop_results.fields.product_image_label', 'Product image');
}
public function getShopDescriptionLabel(): string
{
return 'Description';
return $this->getString('shop_results.fields.description_label', 'Description');
}
public function getShopMetaInformationLabel(): string
{
return 'Meta information';
return $this->getString('shop_results.fields.meta_information_label', 'Meta information');
}
/**
@@ -563,6 +576,6 @@ final class PromptBuilderConfig
public function getTechnicalProductModelPattern(): string
{
return '/\b[\p{L}]{2,}\s?\d{2,5}\b/u';
return $this->getString('technical_product_model_pattern', '/\b[\p{L}]{2,}\s?\d{2,5}\b/u');
}
}