- .gitignore: .claude/docs, rules, commands tracken; settings.local weiter ignorieren - DOCUMENTATION.md: verbindliche Ablage functional/technical/working/issues - .claude/README.md: Agent-Einstieg; GITEA_ISSUES_INDEX aus MCP (Stand 2026-04-08) - Arbeitspapiere von docs/ nach .claude/docs/working/ verschoben - docs/MEMBERSHIP_SYSTEM.md als Stub; kanonisch technical/MEMBERSHIP_SYSTEM.md - CLAUDE.md Pflichtlektüre und Links angepasst; docs/README.md vereinfacht Made-with: Cursor
1791 lines
65 KiB
Markdown
1791 lines
65 KiB
Markdown
# Phase 0b Placeholder Improvements & Missing Values
|
||
|
||
> **Status:** 🔴 Draft - User Input in Progress
|
||
> **Datum:** 29.03.2026
|
||
> **Zweck:** Sammlung fehlender Platzhalter und Verbesserungspotenziale nach Phase 0b
|
||
|
||
---
|
||
|
||
## Executive Summary: Zwei Kritische Gaps (29.03.2026)
|
||
|
||
### 🚨 Gap #1: Quality Label wird nicht berücksichtigt
|
||
**Activity Platzhalter berücksichtigen `quality_label` nicht!**
|
||
|
||
- ❌ **Alle** activity-bezogenen Platzhalter nutzen ALLE Trainings (inkl. poor/excluded)
|
||
- ⚠️ **Betroffene Platzhalter:** `{{activity_summary}}`, `{{activity_detail}}`, `{{trainingstyp_verteilung}}`, `{{training_minutes_week}}`, `{{training_frequency_7d}}`, `{{ability_balance_*}}`, etc.
|
||
- ✅ **Einzige Ausnahme:** `{{quality_sessions_pct}}` (zählt nur excellent/good)
|
||
- 🔧 **Infrastruktur vorhanden:** `quality_filter.py` existiert bereits, wird aber NICHT genutzt
|
||
|
||
**Auswirkung:** Standard-Auswertungen in KI-Prompts zählen schlechte/ausgeschlossene Trainings mit → **verfälschte Analysen**!
|
||
|
||
**Empfohlene Lösung:**
|
||
1. Quality-Filter in `data_layer/activity_metrics.py` einbauen (6-8h)
|
||
2. Neue Platzhalter für Evaluation-Breakdown (3h)
|
||
3. Quality-Weighted Metrics (2h)
|
||
**Teilsumme:** 11-13h
|
||
|
||
---
|
||
|
||
### 🚨 Gap #2: Keine Trainingstyp-spezifischen Platzhalter
|
||
**KI kann nicht nach Trainings-Kategorien differenzieren!**
|
||
|
||
- ❌ **Keine Kategorie-spezifischen Platzhalter** (Kraft, Cardio, Kampfsport getrennt)
|
||
- ❌ **Keine Subcategory-Differenzierung** (Hypertrophie vs. Maximalkraft vs. Kraftausdauer)
|
||
- ❌ **Keine Ruhepausen-Analyse** nach Trainingstyp (48h zwischen Kraft-Einheiten?)
|
||
- ❌ **Keine Muskelerhalt-Logik** (mind. 2x/Woche Kraft?)
|
||
- ❌ **Keine Balance-Analysen** (Cardio:Kraft Ratio, fehlende Kategorien)
|
||
- ✅ **Was existiert:** `{{trainingstyp_verteilung}}` (nur Top 3 Kategorien), `{{ability_balance_*}}`
|
||
|
||
**Auswirkung:** KI kann NICHT coachen zu:
|
||
- "Zu wenig Krafttraining für Muskelerhalt" (kein `{{strength_frequency}}`)
|
||
- "48h Pause zwischen Kraft-Einheiten" (kein `{{days_since_last_strength}}`)
|
||
- "Zu cardio-lastig, mehr Kraft empfohlen" (kein `{{cardio_to_strength_ratio}}`)
|
||
- "Kein Mobility-Training in 30 Tagen" (kein `{{missing_categories}}`)
|
||
|
||
**Empfohlene Lösung:**
|
||
1. Kategorie-spezifische Count/Minutes Platzhalter (4-5h)
|
||
2. Ruhepausen-Analyse nach Kategorie (3h)
|
||
3. Balance-Scores & Ratios (2-3h)
|
||
4. Subcategory-Analysen (2h)
|
||
**Teilsumme:** 11-13h
|
||
|
||
---
|
||
|
||
**GESAMT:** 22-26h Implementierung für beide Gaps
|
||
|
||
---
|
||
|
||
## 0. Governance-Anforderungen & Fit/Gap-Analyse (User Requirements)
|
||
|
||
> **Quelle:** `placeholder_requirements_for_development.md` (29.03.2026)
|
||
> **Zweck:** Professionelle Placeholder-Governance für Prompt-Bibliothek V1
|
||
|
||
### Governance-Prinzipien (verbindlich):
|
||
|
||
1. **Platzhalter = API-Verträge** - keine Ad-hoc-Erfindungen in Prompts
|
||
2. **Keine stillschweigenden Änderungen** - Semantik, Zeitfenster, Einheit stabil
|
||
3. **Fehlende Werte explizit** - `null` oder strukturierter Status, nicht "nicht verfügbar"
|
||
4. **Atomare Platzhalter bevorzugt** - einzelne Felder statt Freitext-Blobs
|
||
5. **Zeitbezug immer eindeutig** - `*_7d`, `*_28d`, `*_90d` im Namen
|
||
6. **JSON vor Freitext** - strukturierte Objekte für komplexe Daten
|
||
7. **Verfügbarkeit trennen** - `*_available` Flags für kritische Felder
|
||
8. **Zwei-Stufen-Architektur** - Komplexe Interpretationen via KI statt hardcodiert ⭐ NEU
|
||
|
||
### Placeholder-Typen (Architektur):
|
||
|
||
#### Typ 1: Atomic Placeholders
|
||
- **Direkt aus Daten** (keine Interpretation)
|
||
- Beispiel: `{{weight_aktuell}}`, `{{training_frequency_7d}}`
|
||
- Implementierung: Python-Funktion → direkter Wert
|
||
|
||
#### Typ 2: Raw Data Placeholders
|
||
- **Strukturierte JSONs für KI-Input** (keine Interpretation)
|
||
- Beispiel: `{{goal_priority_raw_data}}`, `{{constraints_raw_data}}`
|
||
- Implementierung: Python aggregiert Rohdaten → JSON
|
||
- Verwendung: Input für Basis-Prompts
|
||
|
||
#### Typ 3: Interpreted Placeholders
|
||
- **Von KI generiert** (via Basis-Prompt)
|
||
- Beispiel: `{{goal_weighted_priority}}`, `{{main_constraint}}`
|
||
- Implementierung: Basis-Prompt interpretiert Raw Data → strukturierter Output
|
||
- Verwendung: In übergeordneten Prompts
|
||
|
||
### Priorisierte Anforderungen (Sprint 1-3):
|
||
|
||
**Sprint 1 (Must-Have für V1):** 10 Items (P1-P10 + strukturelle)
|
||
**Sprint 2 (Diagnose-Ebene):** 7 Items (P21-P27)
|
||
**Sprint 3 (Verfeinerung):** 6 Items (niedrigere Priorität)
|
||
|
||
---
|
||
|
||
## 0.1 Fit/Gap-Analyse: P1-P27 vs. Aktuelle Datenstruktur
|
||
|
||
### ✅ Vollständig möglich (mit aktueller Struktur):
|
||
|
||
#### P1. `goal_summary_json` ✅
|
||
- **Datenquelle:** `goals` table (Migration 022)
|
||
- **Struktur vorhanden:** id, name, goal_type, status, start_value, target_value, current_value, progress_percentage, target_date, is_primary, focus_contributions
|
||
- **Implementierung:** Neue data_layer Funktion `get_goal_summary_data()` → JSON formatieren
|
||
- **Aufwand:** 2h (Funktion + Platzhalter)
|
||
|
||
#### P2. `focus_area_summary_json` ✅
|
||
- **Datenquelle:** `focus_area_definitions` + `user_focus_area_weights` + `goal_focus_contributions`
|
||
- **Struktur vorhanden:** name, category, user_weight, goal_count, progress per focus area
|
||
- **Implementierung:** Neue data_layer Funktion `get_focus_area_summary_data()`
|
||
- **Aufwand:** 2h
|
||
|
||
#### P3-P4. `*_score_available` Flags ✅
|
||
- **Datenquelle:** Berechnete Scores
|
||
- **Implementierung:** Prüfen ob Score berechnet werden konnte (nicht None)
|
||
- **Aufwand:** 1h (alle Score-Platzhalter erweitern)
|
||
|
||
#### P5. `domain_availability_json` ✅
|
||
- **Datenquelle:** Alle Domain-Tabellen (weight_log, nutrition_log, activity_log, sleep_log, etc.)
|
||
- **Berechnung:**
|
||
```python
|
||
{
|
||
"body": {"available": true, "confidence": "high", "last_entry": "2026-03-29", "days_coverage_28d": 25},
|
||
"nutrition": {"available": true, "confidence": "medium", "last_entry": "2026-03-28", "days_coverage_28d": 18},
|
||
"activity": {"available": true, "confidence": "high", "last_entry": "2026-03-29", "days_coverage_28d": 12},
|
||
"sleep": {"available": true, "confidence": "low", "last_entry": "2026-03-15", "days_coverage_28d": 8},
|
||
"vitals": {"available": true, "confidence": "medium", "last_entry": "2026-03-27", "days_coverage_28d": 15},
|
||
"goals": {"available": true, "confidence": "high", "active_goals": 3},
|
||
"focus_areas": {"available": true, "confidence": "high", "weighted_areas": 5}
|
||
}
|
||
```
|
||
- **Implementierung:** Neue Funktion `calculate_domain_availability()`
|
||
- **Aufwand:** 3-4h (alle Domains prüfen)
|
||
|
||
#### P8-P11. Body Change Availability Flags ✅
|
||
- **Datenquelle:** `weight_log`, `caliper_log`, `circumference_log`
|
||
- **Implementierung:** Prüfen ob genug Datenpunkte für Trend (mind. 2 Messungen, 28d Zeitfenster)
|
||
- **Aufwand:** 1h
|
||
|
||
#### P12. `body_change_summary_json` ✅
|
||
- **Datenquelle:** Phase 0c data_layer/body_metrics.py Funktionen
|
||
- **Struktur:**
|
||
```python
|
||
{
|
||
"weight_trend_28d": {"delta_kg": -2.5, "slope": -0.089, "confidence": "high"},
|
||
"fm_change_28d": {"delta_kg": -3.1, "available": true, "confidence": "medium"},
|
||
"lbm_change_28d": {"delta_kg": 0.6, "available": true, "confidence": "medium"},
|
||
"waist_delta_28d": {"delta_cm": -2.5, "available": true},
|
||
"hip_delta_28d": {"delta_cm": -1.2, "available": true},
|
||
"waist_hip_ratio": 0.85,
|
||
"recomposition_quadrant": "fat_loss_muscle_gain"
|
||
}
|
||
```
|
||
- **Implementierung:** Aggregation bestehender Funktionen
|
||
- **Aufwand:** 2h
|
||
|
||
#### P13. `activity_structure_json` ✅
|
||
- **Datenquelle:** Phase 0c data_layer/activity_metrics.py Funktionen
|
||
- **Struktur:**
|
||
```python
|
||
{
|
||
"volume_weekly_min": 180,
|
||
"frequency_7d": 4,
|
||
"type_distribution": {"cardio": 45, "strength": 35, "mobility": 10, "recovery": 10},
|
||
"quality_sessions_pct": 75,
|
||
"proxy_load_7d": 450,
|
||
"monotony_score": 1.2,
|
||
"strain_score": 540,
|
||
"rest_day_compliance_pct": 85,
|
||
"patterns": ["cardio-heavy", "good-recovery-compliance"]
|
||
}
|
||
```
|
||
- **Implementierung:** Aggregation bestehender Funktionen
|
||
- **Aufwand:** 2h
|
||
|
||
#### P16. `sleep_summary_json` ✅
|
||
- **Datenquelle:** `sleep_log` (Migration 009), Phase 0c recovery_metrics.py
|
||
- **Struktur vorhanden:** duration, segments (deep, rem, light, awake)
|
||
- **Implementierung:** Neue Funktion `get_sleep_summary_data()`
|
||
- **Aufwand:** 2h
|
||
|
||
#### P17. `recovery_summary_json` ✅
|
||
- **Datenquelle:** `vitals_baseline` (RHR, HRV), `sleep_log`, `rest_days`, `activity_log` (load)
|
||
- **Struktur:**
|
||
```python
|
||
{
|
||
"recovery_score": 75,
|
||
"main_drivers": ["good_sleep", "hrv_above_baseline"],
|
||
"rhr_status": "normal",
|
||
"hrv_status": "above_baseline",
|
||
"hrv_vs_baseline_pct": 5.2,
|
||
"rhr_vs_baseline_pct": -3.1,
|
||
"recent_load_interaction": "balanced",
|
||
"confidence": "high"
|
||
}
|
||
```
|
||
- **Implementierung:** Aggregation bestehender recovery_metrics Funktionen
|
||
- **Aufwand:** 3h
|
||
|
||
#### P18. `vitals_summary_json` ✅
|
||
- **Datenquelle:** `vitals_baseline` (Migration 015)
|
||
- **Struktur vorhanden:** resting_hr, hrv, vo2_max, spo2, respiratory_rate
|
||
- **Implementierung:** Neue Funktion `get_vitals_summary_data()`
|
||
- **Aufwand:** 1-2h
|
||
|
||
#### P19-P20. HRV/RHR Availability Flags ✅
|
||
- **Datenquelle:** `vitals_baseline`
|
||
- **Implementierung:** Prüfen ob Baseline berechnet werden kann (mind. 7 Messungen)
|
||
- **Aufwand:** 30min
|
||
|
||
#### P21. `correlation_summary_json` ✅
|
||
- **Datenquelle:** Phase 0c correlations.py Funktionen
|
||
- **Struktur:**
|
||
```python
|
||
{
|
||
"energy_weight_lag": {"best_lag_days": 7, "correlation": 0.68, "confidence": "high"},
|
||
"protein_lbm": {"correlation": 0.52, "confidence": "medium"},
|
||
"load_hrv_lag": {"best_lag_days": 3, "correlation": -0.45, "confidence": "medium"},
|
||
"load_rhr_lag": {"best_lag_days": 2, "correlation": 0.38, "confidence": "low"},
|
||
"sleep_recovery": {"correlation": 0.61, "confidence": "high"}
|
||
}
|
||
```
|
||
- **Implementierung:** Aggregation bestehender Korrelationsfunktionen
|
||
- **Aufwand:** 2h
|
||
|
||
#### P22-P24. Plateau & Drivers ✅
|
||
- **Datenquelle:** Phase 0c correlations.py (`detect_plateau_periods()`, `identify_top_drivers()`)
|
||
- **Struktur:**
|
||
```python
|
||
{
|
||
"plateau_status": "likely", # likely / possible / not_detected / insufficient_data
|
||
"plateau_detected_at": "2026-03-15",
|
||
"plateau_duration_days": 14,
|
||
"top_drivers_positive": [
|
||
{"driver": "protein_adequacy", "impact_score": 0.72},
|
||
{"driver": "sleep_quality", "impact_score": 0.65}
|
||
],
|
||
"top_drivers_negative": [
|
||
{"driver": "training_monotony", "impact_score": -0.58},
|
||
{"driver": "insufficient_strength_volume", "impact_score": -0.42}
|
||
],
|
||
"confidence": "medium"
|
||
}
|
||
```
|
||
- **Implementierung:** Wrapper um bestehende Funktionen + Klassifikation
|
||
- **Aufwand:** 2-3h
|
||
|
||
#### P25-P27. Underfueling Risk ✅
|
||
- **Datenquelle:** `nutrition_log` (kcal), `activity_log` (kcal_active), `weight_log` (trend)
|
||
- **Berechnung:**
|
||
```python
|
||
# Energy Availability = (intake - training_expenditure) / lean_body_mass
|
||
# RED-S Risk: EA < 30 kcal/kg LBM/day
|
||
{
|
||
"underfueling_risk_flag": true,
|
||
"underfueling_risk_reason": "ea_below_threshold",
|
||
"energy_availability_summary": {
|
||
"intake_avg_7d": 1800,
|
||
"training_expenditure_avg_7d": 600,
|
||
"net_availability": 1200,
|
||
"ea_per_kg_lbm": 25.5, # kcal/kg/day
|
||
"deficit_magnitude": "moderate",
|
||
"load_context": "high",
|
||
"recovery_context": "impaired",
|
||
"warning_level": "high",
|
||
"confidence": "medium"
|
||
}
|
||
}
|
||
```
|
||
- **Implementierung:** Neue Funktion `calculate_energy_availability()`
|
||
- **Aufwand:** 3-4h (RED-S Logik komplex)
|
||
|
||
---
|
||
|
||
### ⚠️ Teilweise möglich (braucht Erweiterung):
|
||
|
||
#### P7. `critical_missing_fields_json` ⚠️
|
||
- **Problem:** Braucht Meta-Tracking welche Felder wann erfasst wurden
|
||
- **Was fehlt:** Keine Tabelle die "expected fields" vs. "captured fields" trackt
|
||
- **Lösung:** Heuristik basierend auf entry counts pro Domain
|
||
- **Beispiel:**
|
||
```python
|
||
{
|
||
"critical_missing": [
|
||
{"field": "body_fat_measurements", "last_entry": "2026-02-15", "days_ago": 42, "impact": "high"},
|
||
{"field": "hrv_measurements", "last_entry": null, "days_ago": null, "impact": "medium"},
|
||
{"field": "sleep_tracking", "coverage_28d": 8, "expected": 28, "impact": "medium"}
|
||
],
|
||
"recommendation_priority": ["body_fat_measurements", "hrv_measurements", "sleep_tracking"]
|
||
}
|
||
```
|
||
- **Aufwand:** 3h (Heuristik-Logik)
|
||
|
||
#### P14. `training_quality_score` ⚠️
|
||
- **Was existiert:** `quality_label` per Activity, `quality_sessions_pct` Platzhalter
|
||
- **Was fehlt:** Aggregierter Quality-Score über alle Sessions (gewichtet)
|
||
- **Berechnung:**
|
||
```python
|
||
# Weighted average: excellent=100, good=80, acceptable=60, poor=30, excluded=0
|
||
quality_score = sum(quality_weight × duration) / total_duration
|
||
```
|
||
- **Aufwand:** 1h
|
||
|
||
#### P15. `load_balance_class` ⚠️
|
||
- **Was existiert:** `proxy_internal_load_7d` Platzhalter
|
||
- **Was fehlt:** Klassifikation in low/moderate/high/strained
|
||
- **Berechnung:**
|
||
```python
|
||
if load < 300: "low"
|
||
elif load < 600: "moderate"
|
||
elif load < 900: "high"
|
||
else: "strained"
|
||
```
|
||
- **Aufwand:** 30min
|
||
|
||
---
|
||
|
||
### ✅ Möglich mit Zwei-Stufen-Architektur (Data + KI Interpretation):
|
||
|
||
#### P31. `goal_weighted_priority` ✅ (2-Stufen)
|
||
- **Was existiert:** Goals + Focus Areas + Priorisierungssystem
|
||
- **Architektur:**
|
||
- **Stufe 1 (Data Layer):** `get_goal_priority_raw_data()` → strukturiertes JSON
|
||
```python
|
||
{
|
||
"goals": [
|
||
{
|
||
"id": 1,
|
||
"name": "Gewichtsverlust 85kg",
|
||
"progress_pct": 42,
|
||
"behind_schedule_days": 5,
|
||
"focus_contributions": [
|
||
{"focus_area": "Körpergewicht", "weight": 40}
|
||
],
|
||
"total_focus_weight": 75
|
||
}
|
||
],
|
||
"focus_area_weights": {"Körper": 75, "Aktivität": 20},
|
||
"context": {...}
|
||
}
|
||
```
|
||
- **Stufe 2 (KI-Prompt):** Basis-Prompt "Goal Priority Interpreter" interpretiert Rohdaten
|
||
- Input: `{{goal_priority_raw_data}}`
|
||
- Output: `{{goal_weighted_priority}}` (strukturiertes JSON mit Prioritäten + Rationale)
|
||
- **Aufwand:** 3h (2h Data Layer + 1h Basis-Prompt)
|
||
|
||
#### P32. `main_constraint` ✅ (2-Stufen)
|
||
- **Was existiert:** Activity patterns, frequency, gaps
|
||
- **Architektur:**
|
||
- **Stufe 1:** `get_constraints_raw_data()` → JSON
|
||
```python
|
||
{
|
||
"time_budget": {
|
||
"avg_sessions_per_week": 3.2,
|
||
"longest_gap_days": 4,
|
||
"typical_session_duration_min": 45
|
||
},
|
||
"activity_patterns": {
|
||
"weekday_sessions": 2.5,
|
||
"weekend_sessions": 0.7
|
||
},
|
||
"missing_categories": ["mobility", "recovery"],
|
||
"data_gaps": {...}
|
||
}
|
||
```
|
||
- **Stufe 2:** Basis-Prompt "Constraint Identifier"
|
||
- Input: `{{constraints_raw_data}}`
|
||
- Output: `{{main_constraint}}` (Haupteinschränkung + Typ)
|
||
- **Aufwand:** 2-3h (2h Data Layer + 1h Basis-Prompt)
|
||
|
||
#### P33. `main_strength` ✅ (2-Stufen)
|
||
- **Was existiert:** Tracking compliance, consistency scores, quality metrics
|
||
- **Architektur:**
|
||
- **Stufe 1:** `get_strengths_raw_data()` → JSON
|
||
```python
|
||
{
|
||
"tracking_compliance": {
|
||
"weight_entries_28d": 28,
|
||
"nutrition_entries_28d": 26,
|
||
"activity_entries_28d": 12
|
||
},
|
||
"consistency_scores": {
|
||
"macro_consistency": 85,
|
||
"training_frequency_stability": 92
|
||
},
|
||
"quality_metrics": {
|
||
"quality_sessions_pct": 78,
|
||
"sleep_regularity": 0.85
|
||
},
|
||
"standout_values": [
|
||
{"metric": "protein_adequacy", "value": 95, "percentile": "top_10"}
|
||
]
|
||
}
|
||
```
|
||
- **Stufe 2:** Basis-Prompt "Strength Identifier"
|
||
- Input: `{{strengths_raw_data}}`
|
||
- Output: `{{main_strength}}` (Hauptstärke + Begründung)
|
||
- **Aufwand:** 2-3h (2h Data Layer + 1h Basis-Prompt)
|
||
|
||
#### P34. `next_best_actions` ⚠️ (2-Stufen, teilweise)
|
||
- **Was existiert:** Gaps, low scores, fehlende Kategorien
|
||
- **Architektur:**
|
||
- **Stufe 1:** `get_improvement_opportunities_raw_data()` → JSON
|
||
```python
|
||
{
|
||
"gaps": [
|
||
{"domain": "activity", "gap": "no_strength_training_7d", "severity": "high"},
|
||
{"domain": "nutrition", "gap": "protein_below_target", "severity": "medium"}
|
||
],
|
||
"low_scores": [
|
||
{"metric": "mobility_sessions_28d", "value": 0, "target": 4}
|
||
],
|
||
"quick_wins": [
|
||
{"opportunity": "add_1_strength_session", "effort": "low", "expected_impact": "medium"}
|
||
],
|
||
"high_impact": [
|
||
{"opportunity": "increase_protein_20g", "effort": "medium", "expected_impact": "high"}
|
||
]
|
||
}
|
||
```
|
||
- **Stufe 2:** Basis-Prompt "Action Recommender"
|
||
- Input: `{{improvement_opportunities_raw_data}}`
|
||
- Output: `{{next_best_actions}}` (Top 3 Actions mit Begründung)
|
||
- **Problem:** Braucht domänenspezifisches Wissen (z.B. welche Proteinmenge sinnvoll)
|
||
- **Aufwand:** 3-5h (3h Data Layer + 2h Basis-Prompt mit Domänen-Wissen)
|
||
|
||
---
|
||
|
||
### ❌ Nicht möglich / Redundant:
|
||
|
||
#### P6. `domain_confidence_json` ❌ REDUNDANT
|
||
- **Problem:** Überschneidung mit P5 `domain_availability_json`
|
||
- **Lösung:** In P5 integrieren (bereits confidence per domain)
|
||
- **Aufwand:** 0h
|
||
|
||
#### P30. `blood_pressure_summary_json` ⚠️ OPTIONAL (Sprint 3)
|
||
- **Was existiert:** `blood_pressure_log` table (Migration 015)
|
||
- **Was fehlt:** Aggregationsfunktion für BP summary
|
||
- **Berechnung:**
|
||
```python
|
||
{
|
||
"mean_systolic_28d": 125,
|
||
"mean_diastolic_28d": 82,
|
||
"category": "elevated", # WHO/ISH classification
|
||
"contexts": {"nüchtern": 15, "nach_essen": 8, "nach_training": 5},
|
||
"trend_28d": "stable",
|
||
"irregularity_count": 2,
|
||
"afib_detected": false,
|
||
"confidence": "high"
|
||
}
|
||
```
|
||
- **Aufwand:** 2h (neue data_layer Funktion)
|
||
- **Empfehlung:** Sprint 3 (niedrigere Priorität als P31-P34)
|
||
|
||
---
|
||
|
||
### 📊 Zusammenfassung Fit/Gap (aktualisiert):
|
||
|
||
| Kategorie | Anzahl | Aufwand | Details |
|
||
|-----------|--------|---------|---------|
|
||
| ✅ Vollständig möglich (direkt) | 20 Items | 35-40h | P1-P5, P8-P13, P16-P27 |
|
||
| ✅ Möglich (2-Stufen-Architektur) | 4 Items | 10-14h | P31-P34 (Data Layer + KI) |
|
||
| ⚠️ Teilweise möglich | 3 Items | 4-5h | P7, P14, P15 |
|
||
| ❌ Redundant / Optional | 2 Items | 2h | P6 (redundant), P30 (optional) |
|
||
| **TOTAL MÖGLICH** | **27 Items** | **49-59h** | **Alle User Requirements umsetzbar!** |
|
||
|
||
**Architektur-Verbesserung durch 2-Stufen-Ansatz:**
|
||
- ✅ **P31-P34 von "nicht möglich" → "möglich"** durch intelligente Architektur
|
||
- ✅ **Gleicher Aufwand** wie ursprünglich geschätzt (10-14h)
|
||
- ✅ **Bessere Qualität:** Flexibler, wartbarer, erweiterbarer
|
||
- ✅ **Nutzt bestehendes System:** Unified Prompt System + Basis-Prompts
|
||
|
||
---
|
||
|
||
## 0.2 Governance-Implementierung
|
||
|
||
### Konkrete Maßnahmen für Governance-Regeln:
|
||
|
||
#### G1-G2: API-Stabilität & Keine Semantikänderungen
|
||
**Maßnahme:** Placeholder Versioning System einführen
|
||
```python
|
||
PLACEHOLDER_MAP_V1 = {
|
||
'{{weight_aktuell}}': {
|
||
'resolver': get_latest_weight,
|
||
'version': '1.0.0',
|
||
'semantic': 'latest weight entry, no averaging',
|
||
'unit': 'kg',
|
||
'timeframe': 'latest',
|
||
'fallback': 'nicht verfügbar'
|
||
},
|
||
# ...
|
||
}
|
||
```
|
||
**Aufwand:** 3h (Metadata-System + alle Platzhalter dokumentieren)
|
||
|
||
#### G3: Fehlende Werte explizit
|
||
**Maßnahme 1:** Alle "nicht verfügbar" Strings durch `null` ersetzen
|
||
**Maßnahme 2:** Neue Platzhalter mit `*_available` Flags
|
||
```python
|
||
'{{goal_progress_score}}': 75,
|
||
'{{goal_progress_score_available}}': true,
|
||
'{{goal_progress_score_reason}}': null # or "insufficient_goals"
|
||
```
|
||
**Aufwand:** 2h (alle betroffenen Platzhalter umstellen)
|
||
|
||
#### G4: JSON vor Freitext
|
||
**Maßnahme:** Alle Summary-Felder erhalten strukturierte JSON-Pendants
|
||
- `{{activity_summary}}` bleibt → + `{{activity_structure_json}}`
|
||
- `{{caliper_summary}}` bleibt → + `{{body_change_summary_json}}`
|
||
**Aufwand:** Bereits in P1-P27 enthalten
|
||
|
||
#### G5: Zeitfenster im Namen
|
||
**Maßnahme:** Alle zeitabhängigen Platzhalter prüfen und ggf. umbenennen
|
||
```python
|
||
# ❌ Unklar:
|
||
'{{weight_trend}}' # 7d? 28d? 90d?
|
||
|
||
# ✅ Klar:
|
||
'{{weight_trend_28d}}'
|
||
'{{weight_slope_28d}}'
|
||
```
|
||
**Aufwand:** 2h (Audit + Renaming + Migration)
|
||
|
||
#### G6: Verfügbarkeit trennen
|
||
**Maßnahme:** Systematisch alle kritischen Platzhalter mit Flags ergänzen
|
||
- Body: `{{fm_28d_change_available}}`, `{{lbm_28d_change_available}}`, etc.
|
||
- Vitals: `{{hrv_vs_baseline_available}}`, `{{rhr_vs_baseline_available}}`
|
||
- Goals: `{{goal_progress_score_available}}`
|
||
**Aufwand:** Bereits in P3-P4, P8-P11, P19-P20 enthalten
|
||
|
||
#### G7: Keine Ad-hoc-Platzhalter
|
||
**Maßnahme:** Placeholder Approval Process einführen
|
||
1. Neuer Platzhalter → Request in PHASE_0B_IMPROVEMENTS.md
|
||
2. Bewertung gegen Governance-Regeln
|
||
3. Implementierung nur nach Approval
|
||
4. Dokumentation in placeholder_catalog aktualisieren
|
||
**Aufwand:** 1h (Process-Dokumentation)
|
||
|
||
#### G8: Zwei-Stufen-Architektur für komplexe Interpretationen ⭐ NEU
|
||
**Maßnahme:** Komplexe Logik NICHT hardcoded, sondern via KI-Interpretation
|
||
|
||
**Wann Zwei-Stufen-Ansatz nutzen:**
|
||
- Interpretation braucht Kontext-Verständnis
|
||
- Logik ist nicht eindeutig regelbasiert
|
||
- Flexibilität wichtiger als Performance
|
||
- Domänen-Wissen erforderlich
|
||
|
||
**Implementierungs-Pattern:**
|
||
1. **Data Layer:** `get_<topic>_raw_data()` → strukturiertes JSON (Typ 2 Placeholder)
|
||
2. **Basis-Prompt:** Interpretiert Rohdaten → strukturierter Output (Typ 3 Placeholder)
|
||
3. **Verwendung:** Übergeordnete Prompts nutzen Typ 3 Placeholder
|
||
|
||
**Beispiel:**
|
||
```python
|
||
# Data Layer (Python)
|
||
def get_goal_priority_raw_data(profile_id):
|
||
return {
|
||
"goals": [...],
|
||
"focus_weights": {...},
|
||
"context": {...}
|
||
}
|
||
|
||
# Platzhalter Typ 2 (Raw Data)
|
||
'{{goal_priority_raw_data}}': lambda pid: json.dumps(get_goal_priority_raw_data(pid))
|
||
|
||
# Basis-Prompt (KI)
|
||
Name: "Goal Priority Interpreter"
|
||
Input: {{goal_priority_raw_data}}
|
||
Output: JSON mit priority_ranking
|
||
|
||
# Platzhalter Typ 3 (Interpreted)
|
||
'{{goal_weighted_priority}}': <von KI generiert via Basis-Prompt>
|
||
```
|
||
|
||
**Aufwand:** 0h (bereits in P31-P34 enthalten)
|
||
|
||
**Vorteile:**
|
||
- ✅ Flexibler: Prompt-Änderung ohne Code-Deploy
|
||
- ✅ Wartbarer: Klare Trennung Data vs. Interpretation
|
||
- ✅ Erweiterbarer: Gleiche Rohdaten für mehrere Prompts nutzbar
|
||
- ✅ Testbarer: Rohdaten-Struktur separat testbar
|
||
|
||
---
|
||
|
||
## 0.3 Sprint-Planung (User Requirements integriert)
|
||
|
||
### Sprint 1: Must-Have für V1 (20 Items, 35-40h)
|
||
|
||
**Governance & Infrastructure (8h):**
|
||
1. Placeholder Versioning System (3h)
|
||
2. "nicht verfügbar" → null Migration (2h)
|
||
3. Zeitfenster-Audit & Renaming (2h)
|
||
4. Approval Process Dokumentation (1h)
|
||
|
||
**Strukturierte JSONs (15-18h):**
|
||
- P1: goal_summary_json (2h)
|
||
- P2: focus_area_summary_json (2h)
|
||
- P5: domain_availability_json (3-4h)
|
||
- P12: body_change_summary_json (2h)
|
||
- P13: activity_structure_json (2h)
|
||
- P16: sleep_summary_json (2h)
|
||
- P17: recovery_summary_json (3h)
|
||
- P18: vitals_summary_json (1-2h)
|
||
|
||
**Availability Flags (3h):**
|
||
- P3-P4: Score availability flags (1h)
|
||
- P8-P11: Body change availability (1h)
|
||
- P19-P20: HRV/RHR availability (30min)
|
||
- P7: critical_missing_fields_json (3h) - teilweise
|
||
|
||
**Diagnose & Correlation (5-6h):**
|
||
- P21: correlation_summary_json (2h)
|
||
- P22-P24: plateau_status + drivers (2-3h)
|
||
- P14: training_quality_score (1h) - teilweise
|
||
- P15: load_balance_class (30min) - teilweise
|
||
|
||
**Energy Availability (3-4h):**
|
||
- P25-P27: underfueling risk + energy_availability_summary (3-4h)
|
||
|
||
**Sprint 1 Gesamt:** 34-41h
|
||
|
||
---
|
||
|
||
### Sprint 2: Quality Label + Training Categories (16-19h + 10-13h = 26-32h)
|
||
|
||
**Quality Label Gap (bereits definiert in Sektion 7):**
|
||
- Quality-Filter in activity_metrics.py (6-8h)
|
||
- Evaluation-Breakdown (1h)
|
||
- Placeholder-Updates (2h)
|
||
- Dedizierte Quality-Platzhalter (2h)
|
||
- Poor Sessions Warning (1h)
|
||
|
||
**Training Categories Gap (bereits definiert in Sektion 7):**
|
||
- Category-Specific Metrics (2h)
|
||
- Kategorie-spezifische Platzhalter (3h)
|
||
- Days Since Last Training (2h)
|
||
- Training Balance Calculator (2-3h)
|
||
- Weitere Kategorie-Platzhalter (2h)
|
||
- Subcategory Distribution (3h)
|
||
|
||
**Sprint 2 Gesamt:** 26-32h
|
||
|
||
---
|
||
|
||
### Sprint 3: Zwei-Stufen-Platzhalter + Optional (10-16h)
|
||
|
||
**P31-P34: Zwei-Stufen-Architektur (10-14h):**
|
||
- P31: goal_weighted_priority (3h) - Data Layer + Basis-Prompt
|
||
- P32: main_constraint (2-3h) - Data Layer + Basis-Prompt
|
||
- P33: main_strength (2-3h) - Data Layer + Basis-Prompt
|
||
- P34: next_best_actions (3-5h) - Data Layer + Basis-Prompt (komplex)
|
||
|
||
**Optional (falls gewünscht, 2h):**
|
||
- P30: blood_pressure_summary_json (2h)
|
||
|
||
**Sprint 3 Gesamt:** 12-16h
|
||
|
||
**Besonderheit Sprint 3:**
|
||
- Nutzt Unified Prompt System für KI-Interpretation
|
||
- Basis-Prompts sind wiederverwendbar
|
||
- Flexibler als hardcodierte Logik
|
||
- Kann iterativ verfeinert werden (Prompt-Änderung ohne Code-Deploy)
|
||
|
||
---
|
||
|
||
### Gesamtaufwand (alle 3 Sprints):
|
||
|
||
| Sprint | Inhalt | Aufwand |
|
||
|--------|--------|---------|
|
||
| Sprint 1 | User Requirements P1-P27 + Governance | 34-41h |
|
||
| Sprint 2 | Quality Label + Training Categories | 26-32h |
|
||
| Sprint 3 | Blood Pressure + Goal-Weighted (optional) | 12-16h |
|
||
| **TOTAL** | **Alle Requirements + Beide Gaps** | **72-89h** |
|
||
|
||
---
|
||
|
||
## 1. Fehlende Platzhalter
|
||
|
||
### Kategorie: Körper (Body Metrics)
|
||
|
||
#### `{{placeholder_name}}`
|
||
- **Zweck:** Wofür wird der Wert benötigt?
|
||
- **Datenquelle:** Tabelle/Funktion (z.B. `weight_log`, `body_metrics.py`)
|
||
- **Berechnung:** Wie soll er ermittelt werden?
|
||
- **Format:** Beispiel-Output (z.B. "4.5 kg", "85%", "nicht verfügbar")
|
||
- **Use Case:** In welchem Prompt wird das benötigt?
|
||
|
||
---
|
||
|
||
### Kategorie: Ernährung (Nutrition)
|
||
|
||
#### `{{placeholder_name}}`
|
||
- **Zweck:**
|
||
- **Datenquelle:**
|
||
- **Berechnung:**
|
||
- **Format:**
|
||
- **Use Case:**
|
||
|
||
---
|
||
|
||
### Kategorie: Training / Aktivität
|
||
|
||
#### `{{activity_summary_acceptable_plus}}`
|
||
- **Zweck:** Aktivitäts-Zusammenfassung nur mit mindestens "acceptable" Qualität
|
||
- **Datenquelle:** `activity_log` WHERE quality_label IN ('excellent', 'good', 'acceptable')
|
||
- **Berechnung:** Wie aktuelles `{{activity_summary}}`, aber mit quality_filter
|
||
- **Format:** "8 Einheiten in 14 Tagen (Ø 45 min/Einheit, 2400 kcal gesamt)"
|
||
- **Use Case:** Standard-Auswertungen, die nur valide Trainings berücksichtigen
|
||
|
||
#### `{{activity_summary_excellent}}`
|
||
- **Zweck:** Aktivitäts-Zusammenfassung nur mit "excellent" Qualität
|
||
- **Datenquelle:** `activity_log` WHERE quality_label = 'excellent'
|
||
- **Berechnung:** Wie aktuelles `{{activity_summary}}`, aber nur excellent
|
||
- **Format:** "3 Einheiten in 14 Tagen (Ø 60 min/Einheit, 1200 kcal gesamt)"
|
||
- **Use Case:** Analyse von Top-Trainings, Benchmark für Qualität
|
||
|
||
#### `{{poor_sessions_count}}`
|
||
- **Zweck:** Anzahl schlechter/ausgeschlossener Trainings
|
||
- **Datenquelle:** `activity_log` WHERE quality_label IN ('poor', 'excluded')
|
||
- **Berechnung:** COUNT(*) der poor/excluded sessions (7d oder 28d)
|
||
- **Format:** "2 Sessions" oder "keine schlechten Sessions"
|
||
- **Use Case:** Warnung bei zu vielen schlechten Trainings
|
||
|
||
#### `{{evaluation_breakdown}}`
|
||
- **Zweck:** Verteilung der Trainings nach Evaluation-Stufen
|
||
- **Datenquelle:** `activity_log` GROUP BY quality_label
|
||
- **Berechnung:** COUNT(*) pro quality_label mit Prozentangaben
|
||
- **Format:** "Excellent: 3 (25%), Good: 5 (42%), Acceptable: 3 (25%), Poor: 1 (8%)"
|
||
- **Use Case:** Qualitäts-Übersicht in Coaching-Prompts
|
||
|
||
#### `{{training_minutes_quality}}`
|
||
- **Zweck:** Trainingsminuten nur mit acceptable+ Qualität
|
||
- **Datenquelle:** `activity_log` WHERE quality_label IN ('excellent', 'good', 'acceptable')
|
||
- **Berechnung:** SUM(duration_min) mit quality_filter (7d)
|
||
- **Format:** "180 Minuten" (nur valide Trainings)
|
||
- **Use Case:** WHO-Empfehlung Vergleich (150-300 min/Woche)
|
||
|
||
---
|
||
|
||
### Kategorie: Training / Aktivität - Trainingstyp-spezifisch ⚠️ NEU
|
||
|
||
#### `{{strength_training_count_7d}}`
|
||
- **Zweck:** Anzahl Krafttrainings in letzten 7 Tagen
|
||
- **Datenquelle:** `activity_log` WHERE training_category = 'strength'
|
||
- **Berechnung:** COUNT(*) mit quality='quality' filter (7d)
|
||
- **Format:** "3 Einheiten" oder "keine Krafttrainings"
|
||
- **Use Case:** Muskelerhalt-Check (mind. 2x/Woche empfohlen)
|
||
|
||
#### `{{strength_training_minutes_7d}}`
|
||
- **Zweck:** Trainingsminuten Kraft in letzten 7 Tagen
|
||
- **Datenquelle:** `activity_log` WHERE training_category = 'strength'
|
||
- **Berechnung:** SUM(duration_min) mit quality='quality' filter (7d)
|
||
- **Format:** "135 Minuten" oder "0 Minuten Krafttraining"
|
||
- **Use Case:** Volumen-Check für Muskelerhalt
|
||
|
||
#### `{{cardio_training_count_7d}}`
|
||
- **Zweck:** Anzahl Cardio-Trainings in letzten 7 Tagen
|
||
- **Datenquelle:** `activity_log` WHERE training_category = 'cardio'
|
||
- **Berechnung:** COUNT(*) mit quality='quality' filter (7d)
|
||
- **Format:** "4 Einheiten"
|
||
- **Use Case:** WHO-Empfehlung (3-5x/Woche Cardio)
|
||
|
||
#### `{{cardio_training_minutes_7d}}`
|
||
- **Zweck:** Trainingsminuten Cardio in letzten 7 Tagen
|
||
- **Datenquelle:** `activity_log` WHERE training_category = 'cardio'
|
||
- **Berechnung:** SUM(duration_min) mit quality='quality' filter (7d)
|
||
- **Format:** "180 Minuten"
|
||
- **Use Case:** WHO-Empfehlung (150-300 min/Woche)
|
||
|
||
#### `{{martial_arts_frequency_28d}}`
|
||
- **Zweck:** Kampfsport-Frequenz (Sessions/Woche durchschnittlich über 28d)
|
||
- **Datenquelle:** `activity_log` WHERE training_category = 'martial_arts'
|
||
- **Berechnung:** COUNT(*) / 4 Wochen
|
||
- **Format:** "2.5 Einheiten/Woche" oder "kein Kampfsport in 28 Tagen"
|
||
- **Use Case:** Kampfsport-spezifisches Coaching
|
||
|
||
#### `{{recovery_sessions_count_28d}}`
|
||
- **Zweck:** Anzahl aktiver Erholungseinheiten
|
||
- **Datenquelle:** `activity_log` WHERE training_category = 'recovery'
|
||
- **Berechnung:** COUNT(*) (28d)
|
||
- **Format:** "3 Erholungseinheiten"
|
||
- **Use Case:** Regenerations-Balance prüfen
|
||
|
||
#### `{{mobility_sessions_count_28d}}`
|
||
- **Zweck:** Anzahl Mobility/Dehnung Sessions
|
||
- **Datenquelle:** `activity_log` WHERE training_category = 'mobility'
|
||
- **Berechnung:** COUNT(*) (28d)
|
||
- **Format:** "2 Mobility-Einheiten" oder "keine Mobility-Trainings"
|
||
- **Use Case:** Mobility-Warnung bei 0 Sessions
|
||
|
||
#### `{{days_since_last_strength}}`
|
||
- **Zweck:** Tage seit letzter Krafteinheit
|
||
- **Datenquelle:** `activity_log` WHERE training_category = 'strength'
|
||
- **Berechnung:** CURRENT_DATE - MAX(date)
|
||
- **Format:** "2 Tage" oder ">7 Tage (kritisch!)"
|
||
- **Use Case:** Ruhepausen-Check / Muskelerhalt-Warnung
|
||
|
||
#### `{{days_since_last_cardio}}`
|
||
- **Zweck:** Tage seit letzter Cardio-Einheit
|
||
- **Datenquelle:** `activity_log` WHERE training_category = 'cardio'
|
||
- **Berechnung:** CURRENT_DATE - MAX(date)
|
||
- **Format:** "1 Tag" oder ">5 Tage"
|
||
- **Use Case:** Ausdauer-Kontinuität prüfen
|
||
|
||
#### `{{days_since_last_martial_arts}}`
|
||
- **Zweck:** Tage seit letzter Kampfsport-Einheit
|
||
- **Datenquelle:** `activity_log` WHERE training_category = 'martial_arts'
|
||
- **Berechnung:** CURRENT_DATE - MAX(date)
|
||
- **Format:** "3 Tage" oder ">14 Tage"
|
||
- **Use Case:** Technik-Kontinuität prüfen
|
||
|
||
#### `{{cardio_to_strength_ratio}}`
|
||
- **Zweck:** Verhältnis Cardio zu Kraft (Minuten-basiert, 28d)
|
||
- **Datenquelle:** `activity_log` WHERE training_category IN ('cardio', 'strength')
|
||
- **Berechnung:** cardio_minutes / strength_minutes
|
||
- **Format:** "3:1 (cardio-lastig)" oder "1:2 (kraft-lastig)" oder "balanced"
|
||
- **Use Case:** Balance-Coaching
|
||
|
||
#### `{{training_balance_score}}`
|
||
- **Zweck:** Balance-Score 0-100 basierend auf Kategorie-Verteilung
|
||
- **Datenquelle:** `activity_log` (alle Kategorien)
|
||
- **Berechnung:** Score = 100 - std_dev(category_percentages) × 2
|
||
- **Format:** "75" (Zahl 0-100)
|
||
- **Use Case:** Gesamtbalance bewerten
|
||
|
||
#### `{{missing_training_categories}}`
|
||
- **Zweck:** Liste fehlender Trainings-Kategorien (28d)
|
||
- **Datenquelle:** `activity_log` (alle Kategorien)
|
||
- **Berechnung:** Vergleich [cardio, strength, mobility, recovery] mit tatsächlichen
|
||
- **Format:** "Fehlt: Mobility, Recovery" oder "Alle Kategorien abgedeckt"
|
||
- **Use Case:** Lücken-Identifikation
|
||
|
||
#### `{{strength_frequency_adequate}}`
|
||
- **Zweck:** Ja/Nein Check für Muskelerhalt (mind. 2x/Woche Kraft)
|
||
- **Datenquelle:** `activity_log` WHERE training_category = 'strength'
|
||
- **Berechnung:** COUNT(*) >= 2 in 7d?
|
||
- **Format:** "Ja" oder "Nein (nur 1x Kraft/Woche - Risiko Muskelabbau)"
|
||
- **Use Case:** Direkter Muskelerhalt-Check
|
||
|
||
#### `{{muscle_preservation_risk}}`
|
||
- **Zweck:** Warnung bei zu wenig Krafttraining
|
||
- **Datenquelle:** `activity_log` WHERE training_category = 'strength'
|
||
- **Berechnung:** IF strength_count_7d < 2 THEN "Risiko: nur Xx Kraft"
|
||
- **Format:** "Kein Risiko" oder "Risiko: nur 1x Kraft in 7d"
|
||
- **Use Case:** Explizite Warnung für KI-Prompt
|
||
|
||
#### `{{strength_type_distribution}}`
|
||
- **Zweck:** Verteilung innerhalb Krafttraining (Subcategories)
|
||
- **Datenquelle:** `activity_log` JOIN `training_types` WHERE category = 'strength'
|
||
- **Berechnung:** COUNT(*) per subcategory (hypertrophy, maxstrength, endurance)
|
||
- **Format:** "Hypertrophie: 60%, Maximalkraft: 30%, Kraftausdauer: 10%"
|
||
- **Use Case:** Periodisierungs-Analyse
|
||
|
||
#### `{{hypertrophy_vs_maxstrength_ratio}}`
|
||
- **Zweck:** Verhältnis Hypertrophie zu Maximalkraft
|
||
- **Datenquelle:** `activity_log` WHERE subcategory IN ('hypertrophy', 'maxstrength')
|
||
- **Berechnung:** hypertrophy_count / maxstrength_count (28d)
|
||
- **Format:** "3:1" oder "nur Hypertrophie" oder "nur Maximalkraft"
|
||
- **Use Case:** Kraft-Periodisierungs-Check
|
||
|
||
#### `{{technique_vs_sparring_balance}}`
|
||
- **Zweck:** Technik vs. Kampf im Kampfsport (Subcategories)
|
||
- **Datenquelle:** `activity_log` WHERE subcategory IN ('technique', 'sparring')
|
||
- **Berechnung:** Verhältnis technique / sparring (28d)
|
||
- **Format:** "2:1 (technik-lastig)" oder "balanced"
|
||
- **Use Case:** Kampfsport-spezifisches Coaching
|
||
|
||
---
|
||
|
||
### Kategorie: Erholung / Vitalwerte
|
||
|
||
#### `{{placeholder_name}}`
|
||
- **Zweck:**
|
||
- **Datenquelle:**
|
||
- **Berechnung:**
|
||
- **Format:**
|
||
- **Use Case:**
|
||
|
||
---
|
||
|
||
### Kategorie: Ziele (Goals)
|
||
|
||
#### `{{placeholder_name}}`
|
||
- **Zweck:**
|
||
- **Datenquelle:**
|
||
- **Berechnung:**
|
||
- **Format:**
|
||
- **Use Case:**
|
||
|
||
---
|
||
|
||
### Kategorie: Korrelationen
|
||
|
||
#### `{{placeholder_name}}`
|
||
- **Zweck:**
|
||
- **Datenquelle:**
|
||
- **Berechnung:**
|
||
- **Format:**
|
||
- **Use Case:**
|
||
|
||
---
|
||
|
||
## 2. Verbesserungspotenziale bei existierenden Platzhaltern
|
||
|
||
### `{{activity_summary}}` ⚠️ KRITISCH
|
||
- **Aktuelles Verhalten:** Zeigt ALLE Aktivitäten ohne Rücksicht auf quality_label
|
||
- **Problem:** Schlechte/ausgeschlossene Trainings werden mitgezählt → verfälscht Auswertungen
|
||
- **Vorschlag:**
|
||
- **Option A:** Default auf quality='acceptable_plus' ändern (Breaking Change)
|
||
- **Option B:** Neuen Parameter `quality_level` hinzufügen, default='all' (Non-Breaking)
|
||
- **Option C:** Neuer Platzhalter `{{activity_summary_quality}}`, alter bleibt (Redundanz)
|
||
- **Breaking Change:** Ja (Option A), Nein (Option B+C)
|
||
- **Use Case:** ALLE Standard-Coaching-Prompts sollten nur valide Trainings nutzen
|
||
|
||
### `{{activity_detail}}` ⚠️ KRITISCH
|
||
- **Aktuelles Verhalten:** Listet ALLE Aktivitäten ohne quality_label Berücksichtigung
|
||
- **Problem:** Schlechte Trainings erscheinen in Detail-Liste ohne Kennzeichnung
|
||
- **Vorschlag:**
|
||
- Entweder quality_filter hinzufügen (nur acceptable+)
|
||
- Oder quality_label als Badge in Output aufnehmen: "30.03. Laufen (60 min) [excellent]"
|
||
- **Breaking Change:** Teilweise (Format-Änderung)
|
||
- **Use Case:** Detail-Analyse sollte Qualität pro Training zeigen
|
||
|
||
### `{{trainingstyp_verteilung}}` ⚠️
|
||
- **Aktuelles Verhalten:** Zeigt Verteilung ALLER Trainingstypen
|
||
- **Problem:** Schlechte Sessions werden in Kategorie-Statistik mitgezählt
|
||
- **Vorschlag:** quality_filter auf acceptable+ anwenden
|
||
- **Breaking Change:** Ja (Zahlen ändern sich)
|
||
- **Use Case:** Verteilung sollte nur valide Trainings zeigen
|
||
|
||
### `{{training_minutes_week}}` ⚠️
|
||
- **Aktuelles Verhalten:** Summiert ALLE Trainingsminuten
|
||
- **Problem:** Schlechte Trainings werden voll gezählt
|
||
- **Vorschlag:**
|
||
- Nur acceptable+ Trainings zählen
|
||
- Oder: quality_weighted sum (excellent=1.0, good=0.95, acceptable=0.85, poor=0.5)
|
||
- **Breaking Change:** Ja
|
||
- **Use Case:** WHO-Empfehlung sollte nur valide Minuten berücksichtigen
|
||
|
||
### `{{training_frequency_7d}}` ⚠️
|
||
- **Aktuelles Verhalten:** Zählt ALLE Sessions
|
||
- **Problem:** Poor/excluded sessions werden mitgezählt
|
||
- **Vorschlag:** Nur acceptable+ Sessions zählen
|
||
- **Breaking Change:** Ja
|
||
- **Use Case:** Frequenz-Statistik sollte nur valide Trainings zählen
|
||
|
||
### `{{ability_balance_*}}` ⚠️
|
||
- **Aktuelles Verhalten:** Berechnet Balance aus ALLEN Activities
|
||
- **Problem:** Schlechte Trainings beeinflussen Ability-Balance
|
||
- **Vorschlag:** Nur acceptable+ Activities für Berechnung nutzen
|
||
- **Breaking Change:** Ja (Balance-Scores ändern sich)
|
||
- **Use Case:** Balance-Berechnung sollte nur valide Trainings nutzen
|
||
|
||
### `{{proxy_internal_load_7d}}` ℹ️ TEILWEISE KORREKT
|
||
- **Aktuelles Verhalten:** Nutzt quality_label als Gewichtungsfaktor (excellent=1.15, poor=0.75)
|
||
- **Problem:** Lädt ALLE Activities, gewichtet sie dann runter - aber poor sollte gar nicht gezählt werden
|
||
- **Vorschlag:** Filter auf acceptable+ PLUS quality-Gewichtung
|
||
- **Breaking Change:** Teilweise
|
||
- **Use Case:** Load-Monitoring sollte nur valide Trainings berücksichtigen
|
||
|
||
---
|
||
|
||
## 3. Neue Berechnungslogik / Funktionen
|
||
|
||
### Feature: Quality-Aware Activity Metrics
|
||
- **Beschreibung:** Alle activity_metrics Funktionen erhalten quality_level Parameter
|
||
- **Input:**
|
||
- `profile_id: str`
|
||
- `days: int`
|
||
- `quality_level: str = 'quality'` (new parameter)
|
||
- **Output:** Wie bisher, aber gefiltert nach quality_level
|
||
- **Algorithmus:**
|
||
```python
|
||
from quality_filter import get_quality_filter_sql
|
||
|
||
def get_activity_summary_data(profile_id, days=14, quality_level='quality'):
|
||
profile = get_profile_data(profile_id)
|
||
profile['quality_filter_level'] = quality_level
|
||
|
||
quality_sql = get_quality_filter_sql(profile) # Returns "AND quality_label IN (...)"
|
||
|
||
query = f"""
|
||
SELECT COUNT(*) as count, ...
|
||
FROM activity_log
|
||
WHERE profile_id=%s AND date >= %s
|
||
{quality_sql}
|
||
"""
|
||
```
|
||
- **Modul:** `backend/data_layer/activity_metrics.py` (alle Funktionen)
|
||
- **Dependencies:**
|
||
- `quality_filter.py` (bereits vorhanden)
|
||
- `get_profile_data()` für quality_filter_level
|
||
|
||
### Feature: Evaluation Breakdown Calculator
|
||
- **Beschreibung:** Berechnet Verteilung nach quality_label für Zeitraum
|
||
- **Input:** `profile_id: str`, `days: int = 28`
|
||
- **Output:**
|
||
```python
|
||
{
|
||
"excellent": {"count": 3, "percentage": 25, "minutes": 180},
|
||
"good": {"count": 5, "percentage": 42, "minutes": 225},
|
||
"acceptable": {"count": 3, "percentage": 25, "minutes": 90},
|
||
"poor": {"count": 1, "percentage": 8, "minutes": 30},
|
||
"excluded": {"count": 0, "percentage": 0, "minutes": 0},
|
||
"total": 12,
|
||
"confidence": "high"
|
||
}
|
||
```
|
||
- **Algorithmus:**
|
||
```sql
|
||
SELECT quality_label,
|
||
COUNT(*) as count,
|
||
SUM(duration_min) as minutes
|
||
FROM activity_log
|
||
WHERE profile_id = %s
|
||
AND date >= CURRENT_DATE - INTERVAL '%s days'
|
||
GROUP BY quality_label
|
||
```
|
||
- **Modul:** `backend/data_layer/activity_metrics.py` (neue Funktion)
|
||
- **Dependencies:** Keine
|
||
|
||
### Feature: Category-Specific Activity Metrics
|
||
- **Beschreibung:** Count/Minutes pro Trainings-Kategorie
|
||
- **Input:** `profile_id: str`, `category: str`, `days: int = 7`, `quality_level: str = 'quality'`
|
||
- **Output:**
|
||
```python
|
||
{
|
||
"category": "strength",
|
||
"count": 3,
|
||
"total_minutes": 135,
|
||
"avg_minutes_per_session": 45,
|
||
"sessions_per_week": 3.0,
|
||
"percentage_of_total": 35.0, # 35% aller Trainings
|
||
"confidence": "high",
|
||
"days_analyzed": 7
|
||
}
|
||
```
|
||
- **Algorithmus:**
|
||
```python
|
||
def get_category_activity_data(profile_id, category, days=7, quality_level='quality'):
|
||
profile = get_profile_data(profile_id)
|
||
profile['quality_filter_level'] = quality_level
|
||
quality_sql = get_quality_filter_sql(profile)
|
||
|
||
query = f"""
|
||
SELECT COUNT(*) as count,
|
||
SUM(duration_min) as total_minutes
|
||
FROM activity_log
|
||
WHERE profile_id = %s
|
||
AND training_category = %s
|
||
AND date >= CURRENT_DATE - INTERVAL '%s days'
|
||
{quality_sql}
|
||
"""
|
||
# Calculate derived metrics...
|
||
```
|
||
- **Modul:** `backend/data_layer/activity_metrics.py` (neue Funktion)
|
||
- **Dependencies:** quality_filter.py
|
||
|
||
### Feature: Days Since Last Training (per Category)
|
||
- **Beschreibung:** Tage seit letzter Einheit einer bestimmten Kategorie
|
||
- **Input:** `profile_id: str`, `category: str`, `quality_level: str = 'quality'`
|
||
- **Output:**
|
||
```python
|
||
{
|
||
"category": "strength",
|
||
"days_since_last": 2,
|
||
"last_date": "2026-03-27",
|
||
"status": "ok", # ok / warning / critical
|
||
"recommendation": "Nächste Einheit in 1-2 Tagen empfohlen",
|
||
"confidence": "high"
|
||
}
|
||
```
|
||
- **Algorithmus:**
|
||
```sql
|
||
SELECT MAX(date) as last_date
|
||
FROM activity_log
|
||
WHERE profile_id = %s
|
||
AND training_category = %s
|
||
AND quality_label IN ('excellent', 'good', 'acceptable')
|
||
```
|
||
```python
|
||
days_since = (datetime.now().date() - last_date).days
|
||
|
||
# Status logic (category-specific thresholds)
|
||
if category == 'strength':
|
||
status = 'critical' if days_since > 7 else 'warning' if days_since > 3 else 'ok'
|
||
elif category == 'cardio':
|
||
status = 'critical' if days_since > 5 else 'warning' if days_since > 2 else 'ok'
|
||
# etc.
|
||
```
|
||
- **Modul:** `backend/data_layer/activity_metrics.py` (neue Funktion)
|
||
- **Dependencies:** quality_filter.py
|
||
|
||
### Feature: Training Category Balance Calculator
|
||
- **Beschreibung:** Berechnet Balance-Score basierend auf Verteilung
|
||
- **Input:** `profile_id: str`, `days: int = 28`
|
||
- **Output:**
|
||
```python
|
||
{
|
||
"balance_score": 75, # 0-100
|
||
"cardio_pct": 45,
|
||
"strength_pct": 35,
|
||
"mobility_pct": 10,
|
||
"recovery_pct": 5,
|
||
"martial_arts_pct": 5,
|
||
"missing_categories": ["hiit"],
|
||
"recommendation": "Gut balanciert, aber kein HIIT in 28 Tagen",
|
||
"cardio_to_strength_ratio": "1.3:1",
|
||
"status": "balanced" # balanced / cardio_heavy / strength_heavy
|
||
}
|
||
```
|
||
- **Algorithmus:**
|
||
```python
|
||
# Get category distribution
|
||
categories = {'cardio': 0, 'strength': 0, 'mobility': 0, 'recovery': 0, ...}
|
||
|
||
for category, minutes in distribution.items():
|
||
categories[category] = (minutes / total_minutes) * 100
|
||
|
||
# Balance score = 100 - (std_dev × 2)
|
||
balance_score = 100 - (statistics.stdev(categories.values()) * 2)
|
||
|
||
# Ratio calculations
|
||
cardio_to_strength = categories['cardio'] / categories['strength']
|
||
|
||
# Missing categories (expected but 0)
|
||
expected = ['cardio', 'strength', 'mobility']
|
||
missing = [c for c in expected if categories[c] == 0]
|
||
```
|
||
- **Modul:** `backend/data_layer/activity_metrics.py` (neue Funktion)
|
||
- **Dependencies:** statistics
|
||
|
||
### Feature: Subcategory Distribution (within Category)
|
||
- **Beschreibung:** Verteilung nach Subcategories innerhalb einer Kategorie
|
||
- **Input:** `profile_id: str`, `category: str = 'strength'`, `days: int = 28`
|
||
- **Output:**
|
||
```python
|
||
{
|
||
"category": "strength",
|
||
"distribution": [
|
||
{"subcategory": "hypertrophy", "count": 6, "percentage": 60},
|
||
{"subcategory": "maxstrength", "count": 3, "percentage": 30},
|
||
{"subcategory": "endurance", "count": 1, "percentage": 10}
|
||
],
|
||
"total_sessions": 10,
|
||
"ratio_hypertrophy_to_max": "2:1",
|
||
"recommendation": "Gut verteilt, Periodisierung erkennbar",
|
||
"confidence": "high"
|
||
}
|
||
```
|
||
- **Algorithmus:**
|
||
```sql
|
||
SELECT tt.subcategory,
|
||
COUNT(*) as count
|
||
FROM activity_log a
|
||
JOIN training_types tt ON a.training_type_id = tt.id
|
||
WHERE a.profile_id = %s
|
||
AND a.training_category = %s
|
||
AND a.date >= CURRENT_DATE - INTERVAL '%s days'
|
||
AND a.quality_label IN ('excellent', 'good', 'acceptable')
|
||
GROUP BY tt.subcategory
|
||
ORDER BY count DESC
|
||
```
|
||
- **Modul:** `backend/data_layer/activity_metrics.py` (neue Funktion)
|
||
- **Dependencies:** training_types table, quality_filter.py
|
||
|
||
---
|
||
|
||
### Feature: Zwei-Stufen-Architektur für Interpretations-Platzhalter ⭐ NEU
|
||
|
||
#### Architektur-Überblick:
|
||
```
|
||
Data Layer (Python) → Raw Data Placeholder (JSON) → Basis-Prompt (KI) → Interpreted Placeholder
|
||
```
|
||
|
||
#### Beispiel: Goal-Weighted Priority
|
||
|
||
**Stufe 1: Data Layer**
|
||
```python
|
||
# backend/data_layer/scores.py (neu)
|
||
def get_goal_priority_raw_data(profile_id: str) -> Dict:
|
||
"""
|
||
Aggregiert Rohdaten für Goal-Priorisierung.
|
||
KEINE Interpretation, nur strukturierte Daten!
|
||
"""
|
||
with get_db() as conn:
|
||
cur = get_cursor(conn)
|
||
|
||
# 1. Aktive Ziele mit Focus Contributions
|
||
cur.execute("""
|
||
SELECT g.id, g.name, g.goal_type, g.progress_percentage,
|
||
g.target_date, g.start_date, g.is_primary,
|
||
COALESCE(
|
||
json_agg(
|
||
json_build_object(
|
||
'focus_area', fad.name_de,
|
||
'category', fad.category,
|
||
'contribution_weight', gfc.contribution_weight
|
||
)
|
||
) FILTER (WHERE fad.id IS NOT NULL),
|
||
'[]'::json
|
||
) as focus_contributions
|
||
FROM goals g
|
||
LEFT JOIN goal_focus_contributions gfc ON g.id = gfc.goal_id
|
||
LEFT JOIN focus_area_definitions fad ON gfc.focus_area_id = fad.id
|
||
WHERE g.profile_id = %s AND g.status = 'active'
|
||
GROUP BY g.id
|
||
""", (profile_id,))
|
||
goals = [dict(row) for row in cur.fetchall()]
|
||
|
||
# 2. Focus Area Weights
|
||
from data_layer.scores import get_user_focus_weights
|
||
focus_weights = get_user_focus_weights(profile_id)
|
||
|
||
# 3. Calculate derived metrics
|
||
for goal in goals:
|
||
# Total focus weight (Summe aller Contributions)
|
||
goal['total_focus_weight'] = sum(
|
||
fc['contribution_weight'] for fc in goal['focus_contributions']
|
||
)
|
||
|
||
# Behind schedule calculation
|
||
if goal['target_date']:
|
||
from datetime import datetime
|
||
today = datetime.now().date()
|
||
target = goal['target_date']
|
||
start = goal['start_date']
|
||
|
||
total_days = (target - start).days
|
||
elapsed_days = (today - start).days
|
||
expected_progress = (elapsed_days / total_days) * 100
|
||
actual_progress = goal['progress_percentage']
|
||
|
||
goal['behind_schedule_days'] = int(
|
||
((expected_progress - actual_progress) / 100) * total_days
|
||
)
|
||
else:
|
||
goal['behind_schedule_days'] = None
|
||
|
||
# 4. Context
|
||
context = {
|
||
"behind_schedule_count": sum(
|
||
1 for g in goals if g.get('behind_schedule_days', 0) > 0
|
||
),
|
||
"on_track_count": sum(
|
||
1 for g in goals if g.get('behind_schedule_days', 0) <= 0
|
||
),
|
||
"primary_goal": next(
|
||
(g['name'] for g in goals if g['is_primary']),
|
||
None
|
||
)
|
||
}
|
||
|
||
return {
|
||
"goals": goals,
|
||
"focus_area_weights": focus_weights,
|
||
"context": context
|
||
}
|
||
```
|
||
|
||
**Stufe 2: Platzhalter-Registration (Raw Data)**
|
||
```python
|
||
# backend/placeholder_resolver.py
|
||
PLACEHOLDER_MAP = {
|
||
# ... existing placeholders ...
|
||
|
||
# Raw Data Placeholders (Typ 2)
|
||
'{{goal_priority_raw_data}}': lambda pid: json.dumps(
|
||
get_goal_priority_raw_data(pid),
|
||
indent=2
|
||
),
|
||
}
|
||
```
|
||
|
||
**Stufe 3: Basis-Prompt (KI Interpretation)**
|
||
```yaml
|
||
# Admin → KI-Prompts → Neuer Basis-Prompt
|
||
|
||
Name: Goal Priority Interpreter
|
||
Type: base
|
||
Output Format: json
|
||
Output Schema:
|
||
{
|
||
"priority_ranking": [
|
||
{
|
||
"goal_name": "string",
|
||
"priority_score": "number (0-100)",
|
||
"rationale": "string",
|
||
"conflicts": ["string"]
|
||
}
|
||
],
|
||
"recommended_focus": "string",
|
||
"goal_conflicts": [
|
||
{
|
||
"goal_a": "string",
|
||
"goal_b": "string",
|
||
"conflict_type": "string",
|
||
"severity": "string"
|
||
}
|
||
]
|
||
}
|
||
|
||
Template:
|
||
---
|
||
Analysiere die folgenden Ziel-Rohdaten und erstelle eine priorisierte Liste.
|
||
|
||
# Rohdaten
|
||
{{goal_priority_raw_data}}
|
||
|
||
# Aufgabe
|
||
1. Sortiere Ziele nach kombinierter Priorität:
|
||
- Focus-Weight (höheres Gewicht = höhere Priorität)
|
||
- Behind-Schedule Status (hinter Plan = höhere Priorität)
|
||
- Primary-Flag (Primary-Goal = Boost)
|
||
|
||
2. Für jedes Ziel:
|
||
- Berechne Priority-Score (0-100)
|
||
- Begründe die Priorisierung
|
||
- Identifiziere Konflikte mit anderen Zielen
|
||
|
||
3. Identifiziere Goal-Konflikte:
|
||
- Muskelaufbau vs. Kaloriendefizit (konfliktär)
|
||
- Gewichtsverlust + Kraftaufbau (möglich mit Rekomposition)
|
||
- etc.
|
||
|
||
4. Empfehle Fokus-Reihenfolge für nächste 4 Wochen
|
||
|
||
# Output
|
||
Gib das Ergebnis als JSON zurück (siehe Schema oben).
|
||
---
|
||
```
|
||
|
||
**Stufe 4: Interpreted Placeholder (von KI generiert)**
|
||
```python
|
||
# Wird automatisch durch Unified Prompt System erzeugt
|
||
# Wenn Basis-Prompt "Goal Priority Interpreter" ausgeführt wird:
|
||
'{{goal_weighted_priority}}' = <KI-generiertes JSON>
|
||
|
||
# Beispiel-Output:
|
||
{
|
||
"priority_ranking": [
|
||
{
|
||
"goal_name": "Gewichtsverlust 85kg",
|
||
"priority_score": 92,
|
||
"rationale": "Höchstes Focus-Weight (75) + 5 Tage hinter Plan + Primary Goal",
|
||
"conflicts": ["Kraftaufbau (moderate Konflikt bei zu großem Defizit)"]
|
||
},
|
||
{
|
||
"goal_name": "Kraftaufbau",
|
||
"priority_score": 58,
|
||
"rationale": "Moderates Focus-Weight (20) + On Track + Secondary Goal",
|
||
"conflicts": ["Gewichtsverlust (erfordert moderates Defizit)"]
|
||
}
|
||
],
|
||
"recommended_focus": "Primär Gewichtsverlust mit moderatem Defizit, dabei Krafttraining 2-3x/Woche für Muskelerhalt",
|
||
"goal_conflicts": [
|
||
{
|
||
"goal_a": "Gewichtsverlust",
|
||
"goal_b": "Kraftaufbau",
|
||
"conflict_type": "energy_availability",
|
||
"severity": "moderate"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
**Stufe 5: Verwendung in übergeordneten Prompts**
|
||
```yaml
|
||
# Pipeline-Prompt: "Weekly Coaching Report"
|
||
|
||
Template:
|
||
---
|
||
# Ziel-Priorisierung
|
||
{{goal_weighted_priority}}
|
||
|
||
# Basierend auf deinen Prioritäten...
|
||
[restlicher Prompt nutzt die Priorisierung]
|
||
---
|
||
```
|
||
|
||
#### Vorteile dieser Architektur:
|
||
|
||
1. **Flexibilität:** Prompt-Änderung ohne Code-Deploy
|
||
2. **Wartbarkeit:** Klare Trennung Data vs. Interpretation
|
||
3. **Erweiterbarkeit:** Gleiche Rohdaten für mehrere Prompts nutzbar
|
||
4. **Testbarkeit:** Rohdaten-Struktur separat testbar
|
||
5. **Transparenz:** Rohdaten sichtbar (Debug-Modus)
|
||
6. **Iterierbarkeit:** KI-Interpretation kann verfeinert werden
|
||
|
||
#### Implementierungs-Aufwand:
|
||
|
||
- **Data Layer Funktion:** 2h (get_goal_priority_raw_data)
|
||
- **Placeholder Registration:** 10min (einfacher Lambda)
|
||
- **Basis-Prompt:** 1h (Prompt-Engineering + Testing)
|
||
- **TOTAL:** 3h
|
||
|
||
#### Anwendbar für:
|
||
|
||
- P31: goal_weighted_priority ✅
|
||
- P32: main_constraint ✅
|
||
- P33: main_strength ✅
|
||
- P34: next_best_actions ⚠️ (braucht mehr Domänen-Wissen)
|
||
|
||
---
|
||
|
||
## 4. Datenqualität & Edge Cases
|
||
|
||
### Problem: Activities ohne quality_label (NULL values)
|
||
- **Beschreibung:** Alte Activities oder Imports haben kein quality_label → NULL
|
||
- **Betroffene Platzhalter:** ALLE activity-bezogenen Platzhalter
|
||
- **Vorschlag:** Wie sollte damit umgegangen werden?
|
||
- [x] NULL behandeln als "uncategorized" (separate Kategorie)
|
||
- [x] Bei quality_filter: NULL = excluded (nicht mitzählen)
|
||
- [ ] Batch-Evaluation für alte Activities nachholen
|
||
- [ ] Warnung in confidence_score wenn >20% NULL
|
||
|
||
### Problem: Quality-Filter zu strikt für manche Platzhalter
|
||
- **Beschreibung:** Bei "excellent" only → zu wenig Daten für Statistik
|
||
- **Betroffene Platzhalter:** `{{activity_summary_excellent}}`, etc.
|
||
- **Vorschlag:** Wie sollte damit umgegangen werden?
|
||
- [x] Confidence-Score anpassen (insufficient wenn <3 activities)
|
||
- [x] Fallback-Message: "Nur 2 excellent Sessions in 14 Tagen (zu wenig für Statistik)"
|
||
- [ ] Zeitraum automatisch erweitern (14d → 28d → 90d)
|
||
|
||
### Problem: quality_label vs. user quality_filter_level
|
||
- **Beschreibung:** Es gibt zwei Quality-Konzepte, die verwechselt werden können:
|
||
- `quality_label` (DB-Spalte): Evaluation-Ergebnis pro Activity (excellent, good, acceptable, poor, excluded)
|
||
- `quality_filter_level` (Profile-Einstellung): User-Präferenz für globalen Filter (all, quality, very_good, excellent)
|
||
- **Betroffene Platzhalter:** Alle activity-bezogenen Platzhalter
|
||
- **Vorschlag:**
|
||
- [x] Platzhalter verwenden IMMER default='quality' (acceptable+) → konsistent für KI-Analysen
|
||
- [x] User quality_filter_level nur für manuelle UI-Filter (Charts, Activity Page)
|
||
- [x] Keine Vermischung: Platzhalter = fix 'quality', UI = user preference
|
||
- [ ] Dokumentation: Klarstellung der beiden Konzepte
|
||
|
||
---
|
||
|
||
## 5. Placeholder-Kategorisierung & Organisation
|
||
|
||
### Vorschlag für neue Kategorien:
|
||
- [ ] Kategorie-Name: ___
|
||
- Platzhalter: `{{...}}`, `{{...}}`
|
||
- Begründung: ___
|
||
|
||
### Umstrukturierung existierender Kategorien:
|
||
- [ ] Von Kategorie X nach Y verschieben: `{{placeholder}}`
|
||
- Begründung: ___
|
||
|
||
---
|
||
|
||
## 6. Dokumentation & Metadaten
|
||
|
||
### Fehlende Beschreibungen:
|
||
- `{{placeholder}}` - Beschreibung fehlt oder ist unklar
|
||
|
||
### Fehlende Beispiele:
|
||
- `{{placeholder}}` - Beispiel-Output fehlt
|
||
|
||
---
|
||
|
||
## 7. Prioritäten & Abhängigkeiten
|
||
|
||
### Must-Have (kritisch für Prompt-Qualität):
|
||
|
||
#### Gap #1: Quality Label
|
||
1. **Quality-Filter in data_layer/activity_metrics.py** - ALLE Funktionen betroffen
|
||
- Breaking Change, aber notwendig für korrekte Auswertungen
|
||
- Dauer: 4-6h (9 Funktionen + Tests)
|
||
- Blockiert: Alle neuen Platzhalter
|
||
2. **Neue Platzhalter für Evaluation-Breakdown**
|
||
- `{{evaluation_breakdown}}` - Übersicht über Qualitätsverteilung
|
||
- Dauer: 1h (Funktion + Platzhalter)
|
||
3. **Placeholder-Updates in placeholder_resolver.py**
|
||
- Alle activity-Platzhalter auf quality='quality' umstellen
|
||
- Dauer: 2h (Updates + Testen)
|
||
|
||
**Teilsumme Gap #1:** 7-9h
|
||
|
||
#### Gap #2: Training Categories
|
||
4. **Category-Specific Activity Metrics (data_layer)**
|
||
- `get_category_activity_data(category)` - Count/Minutes pro Kategorie
|
||
- Dauer: 2h (1 Funktion, alle Kategorien abdecken)
|
||
5. **Kategorie-spezifische Platzhalter (Top Priority)**
|
||
- `{{strength_training_count_7d}}`, `{{strength_training_minutes_7d}}`
|
||
- `{{cardio_training_count_7d}}`, `{{cardio_training_minutes_7d}}`
|
||
- `{{strength_frequency_adequate}}`, `{{muscle_preservation_risk}}`
|
||
- Dauer: 3h (6-8 Platzhalter, kritisch für Muskelerhalt-Coaching)
|
||
6. **Days Since Last Training (per Category)**
|
||
- `get_days_since_last_training(category)` - Funktion
|
||
- `{{days_since_last_strength}}`, `{{days_since_last_cardio}}`, etc.
|
||
- Dauer: 2h (Funktion + 3-4 Platzhalter)
|
||
7. **Training Balance Calculator**
|
||
- `get_training_balance_data()` - Balance-Score + Ratios
|
||
- `{{cardio_to_strength_ratio}}`, `{{training_balance_score}}`, `{{missing_training_categories}}`
|
||
- Dauer: 2-3h (Funktion + 3 Platzhalter)
|
||
|
||
**Teilsumme Gap #2:** 9-10h
|
||
|
||
**TOTAL Must-Have:** 16-19h
|
||
|
||
### Should-Have (sinnvolle Ergänzungen):
|
||
|
||
#### Gap #1: Quality Label
|
||
1. **Dedizierte Quality-Platzhalter**
|
||
- `{{activity_summary_acceptable_plus}}`, `{{activity_summary_excellent}}`
|
||
- Dauer: 2h (4-5 neue Platzhalter)
|
||
2. **Poor Sessions Warning Platzhalter**
|
||
- `{{poor_sessions_count}}`, `{{poor_sessions_warning}}`
|
||
- Dauer: 1h
|
||
|
||
#### Gap #2: Training Categories
|
||
3. **Weitere Kategorie-Platzhalter**
|
||
- `{{martial_arts_frequency_28d}}`, `{{recovery_sessions_count_28d}}`, `{{mobility_sessions_count_28d}}`
|
||
- Dauer: 2h (3-4 Platzhalter)
|
||
4. **Subcategory Distribution**
|
||
- `get_subcategory_distribution(category)` - Funktion
|
||
- `{{strength_type_distribution}}`, `{{hypertrophy_vs_maxstrength_ratio}}`, `{{technique_vs_sparring_balance}}`
|
||
- Dauer: 3h (Funktion + 3 Platzhalter)
|
||
5. **NULL-Handling für alte Activities**
|
||
- Batch-Evaluation Script für Activities ohne quality_label
|
||
- Dauer: 2h
|
||
|
||
**Teilsumme Should-Have:** 10h
|
||
|
||
### Nice-to-Have (optional):
|
||
1. **Quality-Weighted Metrics**
|
||
- `{{training_minutes_quality_weighted}}` - gewichtet nach Qualität
|
||
- Dauer: 1h
|
||
2. **Auto-Timeframe-Expansion**
|
||
- Bei insufficient data: automatisch von 14d → 28d → 90d erweitern
|
||
- Dauer: 3h (Logik + alle Funktionen anpassen)
|
||
3. **Advanced Subcategory Analysen**
|
||
- Periodisierungs-Erkennung (Maximalkraft-Phase → Hypertrophie-Phase?)
|
||
- Dauer: 2-3h
|
||
|
||
**Teilsumme Nice-to-Have:** 6-7h
|
||
|
||
### Geschätzte Gesamt-Dauer (Original):
|
||
- **Must-Have (Gap #1 + #2):** 16-19h (beide Gaps kritisch!)
|
||
- **Should-Have:** 10h
|
||
- **Nice-to-Have:** 6-7h
|
||
- **TOTAL (ohne User Requirements):** 32-36h
|
||
|
||
### Gesamt-Dauer (mit User Requirements P1-P27):
|
||
- **Sprint 1 (User Req + Governance):** 34-41h
|
||
- **Sprint 2 (Gap #1 + #2):** 26-32h
|
||
- **Sprint 3 (Optional):** 12-16h
|
||
- **TOTAL (inkl. User Requirements):** 72-89h
|
||
|
||
**Empfehlung:** Sprint 1 + Sprint 2 parallel bearbeiten wo möglich (viele unabhängige Tasks)
|
||
|
||
### Abhängigkeiten:
|
||
1. Quality-Filter in activity_metrics.py (MUSS zuerst)
|
||
2. Dann: Neue Platzhalter können parallel implementiert werden
|
||
3. Dann: Tests für alle Änderungen
|
||
4. Letzter Schritt: Bestehende Prompts auf neue Platzhalter umstellen
|
||
|
||
---
|
||
|
||
## Notizen
|
||
|
||
### Technische Details: quality_label System
|
||
|
||
**Verfügbare Quality Labels (activity_log.quality_label):**
|
||
- `'excellent'` - ⭐ Exzellentes Training (z.B. HR in Zone, Dauer optimal, RPE passend)
|
||
- `'very_good'` - ✓✓ Sehr gutes Training
|
||
- `'good'` - ✓ Gutes Training
|
||
- `'acceptable'` - ✓ Akzeptables Training (Mindestschwelle)
|
||
- `'poor'` - ⚠ Schlechtes Training (zu kurz, HR zu niedrig, etc.)
|
||
- `'excluded'` - ❌ Auszuschließen (fehlerhafte Messung, Dummy-Eintrag)
|
||
- `NULL` - Nicht evaluiert (alte Einträge, Imports ohne Evaluation)
|
||
|
||
**Quality Filter Levels (für User-Präferenz, nicht Platzhalter!):**
|
||
- `'all'` - Alle Activities (kein Filter)
|
||
- `'quality'` - Hochwertig (acceptable+) ← **DEFAULT für Platzhalter**
|
||
- `'very_good'` - Sehr gut+ (excellent, good)
|
||
- `'excellent'` - Nur exzellent
|
||
|
||
**Bestehende Infrastruktur (quality_filter.py):**
|
||
```python
|
||
get_quality_filter_sql(profile, table_alias='')
|
||
# Returns: "AND quality_label IN ('excellent', 'good', 'acceptable')"
|
||
|
||
get_quality_filter_tuple(profile)
|
||
# Returns: ('excellent', 'good', 'acceptable')
|
||
|
||
filter_activities_by_quality(activities, profile)
|
||
# Post-query filtering
|
||
```
|
||
|
||
**Migration Path:**
|
||
1. Alle data_layer Funktionen erweitern um `quality_level='quality'` Parameter
|
||
2. Alle Platzhalter updaten: `lambda pid: get_activity_summary_data(pid, quality_level='quality')`
|
||
3. Neue Platzhalter für andere Levels: `{{activity_summary_excellent}}`
|
||
4. Tests schreiben für alle Kombinationen
|
||
5. Bestehende Prompts prüfen und ggf. anpassen
|
||
|
||
### Verfügbare Trainings-Kategorien & Subcategories:
|
||
|
||
**7 Hauptkategorien (training_category):**
|
||
1. **cardio** → Ausdauer
|
||
- Subcategories: running, cycling, swimming, rowing, other
|
||
2. **strength** → Kraft
|
||
- Subcategories: hypertrophy, maxstrength, endurance, functional
|
||
3. **hiit** → Schnellkraft
|
||
- Subcategories: hiit, explosive, circuit
|
||
4. **martial_arts** → Kampfsport
|
||
- Subcategories: technique, sparring, strength
|
||
5. **mobility** → Beweglichkeit
|
||
- Subcategories: static, dynamic, yoga, fascia
|
||
6. **recovery** → Erholung (aktiv)
|
||
- Subcategories: walk, swim_light, regeneration
|
||
7. **other** → Sonstiges
|
||
|
||
**Abilities-Mapping (JSONB in training_types.abilities):**
|
||
- strength, endurance, mental, coordination, mobility
|
||
|
||
**Bestehende Platzhalter (nutzen bereits Categories):**
|
||
- `{{trainingstyp_verteilung}}` - Top 3 Kategorien mit %
|
||
- `{{ability_balance_strength}}` - Ability-Score 0-100 (nutzt abilities JSONB)
|
||
- `{{ability_balance_endurance}}` - Ability-Score 0-100
|
||
|
||
**Migration Path Training Categories:**
|
||
1. Neue data_layer Funktionen: get_category_activity_data(), get_days_since_last_training(), etc.
|
||
2. Platzhalter für JEDE Kategorie (mindestens count + minutes für strength/cardio)
|
||
3. Balance-Funktionen (Ratios, fehlende Kategorien)
|
||
4. Subcategory-Analysen (innerhalb strength, martial_arts)
|
||
5. Tests für alle Kombinationen
|
||
6. Bestehende Prompts erweitern mit neuen Platzhaltern
|
||
|
||
---
|
||
|
||
### Offene Fragen:
|
||
|
||
#### Gap #1: Quality Label
|
||
1. **Breaking Change akzeptabel?** Zahlen ändern sich bei allen activity-Platzhaltern
|
||
2. **NULL-Handling:** Als 'excluded' behandeln oder separate Kategorie?
|
||
3. **Batch-Evaluation:** Alte Activities nachträglich evaluieren? (Performance-Impact?)
|
||
4. **Confidence-Anpassung:** Thresholds senken wenn quality_filter aktiv? (weniger Daten)
|
||
|
||
#### Gap #2: Training Categories
|
||
5. **Ruhepausen-Thresholds:** Welche Schwellenwerte pro Kategorie?
|
||
- Kraft: >3d warning, >7d critical?
|
||
- Cardio: >2d warning, >5d critical?
|
||
- Kampfsport: >7d warning, >14d critical?
|
||
6. **Balance-Definition:** Was ist "gut balanciert"?
|
||
- Cardio:Kraft 2:1 bis 1:1 ok?
|
||
- Mobility mindestens 5% aller Trainings?
|
||
7. **Subcategory-Priorität:** Welche Subcategories zuerst?
|
||
- Kraft (hypertrophy/maxstrength/endurance) → Top Priority
|
||
- Kampfsport (technique/sparring) → Nice-to-Have?
|
||
8. **Missing Categories Logic:** Welche Kategorien sind "Pflicht"?
|
||
- Core: cardio, strength, mobility
|
||
- Optional: recovery, hiit, martial_arts?
|
||
|
||
### User Requirements Summary (P1-P27):
|
||
|
||
**Sprint 1 - Must-Have (10 Core Items):**
|
||
1. goal_summary_json → vollständige Zielübersicht
|
||
2. focus_area_summary_json → strukturierte Focus Areas
|
||
3. domain_availability_json → Verfügbarkeit pro Domäne
|
||
4. body_change_summary_json → Körperentwicklung strukturiert
|
||
5. activity_structure_json → Training strukturiert
|
||
6. sleep_summary_json → Schlaf strukturiert
|
||
7. recovery_summary_json → Erholung strukturiert
|
||
8. vitals_summary_json → Vitalwerte strukturiert
|
||
9. correlation_summary_json → Korrelationen strukturiert
|
||
10. plateau_status + top_drivers → Diagnose strukturiert
|
||
|
||
**Sprint 1 - Governance (4 Items):**
|
||
- Placeholder Versioning System
|
||
- "nicht verfügbar" → null Migration
|
||
- Zeitfenster-Audit & Renaming
|
||
- Availability Flags (systematic)
|
||
|
||
**Sprint 2 - Energy & Risk:**
|
||
- underfueling_risk_flag + energy_availability_summary
|
||
- training_quality_score (aggregiert)
|
||
- load_balance_class (klassifiziert)
|
||
|
||
**Sprint 3 - Zwei-Stufen-Architektur (P31-P34):**
|
||
- goal_weighted_priority (✅ möglich via 2-Stufen)
|
||
- main_constraint (✅ möglich via 2-Stufen)
|
||
- main_strength (✅ möglich via 2-Stufen)
|
||
- next_best_actions (⚠️ teilweise, braucht Domänen-Wissen)
|
||
|
||
**Sprint 3 - Optional:**
|
||
- blood_pressure_summary_json (niedrigere Priorität)
|
||
|
||
### User Input benötigt:
|
||
|
||
#### Gap #1: Quality Label
|
||
- [ ] Strategie-Entscheidung: Breaking Change (Option A) vs. Non-Breaking (Option B)?
|
||
- [ ] NULL-Handling: excluded oder uncategorized?
|
||
- [ ] Batch-Evaluation: Ja/Nein?
|
||
|
||
#### Gap #2: Training Categories
|
||
- [ ] Ruhepausen-Thresholds definieren (pro Kategorie)
|
||
- [ ] Balance-Kriterien festlegen (Ratios, Mindest-%)
|
||
- [ ] Subcategory-Priorität: Kraft zuerst, dann Kampfsport? Oder parallel?
|
||
- [ ] Muskelerhalt-Schwelle: 2x/Woche Kraft ok? Oder 3x?
|
||
|
||
#### User Requirements (P1-P27):
|
||
- [ ] Sprint-Priorisierung: Sprint 1 + Sprint 2 parallel? Oder sequentiell?
|
||
- [ ] Sprint 3: Zwei-Stufen-Platzhalter (P31-P34) gewünscht? Oder später?
|
||
- [ ] Governance: Placeholder Versioning System sofort einführen oder später?
|
||
- [ ] Breaking Changes: "nicht verfügbar" → null jetzt oder schrittweise?
|
||
- [ ] JSON-Struktur-Review: Beispiel-Strukturen (siehe P1-P27) absegnen?
|
||
|
||
#### Zwei-Stufen-Architektur (⭐ NEU):
|
||
- [ ] **Architektur-Approval:** Zwei-Stufen-Ansatz (Data + KI) für P31-P34 ok?
|
||
- [ ] **Basis-Prompts:** Wer erstellt/reviewed die Basis-Prompts? (User oder Claude?)
|
||
- [ ] **Prompt-Governance:** Wie werden Basis-Prompts versioniert/getestet?
|
||
- [ ] **Raw Data Sichtbarkeit:** Sollen Raw Data Placeholders auch in UI-Export sichtbar sein?
|
||
- [ ] **Iterative Verfeinerung:** Basis-Prompts initial "good enough" oder erst perfekt dann deployen?
|
||
|
||
---
|
||
|
||
## 📊 Finale Zusammenfassung: Impact der Fit/Gap-Analyse
|
||
|
||
### Vor der Analyse (Original):
|
||
- **Gap #1 (Quality Label):** 11-13h
|
||
- **Gap #2 (Training Categories):** 11-13h
|
||
- **TOTAL:** 22-26h
|
||
- **User Requirements:** Nicht bewertet
|
||
- **P31-P34:** Als "nicht möglich" eingestuft
|
||
|
||
### Nach der Analyse (aktualisiert):
|
||
- **Sprint 1 (User Req + Governance):** 34-41h
|
||
- **Sprint 2 (Gap #1 + #2):** 26-32h
|
||
- **Sprint 3 (Zwei-Stufen-Architektur):** 12-16h
|
||
- **TOTAL:** 72-89h
|
||
- **Alle 27 User Requirements:** ✅ Umsetzbar!
|
||
- **P31-P34:** ✅ Möglich via Zwei-Stufen-Architektur
|
||
|
||
### Wichtigste Erkenntnisse:
|
||
|
||
#### ✅ Architektur-Durchbruch:
|
||
**Zwei-Stufen-Architektur** löst das Problem komplexer Interpretations-Platzhalter:
|
||
- Data Layer stellt strukturierte Rohdaten bereit
|
||
- KI interpretiert via Basis-Prompts
|
||
- Flexibler, wartbarer, erweiterbarer als hardcodierte Logik
|
||
|
||
#### ✅ Vollständige Umsetzbarkeit:
|
||
**Alle 27 User Requirements (P1-P34) sind umsetzbar:**
|
||
- 20 Items direkt möglich (mit aktueller Datenstruktur)
|
||
- 4 Items möglich via Zwei-Stufen-Architektur
|
||
- 3 Items teilweise möglich (einfache Erweiterungen)
|
||
- Nur P6 redundant, P30 optional
|
||
|
||
#### ✅ Governance-Framework:
|
||
**8 verbindliche Governance-Regeln** für stabile Placeholder-API:
|
||
- Platzhalter = API-Verträge
|
||
- Keine stillschweigenden Änderungen
|
||
- JSON vor Freitext
|
||
- Zwei-Stufen-Architektur für Interpretationen
|
||
|
||
#### ✅ Klare Sprint-Struktur:
|
||
**3 Sprints mit klaren Zielen:**
|
||
- Sprint 1: Foundation (User Req + Governance)
|
||
- Sprint 2: Quality + Categories (kritische Gaps)
|
||
- Sprint 3: Interpretations-Platzhalter (Zwei-Stufen)
|
||
|
||
### Empfohlenes Vorgehen:
|
||
|
||
1. **User-Entscheidungen einholen** (siehe "User Input benötigt")
|
||
2. **Sprint 1 starten** (strukturierte JSONs + Governance)
|
||
3. **Sprint 2 parallel** (Quality + Categories, unabhängig von Sprint 1)
|
||
4. **Sprint 3 nach Approval** (Zwei-Stufen-Architektur)
|
||
|
||
### Geschätzter Gesamtaufwand:
|
||
- **Minimum:** 72h (optimistisch, parallele Arbeit)
|
||
- **Realistisch:** 89h (inkl. Testing, Iterationen, Reviews)
|
||
- **Mit Buffer:** 100-110h (Puffer für Unvorhergesehenes)
|
||
|
||
### ROI / Nutzen:
|
||
- ✅ **Stabile Placeholder-API** für professionelle Prompt-Bibliothek
|
||
- ✅ **Alle 27 User Requirements** erfüllt
|
||
- ✅ **Zukunftssichere Architektur** (Zwei-Stufen erweiterbar)
|
||
- ✅ **Quality + Categories Gaps** geschlossen (kritisch für KI-Coaching)
|
||
- ✅ **Governance-Framework** verhindert technische Schulden
|
||
|
||
---
|
||
|
||
**Status:** 🔴 Draft - Bereit für User Review & Entscheidungen
|
||
**Nächster Schritt:** User Input einholen → Umsetzungskonzept erstellen → Sprint 1 starten
|