Progression und additional specific text placeholders #58

Merged
Lars merged 11 commits from develop into main 2026-06-18 11:28:45 +02:00
4 changed files with 23 additions and 260 deletions
Showing only changes of commit 35fe31774b - Show all commits

View File

@ -1,4 +1,6 @@
-- Migration 092: Katalog-Prompt-Slots (H2) — Slot-Typ-Vokabular + Werte pro Stammdaten-Zeile -- Migration 092: Katalog-Prompt-Slots (H2) — Slot-Typ-Vokabular + leere Wertetabelle
-- Keine Inhalts-Seeds: Stammdaten (Primärfokus etc.) sind mandantenspezifisch.
-- Slot-Inhalte: Admin-UI oder catalog_slot_fallbacks.py (Namens-Match zur Laufzeit).
CREATE TABLE IF NOT EXISTS catalog_prompt_slot_types ( CREATE TABLE IF NOT EXISTS catalog_prompt_slot_types (
slot_key VARCHAR(64) PRIMARY KEY, slot_key VARCHAR(64) PRIMARY KEY,
@ -82,95 +84,3 @@ VALUES
true true
) )
ON CONFLICT (slot_key) DO NOTHING; ON CONFLICT (slot_key) DO NOTHING;
-- Seed aus H1-Registry (Name-Match auf Stammdaten)
INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content)
SELECT 'focus_area', fa.id, 'description',
'Planung zielt auf Prävention, Deeskalation, Grenzen und sichere Übungsformen — nicht auf Wettkampf-Perfektion oder Technik-Show.'
FROM focus_areas fa WHERE fa.name ILIKE 'Gewaltschutz'
ON CONFLICT (catalog_kind, catalog_id, slot_key) DO UPDATE SET content = EXCLUDED.content, updated_at = NOW();
INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content)
SELECT 'focus_area', fa.id, 'hints_on_path_qa',
'Gute Pfade bauen Sicherheit, Kommunikation und Alternativen auf; „Lücken“ sind fehlende Deeskalations- oder Rollenspiel-Stufen, nicht fehlende Kick-Varianten.'
FROM focus_areas fa WHERE fa.name ILIKE 'Gewaltschutz'
ON CONFLICT (catalog_kind, catalog_id, slot_key) DO UPDATE SET content = EXCLUDED.content, updated_at = NOW();
INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content)
SELECT 'focus_area', fa.id, 'anti_patterns',
'Nicht nach Kumite-Tiefe, Explosivität oder Wettkampf-Belastung bewerten.'
FROM focus_areas fa WHERE fa.name ILIKE 'Gewaltschutz'
ON CONFLICT (catalog_kind, catalog_id, slot_key) DO UPDATE SET content = EXCLUDED.content, updated_at = NOW();
INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content)
SELECT 'training_type', tt.id, 'description',
'Partizipation, Verständlichkeit, Freude am Bewegen; weniger maximale Spezialisierung.'
FROM training_types tt WHERE tt.name ILIKE 'Breitensport'
ON CONFLICT (catalog_kind, catalog_id, slot_key) DO UPDATE SET content = EXCLUDED.content, updated_at = NOW();
INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content)
SELECT 'training_type', tt.id, 'hints_on_path_qa',
'Hohe OK-Rate bei moderatem Schwierigkeitsanstieg; „Perfektion“-Stufen nur optional, nicht als Pflicht-Lücke.'
FROM training_types tt WHERE tt.name ILIKE 'Breitensport'
ON CONFLICT (catalog_kind, catalog_id, slot_key) DO UPDATE SET content = EXCLUDED.content, updated_at = NOW();
INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content)
SELECT 'training_type', tt.id, 'rematch_guard',
'Keine leeren Slots erzwingen, nur um eine Leistungs-Perfektionsstufe zu füllen.'
FROM training_types tt WHERE tt.name ILIKE 'Breitensport'
ON CONFLICT (catalog_kind, catalog_id, slot_key) DO UPDATE SET content = EXCLUDED.content, updated_at = NOW();
INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content)
SELECT 'target_group', tg.id, 'description',
'Kinder: kurze Einheiten, spielerische Einstiege, Sicherheit und altersgerechte Komplexität.'
FROM target_groups tg WHERE tg.name ILIKE 'Kinder'
ON CONFLICT (catalog_kind, catalog_id, slot_key) DO UPDATE SET content = EXCLUDED.content, updated_at = NOW();
INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content)
SELECT 'target_group', tg.id, 'hints_on_path_qa',
'Didaktik ohne Überforderung; klare Regeln und Sicherheit vor Perfektion; Lücken bei Spiel-/Rollenelementen wichtiger als Wettkampftiefe.'
FROM target_groups tg WHERE tg.name ILIKE 'Kinder'
ON CONFLICT (catalog_kind, catalog_id, slot_key) DO UPDATE SET content = EXCLUDED.content, updated_at = NOW();
INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content)
SELECT 'target_group', tg.id, 'anti_patterns',
'Keine Erwachsenen-Wettkampf-Perfektion als QS-Maßstab.'
FROM target_groups tg WHERE tg.name ILIKE 'Kinder'
ON CONFLICT (catalog_kind, catalog_id, slot_key) DO UPDATE SET content = EXCLUDED.content, updated_at = NOW();
INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content)
SELECT 'target_group', tg.id, 'description',
'Leistungsgruppe: höhere Anspruchskurven und Spezialisierung sind fachlich passend.'
FROM target_groups tg WHERE tg.name ILIKE 'Leistungssportler'
ON CONFLICT (catalog_kind, catalog_id, slot_key) DO UPDATE SET content = EXCLUDED.content, updated_at = NOW();
INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content)
SELECT 'target_group', tg.id, 'hints_on_path_qa',
'Höhere Anspruchskurven, Belastungs- und Kombinationsprogressionen sind relevant; Lücken in Spezialisierung können echte Hinweise sein.'
FROM target_groups tg WHERE tg.name ILIKE 'Leistungssportler'
ON CONFLICT (catalog_kind, catalog_id, slot_key) DO UPDATE SET content = EXCLUDED.content, updated_at = NOW();
INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content)
SELECT 'style_direction', sd.id, 'description',
'Shotokan-Linie: klare Kihon-Struktur, Hüft- und Standarbeit als wiederkehrende Qualitätsanker.'
FROM style_directions sd WHERE sd.name ILIKE 'Shotokan'
ON CONFLICT (catalog_kind, catalog_id, slot_key) DO UPDATE SET content = EXCLUDED.content, updated_at = NOW();
INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content)
SELECT 'style_direction', sd.id, 'hints_on_progression',
'Nuancen in Stellung und Hüfttechnik, kein neuer Planungstyp.'
FROM style_directions sd WHERE sd.name ILIKE 'Shotokan'
ON CONFLICT (catalog_kind, catalog_id, slot_key) DO UPDATE SET content = EXCLUDED.content, updated_at = NOW();
INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content)
SELECT 'training_type', tt.id, 'description',
'Wettkampforientiertes Training mit höherer Anspruchskurve und belastungsnahen Phasen.'
FROM training_types tt WHERE tt.name ILIKE 'Wettkampf'
ON CONFLICT (catalog_kind, catalog_id, slot_key) DO UPDATE SET content = EXCLUDED.content, updated_at = NOW();
INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content)
SELECT 'training_type', tt.id, 'hints_on_path_qa',
'Spezialisierung, Kombination und Belastung unter Druck sind relevant; Lücken in Anwendungs- oder Perfektionsphasen können echte Hinweise sein.'
FROM training_types tt WHERE tt.name ILIKE 'Wettkampf'
ON CONFLICT (catalog_kind, catalog_id, slot_key) DO UPDATE SET content = EXCLUDED.content, updated_at = NOW();

