1304 lines
26 KiB
Markdown
1304 lines
26 KiB
Markdown
|
|
# Buying Agent – Source & Discovery Specification v0.3
|
|||
|
|
|
|||
|
|
**Status:** Review Candidate – vor Implementierung erneut durch den Coding-Agenten zu prüfen
|
|||
|
|
**Spezifikationsversion:** 0.3
|
|||
|
|
**Stand:** 2026-10-04
|
|||
|
|
|
|||
|
|
**Ersetzt vollständig:** `buying-agent-source-discovery-spec-v0.3.md`
|
|||
|
|
|
|||
|
|
**Gilt zusammen mit:**
|
|||
|
|
1. `buying-agent-v0.1-spec-v1.1.md`
|
|||
|
|
2. `buying-agent-architecture-addendum-v1.md`
|
|||
|
|
3. `buying-agent-frontend-mvp-spec-v0.3.md`
|
|||
|
|
|
|||
|
|
Bei Widersprüchen innerhalb des Source-/Discovery-Bereichs hat diese v0.3 Vorrang.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 1. Ziel
|
|||
|
|
|
|||
|
|
Diese Spezifikation definiert den Source-/Discovery-Pfad des **funktionierenden MVP**.
|
|||
|
|
|
|||
|
|
Sie ist ausdrücklich **kein Skeleton und kein theoretischer POC**.
|
|||
|
|
|
|||
|
|
Der MVP gilt in diesem Bereich erst dann als funktionsfähig, wenn der reale Pfad:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
Produktkategorie wählen
|
|||
|
|
↓
|
|||
|
|
SourceCategory bestimmen
|
|||
|
|
↓
|
|||
|
|
Geizhals-Taxonomie technisch lernen
|
|||
|
|
↓
|
|||
|
|
Filter und URL-Konstrukte verifizieren
|
|||
|
|
↓
|
|||
|
|
Kriterien über API/UI anbieten
|
|||
|
|
↓
|
|||
|
|
Such-URL aus ProjectCriteria erzeugen
|
|||
|
|
↓
|
|||
|
|
SearchRun ausführen
|
|||
|
|
↓
|
|||
|
|
Ergebnisse und Attribute parsen
|
|||
|
|
↓
|
|||
|
|
Produkte identifizieren / historisieren
|
|||
|
|
↓
|
|||
|
|
Diffing
|
|||
|
|
↓
|
|||
|
|
Kandidaten / Ranking / Shortlist
|
|||
|
|
↓
|
|||
|
|
UI
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
end-to-end funktioniert.
|
|||
|
|
|
|||
|
|
Initiale Source: **Geizhals**
|
|||
|
|
Pilotkategorie: **Notebooks**
|
|||
|
|
|
|||
|
|
Die Architektur darf nicht notebook-spezifisch sein.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 2. Grundprinzipien
|
|||
|
|
|
|||
|
|
1. Geizhals ist Discovery-Quelle, nicht Decision Engine.
|
|||
|
|
2. Source-Kriterien bilden für unterstützte Kategorien zunächst den primären Kriterienkatalog.
|
|||
|
|
3. Es gibt keine separate Canonical-Criteria-Schicht.
|
|||
|
|
4. Jedes gelernte Source-Kriterium erhält eine stabile interne `CriterionDefinition.id` (UUID).
|
|||
|
|
5. SearchFilter und Ergebnisattribute werden auf dieselbe `CriterionDefinition.id` gemappt.
|
|||
|
|
6. INTERNAL-Kriterien werden nur ergänzt, wenn die Source relevante Eigenschaften nicht kennt.
|
|||
|
|
7. URL-Encoding wird aus beobachtetem Source-Verhalten gelernt, nicht erfunden.
|
|||
|
|
8. Nur verifizierte Mappings werden produktiv verwendet.
|
|||
|
|
9. Taxonomy Learning und normale SearchRuns sind getrennte Prozesse.
|
|||
|
|
10. Produktidentität bleibt konservativ.
|
|||
|
|
11. Historie wird strukturiert gespeichert, kein HTML-Archiv.
|
|||
|
|
12. Discovery, Ranking und Decision bleiben getrennt.
|
|||
|
|
13. Vollständige Reconciliation darf nur auf vollständigen SearchRuns beruhen.
|
|||
|
|
|
|||
|
|
## 2.1 Konkret beobachtete Geizhals-Mechanismen für den Notebook-Pilot
|
|||
|
|
|
|||
|
|
Die aktuelle Notebook-Kategorie zeigt bereits ausreichend konkrete Muster, um den MVP-Pfad präziser festzulegen:
|
|||
|
|
|
|||
|
|
- zahlreiche Filter werden als auswählbare Optionen/Links dargestellt,
|
|||
|
|
- Filtergruppen umfassen u. a. Hersteller, Diagonale, Auflösung, CPU-Hersteller, CPU-Serie, RAM, Speicher, Grafik, Betriebssystem und Gewicht,
|
|||
|
|
- Preis, Gewicht und Diagonale sind typische Kandidaten für Range-/Input-Verhalten,
|
|||
|
|
- die Ergebnisliste enthält bereits einen wesentlichen Teil der für den MVP relevanten Produktdaten, darunter Betriebssystem, Display, CPU, RAM, Speicher, Grafik, Modell-/Produktreferenz, Angebote und Bestpreis.
|
|||
|
|
|
|||
|
|
Diese Beobachtungen dienen als Pilotannahmen und müssen vor finaler Parser-Implementierung am echten Live-DOM validiert werden.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 3. ProductCategory und SourceCategory
|
|||
|
|
|
|||
|
|
## 3.1 ProductCategory
|
|||
|
|
|
|||
|
|
Interner stabiler Katalog:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
ProductCategory
|
|||
|
|
id
|
|||
|
|
name
|
|||
|
|
status
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Beispiel:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
id = notebook
|
|||
|
|
name = Notebooks
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 3.2 SourceCategory
|
|||
|
|
|
|||
|
|
Mapping zur Source:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
SourceCategory
|
|||
|
|
id
|
|||
|
|
source
|
|||
|
|
external_category_key
|
|||
|
|
product_category_id
|
|||
|
|
source_url
|
|||
|
|
status
|
|||
|
|
taxonomy_status
|
|||
|
|
last_taxonomy_scan_at
|
|||
|
|
last_verified_at
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Beispiel:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
source = geizhals
|
|||
|
|
external_category_key = nb
|
|||
|
|
product_category_id = notebook
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 4. SourceCategory Readiness
|
|||
|
|
|
|||
|
|
Eine SourceCategory besitzt einen technischen Readiness-Status:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
NOT_LEARNED
|
|||
|
|
LEARNING
|
|||
|
|
READY
|
|||
|
|
DEGRADED
|
|||
|
|
BROKEN
|
|||
|
|
BLOCKED
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 4.1 READY
|
|||
|
|
|
|||
|
|
Eine SourceCategory ist `READY`, wenn mindestens:
|
|||
|
|
|
|||
|
|
- Baseline-Kategorieseite erreichbar ist
|
|||
|
|
- Taxonomie technisch gelesen werden kann
|
|||
|
|
- relevante Filterdefinitionen persistiert sind
|
|||
|
|
- verwendbare URL-Encoding-Regeln verifiziert sind
|
|||
|
|
- Query-Generation erfolgreich getestet wurde
|
|||
|
|
- Result-Parsing mindestens für Basisdaten funktioniert
|
|||
|
|
- kein aktiver Block-/Captcha-Zustand vorliegt
|
|||
|
|
|
|||
|
|
## 4.2 DEGRADED
|
|||
|
|
|
|||
|
|
`DEGRADED`, wenn:
|
|||
|
|
|
|||
|
|
- ein Teil der Filter nicht verifizierbar ist
|
|||
|
|
- einzelne Mappings BROKEN sind
|
|||
|
|
- Result-Parsing teilweise funktioniert
|
|||
|
|
- Kategorie grundsätzlich weiter nutzbar ist
|
|||
|
|
|
|||
|
|
## 4.3 BROKEN
|
|||
|
|
|
|||
|
|
`BROKEN`, wenn Kernfunktionen nicht mehr zuverlässig arbeiten, z. B.:
|
|||
|
|
|
|||
|
|
- DOM-Struktur vollständig inkompatibel
|
|||
|
|
- keine Query-Generierung mehr möglich
|
|||
|
|
- Result Parser liefert keine verwertbaren Produkte
|
|||
|
|
|
|||
|
|
## 4.4 BLOCKED
|
|||
|
|
|
|||
|
|
`BLOCKED`, wenn Sourcezugriff aktuell durch:
|
|||
|
|
|
|||
|
|
- 403
|
|||
|
|
- Captcha
|
|||
|
|
- Challenge
|
|||
|
|
- wiederholte 429
|
|||
|
|
|
|||
|
|
nicht weitergeführt werden soll.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 5. Taxonomy Learning – operativer Algorithmus
|
|||
|
|
|
|||
|
|
Taxonomie wird **on demand** gelernt.
|
|||
|
|
|
|||
|
|
Der technische Ablauf ist verbindlich.
|
|||
|
|
|
|||
|
|
## 5.1 Schritt 1 – Baseline laden
|
|||
|
|
|
|||
|
|
Lade die Baseline-Kategorieseite, z. B.:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
https://geizhals.de/?cat=nb
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Speichere:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
source_category
|
|||
|
|
baseline_url
|
|||
|
|
http_status
|
|||
|
|
loaded_at
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Keine HTML-Archivierung als Produktfunktion.
|
|||
|
|
|
|||
|
|
Fixtures dürfen für Tests aufgezeichnet werden.
|
|||
|
|
|
|||
|
|
## 5.2 Schritt 2 – Filtergruppen und Controls erkennen
|
|||
|
|
|
|||
|
|
Der Geizhals-Adapter analysiert die DOM-Struktur.
|
|||
|
|
|
|||
|
|
Zu erkennen sind, soweit vorhanden:
|
|||
|
|
|
|||
|
|
- Filtergruppen
|
|||
|
|
- Filterlabel
|
|||
|
|
- Checkboxen
|
|||
|
|
- Radio-Optionen
|
|||
|
|
- Dropdowns
|
|||
|
|
- Multi-Selects
|
|||
|
|
- Min/Max-Felder
|
|||
|
|
- Range-Eingaben
|
|||
|
|
- freie Texteingaben
|
|||
|
|
- Links / hrefs
|
|||
|
|
- optionale source-interne Filter-IDs
|
|||
|
|
- Einheiten
|
|||
|
|
- Wertebereiche
|
|||
|
|
- sichtbare Optionslabels
|
|||
|
|
|
|||
|
|
Primärziel ist nicht die visuelle Darstellung, sondern die strukturelle Filtersemantik.
|
|||
|
|
|
|||
|
|
## 5.3 Schritt 3 – Filtermechanismus klassifizieren
|
|||
|
|
|
|||
|
|
Jeder erkannte Filter wird genau einem primären Lernweg zugeordnet:
|
|||
|
|
|
|||
|
|
### Typ A – Link-/href-basiert
|
|||
|
|
|
|||
|
|
Bevorzugter Weg.
|
|||
|
|
|
|||
|
|
Wenn Geizhals für eine Filteroption einen nutzbaren Link / href bereitstellt, wird dieser direkt ausgewertet.
|
|||
|
|
|
|||
|
|
Beispiel konzeptionell:
|
|||
|
|
|
|||
|
|
```html
|
|||
|
|
<a href="?cat=nb&xf=17023_ab+24GB">ab 24 GB</a>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Der Adapter extrahiert:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
label
|
|||
|
|
option_label
|
|||
|
|
href
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Typ B – Range-/Input-Control
|
|||
|
|
|
|||
|
|
Für Controls wie Preis, Gewicht oder Diagonale, bei denen der Nutzer Werte eingibt oder einen Bereich setzt:
|
|||
|
|
|
|||
|
|
1. Baseline-URL erfassen.
|
|||
|
|
2. einen kontrollierten Testwert setzen,
|
|||
|
|
3. resultierende Navigation/Query-Änderung erfassen,
|
|||
|
|
4. Query-Diff bestimmen,
|
|||
|
|
5. Parametermuster persistieren,
|
|||
|
|
6. mit einem zweiten Testwert prüfen, ob das Muster reproduzierbar ist.
|
|||
|
|
|
|||
|
|
### Typ C – JS-only Control
|
|||
|
|
|
|||
|
|
Nur wenn weder href noch statisch erkennbare Parameterstruktur vorhanden sind:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
Playwright-Fallback
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Der Browser setzt den Filterzustand und beobachtet die resultierende URL bzw. Query.
|
|||
|
|
|
|||
|
|
Damit gilt die Priorität:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
A: href/link
|
|||
|
|
→ B: Range/Input
|
|||
|
|
→ C: Playwright-Fallback
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 5.4 Schritt 4 – Query-Diff bestimmen
|
|||
|
|
|
|||
|
|
Vergleiche Baseline-Query und Filter-Query.
|
|||
|
|
|
|||
|
|
Beispiel:
|
|||
|
|
|
|||
|
|
Baseline:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
?cat=nb
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Filterzustand:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
?cat=nb&xf=17023_ab+24GB
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Query-Diff:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
parameter = xf
|
|||
|
|
value = 17023_ab+24GB
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Für Preis können z. B. getrennte Parameter wie `bpmin` / `bpmax` erkannt werden.
|
|||
|
|
|
|||
|
|
## 5.5 Schritt 5 – Filterdefinition speichern
|
|||
|
|
|
|||
|
|
Erzeuge / aktualisiere:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
SourceFilterDefinition
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Mindestens:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
id
|
|||
|
|
source_category_id
|
|||
|
|
criterion_definition_id
|
|||
|
|
external_filter_key nullable
|
|||
|
|
label
|
|||
|
|
filter_type
|
|||
|
|
unit nullable
|
|||
|
|
encoding_type
|
|||
|
|
status
|
|||
|
|
last_seen_at
|
|||
|
|
last_verified_at
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Filtertypen:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
BOOLEAN
|
|||
|
|
ENUM
|
|||
|
|
MULTI_ENUM
|
|||
|
|
NUMERIC_RANGE
|
|||
|
|
NUMERIC_THRESHOLD
|
|||
|
|
TEXT
|
|||
|
|
UNKNOWN
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Encoding-Typen mindestens:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
OPTION_TOKEN
|
|||
|
|
RANGE_PARAMETERS
|
|||
|
|
BOOLEAN_PARAMETER
|
|||
|
|
TEXT_PARAMETER
|
|||
|
|
UNKNOWN
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 6. SourceFilterOption / SourceFilterRange
|
|||
|
|
|
|||
|
|
## 6.1 Diskrete Optionen
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
SourceFilterOption
|
|||
|
|
- id
|
|||
|
|
- source_filter_definition_id
|
|||
|
|
- source_label
|
|||
|
|
- normalized_value nullable
|
|||
|
|
- operator nullable
|
|||
|
|
- unit nullable
|
|||
|
|
- query_parameter
|
|||
|
|
- query_value
|
|||
|
|
- status
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 6.2 Range-Filter
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
SourceFilterRange
|
|||
|
|
- source_filter_definition_id
|
|||
|
|
- min_parameter nullable
|
|||
|
|
- max_parameter nullable
|
|||
|
|
- minimum nullable
|
|||
|
|
- maximum nullable
|
|||
|
|
- step nullable
|
|||
|
|
- unit nullable
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 7. Playwright-Fallback für JS-only Controls
|
|||
|
|
|
|||
|
|
Wenn ein Filter kein direkt verwertbares href bzw. keine statisch erkennbare Query-Struktur liefert:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
Taxonomy Learning
|
|||
|
|
↓
|
|||
|
|
Playwright
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Ablauf:
|
|||
|
|
|
|||
|
|
1. Kategorieseite öffnen
|
|||
|
|
2. Ziel-Filter lokalisieren
|
|||
|
|
3. Filterwert setzen
|
|||
|
|
4. Navigation / URL-Änderung abwarten
|
|||
|
|
5. resultierende URL erfassen
|
|||
|
|
6. Query-Diff zur Baseline berechnen
|
|||
|
|
7. Filterzustand wieder zurücksetzen
|
|||
|
|
|
|||
|
|
Wichtig:
|
|||
|
|
|
|||
|
|
- Playwright ist Fallback, nicht Primärmechanismus.
|
|||
|
|
- Playwright wird für Taxonomy Learning verwendet, nicht für jeden normalen SearchRun.
|
|||
|
|
- Kein Captcha-Bypass.
|
|||
|
|
- Kein Login-Bypass.
|
|||
|
|
- Kein Umgehen technischer Schutzmechanismen.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 8. Mapping Lifecycle
|
|||
|
|
|
|||
|
|
Filter-/Encoding-Mappings besitzen Status:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
DISCOVERED
|
|||
|
|
LEARNED_UNVERIFIED
|
|||
|
|
VERIFIED
|
|||
|
|
BROKEN
|
|||
|
|
DISABLED
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Nur `VERIFIED` wird für produktive Query-Generierung verwendet.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 9. Roundtrip-Verifikation
|
|||
|
|
|
|||
|
|
Die Roundtrip-Verifikation ist verbindlich.
|
|||
|
|
|
|||
|
|
Beispiel:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
RAM >= 24 GB
|
|||
|
|
↓
|
|||
|
|
gelernte Query-Regel
|
|||
|
|
↓
|
|||
|
|
URL selbst generieren
|
|||
|
|
↓
|
|||
|
|
Geizhals aufrufen
|
|||
|
|
↓
|
|||
|
|
aktiven Filterzustand prüfen
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Das Mapping wird nur `VERIFIED`, wenn der Adapter auf der Zielseite eindeutig erkennt, dass der erwartete Filter aktiv ist.
|
|||
|
|
|
|||
|
|
Wenn Verifikation fehlschlägt:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
LEARNED_UNVERIFIED
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Wenn vorher VERIFIED und nun nicht mehr reproduzierbar:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
BROKEN
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 10. Erkennung von Source-Änderungen
|
|||
|
|
|
|||
|
|
Taxonomy Learning vergleicht:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
vorheriger Filterbestand
|
|||
|
|
vs.
|
|||
|
|
aktueller Filterbestand
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Zu erkennen sind mindestens:
|
|||
|
|
|
|||
|
|
- Filter neu
|
|||
|
|
- Filter verschwunden
|
|||
|
|
- Filterlabel geändert
|
|||
|
|
- externe Filter-ID geändert
|
|||
|
|
- Optionen geändert
|
|||
|
|
- Encoding geändert
|
|||
|
|
- Unit geändert
|
|||
|
|
- Control-Typ geändert
|
|||
|
|
|
|||
|
|
Bei relevanter Änderung:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
mapping_version++
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Betroffene DiscoverySearches werden:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
OUT_OF_SYNC
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
wenn ihre Query auf geänderten Mappings basiert.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 11. CriterionDefinition
|
|||
|
|
|
|||
|
|
Jedes Source-Kriterium erhält eine stabile interne ID:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
CriterionDefinition
|
|||
|
|
- id UUID
|
|||
|
|
- origin SOURCE | INTERNAL
|
|||
|
|
- product_category_id
|
|||
|
|
- source_id nullable
|
|||
|
|
- source_category_id nullable
|
|||
|
|
- label
|
|||
|
|
- value_type
|
|||
|
|
- unit nullable
|
|||
|
|
- scoring_attribute_key nullable
|
|||
|
|
- status
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
`CriterionDefinition.id` ist Referenzidentität, keine globale Ontologie.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 12. SourceAttributeMapping
|
|||
|
|
|
|||
|
|
Suchfilter und Produktattribute müssen verbunden werden.
|
|||
|
|
|
|||
|
|
Dafür:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
SourceAttributeMapping
|
|||
|
|
- id
|
|||
|
|
- source_category_id
|
|||
|
|
- source_attribute_key nullable
|
|||
|
|
- source_attribute_label
|
|||
|
|
- criterion_definition_id
|
|||
|
|
- normalizer_id nullable
|
|||
|
|
- status
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Matching-Priorität:
|
|||
|
|
|
|||
|
|
1. stabile Source-Attribute-ID
|
|||
|
|
2. bekannte SourceAttributeMapping-Regel
|
|||
|
|
3. normalisiertes Label-Matching
|
|||
|
|
4. ansonsten UNMAPPED
|
|||
|
|
|
|||
|
|
Keine aggressive automatische Zuordnung bei Unsicherheit.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 13. available-criteria
|
|||
|
|
|
|||
|
|
Endpoint:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
GET /api/v1/product-categories/{product_category_id}/available-criteria?source=geizhals
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Nur verwendbare Kriterien werden dem Standardnutzer als Discovery-Filter angeboten.
|
|||
|
|
|
|||
|
|
Mindestens:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
criterion_definition_id
|
|||
|
|
label
|
|||
|
|
filter_type
|
|||
|
|
unit
|
|||
|
|
operators
|
|||
|
|
options / range
|
|||
|
|
mapping_status
|
|||
|
|
supports_discovery
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 14. Query Generation
|
|||
|
|
|
|||
|
|
Die Source-URL wird ausschließlich aus:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
SourceCategory
|
|||
|
|
+
|
|||
|
|
aktuelle ProjectCriteria
|
|||
|
|
+
|
|||
|
|
VERIFIED SourceFilterMappings
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
erzeugt.
|
|||
|
|
|
|||
|
|
Keine URL-Templates aus LLM.
|
|||
|
|
|
|||
|
|
Keine hartcodierten notebook-spezifischen Query-Fragmente außerhalb des Geizhals-Adapters / Source-Datenmodells.
|
|||
|
|
|
|||
|
|
Die Query-Generierung muss deterministisch, reproduzierbar, testbar und versionsgebunden sein.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 15. SearchRun
|
|||
|
|
|
|||
|
|
Ein SearchRun hält mindestens:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
discovery_search_id
|
|||
|
|
criteria_version
|
|||
|
|
mapping_version
|
|||
|
|
generated_url
|
|||
|
|
started_at
|
|||
|
|
finished_at
|
|||
|
|
status
|
|||
|
|
result_count
|
|||
|
|
page_count
|
|||
|
|
new_count
|
|||
|
|
changed_count
|
|||
|
|
unchanged_count
|
|||
|
|
missing_count
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 16. Ergebnisabruf und Pagination
|
|||
|
|
|
|||
|
|
Ein Full SearchRun versucht grundsätzlich die vollständige Ergebnismenge zu lesen.
|
|||
|
|
|
|||
|
|
Wenn nicht alle Seiten vollständig gelesen werden:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
SearchRun.status = PARTIAL
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Ein `PARTIAL`-Run darf keine:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
MISSING
|
|||
|
|
DISAPPEARED
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Transitions erzeugen.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 17. TOO_BROAD
|
|||
|
|
|
|||
|
|
Eine DiscoverySearch darf einen konfigurierbaren Schwellenwert besitzen.
|
|||
|
|
|
|||
|
|
Beispiel:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
broad_result_threshold = 300
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Bei Überschreitung:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
SearchRun.status = TOO_BROAD
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Kein stilles Top-N-Limit.
|
|||
|
|
|
|||
|
|
Ein technischer Safety-Cap ist zulässig, wenn er explizit konfiguriert und sichtbar dokumentiert ist; der Lauf gilt dann nicht als Full Run.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 18. Zweistufige Datenerfassung
|
|||
|
|
|
|||
|
|
Die Suchergebnisliste ist die **primäre Datenquelle des MVP**.
|
|||
|
|
|
|||
|
|
Der Notebook-Pilot muss zunächst möglichst viel aus der Ergebnisliste gewinnen, bevor zusätzliche Produktdetailseiten geladen werden.
|
|||
|
|
|
|||
|
|
## 18.1 Stufe A – Suchergebnisliste
|
|||
|
|
|
|||
|
|
Jeder SearchRun liest aus der Ergebnisliste mindestens:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
source_product_url
|
|||
|
|
title
|
|||
|
|
manufacturer/model soweit sichtbar
|
|||
|
|
best_price
|
|||
|
|
currency
|
|||
|
|
availability soweit sichtbar
|
|||
|
|
seller_count soweit sichtbar
|
|||
|
|
sichtbare Attribute
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Für die Notebook-Kategorie sind insbesondere folgende sichtbare Basisattribute als realistische MVP-Felder zu validieren:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
Betriebssystem
|
|||
|
|
Display
|
|||
|
|
CPU
|
|||
|
|
RAM
|
|||
|
|
Speicher
|
|||
|
|
Grafik
|
|||
|
|
Modell-/Produktreferenz
|
|||
|
|
Angebote
|
|||
|
|
Bestpreis
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Die genaue DOM-Struktur und die Verfügbarkeit einzelner Felder dürfen nicht vorab erfunden werden; sie werden im technischen Spike und Live-Pilot bestätigt.
|
|||
|
|
|
|||
|
|
## 18.2 Stufe B – Produktdetailseite
|
|||
|
|
|
|||
|
|
Produktdetailseiten werden nicht blind für alle Treffer geladen.
|
|||
|
|
|
|||
|
|
Sie werden für relevante Kandidaten geladen, insbesondere:
|
|||
|
|
|
|||
|
|
- Shortlist
|
|||
|
|
- Top-N nach ProductScore
|
|||
|
|
- Kandidaten mit fehlenden für Ranking relevanten Attributen
|
|||
|
|
- Kandidaten mit unsicherer Identität, wenn Detailseite helfen kann
|
|||
|
|
|
|||
|
|
Der Notebook-Pilot muss praktisch feststellen, welche Daten auf Liste und Detailseite verfügbar sind.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 19. SourceProductReference
|
|||
|
|
|
|||
|
|
Mindestens:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
id
|
|||
|
|
source_id
|
|||
|
|
source_category_id
|
|||
|
|
source_url
|
|||
|
|
source_title
|
|||
|
|
manufacturer nullable
|
|||
|
|
model nullable
|
|||
|
|
manufacturer_sku nullable
|
|||
|
|
gtin_ean nullable
|
|||
|
|
normalized_spec_fingerprint nullable
|
|||
|
|
match_status
|
|||
|
|
lifecycle_status
|
|||
|
|
first_seen_at
|
|||
|
|
last_seen_at
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 20. Produktidentität – konservativer Pilot
|
|||
|
|
|
|||
|
|
Signalreihenfolge:
|
|||
|
|
|
|||
|
|
1. stabile Source-ID, falls entdeckt
|
|||
|
|
2. GTIN/EAN
|
|||
|
|
3. Hersteller-SKU
|
|||
|
|
4. kanonische Source-URL
|
|||
|
|
5. normalisierter Titel
|
|||
|
|
6. Hersteller + Modell
|
|||
|
|
7. differenzierende Specs
|
|||
|
|
|
|||
|
|
Bei Unsicherheit:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
MATCH_UNCERTAIN
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Kein automatisches Merge.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 21. ProductObservation
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
ProductObservation
|
|||
|
|
- id
|
|||
|
|
- source_product_reference_id
|
|||
|
|
- search_run_id
|
|||
|
|
- observed_at
|
|||
|
|
- raw_title
|
|||
|
|
- best_price
|
|||
|
|
- currency
|
|||
|
|
- availability nullable
|
|||
|
|
- seller_count nullable
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 22. ProductAttributeObservation
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
ProductAttributeObservation
|
|||
|
|
- product_observation_id
|
|||
|
|
- criterion_definition_id nullable
|
|||
|
|
- raw_name
|
|||
|
|
- raw_value
|
|||
|
|
- normalized_value nullable
|
|||
|
|
- unit nullable
|
|||
|
|
- mapping_status
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Wenn kein Mapping existiert:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
criterion_definition_id = null
|
|||
|
|
mapping_status = UNMAPPED
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Unmapped Felder dürfen für spätere Admin-Korrektur sichtbar bleiben.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 23. Normalisierung
|
|||
|
|
|
|||
|
|
Normalizer arbeitet kriteriumsabhängig.
|
|||
|
|
|
|||
|
|
Beispiele:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
"32 GB" -> 32 / GB
|
|||
|
|
"1 TB" -> 1024 / GB, falls Zielunit GB
|
|||
|
|
"1,45 kg" -> 1.45 / kg
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Zu speichern:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
raw_value
|
|||
|
|
normalized_value
|
|||
|
|
unit
|
|||
|
|
normalizer_version
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 24. Diffing
|
|||
|
|
|
|||
|
|
Verglichen werden nur vollständige Runs derselben DiscoverySearch.
|
|||
|
|
|
|||
|
|
Run Diff:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
NEW
|
|||
|
|
CHANGED
|
|||
|
|
UNCHANGED
|
|||
|
|
MISSING
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
CHANGED mindestens bei relevanter Änderung von:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
best_price
|
|||
|
|
currency
|
|||
|
|
availability
|
|||
|
|
seller_count
|
|||
|
|
normalisierte Attribute
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 25. Product Lifecycle
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
ACTIVE
|
|||
|
|
DISAPPEARED
|
|||
|
|
ARCHIVED
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Regeln:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
1 Full Run fehlend -> Run Diff MISSING
|
|||
|
|
3 aufeinanderfolgende Full Runs fehlend -> Lifecycle DISAPPEARED
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 26. Ranking und Shortlist
|
|||
|
|
|
|||
|
|
Bestehende Scoring-Engine kann über:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
CriterionDefinition.scoring_attribute_key
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
angesprochen werden.
|
|||
|
|
|
|||
|
|
Ohne Ranking-Konfiguration:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
ranking_status = NOT_CONFIGURED
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Mit Ranking:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
ProductScore
|
|||
|
|
product_eligibility
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Shortlist Default: `5`
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 27. Scheduling und Backoff
|
|||
|
|
|
|||
|
|
Default:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
180 Minuten
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
auf `DiscoverySearch`.
|
|||
|
|
|
|||
|
|
Defensives Verhalten:
|
|||
|
|
|
|||
|
|
- Timeout
|
|||
|
|
- Retry mit exponentiellem Backoff
|
|||
|
|
- Jitter
|
|||
|
|
- 429 -> BACKOFF
|
|||
|
|
- 403 -> BLOCKED
|
|||
|
|
- Captcha / Challenge -> BLOCKED
|
|||
|
|
- wiederholte Parsing-Fehler -> DEGRADED/BROKEN
|
|||
|
|
|
|||
|
|
Keine Schutzumgehung.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 28. UI-Reaktion auf SourceCategory Readiness
|
|||
|
|
|
|||
|
|
## READY
|
|||
|
|
Normale Kriterienauswahl und Suchanlage.
|
|||
|
|
|
|||
|
|
## NOT_LEARNED
|
|||
|
|
UI zeigt: „Kriterien für diese Produktkategorie werden gerade vorbereitet.“
|
|||
|
|
|
|||
|
|
## LEARNING
|
|||
|
|
Loading-/Progress-Zustand.
|
|||
|
|
|
|||
|
|
## DEGRADED
|
|||
|
|
VERIFIED Kriterien dürfen angeboten werden; Warnung anzeigen.
|
|||
|
|
|
|||
|
|
## BROKEN
|
|||
|
|
Keine neue produktive Suche.
|
|||
|
|
|
|||
|
|
## BLOCKED
|
|||
|
|
Quelle derzeit nicht automatisiert abfragbar.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 29. Taxonomy Learning API
|
|||
|
|
|
|||
|
|
Mindestens:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
POST /api/v1/sources/{source}/categories/{category}/discover-taxonomy
|
|||
|
|
GET /api/v1/sources/{source}/categories/{category}/taxonomy-status
|
|||
|
|
GET /api/v1/sources/{source}/categories/{category}/filters
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Response mindestens:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
source_category_status
|
|||
|
|
filters_discovered
|
|||
|
|
filters_verified
|
|||
|
|
filters_unverified
|
|||
|
|
filters_broken
|
|||
|
|
mapping_version
|
|||
|
|
last_scan_at
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 30. Source Adapter Contract
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
class DiscoverySourceAdapter(Protocol):
|
|||
|
|
def load_category_baseline(category_ref): ...
|
|||
|
|
def discover_filter_controls(document): ...
|
|||
|
|
def extract_filter_links(document): ...
|
|||
|
|
def derive_query_diff(baseline_url, filtered_url): ...
|
|||
|
|
def verify_filter_roundtrip(filter_definition, encoded_query): ...
|
|||
|
|
def build_query(criteria, mappings): ...
|
|||
|
|
def fetch_result_page(url): ...
|
|||
|
|
def parse_result_page(document): ...
|
|||
|
|
def parse_product_detail(document): ...
|
|||
|
|
def extract_active_filters(document): ...
|
|||
|
|
def normalize_source_attribute(raw_attribute): ...
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Playwright darf intern als Fallback-Service genutzt werden.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 31. Technische Module
|
|||
|
|
|
|||
|
|
Mindestens logisch getrennt:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
sources/geizhals/
|
|||
|
|
category_loader
|
|||
|
|
taxonomy_parser
|
|||
|
|
filter_encoder
|
|||
|
|
roundtrip_verifier
|
|||
|
|
result_parser
|
|||
|
|
product_detail_parser
|
|||
|
|
|
|||
|
|
discovery/
|
|||
|
|
taxonomy_service
|
|||
|
|
criterion_service
|
|||
|
|
mapping_service
|
|||
|
|
search_execution_service
|
|||
|
|
identity_service
|
|||
|
|
observation_service
|
|||
|
|
diff_service
|
|||
|
|
candidate_service
|
|||
|
|
shortlist_service
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Keine Geizhals-DOM-Logik außerhalb `sources/geizhals`.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 32. Verpflichtender technischer Geizhals-Spike vor finalem Parserbau
|
|||
|
|
|
|||
|
|
Vor der finalen Implementierung der Geizhals-Parser muss der Coding-Agent die echte Notebook-Seite technisch inspizieren.
|
|||
|
|
|
|||
|
|
Der Spike muss dokumentieren:
|
|||
|
|
|
|||
|
|
1. stabile DOM-Anker bzw. semantisch belastbare Selektoren,
|
|||
|
|
2. tatsächliche Struktur der Filtergruppen,
|
|||
|
|
3. Klassifikation der Filter in Typ A/B/C,
|
|||
|
|
4. konkrete href-/Query-Muster mehrerer Filter,
|
|||
|
|
5. tatsächliches Verhalten von Preis, Gewicht und Diagonale,
|
|||
|
|
6. Pagination-Struktur,
|
|||
|
|
7. Produktkarten-/Ergebnisstruktur,
|
|||
|
|
8. verfügbare Felder direkt in der Ergebnisliste,
|
|||
|
|
9. Unterschiede zwischen Listen- und Produktdetaildaten,
|
|||
|
|
10. auffällige dynamische/JS-geladene Bereiche.
|
|||
|
|
|
|||
|
|
Wichtig:
|
|||
|
|
|
|||
|
|
- keine CSS-Klassen, DOM-Pfade oder Selektoren vorab erfinden,
|
|||
|
|
- bevorzugt stabile semantische Attribute oder Source-eigene IDs verwenden,
|
|||
|
|
- fragile rein visuelle Selektoren nur, wenn keine robustere Alternative existiert,
|
|||
|
|
- die Ergebnisse des Spikes müssen unmittelbar in Fixtures und Parser-Tests überführt werden.
|
|||
|
|
|
|||
|
|
Der Spike ist Teil der MVP-Umsetzung, nicht optionale Vorarbeit.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 34. Fixtures
|
|||
|
|
|
|||
|
|
Fixtures sind für reproduzierbare Tests Pflicht.
|
|||
|
|
|
|||
|
|
Mindestens:
|
|||
|
|
|
|||
|
|
- Notebook Baseline Category HTML
|
|||
|
|
- Kategorie mit gesetztem RAM-Filter
|
|||
|
|
- Kategorie mit Preisbereich
|
|||
|
|
- Ergebnisliste Seite 1
|
|||
|
|
- Ergebnisliste Seite 2
|
|||
|
|
- Produktdetailseite
|
|||
|
|
- geänderte DOM-Version / broken fixture
|
|||
|
|
- Captcha/Challenge fixture, soweit technisch sinnvoll
|
|||
|
|
- 429/403 als HTTP-Testzustände
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 34. Unit Tests
|
|||
|
|
|
|||
|
|
Mindestens:
|
|||
|
|
|
|||
|
|
## Taxonomy
|
|||
|
|
- Filtergruppen erkannt
|
|||
|
|
- Filterlabel erkannt
|
|||
|
|
- href extrahiert
|
|||
|
|
- Query-Diff korrekt
|
|||
|
|
- Filteroption gespeichert
|
|||
|
|
- Range-Parameter erkannt
|
|||
|
|
- JS-only Control nutzt Fallback-Pfad
|
|||
|
|
- Mapping Lifecycle korrekt
|
|||
|
|
- Roundtrip -> VERIFIED
|
|||
|
|
- Roundtrip fail -> LEARNED_UNVERIFIED
|
|||
|
|
- ehemals VERIFIED + fail -> BROKEN
|
|||
|
|
|
|||
|
|
## Attribute Mapping
|
|||
|
|
- RAM -> richtige CriterionDefinition
|
|||
|
|
- unbekanntes Label -> UNMAPPED
|
|||
|
|
- Source-ID hat Vorrang vor Label
|
|||
|
|
- Unit-Normalisierung korrekt
|
|||
|
|
|
|||
|
|
## Query Generation
|
|||
|
|
- nur VERIFIED
|
|||
|
|
- deterministisch
|
|||
|
|
- criteria_version/mapping_version gebunden
|
|||
|
|
- INTERNAL ohne Source-Mapping beeinflusst URL nicht
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 35. Integration Tests
|
|||
|
|
|
|||
|
|
Mindestens:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
Taxonomy Fixture
|
|||
|
|
→ DB
|
|||
|
|
→ available-criteria
|
|||
|
|
→ ProjectCriteria
|
|||
|
|
→ Query Generation
|
|||
|
|
→ SearchRun Fixture
|
|||
|
|
→ Result Parse
|
|||
|
|
→ SourceProductReference
|
|||
|
|
→ ProductObservation
|
|||
|
|
→ ProductAttributeObservation
|
|||
|
|
→ Diff
|
|||
|
|
→ Candidate DTO
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 36. Manueller Live-Notebook-Pilot
|
|||
|
|
|
|||
|
|
Der MVP gilt im Source-/Discovery-Bereich **nicht als abgenommen**, solange kein dokumentierter Live-Pilot gegen die echte Geizhals-Notebook-Kategorie erfolgreich durchgeführt wurde.
|
|||
|
|
|
|||
|
|
Der Pilot muss mindestens zeigen:
|
|||
|
|
|
|||
|
|
1. Notebook-Kategorie wird erreichbar geladen.
|
|||
|
|
2. reale Filter werden erkannt.
|
|||
|
|
3. mehrere unterschiedliche Filtertypen werden gelernt.
|
|||
|
|
4. RAM-Filter wird technisch korrekt gelernt.
|
|||
|
|
5. Preisfilter wird technisch korrekt gelernt.
|
|||
|
|
6. Roundtrip-Verifikation funktioniert.
|
|||
|
|
7. `available-criteria` liefert reale Filter.
|
|||
|
|
8. daraus wird eine reale Such-URL generiert.
|
|||
|
|
9. reale Suchergebnisse werden gelesen.
|
|||
|
|
10. mindestens Titel + Produktlink + Preis werden extrahiert.
|
|||
|
|
11. sichtbare Produktattribute werden gemappt.
|
|||
|
|
12. mindestens zwei SearchRuns erzeugen History/Diff.
|
|||
|
|
13. UI kann daraus Kandidaten anzeigen.
|
|||
|
|
|
|||
|
|
Der Live-Pilot muss mindestens einen vollständigen technischen Kettennachweis enthalten:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
echte Filtergruppe erkannt
|
|||
|
|
→ href/Input-Verhalten ermittelt
|
|||
|
|
→ Query-Regel gelernt
|
|||
|
|
→ Roundtrip verifiziert
|
|||
|
|
→ reale Ergebnisse geparst
|
|||
|
|
→ reale Attribute auf CriterionDefinition gemappt
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Live-Pilot wird manuell/dokumentiert ausgeführt, nicht in CI.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 37. Fehlerfälle
|
|||
|
|
|
|||
|
|
## DOM geändert
|
|||
|
|
`DEGRADED` oder `BROKEN`
|
|||
|
|
|
|||
|
|
## Filter nicht verifizierbar
|
|||
|
|
`LEARNED_UNVERIFIED`
|
|||
|
|
|
|||
|
|
## URL-Encoding geändert
|
|||
|
|
früher VERIFIED, Roundtrip scheitert:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
BROKEN
|
|||
|
|
mapping_version++
|
|||
|
|
DiscoverySearch -> OUT_OF_SYNC
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Result Parser unvollständig
|
|||
|
|
teilweise Daten -> `DEGRADED`
|
|||
|
|
keine verwertbaren Produkte -> `BROKEN`
|
|||
|
|
|
|||
|
|
## Source blockiert
|
|||
|
|
`BLOCKED`
|
|||
|
|
|
|||
|
|
## Taxonomie teilweise verfügbar
|
|||
|
|
VERIFIED Kriterien dürfen weiter angeboten werden.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 38. Kein stiller Fallback
|
|||
|
|
|
|||
|
|
Nicht erlaubt:
|
|||
|
|
|
|||
|
|
- Filter-Encoding raten
|
|||
|
|
- unbekannte Source-Attribute automatisch mergen
|
|||
|
|
- unvollständigen Run als vollständigen Run behandeln
|
|||
|
|
- BROKEN Mapping weiterverwenden
|
|||
|
|
- fehlende Kriterien durch frei erfundene Defaults ersetzen
|
|||
|
|
- Candidate Ranking mit nicht belegten Attributen auffüllen
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 39. Definition of Done
|
|||
|
|
|
|||
|
|
## Taxonomy / URL Learning
|
|||
|
|
- [ ] Kategorieseite wird real gelesen.
|
|||
|
|
- [ ] Filter werden aus dem DOM erkannt.
|
|||
|
|
- [ ] href-basierte Filter werden automatisch gelernt.
|
|||
|
|
- [ ] Query-Diff funktioniert.
|
|||
|
|
- [ ] Range-Parameter funktionieren.
|
|||
|
|
- [ ] Playwright-Fallback existiert für JS-only Controls.
|
|||
|
|
- [ ] Roundtrip-Verifikation existiert.
|
|||
|
|
- [ ] Mapping Lifecycle funktioniert.
|
|||
|
|
- [ ] Geizhals-Strukturänderungen werden erkannt.
|
|||
|
|
- [ ] SourceCategory Readiness funktioniert.
|
|||
|
|
|
|||
|
|
## Criteria / Mapping
|
|||
|
|
- [ ] Source-Kriterien erhalten stabile CriterionDefinition-UUIDs.
|
|||
|
|
- [ ] SearchFilter referenziert CriterionDefinition.
|
|||
|
|
- [ ] SourceAttributeMapping referenziert dieselbe CriterionDefinition.
|
|||
|
|
- [ ] UNMAPPED Attribute bleiben sichtbar.
|
|||
|
|
- [ ] INTERNAL-Kriterien möglich.
|
|||
|
|
- [ ] available-criteria basiert auf real gelernter Taxonomie.
|
|||
|
|
|
|||
|
|
## Search / Results
|
|||
|
|
- [ ] Query-Generierung nutzt nur VERIFIED Mappings.
|
|||
|
|
- [ ] Full Pagination funktioniert.
|
|||
|
|
- [ ] PARTIAL wird korrekt erkannt.
|
|||
|
|
- [ ] PARTIAL löst kein MISSING/DISAPPEARED aus.
|
|||
|
|
- [ ] Basisdaten aus Ergebnisliste werden extrahiert.
|
|||
|
|
- [ ] Produktdetailseite kann gezielt angereichert werden.
|
|||
|
|
- [ ] SourceProductReference funktioniert.
|
|||
|
|
- [ ] ProductObservation funktioniert.
|
|||
|
|
- [ ] ProductAttributeObservation funktioniert.
|
|||
|
|
- [ ] Attribute werden normalisiert/historisiert.
|
|||
|
|
|
|||
|
|
## Diff / Candidate
|
|||
|
|
- [ ] NEW / CHANGED / UNCHANGED / MISSING funktionieren.
|
|||
|
|
- [ ] DISAPPEARED nach drei Full Runs.
|
|||
|
|
- [ ] Candidate DTOs werden erzeugt.
|
|||
|
|
- [ ] Ranking nutzt nur vorhandene gemappte Daten.
|
|||
|
|
- [ ] Shortlist funktioniert, wenn Ranking konfiguriert.
|
|||
|
|
|
|||
|
|
## MVP Acceptance
|
|||
|
|
- [ ] Notebook Live-Pilot vollständig dokumentiert.
|
|||
|
|
- [ ] echte Geizhals-Kriterien im UI sichtbar.
|
|||
|
|
- [ ] echte Such-URL aus UI-Kriterien generiert.
|
|||
|
|
- [ ] echte Resultate eingelesen.
|
|||
|
|
- [ ] echte Preise/Attribute angezeigt.
|
|||
|
|
- [ ] History/Diff über mindestens zwei Runs sichtbar.
|
|||
|
|
- [ ] Source-/Discovery-Pfad end-to-end über UI nutzbar.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# 40. Arbeitsauftrag an den Coding-Agenten
|
|||
|
|
|
|||
|
|
Diese Datei ersetzt `buying-agent-source-discovery-spec-v0.2.md` vollständig.
|
|||
|
|
|
|||
|
|
Bitte **noch nicht implementieren**.
|
|||
|
|
|
|||
|
|
Prüfe erneut den aktuellen Repository-Stand gegen:
|
|||
|
|
|
|||
|
|
1. `buying-agent-v0.1-spec-v1.1.md`
|
|||
|
|
2. `buying-agent-architecture-addendum-v1.md`
|
|||
|
|
3. `buying-agent-source-discovery-spec-v0.3.md`
|
|||
|
|
4. `buying-agent-frontend-mvp-spec-v0.3.md`
|
|||
|
|
|
|||
|
|
Prüfe insbesondere:
|
|||
|
|
|
|||
|
|
- ob der Taxonomy-Learning-Algorithmus vollständig implementierbar beschrieben ist,
|
|||
|
|
- ob DOM/href-basierte Discovery ausreicht bzw. welche konkreten Geizhals-Strukturen im Live-Pilot validiert werden müssen,
|
|||
|
|
- ob Playwright-Fallback sauber begrenzt ist,
|
|||
|
|
- ob Query-Diff + Roundtrip-Verifikation widerspruchsfrei sind,
|
|||
|
|
- ob SourceFilterMapping und SourceAttributeMapping klar getrennt sind,
|
|||
|
|
- ob dieselbe CriterionDefinition-ID Search und Result Attributes verbindet,
|
|||
|
|
- ob Readiness-Status ausreichend definiert sind,
|
|||
|
|
- ob zweistufige Datenerfassung (Liste/Detailseite) implementierbar ist,
|
|||
|
|
- ob Full/PARTIAL-Semantik vollständig ist,
|
|||
|
|
- ob der Notebook Live-Pilot als echte MVP-Abnahme genügt,
|
|||
|
|
- ob der technische Geizhals-Spike vor finalem Parserbau ausreichend konkret beschrieben ist,
|
|||
|
|
- ob stabile DOM-Anker und Pagination im Live-DOM ohne erfundene Selektoren verifiziert werden können,
|
|||
|
|
- ob Ergebnislisten-Daten für den MVP tatsächlich ausreichen und welche Felder zwingend Detailseiten benötigen.
|
|||
|
|
|
|||
|
|
Berichte ausschließlich:
|
|||
|
|
|
|||
|
|
- verbleibende echte fachliche Widersprüche,
|
|||
|
|
- fehlende technische Verträge,
|
|||
|
|
- technisch riskante Annahmen,
|
|||
|
|
- notwendige Backend-Änderungen,
|
|||
|
|
- notwendige Frontend-Änderungen.
|
|||
|
|
|
|||
|
|
Keine neue Fachlogik erfinden.
|
|||
|
|
|
|||
|
|
Wenn keine blockierenden Punkte mehr bestehen:
|
|||
|
|
|
|||
|
|
1. aktualisierten vertikalen Implementierungsplan erstellen,
|
|||
|
|
2. jeder Slice muss über die UI prüfbar sein,
|
|||
|
|
3. Taxonomy Learning + Query Generation + Result Parsing früh implementieren,
|
|||
|
|
4. Live-Notebook-Pilot vor Abschluss durchführen,
|
|||
|
|
5. Implementierung erst nach ausdrücklicher Freigabe des Reviews starten.
|