shinkan-jinkendo/.claude/docs/working/PLANNING_EXERCISE_SUGGEST_CONTEXT.md
Lars a19ed02300
All checks were successful
Deploy Development / deploy (push) Successful in 44s
Test Suite / pytest-backend (push) Successful in 40s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 12s
Test Suite / k6 /health Baseline (push) Successful in 33s
Test Suite / playwright-tests (push) Successful in 1m13s
Implement Phase C3 Enhancements for Progression Path Suggestion
- Incremented version to 0.8.185, reflecting the implementation of Phase C3 features.
- Introduced the `POST /api/planning/progression-path-suggest` endpoint for generating exercise progression paths.
- Enhanced the ExerciseProgressionGraphPanel with a new ExerciseProgressionPathBuilder for reviewing and saving paths.
- Updated changelog to document the new capabilities in planning AI functionality.
2026-05-23 11:46:25 +02:00

18 KiB
Raw Blame History

Planungs-KI: Übungssuche & Kontext für Neu-Anlage

Version: 0.2
Datum: 2026-05-23
Status: P0P2 · Phase A/B/B2 · Phase C1C3 (Progressionsgraph + Varianten + Pfad-Builder)
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 LLM planning_exercise_search_intent (Szenario-Pipeline) P1
S1b Hybrid-Retrieval Score: Volltext + Graph + Skills + Plan + Profil
S1b+ Profil-Vorselektion ExerciseMatchProfile × PlanningTargetProfile profile_v1
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_ids (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 oder Auto-Match vom Anker (sichtbarer Graph mit passenden Ausgangskanten) Graph
progression_graph_name, progression_graph_auto_resolved Response context_summary Graph (auto)
anchor_exercise_variant_id Request / Abschnitt-Item / DB (intern)
progression_successor_ids[] exercise_progression_edges ab Anker (variantenbewusst, Migration 034) (intern)
progression_successor_variants to_exercise_variant_id pro Nachfolger (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 (01, gewichtet nach Intent):

score = w_ft * fulltext_rank
      + w_prog * progression_hit
      + w_skill * skill_jaccard(anchor, candidate)
      + w_plan * plan_affinity
      + w_profile * profile_match(exercise, target)
      + w_repeat * (candidate in unit_plan ? -1 : 0)
      + w_group_repeat * (candidate in group_recent ? -0.5 : 0)

profile_match (01): siehe §12§13 — Katalog-Dimensionen + Skill-Gewichte + Skill-Gap.

reasons[] (regelbasiert, Deutsch): z. B. „Nachfolger im Progressionsgraph“, „Fähigkeiten passen zur Anker-Übung“, „Fokusbereich passend zum Planungsziel“, „Deckt Skill-Lücke im bisherigen Plan“, „Volltext-Treffer“.


6. API

POST /api/planning/exercise-suggest

Body:

{
  "unit_id": 123,
  "section_order_index": 0,
  "phase_order_index": null,
  "parallel_stream_order_index": null,
  "anchor_exercise_id": 456,
  "anchor_exercise_variant_id": 12,
  "progression_graph_id": 7,
  "query": "Schlage mir die nächste Übung vor",
  "intent_hint": "suggest_next",
  "limit": 20,
  "exercise_kind_any": ["simple"]
}

Response:

{
  "context_summary": {
    "unit_title": "…",
    "group_name": "…",
    "section_title": "Hauptteil",
    "planned_count": 4,
    "anchor_title": "Partner-Fangspiel"
  },
  "target_profile_summary": {
    "sources": ["framework_catalog", "current_unit_plan", "anchor_exercise"],
    "focus_areas": ["Reaktion & Abwehr"],
    "top_skills": [{ "skill_id": 12, "name": "Reaktionsgeschwindigkeit", "weight": 1.0 }],
    "has_skill_gap": true
  },
  "retrieval_phase": "profile_v1",
  "intent_resolved": "suggest_next",
  "hits": [
    {
      "id": 99,
      "title": "…",
      "summary": "…",
      "score": 0.78,
      "reasons": ["Nachfolger im Progressionsgraph", "Fokusbereich passend zum Planungsziel"],
      "focus_area": "…"
    }
  ]
}

Modul: backend/planning_exercise_suggest.py · backend/planning_exercise_profiles.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)
ExercisesListPageRoot Schalter „Neu mit KI-Assistent“: Planungs-KI-Suche (frei, ohne unit_id) + Neuanlage im Modal; „+ Neu“ ausgeblendet
Rahmen / Kombi-Formular analog, sobald unit_id / Slot-Blueprint bekannt
Übungsliste (ohne KI-Schalter) weiter Volltext

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 Status
P0 Context-Pack, Hybrid-Score, API, Picker in Planung
P0.1 ExerciseMatchProfile / PlanningTargetProfile, profile_v1
P1 Szenario-Pipeline + LLM Query-Intent → Erwartungsprofil
P2 / B2 LLM-Rerank bei engem Top-Feld (max. 2 Calls)
P3 Skill-Discovery / Framework-Ziele im Pack 🔲
A Voll-Library Hybrid-Ranking 0.8.177
B Text-Signale guidance/Rahmen-Ziele 0.8.181
C1 Graph auto-match + variantenbewusste Nachfolger 0.8.183
C2 Varianten in Trefferliste / Picker 0.8.184
C3 Graph-Builder (Ziel → Pfad → speichern) 0.8.185
D Neu-Anlage: Pack an suggestExerciseAi 🔲

10. Changelog

  • 2026-05-23: Phase C1 — Graph auto-match, variantenbewusste Nachfolger (planning_exercise_progression.py).
  • 2026-05-23: Phase B2 — Rerank bei engem Top-Feld; Phase B — Text-Signale; Phase A — Voll-Library (siehe §17§19).
  • 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).
  • 2026-05-22: P0.1 — planning_exercise_profiles.py, Profil-Score in Hybrid-Retrieval, retrieval_phase: profile_v1, target_profile_summary.
  • 2026-05-22: P2 — LLM-Rerank optional (include_llm_rank); Client planned_exercise_ids[]; Prompt Migration 072.