View File

@ -1,167 +1,11 @@
-- Migration 094: Vollständige Befüllung catalog_prompt_slots (H1-Inhalte + Defaults für alle Stammdaten) -- Migration 094: bewusst ohne Daten-Seed (ersetzt frühere Voll-Befüllung)
--
-- Katalog-Stammdaten (Primärfokus, Trainingsstile, …) sind pro Umgebung unterschiedlich.
-- Migrationen dürfen keine Dev-Standardtexte an Prod-Kataloge hängen.
--
-- Slot-Inhalte stattdessen:
-- • Admin-UI (catalog_prompt_slots pro Eintrag)
-- • Laufzeit-Fallback catalog_slot_fallbacks.py (Namens-Match, kein DB-Schreiben)
-- • optional: backend/scripts/seed_catalog_prompt_slots_dev.py (nur lokale Dev-DB)
CREATE TEMP TABLE IF NOT EXISTS _catalog_slot_seed ( SELECT 1;
catalog_kind VARCHAR(32) NOT NULL,
name_pattern TEXT NOT NULL,
slot_key VARCHAR(64) NOT NULL,
content TEXT NOT NULL
);
TRUNCATE _catalog_slot_seed;
-- Primärfokus Karate (häufigster Technik-Pfad)
INSERT INTO _catalog_slot_seed (catalog_kind, name_pattern, slot_key, content) VALUES
('focus_area', 'Karate', 'description',
'Technik-Curriculum im Karate-Kontext: aufeinander aufbauende Kihon-Progression mit klaren Qualitätsankern (Stand, Hüfte, Kime).'),
('focus_area', 'Karate', 'hints_on_progression',
'Typische Phasen: Einstieg → Grundlagen → Koordination/Kraft → Anwendung → optional Vertiefung; Grundlagen vor Perfektion.'),
('focus_area', 'Karate', 'hints_on_exercise',
'Kihon und Partnerübungen mit Technikbezug; reine Kraft-/Ausdauer-Inseln nur mit klarer Begründung.'),
('focus_area', 'Karate', 'hints_on_path_qa',
'Kohärente Progression Grundlagen → Anwendung → Vertiefung; Übergänge ohne Sprünge; themenfremde Kraft-/Ausdauer-Inseln abwerten.'),
('focus_area', 'Karate', 'anti_patterns',
'Keine pauschale Perfektions-Stufe verlangen, wenn der Trainingsstil Breitensport ist.');
-- Selbstverteidigung
INSERT INTO _catalog_slot_seed VALUES
('focus_area', 'Selbstverteidigung', 'description',
'Praktische Selbstverteidigung: realistische Szenarien, Sicherheit und anwendungsnahe Progression — nicht Show-Technik oder Wettkampf-Kata.'),
('focus_area', 'Selbstverteidigung', 'hints_on_progression',
'Von Wahrnehmung und Distanz zu einfachen Abwehrmustern und kontrollierter Anwendung.'),
('focus_area', 'Selbstverteidigung', 'hints_on_exercise',
'Partnerübungen mit klaren Sicherheitsregeln; Szenario-Bezug wichtiger als Stil-Show.'),
('focus_area', 'Selbstverteidigung', 'hints_on_path_qa',
'Lücken bei Szenario- oder Sicherheitsstufen sind relevant; fehlende Kick-Varianten oder Wettkampftiefe sind kein Mangel.'),
('focus_area', 'Selbstverteidigung', 'anti_patterns',
'Keine Wettkampf- oder Kata-Perfektion als QS-Maßstab.');
-- Gewaltschutz (ergänzt 092)
INSERT INTO _catalog_slot_seed VALUES
('focus_area', 'Gewaltschutz', 'hints_on_progression',
'Phasen: Wahrnehmung → Grenzen → Deeskalation → sichere Übungsformen; keine Kumite-Perfektionsstufen erzwingen.'),
('focus_area', 'Gewaltschutz', 'hints_on_exercise',
'Übungen mit Rollen, Kommunikation, Ausweichen; keine rein technischen Kick-Fokus-Inseln ohne Bezug.');
-- Fitness (falls vorhanden)
INSERT INTO _catalog_slot_seed VALUES
('focus_area', 'Fitness', 'description',
'Fitness- und Konditionsorientierung mit sicherer Belastungssteuerung; Technikbezug nur wo fachlich sinnvoll.'),
('focus_area', 'Fitness', 'hints_on_progression',
'Progression von niedriger zu moderater Belastung; klare Pausen und Technikhygiene.'),
('focus_area', 'Fitness', 'hints_on_path_qa',
'Keine Wettkampf-Spezialisierung als Pflicht-Kriterium; Belastungssteigerung ohne Technikbezug abwerten.'),
('focus_area', 'Fitness', 'anti_patterns',
'Keine Kumite-Perfektion oder Wettkampf-Kombinationen als QS-Maßstab verlangen.');
-- Trainingsstile (global)
INSERT INTO _catalog_slot_seed VALUES
('training_type', 'Breitensport', 'hints_on_progression',
'Moderater Schwierigkeitsanstieg; Perfektionsphasen optional.'),
('training_type', 'Breitensport', 'anti_patterns',
'Keine Leistungssport-Perfektion als Pflicht-Lücke.'),
('training_type', 'Leistungssport', 'description',
'Leistungsorientiertes Training mit höherer Anspruchskurve und Spezialisierung.'),
('training_type', 'Leistungssport', 'hints_on_progression',
'Belastungs- und Kombinationsprogressionen sind erwünscht.'),
('training_type', 'Leistungssport', 'hints_on_path_qa',
'Höhere Anspruchskurven sind passend; Lücken in Spezialisierung können echte Hinweise sein.'),
('training_type', 'Wettkampf', 'hints_on_progression',
'Anwendungs- und Druckphasen zeitig einplanen.');
-- Zielgruppen
INSERT INTO _catalog_slot_seed VALUES
('target_group', 'Breitensportler', 'description',
'Breitensport: Partizipation und Verständlichkeit vor maximaler Spezialisierung.'),
('target_group', 'Breitensportler', 'hints_on_path_qa',
'Moderate Progression; Perfektions-Lücken sind selten echte Mängel.'),
('target_group', 'Breitensportler', 'anti_patterns',
'Keine Leistungssport-Perfektion als Pflicht-Kriterium.'),
('target_group', 'Kinder', 'hints_on_progression',
'Spielerische Einstiege; kurze Abschnitte; Sicherheit vor Perfektion.'),
('target_group', 'Leistungssportler', 'hints_on_progression',
'Anspruchskurve und Spezialisierung dürfen steiler sein.');
-- Stilrichtungen (generisch + Shotokan-Details via 092)
INSERT INTO _catalog_slot_seed VALUES
('style_direction', 'Goju-Ryu', 'hints_on_progression',
'Stil-Nuancen (Stand, Atem, Kime) einbeziehen — kein Stilwechsel erzwingen.'),
('style_direction', 'Wado-Ryu', 'hints_on_progression',
'Stil-Nuancen (Stand, Atem, Kime) einbeziehen — kein Stilwechsel erzwingen.'),
('style_direction', 'Shito-Ryu', 'hints_on_progression',
'Stil-Nuancen (Stand, Atem, Kime) einbeziehen — kein Stilwechsel erzwingen.'),
('style_direction', 'Kyokushin', 'hints_on_progression',
'Stil-Nuancen (Stand, Belastung, Kime) einbeziehen — kein Stilwechsel erzwingen.');
-- Fokusbereiche: aus Seed-Tabelle
INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content)
SELECT s.catalog_kind, fa.id, s.slot_key, s.content
FROM _catalog_slot_seed s
JOIN focus_areas fa ON fa.name ILIKE s.name_pattern
WHERE s.catalog_kind = 'focus_area'
ON CONFLICT (catalog_kind, catalog_id, slot_key)
DO UPDATE SET content = EXCLUDED.content, updated_at = NOW();
INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content)
SELECT s.catalog_kind, tt.id, s.slot_key, s.content
FROM _catalog_slot_seed s
JOIN training_types tt ON tt.name ILIKE s.name_pattern
WHERE s.catalog_kind = 'training_type'
ON CONFLICT (catalog_kind, catalog_id, slot_key)
DO UPDATE SET content = EXCLUDED.content, updated_at = NOW();
INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content)
SELECT s.catalog_kind, tg.id, s.slot_key, s.content
FROM _catalog_slot_seed s
JOIN target_groups tg ON tg.name ILIKE s.name_pattern
WHERE s.catalog_kind = 'target_group'
ON CONFLICT (catalog_kind, catalog_id, slot_key)
DO UPDATE SET content = EXCLUDED.content, updated_at = NOW();
INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content)
SELECT s.catalog_kind, sd.id, s.slot_key, s.content
FROM _catalog_slot_seed s
JOIN style_directions sd ON sd.name ILIKE s.name_pattern
WHERE s.catalog_kind = 'style_direction'
ON CONFLICT (catalog_kind, catalog_id, slot_key)
DO UPDATE SET content = EXCLUDED.content, updated_at = NOW();
-- Default-Technik-Pack für Fokusbereiche ohne hints_on_path_qa (außer Gewaltschutz/Fitness)
INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content)
SELECT 'focus_area', fa.id, 'hints_on_path_qa',
'Kohärente Progression zum Anfrage-Thema; Lücken sind fehlende Zwischenstufen im Lernpfad, nicht fehlende Nebenthemen.'
FROM focus_areas fa
WHERE fa.name NOT ILIKE 'Gewaltschutz'
AND fa.name NOT ILIKE 'Fitness'
AND NOT EXISTS (
SELECT 1 FROM catalog_prompt_slots cps
WHERE cps.catalog_kind = 'focus_area' AND cps.catalog_id = fa.id AND cps.slot_key = 'hints_on_path_qa'
AND TRIM(cps.content) <> ''
)
ON CONFLICT (catalog_kind, catalog_id, slot_key) DO NOTHING;
INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content)
SELECT 'focus_area', fa.id, 'hints_on_progression',
'Grundlagen vor Anwendung; moderate Sprünge zwischen Stufen vermeiden.'
FROM focus_areas fa
WHERE fa.name NOT ILIKE 'Gewaltschutz'
AND fa.name NOT ILIKE 'Fitness'
AND NOT EXISTS (
SELECT 1 FROM catalog_prompt_slots cps
WHERE cps.catalog_kind = 'focus_area' AND cps.catalog_id = fa.id AND cps.slot_key = 'hints_on_progression'
AND TRIM(cps.content) <> ''
)
ON CONFLICT (catalog_kind, catalog_id, slot_key) DO NOTHING;
-- Stilrichtungen ohne Eintrag: generischer Progressions-Hinweis
INSERT INTO catalog_prompt_slots (catalog_kind, catalog_id, slot_key, content)
SELECT 'style_direction', sd.id, 'hints_on_progression',
'Stil-spezifische Nuancen (Stand, Hüfte, Rhythmus) einbeziehen — ohne Stilwechsel zu erzwingen.'
FROM style_directions sd
WHERE NOT EXISTS (
SELECT 1 FROM catalog_prompt_slots cps
WHERE cps.catalog_kind = 'style_direction' AND cps.catalog_id = sd.id AND cps.slot_key = 'hints_on_progression'
AND TRIM(cps.content) <> ''
)
ON CONFLICT (catalog_kind, catalog_id, slot_key) DO NOTHING;
DROP TABLE IF EXISTS _catalog_slot_seed;

