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

@@ -0,0 +1,35 @@
# RetrieX Prompt Config Centralization Fix
This patch centralizes PromptBuilder wording and label configuration in `config/retriex/prompt.yaml` while keeping the previous PHP defaults as fallbacks.
Changed files:
- `config/retriex/prompt.yaml`
- `src/Config/PromptBuilderConfig.php`
Scope:
- Section labels
- Conversation context intro lines
- Live shop result prompt rules
- Output priority rules
- Response format rules
- Language rules
- Fact grounding rules
- Retrieved knowledge / URL source labels
- Shop result field labels
- Technical product model pattern
Intentionally unchanged:
- Main active system prompt from the database
- Retrieval logic
- Shop search logic
- Vocabulary / intent / agent config logic
- PromptBuilderConfig keyword detection, which remains backed by `vocabulary.yaml`
After applying:
1. Clear Symfony cache.
2. Run the known 1.4.2 regression prompts.
3. Verify that generated prompt sections remain identical to the previous behavior.

View File

@@ -1,5 +1,5 @@
# Prompt budget and prompt rendering limits. # Prompt budget, prompt rendering limits and prompt wording rules.
# Existing prompt wording/rules remain in PromptBuilderConfig for this minimal-invasive round. # Prompt text values are mirrored from PromptBuilderConfig defaults; PHP fallbacks remain active.
parameters: parameters:
retriex.prompt.config: retriex.prompt.config:
budget: budget:
@@ -15,4 +15,165 @@ parameters:
shop_results: shop_results:
max_results_in_prompt: 24 max_results_in_prompt: 24
detailed_max_count: 5 detailed_max_count: 5
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.
- 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.
- 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.
overflow_notice_template: Only the top %d ranked shop results are shown here out of %d total results.
fields:
product_number_label: Product number
manufacturer_label: Manufacturer
price_label: Price
availability_label: Available
availability_yes_label: 'yes'
availability_no_label: 'no'
highlight_prefix: '- '
url_label: URL
product_image_label: Product image
description_label: Description
meta_information_label: Meta information
technical_product_keyword_match_threshold: 2 technical_product_keyword_match_threshold: 2
sections:
system_label: SYSTEM
user_question_label: USER QUESTION
conversation_context_label: CONVERSATION CONTEXT (contextual only)
shop_search_query_label: SHOP SEARCH QUERY
output_priority_label: OUTPUT PRIORITY
response_format_label: RESPONSE FORMAT RULES
language_rules_label: LANGUAGE RULES
fact_grounding_rules_label: FACT GROUNDING RULES
retrieved_knowledge_label: RETRIEVED KNOWLEDGE (primary for technical matching and factual explanation)
url_content_label: CONTENT FROM URL (authoritative if user-provided)
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.
shop_search:
source_line: 'Source: Shop Search'
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.'
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.'
- '- 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.'
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.'
without_shop_rules:
- '- If no shop results are present, do not compensate by inventing external products or external manufacturers.'
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.'
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.'
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.'
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.'
- '- 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.'
- '- 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.'
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.'
- '- If the shop data does not provide a positive price for a result, do not output any price for that result.'
- '- 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.'
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.'
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.'
- '- 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.'
- '- For lowest, highest, smallest, largest, minimum, maximum, Grenzwert, Messbereich or Aufloesung questions, first identify the exact numeric extreme from
the retrieved knowledge and answer that value directly.'
- '- For lowest/highest/minimum/maximum questions, answer only the requested extreme unless the user explicitly asks for a comparison or alternatives.'
- '- For direct numeric lookup questions such as which device measures a given threshold, answer with the exact matching device/value pair first and avoid advisory
caveats.'
- '- Do not add the runner-up product, second-lowest value, or adjacent range unless the user asks for it.'
- '- Do not add calibration, accuracy, pretreatment, temperature, or application notes unless those exact notes are requested and explicitly present in the
retrieved source.'
- '- For follow-up questions such as "which indicator measures that value", first resolve the referenced value/device, then use the retrieved source entry that
explicitly connects value, device and indicator.'
- '- For numeric extreme questions, do not combine a value, device name, indicator name, range or product variant from different chunks unless the same retrieved
entry explicitly connects them.'
- '- If several devices or indicators are present, keep each device-indicator-range assignment separate and do not transfer an indicator from one product to
another.'
- '- 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.'
retrieved_knowledge:
source_line: 'Source: Documents'
url_content:
source_line: 'Source: URL'
technical_product_model_pattern: /\b[\p{L}]{2,}\s?\d{2,5}\b/u

