# 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.