optimize system and cleanup
This commit is contained in:
@@ -11,9 +11,7 @@ use App\Tag\TagTypes;
|
||||
/**
|
||||
* CatalogIntentLite
|
||||
*
|
||||
* Reiner Entity-Detector.
|
||||
*
|
||||
* Verantwortlich nur für:
|
||||
* Verantwortlich ausschließlich für:
|
||||
* - Vector-Tag-Erkennung
|
||||
* - Score-Gate
|
||||
* - Ambiguity-Check
|
||||
@@ -26,27 +24,14 @@ use App\Tag\TagTypes;
|
||||
*/
|
||||
final class CatalogIntentLite
|
||||
{
|
||||
/**
|
||||
* Minimaler Similarity-Score.
|
||||
* Verhindert Rauschen.
|
||||
*/
|
||||
private const MIN_SCORE = 0.72;
|
||||
|
||||
/**
|
||||
* Differenz zwischen Top1 und Top2,
|
||||
* damit kein unsicherer Treffer akzeptiert wird.
|
||||
*/
|
||||
private const AMBIGUITY_DELTA = 0.03;
|
||||
|
||||
public function __construct(
|
||||
private readonly TagVectorSearchClient $tagVectorClient,
|
||||
private readonly QueryCleaner $queryCleaner,
|
||||
private readonly QueryCleaner $queryCleaner,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Gibt das canonical Label der erkannten catalog_entity zurück
|
||||
* oder null, wenn kein sauberer Treffer.
|
||||
*/
|
||||
public function detect(string $prompt): ?string
|
||||
{
|
||||
$prompt = trim($prompt);
|
||||
@@ -54,10 +39,82 @@ final class CatalogIntentLite
|
||||
return null;
|
||||
}
|
||||
|
||||
$promptTag = $this->queryCleaner->clean($prompt);
|
||||
$clean = $this->queryCleaner->clean($prompt);
|
||||
if ($clean === '') {
|
||||
$clean = $prompt;
|
||||
}
|
||||
|
||||
// 1) Tag-Vector-Suche
|
||||
$hits = $this->tagVectorClient->search($promptTag, 3);
|
||||
// ----------------------------------------------------
|
||||
// 1️⃣ Primär: Vollquery testen
|
||||
// ----------------------------------------------------
|
||||
|
||||
$label = $this->detectFromQuery($clean);
|
||||
if ($label !== null) {
|
||||
return $label;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
// 2️⃣ Fallback: Tokenweise testen
|
||||
// (wichtig für "geräteliste testomat")
|
||||
// ----------------------------------------------------
|
||||
|
||||
$tokens = $this->tokenize($clean);
|
||||
|
||||
$bestLabel = null;
|
||||
$bestScore = 0.0;
|
||||
|
||||
foreach ($tokens as $token) {
|
||||
|
||||
// sehr kurze Tokens ignorieren (Noise)
|
||||
if (mb_strlen($token) < 3) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$hits = $this->tagVectorClient->search($token, 3);
|
||||
|
||||
if ($hits === []) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$top = $hits[0] ?? null;
|
||||
if (!is_array($top)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$score = (float)($top['score'] ?? 0.0);
|
||||
|
||||
if ($score < self::MIN_SCORE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ambiguity-Check
|
||||
if (isset($hits[1])) {
|
||||
$secondScore = (float)($hits[1]['score'] ?? 0.0);
|
||||
if (abs($score - $secondScore) < self::AMBIGUITY_DELTA) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (($top['tag_type'] ?? null) !== TagTypes::CATALOG_ENTITY) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($score > $bestScore) {
|
||||
$bestScore = $score;
|
||||
$bestLabel = trim((string)($top['label'] ?? ''));
|
||||
}
|
||||
}
|
||||
|
||||
if ($bestLabel === null || $bestLabel === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return mb_strtolower($bestLabel);
|
||||
}
|
||||
|
||||
private function detectFromQuery(string $query): ?string
|
||||
{
|
||||
$hits = $this->tagVectorClient->search($query, 3);
|
||||
|
||||
if ($hits === []) {
|
||||
return null;
|
||||
@@ -66,26 +123,21 @@ final class CatalogIntentLite
|
||||
$best = $hits[0];
|
||||
$bestScore = (float)($best['score'] ?? 0.0);
|
||||
|
||||
// 2) Score-Tags
|
||||
if ($bestScore < self::MIN_SCORE) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 3) Ambiguity-Check
|
||||
if (isset($hits[1])) {
|
||||
$secondScore = (float)($hits[1]['score'] ?? 0.0);
|
||||
|
||||
if (abs($bestScore - $secondScore) < self::AMBIGUITY_DELTA) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 4) Nur catalog_entity zulassen
|
||||
if (($best['tag_type'] ?? null) !== TagTypes::CATALOG_ENTITY) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 5) Canonical Label
|
||||
$label = trim((string)($best['label'] ?? ''));
|
||||
|
||||
if ($label === '') {
|
||||
@@ -94,4 +146,31 @@ final class CatalogIntentLite
|
||||
|
||||
return mb_strtolower($label);
|
||||
}
|
||||
|
||||
private function tokenize(string $text): array
|
||||
{
|
||||
$parts = preg_split('/\s+/u', trim($text));
|
||||
if (!$parts) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$seen = [];
|
||||
$out = [];
|
||||
|
||||
foreach ($parts as $p) {
|
||||
$p = trim($p);
|
||||
if ($p === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($seen[$p])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$seen[$p] = true;
|
||||
$out[] = $p;
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user