This commit is contained in:
team 1
2026-05-06 14:41:37 +02:00
parent ced1431a35
commit e02c885527
5 changed files with 348 additions and 6 deletions

View File

@@ -486,12 +486,29 @@ final readonly class AgentRunner
}
}
$shopResults = $repairPayload['results'];
$shopResults = $this->guardDirectProductShopResults($prompt, $shopSearchQuery, $shopResults);
$unguardedShopResults = $repairPayload['results'];
$shopResults = $this->guardDirectProductShopResults($prompt, $shopSearchQuery, $unguardedShopResults);
$directIdentityRepairPayload = $this->repairEmptyDirectProductPrimaryIdentityResults(
prompt: $prompt,
userId: $userId,
commerceIntent: $commerceIntent,
shopSearchQuery: $shopSearchQuery,
unguardedShopResults: $unguardedShopResults,
guardedShopResults: $shopResults
);
if ($directIdentityRepairPayload['results'] !== null) {
$shopResults = $directIdentityRepairPayload['results'];
}
$shopResults = $this->sortShopResultsForLengthRequest($prompt, $shopSearchQuery, $shopResults);
$attemptedShopRepair = $repairPayload['attemptedRepair'];
$usedShopRepair = $repairPayload['usedRepair'];
$shopRepairQueries = $repairPayload['repairQueries'];
$attemptedShopRepair = $repairPayload['attemptedRepair'] || $directIdentityRepairPayload['attemptedRepair'];
$usedShopRepair = $repairPayload['usedRepair'] || $directIdentityRepairPayload['usedRepair'];
$shopRepairQueries = array_values(array_unique(array_merge(
$repairPayload['repairQueries'],
$directIdentityRepairPayload['repairQueries']
)));
if (!$primaryShopSearchHadSystemFailure) {
yield $this->systemMsg(
@@ -3081,6 +3098,157 @@ final readonly class AgentRunner
return array_values(array_merge($primaryMatches, $corpusMatches));
}
/**
* @param ShopProductResult[] $unguardedShopResults
* @param ShopProductResult[] $guardedShopResults
* @return array{results: array|null, attemptedRepair: bool, usedRepair: bool, repairQueries: string[]}
*/
private function repairEmptyDirectProductPrimaryIdentityResults(
string $prompt,
string $userId,
string $commerceIntent,
string $shopSearchQuery,
array $unguardedShopResults,
array $guardedShopResults
): array {
$emptyResult = [
'results' => null,
'attemptedRepair' => false,
'usedRepair' => false,
'repairQueries' => [],
];
if (
$guardedShopResults !== []
|| $unguardedShopResults === []
|| !$this->agentRunnerConfig->isDirectShopResultGuardPrimaryIdentityRepairEnabled()
) {
return $emptyResult;
}
$requestedTerms = $this->extractRequestedDirectProductTerms($prompt, $shopSearchQuery);
if ($requestedTerms === []) {
return $emptyResult;
}
$repairQuery = $this->buildDirectProductPrimaryIdentityRepairQuery(
shopSearchQuery: $shopSearchQuery,
requestedTerms: $requestedTerms
);
if ($repairQuery === '' || $this->normalizeShopQueryForComparison($repairQuery) === $this->normalizeShopQueryForComparison($shopSearchQuery)) {
return $emptyResult;
}
$this->agentLogger->info('Direct product primary identity guard retrying with cleaned repair query', [
'userId' => $userId,
'commerceIntent' => $commerceIntent,
'prompt' => $prompt,
'shopSearchQuery' => $shopSearchQuery,
'repairQuery' => $repairQuery,
'unguardedShopResultsCount' => count($unguardedShopResults),
'requestedTerms' => $requestedTerms,
]);
$repairResults = $this->searchShop(
$repairQuery,
$commerceIntent,
$userId,
''
);
if ($this->shopSearchService->hadLastSearchSystemFailure()) {
return [
'results' => null,
'attemptedRepair' => true,
'usedRepair' => false,
'repairQueries' => [$repairQuery],
];
}
$guardedRepairResults = $this->guardDirectProductShopResults($prompt, $repairQuery, $repairResults);
if ($guardedRepairResults === []) {
$this->agentLogger->info('Direct product primary identity repair finished without matching products', [
'userId' => $userId,
'commerceIntent' => $commerceIntent,
'prompt' => $prompt,
'shopSearchQuery' => $shopSearchQuery,
'repairQuery' => $repairQuery,
'repairResultsCount' => count($repairResults),
]);
return [
'results' => null,
'attemptedRepair' => true,
'usedRepair' => false,
'repairQueries' => [$repairQuery],
];
}
return [
'results' => $guardedRepairResults,
'attemptedRepair' => true,
'usedRepair' => true,
'repairQueries' => [$repairQuery],
];
}
/**
* @param string[] $requestedTerms
*/
private function buildDirectProductPrimaryIdentityRepairQuery(string $shopSearchQuery, array $requestedTerms): string
{
$tokens = $this->tokenizeShopQueryCandidate($shopSearchQuery);
if ($tokens === []) {
return '';
}
$stopTokens = [];
foreach ($this->agentRunnerConfig->getDirectShopResultGuardPrimaryIdentityRepairStopTerms() as $term) {
foreach ($this->tokenizeShopQueryCandidate($term) as $token) {
$stopTokens[$token] = true;
}
}
$requestedTokens = [];
foreach ($requestedTerms as $term) {
foreach ($this->tokenizeShopQueryCandidate($term) as $token) {
$requestedTokens[$token] = true;
}
}
$kept = [];
foreach ($tokens as $token) {
if (isset($stopTokens[$token]) && !isset($requestedTokens[$token])) {
continue;
}
if (isset($kept[$token])) {
continue;
}
$kept[$token] = $token;
}
foreach (array_keys($requestedTokens) as $requestedToken) {
if (!isset($kept[$requestedToken])) {
$kept[$requestedToken] = $requestedToken;
}
}
if (count($kept) < max(1, $this->agentRunnerConfig->getDirectShopResultGuardPrimaryIdentityRepairMinQueryTokens())) {
return '';
}
return trim(implode(' ', array_values($kept)));
}
private function normalizeShopQueryForComparison(string $query): string
{
return trim(implode(' ', $this->tokenizeShopQueryCandidate($query)));
}
/**
* @param ShopProductResult[] $shopResults
* @return ShopProductResult[]

View File

@@ -301,6 +301,21 @@ final class AgentRunnerConfig
throw new \InvalidArgumentException(sprintf('RetrieX agent config key "%s" must be numeric.', $key));
}
private function getOptionalInt(string $key, int $default): int
{
$value = $this->optionalValue($key);
if ($value === null) {
return $default;
}
if (is_numeric($value)) {
return (int) $value;
}
throw new \InvalidArgumentException(sprintf('RetrieX agent config key "%s" must be numeric.', $key));
}
private function getRequiredBool(string $key): bool
{
$value = $this->requiredValue($key);
@@ -1158,6 +1173,24 @@ final class AgentRunnerConfig
return $this->getOptionalStringList('shop_prompt.direct_result_guard.compound_prefix_match.terms');
}
public function isDirectShopResultGuardPrimaryIdentityRepairEnabled(): bool
{
return $this->getOptionalBool('shop_prompt.direct_result_guard.primary_identity_repair.enabled', true);
}
public function getDirectShopResultGuardPrimaryIdentityRepairMinQueryTokens(): int
{
return $this->getOptionalInt('shop_prompt.direct_result_guard.primary_identity_repair.min_query_tokens_after_cleanup', 2);
}
/**
* @return string[]
*/
public function getDirectShopResultGuardPrimaryIdentityRepairStopTerms(): array
{
return $this->getOptionalStringList('shop_prompt.direct_result_guard.primary_identity_repair.stop_terms');
}
public function isShopResultLengthSortEnabled(): bool
{
return $this->getRequiredBool('shop_prompt.length_sort.enabled');