This commit is contained in:
team 1
2026-05-07 18:14:30 +02:00
parent 0977cec651
commit 8ff06cee2c
8 changed files with 285 additions and 8 deletions

View File

@@ -502,6 +502,7 @@ final readonly class AgentRunner
$shopResults = $directIdentityRepairPayload['results'];
}
$shopResults = $this->guardShopResultsByReferencedProductAnchor($shopSearchQuery, $shopResults);
$shopResults = $this->sortShopResultsForLengthRequest($prompt, $shopSearchQuery, $shopResults);
$attemptedShopRepair = $repairPayload['attemptedRepair'] || $directIdentityRepairPayload['attemptedRepair'];
$usedShopRepair = $repairPayload['usedRepair'] || $directIdentityRepairPayload['usedRepair'];
@@ -2672,20 +2673,40 @@ final readonly class AgentRunner
return '';
}
$triggerTokens = [];
foreach ($this->agentRunnerConfig->getShopQueryContextAnchorEnrichmentTriggerTerms() as $term) {
foreach ($this->tokenizeShopQueryCandidate($term) as $termToken) {
$triggerTokens[$termToken] = true;
}
}
$triggerTokens = $this->buildShopQueryTokenSet(
$this->agentRunnerConfig->getShopQueryContextAnchorEnrichmentTriggerTerms()
);
if ($triggerTokens === []) {
return '';
}
$hasTrigger = false;
foreach ($tokens as $token) {
if (isset($triggerTokens[$token])) {
$hasTrigger = true;
break;
}
}
if (!$hasTrigger) {
return '';
}
$queryTokens = $this->buildShopQueryTokenSet(
$this->agentRunnerConfig->getShopQueryContextAnchorEnrichmentQueryTerms()
);
if ($queryTokens === []) {
$queryTokens = $triggerTokens;
}
$noiseTokens = $this->buildShopQueryTokenSet(
$this->agentRunnerConfig->getShopQueryContextAnchorEnrichmentQueryNoiseTerms()
);
$out = [];
foreach ($tokens as $token) {
if (!isset($triggerTokens[$token]) || isset($out[$token])) {
if (!isset($queryTokens[$token]) || isset($noiseTokens[$token]) || isset($out[$token])) {
continue;
}
@@ -2695,6 +2716,23 @@ final readonly class AgentRunner
return implode(' ', array_values($out));
}
/**
* @param string[] $terms
* @return array<string, true>
*/
private function buildShopQueryTokenSet(array $terms): array
{
$tokens = [];
foreach ($terms as $term) {
foreach ($this->tokenizeShopQueryCandidate($term) as $termToken) {
$tokens[$termToken] = true;
}
}
return $tokens;
}
private function enrichReferentialShopQueryFromHistory(
string $query,
string $sourcePrompt,
@@ -2763,11 +2801,33 @@ final readonly class AgentRunner
}
private function extractLatestConfiguredShopQueryContextAnchor(string $commerceHistoryContext): string
{
foreach ($this->extractHistoryTurnsNewestFirst($commerceHistoryContext) as $turn) {
if (!$this->containsConfiguredShopQueryAnchorTrigger($turn)) {
continue;
}
$modelAnchor = $this->referenceAnchorExtractor->extractFirstProductModelAnchor($turn);
$turnAnchor = $this->extractLatestConfiguredShopQueryPatternAnchor($turn);
if ($modelAnchor !== '') {
return $this->buildModelQualifiedShopQueryAnchor($modelAnchor, $turnAnchor);
}
if ($turnAnchor !== '') {
return $turnAnchor;
}
}
return $this->extractLatestConfiguredShopQueryPatternAnchor($commerceHistoryContext);
}
private function extractLatestConfiguredShopQueryPatternAnchor(string $text): string
{
$latest = '';
foreach ($this->agentRunnerConfig->getShopQueryContextAnchorEnrichmentPatterns() as $pattern) {
if (@preg_match_all($pattern, $commerceHistoryContext, $matches, PREG_SET_ORDER) === false) {
if (@preg_match_all($pattern, $text, $matches, PREG_SET_ORDER) === false) {
continue;
}
@@ -2782,6 +2842,51 @@ final readonly class AgentRunner
return $latest;
}
private function buildModelQualifiedShopQueryAnchor(string $modelAnchor, string $detailAnchor): string
{
$modelAnchor = trim($modelAnchor);
if ($modelAnchor === '') {
return trim($detailAnchor);
}
$detailTokens = $this->extractShopQueryDetailAnchorTokens($detailAnchor, $modelAnchor);
if ($detailTokens === []) {
return $modelAnchor;
}
return trim($modelAnchor . ' ' . implode(' ', $detailTokens));
}
/**
* @return string[]
*/
private function extractShopQueryDetailAnchorTokens(string $detailAnchor, string $modelAnchor): array
{
$tokens = $this->tokenizeShopQueryCandidate($detailAnchor);
if ($tokens === []) {
return [];
}
$modelTokens = array_fill_keys($this->tokenizeShopQueryCandidate($modelAnchor), true);
$queryTokens = $this->buildShopQueryTokenSet(
$this->agentRunnerConfig->getShopQueryContextAnchorEnrichmentQueryTerms()
);
$noiseTokens = $this->buildShopQueryTokenSet(
$this->agentRunnerConfig->getShopQueryContextAnchorEnrichmentQueryNoiseTerms()
);
$out = [];
foreach ($tokens as $token) {
if (isset($modelTokens[$token]) || isset($queryTokens[$token]) || isset($noiseTokens[$token]) || isset($out[$token])) {
continue;
}
$out[$token] = $token;
}
return array_values($out);
}
private function normalizeShopQueryAnchor(string $anchor): string
{
$anchor = str_replace('®', '', $anchor);
@@ -3249,6 +3354,48 @@ final readonly class AgentRunner
return trim(implode(' ', $this->tokenizeShopQueryCandidate($query)));
}
/**
* @param ShopProductResult[] $shopResults
* @return ShopProductResult[]
*/
private function guardShopResultsByReferencedProductAnchor(string $shopSearchQuery, array $shopResults): array
{
if ($shopResults === []) {
return $shopResults;
}
$anchor = $this->referenceAnchorExtractor->extractFirstProductModelAnchor($shopSearchQuery);
if ($anchor === '') {
return $shopResults;
}
$filtered = [];
foreach ($shopResults as $product) {
if (!$product instanceof ShopProductResult) {
continue;
}
if ($this->shopProductMatchesReferencedProductAnchor($product, $anchor)) {
$filtered[] = $product;
}
}
return $filtered;
}
private function shopProductMatchesReferencedProductAnchor(ShopProductResult $product, string $anchor): bool
{
$productText = trim(implode(' ', array_filter([
$product->name,
$product->description,
implode(' ', $product->highlights),
$product->customFields,
$product->url,
])));
return $this->containsAllShopQueryTokens($productText, $anchor);
}
/**
* @param ShopProductResult[] $shopResults
* @return ShopProductResult[]