Grundregel (CLAUDE.md): So wie das System sichtbar abbricht, muss es auch sichtbar zeigen, wenn es arbeitet. Jede LLM-Aktion = laufende Aktivität mit Fortschritt + animiertem Indikator; nie wie ein eingefrorenes UI. UI: prominenter LLM-Indikator beim Kategorisieren -- rotierender Spinner, '🤖 LLM kategorisiert … Batch X/Y', Modellname, Fortschrittsbalken (bestimmt oder Shimmer bei unbekanntem Total), Button im Lade-Zustand ('⏳ LLM arbeitet'). prefers-reduced-motion respektiert. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
106 lines
5 KiB
Markdown
106 lines
5 KiB
Markdown
# CLAUDE.md -- Angebots-Übersicht
|
|
|
|
Leitfaden für die Entwicklung dieses Projekts mit Claude Code.
|
|
|
|
## Was dieses Projekt ist
|
|
|
|
Eine sachliche, ortskonkrete Übersicht der wöchentlichen Supermarkt- und
|
|
Discounter-Angebote, sortiert nach Produktgruppen. Kein Hochglanzprospekt
|
|
einzelner Händler, sondern eine händlerübergreifende, neutrale Liste:
|
|
"Was ist diese Woche an Ort X im Angebot, geordnet nach Kategorie."
|
|
|
|
## Das Leitprinzip: der Schnitt
|
|
|
|
Diese Aufgabe sieht nach einer KI-Aufgabe aus. Sie ist es nur zur Hälfte.
|
|
Sie zerfällt in zwei grundverschiedene Teilprobleme, die strikt getrennt
|
|
gehören:
|
|
|
|
1. **Daten holen** (ortskonkret, händlerübergreifend) -- das ist
|
|
**deterministisch**. Hier wird KI eingespart. Ein HTTP-Abruf / Parser
|
|
leistet das exakt und reproduzierbar. Ein LLM würde hier nur Preise und
|
|
Gültigkeiten halluzinieren. Dieser Teil enthält **keine** LLM-Aufrufe.
|
|
|
|
2. **Kategorisieren** (flacher Angebots-Stream → saubere Produktgruppen) --
|
|
das ist die **echte Ambiguität**: "Ist Toffifee Süßwaren? Ist Schwarzwälder
|
|
Schinken Fleisch/Wurst? Gehört eine Blühpflanze überhaupt in eine
|
|
Lebensmittel-Übersicht?" Genau hier ist ein LLM das richtige Werkzeug --
|
|
und nur hier.
|
|
|
|
> Merksatz: Vor KI muss man erst einmal KI einsparen. Aber dort, wo nur sie
|
|
> passt, muss man sie auch einsetzen. Wer die ganze Aufgabe dem Modell gibt,
|
|
> bekommt Fiktion. Wer sie ganz deterministisch löst, scheitert an der
|
|
> Kategorisierung. Richtig ist die Arbeitsteilung.
|
|
|
|
Dieser Schnitt ist nicht verhandelbar. Er ist die Architektur des Projekts,
|
|
nicht eine Stilpräferenz. Vermische die beiden Teile nicht.
|
|
|
|
## Architektur statt Präferenz
|
|
|
|
Qualitätsregeln werden hier **festverdrahtet**, nicht "mitgedacht". Eine Regel,
|
|
die nur als höfliche Bitte im Code steht ("möglichst keine erfundenen Preise"),
|
|
bricht unter Druck still weg. Eine Regel, die als prüfbare Bedingung im
|
|
Datenfluss steht, hält.
|
|
|
|
Konkrete Konsequenzen, die jederzeit gelten:
|
|
|
|
- **Kein Auffüllen.** Wenn für eine Produktgruppe keine belegten Angebote
|
|
vorliegen, wird "keine Daten" ausgegeben -- niemals ein plausibel klingendes
|
|
Beispiel erfunden.
|
|
- **Jedes ausgegebene Angebot ist belegt.** Preis, Gültigkeit und Händler
|
|
stammen aus dem abgerufenen Datensatz, nicht aus dem Modell. Wenn ein Feld
|
|
fehlt, wird es als fehlend markiert, nicht geraten.
|
|
- **Abbruch statt stiller Drift.** Wenn die Datenlage die Anforderung nicht
|
|
hergibt (z. B. kein Treffer für den Ort), bricht das Programm mit einer
|
|
klaren Meldung ab und nennt die Ursache und einen Erweiterungsvorschlag.
|
|
Es liefert kein "irgendwie vollständig aussehendes" Ergebnis.
|
|
|
|
Diese drei Punkte sind der Grund, warum "die KI soll sagen, wenn sie scheitert"
|
|
hier funktioniert: nicht weil das Modell Einsicht hätte, sondern weil eine
|
|
externe, prüfbare Bedingung es erzwingt.
|
|
|
|
## Sichtbarkeit der LLM-Arbeit (Grundregel)
|
|
|
|
So wie das System **sichtbar abbricht**, wenn es scheitert, muss es auch
|
|
**sichtbar zeigen**, wenn es arbeitet. Jede LLM-Aktion (die Kategorisierung)
|
|
wird in der Oberfläche als *laufende Aktivität* dargestellt: erkennbarer
|
|
Fortschritt (z. B. „LLM kategorisiert … Batch X/Y"), ein animierter Indikator,
|
|
und das auslösende Bedien-Element im Lade-Zustand. Eine LLM-Aktion darf **nie**
|
|
wie ein eingefrorenes, totes UI aussehen.
|
|
|
|
Der Grund ist derselbe wie beim Abbruch: Transparenz. Der Nutzer muss jederzeit
|
|
zwischen „arbeitet gerade" und „hängt / ist fertig" unterscheiden können -- sonst
|
|
wirkt selbst ein korrekt laufender Prozess wie ein Defekt. Das gilt für die
|
|
Web-UI (animierter Status + Fortschrittsbalken) ebenso wie für die CLI (laufende
|
|
Status-/Batch-Zeile). Deterministische Schritte (Fetch) dürfen still sein; die
|
|
LLM-Schritte nicht.
|
|
|
|
## Skills
|
|
|
|
Das Projekt nutzt zwei Skills (in `.claude/skills/`), die exakt dem Schnitt
|
|
folgen:
|
|
|
|
- **angebote-fetch** -- deterministischer Datenabruf. Keine LLM-Aufrufe.
|
|
- **angebote-kategorisieren** -- LLM-gestützte Einordnung in Produktgruppen.
|
|
|
|
Lies die jeweilige `SKILL.md`, bevor du an dem zugehörigen Teil arbeitest.
|
|
Die SKILL.md ist die verbindliche Spezifikation für ihren Bereich.
|
|
|
|
## Tech-Kontext
|
|
|
|
- Sprache: **Python** (FastAPI-nah).
|
|
- Öffentlicher Stack-Bezug, falls relevant: FastAPI, Qdrant, A2A.
|
|
- Datenquellen-Kandidaten für den Fetch-Teil: Angebots-Aggregatoren, die nach
|
|
Ort/PLZ filtern (z. B. marktguru, kaufda, MeinProspekt). Discounter wie
|
|
Aldi/Lidl liefern oft nicht an Aggregatoren -- das ist ein bekanntes
|
|
Abdeckungsloch und gehört ehrlich als solches ausgewiesen, nicht kaschiert.
|
|
|
|
## Arbeitsweise mit Claude Code
|
|
|
|
- Beginne jede Aufgabe damit, den relevanten Teil dieses Dokuments und die
|
|
passende SKILL.md zu lesen.
|
|
- Halte den Schnitt sauber: kein LLM-Aufruf im Fetch-Teil, kein heimliches
|
|
Daten-"Reparieren" im Kategorisier-Teil.
|
|
- Wenn eine Anforderung den Schnitt verletzen würde, benenne den Konflikt,
|
|
bevor du Code schreibst.
|
|
- Schreibe Tests für die Architektur-Regeln (kein Auffüllen, Abbruch bei
|
|
leerer Datenlage), nicht nur für den Happy Path.
|