fix p46
This commit is contained in:
@@ -218,6 +218,8 @@ parameters:
|
||||
intro: 'Aus den Shopdaten ergeben sich folgende passende Treffer:'
|
||||
no_results: 'Ich finde in den Shopdaten keine passenden Treffer für die angefragte Produktsuche. Ich liste deshalb keine fachfremden Ersatzprodukte auf.'
|
||||
sorted_by_length_note: 'Sortierung: aufsteigend nach erkannter Kabellänge.'
|
||||
min_length_filter_note: 'Filter: nur Treffer ab {value} m.'
|
||||
max_length_filter_note: 'Filter: nur Treffer bis {value} m.'
|
||||
|
||||
no_llm_fallback:
|
||||
max_shop_results: 5
|
||||
@@ -507,6 +509,17 @@ parameters:
|
||||
value_patterns:
|
||||
- '/(?P<value>\d+(?:[,.]\d+)?)\s*(?:m|meter|metern)\b/iu'
|
||||
|
||||
length_filter:
|
||||
enabled: true
|
||||
min_patterns:
|
||||
- '/\b(?:ab|mindestens|minimum|min\.?|>=|größer\s+gleich|groesser\s+gleich)\s*(?P<value>\d+(?:[,.]\d+)?)\s*(?:m|meter|metern)\b/iu'
|
||||
- '/\b(?:länger|laenger|größer|groesser|mehr)\s+(?:als\s+)?(?P<value>\d+(?:[,.]\d+)?)\s*(?:m|meter|metern)\b/iu'
|
||||
- '/(?P<value>\d+(?:[,.]\d+)?)\s*(?:m|meter|metern)\s*(?:oder\s+)?(?:länger|laenger|mehr)\b/iu'
|
||||
max_patterns:
|
||||
- '/\b(?:bis|maximal|maximum|max\.?|höchstens|hoechstens|<=|kleiner\s+gleich)\s*(?P<value>\d+(?:[,.]\d+)?)\s*(?:m|meter|metern)\b/iu'
|
||||
- '/\b(?:kürzer|kuerzer|kleiner|weniger)\s+(?:als\s+)?(?P<value>\d+(?:[,.]\d+)?)\s*(?:m|meter|metern)\b/iu'
|
||||
- '/(?P<value>\d+(?:[,.]\d+)?)\s*(?:m|meter|metern)\s*(?:oder\s+)?(?:kürzer|kuerzer|weniger)\b/iu'
|
||||
|
||||
context_usage:
|
||||
referential_terms:
|
||||
- der
|
||||
|
||||
@@ -3006,6 +3006,15 @@ final readonly class AgentRunner
|
||||
return $shopResults;
|
||||
}
|
||||
|
||||
return $this->sortDecoratedShopProductsByLength($decorated);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, array{index: int, length: float|null, product: mixed}> $decorated
|
||||
* @return ShopProductResult[]
|
||||
*/
|
||||
private function sortDecoratedShopProductsByLength(array $decorated): array
|
||||
{
|
||||
usort($decorated, static function (array $a, array $b): int {
|
||||
if ($a['length'] === null && $b['length'] === null) {
|
||||
return $a['index'] <=> $b['index'];
|
||||
@@ -3041,6 +3050,112 @@ final readonly class AgentRunner
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{type: string, value: float}|null
|
||||
*/
|
||||
private function resolveShopResultLengthFilter(string $prompt, string $shopSearchQuery): ?array
|
||||
{
|
||||
if (!$this->agentRunnerConfig->isShopResultLengthFilterEnabled()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$text = trim($prompt . ' ' . $shopSearchQuery);
|
||||
if ($text === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach ($this->agentRunnerConfig->getShopResultMinLengthFilterPatterns() as $pattern) {
|
||||
$value = $this->extractShopLengthFilterPatternValue($pattern, $text);
|
||||
if ($value !== null) {
|
||||
return ['type' => 'min', 'value' => $value];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->agentRunnerConfig->getShopResultMaxLengthFilterPatterns() as $pattern) {
|
||||
$value = $this->extractShopLengthFilterPatternValue($pattern, $text);
|
||||
if ($value !== null) {
|
||||
return ['type' => 'max', 'value' => $value];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function extractShopLengthFilterPatternValue(string $pattern, string $text): ?float
|
||||
{
|
||||
if (@preg_match($pattern, $text, $matches) !== 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$value = $matches['value'] ?? ($matches[1] ?? null);
|
||||
if (!is_scalar($value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$normalized = str_replace(',', '.', (string) $value);
|
||||
if (!is_numeric($normalized)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (float) $normalized;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ShopProductResult[] $shopResults
|
||||
* @param array{type: string, value: float} $lengthFilter
|
||||
* @return ShopProductResult[]
|
||||
*/
|
||||
private function filterDirectShopAnswerResultsByLength(array $shopResults, array $lengthFilter): array
|
||||
{
|
||||
$decorated = [];
|
||||
|
||||
foreach (array_values($shopResults) as $index => $product) {
|
||||
if (!$product instanceof ShopProductResult) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$length = $this->extractShopProductLengthMeters($product);
|
||||
if ($length === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (($lengthFilter['type'] === 'min' && $length < $lengthFilter['value'])
|
||||
|| ($lengthFilter['type'] === 'max' && $length > $lengthFilter['value'])
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$decorated[] = [
|
||||
'index' => $index,
|
||||
'length' => $length,
|
||||
'product' => $product,
|
||||
];
|
||||
}
|
||||
|
||||
return $this->sortDecoratedShopProductsByLength($decorated);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array{type: string, value: float} $lengthFilter
|
||||
*/
|
||||
private function buildDirectShopAnswerLengthFilterNote(array $lengthFilter): string
|
||||
{
|
||||
$template = $lengthFilter['type'] === 'max'
|
||||
? $this->agentRunnerConfig->getDirectShopResultAnswerMaxLengthFilterNote()
|
||||
: $this->agentRunnerConfig->getDirectShopResultAnswerMinLengthFilterNote();
|
||||
|
||||
return str_replace('{value}', $this->formatShopLengthFilterValue($lengthFilter['value']), $template);
|
||||
}
|
||||
|
||||
private function formatShopLengthFilterValue(float $value): string
|
||||
{
|
||||
if (abs($value - round($value)) < 0.00001) {
|
||||
return (string) (int) round($value);
|
||||
}
|
||||
|
||||
return rtrim(rtrim(str_replace('.', ',', sprintf('%.2F', $value)), '0'), ',');
|
||||
}
|
||||
|
||||
private function extractShopProductLengthMeters(ShopProductResult $product): ?float
|
||||
{
|
||||
$text = trim(implode(' ', array_filter([
|
||||
@@ -3153,13 +3268,26 @@ final readonly class AgentRunner
|
||||
return '';
|
||||
}
|
||||
|
||||
if ($shopResults === []) {
|
||||
$answerShopResults = $shopResults;
|
||||
$lengthFilter = $this->resolveShopResultLengthFilter($prompt, $shopSearchQuery);
|
||||
if ($lengthFilter !== null) {
|
||||
$answerShopResults = $this->filterDirectShopAnswerResultsByLength($answerShopResults, $lengthFilter);
|
||||
}
|
||||
|
||||
if ($answerShopResults === []) {
|
||||
return $this->agentRunnerConfig->getDirectShopResultAnswerNoResultsMessage();
|
||||
}
|
||||
|
||||
$lines = [$this->agentRunnerConfig->getDirectShopResultAnswerIntro()];
|
||||
|
||||
if ($this->isShopResultLengthSortRequested($prompt . ' ' . $shopSearchQuery)) {
|
||||
if ($lengthFilter !== null) {
|
||||
$note = trim($this->buildDirectShopAnswerLengthFilterNote($lengthFilter));
|
||||
if ($note !== '') {
|
||||
$lines[] = $note;
|
||||
}
|
||||
}
|
||||
|
||||
if ($lengthFilter !== null || $this->isShopResultLengthSortRequested($prompt . ' ' . $shopSearchQuery)) {
|
||||
$note = trim($this->agentRunnerConfig->getDirectShopResultAnswerSortedByLengthNote());
|
||||
if ($note !== '') {
|
||||
$lines[] = $note;
|
||||
@@ -3167,7 +3295,7 @@ final readonly class AgentRunner
|
||||
}
|
||||
|
||||
$lines[] = '';
|
||||
foreach ($this->buildDirectShopProductLines($shopResults, 'accessory_or_consumable') as $line) {
|
||||
foreach ($this->buildDirectShopProductLines($answerShopResults, 'accessory_or_consumable') as $line) {
|
||||
$lines[] = $line;
|
||||
}
|
||||
|
||||
|
||||
@@ -757,6 +757,16 @@ final class AgentRunnerConfig
|
||||
return $this->getRequiredString('direct_shop_result_answer.sorted_by_length_note');
|
||||
}
|
||||
|
||||
public function getDirectShopResultAnswerMinLengthFilterNote(): string
|
||||
{
|
||||
return $this->getRequiredString('direct_shop_result_answer.min_length_filter_note');
|
||||
}
|
||||
|
||||
public function getDirectShopResultAnswerMaxLengthFilterNote(): string
|
||||
{
|
||||
return $this->getRequiredString('direct_shop_result_answer.max_length_filter_note');
|
||||
}
|
||||
|
||||
public function getNoLlmFallbackMaxShopResults(): int
|
||||
{
|
||||
return $this->getRequiredInt('no_llm_fallback.max_shop_results');
|
||||
@@ -1151,6 +1161,27 @@ final class AgentRunnerConfig
|
||||
return $this->getRequiredStringList('shop_prompt.length_sort.value_patterns');
|
||||
}
|
||||
|
||||
public function isShopResultLengthFilterEnabled(): bool
|
||||
{
|
||||
return $this->getRequiredBool('shop_prompt.length_filter.enabled');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getShopResultMinLengthFilterPatterns(): array
|
||||
{
|
||||
return $this->getRequiredStringList('shop_prompt.length_filter.min_patterns');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getShopResultMaxLengthFilterPatterns(): array
|
||||
{
|
||||
return $this->getRequiredStringList('shop_prompt.length_filter.max_patterns');
|
||||
}
|
||||
|
||||
public function getShopPromptIntro(): string
|
||||
{
|
||||
return $this->getRequiredString('shop_prompt.intro');
|
||||
|
||||
Reference in New Issue
Block a user