# RetrieX Patch 20 - LLM-assisted Input Normalization before Routing

## Ziel

Patch 20 ersetzt den p19-Symptom-Fix fuer einzelne Preis-Tippfehler (`kpstet`, `ksotet`) durch eine generische, LLM-gestuetzte Eingabe-Normalisierung vor der Intent-/Commerce-/Retrieval-Erkennung.

Der Ausloeser war der Flow:

1. `Was ist der niedrigste Grenzwert fuer die Wasserhaerte, welcher mit einem Testomaten ueberwacht werden kann?`
2. `mit welchem indikator wird der wert gemessen`
3. `was kpstet der indikator`

p19 konnte diesen konkreten Tippfehler per YAML-Liste auffangen, skaliert aber nicht. p20 normalisiert die Nutzereingabe vorher, z. B. `was kpstet der indikator` -> `was kostet der indikator`, ohne Produktkontext fachlich aufzufuellen.

## Architektur

Der Normalisierungsschritt sitzt in `AgentRunner::run()` direkt nach `analyze_request` und vor:

- URL-/Quellenpruefung
- CommerceIntentLite-Erkennung
- Knowledge-Retrieval-Prompt-Bau
- Shop-Query-Optimierung
- finalem PromptBuilder-Aufruf

Das LLM ist ueber den bereits vorhandenen `final class OllamaClient` angebunden und nutzt analog zur Shop-Query-Optimierung:

```php
foreach ($this->ollamaClient->stream($normalizationPrompt) as $token) {
    ...
}
```

Die Originalfrage bleibt erhalten und wird weiterhin in die Conversation History geschrieben. Die normalisierte Frage wird nur als effektive Eingabe fuer Routing, Intent, Retrieval, Shop-Optimierung und Antwortgenerierung genutzt.

## Guardrails

Die Normalisierung darf nur offensichtliche Tippfehler korrigieren. Sie darf nicht fachlich interpretieren.

Konkrete Schutzmechanismen:

- YAML-konfigurierbarer Normalizer-Prompt in `config/retriex/agent.yaml`
- keine fachliche Kontextauflösung im Normalizer
- keine Produktnamen, Modellnummern, Messwerte, Artikelnummern oder Einsatzbereiche hinzufuegen
- vage Referenzen wie `der indikator` bleiben vage und werden erst spaeter ueber bestehende Kontextlogik aufgeloest
- URL-/Code-aehnliche Eingaben werden uebersprungen
- maximale Eingabe-/Ausgabelaenge
- maximale Laengenvergroesserung
- maximale Token-Zunahme
- neue Zahlen in der normalisierten Eingabe werden verworfen
- bei Fehlern, leerer Ausgabe oder unsicherem Ergebnis faellt RetrieX auf die Originalfrage zurueck

## Geaenderte Dateien

- `src/Agent/AgentRunner.php`
- `src/Config/AgentRunnerConfig.php`
- `src/Config/RetriexEffectiveConfigProvider.php`
- `config/retriex/agent.yaml`
- `config/retriex/intent.yaml`
- `config/retriex/commerce.yaml`

## Entfernt aus p19-Symptomlisten

Die expliziten Tippfehler `kpstet` und `ksotet` wurden entfernt aus:

- `intent.yaml` strong/non-product/price/explicit-commerce Listen
- `commerce.yaml` stopword- und correction-Listen

Damit ist p20 nicht mehr auf diese konkreten Fehlerlisten angewiesen.

## Erwartetes Verhalten

Eingabe:

```text
was kpstet der indikator
```

Interne Normalisierung:

```text
was kostet der indikator
```

Danach sollte der bestehende Commerce-/Shop-Follow-up-Flow greifen:

- Commerce Intent wird erkannt
- Shop-Suche wird angefragt
- referenzieller Kontext `Indikatortyp 300` kann durch bestehende Shop-Query-Context-Anchor-Logik ergaenzt werden

## Lokal ausgefuehrte Pruefungen

Im Container ausgefuehrt:

```bash
php -l src/Agent/AgentRunner.php
php -l src/Config/AgentRunnerConfig.php
php -l src/Config/RetriexEffectiveConfigProvider.php
python3 - <<'PY'
import yaml
from pathlib import Path
for rel in ['config/retriex/agent.yaml','config/retriex/intent.yaml','config/retriex/commerce.yaml']:
    with (Path('.') / rel).open() as f:
        yaml.safe_load(f)
PY
php -r '$patterns=["/^(?:normalisiert|korrigiert|corrected|normalized)\\s*:\\s*/iu","/https?:\\/\\//iu","/\\bwww\\./iu","/```/u"]; foreach($patterns as $p){ if(@preg_match($p, "was kpstet der indikator")===false){ exit(1); } } echo "OK\n";'
grep -R "kpstet\|ksotet" -n config src || true
```

Ergebnis:

- PHP-Syntax: OK
- YAML-Parse: OK
- Regex-Smoke-Test: OK
- `kpstet` / `ksotet`: nicht mehr in `config` oder `src`

## Nicht lokal ausfuehrbar

Die Symfony-/Composer-basierten Pflichtchecks konnten im Container nicht ausgefuehrt werden, weil im ZIP keine installierten Vendor-Dependencies enthalten sind.

Bitte nach dem Einspielen ausfuehren:

```bash
bin/console mto:agent:config:validate
bin/console mto:agent:regression:test
bin/console mto:agent:config:audit-source --details
bin/console mto:agent:config:audit-patterns --details
```

## Empfohlene Regressionstests

1. Stabiler v1.5.1-Flow:

```text
Was ist der niedrigste Grenzwert fuer die Wasserhaerte, welcher mit einem Testomaten ueberwacht werden kann?
mit welchem indikator wird der wert gemessen
was kostet der indikator
```

2. Tippfehler-Flow:

```text
Was ist der niedrigste Grenzwert fuer die Wasserhaerte, welcher mit einem Testomaten ueberwacht werden kann?
mit welchem indikator wird der wert gemessen
was kpstet der indikator
```

Erwartung fuer beide Preisfragen:

- Shop-Suche wird angefragt
- Shop-Treffer werden genutzt
- keine Rueckkehr in RAG-only mit Testomat-2000-Indikatoren

3. Guardrail-Test:

```text
was kpstet der indikator 300
```

Erwartung:

- Normalisierung darf `300` erhalten
- keine neue Modellnummer / Artikelnummer hinzufuegen

4. URL-Skip-Test:

```text
pruefe https://example.com/test?x=kpstet
```

Erwartung:

- Normalisierung wird uebersprungen
- URL bleibt unveraendert
