patch 9.1
This commit is contained in:
@@ -0,0 +1,46 @@
|
|||||||
|
# RetrieX Patch 9.0b - PromptBuilder Labels and Shop Result Texts YAML-only
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This patch builds on Patch 9.0a.
|
||||||
|
|
||||||
|
It only removes PHP fallback defaults from small PromptBuilder text/label getters:
|
||||||
|
|
||||||
|
- section labels
|
||||||
|
- conversation context intro lines
|
||||||
|
- shop search source line
|
||||||
|
- live shop result header lines
|
||||||
|
- shop product record templates
|
||||||
|
- shop result field labels
|
||||||
|
- retrieved knowledge / URL content labels and source lines
|
||||||
|
|
||||||
|
## Not changed
|
||||||
|
|
||||||
|
This patch does not change:
|
||||||
|
|
||||||
|
- output priority rule texts
|
||||||
|
- fallback escalation rules
|
||||||
|
- response format rules
|
||||||
|
- language rules
|
||||||
|
- fact grounding rules
|
||||||
|
- measurement evidence guard
|
||||||
|
- role guard keyword lists
|
||||||
|
- retrieval
|
||||||
|
- commerce parser
|
||||||
|
- shop matching
|
||||||
|
- AgentRunner
|
||||||
|
- SSE / frontend
|
||||||
|
|
||||||
|
## Expected audit effect
|
||||||
|
|
||||||
|
PromptBuilderConfig should lose the fallback accessor entries for the migrated labels/templates/lists.
|
||||||
|
Remaining PromptBuilder fallback accessors are expected for the larger rule groups that will be handled in later 9.x patches.
|
||||||
|
|
||||||
|
## Test commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
php bin/console cache:clear
|
||||||
|
php bin/console mto:agent:config:validate
|
||||||
|
php bin/console mto:agent:config:audit-source --details
|
||||||
|
php bin/console mto:agent:regression:test
|
||||||
|
```
|
||||||
@@ -263,6 +263,22 @@ final class PromptBuilderConfig
|
|||||||
return $value !== '' ? $value : $default;
|
return $value !== '' ? $value : $default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getRequiredString(string $path): string
|
||||||
|
{
|
||||||
|
$value = $this->getRequiredValue($path);
|
||||||
|
|
||||||
|
if (!is_scalar($value)) {
|
||||||
|
throw new \InvalidArgumentException(sprintf('RetrieX prompt config value "%s" must be a scalar string.', $path));
|
||||||
|
}
|
||||||
|
|
||||||
|
$value = (string) $value;
|
||||||
|
if (trim($value) === '') {
|
||||||
|
throw new \InvalidArgumentException(sprintf('RetrieX prompt config value "%s" must not be empty.', $path));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string[]
|
* @return string[]
|
||||||
*/
|
*/
|
||||||
@@ -291,6 +307,38 @@ final class PromptBuilderConfig
|
|||||||
return $out !== [] ? $out : $default;
|
return $out !== [] ? $out : $default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
private function getRequiredStringList(string $path): array
|
||||||
|
{
|
||||||
|
$value = $this->getRequiredValue($path);
|
||||||
|
|
||||||
|
if (!is_array($value)) {
|
||||||
|
throw new \InvalidArgumentException(sprintf('RetrieX prompt config value "%s" must be a string list.', $path));
|
||||||
|
}
|
||||||
|
|
||||||
|
$out = [];
|
||||||
|
foreach ($value as $item) {
|
||||||
|
if (!is_scalar($item)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$item = trim((string) $item);
|
||||||
|
if ($item === '' || in_array($item, $out, true)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$out[] = $item;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($out === []) {
|
||||||
|
throw new \InvalidArgumentException(sprintf('RetrieX prompt config value "%s" must contain at least one string.', $path));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $out;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string[]
|
* @return string[]
|
||||||
*/
|
*/
|
||||||
@@ -331,17 +379,17 @@ final class PromptBuilderConfig
|
|||||||
|
|
||||||
public function getSystemSectionLabel(): string
|
public function getSystemSectionLabel(): string
|
||||||
{
|
{
|
||||||
return $this->getString('sections.system_label', 'SYSTEM');
|
return $this->getRequiredString('sections.system_label');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getUserQuestionSectionLabel(): string
|
public function getUserQuestionSectionLabel(): string
|
||||||
{
|
{
|
||||||
return $this->getString('sections.user_question_label', 'USER QUESTION');
|
return $this->getRequiredString('sections.user_question_label');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getConversationContextSectionLabel(): string
|
public function getConversationContextSectionLabel(): string
|
||||||
{
|
{
|
||||||
return $this->getString('sections.conversation_context_label', 'CONVERSATION CONTEXT (contextual only)');
|
return $this->getRequiredString('sections.conversation_context_label');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -349,23 +397,17 @@ final class PromptBuilderConfig
|
|||||||
*/
|
*/
|
||||||
public function getConversationContextIntroLines(): array
|
public function getConversationContextIntroLines(): array
|
||||||
{
|
{
|
||||||
return $this->getStringList('conversation_context.intro_lines', [
|
return $this->getRequiredStringList('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
|
public function getShopSearchQuerySectionLabel(): string
|
||||||
{
|
{
|
||||||
return $this->getString('sections.shop_search_query_label', 'SHOP SEARCH QUERY');
|
return $this->getRequiredString('sections.shop_search_query_label');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getShopSearchQuerySourceLine(): string
|
public function getShopSearchQuerySourceLine(): string
|
||||||
{
|
{
|
||||||
return $this->getString('shop_search.source_line', 'Source: Shop Search');
|
return $this->getRequiredString('shop_search.source_line');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -373,36 +415,22 @@ final class PromptBuilderConfig
|
|||||||
*/
|
*/
|
||||||
public function getLiveShopResultsHeaderLines(): array
|
public function getLiveShopResultsHeaderLines(): array
|
||||||
{
|
{
|
||||||
return $this->getStringList('shop_results.header_lines', [
|
return $this->getRequiredStringList('shop_results.header_lines');
|
||||||
'LIVE SHOP RESULTS (authoritative for current commercial details):',
|
|
||||||
'Use these results as the primary source for current price, availability, URL, current shop-visible product naming, and explicitly shop-visible product suitability when the user asks which product/device can measure or monitor something.',
|
|
||||||
'If retrieved documents conflict with shop data on price, availability, URL, current naming, or explicitly shop-visible product suitability, prefer the shop data for those fields.',
|
|
||||||
'If retrieved documents do not identify a matching product, but a live shop result explicitly names the requested measurement parameter or application, do not conclude that no matching product exists.',
|
|
||||||
'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.',
|
|
||||||
'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.',
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getLiveShopResultsOverflowNoticeTemplate(): string
|
public function getLiveShopResultsOverflowNoticeTemplate(): string
|
||||||
{
|
{
|
||||||
return $this->getString('shop_results.overflow_notice_template', 'Only the top %d ranked shop results are shown here out of %d total results.');
|
return $this->getRequiredString('shop_results.overflow_notice_template');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getShopRecordHeaderTemplate(): string
|
public function getShopRecordHeaderTemplate(): string
|
||||||
{
|
{
|
||||||
return $this->getString('shop_results.record_header_template', '[%d] SHOP PRODUCT RECORD');
|
return $this->getRequiredString('shop_results.record_header_template');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getShopExactProductNameLabel(): string
|
public function getShopExactProductNameLabel(): string
|
||||||
{
|
{
|
||||||
return $this->getString('shop_results.exact_product_name_label', 'Exact shop product name');
|
return $this->getRequiredString('shop_results.exact_product_name_label');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -410,14 +438,12 @@ final class PromptBuilderConfig
|
|||||||
*/
|
*/
|
||||||
public function getShopAtomicRecordNoteLines(): array
|
public function getShopAtomicRecordNoteLines(): array
|
||||||
{
|
{
|
||||||
return $this->getStringList('shop_results.atomic_record_note_lines', [
|
return $this->getRequiredStringList('shop_results.atomic_record_note_lines');
|
||||||
'Record boundary: all fields below belong only to this exact shop product record.',
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getOutputPrioritySectionLabel(): string
|
public function getOutputPrioritySectionLabel(): string
|
||||||
{
|
{
|
||||||
return $this->getString('sections.output_priority_label', 'OUTPUT PRIORITY');
|
return $this->getRequiredString('sections.output_priority_label');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -700,100 +726,97 @@ final class PromptBuilderConfig
|
|||||||
|
|
||||||
public function getRetrievedKnowledgeSectionLabel(): string
|
public function getRetrievedKnowledgeSectionLabel(): string
|
||||||
{
|
{
|
||||||
return $this->getString('sections.retrieved_knowledge_label', 'RETRIEVED KNOWLEDGE (primary for technical matching and factual explanation)');
|
return $this->getRequiredString('sections.retrieved_knowledge_label');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRetrievedKnowledgeSourceLine(): string
|
public function getRetrievedKnowledgeSourceLine(): string
|
||||||
{
|
{
|
||||||
return $this->getString('retrieved_knowledge.source_line', 'Source: Documents');
|
return $this->getRequiredString('retrieved_knowledge.source_line');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getUrlContentSectionLabel(): string
|
public function getUrlContentSectionLabel(): string
|
||||||
{
|
{
|
||||||
return $this->getString('sections.url_content_label', 'CONTENT FROM URL (authoritative if user-provided)');
|
return $this->getRequiredString('sections.url_content_label');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getUrlContentSourceLine(): string
|
public function getUrlContentSourceLine(): string
|
||||||
{
|
{
|
||||||
return $this->getString('url_content.source_line', 'Source: URL');
|
return $this->getRequiredString('url_content.source_line');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getShopProductNumberLabel(): string
|
public function getShopProductNumberLabel(): string
|
||||||
{
|
{
|
||||||
return $this->getString('shop_results.fields.product_number_label', 'Product number');
|
return $this->getRequiredString('shop_results.fields.product_number_label');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getShopManufacturerLabel(): string
|
public function getShopManufacturerLabel(): string
|
||||||
{
|
{
|
||||||
return $this->getString('shop_results.fields.manufacturer_label', 'Manufacturer');
|
return $this->getRequiredString('shop_results.fields.manufacturer_label');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getShopPriceLabel(): string
|
public function getShopPriceLabel(): string
|
||||||
{
|
{
|
||||||
return $this->getString('shop_results.fields.price_label', 'Price');
|
return $this->getRequiredString('shop_results.fields.price_label');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getShopAvailabilityLabel(): string
|
public function getShopAvailabilityLabel(): string
|
||||||
{
|
{
|
||||||
return $this->getString('shop_results.fields.availability_label', 'Available');
|
return $this->getRequiredString('shop_results.fields.availability_label');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getShopAvailabilityYesLabel(): string
|
public function getShopAvailabilityYesLabel(): string
|
||||||
{
|
{
|
||||||
return $this->getString('shop_results.fields.availability_yes_label', 'yes');
|
return $this->getRequiredString('shop_results.fields.availability_yes_label');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getShopAvailabilityNoLabel(): string
|
public function getShopAvailabilityNoLabel(): string
|
||||||
{
|
{
|
||||||
return $this->getString('shop_results.fields.availability_no_label', 'no');
|
return $this->getRequiredString('shop_results.fields.availability_no_label');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getShopHighlightPrefix(): string
|
public function getShopHighlightPrefix(): string
|
||||||
{
|
{
|
||||||
return $this->getString('shop_results.fields.highlight_prefix', '- ');
|
return $this->getRequiredString('shop_results.fields.highlight_prefix');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getShopUrlLabel(): string
|
public function getShopUrlLabel(): string
|
||||||
{
|
{
|
||||||
return $this->getString('shop_results.fields.url_label', 'URL');
|
return $this->getRequiredString('shop_results.fields.url_label');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getShopProductImageLabel(): string
|
public function getShopProductImageLabel(): string
|
||||||
{
|
{
|
||||||
return $this->getString('shop_results.fields.product_image_label', 'Product image');
|
return $this->getRequiredString('shop_results.fields.product_image_label');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getShopDescriptionLabel(): string
|
public function getShopDescriptionLabel(): string
|
||||||
{
|
{
|
||||||
return $this->getString('shop_results.fields.description_label', 'Description');
|
return $this->getRequiredString('shop_results.fields.description_label');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getShopMetaInformationLabel(): string
|
public function getShopMetaInformationLabel(): string
|
||||||
{
|
{
|
||||||
return $this->getString('shop_results.fields.meta_information_label', 'Meta information');
|
return $this->getRequiredString('shop_results.fields.meta_information_label');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getShopRequestedRoleLabel(): string
|
public function getShopRequestedRoleLabel(): string
|
||||||
{
|
{
|
||||||
return $this->getString('shop_results.fields.requested_role_label', 'Requested product role');
|
return $this->getRequiredString('shop_results.fields.requested_role_label');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getShopInferredRoleLabel(): string
|
public function getShopInferredRoleLabel(): string
|
||||||
{
|
{
|
||||||
return $this->getString('shop_results.fields.inferred_role_label', 'Inferred shop product role');
|
return $this->getRequiredString('shop_results.fields.inferred_role_label');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getShopRoleCompatibilityLabel(): string
|
public function getShopRoleCompatibilityLabel(): string
|
||||||
{
|
{
|
||||||
return $this->getString('shop_results.fields.role_compatibility_label', 'Role compatibility with request');
|
return $this->getRequiredString('shop_results.fields.role_compatibility_label');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getShopRoleIncompatibleCommercialSuppressionNote(): string
|
public function getShopRoleIncompatibleCommercialSuppressionNote(): string
|
||||||
{
|
{
|
||||||
return $this->getString(
|
return $this->getRequiredString('shop_results.fields.role_incompatible_commercial_suppression_note');
|
||||||
'shop_results.fields.role_incompatible_commercial_suppression_note',
|
|
||||||
'Commercial fields suppressed: this shop record is not a matching main-device result for the requested product role.'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user