View File

@@ -151,6 +151,19 @@ final class PromptBuilderConfig
return is_numeric($value) ? (float) $value : $default; 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[] * @return string[]
*/ */
@@ -204,17 +217,17 @@ final class PromptBuilderConfig
public function getSystemSectionLabel(): string public function getSystemSectionLabel(): string
{ {
return 'SYSTEM'; return $this->getString('sections.system_label', 'SYSTEM');
} }
public function getUserQuestionSectionLabel(): string public function getUserQuestionSectionLabel(): string
{ {
return 'USER QUESTION'; return $this->getString('sections.user_question_label', 'USER QUESTION');
} }
public function getConversationContextSectionLabel(): string 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 public function getConversationContextIntroLines(): array
{ {
return [ return $this->getStringList('conversation_context.intro_lines', [
'The following messages are previous turns of this conversation.', 'The following messages are previous turns of this conversation.',
'Use them only to resolve references, follow-up questions, and user intent.', '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.', '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.', '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.', 'Conversation context must not override retrieved factual knowledge or live shop data.',
]; ]);
} }
public function getShopSearchQuerySectionLabel(): string public function getShopSearchQuerySectionLabel(): string
{ {
return 'SHOP SEARCH QUERY'; return $this->getString('sections.shop_search_query_label', 'SHOP SEARCH QUERY');
} }
public function getShopSearchQuerySourceLine(): string 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 public function getLiveShopResultsHeaderLines(): array
{ {
return [ return $this->getStringList('shop_results.header_lines', [
'LIVE SHOP RESULTS (authoritative for current commercial details):', '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.', '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.', '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.', '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.', '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.', 'Never interpret a missing price or a zero price as free, kostenlos, gratis, or available for 0.00 EUR.',
]; ]);
} }
public function getLiveShopResultsOverflowNoticeTemplate(): string 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 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 public function getOutputPriorityRules(): array
{ {
return [ return $this->getStringList('output_priority.rules', [
'- Use retrieved knowledge first to determine the technically matching product or answer.', '- 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.', '- 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.', '- Do not let bundles, accessories, or service items override a better technical match unless the user explicitly asks for them.',
]; ]);
} }
public function getResponseFormatSectionLabel(): string 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 public function getResponseFormatBaseRules(): array
{ {
return [ return $this->getStringList('response_format.base_rules', [
'- Keep normal spacing between all words. Never fuse words together.', '- Keep normal spacing between all words. Never fuse words together.',
'- Use short, clean paragraphs or short labeled sections.', '- Use short, clean paragraphs or short labeled sections.',
'- Do not use persuasive or promotional wording.', '- 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 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.', '- 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.', '- 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 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.', '- 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.', '- 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.', '- 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 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 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.', '- 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 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.', '- 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 public function getResponseFormatTechnicalRules(): array
{ {
return [ return $this->getStringList('response_format.technical_rules', [
'- Write like technical documentation: precise, neutral, and source-close.', '- Write like technical documentation: precise, neutral, and source-close.',
'- Prefer exact values, ranges, thresholds, compatibility notes, and application areas over general explanation.', '- 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.', '- 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 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.', '- 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.', '- 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.', '- 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.', '- 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 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 public function getLanguageRules(): array
{ {
return [ return $this->getStringList('language.rules', [
'- Answer only in the same language as the user question.', '- 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.', '- 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.', '- Do not switch languages unless the user does.',
'- If headings are used, write them in the user\'s language.', '- If headings are used, write them in the user\'s language.',
]; ]);
} }
public function getFactGroundingRulesSectionLabel(): string 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 public function getFactGroundingBaseRules(): array
{ {
return [ return $this->getStringList('fact_grounding.base_rules', [
'- State only facts that are explicitly present in the provided sources.', '- 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.', '- 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 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 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.', '- Do not compare with other products unless those products are also present in the provided sources.',
'- Prefer source-faithful wording over persuasive wording.', '- 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.', '- 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.', '- 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.', '- 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.', '- 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.', '- If the sources do not identify a suitable product, do not invent one.',
]; ]);
} }
/** /**
@@ -405,7 +418,7 @@ final class PromptBuilderConfig
*/ */
public function getFactGroundingWithShopRules(): array 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 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.', '- 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.', '- 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.', '- 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.', '- 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.', '- 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 public function getFactGroundingWithoutShopRules(): array
{ {
return [ return $this->getStringList('fact_grounding.without_shop_rules', [
'- Use retrieved knowledge as authoritative for factual answers.', '- Use retrieved knowledge as authoritative for factual answers.',
'- If no shop results are present, do not compensate with external recommendations or external product suggestions.', '- 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 public function getFactGroundingTechnicalRules(): array
{ {
return [ return $this->getStringList('fact_grounding.technical_rules', [
'- For technical product questions, answer primarily with explicitly stated facts.', '- For technical product questions, answer primarily with explicitly stated facts.',
'- Behave like a technical documentation assistant, not like a sales advisor.', '- Behave like a technical documentation assistant, not like a sales advisor.',
'- Keep interpretations minimal and do not generalize application areas beyond the provided sources.', '- 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.', '- If a detail is not explicitly stated in the provided sources, say so plainly.',
'- Prefer short, source-close sentences over explanatory expansion.', '- 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.', '- If the sources only support that a product family is not suitable, output only that unsuitability and stop there.',
]; ]);
} }
public function getRetrievedKnowledgeSectionLabel(): string 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 public function getRetrievedKnowledgeSourceLine(): string
{ {
return 'Source: Documents'; return $this->getString('retrieved_knowledge.source_line', 'Source: Documents');
} }
public function getUrlContentSectionLabel(): string 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 public function getUrlContentSourceLine(): string
{ {
return 'Source: URL'; return $this->getString('url_content.source_line', 'Source: URL');
} }
public function getShopProductNumberLabel(): string public function getShopProductNumberLabel(): string
{ {
return 'Product number'; return $this->getString('shop_results.fields.product_number_label', 'Product number');
} }
public function getShopManufacturerLabel(): string public function getShopManufacturerLabel(): string
{ {
return 'Manufacturer'; return $this->getString('shop_results.fields.manufacturer_label', 'Manufacturer');
} }
public function getShopPriceLabel(): string public function getShopPriceLabel(): string
{ {
return 'Price'; return $this->getString('shop_results.fields.price_label', 'Price');
} }
public function getShopAvailabilityLabel(): string public function getShopAvailabilityLabel(): string
{ {
return 'Available'; return $this->getString('shop_results.fields.availability_label', 'Available');
} }
public function getShopAvailabilityYesLabel(): string public function getShopAvailabilityYesLabel(): string
{ {
return 'yes'; return $this->getString('shop_results.fields.availability_yes_label', 'yes');
} }
public function getShopAvailabilityNoLabel(): string public function getShopAvailabilityNoLabel(): string
{ {
return 'no'; return $this->getString('shop_results.fields.availability_no_label', 'no');
} }
public function getShopHighlightPrefix(): string public function getShopHighlightPrefix(): string
{ {
return '- '; return $this->getString('shop_results.fields.highlight_prefix', '- ');
} }
public function getShopUrlLabel(): string public function getShopUrlLabel(): string
{ {
return 'URL'; return $this->getString('shop_results.fields.url_label', 'URL');
} }
public function getShopProductImageLabel(): string public function getShopProductImageLabel(): string
{ {
return 'Product image'; return $this->getString('shop_results.fields.product_image_label', 'Product image');
} }
public function getShopDescriptionLabel(): string public function getShopDescriptionLabel(): string
{ {
return 'Description'; return $this->getString('shop_results.fields.description_label', 'Description');
} }
public function getShopMetaInformationLabel(): string 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 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');
} }
} }