731 lines
12 KiB
Markdown
731 lines
12 KiB
Markdown
# RetrieX How-to: Neue Eval-Cases korrekt erstellen
|
|
|
|
Dieses How-to beschreibt, wie neue Regressionstests für die RetrieX Eval-Suite über den Admin-Bereich angelegt werden.
|
|
|
|
Ziel ist, neue rote oder fachlich wichtige Fälle dauerhaft abzusichern, ohne direkt Core-Logik, Retrieval-Regeln oder Shopquery-Heuristiken zu verändern.
|
|
|
|
## Einstieg
|
|
|
|
Admin-Pfad:
|
|
|
|
```text
|
|
/admin/evals/
|
|
```
|
|
|
|
Im Bereich **„Eval-Case erstellen“** können neue Cases für folgende Typen angelegt werden:
|
|
|
|
```text
|
|
retrieval
|
|
shop_query
|
|
followup
|
|
answer_guard
|
|
```
|
|
|
|
Nach dem Speichern wird der Case in die passende Datei geschrieben:
|
|
|
|
```text
|
|
tests/evals/cases/retrieval.ndjson
|
|
tests/evals/cases/shop_query.ndjson
|
|
tests/evals/cases/followup.ndjson
|
|
tests/evals/cases/answer_guard.ndjson
|
|
```
|
|
|
|
---
|
|
|
|
## Grundregel
|
|
|
|
Ein guter Eval-Case prüft genau **einen klaren Sachverhalt**.
|
|
|
|
Gut:
|
|
|
|
```json
|
|
{
|
|
"expected_query": "testomat 808",
|
|
"must_not_include_terms": [
|
|
"indikator",
|
|
"300"
|
|
]
|
|
}
|
|
```
|
|
|
|
Weniger gut:
|
|
|
|
```json
|
|
{
|
|
"expected_query": "testomat 808",
|
|
"must_include_terms": [
|
|
"testomat",
|
|
"808",
|
|
"gerät",
|
|
"preis",
|
|
"wasserhärte"
|
|
],
|
|
"must_not_include_terms": [
|
|
"indikator",
|
|
"300",
|
|
"testomat 2000",
|
|
"chlor",
|
|
"versand"
|
|
]
|
|
}
|
|
```
|
|
|
|
Je kleiner und eindeutiger der Case ist, desto besser eignet er sich als Regressionstest.
|
|
|
|
---
|
|
|
|
# Felder im Admin
|
|
|
|
## 1. Eval-Typ
|
|
|
|
Wähle den Typ passend zum Ziel des Tests.
|
|
|
|
```text
|
|
retrieval → prüft, ob die richtigen RAG-Dokumente/Chunks gefunden werden
|
|
shop_query → prüft, welche Shopquery aus einem direkten Prompt entsteht
|
|
followup → prüft, welche Shopquery aus Prompt + Chatverlauf entsteht
|
|
answer_guard → prüft No-Answer-, Nicht-Halluzinations- oder Evidenzfälle
|
|
```
|
|
|
|
Faustregel:
|
|
|
|
```text
|
|
Wird das richtige Dokument gefunden? → retrieval
|
|
Wird die richtige Shopquery erzeugt? → shop_query
|
|
Versteht RetrieX die Folgefrage im Verlauf? → followup
|
|
Erfindet RetrieX nichts bei schwacher Evidenz? → answer_guard
|
|
```
|
|
|
|
---
|
|
|
|
## 2. Neue Case-ID
|
|
|
|
Die Case-ID muss eindeutig sein und darf nur folgende Zeichen enthalten:
|
|
|
|
```text
|
|
Buchstaben
|
|
Zahlen
|
|
_
|
|
-
|
|
```
|
|
|
|
Gute Beispiele:
|
|
|
|
```text
|
|
retrieval_semantic_chlor_clt_001
|
|
shop_query_indicator_300_exact_002
|
|
followup_main_device_price_002
|
|
answer_guard_unknown_medium_001
|
|
```
|
|
|
|
Nicht verwenden:
|
|
|
|
```text
|
|
Test 1
|
|
shop query indikator 300
|
|
gerät/frage/neue-version
|
|
```
|
|
|
|
Empfohlenes Schema:
|
|
|
|
```text
|
|
<typ>_<thema>_<ziel>_<nummer>
|
|
```
|
|
|
|
Beispiel:
|
|
|
|
```text
|
|
followup_testomat808_device_price_001
|
|
```
|
|
|
|
---
|
|
|
|
## 3. Prompt
|
|
|
|
Hier kommt exakt der Nutzerprompt hinein, der getestet werden soll.
|
|
|
|
Beispiele:
|
|
|
|
```text
|
|
welches geraet ist fuer chlorueberwachung gedacht
|
|
```
|
|
|
|
```text
|
|
was kostet der indikator
|
|
```
|
|
|
|
```text
|
|
und was kostet das gerät selber
|
|
```
|
|
|
|
```text
|
|
welcher testomat misst drachenblut
|
|
```
|
|
|
|
Der Prompt sollte möglichst so eingetragen werden, wie er real im Chat vorkommt. Tippfehler dürfen bewusst enthalten sein, wenn genau dieses Verhalten abgesichert werden soll.
|
|
|
|
---
|
|
|
|
## 4. Assert-JSON
|
|
|
|
Das Assert-JSON beschreibt, was der Test prüfen soll.
|
|
|
|
Das Feld muss immer ein gültiges JSON-Objekt sein:
|
|
|
|
```json
|
|
{
|
|
}
|
|
```
|
|
|
|
Wichtig:
|
|
|
|
- Keine Kommentare im JSON
|
|
- Keine trailing commas
|
|
- Doppelte Anführungszeichen verwenden
|
|
- Das Feld muss ein Objekt `{ ... }` sein, kein Array
|
|
|
|
---
|
|
|
|
# Eval-Typen und Beispiele
|
|
|
|
## A) Retrieval-Case
|
|
|
|
Retrieval-Cases prüfen, ob die richtigen RAG-Dokumente oder Chunks gefunden werden.
|
|
|
|
### Minimaler positiver Retrieval-Case
|
|
|
|
```json
|
|
{
|
|
"min_results": 1
|
|
}
|
|
```
|
|
|
|
### Retrieval-Case mit erwarteter Dokument-ID
|
|
|
|
```json
|
|
{
|
|
"min_results": 1,
|
|
"must_include_one_of_document_ids": [
|
|
"DOKUMENT-ID-HIER"
|
|
]
|
|
}
|
|
```
|
|
|
|
### Retrieval-Case mit mehreren möglichen Ziel-Dokumenten
|
|
|
|
```json
|
|
{
|
|
"min_results": 1,
|
|
"must_include_one_of_document_ids": [
|
|
"DOKUMENT-ID-1",
|
|
"DOKUMENT-ID-2"
|
|
]
|
|
}
|
|
```
|
|
|
|
### Retrieval-Case mit Pflichtbegriffen
|
|
|
|
```json
|
|
{
|
|
"min_results": 1,
|
|
"must_include_any_terms": [
|
|
"lieferung",
|
|
"versand"
|
|
]
|
|
}
|
|
```
|
|
|
|
### Retrieval-Case mit verbotenen Dokumenten
|
|
|
|
```json
|
|
{
|
|
"min_results": 1,
|
|
"must_not_include_document_ids": [
|
|
"FALSCHE-DOKUMENT-ID"
|
|
]
|
|
}
|
|
```
|
|
|
|
### Retrieval-Case für No-Result / Unsinn
|
|
|
|
```json
|
|
{
|
|
"max_results": 0
|
|
}
|
|
```
|
|
|
|
### Empfohlene Retrieval-Struktur
|
|
|
|
```json
|
|
{
|
|
"min_results": 1,
|
|
"must_include_one_of_document_ids": [
|
|
"DOKUMENT-ID-HIER"
|
|
],
|
|
"must_include_any_terms": [
|
|
"wichtiger fachbegriff",
|
|
"produktname"
|
|
]
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## B) Shopquery-Case
|
|
|
|
Shopquery-Cases prüfen, welche Shopquery aus einem direkten Prompt entsteht.
|
|
|
|
### Exakte Shopquery
|
|
|
|
Prompt:
|
|
|
|
```text
|
|
was kostet der Testomat 808 Indikator 300
|
|
```
|
|
|
|
Assert-JSON:
|
|
|
|
```json
|
|
{
|
|
"expected_query": "testomat 808 300 indikator"
|
|
}
|
|
```
|
|
|
|
### Shopquery mit Pflicht- und Verbotsbegriffen
|
|
|
|
```json
|
|
{
|
|
"must_include_terms": [
|
|
"testomat",
|
|
"808",
|
|
"300",
|
|
"indikator"
|
|
],
|
|
"must_not_include_terms": [
|
|
"300 s",
|
|
"301",
|
|
"302",
|
|
"303"
|
|
]
|
|
}
|
|
```
|
|
|
|
### Query darf nicht auf Noise fallen
|
|
|
|
```json
|
|
{
|
|
"must_not_equal_query": "information"
|
|
}
|
|
```
|
|
|
|
### Multi-Produkt- oder Link-Follow-up mit Einzelqueries
|
|
|
|
```json
|
|
{
|
|
"expected_individual_queries": [
|
|
"testomat 2000 self clean",
|
|
"testomat 2000 cal",
|
|
"testomat 808"
|
|
],
|
|
"expected_individual_queries_exact": true
|
|
}
|
|
```
|
|
|
|
### Empfehlung für Shopquery-Cases
|
|
|
|
Nicht jeden Case sofort zu streng mit `expected_query` absichern. Bei noch variabler Query-Bildung ist oft besser:
|
|
|
|
```json
|
|
{
|
|
"must_include_terms": [
|
|
"testomat",
|
|
"808",
|
|
"sio2"
|
|
],
|
|
"must_not_include_terms": [
|
|
"gerät",
|
|
"möchte",
|
|
"messen"
|
|
]
|
|
}
|
|
```
|
|
|
|
`expected_query` nur verwenden, wenn die Query bereits stabil und bewusst exakt sein soll.
|
|
|
|
---
|
|
|
|
## C) Follow-up-Case
|
|
|
|
Follow-up-Cases prüfen, ob RetrieX den Verlauf korrekt nutzt.
|
|
|
|
Bei `followup` ist **History-JSON praktisch Pflicht**, weil sonst kein echter Verlauf getestet wird.
|
|
|
|
### Beispiel: Indikatorpreis nach Verlauf
|
|
|
|
Prompt:
|
|
|
|
```text
|
|
was kostet der indikator
|
|
```
|
|
|
|
History-JSON:
|
|
|
|
```json
|
|
[
|
|
{
|
|
"prompt": "Was ist der niedrigste Grenzwert für die Wasserhärte, welcher mit einem Testomaten überwacht werden kann?",
|
|
"answer": "Der niedrigste Grenzwert für die Wasserhärte beträgt 0,02 °dH. Dieser Wert wird vom Testomat 808 gemessen."
|
|
},
|
|
{
|
|
"prompt": "mit welchem indikator",
|
|
"answer": "Der niedrigste messbare Grenzwert für Wasserhärte mit dem Testomat 808 wird mit dem Indikatortyp 300 erreicht."
|
|
}
|
|
]
|
|
```
|
|
|
|
Assert-JSON:
|
|
|
|
```json
|
|
{
|
|
"expected_query": "testomat 808 300 indikator",
|
|
"must_include_terms": [
|
|
"testomat",
|
|
"808",
|
|
"300",
|
|
"indikator"
|
|
],
|
|
"must_not_include_terms": [
|
|
"300 s",
|
|
"301",
|
|
"302",
|
|
"303",
|
|
"testomat 2000"
|
|
]
|
|
}
|
|
```
|
|
|
|
### Beispiel: Wechsel vom Indikator zurück zum Hauptgerät
|
|
|
|
Prompt:
|
|
|
|
```text
|
|
und was kostet das gerät selber
|
|
```
|
|
|
|
History-JSON:
|
|
|
|
```json
|
|
[
|
|
{
|
|
"prompt": "was kostet der indikator",
|
|
"answer": "Shop-Suche abgeschlossen. Gesendete Suchquery: testomat 808 300 indikator. Testomat® 808 Indikator 300 500 ml, Produkt-Nummer 141001. Testomat® 808 Indikator 300 2 x 100 ml, Produkt-Nummer 140001. Der zugehörige Testomat ist Testomat 808."
|
|
}
|
|
]
|
|
```
|
|
|
|
Assert-JSON:
|
|
|
|
```json
|
|
{
|
|
"expected_query": "testomat 808",
|
|
"must_include_terms": [
|
|
"testomat",
|
|
"808"
|
|
],
|
|
"must_not_include_terms": [
|
|
"indikator",
|
|
"300",
|
|
"141001",
|
|
"140001"
|
|
]
|
|
}
|
|
```
|
|
|
|
### Empfehlung für Follow-up-Cases
|
|
|
|
Die History sollte genau die Informationen enthalten, die der echte Chat vorher hatte.
|
|
|
|
Nicht zu wenig:
|
|
|
|
```text
|
|
Nur "Indikator 300" ohne Geräteanker kann zu unklar sein.
|
|
```
|
|
|
|
Nicht zu viel:
|
|
|
|
```text
|
|
Ein kompletter langer Chatverlauf kann den Case unnötig instabil machen.
|
|
```
|
|
|
|
Gut ist ein kurzer, fachlich relevanter Auszug.
|
|
|
|
---
|
|
|
|
## D) Answer-Guard-Case
|
|
|
|
Answer-Guard-Cases prüfen, dass RetrieX bei Unsinn, schwacher Evidenz oder falschen Zuordnungen nichts erfindet.
|
|
|
|
### Unsinn soll keine Treffer liefern
|
|
|
|
Prompt:
|
|
|
|
```text
|
|
dsgfsdgfsdgf
|
|
```
|
|
|
|
Assert-JSON:
|
|
|
|
```json
|
|
{
|
|
"max_results": 0
|
|
}
|
|
```
|
|
|
|
### Erfundenes Medium soll nicht als echtes Produkt beantwortet werden
|
|
|
|
Prompt:
|
|
|
|
```text
|
|
welcher testomat misst drachenblut
|
|
```
|
|
|
|
Assert-JSON:
|
|
|
|
```json
|
|
{
|
|
"must_not_include_terms": [
|
|
"drachenblut"
|
|
]
|
|
}
|
|
```
|
|
|
|
### Falsches Dokument darf nicht gezogen werden
|
|
|
|
```json
|
|
{
|
|
"min_results": 1,
|
|
"must_not_include_document_ids": [
|
|
"FALSCHE-DOKUMENT-ID"
|
|
]
|
|
}
|
|
```
|
|
|
|
### Empfehlung für Answer-Guard-Cases
|
|
|
|
Bei Answer-Guard-Cases möglichst nicht auf einzelne Wörter im kompletten Retrieval-Text überreagieren. Besser sind:
|
|
|
|
```text
|
|
Dokument-IDs
|
|
klare Produktnamen
|
|
klare verbotene Zielbegriffe
|
|
max_results bei Unsinn
|
|
```
|
|
|
|
Ein Wort irgendwo im Retrieval-Kontext ist nicht automatisch ein fachlicher Fehler.
|
|
|
|
---
|
|
|
|
# Optionales Feld: History-JSON
|
|
|
|
History-JSON wird vor allem für `followup` verwendet.
|
|
|
|
Format:
|
|
|
|
```json
|
|
[
|
|
{
|
|
"prompt": "vorherige Nutzerfrage",
|
|
"answer": "vorherige Antwort oder relevanter Auszug"
|
|
}
|
|
]
|
|
```
|
|
|
|
Mehrere Turns:
|
|
|
|
```json
|
|
[
|
|
{
|
|
"prompt": "erste Frage",
|
|
"answer": "erste Antwort"
|
|
},
|
|
{
|
|
"prompt": "zweite Frage",
|
|
"answer": "zweite Antwort"
|
|
}
|
|
]
|
|
```
|
|
|
|
Wichtig:
|
|
|
|
```text
|
|
History-JSON ist ein Array [...]
|
|
Assert-JSON ist ein Objekt {...}
|
|
```
|
|
|
|
---
|
|
|
|
# Optionales Feld: Request Context Hint
|
|
|
|
Dieses Feld kann meistens leer bleiben.
|
|
|
|
Es ist nur sinnvoll, wenn ein Case zusätzlichen Kontext simulieren soll, der nicht sauber über History abbildbar ist.
|
|
|
|
Beispiel:
|
|
|
|
```text
|
|
Sichtbare Shop-Ergebnisse enthalten Testomat 808 und Testomat 808 Indikator 300.
|
|
Der Nutzer fragt nach dem Gerät selber.
|
|
```
|
|
|
|
Empfehlung:
|
|
|
|
```text
|
|
Für normale Regressionen lieber History-JSON verwenden.
|
|
Request Context Hint nur für Spezialfälle nutzen.
|
|
```
|
|
|
|
---
|
|
|
|
# Vollständiges Beispiel: Follow-up-Gerätepreis
|
|
|
|
## Eval-Typ
|
|
|
|
```text
|
|
followup
|
|
```
|
|
|
|
## Neue Case-ID
|
|
|
|
```text
|
|
followup_testomat808_main_device_price_002
|
|
```
|
|
|
|
## Prompt
|
|
|
|
```text
|
|
und was kostet das gerät selber
|
|
```
|
|
|
|
## Assert-JSON
|
|
|
|
```json
|
|
{
|
|
"expected_query": "testomat 808",
|
|
"must_include_terms": [
|
|
"testomat",
|
|
"808"
|
|
],
|
|
"must_not_include_terms": [
|
|
"indikator",
|
|
"300",
|
|
"141001",
|
|
"140001"
|
|
]
|
|
}
|
|
```
|
|
|
|
## History-JSON
|
|
|
|
```json
|
|
[
|
|
{
|
|
"prompt": "was kostet der indikator",
|
|
"answer": "Shop-Suche abgeschlossen. Gesendete Suchquery: testomat 808 300 indikator. Testomat® 808 Indikator 300 500 ml, Produkt-Nummer 141001. Testomat® 808 Indikator 300 2 x 100 ml, Produkt-Nummer 140001. Der zugehörige Testomat ist Testomat 808."
|
|
}
|
|
]
|
|
```
|
|
|
|
## Request Context Hint
|
|
|
|
Leer lassen.
|
|
|
|
---
|
|
|
|
# Nach dem Speichern prüfen
|
|
|
|
Nach dem Speichern sollte der passende Eval-Typ ausgeführt werden.
|
|
|
|
Im Admin:
|
|
|
|
```text
|
|
/admin/evals/
|
|
```
|
|
|
|
Oder per CLI:
|
|
|
|
```bash
|
|
php bin/console mto:agent:config:validate
|
|
php bin/console mto:agent:eval:run retrieval
|
|
php bin/console mto:agent:eval:run shop_query
|
|
php bin/console mto:agent:eval:run followup
|
|
php bin/console mto:agent:eval:run answer_guard
|
|
```
|
|
|
|
Für einen einzelnen Typ:
|
|
|
|
```bash
|
|
php bin/console mto:agent:eval:run followup
|
|
```
|
|
|
|
---
|
|
|
|
# Praktische Checkliste
|
|
|
|
Vor dem Speichern prüfen:
|
|
|
|
```text
|
|
[ ] Eval-Typ passt zum Ziel
|
|
[ ] Case-ID ist eindeutig
|
|
[ ] Case-ID enthält nur Buchstaben, Zahlen, _ oder -
|
|
[ ] Prompt ist realistisch und exakt
|
|
[ ] Assert-JSON ist gültiges JSON-Objekt
|
|
[ ] History-JSON ist bei Follow-up-Cases vorhanden
|
|
[ ] History-JSON ist gültiges JSON-Array
|
|
[ ] Der Case prüft nur einen klaren Sachverhalt
|
|
[ ] Assertions sind nicht unnötig streng
|
|
[ ] Nach dem Speichern läuft der passende Eval-Typ grün
|
|
```
|
|
|
|
---
|
|
|
|
# Wann ein neuer Eval-Case angelegt werden sollte
|
|
|
|
Ein neuer Case ist sinnvoll, wenn:
|
|
|
|
```text
|
|
ein realer Prompt rot war
|
|
ein wichtiger grüner Flow dauerhaft abgesichert werden soll
|
|
ein Tippfehler-/Noise-Fall stabil bleiben soll
|
|
eine Produktidentität nicht verloren gehen darf
|
|
eine falsche Dokumentzuordnung verhindert werden soll
|
|
eine No-Answer-Situation nicht halluzinieren darf
|
|
```
|
|
|
|
Kein neuer Case ist nötig, wenn:
|
|
|
|
```text
|
|
nur die Formulierung einer Antwort leicht anders war
|
|
der Prompt fachlich nicht relevant ist
|
|
die Erwartung nicht eindeutig definiert werden kann
|
|
der Case mehrere unabhängige Dinge gleichzeitig prüfen würde
|
|
```
|
|
|
|
---
|
|
|
|
# Leitlinie
|
|
|
|
Ab RetrieX v1.6.2 gilt:
|
|
|
|
```text
|
|
Keine neue Genauigkeitslogik ohne konkreten roten oder fachlich wichtigen Eval-Fall.
|
|
```
|
|
|
|
Daher sollten neue Optimierungen möglichst immer so ablaufen:
|
|
|
|
```text
|
|
1. Prompt testen
|
|
2. Verhalten bewerten
|
|
3. Wenn wichtig: Eval-Case anlegen
|
|
4. Eval grün bekommen
|
|
5. Erst danach Logik, YAML oder Parameter ändern
|
|
``` |