View File

@ -0,0 +1,7 @@
-- Migration 095: Entfernt per 092/094 (alte Version) eingespielte Standard-Slot-Texte
--
-- Betrifft Dev-Umgebungen, die die frühen Seed-Migrationen bereits erhalten haben.
-- Prod mit eigener Katalogstruktur: Tabelle bleibt leer bis Admin-Inhalte gepflegt werden.
-- Manuell in der Admin-UI gesetzte Texte nach 095 bitte erneut prüfen (waren ggf. identisch mit Seeds).
DELETE FROM catalog_prompt_slots;

View File

@ -115,6 +115,8 @@ UNIQUE (catalog_kind, catalog_id, slot_key)
Neuer Katalog-Eintrag im Admin → **keine** Code-Änderung; Slots optional befüllen. Neuer Katalog-Eintrag im Admin → **keine** Code-Änderung; Slots optional befüllen.
**Keine Migrations-Seeds:** Primärfokus und andere Stammdaten sind pro Umgebung unterschiedlich. Leere `catalog_prompt_slots` sind normal; zur Laufzeit greifen Namens-Fallbacks (`catalog_slot_fallbacks.py`) und die `description`-Spalte der Stammdaten.
--- ---
## 7. Laufzeit-Architektur ## 7. Laufzeit-Architektur
@ -162,7 +164,7 @@ Hardcodierte `SNIPPET_REGISTRY` — Proof of Concept für `catalog_guidance_bloc
### H2 — Slot-Modell (0.8.235) ✓ ### H2 — Slot-Modell (0.8.235) ✓
- [x] Tabellen `catalog_prompt_slot_types`, `catalog_prompt_slots` - [x] Tabellen `catalog_prompt_slot_types`, `catalog_prompt_slots`
- [x] Seed aus H1-Texten (Name-Match auf Stammdaten) - [x] Seed aus H1-Texten (Name-Match auf Stammdaten)**entfernt (095)**: keine Migrations-Seeds; Inhalte Admin oder `catalog_slot_fallbacks.py`
- [x] Resolver mit granularen Platzhaltern + Aggregat - [x] Resolver mit granularen Platzhaltern + Aggregat
- [x] Admin-API GET/PUT - [x] Admin-API GET/PUT
- [x] `SNIPPET_REGISTRY` aus Laufzeit-Pfad entfernt - [x] `SNIPPET_REGISTRY` aus Laufzeit-Pfad entfernt