move intent an config value into config files

This commit is contained in:
team2
2026-04-23 21:49:54 +02:00
parent 87417febf4
commit fce44e971d
17 changed files with 1937 additions and 1133 deletions

View File

@@ -1,97 +1,459 @@
<?php
declare(strict_types=1);
namespace App\Config;
class PromptBuilderConfig{
/**
* Approximate character-to-token ratio for conservative prompt budgeting.
*/
public const CHARS_PER_TOKEN = 4;
final class PromptBuilderConfig
{
public function getCharsPerToken(): int
{
return 4;
}
public function getHistoryPaddingChars(): int
{
return 400;
}
public function getOutputReserveRatio(): float
{
return 0.25;
}
public function getOutputReserveMinTokens(): int
{
return 768;
}
public function getOutputReserveMaxTokens(): int
{
return 6000;
}
public function getSafetyReserveRatio(): float
{
return 0.05;
}
public function getSafetyReserveMinTokens(): int
{
return 256;
}
public function getSafetyReserveMaxTokens(): int
{
return 1024;
}
public function getMinPromptBudgetTokens(): int
{
return 1024;
}
public function getMaxShopResultsInPrompt(): int
{
return 24;
}
public function getDetailedShopResultsMaxCount(): int
{
return 5;
}
public function getTechnicalProductKeywordMatchThreshold(): int
{
return 2;
}
public function getSystemSectionLabel(): string
{
return 'SYSTEM';
}
public function getUserQuestionSectionLabel(): string
{
return 'USER QUESTION';
}
public function getConversationContextSectionLabel(): string
{
return 'CONVERSATION CONTEXT (contextual only)';
}
/**
* Keep a small gap so history does not consume the last available prompt space.
* @return string[]
*/
public const HISTORY_PADDING_CHARS = 400;
public function getConversationContextIntroLines(): array
{
return [
'The following messages are previous turns of this conversation.',
'Use them to resolve references, follow-up questions, and user intent.',
'They must not override retrieved factual knowledge or live shop data.',
];
}
public function getShopSearchQuerySectionLabel(): string
{
return 'SHOP SEARCH QUERY';
}
public function getShopSearchQuerySourceLine(): string
{
return 'Source: Shop Search';
}
/**
* Reserve some space for the model output.
* @return string[]
*/
public const OUTPUT_RESERVE_RATIO = 0.25;
public const OUTPUT_RESERVE_MIN_TOKENS = 768;
public const OUTPUT_RESERVE_MAX_TOKENS = 6000;
public function getLiveShopResultsHeaderLines(): array
{
return [
'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.',
'Output real URL values exactly as provided in the shop results. Do not replace them with placeholders, link labels, or product names.',
'Do not infer undocumented technical specifications from shop data.',
'Commercial fields from shop data may only be assigned to a product if the shop item clearly matches the same product identity.',
'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.',
];
}
public function getLiveShopResultsOverflowNoticeTemplate(): string
{
return 'Only the top %d ranked shop results are shown here out of %d total results.';
}
public function getOutputPrioritySectionLabel(): string
{
return 'OUTPUT PRIORITY';
}
/**
* Reserve a small safety buffer to avoid hitting the context limit too tightly.
* @return string[]
*/
public const SAFETY_RESERVE_RATIO = 0.05;
public const SAFETY_RESERVE_MIN_TOKENS = 256;
public const SAFETY_RESERVE_MAX_TOKENS = 1024;
public function getOutputPriorityRules(): array
{
return [
'- 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';
}
/**
* Ensure the prompt budget never collapses completely on smaller models.
* @return string[]
*/
public const MIN_PROMPT_BUDGET_TOKENS = 1024;
public function getResponseFormatBaseRules(): array
{
return [
'- 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.',
'- Never mention brands, manufacturers, model names, or product families that do not appear in the provided shop results, retrieved knowledge, URL content, or conversation context.',
'- If no suitable product is explicitly grounded in the provided sources, say that plainly instead of inventing alternatives.',
'- 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.',
];
}
/**
* 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.
* @return string[]
*/
public const MAX_SHOP_RESULTS_IN_PROMPT = 24;
public function getResponseFormatWithShopRules(): array
{
return [
'- 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.',
];
}
/**
* Technical product prompts should be answered like documentation,
* not like sales copy.
* @return string[]
*/
public const TECHNICAL_PRODUCT_KEYWORDS = [
'technisch',
'technical',
'produkt',
'product',
'gerät',
'device',
'modell',
'model',
'messprinzip',
'measurement principle',
'schnittstelle',
'interface',
'relais',
'relay',
'indikator',
'indicator',
'spannung',
'voltage',
'strom',
'current',
'druck',
'pressure',
'temperatur',
'temperature',
'schutzart',
'ip',
'fehlercode',
'error code',
'wasserhärte',
'hardness',
'testomat',
'chlor',
'chlormessung',
];
public function getResponseFormatWithoutShopRules(): array
{
return [
'- If no shop results are present, do not compensate by inventing external products or external manufacturers.',
];
}
public const ACCESSORY_REQUEST_KEYWORDS = [
'passend',
'passende',
'passendes',
'zubehör',
'zubehor',
'dazu',
'indikator',
'reagenz',
'kit',
'set',
'zusatz',
'ergänzung',
'ergaenzung',
];
}
/**
* @return string[]
*/
public function getResponseFormatTechnicalRules(): array
{
return [
'- 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.',
];
}
/**
* @return string[]
*/
public function getResponseFormatAccessoryRules(): array
{
return [
'- 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 string[]
*/
public function getLanguageRules(): array
{
return [
'- 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 string[]
*/
public function getFactGroundingBaseRules(): array
{
return [
'- 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.',
'- Do not replace missing values with estimates, defaults, or typical industry assumptions.',
'- 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\'.',
'- Clearly separate explicit facts from inferences.',
'- 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.',
'- 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.',
];
}
/**
* @return string[]
*/
public function getFactGroundingWithShopRules(): array
{
return [
'- 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.',
'- 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.',
'- 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.',
];
}
/**
* @return string[]
*/
public function getFactGroundingWithoutShopRules(): array
{
return [
'- Use retrieved knowledge as authoritative for factual answers.',
'- If no shop results are present, do not compensate with external recommendations or external product suggestions.',
];
}
/**
* @return string[]
*/
public function getFactGroundingTechnicalRules(): array
{
return [
'- 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.',
'- Do not describe benefits, consequences, risks, or operational outcomes unless they are explicitly stated in the sources.',
'- Do not translate technical facts into business value unless the source explicitly does so.',
'- Do not recommend process changes unless explicitly present in the source.',
'- Do not use persuasive summaries or advisory conclusions.',
'- If the retrieved knowledge describes one specific named product, stay within that product and do not merge related product families or variants.',
'- Use neutral engineering language.',
'- Do not name specific chemicals, indicator substances, standards, or mechanisms unless explicitly stated in the source.',
'- If the source states signal logic such as green/red, output that signal logic only and do not expand it into operational recommendations or alarm semantics unless explicitly stated.',
'- If the source lists application areas, repeat only those areas and do not broaden them.',
'- If the source names an indicator and threshold, reproduce that exactly without extrapolation.',
'- If the source states only a threshold function, do not expand it into broader control logic.',
'- 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)';
}
public function getRetrievedKnowledgeSourceLine(): string
{
return 'Source: Documents';
}
public function getUrlContentSectionLabel(): string
{
return 'CONTENT FROM URL (authoritative if user-provided)';
}
public function getUrlContentSourceLine(): string
{
return 'Source: URL';
}
public function getShopProductNumberLabel(): string
{
return 'Product number';
}
public function getShopManufacturerLabel(): string
{
return 'Manufacturer';
}
public function getShopPriceLabel(): string
{
return 'Price';
}
public function getShopAvailabilityLabel(): string
{
return 'Available';
}
public function getShopAvailabilityYesLabel(): string
{
return 'yes';
}
public function getShopAvailabilityNoLabel(): string
{
return 'no';
}
public function getShopHighlightPrefix(): string
{
return '- ';
}
public function getShopUrlLabel(): string
{
return 'URL';
}
public function getShopProductImageLabel(): string
{
return 'Product image';
}
public function getShopDescriptionLabel(): string
{
return 'Description';
}
public function getShopMetaInformationLabel(): string
{
return 'Meta information';
}
/**
* @return string[]
*/
public function getTechnicalProductKeywords(): array
{
return [
'technisch',
'technical',
'produkt',
'product',
'gerät',
'device',
'modell',
'model',
'messprinzip',
'measurement principle',
'schnittstelle',
'interface',
'relais',
'relay',
'indikator',
'indicator',
'spannung',
'voltage',
'strom',
'current',
'druck',
'pressure',
'temperatur',
'temperature',
'schutzart',
'ip',
'fehlercode',
'error code',
'wasserhärte',
'hardness',
'testomat',
'chlor',
'chlormessung',
];
}
/**
* @return string[]
*/
public function getAccessoryRequestKeywords(): array
{
return [
'passend',
'passende',
'passendes',
'zubehör',
'zubehor',
'dazu',
'indikator',
'reagenz',
'kit',
'set',
'zusatz',
'ergänzung',
'ergaenzung',
];
}
public function getTechnicalProductModelPattern(): string
{
return '/\b[\p{L}]{2,}\s?\d{2,5}\b/u';
}
}