11. Bekannte Lücken & Backlog

  • Ungespeicherte Plan-Änderungen: Client übergibt planned_exercise_ids[] aus Formular (TrainingUnitEditPage).
  • Progressionsgraph-ID: Auto-Match vom Anker (C1); manuelle Auswahl in UI noch offen.
  • Anker-Variante: Client + DB (C1); Picker wählt Variante bei Treffer (C2 — Dropdown + Graph-Vorschlag).
  • Graph-Builder (C3): Ziel → Pfad vorschlagen → in Graph speichern — 0.8.185
  • Varianten-Suche: Library-Picker nutzt include_variants; Planungs-KI rankt primär Übungsebene — Varianten-Expansion nur gezielt (C2).
  • Enrichment: Superadmin-Tool für Skills; Datenqualität der Bibliothek entscheidend für Profil-Score.
  • LLM-Intent: P1 Szenario-Pipeline + planning_exercise_search_intent (Migration 073).
  • Preset + LLM: Erwartungs-LLM (074) bei Planungsbezug; Preset ohne Plan = kein Erwartungs-LLM.

16. Szenario-Pipeline & Query-Erwartungsprofil (P1)

Komplexe Planungsanfragen brauchen Schritte vor dem Profil-Match — nicht jede Query ist gleich.

16.1 Szenario-Klassen

scenario_kind Typische Anfrage LLM Intent?
preset_next „Nächste Übung vorschlagen“ (Preset) Erwartungs-LLM (074) wenn Planungsbezug
progression Progressionsgraph / Pfad Ja (wenn Freitext)
deepen Vertiefung Anker Ja
continue_plan Auf bisherigen Plan aufbauen Ja
additive_constraint Plan + Zusatz (z. B. Schnellkraft) Ja
free_search Offene Stichwortsuche Ja

Routing: planning_exercise_target_pipeline.classify_planning_scenario()should_run_llm_intent_pipeline().

16.2 Pipeline (Reihenfolge)

