This commit is contained in:
team 1
2026-05-10 12:04:40 +02:00
parent 82bfdad02b
commit 0174c8d1b1

View File

@@ -355,6 +355,21 @@ final readonly class AgentRunner
$optimizedShopQuery = '';
}
$deviceAnchoredShopSearchQuery = $this->enrichGenericDeviceShopQueryWithConfiguredAnchor($shopSearchQuery);
if ($deviceAnchoredShopSearchQuery !== $shopSearchQuery) {
$this->agentLogger->info('Enriched generic device shop query with configured product-family anchor', [
'userId' => $userId,
'prompt' => $prompt,
'routingPrompt' => $routingPrompt,
'optimizedShopQuery' => $optimizedShopQuery,
'shopSearchQuery' => $shopSearchQuery,
'deviceAnchoredShopSearchQuery' => $deviceAnchoredShopSearchQuery,
]);
$shopSearchQuery = $deviceAnchoredShopSearchQuery;
$optimizedShopQuery = '';
}
$positiveFilteredShopSearchQuery = $this->filterShopQueryToPositiveTokens($shopSearchQuery);
if ($positiveFilteredShopSearchQuery !== $shopSearchQuery) {
$this->agentLogger->info('Filtered final shop search query to positive product tokens', [
@@ -1718,6 +1733,99 @@ final readonly class AgentRunner
return $cleaned !== '' ? $cleaned : $shopSearchQuery;
}
private function enrichGenericDeviceShopQueryWithConfiguredAnchor(string $shopSearchQuery): string
{
$shopSearchQuery = trim($shopSearchQuery);
if (
$shopSearchQuery === ''
|| !$this->agentRunnerConfig->isGenericDeviceQueryAnchorEnabled()
) {
return $shopSearchQuery;
}
$tokens = $this->tokenizeShopQueryCandidate($shopSearchQuery);
if ($tokens === []) {
return $shopSearchQuery;
}
$tokenSet = array_fill_keys($tokens, true);
$genericDeviceTokens = $this->buildShopQueryTokenSet(
$this->agentRunnerConfig->getGenericDeviceQueryAnchorTriggerTerms()
);
if (!$this->tokenSetIntersects($tokenSet, $genericDeviceTokens)) {
return $shopSearchQuery;
}
$suppressTokens = $this->buildShopQueryTokenSet(
$this->agentRunnerConfig->getGenericDeviceQueryAnchorSuppressTerms()
);
if ($this->tokenSetIntersects($tokenSet, $suppressTokens)) {
return $shopSearchQuery;
}
foreach ($this->agentRunnerConfig->getGenericDeviceQueryAnchorRules() as $rule) {
$anchor = $rule['anchor'];
if ($anchor === '' || !$this->containsAnyShopQueryTerm($shopSearchQuery, $rule['match_terms'])) {
continue;
}
if ($this->queryAlreadyContainsAllAnchorTokens($shopSearchQuery, $anchor)) {
return $shopSearchQuery;
}
$query = $this->agentRunnerConfig->shouldGenericDeviceQueryAnchorRemoveGenericDeviceTerms()
? $this->removeConfiguredGenericDeviceShopQueryTerms(
$shopSearchQuery,
$this->agentRunnerConfig->getGenericDeviceQueryAnchorTriggerTerms()
)
: $shopSearchQuery;
$template = $this->agentRunnerConfig->getGenericDeviceQueryAnchorTemplate();
if ($template === '') {
return $shopSearchQuery;
}
$enriched = $this->renderAgentTemplate($template, [
'anchor' => $anchor,
'query' => $query,
]);
$enriched = preg_replace('/\s+/u', ' ', $enriched) ?? $enriched;
$enriched = trim($enriched);
return $enriched !== '' ? $enriched : $shopSearchQuery;
}
return $shopSearchQuery;
}
/**
* @param string[] $genericDeviceTerms
*/
private function removeConfiguredGenericDeviceShopQueryTerms(string $shopSearchQuery, array $genericDeviceTerms): string
{
$removeTokens = $this->buildShopQueryTokenSet($genericDeviceTerms);
if ($removeTokens === []) {
return $shopSearchQuery;
}
$kept = [];
foreach ($this->tokenizeShopQueryCandidate($shopSearchQuery) as $token) {
if (isset($removeTokens[$token]) || isset($kept[$token])) {
continue;
}
$kept[$token] = $token;
}
$cleaned = implode(' ', array_values($kept));
return $cleaned !== '' ? $cleaned : $shopSearchQuery;
}
private function filterShopQueryToPositiveTokens(string $shopSearchQuery): string
{
$shopSearchQuery = trim($shopSearchQuery);
@@ -1797,11 +1905,15 @@ final readonly class AgentRunner
array $variantPatterns,
array $variantTokens
): bool {
if (!isset($variantTokens[$token]) && !$this->matchesAnyConfiguredShopQueryCodePattern($token, $variantPatterns)) {
$isExplicitVariantToken = isset($variantTokens[$token]);
$isPatternVariantToken = $this->matchesAnyConfiguredShopQueryCodePattern($token, $variantPatterns);
if (!$isExplicitVariantToken && !$isPatternVariantToken) {
return false;
}
$hasAdjacentNumericContext = false;
$hasAdjacentExplicitVariantContext = false;
$nearbyKeptContextCount = 0;
for ($offset = -2; $offset <= 2; ++$offset) {
@@ -1822,9 +1934,23 @@ final readonly class AgentRunner
if (abs($offset) === 1 && preg_match('/\d/u', $nearbyToken) === 1) {
$hasAdjacentNumericContext = true;
}
if (abs($offset) === 1 && isset($variantTokens[$nearbyToken])) {
$hasAdjacentExplicitVariantContext = true;
}
}
return $hasAdjacentNumericContext && $nearbyKeptContextCount >= 2;
if ($hasAdjacentNumericContext && $nearbyKeptContextCount >= 2) {
return true;
}
// Preserve compact all-alpha model/acronym chains such as
// "Testomat LAB CL" without allowing arbitrary descriptive words to
// pass the positive token filter. The non-numeric path therefore uses
// only explicitly configured neighbouring variant terms from YAML.
return $isExplicitVariantToken
&& $hasAdjacentExplicitVariantContext
&& $nearbyKeptContextCount >= 1;
}
/**
@@ -1855,6 +1981,11 @@ final readonly class AgentRunner
);
}
$terms = $this->mergeUniqueStrings(
$terms,
$this->agentRunnerConfig->getGenericDeviceQueryAnchorPositiveFilterTerms()
);
$tokens = [];
foreach ($terms as $term) {
foreach ($this->tokenizeShopQueryCandidate($term) as $token) {