p43B
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
# RetrieX Patch 43B - Product Role Resolver Consolidation
|
||||
|
||||
## Ziel
|
||||
|
||||
Dieser Patch reduziert doppelte PHP-Rollenlogik fuer Produktrollen, ohne fachliche YAML-Werte, Ranking-Gewichte oder Prompt-Regeln zu aendern.
|
||||
|
||||
## Inhalt
|
||||
|
||||
Neu:
|
||||
|
||||
- `src/Commerce/ProductRoleResolver.php`
|
||||
|
||||
Geaendert:
|
||||
|
||||
- `src/Commerce/ShopSearchService.php`
|
||||
- `src/Agent/PromptBuilder.php`
|
||||
|
||||
Die bisher lokal duplizierte Logik fuer angefragte Produktrolle, Produktrolle und Rollenkompatibilitaet wird ueber den zentralen Resolver gefuehrt.
|
||||
|
||||
## Bewusst nicht geaendert
|
||||
|
||||
- Keine YAML-Werte geaendert
|
||||
- Keine neuen Token-/Stringlisten im PHP-Core
|
||||
- Keine Scoring-Gewichte geaendert
|
||||
- Keine Search-Repair-Logik geaendert
|
||||
- Kein Adminbereich
|
||||
- Keine fachliche Runtime-Strategie geaendert
|
||||
|
||||
## Erwartete Wirkung
|
||||
|
||||
Der Effekt soll gleich bleiben. Der Patch bereitet die weitere Reduktion und generischere Rollenbehandlung vor, indem die mehrfach vorhandene Rollenlogik zentralisiert wird.
|
||||
|
||||
## Empfohlene Checks
|
||||
|
||||
```bash
|
||||
bin/console mto:agent:config:validate
|
||||
bin/console mto:agent:regression:test
|
||||
bin/console mto:agent:config:audit-source --details
|
||||
bin/console mto:agent:config:audit-patterns --details
|
||||
```
|
||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace App\Agent;
|
||||
|
||||
use App\Commerce\Dto\ShopProductResult;
|
||||
use App\Commerce\ProductRoleResolver;
|
||||
use App\Config\LanguageCleanupConfig;
|
||||
use App\Config\PromptBuilderConfig;
|
||||
use App\Context\ContextService;
|
||||
@@ -20,6 +21,7 @@ final readonly class PromptBuilder
|
||||
private SystemPromptRepository $systemPromptRepository,
|
||||
private ModelGenerationConfigProvider $modelGenerationConfigProvider,
|
||||
private PromptBuilderConfig $config,
|
||||
private ProductRoleResolver $productRoleResolver,
|
||||
private LanguageCleanupConfig $languageCleanupConfig,
|
||||
) {
|
||||
}
|
||||
@@ -1258,112 +1260,40 @@ final readonly class PromptBuilder
|
||||
|
||||
private function resolveRequestedProductRole(string $prompt): string
|
||||
{
|
||||
$normalized = mb_strtolower($this->normalizeBlockText($prompt), 'UTF-8');
|
||||
$hasAccessoryIntent = $this->containsAnyPromptKeyword($normalized, $this->config->getAccessoryProductRoleKeywords());
|
||||
$hasMainDeviceIntent = $this->containsAnyPromptKeyword($normalized, $this->config->getMainDeviceRequestRoleKeywords());
|
||||
|
||||
if ($hasAccessoryIntent && !$this->hasDirectMainDeviceRequest($normalized)) {
|
||||
return 'accessory_or_consumable';
|
||||
}
|
||||
|
||||
if ($hasMainDeviceIntent) {
|
||||
return 'main_device';
|
||||
}
|
||||
|
||||
if ($hasAccessoryIntent) {
|
||||
return 'accessory_or_consumable';
|
||||
}
|
||||
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
private function hasDirectMainDeviceRequest(string $normalizedPrompt): bool
|
||||
{
|
||||
foreach ($this->config->getDirectMainDeviceRequestPatterns() as $pattern) {
|
||||
if (preg_match($pattern, $normalizedPrompt) === 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return $this->productRoleResolver->resolveRequestedRole(
|
||||
prompt: $prompt,
|
||||
accessoryIntentKeywords: $this->config->getAccessoryProductRoleKeywords(),
|
||||
mainDeviceIntentKeywords: $this->config->getMainDeviceRequestRoleKeywords(),
|
||||
directMainDeviceRequestPatterns: $this->config->getDirectMainDeviceRequestPatterns(),
|
||||
normalize: fn(string $value): string => $this->normalizeBlockText($value)
|
||||
);
|
||||
}
|
||||
|
||||
private function resolveShopProductRole(ShopProductResult $product): string
|
||||
{
|
||||
$primaryRole = $this->resolveShopPrimaryProductRole($product);
|
||||
|
||||
if ($primaryRole !== 'unknown') {
|
||||
return $primaryRole;
|
||||
}
|
||||
|
||||
$corpus = mb_strtolower(implode(' ', array_filter([
|
||||
$product->name,
|
||||
$product->productNumber,
|
||||
$product->manufacturer,
|
||||
implode(' ', $product->highlights),
|
||||
$product->description,
|
||||
$product->customFields,
|
||||
$product->url,
|
||||
])), 'UTF-8');
|
||||
|
||||
$isAccessory = $this->containsAnyPromptKeyword($corpus, $this->config->getAccessoryProductRoleKeywords());
|
||||
$isMainDevice = $this->containsAnyPromptKeyword($corpus, $this->config->getMainDeviceProductRoleKeywords());
|
||||
|
||||
if ($isAccessory) {
|
||||
return 'accessory_or_consumable';
|
||||
}
|
||||
|
||||
if ($isMainDevice) {
|
||||
return 'main_device';
|
||||
}
|
||||
|
||||
return 'unknown';
|
||||
return $this->productRoleResolver->resolveProductRole(
|
||||
product: $product,
|
||||
accessoryKeywords: $this->config->getAccessoryProductRoleKeywords(),
|
||||
deviceKeywords: $this->config->getMainDeviceProductRoleKeywords(),
|
||||
normalize: fn(string $value): string => $this->normalizeBlockText($value),
|
||||
detectAmbiguousPrimaryRole: false
|
||||
);
|
||||
}
|
||||
|
||||
private function resolveShopPrimaryProductRole(ShopProductResult $product): string
|
||||
{
|
||||
$primaryText = mb_strtolower(implode(' ', array_filter([
|
||||
$product->name,
|
||||
$product->url,
|
||||
])), 'UTF-8');
|
||||
|
||||
if ($this->normalizeBlockText($primaryText) === '') {
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
$isAccessory = $this->containsAnyPromptKeyword($primaryText, $this->config->getAccessoryProductRoleKeywords());
|
||||
$isMainDevice = $this->containsAnyPromptKeyword($primaryText, $this->config->getMainDeviceProductRoleKeywords());
|
||||
|
||||
if ($isAccessory) {
|
||||
return 'accessory_or_consumable';
|
||||
}
|
||||
|
||||
if ($isMainDevice) {
|
||||
return 'main_device';
|
||||
}
|
||||
|
||||
return 'unknown';
|
||||
return $this->productRoleResolver->resolvePrimaryProductRole(
|
||||
product: $product,
|
||||
accessoryKeywords: $this->config->getAccessoryProductRoleKeywords(),
|
||||
deviceKeywords: $this->config->getMainDeviceProductRoleKeywords(),
|
||||
normalize: fn(string $value): string => $this->normalizeBlockText($value),
|
||||
detectAmbiguousPrimaryRole: false
|
||||
);
|
||||
}
|
||||
|
||||
private function resolveShopRoleCompatibility(string $requestedRole, string $inferredRole): string
|
||||
{
|
||||
if ($requestedRole === 'unknown' || $inferredRole === 'unknown') {
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
if ($requestedRole === 'main_device' && $inferredRole === 'accessory_or_consumable') {
|
||||
return 'incompatible_accessory_for_main_device_request';
|
||||
}
|
||||
|
||||
if ($requestedRole === 'accessory_or_consumable' && $inferredRole === 'main_device') {
|
||||
return 'incompatible_main_device_for_accessory_request';
|
||||
}
|
||||
|
||||
if ($inferredRole === 'ambiguous_mixed_role') {
|
||||
return 'ambiguous_keep_separate';
|
||||
}
|
||||
|
||||
return 'compatible';
|
||||
return $this->productRoleResolver->resolveCompatibility($requestedRole, $inferredRole);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -31,6 +31,7 @@ final class ShopSearchService
|
||||
private readonly ShopwareCriteriaBuilder $criteriaBuilder,
|
||||
private readonly StoreApiClient $storeApiClient,
|
||||
private readonly ShopServiceConfig $shopConfig,
|
||||
private readonly ProductRoleResolver $productRoleResolver,
|
||||
private readonly LoggerInterface $logger,
|
||||
private readonly bool $enabled = true,
|
||||
private readonly int $maxResults = 25,
|
||||
@@ -1330,80 +1331,24 @@ final class ShopSearchService
|
||||
|
||||
private function isAccessoryLikeProduct(ShopProductResult $product): bool
|
||||
{
|
||||
$primaryRole = $this->resolvePrimaryShopProductRole($product);
|
||||
|
||||
if ($primaryRole === 'accessory_or_consumable') {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($primaryRole === 'main_device') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->containsAnyShopKeyword(
|
||||
$this->buildNormalizedProductCorpus($product),
|
||||
$this->shopConfig->getAccessoryProductKeywords()
|
||||
return $this->productRoleResolver->isAccessoryLikeProduct(
|
||||
product: $product,
|
||||
accessoryKeywords: $this->shopConfig->getAccessoryProductKeywords(),
|
||||
deviceKeywords: $this->shopConfig->getDeviceProductKeywords(),
|
||||
normalize: fn(string $value): string => $this->normalizeForMatching($value)
|
||||
);
|
||||
}
|
||||
|
||||
private function isDeviceLikeProduct(ShopProductResult $product): bool
|
||||
{
|
||||
$primaryRole = $this->resolvePrimaryShopProductRole($product);
|
||||
|
||||
if ($primaryRole === 'main_device') {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($primaryRole === 'accessory_or_consumable') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->containsAnyShopKeyword(
|
||||
$this->buildNormalizedProductCorpus($product),
|
||||
$this->shopConfig->getDeviceProductKeywords()
|
||||
return $this->productRoleResolver->isDeviceLikeProduct(
|
||||
product: $product,
|
||||
accessoryKeywords: $this->shopConfig->getAccessoryProductKeywords(),
|
||||
deviceKeywords: $this->shopConfig->getDeviceProductKeywords(),
|
||||
normalize: fn(string $value): string => $this->normalizeForMatching($value)
|
||||
);
|
||||
}
|
||||
|
||||
private function resolvePrimaryShopProductRole(ShopProductResult $product): string
|
||||
{
|
||||
$primaryText = $this->buildNormalizedPrimaryProductIdentity($product);
|
||||
|
||||
if ($primaryText === '') {
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
$isAccessoryLike = $this->containsAnyShopKeyword(
|
||||
$primaryText,
|
||||
$this->shopConfig->getAccessoryProductKeywords()
|
||||
);
|
||||
$isDeviceLike = $this->containsAnyShopKeyword(
|
||||
$primaryText,
|
||||
$this->shopConfig->getDeviceProductKeywords()
|
||||
);
|
||||
|
||||
if ($isAccessoryLike && !$isDeviceLike) {
|
||||
return 'accessory_or_consumable';
|
||||
}
|
||||
|
||||
if ($isDeviceLike && !$isAccessoryLike) {
|
||||
return 'main_device';
|
||||
}
|
||||
|
||||
if ($isAccessoryLike && $isDeviceLike) {
|
||||
return 'ambiguous_mixed_role';
|
||||
}
|
||||
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
private function buildNormalizedPrimaryProductIdentity(ShopProductResult $product): string
|
||||
{
|
||||
return $this->normalizeForMatching(implode(' ', array_filter([
|
||||
$product->name,
|
||||
$product->url,
|
||||
])));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $keywords
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user