README als Lern-Referenz, MIT-Lizenz, CI-Workflow
- README führt jetzt mit der Lehre (der Schnitt, Architektur als prüfbare Regeln, Cache->Mini-Model, ehrliche Grenzen, KI-Sichtbarkeit) + Live-Demo-Link + ehrlicher Datenquellen-Hinweis (Bildungskontext, robots.txt, gekapselter Adapter) - LICENSE (MIT) - .github/workflows/tests.yml: volle Suite (inkl. Playwright-E2E) bei jedem Push -> Badge im README Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
20012a7d46
commit
4ba5f65f39
3 changed files with 185 additions and 75 deletions
30
.github/workflows/tests.yml
vendored
Normal file
30
.github/workflows/tests.yml
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
name: tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Python 3.12 einrichten
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: "3.12"
|
||||||
|
cache: pip
|
||||||
|
|
||||||
|
- name: Abhängigkeiten installieren
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
- name: Playwright-Browser (für den E2E-Test)
|
||||||
|
run: python -m playwright install --with-deps chromium
|
||||||
|
|
||||||
|
- name: Testsuite (Architektur-Regeln, Cache, Web, E2E)
|
||||||
|
env:
|
||||||
|
PYTHONPATH: src
|
||||||
|
run: python -m pytest -q
|
||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 Jeuners
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
209
README.md
209
README.md
|
|
@ -1,32 +1,108 @@
|
||||||
# Angebots-Übersicht
|
# Angebots-Übersicht
|
||||||
|
|
||||||
|
[](https://github.com/Jeuners/timopro/actions/workflows/tests.yml)
|
||||||
|
|
||||||
Ortskonkrete, händlerübergreifende Übersicht wöchentlicher Supermarkt-Angebote,
|
Ortskonkrete, händlerübergreifende Übersicht wöchentlicher Supermarkt-Angebote,
|
||||||
geordnet nach Produktgruppen. Neutral, nicht als Hochglanzprospekt.
|
geordnet nach Produktgruppen. Neutral, nicht als Hochglanzprospekt.
|
||||||
|
|
||||||
## Der Gedanke
|
**Live-Demo:** <https://dev.dillenberg.net/angebote/>
|
||||||
|
|
||||||
Die Aufgabe sieht nach einer KI-Aufgabe aus, ist es aber nur zur Hälfte. Sie
|
---
|
||||||
|
|
||||||
|
## Worum es hier eigentlich geht
|
||||||
|
|
||||||
|
Dieses Repo ist mehr als ein Angebots-Tool. Es ist eine **Referenz dafür, wie
|
||||||
|
man KI *richtig* einsetzt** -- nicht, indem man die ganze Aufgabe einem Modell
|
||||||
|
übergibt, sondern indem man genau trennt, was deterministisch gehört und was
|
||||||
|
nur ein LLM kann. Wer die fünf Punkte unten versteht, hat das Wesentliche.
|
||||||
|
|
||||||
|
### 1. Der Schnitt: Vor KI muss man erst KI einsparen
|
||||||
|
|
||||||
|
Die Aufgabe *sieht* nach einer KI-Aufgabe aus. Sie ist es nur zur Hälfte. Sie
|
||||||
zerfällt in zwei strikt getrennte Teile:
|
zerfällt in zwei strikt getrennte Teile:
|
||||||
|
|
||||||
1. **Daten holen** -- deterministisch, ohne LLM. (Skill `angebote-fetch`)
|
| Teil | Wesen | Werkzeug |
|
||||||
2. **Kategorisieren** -- die echte Ambiguität, hier gehört das LLM hin.
|
|---|---|---|
|
||||||
(Skill `angebote-kategorisieren`)
|
| **Daten holen** (Preise, Gültigkeiten, Händler an Ort X) | reproduzierbar, exakt | **kein LLM** -- HTTP + Parser |
|
||||||
|
| **Kategorisieren** (ist Toffifee Süßware? gehört eine Blühpflanze in eine Lebensmittelliste?) | echte Ambiguität | **LLM** -- und nur hier |
|
||||||
|
|
||||||
Wer alles dem Modell gibt, bekommt erfundene Preise. Wer alles deterministisch
|
> Wer alles dem Modell gibt, bekommt erfundene Preise. Wer alles deterministisch
|
||||||
löst, scheitert an der Einordnung. Die Architektur erzwingt den Schnitt.
|
> löst, scheitert an der Einordnung. Richtig ist die Arbeitsteilung.
|
||||||
|
|
||||||
Der zweite Punkt, der dieses Projekt trägt: Qualitätsregeln stehen nicht als
|
Im Code ist dieser Schnitt nicht verhandelbar: Der Fetch-Teil enthält **keinen
|
||||||
gut gemeinte Bitten im Code, sondern als prüfbare Bedingungen -- kein
|
einzigen LLM-Aufruf** -- ein Test (`tests/`) prüft genau das.
|
||||||
Auffüllen, nur Belegtes, Abbruch statt stiller Drift. Genau dadurch "sagt das
|
|
||||||
System, wenn es scheitert": nicht aus Einsicht des Modells, sondern weil eine
|
|
||||||
externe Bedingung es erzwingt.
|
|
||||||
|
|
||||||
## Entwicklung mit Claude Code
|
### 2. Architektur statt Präferenz: Qualität als prüfbare Bedingung
|
||||||
|
|
||||||
`CLAUDE.md` ist der verbindliche Leitfaden. Die beiden `SKILL.md` in
|
Eine Regel, die nur als höfliche Bitte im Prompt steht („möglichst keine
|
||||||
`.claude/skills/` sind die Spezifikationen der zwei Teile. Claude Code liest
|
erfundenen Preise"), bricht unter Druck still weg. Eine Regel, die als
|
||||||
sie automatisch -- beim Arbeiten am jeweiligen Teil zuerst die passende
|
**prüfbare Bedingung im Datenfluss** steht, hält. Drei solcher Regeln sind hier
|
||||||
SKILL.md lesen.
|
festverdrahtet -- und durch Tests abgesichert:
|
||||||
|
|
||||||
|
- **Kein Auffüllen.** Keine belegten Angebote für eine Gruppe → „keine Daten",
|
||||||
|
niemals ein plausibel klingendes erfundenes Beispiel.
|
||||||
|
- **Nur Belegtes.** Preis, Gültigkeit, Händler stammen aus dem Datensatz, nicht
|
||||||
|
aus dem Modell. Fehlendes Feld = als fehlend markiert, nicht geraten.
|
||||||
|
- **Abbruch statt stiller Drift.** Gibt die Datenlage die Anforderung nicht her
|
||||||
|
(kein Treffer für den Ort), bricht das Programm mit klarer Ursache + Vorschlag
|
||||||
|
ab -- statt ein „irgendwie vollständig aussehendes" Ergebnis zu liefern.
|
||||||
|
|
||||||
|
Deshalb „sagt das System, wenn es scheitert" -- nicht aus Einsicht des Modells,
|
||||||
|
sondern weil eine externe Bedingung es erzwingt.
|
||||||
|
|
||||||
|
### 3. Der Cache macht ein Mini-Model möglich
|
||||||
|
|
||||||
|
Jedes vom LLM (oder per Hand) eingeordnete Produkt landet in einem schnellen
|
||||||
|
SQLite-Cache (`titel+marke → gruppe`, modell-agnostischer Schlüssel). Beim
|
||||||
|
nächsten Lauf überspringen bekannte Produkte das LLM komplett. In der Praxis
|
||||||
|
sinkt die LLM-Last über die Wochen drastisch -- bis ein kleines, billiges
|
||||||
|
Modell (oder gar keins) für die wenigen Neuzugänge reicht. **KI sparsam machen,
|
||||||
|
indem man sie ihre eigene Arbeit zwischenspeichern lässt.**
|
||||||
|
|
||||||
|
### 4. Ehrlich über Grenzen
|
||||||
|
|
||||||
|
- Lokale Modelle (3-9 B über Ollama) liefern für diese Batch-Tool-Calling-Aufgabe
|
||||||
|
oft *kein* zuverlässiges Ergebnis. Das System rät dann nicht -- es markiert
|
||||||
|
alles als „Sonstiges/unsicher". Der Mangel ist sichtbar, nicht kaschiert.
|
||||||
|
- Discounter-Abdeckung wird **datengetrieben** ausgewiesen (beobachtete Händler),
|
||||||
|
nicht behauptet. Die pauschale Annahme „Aldi/Lidl fehlen bei Aggregatoren"
|
||||||
|
wurde von den echten Daten widerlegt -- also steht sie auch nicht im Code.
|
||||||
|
|
||||||
|
### 5. Mensch im Loop + sichtbare KI-Arbeit
|
||||||
|
|
||||||
|
- Unsichere (oder falsch eingeordnete) Angebote sind per Klick korrigierbar; die
|
||||||
|
Korrektur fließt als **stärkstes Cache-Signal** (`modell="manuell"`) zurück --
|
||||||
|
das Produkt ist danach nie wieder unsicher.
|
||||||
|
- Jede LLM-Aktion zeigt **sichtbar Aktivität** (animierter Indikator, Fortschritt,
|
||||||
|
Modellname, Button-Ladezustand). Das ist als Grundregel in `CLAUDE.md`
|
||||||
|
verankert: deterministische Schritte dürfen still laufen, LLM-Schritte nicht.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## So sieht es aus
|
||||||
|
|
||||||
|
Zweistufige UI -- Rohdaten holen (deterministisch), dann kategorisieren (LLM):
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Die nach Produktgruppen gruppierte Ergebnisansicht nach Stufe 2:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Die Datenquelle (ehrlich)
|
||||||
|
|
||||||
|
Der erste echte Adapter spricht die öffentliche Angebots-API von **marktguru**
|
||||||
|
an. Das ist ein **Bildungs-/Recherche-Projekt**, kein Produkt: Der Abruf
|
||||||
|
respektiert `robots.txt`, drosselt sich und bricht bei fehlender Erlaubnis
|
||||||
|
sauber ab (Regel 4), statt blind weiterzulaufen. Die Quellen-Schicht ist hinter
|
||||||
|
einer Adapter-Schnittstelle (`quellen/basis.py`) gekapselt -- ein anderer
|
||||||
|
Anbieter mit ausdrücklicher API-Erlaubnis lässt sich ohne Eingriff in den Rest
|
||||||
|
ergänzen. Wer das Projekt produktiv nutzen will, klärt die Nutzungsbedingungen
|
||||||
|
der jeweiligen Quelle selbst.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
|
|
@ -35,7 +111,7 @@ python3 -m venv .venv && source .venv/bin/activate
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
## Nutzung
|
## Nutzung (CLI)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# PLZ direkt (immer verlässlich):
|
# PLZ direkt (immer verlässlich):
|
||||||
|
|
@ -52,8 +128,8 @@ python -m angebote 60487 --no-llm
|
||||||
Der Kategorisier-Schritt braucht einen LLM-Zugang in der Umgebung --
|
Der Kategorisier-Schritt braucht einen LLM-Zugang in der Umgebung --
|
||||||
`OPENROUTER_API_KEY` (empfohlen, viele Modelle) **oder** `ANTHROPIC_API_KEY`.
|
`OPENROUTER_API_KEY` (empfohlen, viele Modelle) **oder** `ANTHROPIC_API_KEY`.
|
||||||
Fehlt beides und es wird kein `--no-llm` gesetzt, bricht das Programm ehrlich
|
Fehlt beides und es wird kein `--no-llm` gesetzt, bricht das Programm ehrlich
|
||||||
ab, statt ungeordnet weiterzulaufen. Modell überschreiben mit `--modell`,
|
ab. Modell überschreiben mit `--modell`, Anbieter erzwingen mit
|
||||||
Anbieter erzwingen mit `--anbieter openrouter|anthropic`. Modelle auflisten:
|
`--anbieter openrouter|anthropic`. Modelle auflisten:
|
||||||
`python -m angebote --modelle [suchbegriff]`.
|
`python -m angebote --modelle [suchbegriff]`.
|
||||||
|
|
||||||
## Web-UI
|
## Web-UI
|
||||||
|
|
@ -63,30 +139,19 @@ gewahrt. Sie macht den **zweistufigen Ablauf** sichtbar und erzwingt seine
|
||||||
Reihenfolge:
|
Reihenfolge:
|
||||||
|
|
||||||
1. **Stufe 1 -- Rohdaten holen & speichern** (deterministisch, kein LLM, kein
|
1. **Stufe 1 -- Rohdaten holen & speichern** (deterministisch, kein LLM, kein
|
||||||
Key): Abruf für eine PLZ, Persistenz pro PLZ/Woche unter `data/roh/`. Die
|
Key): Abruf für eine PLZ, Persistenz pro PLZ/Woche unter `data/roh/`.
|
||||||
belegte Rohliste ist für sich ansehbar.
|
2. **LLM-Konfiguration** -- Panel mit **Anbieter-Umschalter**:
|
||||||
2. **LLM-Konfiguration** -- separates Panel mit **Anbieter-Umschalter**:
|
|
||||||
- **OpenRouter** (Cloud): Key + Modellauswahl (Liste/Suche/Aktualisieren).
|
- **OpenRouter** (Cloud): Key + Modellauswahl (Liste/Suche/Aktualisieren).
|
||||||
Default `deepseek/deepseek-v4-flash` -- günstig (~1-2 Cent/Lauf), verlässlich.
|
Default `deepseek/deepseek-v4-flash` -- günstig (~1-2 Cent/Lauf), verlässlich.
|
||||||
- **Ollama** (lokal): zeigt die lokal installierten Modelle (kein Key, kein
|
- **Ollama** (lokal): zeigt die lokal installierten Modelle (kein Key, kein
|
||||||
Netz). Ehrlicher Hinweis: kleine lokale Modelle (3-9 B) liefern für diese
|
Netz) -- mit ehrlichem Hinweis zur Tool-Calling-Grenze kleiner Modelle.
|
||||||
Batch-Aufgabe oft kein zuverlässiges Tool-Calling -- dann markiert das
|
|
||||||
System alles als „Sonstiges/unsicher" statt zu raten. Brauchbare lokale
|
|
||||||
Kategorisierung braucht ein starkes tool-fähiges Modell.
|
|
||||||
|
|
||||||
Anbieter + Modell werden gemerkt (`localStorage`) und überleben einen Reload.
|
Anbieter + Modell werden gemerkt (`localStorage`) und überleben einen Reload;
|
||||||
Das gewählte Modell ist dauerhaft sichtbar (Chip im Kopf + im Ergebnis:
|
das gewählte Modell ist dauerhaft sichtbar.
|
||||||
„kategorisiert mit … (anbieter)").
|
|
||||||
3. **Stufe 2 -- Kategorisieren** (LLM): läuft **nur auf den gespeicherten
|
3. **Stufe 2 -- Kategorisieren** (LLM): läuft **nur auf den gespeicherten
|
||||||
Rohdaten** und ist gesperrt, solange keine vorliegen. Ergebnis ist die nach
|
Rohdaten** und ist gesperrt, solange keine vorliegen. Ergebnis ist die nach
|
||||||
Produktgruppen gruppierte Übersicht mit Filtern, Unsicherheits-Markierung
|
Produktgruppen gruppierte Übersicht mit Filtern, Unsicherheits-Markierung,
|
||||||
und belegter Quelle je Angebot.
|
Korrektur-Button und belegter Quelle je Angebot.
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Die gruppierte Ergebnisansicht nach Stufe 2:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Starten:
|
Starten:
|
||||||
|
|
||||||
|
|
@ -97,43 +162,30 @@ OPENROUTER_API_KEY=… PYTHONPATH=. uvicorn angebote.web:app --port 8077
|
||||||
# Browser: http://127.0.0.1:8077/
|
# Browser: http://127.0.0.1:8077/
|
||||||
```
|
```
|
||||||
|
|
||||||
Der Key kann auch im UI-Konfigpanel eingegeben werden (bleibt lokal). Die App
|
Die App ist als **Single-User-Werkzeug für localhost** gedacht -- nicht mit
|
||||||
ist als **Single-User-Werkzeug für localhost** gedacht -- nicht mit
|
`--host 0.0.0.0` ungeschützt ins Netz stellen (keine Auth auf den Endpoints).
|
||||||
`--host 0.0.0.0` ins Netz stellen (keine Auth auf den Endpoints).
|
Die öffentliche Demo läuft hinter einem Reverse-Proxy als Schaufenster.
|
||||||
|
|
||||||
## Stand der Implementierung (ehrlich)
|
## Tests
|
||||||
|
|
||||||
- `requirements.txt` -- **vorhanden**.
|
```bash
|
||||||
- `src/angebote/` -- **vorhanden**: Datenmodell, Adapter-Schnittstelle,
|
python -m pytest -q
|
||||||
Fetch-Orchestrator, Kategorisier-Schritt, Übersicht-Renderer, CLI.
|
```
|
||||||
- `tests/` -- **57 Tests**: Architektur-Regeln (kein Auffüllen, Abbruch bei
|
|
||||||
leerem/unauflösbarem Ort, Daten-Integrität nach Kategorisierung,
|
Die Suite prüft **die Architektur-Regeln**, nicht nur den Happy Path:
|
||||||
geschlossene Kategorienliste, Unsicherheits-Flag, Schnitt-Test "kein LLM im
|
kein Auffüllen, Abbruch bei leerem/unauflösbarem Ort, Daten-Integrität nach der
|
||||||
Fetch-Teil"), Modell-Discovery/-Auswahl (OpenRouter + Ollama), Anbieter-/
|
Kategorisierung, geschlossene Kategorienliste, Unsicherheits-Flag, der
|
||||||
Retry-Logik, content-JSON-Fallback, Rohdaten-Persistenz und die Web-Endpoints.
|
Schnitt-Test „kein LLM im Fetch-Teil", Cache-Verhalten (bekannte Produkte
|
||||||
Laufen offline; ein E2E-Test (Playwright) prüft die Konfig-Persistenz im
|
überspringen das LLM), die Korrektur-zu-Cache-Schleife, Modell-Discovery
|
||||||
Browser.
|
(OpenRouter + Ollama), Anbieter-/Retry-Logik und die Web-Endpoints. Alles läuft
|
||||||
- **Web-UI + zweistufiger Flow** -- vorhanden und live verifiziert: Stufe 1
|
offline (Fakes); ein E2E-Test (Playwright) prüft die Konfig-Persistenz im Browser.
|
||||||
(Fetch + Speichern) und Stufe 2 (LLM-Kategorisierung auf den gespeicherten
|
CI führt die volle Suite bei jedem Push aus (Badge oben).
|
||||||
Rohdaten, gesperrt bis Daten da sind) end-to-end gegen PLZ 60487 getestet.
|
|
||||||
Kategorisierung modellstabil (gpt-oss-120b und gemini-3.1-flash-lite liefern
|
## Entwicklung mit Claude Code
|
||||||
praktisch dieselbe Gruppenverteilung).
|
|
||||||
- **Live bestätigt:** der marktguru-Adapter wurde gegen die echte API getestet
|
`CLAUDE.md` ist der verbindliche Leitfaden. Die beiden `SKILL.md` in
|
||||||
(PLZ 60487) und liefert reale, belegte Angebote (u. a. ALDI SÜD, PENNY, Lidl,
|
`.claude/skills/` sind die Spezifikationen der zwei Teile -- beim Arbeiten am
|
||||||
REWE, Kaufland, nahkauf). Erkenntnisse aus dem echten Lauf, die direkt in den
|
jeweiligen Teil zuerst die passende SKILL.md lesen, den Schnitt nie vermischen.
|
||||||
Code geflossen sind:
|
|
||||||
- `offers/search` ist query-orientiert -- leeres `q` liefert 0 Treffer. Der
|
|
||||||
Adapter aggregiert daher über Kategorie-Seedbegriffe (config) und weist
|
|
||||||
diese **Teilabdeckung** ehrlich aus, statt Vollständigkeit zu behaupten.
|
|
||||||
- Die pauschale Annahme "Aldi/Lidl fehlen bei Aggregatoren" wurde von den
|
|
||||||
Daten **widerlegt** (beide sind enthalten). Die Abdeckung wird deshalb
|
|
||||||
**datengetrieben** ausgewiesen (beobachtete Händler), nicht hartkodiert.
|
|
||||||
- marktgurus Marken-Sentinel `thisisnobrand123` wird als "keine Marke"
|
|
||||||
behandelt, nicht als Beleg durchgereicht.
|
|
||||||
- **Voraussetzungen für den Live-Lauf:** installiertes `requests` (certifi für
|
|
||||||
TLS), erreichbares Netz, von der Seite lesbare API-Schlüssel. Fehlt eines
|
|
||||||
davon, ist das der vorgesehene Abbruchfall (Regel 4) mit *zutreffend*
|
|
||||||
benannter Ursache -- keine Krücke.
|
|
||||||
|
|
||||||
## Struktur
|
## Struktur
|
||||||
|
|
||||||
|
|
@ -141,6 +193,7 @@ ist als **Single-User-Werkzeug für localhost** gedacht -- nicht mit
|
||||||
CLAUDE.md Leitfaden / Architektur
|
CLAUDE.md Leitfaden / Architektur
|
||||||
README.md dieses Dokument
|
README.md dieses Dokument
|
||||||
requirements.txt Abhängigkeiten
|
requirements.txt Abhängigkeiten
|
||||||
|
.github/workflows/tests.yml CI: volle Testsuite bei jedem Push
|
||||||
docs/ Screenshots für dieses Dokument
|
docs/ Screenshots für dieses Dokument
|
||||||
.claude/skills/angebote-fetch/ Spec: deterministischer Datenabruf
|
.claude/skills/angebote-fetch/ Spec: deterministischer Datenabruf
|
||||||
.claude/skills/angebote-kategorisieren/ Spec: LLM-gestützte Einordnung
|
.claude/skills/angebote-kategorisieren/ Spec: LLM-gestützte Einordnung
|
||||||
|
|
@ -154,12 +207,18 @@ src/angebote/ Implementierung
|
||||||
fetch.py Orchestrator (Ort rein, belegte Angebote raus)
|
fetch.py Orchestrator (Ort rein, belegte Angebote raus)
|
||||||
speicher.py Persistenz der belegten Rohdaten (Stufe 1), kein LLM
|
speicher.py Persistenz der belegten Rohdaten (Stufe 1), kein LLM
|
||||||
kategorisieren.py LLM-Schritt hinter Protokoll (OpenRouter/Anthropic), testbar
|
kategorisieren.py LLM-Schritt hinter Protokoll (OpenRouter/Anthropic), testbar
|
||||||
|
produktcache.py schneller SQLite-Cache (titel+marke -> gruppe), kein LLM
|
||||||
modelle.py OpenRouter-Modell-Discovery (Liste/Suche/Top-Free)
|
modelle.py OpenRouter-Modell-Discovery (Liste/Suche/Top-Free)
|
||||||
modellauswahl.py interaktive Modellauswahl (CLI)
|
modellauswahl.py interaktive Modellauswahl (CLI)
|
||||||
uebersicht.py Gruppierung + Rendering (Markdown + JSON-Struktur)
|
uebersicht.py Gruppierung + Rendering (Markdown + JSON-Struktur)
|
||||||
web.py FastAPI-Web-UI (Stufe-1-/Stufe-2-Endpoints)
|
web.py FastAPI-Web-UI (Stufe-1-/Stufe-2-/Korrektur-Endpoints)
|
||||||
web_static/ Frontend (index.html)
|
web_static/ Frontend (index.html)
|
||||||
cli.py / __main__.py CLI-Einstieg
|
cli.py / __main__.py CLI-Einstieg
|
||||||
tests/ 57 Architektur-/Web-/E2E-Tests
|
tests/ Architektur-/Cache-/Web-/E2E-Tests
|
||||||
data/roh/ generierte Rohdaten (ge-ignored)
|
data/roh/ generierte Rohdaten (ge-ignored)
|
||||||
|
data/kategorie_cache.sqlite Produkt->Kategorie-Cache (ge-ignored)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Lizenz
|
||||||
|
|
||||||
|
[MIT](LICENSE).
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue