p85
This commit is contained in:
@@ -355,6 +355,21 @@ final readonly class AgentRunner
|
|||||||
$optimizedShopQuery = '';
|
$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);
|
$positiveFilteredShopSearchQuery = $this->filterShopQueryToPositiveTokens($shopSearchQuery);
|
||||||
if ($positiveFilteredShopSearchQuery !== $shopSearchQuery) {
|
if ($positiveFilteredShopSearchQuery !== $shopSearchQuery) {
|
||||||
$this->agentLogger->info('Filtered final shop search query to positive product tokens', [
|
$this->agentLogger->info('Filtered final shop search query to positive product tokens', [
|
||||||
@@ -1718,6 +1733,99 @@ final readonly class AgentRunner
|
|||||||
return $cleaned !== '' ? $cleaned : $shopSearchQuery;
|
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
|
private function filterShopQueryToPositiveTokens(string $shopSearchQuery): string
|
||||||
{
|
{
|
||||||
$shopSearchQuery = trim($shopSearchQuery);
|
$shopSearchQuery = trim($shopSearchQuery);
|
||||||
@@ -1797,11 +1905,15 @@ final readonly class AgentRunner
|
|||||||
array $variantPatterns,
|
array $variantPatterns,
|
||||||
array $variantTokens
|
array $variantTokens
|
||||||
): bool {
|
): bool {
|
||||||
if (!isset($variantTokens[$token]) && !$this->matchesAnyConfiguredShopQueryCodePattern($token, $variantPatterns)) {
|
$isExplicitVariantToken = isset($variantTokens[$token]);
|
||||||
|
$isPatternVariantToken = $this->matchesAnyConfiguredShopQueryCodePattern($token, $variantPatterns);
|
||||||
|
|
||||||
|
if (!$isExplicitVariantToken && !$isPatternVariantToken) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$hasAdjacentNumericContext = false;
|
$hasAdjacentNumericContext = false;
|
||||||
|
$hasAdjacentExplicitVariantContext = false;
|
||||||
$nearbyKeptContextCount = 0;
|
$nearbyKeptContextCount = 0;
|
||||||
|
|
||||||
for ($offset = -2; $offset <= 2; ++$offset) {
|
for ($offset = -2; $offset <= 2; ++$offset) {
|
||||||
@@ -1822,9 +1934,23 @@ final readonly class AgentRunner
|
|||||||
if (abs($offset) === 1 && preg_match('/\d/u', $nearbyToken) === 1) {
|
if (abs($offset) === 1 && preg_match('/\d/u', $nearbyToken) === 1) {
|
||||||
$hasAdjacentNumericContext = true;
|
$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 = [];
|
$tokens = [];
|
||||||
foreach ($terms as $term) {
|
foreach ($terms as $term) {
|
||||||
foreach ($this->tokenizeShopQueryCandidate($term) as $token) {
|
foreach ($this->tokenizeShopQueryCandidate($term) as $token) {
|
||||||
|
|||||||
Reference in New Issue
Block a user