maboto/CLAUDE.md
Jeuner 20012a7d46 LLM-Arbeit sichtbar machen + als Grundregel verankern
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>
2026-06-03 21:06:36 +02:00

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.