shinkan-jinkendo/.claude/docs/working/PLANNING_EXERCISE_SUGGEST_CONTEXT.md

187 lines
6.4 KiB
Markdown
Raw Normal View History

# Planungs-KI: Übungssuche & Kontext für Neu-Anlage
**Version:** 0.1
**Datum:** 2026-05-22
**Status:** P0 in Umsetzung (Hybrid-Retrieval ohne LLM-Intent)
**Bezüge:** `AI_PLANNING_KI_MULTISTAGE_FORECAST.md` · `AI_PROMPT_TARGET_ARCHITECTURE.md` · `SKILL_SCORING_SPEC.md` · `TRAINING_FRAMEWORK_SPEC.md` §3 (Progressionsgraph)
---
## 1. Ziel
Trainer in der **Trainingsplanung** sollen Übungen finden oder anlegen können mit natürlichen Anfragen wie:
- „Vertiefung zu Übung XY“
- „Nächste sinnvolle Übung im Progressionsgraph Z“
- „Baut auf der bisherigen Planung auf — Reaktionsschnelligkeit mit Partnern“
- **Preset:** „Schlage mir die nächste Übung vor“
**Suche** (Bibliothek) und **Neu mit KI-Assistent** (Anlage) nutzen dasselbe **`PlanningExerciseContextPack`** — unterschiedliches Ergebnis (Treffer vs. Entwurf).
---
## 2. Architektur (Mehrstufig)
| Stufe | Name | Technik | P0 |
|-------|------|---------|-----|
| **S0** | Kontext-Pack | SQL/API, deterministisch | ✅ |
| **S1a** | Intent strukturieren | Optional LLM `planning_exercise_search_intent` | Heuristik |
| **S1b** | Hybrid-Retrieval | Score: Volltext + Graph + Skills + Plan | ✅ |
| **S1c** | Rerank + Begründung | Optional LLM `planning_exercise_search_rank` | Regelbasierte `reasons[]` |
| **S2** | Neu-Anlage | Bestehende `suggestExerciseAi` + Pack als Zusatzkontext | Später |
Zwischen jeder Stufe: **nur erlaubte `exercise_id`s** (Governance / Sichtbarkeit).
---
## 3. Intent-Typen
| `intent_hint` | Bedeutung | Retrieval-Gewichtung (P0) |
|---------------|-----------|---------------------------|
| `suggest_next` | Nächste Übung (Default bei leerer/kurzer Query) | Progression + Skill-Overlap + Plan-Kontinuität |
| `progression_next` | Explizit Graph-Folge | Progression hoch |
| `deepen_exercise` | Vertiefung zu Anker-Übung | Skill-Overlap hoch, ähnlicher Fokus |
| `continue_plan_goal` | Auf bisherigen Plan aufbauen | Plan-Kontinuität, Wiederholungsstrafe |
| `free_search` | Freitext / Stichwort | Volltext hoch |
**S1a (später):** Freitext → JSON `{ intent, skill_hints[], requires_partner, level_hint, … }` validiert per Pydantic.
**P0:** `intent_hint` vom Client oder Keyword-Heuristik auf `query`.
---
## 4. PlanningExerciseContextPack (S0)
Serverseitig aus Request + DB (tokenbewusst für spätere LLM-Stufen):
| Feld | Quelle | UI-Chip |
|------|--------|---------|
| `unit_id`, Titel, `group_id`, Gruppenname | `training_units` + `training_groups` | Gruppe · Einheit |
| `section_order_index`, Abschnittstitel | `training_unit_sections` | Abschnitt |
| `planned_exercise_ids[]` | Items der Einheit (Reihenfolge) | „N Übungen im Plan“ |
| `anchor_exercise_id`, Titel | Request oder letzte Übung vor Einfügepunkt | Anker |
| `anchor_skill_ids[]` | `exercise_skills` | (intern) |
| `progression_graph_id` | Request (optional) | Graph |
| `progression_successor_ids[]` | `exercise_progression_edges` ab Anker | (intern) |
| `group_recent_exercise_ids[]` | Letzte Einheiten derselben Gruppe | Wiederholungsstrafe |
| `framework_slot_notes` | Rahmen-Slot falls `framework_slot_id` | (später) |
**Berechtigung:** `get_tenant_context` + `_assert_training_unit_permission` wie `GET /training-units/{id}`.
---
## 5. Hybrid-Retrieval (S1b, P0)
Kandidaten: sichtbare Übungen (`library_content_visibility_sql`), ohne `archived`, max. ~400 (recent).
**Score** (0–1, gewichtet nach Intent):
```
score = w_ft * fulltext_rank
+ w_prog * progression_hit
+ w_skill * skill_jaccard(anchor, candidate)
+ w_plan * plan_affinity
+ w_repeat * (candidate in unit_plan ? -1 : 0)
+ w_group_repeat * (candidate in group_recent ? -0.5 : 0)
```
**`reasons[]`** (regelbasiert, Deutsch): z. B. „Nachfolger im Progressionsgraph“, „Fähigkeiten passen zur Anker-Übung“, „Volltext-Treffer“.
---
## 6. API
### `POST /api/planning/exercise-suggest`
**Body:**
```json
{
"unit_id": 123,
"section_order_index": 0,
"phase_order_index": null,
"parallel_stream_order_index": null,
"anchor_exercise_id": 456,
"progression_graph_id": 7,
"query": "Schlage mir die nächste Übung vor",
"intent_hint": "suggest_next",
"limit": 20,
"exercise_kind_any": ["simple"]
}
```
**Response:**
```json
{
"context_summary": {
"unit_title": "…",
"group_name": "…",
"section_title": "Hauptteil",
"planned_count": 4,
"anchor_title": "Partner-Fangspiel"
},
"intent_resolved": "suggest_next",
"hits": [
{
"id": 99,
"title": "…",
"summary": "…",
"score": 0.78,
"reasons": ["Nachfolger im Progressionsgraph", "3 gemeinsame Fähigkeiten mit Anker-Übung"],
"focus_area": "…"
}
]
}
```
**Modul:** `backend/planning_exercise_suggest.py` · Router `backend/routers/planning_exercise_suggest.py`
---
## 7. Frontend
| Ort | Verhalten |
|-----|-----------|
| `ExercisePickerModal` | Prop `planningContext` → Planungs-API statt reiner `listExercises`; Kontext-Chips; `reasons` unter Treffer |
| `TrainingUnitEditPage` | `planningContext` aus Einheit + Picker-Ziel (Anker = letzte Übung im Abschnitt) |
| Rahmen / Kombi-Formular | analog, sobald `unit_id` / Slot-Blueprint bekannt |
| Übungsliste | weiter Volltext; Schalter „Neu mit KI-Assistent“ ohne Planungs-Pack |
**Zweites Suchfeld** im Picker: Query = Volltext + ergänzender Begriff (ODER in P0 als Konkatenation an Backend).
---
## 8. Neu-Anlage (Anbindung, Phase P1)
Wenn `hits` leer oder Trainer wählt „Mit KI anlegen“:
- Gleiches `context_summary` an `suggestExerciseAi` anhängen (Felder `planning_context_json` o. ä. — noch offen)
- Kurzbeschreibung optional leer (freier Vorschlag) oder aus Intent/Skizze
---
## 9. Phasen-Roadmap
| Phase | Inhalt |
|-------|--------|
| **P0** ✅ | Context-Pack, Hybrid-Score, API, Picker in Planung |
| **P1** | LLM Intent-JSON; Neu-Anlage mit Pack |
| **P2** | LLM-Rerank + Kurzbegründung |
| **P3** | Skill-Discovery / Framework-Ziele im Pack |
---
## 10. Changelog
- **2026-05-22:** Erstfassung; P0 API + Planungs-Picker.
- **2026-05-22:** P0 implementiert (`planning_exercise_suggest.py`, Router, Picker); unsaved Formular-Plan noch nicht an API (nur persistierte Einheit).
---
## 11. Bekannte P0-Lücken
- **Ungespeicherte Plan-Änderungen:** API liest DB-Stand der Einheit — offene Formular-Items folgen in P0.1 (Client übergibt `planned_exercise_ids[]`).
- **Progressionsgraph-ID:** noch nicht aus UI wählbar (`progression_graph_id` nur per API).
- **LLM-Intent / Rerank:** P1/P2 laut Roadmap §9.