add history to shop search

This commit is contained in:
team 1
2026-04-17 12:59:21 +02:00
parent bab3682975
commit ae2b52ad18
8 changed files with 630 additions and 172 deletions

View File

@@ -21,29 +21,61 @@ final readonly class CommerceQueryParser
{
}
public function parse(string $originalPrompt, string $intent): CommerceSearchQuery
public function parse(
string $originalPrompt,
string $intent,
string $historyContext = ''
): CommerceSearchQuery
{
$normalized = $this->normalize($originalPrompt);
[$priceMin, $priceMax] = $this->extractPriceRange($normalized);
$sizes = $this->extractSizes($normalized);
$brand = $this->extractBrand($normalized);
$properties = [];
$normalizedPrompt = $this->normalize($originalPrompt);
[$priceMin, $priceMax] = $this->extractPriceRange($normalizedPrompt);
$sizes = $this->extractSizes($normalizedPrompt);
$brand = $this->extractBrand($normalizedPrompt);
$searchText = $this->buildSearchText(
$normalized,
$normalizedPrompt,
$sizes,
$brand,
$priceMin,
$priceMax
);
if ($historyContext !== '' && $this->shouldUseHistoryContext($normalizedPrompt)) {
$latestHistoryQuestion = $this->extractLatestQuestionFromHistory($historyContext);
if ($latestHistoryQuestion !== '') {
$normalizedHistoryPrompt = $this->normalize($latestHistoryQuestion);
[$historyPriceMin, $historyPriceMax] = $this->extractPriceRange($normalizedHistoryPrompt);
$historySizes = $this->extractSizes($normalizedHistoryPrompt);
$historyBrand = $this->extractBrand($normalizedHistoryPrompt);
$historySearchText = $this->buildSearchText(
$normalizedHistoryPrompt,
$historySizes,
$historyBrand,
$historyPriceMin,
$historyPriceMax
);
$searchText = $this->mergeSearchTexts($historySearchText, $searchText);
if (($brand === null || $brand === '') && $historyBrand !== null && $historyBrand !== '') {
$brand = $historyBrand;
}
}
}
$finalSearchText = $searchText !== '' ? $searchText : $normalizedPrompt;
return new CommerceSearchQuery(
originalPrompt: $originalPrompt,
normalizedPrompt: $normalized,
searchText: $searchText !== '' ? $searchText : $normalized,
normalizedPrompt: $normalizedPrompt,
searchText: $finalSearchText,
brand: $brand,
sizes: $sizes,
properties: $properties,
properties: [],
priceMin: $priceMin,
priceMax: $priceMax,
intent: $intent,
@@ -138,9 +170,7 @@ final readonly class CommerceQueryParser
{
$text = ' ' . $prompt . ' ';
$phrasesToRemove = $this->config->getPhrasesToRemove();
foreach ($phrasesToRemove as $phrase) {
foreach ($this->config->getPhrasesToRemove() as $phrase) {
$text = str_replace($phrase, ' ', $text);
}
@@ -155,16 +185,83 @@ final readonly class CommerceQueryParser
if ($priceMin !== null || $priceMax !== null) {
$text = preg_replace('/\bzwischen\s+\d+(?:[.,]\d+)?\s+und\s+\d+(?:[.,]\d+)?\s*euro\b/u', ' ', $text) ?? $text;
$text = preg_replace('/\b(?:unter|bis|max(?:imal)?|ab|mindestens|min)\s+\d+(?:[.,]\d+)?\s*euro\b/u', ' ', $text) ?? $text;
$text = preg_replace('/\b'.$this->intentConfig->getPricePattern().'\b/u', ' ', $text) ?? $text;
$text = preg_replace('/\b' . $this->intentConfig->getPricePattern() . '\b/u', ' ', $text) ?? $text;
}
$text = preg_replace('/\s+/u', ' ', $text) ?? $text;
$text = trim($text, " \t\n\r\0\x0B-.,");
$tokens = array_filter(explode(' ', $text), static fn(string $token): bool => mb_strlen($token) > 1);
$tokens = array_filter(
explode(' ', $text),
static fn(string $token): bool => mb_strlen($token) > 1
);
$tokens = $this->filterSearchTokens($tokens);
return trim(implode(' ', $tokens));
}
private function shouldUseHistoryContext(string $prompt): bool
{
return preg_match(
'/\b(' . $this->config->getHistoryContextPattern() . ')\b/u',
$prompt
) === 1;
}
private function extractLatestQuestionFromHistory(string $historyContext): string
{
if (preg_match_all('/^Question:\s*(.+)$/m', $historyContext, $matches) !== 1 && preg_match_all('/^Question:\s*(.+)$/m', $historyContext, $matches) === false) {
return '';
}
$questions = $matches[1] ?? [];
if ($questions === []) {
return '';
}
$lastQuestion = end($questions);
return is_string($lastQuestion) ? trim($lastQuestion) : '';
}
private function mergeSearchTexts(string $historySearchText, string $currentSearchText): string
{
$tokens = [];
foreach ([$historySearchText, $currentSearchText] as $text) {
if ($text === '') {
continue;
}
foreach (explode(' ', $text) as $token) {
$token = trim($token);
if ($token === '' || mb_strlen($token) <= 1) {
continue;
}
$tokens[$token] = $token;
}
}
return implode(' ', array_values($tokens));
}
/**
* @param string[] $tokens
* @return string[]
*/
private function filterSearchTokens(array $tokens): array
{
$stopWords = $this->config->getFilterSearchTokensPattern();
return array_values(array_filter(
$tokens,
static fn(string $token): bool => !in_array($token, $stopWords, true)
));
}
private function toFloat(string $value): ?float
{
$value = str_replace(',', '.', trim($value));