S0 Kontext-Pack
 → Heuristik-Intent + Szenario
 → [optional] LLM planning_exercise_search_intent
 → Basis PlanningTargetProfile (Rahmen, Plan, Anker, Gap)
 → Merge Query-Overlay (Katalog-IDs aus Hints)
 → Hybrid-Retrieval + Profil-Score
 → [optional] LLM-Rerank

Module: planning_exercise_target_pipeline.py · planning_exercise_intent.py

16.3 API (Erweiterung)

Request Default Bedeutung
include_llm_intent true LLM nur wenn Szenario ≠ preset_next und Query nicht leer
Response Bedeutung
scenario_kind Szenario-Klasse
query_intent_summary intent, llm_applied, rationale, skill_hints_resolved
intent_heuristic Heuristik vor LLM
retrieval_phase z. B. profile_v1+query_intent+llm_rank

Prompt 073: planning_exercise_search_intent — Ausgabe JSON mit skill_hints, focus_hints, emphasis (additive|replace).


15. LLM-Rerank (P2)

Request:

Feld Typ Default Bedeutung
planned_exercise_ids int[] Optional: Reihenfolge aus Formular (überschreibt DB-Plan)
include_llm_rank bool true (Client) Backend gated (B2): Rerank nur bei engem Top-Feld, max. 2 LLM-Calls

Response:

Feld Wert
retrieval_phase profile_v1 oder profile_v1+llm_rank
llm_rank_applied true wenn LLM erfolgreich sortiert hat
hits[].llm_rank optional: Position nach LLM (1…n)

Fallback: Kein API-Key, inaktiver Prompt oder Parse-Fehler → Hybrid-Reihenfolge unverändert, llm_rank_applied: false.

Prompt: Migration 072, Slug planning_exercise_search_rank — Kandidaten als JSON mit Titel, summary, goal (Plaintext), skills; Ausgabe { ranked_ids, reasons }.


12. ExerciseMatchProfile & PlanningTargetProfile (Phase 1)

Ziel: deterministische Vorselektion über Profil-Dimensionen statt nur Titel/Jaccard.

12.1 ExerciseMatchProfile (pro Übung)

Feld Quelle
focus_area_ids exercise_focus_areas (Primary = 1.0, sonst 0.85)
style_direction_ids exercise_style_directions
training_type_ids exercise_training_types
target_group_ids exercise_target_groups
skill_weights exercise_skills × Intensitäts-Multiplikator (skill_scoring._skill_link_multiplier)

Bulk-Lader: load_exercise_match_profiles_bulk(cur, exercise_ids).

12.2 PlanningTargetProfile (Planungsziel)

Zusammensetzung aus mehreren Quellen (sources[]):

Quelle Inhalt
framework_catalog Fokus/Stil/Trainingsstil/Zielgruppe aus training_framework_program_*
framework_slot_skill_profile Skill-Profil des Slot-Blueprints (profile_for_occurrences)
framework_overall_skill_profile Fallback: alle Blueprint-Einheiten des Rahmens
current_unit_plan Skill-Profil der bereits eingeplanten Übungen dieser Einheit
anchor_exercise Katalog + Skills der Anker-Übung (Intent-abhängig)
skill_gap_vs_plan target_skills plan_skills (normalisiert, Schwelle > 0.08)

Builder: build_planning_target_profile(cur, unit=…, planned_exercise_ids=…, anchor_exercise_id=…, intent=…).

Rahmen-Anbindung über unit.framework_slot_id oder origin_framework_slot_id.


13. Profil-Score (Formeln)

Gewichtete Überlappung (Katalog + Skills):

overlap(a, b) = Σ min(a[k], b[k]) / Σ max(a[k], b[k])

Skill-Gap-Abdeckung:

gap_coverage(gap, candidate) = Σ min(gap[k], candidate[k]) / Σ gap[k]

Profil-Score (intent-gewichtet, Summe Dimensionen = 1.0):

profile_score = w_focus * overlap(focus)
              + w_style * overlap(style)
              + w_tt    * overlap(training_type)
              + w_tg    * overlap(target_group)
              + w_skill * overlap(skill_weights)
              + w_gap   * gap_coverage(skill_gap)

