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
|