|
|
||
|---|---|---|
| .gitea/workflows | ||
| .specs | ||
| app | ||
| backend | ||
| docs | ||
| frontend | ||
| scripts | ||
| .dockerignore | ||
| .env.example | ||
| .gitattributes | ||
| .gitignore | ||
| docker-compose.dev-env.yml | ||
| docker-compose.test.yml | ||
| docker-compose.yml | ||
| README.md | ||
BuyingAgent
Backend für einen generischen Buying-/Deal-Agenten. Das System bewertet, wie gut eine Produktvariante zu einem Profil passt und wie attraktiv ein konkretes Angebot preislich ist. Es kauft nicht.
v0.1 ist ein Walking Skeleton: Profil → Produkt → Variante → Angebot → Preisbeobachtungen → Evaluation. Notebooks sind der erste Anwendungsfall, nicht das Datenmodell.
Vorgaben: docs/buying-agent-v0.1-spec-v1.1.md (fachlich) und docs/buying-agent-architecture-addendum-v1.md (Architektur, hat Vorrang bei Strukturfragen).
Monorepo
backend/ System of Record: Domain, Scoring, Persistenz, /api/v1
frontend/ Platzhalter — keine UI in v0.1
docs/ Spezifikationen und Deployment
docker-compose.yml
Entwicklung auf develop. Push nach develop baut Dev auf dem Raspberry Pi. Merge --no-ff nach main deployt Production. Details: docs/DEPLOYMENT.md.
Stack
Python 3.13, FastAPI, Pydantic 2, SQLAlchemy 2 (sync, psycopg 3), Alembic, PostgreSQL 17, pytest, Docker Compose. Build-Tool: uv 0.10.11.
Abhängigkeiten: HTTP → Services → Domain/Scoring → Repositories → PostgreSQL. domain und scoring importieren weder FastAPI noch SQLAlchemy.
Lokal starten
Copy-Item .env.example .env
docker compose up --build -d
docker compose exec api python -m scripts.demo
cp .env.example .env
docker compose up --build -d
docker compose exec api python -m scripts.demo
API: http://127.0.0.1:8000
OpenAPI: http://127.0.0.1:8000/docs
Live: GET /health/live
Ready: GET /health/ready
Die Demo legt Definitionen, Profil und die Fälle A–D über HTTP an und wertet sie mit as_of=2026-10-03T12:00:00Z aus. Wiederholter Lauf erzeugt keine Duplikate.
Tests
Lokal, isolierter Stack (tmpfs, kein Dev-/Prod-Volume):
docker compose -f docker-compose.test.yml up --build --abort-on-container-exit --exit-code-from api
Nur Unit-Tests (ohne PostgreSQL), im Verzeichnis backend/:
pytest tests/unit -ra --tb=short
Auf dem Server nach Dev-Deploy (Gitea test.yml): pytest gegen buyingagent_test auf dem Dev-Postgres plus Smoke gegen http://192.168.2.49:8095/health/ready.
Datenmodell (global)
Product, ProductVariant, Offer, PriceObservation, AttributeDefinition und Source sind systemweit und haben keine user_id/workspace_id. Workspace, BuyingProject und Benutzer kommen später und dürfen diese Daten nicht duplizieren.
Profile und Evaluationen können bereits parallel existieren; es gibt kein globales „aktuelles Kaufvorhaben“.
Scoring generic-v1
- Preference-Strategie nur
numeric_linear. Unbekannte Strategien werden beim Profil abgewiesen. product_eligibility,offer_eligibilityundoverall_eligibilitysind getrennt.- PriceScore ist eine reine Funktion über
current_price,reference_historyundbudget. v0.1 setztprice_reference_scope=offer. - DealScore nur bei
overall_eligibility=ELIGIBLEund vorhandenem PriceScore. - Klassifikation am ungerundeten DealScore; Anzeige auf zwei Dezimalen,
ROUND_HALF_UP. - Geld intern
Decimal/NUMERIC(12,2), in der API als Dezimalstring.
Referenzfall A (synthetisch): ProductScore 90.21, PriceScore 98.24, DealScore 93.42, EXCEPTIONAL. Das ist keine Marktaussage.
Fehler
Einheitliches Format {error: {code, message, details}}. 404 fehlende ID, 409 Konflikt/abweichender Wiederholungsimport, 422 ungültige Payload. Eine fachlich negative Evaluation ist HTTP 201.
Grenzen von v0.1
Keine Web-UI, keine Authentifizierung, keine Workspaces, kein BuyingProject, kein n8n, keine externen Quellen, keine Auto-Discovery, keine Wechselkurse, keine Variant-/Market-Level-Preisaggregation, keine Plugin-Infrastruktur.
Server
| Umgebung | Branch | Verzeichnis | API |
|---|---|---|---|
| Development | develop |
~/docker/buyingagent-dev |
http://192.168.2.49:8095 |
| Production | main |
~/docker/buyingagent |
http://192.168.2.49:8006 |
Repo: http://192.168.2.144:3000/Lars/BuyingAgent.git