BuyingAgent/.specs/buying-agent-source-discovery-spec-v0.1.md
Lars b276758ee1
All checks were successful
Deploy Development / deploy (push) Successful in 1m19s
Test Suite / pytest-backend (push) Successful in 31s
Test Suite / smoke-dev (push) Successful in 0s
Test Suite / frontend-checks (push) Successful in 28s
Stelle den Frontend-MVP und die v0.3-API auf develop bereit, damit Dev API und PWA zusammen deployen.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-04 13:04:59 +02:00

20 KiB
Raw Blame History

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.

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.

{
  "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.

{
  "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:

BOOLEAN
ENUM
MULTI_ENUM
NUMERIC_RANGE
NUMERIC_THRESHOLD
TEXT
UNKNOWN

4.3 SourceFilterOption

Für diskrete Filterwerte:

{
  "filter_key": "17023",
  "source_value": "ab 24GB",
  "normalized_value": 24,
  "operator": ">=",
  "unit": "GB"
}

4.4 SourceFilterRange

Für Bereiche bzw. frei parametrisierbare Werte:

{
  "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:

Geizhals → Notebooks

Später z. B.:

Monitore
Fernseher
Kameras
Rasenmäher
...

Ablauf

Kategorie öffnen
    ↓
verfügbare Filter erkennen
    ↓
Filterdefinitionen speichern
    ↓
Optionen / Bereiche erfassen
    ↓
URL-Verhalten beobachten
    ↓
Mapping erzeugen
    ↓
technisch verifizieren

Mapping-Status

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.

{
  "key": "ram_gb",
  "value_type": "number",
  "unit": "GB",
  "semantic": "working_memory"
}

Ein BuyingProject verwendet daraus konkrete Anforderungen:

{
  "criterion": "ram_gb",
  "operator": ">=",
  "value": 24,
  "mandatory": true
}

Source Mapping

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:

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:

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:

schedule_interval_minutes = 180
shortlist_size = 5
broad_result_threshold = 300

8. Search URL Generation

Die URL wird ausschließlich aus:

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:

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:

NEW
CHANGED
MISSING
DISAPPEARED

TOO_BROAD

Überschreitet die Treffermenge den konfigurierten Schwellwert:

result_count > broad_result_threshold

wird die Suche markiert:

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:

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:

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:

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:

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

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:

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

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:

ACTIVE
    ↓
DiscoverySearches laufen
    ↓
Produkte + Historie wachsen
    ↓
Ranking + Shortlist

Nach finalem Kauf:

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.

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:

shortlist_size = 5

Der Wert ist pro BuyingProject konfigurierbar.

Shortlist-Kandidaten erhalten:

SHORTLISTED

Andere gültige Kandidaten:

CANDIDATE

Die spätere Deep-Enrichment-Stufe arbeitet primär auf der Shortlist.

18. Deep Enrichment

Nicht Bestandteil dieses Implementierungsslices.

Zielbild:

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

Welche Produkte kommen potenziell infrage?

Ranking

Welche dieser Produkte passen am besten zum BuyingProject?

Decision

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:

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

BACKOFF

HTTP 403

BLOCKED

Captcha / Challenge erkannt

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:

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:

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:

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:

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

POST /api/v1/sources/geizhals/categories/{category}/discover-taxonomy
GET  /api/v1/sources/geizhals/categories/{category}/filters

Mapping

GET   /api/v1/criterion-mappings
POST  /api/v1/criterion-mappings
PATCH /api/v1/criterion-mappings/{id}

DiscoverySearch

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

GET /api/v1/discovery-searches/{id}/runs
GET /api/v1/search-runs/{id}

Candidates

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