# KI-Funktionen Specification – Exercises

**Version:** 1.1
**Datum:** 2026-04-24
**Status:** DRAFT
**Autor:** Claude Code
**Änderungen v1.1:** Prompts sind nicht hardcoded – sie werden aus der DB geladen (AI_PROMPT_SYSTEM_SPEC.md)
**Verwandte Specs:** AI_PROMPT_SYSTEM_SPEC.md (Prompt-DB + Platzhalter), SKILLS_MATRIX_SPEC.md (Fähigkeitsmatrix)

**Übergeordnete Produkt-Vision** (breiter Scope: Zielausbau, bereichsweise vs. Gesamtüberarbeitung, Varianten, Planungs-/Nachbereitungskontext, Admin-Masse):  
`functional/AI_EXERCISE_ASSISTANT_VISION.md`

---

## 1. Übersicht

KI-gestützte Assistenzfunktionen beim Anlegen und Bearbeiten von Übungen (Mindestpaket dieser Spec):

**Hinweis:** Die beiden folgenden Zeilen entsprechen **P0** der Phasierung in **`AI_EXERCISE_ASSISTANT_VISION.md`**; spätere Funkteile sind dort beschrieben.

| Funktion | Ziel |
|---------|------|
| **KI-Zusammenfassung** | Generiert `summary` aus `goal` + `execution` (für Listen + Trainingspläne) |
| **KI-Skill-Empfehlung** | Schlägt passende Fähigkeiten + Stufen vor (reduziert manuelle Zuordungsarbeit) |

**Abhängigkeit:** OpenRouter API (`OPENROUTER_API_KEY` in `.env`).
Ist der Key nicht gesetzt, sind alle KI-Funktionen deaktiviert – Formular funktioniert ohne KI normal.

---

## 2. UX-Prinzip: Manual First, Auto als Fallback

```
Trainer gibt goal + execution ein
        │
        ├─► [KI-Vorschlag]-Button sichtbar
        │          │
        │          ▼ (Klick)
        │   KI generiert sofort Vorschlag
        │   → Trainer sieht Vorschlag inline
        │   → Kann akzeptieren / bearbeiten / ablehnen
        │
        └─► Kein Klick bis zum Speichern?
                   │
                   ▼ (automatisch beim POST/PUT)
            KI läuft im Hintergrund
            → summary + skill_suggestions werden gesetzt
            → Trainer kann danach noch bearbeiten
```

**Regel:** Manuell generierte Werte (wo Trainer aktiv bestätigt hat) werden
beim Auto-Fallback NICHT überschrieben. Auto-generierte Werte werden als
`ai_generated: true` markiert und können jederzeit neu generiert oder überschrieben werden.

---

## 3. KI-Zusammenfassung (`summary`)

### 3.1 Was wird generiert

Aus `title` + `goal` + `execution` generiert die KI eine **kurze Zusammenfassung**
(2-4 Sätze, max. 200 Zeichen) für:
- Übungslisten (Karten-Ansicht)
- Trainingsplan-Darstellung (kompakte Anzeige)
- Export-Berichte

### 3.2 Prompt-Logik (Backend)

**Prompt ist NICHT hardcoded** – er wird aus der `ai_prompts` Tabelle geladen
(Slug: `exercise_summary`). Admins können das Template anpassen.

```python
# Aufruf via zentralem AI-Service (siehe AI_PROMPT_SYSTEM_SPEC.md)
result = await ai_service.run_ai_prompt(
    slug="exercise_summary",
    context_data={"exercise": exercise_data},
    db=db,
    openrouter_key=settings.openrouter_api_key
)
```

**Default-Template** (in Migration 020 als `default_template` gespeichert):
Siehe `AI_PROMPT_SYSTEM_SPEC.md` §2.1 – Prompt `exercise_summary`.

### 3.3 DB-Feld

Kein neues Feld nötig – `exercises.summary` nimmt den generierten Text auf.
Zusätzliches Tracking-Feld: `summary_ai_generated BOOLEAN DEFAULT false`

---

## 4. KI-Skill-Empfehlung

### 4.1 Was wird empfohlen

Aus `title` + `goal` + `execution` + Katalog aller verfügbaren Skills empfiehlt die KI:

- **Welche Skills** relevant sind (aus dem bestehenden Skill-Katalog)
- **Erforderliche Stufe** (`required_level`) – was der Trainierende mitbringen muss
- **Ziel-Stufe** (`target_level`) – was durch regelmäßige Ausführung erreicht wird
- **Intensität** (`intensity`) – wie stark diese Fähigkeit trainiert wird

### 4.2 Prompt-Logik (Backend)

**Prompt ist NICHT hardcoded** – Slug: `exercise_skill_suggestions`.
Der `{{skills_catalog}}`-Platzhalter wird vom Backend automatisch mit der aktuellen
Skill-Liste aus der DB aufgelöst.

Admins können den Prompt anpassen (z.B. die Anweisung zur Auswahl verschärfen oder
fachliche Hinweise ergänzen). Siehe `AI_PROMPT_SYSTEM_SPEC.md`.

### 4.3 Bestätigungsflow (UX)

```
KI gibt Vorschläge
       │
       ▼
┌─────────────────────────────────────────┐
│ KI-Vorschläge (3 Fähigkeiten)          │
│ ─────────────────────────────────────── │
│ ✓ Distanzgefühl                        │
│   Voraussetzung: Grundlagen             │
│   Ziel: Aufbau · Intensität: Hoch       │
│   [✓ Übernehmen] [Stufe ändern ▼] [✕]  │
│ ─────────────────────────────────────── │
│ ✓ Reaktionsschnelligkeit               │
│   Voraussetzung: Einsteiger             │
│   Ziel: Grundlagen · Intensität: Mittel │
│   [✓ Übernehmen] [Stufe ändern ▼] [✕]  │
│ ─────────────────────────────────────── │
│ ◦ Gleichgewicht (optional)             │
│   Voraussetzung: Grundlagen             │
│   Ziel: Grundlagen · Intensität: Niedrig│
│   [✓ Übernehmen] [Stufe ändern ▼] [✕]  │
│ ─────────────────────────────────────── │
│ [Alle übernehmen]      [Verwerfen]      │
└─────────────────────────────────────────┘
```

- Jeder Vorschlag kann **einzeln** übernommen, angepasst oder abgelehnt werden
- „Alle übernehmen" nimmt alle aktuell angezeigten Vorschläge
- Stufen können per Dropdown direkt angepasst werden bevor sie übernommen werden
- Abgelehnte Vorschläge werden nicht gespeichert

---

## 5. API-Endpoints

### 5.1 Endpoints Overview

| Method | Endpoint | Beschreibung |
|--------|----------|--------------|
| POST | `/exercises/ai/suggest` | Vorschläge für neue Übung (vor dem Speichern) |
| POST | `/exercises/{id}/ai/regenerate` | Neu generieren für bestehende Übung |

---

### 5.2 `POST /exercises/ai/suggest`

Liefert KI-Vorschläge auf Basis von Eingabe-Text, **bevor** die Übung gespeichert wurde.
Wird beim Klick auf „KI-Vorschlag" im Formular aufgerufen.

**Required Fields:** mindestens `goal` ODER `execution`

**Optional – Skill-Katalogpriorisierung (Stand 068):**

```json
{
  "focus_areas_context": [
    { "focus_area_id": 3, "is_primary": true },
    { "focus_area_id": 1, "is_primary": false }
  ],
  "focus_area_hint": "Karate, Kumite…"
}
```

- `focus_areas_context`: IDs aus Stammdatum **Fokusbereiche**; Primär soll zuerst stehen (`is_primary`). Ohne Feld oder leere Liste gilt das DB-Profil **`is_default`** (`ai_skill_retrieval_profiles`).
- `focus_area_hint`: bleibt lesbarer Text für den Prompt (bestehende Prompts).


**Minimal-Beispiel (Mit Fokus für Retrieval):**

```json
{
  "title": "Maai - Distanzübung",
  "goal": "…",
  "execution": "…",
  "focus_areas_context": [ { "focus_area_id": 1, "is_primary": true } ]
}
```


**Minimal-Beispiel ( ohne Fokus — nur Texts):**

```json
{
  "title": "Maai - Distanzübung",
  "goal": "Distanzgefühl entwickeln durch Partnerübungen...",
  "execution": "1. Partnerwahl\n2. Ausgangsstellung..."
}
```

**Response:** `200 OK`
```json
{
  "summary": {
    "text": "Partnerübung zur Entwicklung des Distanzgefühls. Trainiert werden räumliche Wahrnehmung und reaktives Verhalten in der Kumite-Distanz.",
    "ai_generated": true,
    "model": "anthropic/claude-sonnet-4"
  },
  "skills": [
    {
      "skill_id": 10,
      "skill_name": "Distanzgefühl",
      "skill_category": "Kumite",
      "required_level": "grundlagen",
      "target_level": "aufbau",
      "intensity": "hoch",
      "confidence": 0.92
    },
    {
      "skill_id": 15,
      "skill_name": "Reaktionsschnelligkeit",
      "skill_category": "Athletik",
      "required_level": "einsteiger",
      "target_level": "grundlagen",
      "intensity": "mittel",
      "confidence": 0.74
    }
  ]
}
```

**Errors:**
- `400` - Zu wenig Text (goal und execution leer)
- `503` - KI nicht verfügbar (OPENROUTER_API_KEY fehlt)

---

### 5.3 `POST /exercises/{id}/ai/regenerate`

Generiert summary und/oder skill-Empfehlungen für eine bestehende Übung neu.
Verwendet die aktuellen gespeicherten Felder als Input.

**Request Body:**
```json
{
  "regenerate": ["summary", "skills"]
}
```

**Werte:** `"summary"` | `"skills"` (oder beide)

**Response:** `200 OK` (gleiche Struktur wie `/ai/suggest`)

**Hinweis:** Regenerieren überschreibt NICHT Felder, die manuell gesetzt wurden
(`summary_ai_generated = false`). Es wird nur der Vorschlag zurückgegeben,
Trainer muss aktiv übernehmen.

---

## 6. Datenbank

### 6.1 Neues Feld: `summary_ai_generated`

```sql
-- Als Teil von Migration 014 ergänzen:
ALTER TABLE exercises
ADD COLUMN IF NOT EXISTS summary_ai_generated BOOLEAN DEFAULT false;
```

**Semantik:**
- `false` (default) = Zusammenfassung manuell geschrieben oder noch leer
- `true` = Zuletzt von KI generiert (kann überschrieben werden)

Beim manuellen Überschreiben durch den Trainer: `summary_ai_generated = false` setzen.

### 6.2 Kein Feld für Skill-Empfehlungen

Skill-Vorschläge der KI werden **nicht** persistent gespeichert – sie erscheinen
nur im Formular-UI und werden erst bei expliziter Bestätigung als reguläre
`exercise_skills`-Einträge gespeichert.

---

## 7. Integration in Speicher-Flow

### 7.1 Auto-Fallback beim Speichern (POST/PUT exercises)

```python
# In backend/routers/exercises.py
async def create_exercise(data, session):
    # 1. Übung normal speichern
    exercise_id = db.insert_exercise(data)

    # 2. Auto-KI nur wenn:
    #    - OPENROUTER_API_KEY gesetzt
    #    - summary noch leer
    #    - goal oder execution vorhanden
    if settings.openrouter_api_key and not data.summary and (data.goal or data.execution):
        # Background Task (non-blocking)
        background_tasks.add_task(
            auto_generate_summary,
            exercise_id=exercise_id,
            title=data.title,
            goal=data.goal,
            execution=data.execution
        )

    return exercise_id
```

### 7.2 Skill-Auto-Suggestion

Skill-Empfehlungen werden beim Auto-Fallback **NICHT** automatisch gespeichert
(wegen Bestätigungs-Pflicht). Sie werden nur beim manuellen Trigger angeboten.

---

## 8. Frontend-Integration

### 8.1 ExerciseFormPage – KI-Buttons

```
┌──────────────────────────────────────┐
│ Ziel der Übung *                     │
│ ┌────────────────────────────────┐   │
│ │ Distanzgefühl entwickeln...    │   │
│ └────────────────────────────────┘   │
│                                      │
│ Durchführung *                       │
│ ┌────────────────────────────────┐   │
│ │ 1. Partnerwahl...              │   │
│ └────────────────────────────────┘   │
│                              ↓       │
│ Zusammenfassung (für Listen)         │
│ ┌────────────────────────────────┐   │
│ │ [Leer]                         │   │
│ └────────────────────────────────┘   │
│ [✨ KI-Vorschlag]  ← Button          │
│                                      │
│ ─────────────────────────────────── │
│ Fähigkeiten                          │
│ [Keine zugeordnet]                   │
│ [+ Manuell hinzufügen]               │
│ [✨ KI-Empfehlungen]  ← Button      │
└──────────────────────────────────────┘
```

### 8.2 KI-Vorschlag für Summary (Inline)

Beim Klick auf „✨ KI-Vorschlag":
```
┌──────────────────────────────────────┐
│ ✨ KI-Vorschlag                      │
│ ─────────────────────────────────── │
│ "Partnerübung zur Entwicklung des    │
│  Distanzgefühls. Trainiert räumliche │
│  Wahrnehmung und reaktives Verhalten."│
│ ─────────────────────────────────── │
│ [✓ Übernehmen]    [✕ Verwerfen]     │
└──────────────────────────────────────┘
```

Nach „Übernehmen": Text landet im Summary-Feld, Button zeigt „↺ Neu generieren".

### 8.3 Anzeige KI-generierter Inhalte

Felder die KI-generiert sind, werden mit einem kleinen Badge markiert:
```
Zusammenfassung ✨ KI-generiert  [bearbeiten]
```

Nach manuellem Bearbeiten verschwindet der Badge.

---

## 9. Konfiguration

```env
OPENROUTER_API_KEY=sk-or-...
OPENROUTER_MODEL=anthropic/claude-sonnet-4   # Für Zusammenfassung
OPENROUTER_MODEL_FAST=anthropic/claude-haiku-4-5  # Für Skill-Empfehlungen (billiger)
```

---

## 10. Fehlerbehandlung

| Szenario | Verhalten |
|----------|-----------|
| OPENROUTER_API_KEY nicht gesetzt | KI-Buttons nicht anzeigen, kein Auto-Fallback |
| API-Timeout (> 10s) | Fehlermeldung inline: "KI momentan nicht verfügbar. Bitte manuell ausfüllen." |
| API-Fehler (5xx) | Gleiche Meldung, kein Absturz |
| Unbekannte Skills in Empfehlung | Backend filtert Skills die nicht im Katalog sind heraus |
| Summary zu lang generiert | Backend kürzt auf 200 Zeichen |

---

**Version:** 1.0
**Datum:** 2026-04-24
**Status:** DRAFT
