optimize technical truth

This commit is contained in:
team 1
2026-04-28 12:31:13 +02:00
parent fd8516c7f8
commit 0ee8799b9d
8 changed files with 523 additions and 21 deletions

View File

@@ -6,6 +6,53 @@ namespace App\Config;
final class AgentRunnerConfig
{
private const NO_LLM_MAIN_DEVICE_REQUEST_ROLE_KEYWORDS = [
'anlage',
'messanlage',
'gerät',
'geraet',
'messgerät',
'messgeraet',
'analysegerät',
'analysegeraet',
'analysator',
'analyzer',
'system',
'testomat',
'pockettester',
];
private const NO_LLM_ACCESSORY_PRODUCT_ROLE_KEYWORDS = [
'indikator',
'indicator',
'indikatortyp',
'reagenz',
'reagent',
'reagenzsatz',
'kalibrierlösung',
'kalibrierloesung',
'pufferlösung',
'pufferloesung',
'reinigungslösung',
'reinigungsloesung',
'kalibrier',
'puffer',
'zubehör',
'zubehor',
'accessory',
'ersatzteil',
'verbrauch',
'consumable',
'kit',
'set',
'flasche',
'bottle',
'100 ml',
'500 ml',
'100ml',
'500ml',
];
/**
* @param array<string, mixed> $config
*/
@@ -194,6 +241,14 @@ final class AgentRunnerConfig
);
}
public function getNoLlmFallbackAccessoryOnlyForMainDeviceMessage(): string
{
return $this->getString(
'no_llm_fallback.messages.accessory_only_for_main_device',
'Die Shop-Treffer wirken wie Zubehör/Verbrauchsmaterial und nicht wie eine angefragte Messanlage oder ein Hauptgerät. Ich werte sie deshalb nicht als passende Hauptlösung.'
);
}
public function getNoLlmFallbackEscalationMessage(): string
{
return $this->getString(
@@ -234,6 +289,28 @@ final class AgentRunnerConfig
);
}
/**
* @return string[]
*/
public function getNoLlmMainDeviceRequestRoleKeywords(): array
{
return $this->getStringList(
'no_llm_fallback.product_roles.main_device_request_keywords',
self::NO_LLM_MAIN_DEVICE_REQUEST_ROLE_KEYWORDS
);
}
/**
* @return string[]
*/
public function getNoLlmAccessoryProductRoleKeywords(): array
{
return $this->getStringList(
'no_llm_fallback.product_roles.accessory_product_keywords',
self::NO_LLM_ACCESSORY_PRODUCT_ROLE_KEYWORDS
);
}
public function getNoLlmFallbackShopUnavailableWithKnowledgeMessage(): string
{
return $this->getString(

View File

@@ -272,6 +272,9 @@ 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.',
'Treat every SHOP PRODUCT RECORD as atomic: exact product name, product number, price, availability, URL, image, description, and metadata must stay together.',
'When outputting a shop item, use the exact shop product name from that same SHOP PRODUCT RECORD as the heading. Never use a retrieved-knowledge device name as the heading for a different shop URL or product number.',
'If a technical device from retrieved knowledge and a shop record are not clearly the same exact product identity, separate Fachliche Einordnung from Shop-Treffer instead of merging them.',
]);
}
@@ -280,6 +283,49 @@ final class PromptBuilderConfig
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 getShopRecordHeaderTemplate(): string
{
return $this->getString('shop_results.record_header_template', '[%d] SHOP PRODUCT RECORD');
}
public function getShopExactProductNameLabel(): string
{
return $this->getString('shop_results.exact_product_name_label', 'Exact shop product name');
}
public function getShopRequestedRoleLabel(): string
{
return $this->getString('shop_results.requested_role_label', 'Requested product role');
}
public function getShopInferredRoleLabel(): string
{
return $this->getString('shop_results.inferred_role_label', 'Inferred shop product role');
}
public function getShopRoleCompatibilityLabel(): string
{
return $this->getString('shop_results.role_compatibility_label', 'Role compatibility with request');
}
public function getShopRoleMismatchNotice(): string
{
return $this->getString(
'shop_results.role_mismatch_notice',
'Role mismatch: this record is kept only as a separate shop hit; do not use its description, price, URL, or product number as the main device/system answer.'
);
}
/**
* @return string[]
*/
public function getShopAtomicRecordNoteLines(): array
{
return $this->getStringList('shop_results.atomic_record_note_lines', [
'Record boundary: all fields below belong only to this exact shop product record.',
]);
}
public function getOutputPrioritySectionLabel(): string
{
return $this->getString('sections.output_priority_label', 'OUTPUT PRIORITY');
@@ -387,8 +433,12 @@ final class PromptBuilderConfig
'- 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 a SHOP PRODUCT RECORD is classified as accessory_or_consumable while the requested product role is main_device_or_system, do not use that record as a product recommendation headline.',
'- 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.',
'- For every shop hit shown in the answer, copy the exact shop product name verbatim from the same SHOP PRODUCT RECORD as the item heading.',
'- Never place a shop URL, product number, price, or availability below a different heading taken from retrieved knowledge.',
'- If technical RAG knowledge and shop records cannot be matched with high confidence, use separate sections: Fachliche Einordnung and Shop-Treffer.',
]);
}
@@ -491,6 +541,8 @@ 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.',
'- Shop product names are authoritative for their own shop URL, product number, price, availability, image, description, and metadata.',
'- Do not rewrite a shop record heading with a similar device name from retrieved knowledge. If identities differ or are uncertain, separate the RAG device from the shop hit.',
]);
}
@@ -638,6 +690,39 @@ final class PromptBuilderConfig
);
}
/**
* @return string[]
*/
public function getMainDeviceRequestRoleKeywords(): array
{
return $this->getStringList(
'product_roles.main_device_request_keywords',
self::MAIN_DEVICE_REQUEST_ROLE_KEYWORDS
);
}
/**
* @return string[]
*/
public function getMainDeviceProductRoleKeywords(): array
{
return $this->getStringList(
'product_roles.main_device_product_keywords',
self::MAIN_DEVICE_PRODUCT_ROLE_KEYWORDS
);
}
/**
* @return string[]
*/
public function getAccessoryProductRoleKeywords(): array
{
return $this->getStringList(
'product_roles.accessory_product_keywords',
self::ACCESSORY_PRODUCT_ROLE_KEYWORDS
);
}
public function getTechnicalProductModelPattern(): string
{
return $this->getString('technical_product_model_pattern', '/\b[\p{L}]{2,}\s?\d{2,5}\b/u');