987 lines
20 KiB
Markdown
987 lines
20 KiB
Markdown
|
|
# Buying Agent – Source & Discovery Specification v0.1
|
|||
|
|
|
|||
|
|
**Status:** implementierungsbereit
|
|||
|
|
**Spezifikationsversion:** 0.1
|
|||
|
|
**Stand:** 2026-10-04
|
|||
|
|
**Ergänzt:** `buying-agent-v0.1-spec-v1.1.md` und `buying-agent-architecture-addendum-v1.md`
|
|||
|
|
|
|||
|
|
## 1. Zweck
|
|||
|
|
|
|||
|
|
Diese Spezifikation definiert den ersten real nutzbaren Discovery-Slice des Buying Agents.
|
|||
|
|
|
|||
|
|
Ziel ist, externe Produktsuchen automatisiert aus den Kriterien eines BuyingProjects abzuleiten, regelmäßig auszuführen, Ergebnisse dauerhaft zu verwalten, Veränderungen zu erkennen und die Treffer anschließend durch die eigene Buying-Engine zu bewerten.
|
|||
|
|
|
|||
|
|
Der erste Pilot erfolgt mit **Geizhals, Kategorie Notebooks**. Die Architektur darf jedoch nicht notebook-spezifisch sein.
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
BuyingProject
|
|||
|
|
↓
|
|||
|
|
Canonical Criteria
|
|||
|
|
↓
|
|||
|
|
Source Taxonomy + Mapping
|
|||
|
|
↓
|
|||
|
|
generierte Geizhals-Suche
|
|||
|
|
↓
|
|||
|
|
periodischer SearchRun
|
|||
|
|
↓
|
|||
|
|
persistierte Produkte + Beobachtungen
|
|||
|
|
↓
|
|||
|
|
Diff zum vorherigen Lauf
|
|||
|
|
↓
|
|||
|
|
NEW / CHANGED / MISSING / DISAPPEARED
|
|||
|
|
↓
|
|||
|
|
interne Constraints + Ranking
|
|||
|
|
↓
|
|||
|
|
Shortlist
|
|||
|
|
↓
|
|||
|
|
später Deep Enrichment
|
|||
|
|
↓
|
|||
|
|
später Decision
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 2. Fachliche Leitprinzipien
|
|||
|
|
|
|||
|
|
1. Geizhals ist Discovery-Quelle, nicht Decision Engine.
|
|||
|
|
2. Die eigenen BuyingProject-Kriterien bleiben die fachliche Quelle der Wahrheit.
|
|||
|
|
3. Geizhals wird als möglichst starker Vorfilter genutzt.
|
|||
|
|
4. Nicht auf Geizhals abbildbare Kriterien bleiben interne Kriterien.
|
|||
|
|
5. Discovery, Ranking und Decision bleiben getrennte fachliche Stufen.
|
|||
|
|
6. Externe Filtertaxonomien werden **on demand** gelernt, nicht vollständig vorab katalogisiert.
|
|||
|
|
7. URL-Encoding und Filtersyntax dürfen nicht vom LLM erfunden werden.
|
|||
|
|
8. Produktidentität wird zunächst konservativ und pragmatisch behandelt.
|
|||
|
|
9. Bei Unsicherheit erfolgt keine automatische Zusammenführung.
|
|||
|
|
10. Suchläufe speichern strukturierte Zustände, keine HTML-Archive.
|
|||
|
|
|
|||
|
|
## 3. Scope v0.1
|
|||
|
|
|
|||
|
|
### Enthalten
|
|||
|
|
|
|||
|
|
- Geizhals als erste Discovery-Quelle
|
|||
|
|
- Pilotkategorie Notebooks
|
|||
|
|
- On-Demand-Erfassung der Geizhals-Filtertaxonomie
|
|||
|
|
- Speicherung von Source-Kategorien, Filterdefinitionen, Optionen und Wertebereichen
|
|||
|
|
- Mapping zwischen Canonical Criteria und Source-Filtern
|
|||
|
|
- reproduzierbare Generierung von Geizhals-Such-URLs
|
|||
|
|
- periodische SearchRuns
|
|||
|
|
- vollständige Ergebnisaufnahme einer aktiven Suche
|
|||
|
|
- persistierte SourceProductReferences
|
|||
|
|
- Historisierung sichtbarer Attribute und Preise
|
|||
|
|
- Diffing zwischen Suchläufen
|
|||
|
|
- Status `NEW`, `CHANGED`, `UNCHANGED`, `MISSING`, `DISAPPEARED`
|
|||
|
|
- interne Bewertung der Kandidaten
|
|||
|
|
- konfigurierbare Shortlist, Default 5
|
|||
|
|
- defensives Fehler-/Backoff-Verhalten
|
|||
|
|
- Tests
|
|||
|
|
|
|||
|
|
### Nicht enthalten
|
|||
|
|
|
|||
|
|
- Frontend-Implementierung
|
|||
|
|
- Keepa
|
|||
|
|
- Idealo
|
|||
|
|
- LLM-Automation
|
|||
|
|
- automatische Kaufentscheidung
|
|||
|
|
- Kaufautomatisierung
|
|||
|
|
- Warenkorbmanagement
|
|||
|
|
- Authentifizierung / RBAC
|
|||
|
|
- Multi-User-UI
|
|||
|
|
- automatische Deep-Enrichment-Quellen
|
|||
|
|
- Umgehung von Schutzmechanismen externer Websites
|
|||
|
|
|
|||
|
|
## 4. Source Taxonomy
|
|||
|
|
|
|||
|
|
### 4.1 SourceCategory
|
|||
|
|
|
|||
|
|
Repräsentiert eine Kategorie einer externen Quelle.
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"source": "geizhals",
|
|||
|
|
"external_category_key": "nb",
|
|||
|
|
"name": "Notebooks",
|
|||
|
|
"source_url": "https://geizhals.de/?cat=nb",
|
|||
|
|
"status": "ACTIVE",
|
|||
|
|
"last_verified_at": "2026-10-04T10:00:00Z"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 4.2 SourceFilterDefinition
|
|||
|
|
|
|||
|
|
Beschreibt einen von Geizhals angebotenen Filter.
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"source": "geizhals",
|
|||
|
|
"category_key": "nb",
|
|||
|
|
"external_filter_key": "17023",
|
|||
|
|
"label": "Arbeitsspeicher",
|
|||
|
|
"filter_type": "NUMERIC_THRESHOLD",
|
|||
|
|
"canonical_unit": "GB",
|
|||
|
|
"status": "VERIFIED"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Mögliche `filter_type`-Werte:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
BOOLEAN
|
|||
|
|
ENUM
|
|||
|
|
MULTI_ENUM
|
|||
|
|
NUMERIC_RANGE
|
|||
|
|
NUMERIC_THRESHOLD
|
|||
|
|
TEXT
|
|||
|
|
UNKNOWN
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 4.3 SourceFilterOption
|
|||
|
|
|
|||
|
|
Für diskrete Filterwerte:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"filter_key": "17023",
|
|||
|
|
"source_value": "ab 24GB",
|
|||
|
|
"normalized_value": 24,
|
|||
|
|
"operator": ">=",
|
|||
|
|
"unit": "GB"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 4.4 SourceFilterRange
|
|||
|
|
|
|||
|
|
Für Bereiche bzw. frei parametrisierbare Werte:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"filter_key": "price",
|
|||
|
|
"minimum": 0,
|
|||
|
|
"maximum": null,
|
|||
|
|
"step": 1,
|
|||
|
|
"unit": "EUR"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 5. On-Demand Taxonomy Learning
|
|||
|
|
|
|||
|
|
Die Taxonomie wird nur für tatsächlich verwendete Kategorien gelernt.
|
|||
|
|
|
|||
|
|
Initial:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
Geizhals → Notebooks
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Später z. B.:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
Monitore
|
|||
|
|
Fernseher
|
|||
|
|
Kameras
|
|||
|
|
Rasenmäher
|
|||
|
|
...
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Ablauf
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
Kategorie öffnen
|
|||
|
|
↓
|
|||
|
|
verfügbare Filter erkennen
|
|||
|
|
↓
|
|||
|
|
Filterdefinitionen speichern
|
|||
|
|
↓
|
|||
|
|
Optionen / Bereiche erfassen
|
|||
|
|
↓
|
|||
|
|
URL-Verhalten beobachten
|
|||
|
|
↓
|
|||
|
|
Mapping erzeugen
|
|||
|
|
↓
|
|||
|
|
technisch verifizieren
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Mapping-Status
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
DISCOVERED
|
|||
|
|
LEARNED_UNVERIFIED
|
|||
|
|
VERIFIED
|
|||
|
|
BROKEN
|
|||
|
|
DISABLED
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Nur `VERIFIED` darf für automatische produktive Suchläufe verwendet werden.
|
|||
|
|
|
|||
|
|
### Änderbarkeit
|
|||
|
|
|
|||
|
|
Die gelernten Mappings sollen später im Frontend administrierbar sein.
|
|||
|
|
|
|||
|
|
v0.1 benötigt dafür noch keine UI, aber die Persistenz muss Änderungen ermöglichen, ohne Source-Code anzupassen.
|
|||
|
|
|
|||
|
|
## 6. Canonical Criteria
|
|||
|
|
|
|||
|
|
Canonical Criteria sind die interne fachliche Darstellung eines Suchkriteriums.
|
|||
|
|
|
|||
|
|
Sie sollen sich stark an der Source-Taxonomie orientieren, aber nicht fest an Geizhals gekoppelt sein.
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"key": "ram_gb",
|
|||
|
|
"value_type": "number",
|
|||
|
|
"unit": "GB",
|
|||
|
|
"semantic": "working_memory"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Ein BuyingProject verwendet daraus konkrete Anforderungen:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"criterion": "ram_gb",
|
|||
|
|
"operator": ">=",
|
|||
|
|
"value": 24,
|
|||
|
|
"mandatory": true
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Source Mapping
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
Canonical Criterion
|
|||
|
|
ram_gb >= 24
|
|||
|
|
|
|||
|
|
↓
|
|||
|
|
|
|||
|
|
Geizhals Mapping
|
|||
|
|
filter_key = 17023
|
|||
|
|
source_value = "ab 24GB"
|
|||
|
|
|
|||
|
|
↓
|
|||
|
|
|
|||
|
|
URL fragment
|
|||
|
|
xf=17023_ab+24GB
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Nicht jedes Canonical Criterion muss ein Source Mapping besitzen.
|
|||
|
|
|
|||
|
|
Fehlt ein Mapping:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
Criterion → INTERNAL_ONLY
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Das Kriterium wird dann erst nach Discovery intern bewertet.
|
|||
|
|
|
|||
|
|
## 7. DiscoverySearch
|
|||
|
|
|
|||
|
|
Eine `DiscoverySearch` gehört zu einem BuyingProject und einer Source.
|
|||
|
|
|
|||
|
|
Sie enthält mindestens:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
id
|
|||
|
|
buying_project_id
|
|||
|
|
source_id
|
|||
|
|
source_category_id
|
|||
|
|
criteria_version
|
|||
|
|
mapping_version
|
|||
|
|
generated_url
|
|||
|
|
status
|
|||
|
|
schedule_interval_minutes
|
|||
|
|
broad_result_threshold
|
|||
|
|
created_at
|
|||
|
|
updated_at
|
|||
|
|
last_successful_run_at
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Defaults:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
schedule_interval_minutes = 180
|
|||
|
|
shortlist_size = 5
|
|||
|
|
broad_result_threshold = 300
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 8. Search URL Generation
|
|||
|
|
|
|||
|
|
Die URL wird ausschließlich aus:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
BuyingProject Criteria
|
|||
|
|
+
|
|||
|
|
VERIFIED CriterionMappings
|
|||
|
|
+
|
|||
|
|
SourceCategory
|
|||
|
|
+
|
|||
|
|
konfigurierbaren Source-Optionen
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
generiert.
|
|||
|
|
|
|||
|
|
Die URL ist abgeleitet, nicht die fachliche Quelle der Wahrheit.
|
|||
|
|
|
|||
|
|
Eine Änderung der BuyingProject-Kriterien erzeugt eine neue `criteria_version`.
|
|||
|
|
|
|||
|
|
Wenn die gespeicherte DiscoverySearch nicht mehr zur aktuellen Criteria-/Mapping-Version passt:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
status = OUT_OF_SYNC
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Danach muss die URL neu generiert werden.
|
|||
|
|
|
|||
|
|
## 9. Ergebnisumfang
|
|||
|
|
|
|||
|
|
Aktive Suchläufe werden grundsätzlich vollständig eingelesen.
|
|||
|
|
|
|||
|
|
Keine stille Begrenzung auf Top-N.
|
|||
|
|
|
|||
|
|
Nur vollständige Ergebnismengen erlauben belastbare Erkennung von:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
NEW
|
|||
|
|
CHANGED
|
|||
|
|
MISSING
|
|||
|
|
DISAPPEARED
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### TOO_BROAD
|
|||
|
|
|
|||
|
|
Überschreitet die Treffermenge den konfigurierten Schwellwert:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
result_count > broad_result_threshold
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
wird die Suche markiert:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
TOO_BROAD
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Die Suche darf technisch weiter ausgeführt werden, erzeugt aber einen Hinweis, dass die Source-Kriterien geschärft werden sollten.
|
|||
|
|
|
|||
|
|
Spätere Versionen dürfen breite Suchen differenziert behandeln, etwa seltenere Full-Reconciliation.
|
|||
|
|
|
|||
|
|
## 10. SearchRun
|
|||
|
|
|
|||
|
|
Jeder Abruf erzeugt einen unveränderlichen `SearchRun`.
|
|||
|
|
|
|||
|
|
Felder:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
id
|
|||
|
|
discovery_search_id
|
|||
|
|
criteria_version
|
|||
|
|
mapping_version
|
|||
|
|
generated_url
|
|||
|
|
started_at
|
|||
|
|
finished_at
|
|||
|
|
status
|
|||
|
|
result_count
|
|||
|
|
page_count
|
|||
|
|
http_status_summary
|
|||
|
|
error_code
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Status:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
RUNNING
|
|||
|
|
SUCCESS
|
|||
|
|
PARTIAL
|
|||
|
|
FAILED
|
|||
|
|
BLOCKED
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Es wird kein HTML-Snapshot gespeichert.
|
|||
|
|
|
|||
|
|
## 11. SourceProductReference
|
|||
|
|
|
|||
|
|
Ein Treffer einer externen Quelle wird als `SourceProductReference` dauerhaft angelegt.
|
|||
|
|
|
|||
|
|
Mindestens verfügbare Felder:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
id
|
|||
|
|
source_id
|
|||
|
|
source_category_id
|
|||
|
|
source_url
|
|||
|
|
source_title
|
|||
|
|
manufacturer
|
|||
|
|
model
|
|||
|
|
manufacturer_sku
|
|||
|
|
gtin_ean
|
|||
|
|
normalized_spec_fingerprint
|
|||
|
|
first_seen_at
|
|||
|
|
last_seen_at
|
|||
|
|
status
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Keines dieser Felder außer der internen UUID wird pauschal als garantiert stabil betrachtet.
|
|||
|
|
|
|||
|
|
## 12. Produktidentität – Pilotansatz
|
|||
|
|
|
|||
|
|
v0.1 implementiert bewusst keine komplexe globale Identity Engine.
|
|||
|
|
|
|||
|
|
Für den Notebook-Pilot werden folgende Signale verwendet:
|
|||
|
|
|
|||
|
|
1. vorhandene stabile Source-ID, falls entdeckt
|
|||
|
|
2. GTIN/EAN, falls vorhanden
|
|||
|
|
3. Hersteller-SKU, falls vorhanden
|
|||
|
|
4. kanonische Source-URL
|
|||
|
|
5. normalisierter Titel
|
|||
|
|
6. Hersteller + Modell
|
|||
|
|
7. differenzierende technische Specs
|
|||
|
|
|
|||
|
|
Für Notebooks kann der initiale Fingerprint beispielsweise enthalten:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
manufacturer
|
|||
|
|
model
|
|||
|
|
cpu
|
|||
|
|
ram
|
|||
|
|
storage
|
|||
|
|
display_size
|
|||
|
|
gpu
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Matching-Regeln
|
|||
|
|
|
|||
|
|
- Hohe Sicherheit → existierende SourceProductReference verwenden
|
|||
|
|
- Unsicherheit → `MATCH_UNCERTAIN`
|
|||
|
|
- Keine sichere Übereinstimmung → neue SourceProductReference
|
|||
|
|
- niemals zwei Produkte allein aufgrund ähnlicher Namen automatisch verschmelzen
|
|||
|
|
|
|||
|
|
Die tatsächliche Stabilität der Identitätsmerkmale wird im Notebook-Pilot beobachtet und danach verfeinert.
|
|||
|
|
|
|||
|
|
## 13. Produktbeobachtungen und Historie
|
|||
|
|
|
|||
|
|
Technische Eigenschaften und Preise werden nicht einfach überschrieben.
|
|||
|
|
|
|||
|
|
Jeder SearchRun kann neue Beobachtungen erzeugen.
|
|||
|
|
|
|||
|
|
### ProductObservation
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
source_product_reference_id
|
|||
|
|
search_run_id
|
|||
|
|
observed_at
|
|||
|
|
raw_title
|
|||
|
|
best_price
|
|||
|
|
currency
|
|||
|
|
raw_attributes
|
|||
|
|
normalized_attributes
|
|||
|
|
availability
|
|||
|
|
seller_count
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Veränderte Werte bleiben historisch nachvollziehbar.
|
|||
|
|
|
|||
|
|
Beispiele:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
Preis: 1499 → 1299
|
|||
|
|
RAM: 16 → 32
|
|||
|
|
Anbieterzahl: 8 → 12
|
|||
|
|
Verfügbarkeit geändert
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Bei technischen Attributänderungen muss das System berücksichtigen, dass auch fehlerhaftes Matching vorliegen könnte.
|
|||
|
|
|
|||
|
|
## 14. Diffing
|
|||
|
|
|
|||
|
|
Jeder erfolgreiche Full SearchRun wird mit dem vorherigen erfolgreichen Full SearchRun derselben DiscoverySearch verglichen.
|
|||
|
|
|
|||
|
|
### Status
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
NEW
|
|||
|
|
CHANGED
|
|||
|
|
UNCHANGED
|
|||
|
|
MISSING
|
|||
|
|
DISAPPEARED
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Regeln
|
|||
|
|
|
|||
|
|
**NEW**
|
|||
|
|
Produkt war im vorherigen vollständigen Lauf nicht vorhanden.
|
|||
|
|
|
|||
|
|
**CHANGED**
|
|||
|
|
Produkt vorhanden, aber mindestens ein relevantes beobachtetes Feld hat sich verändert.
|
|||
|
|
|
|||
|
|
**UNCHANGED**
|
|||
|
|
Produkt vorhanden und relevante Felder unverändert.
|
|||
|
|
|
|||
|
|
**MISSING**
|
|||
|
|
Produkt fehlt in einem vollständigen Lauf.
|
|||
|
|
|
|||
|
|
**DISAPPEARED**
|
|||
|
|
Produkt fehlt in drei aufeinanderfolgenden vollständigen Läufen.
|
|||
|
|
|
|||
|
|
Produkte werden nicht gelöscht.
|
|||
|
|
|
|||
|
|
Historie bleibt erhalten.
|
|||
|
|
|
|||
|
|
Ein später erneut auftauchendes Produkt wird reaktiviert und nicht als neues Produkt dupliziert, sofern das Matching ausreichend sicher ist.
|
|||
|
|
|
|||
|
|
## 15. Lifecycle eines BuyingProjects
|
|||
|
|
|
|||
|
|
Während ein BuyingProject aktiv ist:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
ACTIVE
|
|||
|
|
↓
|
|||
|
|
DiscoverySearches laufen
|
|||
|
|
↓
|
|||
|
|
Produkte + Historie wachsen
|
|||
|
|
↓
|
|||
|
|
Ranking + Shortlist
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Nach finalem Kauf:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
PURCHASED
|
|||
|
|
↓
|
|||
|
|
BuyingProject archivieren
|
|||
|
|
↓
|
|||
|
|
Discovery stoppen
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Produkte, Beobachtungen und Evaluationen können mit dem Projekt archiviert bzw. aus aktiven Ansichten entfernt werden.
|
|||
|
|
|
|||
|
|
Keine physische Löschung historischer Daten als Standardverhalten.
|
|||
|
|
|
|||
|
|
## 16. Ranking
|
|||
|
|
|
|||
|
|
Nach Discovery übernimmt ausschließlich die interne Buying Engine.
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
Source Results
|
|||
|
|
↓
|
|||
|
|
interne Hard Constraints
|
|||
|
|
↓
|
|||
|
|
ProductScore
|
|||
|
|
↓
|
|||
|
|
Ranking
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Geizhals-Sortierung darf für die technische Ausgabe genutzt werden, ist aber keine fachliche Ranking-Entscheidung.
|
|||
|
|
|
|||
|
|
## 17. Shortlist
|
|||
|
|
|
|||
|
|
Nach internem Ranking wird eine Shortlist gebildet.
|
|||
|
|
|
|||
|
|
Default:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
shortlist_size = 5
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Der Wert ist pro BuyingProject konfigurierbar.
|
|||
|
|
|
|||
|
|
Shortlist-Kandidaten erhalten:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
SHORTLISTED
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Andere gültige Kandidaten:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
CANDIDATE
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Die spätere Deep-Enrichment-Stufe arbeitet primär auf der Shortlist.
|
|||
|
|
|
|||
|
|
## 18. Deep Enrichment
|
|||
|
|
|
|||
|
|
Nicht Bestandteil dieses Implementierungsslices.
|
|||
|
|
|
|||
|
|
Zielbild:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
Shortlist
|
|||
|
|
↓
|
|||
|
|
Herstellerdaten
|
|||
|
|
Tests
|
|||
|
|
Benchmarks
|
|||
|
|
Reviews
|
|||
|
|
weitere Händler
|
|||
|
|
Preisportale
|
|||
|
|
Qualitätsinformationen
|
|||
|
|
↓
|
|||
|
|
erneute Bewertung
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Discovery darf daher nicht voraussetzen, dass Geizhals alle für die finale Kaufentscheidung notwendigen Daten liefert.
|
|||
|
|
|
|||
|
|
## 19. Discovery, Ranking und Decision
|
|||
|
|
|
|||
|
|
Die drei fachlichen Stufen müssen getrennt bleiben.
|
|||
|
|
|
|||
|
|
### Discovery
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
Welche Produkte kommen potenziell infrage?
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Ranking
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
Welche dieser Produkte passen am besten zum BuyingProject?
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Decision
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
Ist jetzt der richtige Zeitpunkt, Kandidat X zu kaufen?
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Decision wird später u. a. berücksichtigen können:
|
|||
|
|
|
|||
|
|
- Zielpreis
|
|||
|
|
- Deadline
|
|||
|
|
- historische Preisentwicklung
|
|||
|
|
- aktuelle Verfügbarkeit
|
|||
|
|
- Product Fit
|
|||
|
|
- Offer Fit
|
|||
|
|
- Alternativen
|
|||
|
|
- Beschaffungsdringlichkeit
|
|||
|
|
|
|||
|
|
Decision ist nicht Bestandteil dieses Slices.
|
|||
|
|
|
|||
|
|
## 20. Scheduling und defensiver Abruf
|
|||
|
|
|
|||
|
|
Default:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
alle 180 Minuten
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
pro DiscoverySearch konfigurierbar.
|
|||
|
|
|
|||
|
|
### Anforderungen
|
|||
|
|
|
|||
|
|
- keine parallelen Massenabrufe
|
|||
|
|
- Pagination sequenziell oder stark begrenzt parallel
|
|||
|
|
- Caching, wo sinnvoll
|
|||
|
|
- Timeout
|
|||
|
|
- Retry mit exponentiellem Backoff
|
|||
|
|
- Jitter
|
|||
|
|
- Stop bei wiederholten Fehlern
|
|||
|
|
- keine Umgehung von Captchas oder Bot-Schutz
|
|||
|
|
|
|||
|
|
### Reaktion
|
|||
|
|
|
|||
|
|
**HTTP 429**
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
BACKOFF
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**HTTP 403**
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
BLOCKED
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Captcha / Challenge erkannt**
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
BLOCKED
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Automatische weitere Abrufe der betroffenen DiscoverySearch werden pausiert.
|
|||
|
|
|
|||
|
|
Keine Schutzmechanismen umgehen.
|
|||
|
|
|
|||
|
|
## 21. Source Adapter Contract
|
|||
|
|
|
|||
|
|
Ein Discovery-Adapter soll mindestens folgende Fähigkeiten kapseln:
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
class DiscoverySourceAdapter(Protocol):
|
|||
|
|
def discover_taxonomy(category_ref): ...
|
|||
|
|
def build_query(criteria, mappings): ...
|
|||
|
|
def fetch_results(query, pagination): ...
|
|||
|
|
def parse_results(response): ...
|
|||
|
|
def normalize_result(result): ...
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Taxonomy Learning und Result Retrieval dürfen intern getrennte Services sein.
|
|||
|
|
|
|||
|
|
Der Rest des Systems darf keine Geizhals-spezifische URL- oder HTML-Logik kennen.
|
|||
|
|
|
|||
|
|
## 22. Services
|
|||
|
|
|
|||
|
|
Mindestens:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
TaxonomyService
|
|||
|
|
CriterionMappingService
|
|||
|
|
DiscoverySearchService
|
|||
|
|
SearchExecutionService
|
|||
|
|
SourceProductIdentityService
|
|||
|
|
SearchDiffService
|
|||
|
|
CandidateRankingService
|
|||
|
|
ShortlistService
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Keine Businesslogik in n8n.
|
|||
|
|
|
|||
|
|
n8n kann später den Scheduler bzw. externe Events orchestrieren.
|
|||
|
|
|
|||
|
|
## 23. Persistenz
|
|||
|
|
|
|||
|
|
Neue bzw. ergänzende Tabellen können sein:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
source_categories
|
|||
|
|
source_filter_definitions
|
|||
|
|
source_filter_options
|
|||
|
|
source_filter_ranges
|
|||
|
|
criterion_definitions
|
|||
|
|
criterion_mappings
|
|||
|
|
discovery_searches
|
|||
|
|
search_runs
|
|||
|
|
source_product_references
|
|||
|
|
product_observations
|
|||
|
|
project_candidates
|
|||
|
|
candidate_status_history
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Bestehende globale Entitäten aus der Hauptspezifikation bleiben bestehen:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
Product
|
|||
|
|
ProductVariant
|
|||
|
|
Offer
|
|||
|
|
PriceObservation
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Diese dürfen nicht unnötig dupliziert werden.
|
|||
|
|
|
|||
|
|
Der Discovery-Slice kann zunächst mit SourceProductReference arbeiten und erst bei ausreichender Identität eine Verknüpfung zu canonical Product/ProductVariant herstellen.
|
|||
|
|
|
|||
|
|
## 24. API – minimaler Vertrag
|
|||
|
|
|
|||
|
|
### Taxonomie
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
POST /api/v1/sources/geizhals/categories/{category}/discover-taxonomy
|
|||
|
|
GET /api/v1/sources/geizhals/categories/{category}/filters
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Mapping
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
GET /api/v1/criterion-mappings
|
|||
|
|
POST /api/v1/criterion-mappings
|
|||
|
|
PATCH /api/v1/criterion-mappings/{id}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### DiscoverySearch
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
POST /api/v1/discovery-searches
|
|||
|
|
GET /api/v1/discovery-searches/{id}
|
|||
|
|
POST /api/v1/discovery-searches/{id}/generate-query
|
|||
|
|
POST /api/v1/discovery-searches/{id}/run
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### SearchRuns
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
GET /api/v1/discovery-searches/{id}/runs
|
|||
|
|
GET /api/v1/search-runs/{id}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Candidates
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
GET /api/v1/buying-projects/{id}/candidates
|
|||
|
|
GET /api/v1/buying-projects/{id}/shortlist
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Falls BuyingProject im aktuellen Code noch nicht existiert, muss die Integration mit der im Architecture Addendum vorgesehenen nächsten Domain-Erweiterung abgestimmt werden.
|
|||
|
|
|
|||
|
|
## 25. Tests
|
|||
|
|
|
|||
|
|
### Taxonomy
|
|||
|
|
|
|||
|
|
- Notebook-Kategorie kann on demand eingelesen werden
|
|||
|
|
- Filterdefinitionen werden reproduzierbar gespeichert
|
|||
|
|
- Optionen werden nicht dupliziert
|
|||
|
|
- Filteränderungen werden erkannt
|
|||
|
|
- BROKEN-Mapping wird nicht produktiv verwendet
|
|||
|
|
|
|||
|
|
### Query Generation
|
|||
|
|
|
|||
|
|
- identische Criteria + Mapping-Version erzeugen identische URL
|
|||
|
|
- RAM >= 24 erzeugt erwartetes Geizhals-Fragment
|
|||
|
|
- Preisbereich korrekt
|
|||
|
|
- mehrere Filter kombinierbar
|
|||
|
|
- INTERNAL_ONLY-Kriterien verändern die Source-URL nicht
|
|||
|
|
- veraltete Mapping-Version markiert Search als OUT_OF_SYNC
|
|||
|
|
|
|||
|
|
### Search Execution
|
|||
|
|
|
|||
|
|
- Pagination vollständig
|
|||
|
|
- Ergebniszahl korrekt
|
|||
|
|
- TOO_BROAD bei Überschreitung
|
|||
|
|
- kein stilles Top-N-Abschneiden
|
|||
|
|
- 429 → Backoff
|
|||
|
|
- 403/Captcha → BLOCKED
|
|||
|
|
|
|||
|
|
### Identity
|
|||
|
|
|
|||
|
|
- identische SKU wird wiedererkannt
|
|||
|
|
- identische EAN wird wiedererkannt
|
|||
|
|
- gleiche URL wird wiedererkannt
|
|||
|
|
- leicht geänderter Titel bei stabilen Specs wird erkannt
|
|||
|
|
- unsicheres Match erzeugt MATCH_UNCERTAIN
|
|||
|
|
- ähnliche Produktnamen werden nicht automatisch verschmolzen
|
|||
|
|
|
|||
|
|
### Diffing
|
|||
|
|
|
|||
|
|
- neuer Treffer → NEW
|
|||
|
|
- geänderter Preis → CHANGED
|
|||
|
|
- unverändert → UNCHANGED
|
|||
|
|
- einmal fehlend → MISSING
|
|||
|
|
- dreimal fehlend → DISAPPEARED
|
|||
|
|
- Wiederauftauchen reaktiviert bestehende Referenz
|
|||
|
|
|
|||
|
|
### Historie
|
|||
|
|
|
|||
|
|
- Preisänderungen bleiben historisiert
|
|||
|
|
- technische Änderungen bleiben historisiert
|
|||
|
|
- alte SearchRuns bleiben unverändert
|
|||
|
|
- Projektarchivierung löscht keine History
|
|||
|
|
|
|||
|
|
### Ranking
|
|||
|
|
|
|||
|
|
- nur interne Engine bestimmt Ranking
|
|||
|
|
- Source-Sortierung beeinflusst ProductScore nicht
|
|||
|
|
- Shortlistgröße Default 5
|
|||
|
|
- Shortlistgröße konfigurierbar
|
|||
|
|
|
|||
|
|
## 26. Implementierungsreihenfolge
|
|||
|
|
|
|||
|
|
1. Bestehende Implementierung gegen diese Spec und das Architecture Addendum prüfen.
|
|||
|
|
2. Notwendige BuyingProject-/ProjectCandidate-Minimalstruktur ergänzen, falls für Discovery erforderlich.
|
|||
|
|
3. Source Taxonomy + Mapping persistieren.
|
|||
|
|
4. Geizhals-Taxonomie-Pilot für Notebooks implementieren.
|
|||
|
|
5. reproduzierbare Query Generation implementieren.
|
|||
|
|
6. SearchRun + Pagination implementieren.
|
|||
|
|
7. SourceProductReference + ProductObservation implementieren.
|
|||
|
|
8. Identity Pilot implementieren.
|
|||
|
|
9. Diffing implementieren.
|
|||
|
|
10. interne Ranking-Engine anbinden.
|
|||
|
|
11. Shortlist bilden.
|
|||
|
|
12. Scheduling-Schnittstelle vorbereiten bzw. internen Scheduler minimal anbinden.
|
|||
|
|
13. Tests vollständig ausführen.
|
|||
|
|
14. Notebook-Pilot mit realer Suche validieren.
|
|||
|
|
|
|||
|
|
## 27. Definition of Done
|
|||
|
|
|
|||
|
|
- [ ] Geizhals-Notebook-Taxonomie wird on demand eingelesen und gespeichert.
|
|||
|
|
- [ ] Source-Filter und Optionen werden persistiert.
|
|||
|
|
- [ ] Filter-Mappings besitzen Lifecycle-Status.
|
|||
|
|
- [ ] BuyingProject-Kriterien erzeugen reproduzierbar eine Geizhals-URL.
|
|||
|
|
- [ ] Nicht abbildbare Kriterien bleiben INTERNAL_ONLY.
|
|||
|
|
- [ ] Aktive Suche kann vollständig ausgeführt werden.
|
|||
|
|
- [ ] Pagination wird vollständig verarbeitet.
|
|||
|
|
- [ ] TOO_BROAD wird erkannt.
|
|||
|
|
- [ ] SearchRuns werden gespeichert.
|
|||
|
|
- [ ] Produkte werden dauerhaft als SourceProductReference angelegt.
|
|||
|
|
- [ ] Preise und relevante Attribute werden historisiert.
|
|||
|
|
- [ ] NEW / CHANGED / UNCHANGED / MISSING / DISAPPEARED funktionieren.
|
|||
|
|
- [ ] DISAPPEARED erst nach drei aufeinanderfolgenden fehlenden Full Runs.
|
|||
|
|
- [ ] Unsichere Produktidentität wird nicht automatisch verschmolzen.
|
|||
|
|
- [ ] interne Engine bewertet Kandidaten.
|
|||
|
|
- [ ] Shortlist Default 5 funktioniert.
|
|||
|
|
- [ ] Suchfrequenz ist konfigurierbar, Default 180 Minuten.
|
|||
|
|
- [ ] 403/429/Captcha führen zu defensivem Stop/Backoff.
|
|||
|
|
- [ ] keine HTML-Archive.
|
|||
|
|
- [ ] keine Notebook-spezifische Businesslogik außerhalb des Geizhals-Pilotmappings.
|
|||
|
|
- [ ] Tests grün.
|
|||
|
|
- [ ] Ergebnisse des Notebook-Piloten dokumentiert.
|
|||
|
|
|
|||
|
|
# Coding-Agent-Arbeitsauftrag
|
|||
|
|
|
|||
|
|
Verwende als verbindliche Anforderungen:
|
|||
|
|
|
|||
|
|
1. `buying-agent-v0.1-spec-v1.1.md`
|
|||
|
|
2. `buying-agent-architecture-addendum-v1.md`
|
|||
|
|
3. `buying-agent-source-discovery-spec-v0.1.md`
|
|||
|
|
|
|||
|
|
Die neue Source-/Discovery-Spec ergänzt die bestehenden Dokumente. Bei Konflikten in ihrem Fachbereich hat sie Vorrang.
|
|||
|
|
|
|||
|
|
## Vorgehen
|
|||
|
|
|
|||
|
|
### 1. Zuerst analysieren
|
|||
|
|
|
|||
|
|
Prüfe den aktuellen Repository-Stand gegen alle drei Dokumente.
|
|||
|
|
|
|||
|
|
Berichte vor größeren Änderungen:
|
|||
|
|
|
|||
|
|
- welche Teile bereits kompatibel sind,
|
|||
|
|
- welche Domain-/DB-/API-Erweiterungen erforderlich sind,
|
|||
|
|
- ob bestehende Implementierung refaktoriert werden muss,
|
|||
|
|
- welche Änderungen rein additiv möglich sind.
|
|||
|
|
|
|||
|
|
### 2. Minimal integrieren
|
|||
|
|
|
|||
|
|
Implementiere nur den beschriebenen Source-/Discovery-Slice.
|
|||
|
|
|
|||
|
|
Nicht implementieren:
|
|||
|
|
|
|||
|
|
- Frontend
|
|||
|
|
- Keepa
|
|||
|
|
- Idealo
|
|||
|
|
- LLM-Automation
|
|||
|
|
- Auth/RBAC
|
|||
|
|
- Kaufautomatisierung
|
|||
|
|
- Warenkorb
|
|||
|
|
|
|||
|
|
### 3. Besonders beachten
|
|||
|
|
|
|||
|
|
- nicht notebook-spezifisch modellieren
|
|||
|
|
- Geizhals-spezifische Logik ausschließlich im Source Adapter / Mapping Layer
|
|||
|
|
- URL-Encoding nur aus verifizierten Source-Mappings
|
|||
|
|
- vollständige SearchRuns, kein stilles Top-N-Limit
|
|||
|
|
- konservatives Product Matching
|
|||
|
|
- keine automatische Zusammenführung bei Unsicherheit
|
|||
|
|
- alle Änderungen historisieren
|
|||
|
|
- Discovery, Ranking und Decision getrennt halten
|
|||
|
|
|
|||
|
|
### 4. Tests
|
|||
|
|
|
|||
|
|
Führe Unit-, Integration- und End-to-End-Tests aus.
|
|||
|
|
|
|||
|
|
Mindestens:
|
|||
|
|
|
|||
|
|
- Taxonomy Discovery
|
|||
|
|
- Query Generation
|
|||
|
|
- Pagination
|
|||
|
|
- Identity Matching
|
|||
|
|
- Diffing
|
|||
|
|
- History
|
|||
|
|
- Ranking
|
|||
|
|
- Shortlist
|
|||
|
|
- Backoff / BLOCKED
|
|||
|
|
|
|||
|
|
### 5. Abschlussbericht
|
|||
|
|
|
|||
|
|
Dokumentiere:
|
|||
|
|
|
|||
|
|
- geänderte Dateien
|
|||
|
|
- neue Migrationen
|
|||
|
|
- neue API-Endpunkte
|
|||
|
|
- Testergebnisse
|
|||
|
|
- real durchgeführten Notebook-Pilot
|
|||
|
|
- erkannte Geizhals-Eigenheiten
|
|||
|
|
- verbleibende Risiken
|
|||
|
|
- insbesondere Beobachtungen zur Stabilität der Produktidentität
|