fix 4 optimize systemMsg and model default config values
This commit is contained in:
@@ -205,13 +205,8 @@ final readonly class CommerceQueryParser
|
||||
) ?? $text;
|
||||
}
|
||||
|
||||
if ($brand !== null && $brand !== '' && !$this->isBrandPartOfModelPhrase($prompt, $brand)) {
|
||||
$text = preg_replace(
|
||||
$this->config->buildExactTokenRemovalPattern($brand),
|
||||
' ',
|
||||
$text
|
||||
) ?? $text;
|
||||
}
|
||||
// Keep known brand terms in the shop search text because the Store API
|
||||
// request does not add a separate manufacturer filter.
|
||||
|
||||
if ($priceMin !== null || $priceMax !== null) {
|
||||
foreach ($this->config->getPriceRemovalPatterns($this->intentConfig) as $pattern) {
|
||||
|
||||
@@ -188,6 +188,14 @@ final readonly class SearchRepairService
|
||||
|
||||
$modelCandidates = $this->extractModelCandidates($combinedText);
|
||||
$accessoryCandidates = $this->extractAccessoryCandidates($combinedText);
|
||||
$requestedAccessoryCodes = $this->extractRequestedAccessoryCodes($prompt . "\n" . $primaryQuery);
|
||||
|
||||
if ($requestedAccessoryCodes !== []) {
|
||||
$accessoryCandidates = $this->filterAccessoryCandidatesByRequestedCodes(
|
||||
accessoryCandidates: $accessoryCandidates,
|
||||
requestedCodes: $requestedAccessoryCodes
|
||||
);
|
||||
}
|
||||
|
||||
$topPrimaryName = $primaryShopResults[0]->name ?? '';
|
||||
$topPrimaryProductNumber = $primaryShopResults[0]->productNumber ?? null;
|
||||
@@ -195,6 +203,18 @@ final readonly class SearchRepairService
|
||||
|
||||
$queries = [];
|
||||
|
||||
$queries = array_merge(
|
||||
$queries,
|
||||
$this->buildFocusedModelAccessoryQueries(
|
||||
prompt: $prompt,
|
||||
primaryQuery: $primaryQuery,
|
||||
knowledgeText: $knowledgeText,
|
||||
modelCandidates: $modelCandidates,
|
||||
accessoryCandidates: $accessoryCandidates,
|
||||
requestedAccessoryCodes: $requestedAccessoryCodes
|
||||
)
|
||||
);
|
||||
|
||||
if ($topPrimaryPhrase !== '' && $this->containsModelLikePhrase($topPrimaryPhrase)) {
|
||||
$queries[] = $topPrimaryPhrase;
|
||||
} elseif ($topPrimaryName !== '' && $this->containsModelLikePhrase($topPrimaryName)) {
|
||||
@@ -293,6 +313,7 @@ final readonly class SearchRepairService
|
||||
|
||||
foreach ($matches[1] ?? [] as $candidate) {
|
||||
$candidate = $this->sanitizeQuery($candidate);
|
||||
$candidate = $this->reduceToSpecificModelCandidate($candidate);
|
||||
|
||||
if ($candidate === '') {
|
||||
continue;
|
||||
@@ -377,6 +398,245 @@ final readonly class SearchRepairService
|
||||
return $score;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $modelCandidates
|
||||
* @param string[] $accessoryCandidates
|
||||
* @param string[] $requestedAccessoryCodes
|
||||
* @return string[]
|
||||
*/
|
||||
private function buildFocusedModelAccessoryQueries(
|
||||
string $prompt,
|
||||
string $primaryQuery,
|
||||
string $knowledgeText,
|
||||
array $modelCandidates,
|
||||
array $accessoryCandidates,
|
||||
array $requestedAccessoryCodes
|
||||
): array {
|
||||
if ($requestedAccessoryCodes === []) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$queries = [];
|
||||
$models = $this->filterModelCandidatesByRequestedAccessoryCodes(
|
||||
prompt: $prompt . "\n" . $primaryQuery,
|
||||
knowledgeText: $knowledgeText,
|
||||
modelCandidates: $modelCandidates,
|
||||
requestedCodes: $requestedAccessoryCodes
|
||||
);
|
||||
|
||||
if ($models === []) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$accessories = $accessoryCandidates;
|
||||
if ($accessories === []) {
|
||||
foreach ($requestedAccessoryCodes as $code) {
|
||||
$accessories[] = 'Indikator ' . $code;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($models as $model) {
|
||||
foreach ($accessories as $accessory) {
|
||||
if (!$this->candidateMatchesRequestedAccessoryCodes($accessory, $requestedAccessoryCodes)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$queries[] = trim($model . ' ' . $accessory);
|
||||
}
|
||||
}
|
||||
|
||||
return array_values(array_unique(array_filter(
|
||||
array_map(fn(string $query): string => $this->sanitizeQuery($query), $queries),
|
||||
static fn(string $query): bool => $query !== ''
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
private function extractRequestedAccessoryCodes(string $text): array
|
||||
{
|
||||
$codes = [];
|
||||
|
||||
if (preg_match_all('/\b(?:indikator|indicator|reagenz|reagent)\s*([A-Za-z]{0,3}\s*\d{1,5}[A-Za-z0-9\-]*)\b/iu', $text, $matches) !== false) {
|
||||
foreach ($matches[1] ?? [] as $code) {
|
||||
$normalized = $this->normalizeAccessoryCode((string) $code);
|
||||
if ($normalized !== '') {
|
||||
$codes[$normalized] = $normalized;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array_values($codes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $accessoryCandidates
|
||||
* @param string[] $requestedCodes
|
||||
* @return string[]
|
||||
*/
|
||||
private function filterAccessoryCandidatesByRequestedCodes(array $accessoryCandidates, array $requestedCodes): array
|
||||
{
|
||||
return array_values(array_filter(
|
||||
$accessoryCandidates,
|
||||
fn(string $candidate): bool => $this->candidateMatchesRequestedAccessoryCodes($candidate, $requestedCodes)
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $modelCandidates
|
||||
* @param string[] $requestedCodes
|
||||
* @return string[]
|
||||
*/
|
||||
private function filterModelCandidatesByRequestedAccessoryCodes(
|
||||
string $prompt,
|
||||
string $knowledgeText,
|
||||
array $modelCandidates,
|
||||
array $requestedCodes
|
||||
): array {
|
||||
$models = [];
|
||||
$normalizedPrompt = $this->normalizeForRepairMatching($prompt);
|
||||
|
||||
foreach ($modelCandidates as $candidate) {
|
||||
$candidate = $this->reduceToSpecificModelCandidate($candidate);
|
||||
if ($candidate === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$normalizedCandidate = $this->normalizeForRepairMatching($candidate);
|
||||
$isPromptAnchored = $normalizedCandidate !== '' && str_contains($normalizedPrompt, $normalizedCandidate);
|
||||
|
||||
foreach ($requestedCodes as $code) {
|
||||
if ($isPromptAnchored || $this->modelAppearsNearAccessoryCode($knowledgeText, $candidate, $code)) {
|
||||
$models[$candidate] = $candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array_values($models);
|
||||
}
|
||||
|
||||
private function candidateMatchesRequestedAccessoryCodes(string $candidate, array $requestedCodes): bool
|
||||
{
|
||||
$normalizedCandidate = $this->normalizeForRepairMatching($candidate);
|
||||
$compactCandidate = preg_replace('/\s+/u', '', $normalizedCandidate) ?? $normalizedCandidate;
|
||||
|
||||
foreach ($requestedCodes as $code) {
|
||||
$normalizedCode = $this->normalizeAccessoryCode($code);
|
||||
|
||||
if ($normalizedCode === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$pattern = '/\b' . preg_quote($normalizedCode, '/') . '\b/u';
|
||||
if (preg_match($pattern, $normalizedCandidate) === 1 || preg_match($pattern, $compactCandidate) === 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function modelAppearsNearAccessoryCode(string $knowledgeText, string $model, string $code): bool
|
||||
{
|
||||
$normalizedText = $this->normalizeForRepairMatching($knowledgeText);
|
||||
$normalizedModel = $this->normalizeForRepairMatching($model);
|
||||
$normalizedCode = $this->normalizeAccessoryCode($code);
|
||||
|
||||
if ($normalizedText === '' || $normalizedModel === '' || $normalizedCode === '') {
|
||||
return false;
|
||||
}
|
||||
|
||||
$modelPositions = $this->findNeedlePositions($normalizedText, $normalizedModel);
|
||||
if ($modelPositions === []) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$codeNeedles = [
|
||||
'indikator ' . $normalizedCode,
|
||||
'indicator ' . $normalizedCode,
|
||||
'indikatortyp ' . $normalizedCode,
|
||||
$normalizedCode,
|
||||
];
|
||||
|
||||
foreach ($codeNeedles as $needle) {
|
||||
foreach ($this->findNeedlePositions($normalizedText, $needle) as $codePos) {
|
||||
foreach ($modelPositions as $modelPos) {
|
||||
if (abs($codePos - $modelPos) <= 1600) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
*/
|
||||
private function findNeedlePositions(string $haystack, string $needle): array
|
||||
{
|
||||
if ($haystack === '' || $needle === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
$positions = [];
|
||||
$offset = 0;
|
||||
|
||||
while (($position = mb_strpos($haystack, $needle, $offset, 'UTF-8')) !== false) {
|
||||
$positions[] = $position;
|
||||
$offset = $position + max(1, strlen($needle));
|
||||
}
|
||||
|
||||
return $positions;
|
||||
}
|
||||
|
||||
private function reduceToSpecificModelCandidate(string $candidate): string
|
||||
{
|
||||
$candidate = $this->sanitizeQuery($candidate);
|
||||
|
||||
if ($candidate === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$patterns = [
|
||||
'/\b(Testomat(?:®)?\s+(?:\d{3,4}|EVO(?:\s+[A-ZÄÖÜ]{1,8})?|ECO(?:[-\s]?(?:PLUS|C))?|DUO(?:\s+\d{3,4})?|LAB(?:\s+[A-ZÄÖÜ]{1,8})?))\b/iu',
|
||||
'/\b(Horiba\s+LAQUA\s+[A-Z0-9\-]+)\b/iu',
|
||||
];
|
||||
|
||||
foreach ($patterns as $pattern) {
|
||||
if (preg_match($pattern, $candidate, $matches) === 1) {
|
||||
return $this->sanitizeQuery((string) ($matches[1] ?? ''));
|
||||
}
|
||||
}
|
||||
|
||||
if (preg_match('/\b(?:indikator|indicator|reagenz|reagent|verfuegbarkeit|verfügbarkeit|shop)\b/iu', $candidate) === 1) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $candidate;
|
||||
}
|
||||
|
||||
private function normalizeAccessoryCode(string $code): string
|
||||
{
|
||||
$code = $this->normalizeForRepairMatching($code);
|
||||
$code = preg_replace('/\s+/u', '', $code) ?? $code;
|
||||
|
||||
return trim($code);
|
||||
}
|
||||
|
||||
private function normalizeForRepairMatching(string $value): string
|
||||
{
|
||||
$value = mb_strtolower(trim($value), 'UTF-8');
|
||||
$value = str_replace('®', '', $value);
|
||||
$value = preg_replace('/[^\p{L}\p{N}]+/u', ' ', $value) ?? $value;
|
||||
$value = preg_replace($this->config->getWhitespaceCollapsePattern(), ' ', $value) ?? $value;
|
||||
|
||||
return trim($value);
|
||||
}
|
||||
|
||||
private function asksForBundleOrAccessory(string $prompt): bool
|
||||
{
|
||||
return preg_match($this->config->getAccessoryOrBundlePattern(), $prompt) === 1;
|
||||
|
||||
Reference in New Issue
Block a user