Intent-Gewichte (Auszug): deepen_exercise → Skill hoch; continue_plan_goal → Gap hoch; free_search → Gap + Skill moderat.

Scorer: score_exercise_against_target(exercise_profile, target_profile, intent=…) → (score, reasons[]).


14. Hybrid + Profil (P0.1)

Im Hybrid-Score kommt w_profile * profile_score hinzu (Intent-abhängig ~0.150.35). Jaccard auf Anker-Skills bleibt parallel (schneller Anker-Fokus).

Response-Felder:

Feld Bedeutung
retrieval_phase "profile_v1" — Phase-1 aktiv, kein LLM-Rerank
target_profile_summary Lesbare Kurzinfo für UI-Chips (Fokus, Top-Skills, Quellen)

Phase 2 (P2 / B2): siehe §15 und §18 — include_llm_rank: true vom Client, Backend entscheidet.


17. Phase A — Voll-Library-Ranking (0.8.177)

  • Kein OR-Profil-Pool (~500 Übungen) mehr.
  • Alle sichtbaren Übungen (bis 8000) werden hybrid gescored (fetch_all_visible_exercise_rows + rank_visible_library_hits).
  • API: full_library_ranked: true, retrieval_phase enthält +full_library+.

18. Phase B / B2 — Text-Signale & Rerank-Gates (0.8.1810.8.182)

B — Text-Signale (planning_exercise_text_signals.py):

  • section_guidance_notes, Rahmen-Ziele/Notizen → Skill-/Katalog-Gewichte ohne LLM.
  • requires_partner aus Intent filtert Kandidaten.
  • retrieval_phase +text_signals.

B2 — Rerank bei unklarem Ranking:

  • hybrid_ranking_ambiguous(hits) (Top-4-/Top-10-Gap).
  • Rerank auch nach Erwartungs-/Intent-LLM, wenn Scores eng beieinander.
  • Budget: max. 2 LLM-Calls (Profil + optional Rerank).

19. Phase C1 — Progressionsgraph im Planungskontext (0.8.183)

Modul: planning_exercise_progression.py

Auto-Match Graph

Wenn progression_graph_id fehlt und Anker-Übung gesetzt: sichtbarer Graph mit passender next_exercise-Kante vom Anker (variantenbewusst). Bevorzugung: variantenspezifische Kanten > Anzahl Kanten.

Variantenbewusste Nachfolger (Migration 034)

Generische Kante (from_exercise_variant_id IS NULL) gilt für jeden Anker; variantenspezifische Kante nur bei passender Anker-Variante.

Treffer: optional hits[].suggested_variant_id.

Request / Response

Feld Bedeutung
anchor_exercise_variant_id Request — Variante der Anker-Übung
progression_graph_name Response — Name des (auto-)Graphs
progression_graph_auto_resolved Response — Auto-Match aktiv

20. Phase C2 — Varianten in Treffern (0.8.184)

  • API: variants[], suggested_variant_name pro Treffer (Batch aus exercise_variants).
  • ExercisePickerModal: Dropdown pro Treffer; Graph-suggested_variant_id vorausgewählt; Übernahme setzt exercise_variant_id.
  • hydrateExercisePlanningRow: übernimmt exercise_variant_id / suggested_variant_id in die Planungszeile.

21. Phase C3 — Graph-Builder (0.8.185)

API: POST /api/planning/progression-path-suggest

Feld Bedeutung
query Ziel / Entwicklungsrichtung (Freitext, min. 3 Zeichen)
max_steps 210, Default 5
progression_graph_id optional — Graph-Kontext für Nachfolger ab Schritt 2
include_llm_intent LLM nur Schritt 1 (Budget)

Response: steps[] mit exercise_id, variant_id, title, reasons, variants; retrieval_phase: …+path_builder.

Algorithmus: Iterativ Hybrid-Ranking — Schritt 1 aus Zielprofil, Folgeschritte mit Anker = letzte Übung, ohne Duplikate.

UI: ExerciseProgressionPathBuilder im Progressionsgraph-Panel — Review, Varianten, POST …/edges/sequence.


22. Backlog (offen)