Enhance Planning AI Context with Catalog Exercise Hints
All checks were successful
Deploy Development / deploy (push) Successful in 47s
Test Suite / pytest-backend (push) Successful in 44s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 14s
Test Suite / k6 /health Baseline (push) Successful in 35s
Test Suite / playwright-tests (push) Successful in 1m15s
All checks were successful
Deploy Development / deploy (push) Successful in 47s
Test Suite / pytest-backend (push) Successful in 44s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 14s
Test Suite / k6 /health Baseline (push) Successful in 35s
Test Suite / playwright-tests (push) Successful in 1m15s
- Introduced support for catalog exercise hints in various functions, including `_build_stage_ai_context`, `try_suggest_ai_stage_step`, and `build_gap_fill_goal_text`, improving the contextual information available for AI suggestions. - Added a new function `catalog_exercise_hints_block` to consolidate hints retrieval from the catalog context. - Updated the `PlanningIntentContext` to include catalog exercise hints, ensuring they are part of the API output. - Implemented tests to verify the inclusion of catalog exercise hints in goal text generation and rematch logic. - Incremented version numbers and updated changelog to reflect these enhancements.
This commit is contained in:
parent
35fe31774b
commit
3181cc126d
|
|
@ -19,6 +19,8 @@ from planning_exercise_form_context import (
|
|||
prior_path_steps_before_major,
|
||||
)
|
||||
from planning_exercise_semantics import PlanningSemanticBrief, brief_to_summary_dict
|
||||
from planning_catalog_context import ProgressionPlanningCatalogContext
|
||||
from planning_prompt_variables import catalog_exercise_hints_block
|
||||
|
||||
_logger = logging.getLogger("shinkan.planning_exercise_path_ai_fill")
|
||||
|
||||
|
|
@ -54,6 +56,7 @@ def _build_stage_ai_context(
|
|||
step_after: Optional[Mapping[str, Any]] = None,
|
||||
prior_steps: Optional[Sequence[Mapping[str, Any]]] = None,
|
||||
start_situation: Optional[str] = None,
|
||||
catalog_exercise_hints: Optional[str] = None,
|
||||
) -> ExerciseFormAiPromptContext:
|
||||
"""KI-Kontext für unbesetzte Roadmap-Stufe (keine Brücke zwischen falschen Array-Indizes)."""
|
||||
gap = dict(spec.get("gap") or {})
|
||||
|
|
@ -97,6 +100,9 @@ def _build_stage_ai_context(
|
|||
sketch = (spec.get("sketch") or "").strip()
|
||||
if sketch and sketch != learning_goal:
|
||||
goal_parts.extend(["", f"Kontext: {sketch}"])
|
||||
hints = (catalog_exercise_hints or "").strip()
|
||||
if hints:
|
||||
goal_parts.extend(["", "Katalog-Hinweise Übungsanlage:", hints])
|
||||
goal = "\n".join(goal_parts)
|
||||
|
||||
focus_hint = topic if brief.topic_type == "technique" else None
|
||||
|
|
@ -118,6 +124,7 @@ def try_suggest_ai_stage_step(
|
|||
brief: PlanningSemanticBrief,
|
||||
spec: Mapping[str, Any],
|
||||
steps: Sequence[Mapping[str, Any]],
|
||||
catalog: Optional[ProgressionPlanningCatalogContext] = None,
|
||||
) -> Optional[Dict[str, Any]]:
|
||||
"""KI-Vorschlag für leere Roadmap-Stufe."""
|
||||
major_idx = spec.get("roadmap_major_step_index")
|
||||
|
|
@ -136,6 +143,8 @@ def try_suggest_ai_stage_step(
|
|||
if not gap.get("learning_goal"):
|
||||
gap["learning_goal"] = spec.get("title_hint") or spec.get("sketch")
|
||||
|
||||
exercise_hints = catalog_exercise_hints_block(cur, catalog) if catalog else ""
|
||||
|
||||
ctx = _build_stage_ai_context(
|
||||
goal_query=goal_query,
|
||||
brief=brief,
|
||||
|
|
@ -143,6 +152,7 @@ def try_suggest_ai_stage_step(
|
|||
step_before=step_before,
|
||||
step_after=step_after,
|
||||
prior_steps=prior_steps,
|
||||
catalog_exercise_hints=exercise_hints or None,
|
||||
)
|
||||
try:
|
||||
ai_payload = run_exercise_form_ai_suggestion(cur, ctx=ctx)
|
||||
|
|
@ -189,6 +199,7 @@ def _build_gap_ai_context(
|
|||
gap: Mapping[str, Any],
|
||||
title_hint: Optional[str] = None,
|
||||
sketch_hint: Optional[str] = None,
|
||||
catalog_exercise_hints: Optional[str] = None,
|
||||
) -> ExerciseFormAiPromptContext:
|
||||
topic = (brief.primary_topic or "Technik").strip()
|
||||
phase = gap.get("expected_phase") or "vertiefung"
|
||||
|
|
@ -206,6 +217,9 @@ def _build_gap_ai_context(
|
|||
]
|
||||
if sketch:
|
||||
goal_parts.extend(["", f"Hinweis: {sketch}"])
|
||||
hints = (catalog_exercise_hints or "").strip()
|
||||
if hints:
|
||||
goal_parts.extend(["", "Katalog-Hinweise Übungsanlage:", hints])
|
||||
goal = "\n".join(goal_parts)
|
||||
|
||||
focus_hint = topic if brief.topic_type == "technique" else None
|
||||
|
|
@ -271,8 +285,10 @@ def try_suggest_ai_bridge_step(
|
|||
gap: Mapping[str, Any],
|
||||
title_hint: Optional[str] = None,
|
||||
sketch_hint: Optional[str] = None,
|
||||
catalog: Optional[ProgressionPlanningCatalogContext] = None,
|
||||
) -> Optional[Dict[str, Any]]:
|
||||
"""Ruft exercise AI suggest auf — kein Speichern in DB."""
|
||||
exercise_hints = catalog_exercise_hints_block(cur, catalog) if catalog else ""
|
||||
ctx = _build_gap_ai_context(
|
||||
goal_query=goal_query,
|
||||
brief=brief,
|
||||
|
|
@ -281,6 +297,7 @@ def try_suggest_ai_bridge_step(
|
|||
gap=gap,
|
||||
title_hint=title_hint,
|
||||
sketch_hint=sketch_hint,
|
||||
catalog_exercise_hints=exercise_hints or None,
|
||||
)
|
||||
g_plain = strip_html_to_plain(ctx.goal)
|
||||
if not g_plain.strip() and not (ctx.title or "").strip():
|
||||
|
|
@ -478,6 +495,7 @@ def build_gap_fill_goal_text(
|
|||
step_a: Optional[Mapping[str, Any]] = None,
|
||||
step_b: Optional[Mapping[str, Any]] = None,
|
||||
roadmap_snapshot: Optional[Mapping[str, Any]] = None,
|
||||
catalog_exercise_hints: Optional[str] = None,
|
||||
) -> str:
|
||||
"""Ausführlicher Zieltext für KI-Neuanlage aus Pfad-, Roadmap- und Stufen-Kontext."""
|
||||
topic = (brief.primary_topic or "Technik").strip()
|
||||
|
|
@ -555,6 +573,9 @@ def build_gap_fill_goal_text(
|
|||
parts.append(f"Qualitätsprüfung: {spec['rationale']}")
|
||||
if spec.get("sketch"):
|
||||
parts.append(f"Skizze: {spec['sketch']}")
|
||||
hints = (catalog_exercise_hints or "").strip()
|
||||
if hints:
|
||||
parts.append(f"Katalog-Hinweise Übungsanlage:\n{hints}")
|
||||
parts.append(
|
||||
"Die Übung muss die Stufe didaktisch erfüllen: klare Voraussetzungen, messbares Stufenziel, "
|
||||
"Bezug zum Gesamtpfad — keine generische Kraftübung ohne Technikbezug. "
|
||||
|
|
@ -571,6 +592,7 @@ def build_gap_fill_offer(
|
|||
brief: Optional[PlanningSemanticBrief] = None,
|
||||
proposal: Optional[Mapping[str, Any]] = None,
|
||||
roadmap_snapshot: Optional[Mapping[str, Any]] = None,
|
||||
catalog_exercise_hints: Optional[str] = None,
|
||||
) -> Dict[str, Any]:
|
||||
source = spec.get("source")
|
||||
idx = int(spec.get("insert_after_index") or 0)
|
||||
|
|
@ -603,6 +625,7 @@ def build_gap_fill_offer(
|
|||
step_a=step_a,
|
||||
step_b=step_b,
|
||||
roadmap_snapshot=enriched_snapshot or None,
|
||||
catalog_exercise_hints=catalog_exercise_hints,
|
||||
)
|
||||
ctx_preview = enriched_snapshot or None
|
||||
offer: Dict[str, Any] = {
|
||||
|
|
@ -642,6 +665,7 @@ def apply_gap_fill_after_qa(
|
|||
max_ai_proposals: int = 3,
|
||||
auto_insert_proposals: bool = False,
|
||||
roadmap_snapshot: Optional[Mapping[str, Any]] = None,
|
||||
catalog: Optional[ProgressionPlanningCatalogContext] = None,
|
||||
) -> Tuple[List[Dict[str, Any]], List[Dict[str, Any]], List[Dict[str, Any]]]:
|
||||
"""
|
||||
Erzeugt gap_fill_offers für die UI; optional KI-Vorschläge einfügen.
|
||||
|
|
@ -653,6 +677,7 @@ def apply_gap_fill_after_qa(
|
|||
out = list(steps)
|
||||
proposals: List[Dict[str, Any]] = []
|
||||
offers: List[Dict[str, Any]] = []
|
||||
exercise_hints = catalog_exercise_hints_block(cur, catalog) if catalog else ""
|
||||
|
||||
for spec in specs:
|
||||
source = spec.get("source")
|
||||
|
|
@ -666,6 +691,7 @@ def apply_gap_fill_after_qa(
|
|||
brief=brief,
|
||||
spec=spec,
|
||||
steps=out,
|
||||
catalog=catalog,
|
||||
)
|
||||
offer = build_gap_fill_offer(
|
||||
spec=spec,
|
||||
|
|
@ -674,6 +700,7 @@ def apply_gap_fill_after_qa(
|
|||
brief=brief,
|
||||
proposal=proposal,
|
||||
roadmap_snapshot=roadmap_snapshot,
|
||||
catalog_exercise_hints=exercise_hints or None,
|
||||
)
|
||||
offers.append(offer)
|
||||
if proposal and auto_insert_proposals:
|
||||
|
|
@ -700,6 +727,7 @@ def apply_gap_fill_after_qa(
|
|||
brief=brief,
|
||||
proposal=None,
|
||||
roadmap_snapshot=roadmap_snapshot,
|
||||
catalog_exercise_hints=exercise_hints or None,
|
||||
)
|
||||
offers.append(offer)
|
||||
continue
|
||||
|
|
@ -719,6 +747,7 @@ def apply_gap_fill_after_qa(
|
|||
gap=gap,
|
||||
title_hint=str(spec.get("title_hint") or ""),
|
||||
sketch_hint=str(spec.get("sketch") or ""),
|
||||
catalog=catalog,
|
||||
)
|
||||
|
||||
offer = build_gap_fill_offer(
|
||||
|
|
@ -728,6 +757,7 @@ def apply_gap_fill_after_qa(
|
|||
brief=brief,
|
||||
proposal=proposal,
|
||||
roadmap_snapshot=roadmap_snapshot,
|
||||
catalog_exercise_hints=exercise_hints or None,
|
||||
)
|
||||
offers.append(offer)
|
||||
|
||||
|
|
|
|||
|
|
@ -24,9 +24,11 @@ from planning_catalog_context import (
|
|||
load_catalog_context_from_graph_row,
|
||||
merge_catalog_context_into_target,
|
||||
)
|
||||
from catalog_prompt_slots import get_rematch_guard_for_catalog
|
||||
from planning_exercise_profiles import PlanningTargetProfile
|
||||
from planning_path_qa_pipeline import run_multistage_path_qa
|
||||
from planning_path_rematch import (
|
||||
apply_rematch_catalog_guard,
|
||||
collect_rematch_slot_indices,
|
||||
filter_rematch_slot_indices,
|
||||
prune_stripped_after_rematch,
|
||||
|
|
@ -1737,6 +1739,13 @@ def _run_roadmap_rematch_loop(
|
|||
stripped_off_topic=current_stripped if round_idx == 0 else [],
|
||||
off_topic_steps=off_topic_before_strip if round_idx == 0 and use_initial_off_topic else [],
|
||||
)
|
||||
catalog_context = _resolve_planning_catalog_context(cur, body)
|
||||
rematch_guard = get_rematch_guard_for_catalog(cur, catalog_context)
|
||||
slot_indices = apply_rematch_catalog_guard(
|
||||
slot_indices,
|
||||
rematch_reasons,
|
||||
rematch_guard=rematch_guard,
|
||||
)
|
||||
if not slot_indices:
|
||||
break
|
||||
|
||||
|
|
@ -2191,6 +2200,7 @@ def _run_evaluate_only_path_qa(
|
|||
max_ai_proposals=0,
|
||||
auto_insert_proposals=False,
|
||||
roadmap_snapshot=path_roadmap_snapshot,
|
||||
catalog=catalog_context,
|
||||
)
|
||||
|
||||
multistage_qa = run_multistage_path_qa(
|
||||
|
|
@ -4294,6 +4304,7 @@ def suggest_progression_path(
|
|||
max_ai_proposals=0,
|
||||
auto_insert_proposals=False,
|
||||
roadmap_snapshot=path_roadmap_snapshot,
|
||||
catalog=catalog_context,
|
||||
)
|
||||
|
||||
if roadmap_gap_offers:
|
||||
|
|
|
|||
|
|
@ -49,9 +49,10 @@ class PlanningIntentContext:
|
|||
context_notes: str = ""
|
||||
topic_type: str = "general"
|
||||
technique_sibling_excludes: List[str] = field(default_factory=list)
|
||||
catalog_exercise_hints: Optional[str] = None
|
||||
|
||||
def to_api_dict(self) -> Dict[str, Any]:
|
||||
return {
|
||||
out = {
|
||||
"source_query": self.source_query,
|
||||
"primary_topic": self.primary_topic,
|
||||
"topic_type": self.topic_type,
|
||||
|
|
@ -61,6 +62,10 @@ class PlanningIntentContext:
|
|||
"technique_sibling_excludes": self.technique_sibling_excludes[:16],
|
||||
"context_notes": self.context_notes[:1200] or None,
|
||||
}
|
||||
hints = (self.catalog_exercise_hints or "").strip()
|
||||
if hints:
|
||||
out["catalog_exercise_hints"] = hints[:2000]
|
||||
return out
|
||||
|
||||
|
||||
def build_planning_intent_context(
|
||||
|
|
@ -70,6 +75,8 @@ def build_planning_intent_context(
|
|||
goal_analysis: Optional[Mapping[str, Any]] = None,
|
||||
extra_context: Optional[str] = None,
|
||||
primary_topic: Optional[str] = None,
|
||||
cur=None,
|
||||
catalog=None,
|
||||
) -> PlanningIntentContext:
|
||||
"""Intent aus Anfrage, Zielanalyse und optionalem Kontext — ohne Sonderregeln pro Thema."""
|
||||
ga = dict(goal_analysis or {})
|
||||
|
|
@ -124,6 +131,12 @@ def build_planning_intent_context(
|
|||
if line not in path_success:
|
||||
path_success.insert(0, line)
|
||||
|
||||
catalog_hints = ""
|
||||
if cur is not None and catalog is not None:
|
||||
from planning_prompt_variables import catalog_exercise_hints_block
|
||||
|
||||
catalog_hints = catalog_exercise_hints_block(cur, catalog)
|
||||
|
||||
return PlanningIntentContext(
|
||||
source_query=(goal_query or "").strip(),
|
||||
primary_topic=topic,
|
||||
|
|
@ -133,6 +146,7 @@ def build_planning_intent_context(
|
|||
explicit_exclusions=explicit,
|
||||
technique_sibling_excludes=siblings[:16],
|
||||
context_notes=combined_notes[:1200],
|
||||
catalog_exercise_hints=catalog_hints or None,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ from typing import Any, Dict, List, Mapping, Optional, Sequence, Set, Tuple
|
|||
|
||||
from planning_progression_roadmap import ProgressionRoadmapContext, StageSpecArtifact
|
||||
|
||||
_UNFILLED_REMATCH_REASON = "Keine passende Übung für Roadmap-Stufe"
|
||||
|
||||
|
||||
def _slot_priority_for_rematch(
|
||||
body,
|
||||
|
|
@ -103,7 +105,7 @@ def collect_rematch_slot_indices(
|
|||
if isinstance(item, (list, tuple)) and len(item) >= 2:
|
||||
idx, spec = item[0], item[1]
|
||||
midx = getattr(spec, "major_step_index", idx)
|
||||
_register(int(midx), "Keine passende Übung für Roadmap-Stufe")
|
||||
_register(int(midx), _UNFILLED_REMATCH_REASON)
|
||||
elif isinstance(item, dict):
|
||||
midx = _resolve_major(item)
|
||||
if midx is not None:
|
||||
|
|
@ -114,6 +116,25 @@ def collect_rematch_slot_indices(
|
|||
return indices, reasons
|
||||
|
||||
|
||||
def apply_rematch_catalog_guard(
|
||||
slot_indices: Set[int],
|
||||
rematch_reasons: Mapping[int, str],
|
||||
*,
|
||||
rematch_guard: Optional[str],
|
||||
) -> Set[int]:
|
||||
"""
|
||||
Bei gesetztem rematch_guard (Katalog-Slot): kein Auto-Rematch nur wegen leerer Roadmap-Stufe.
|
||||
Off-Topic, explizite QS-Hinweise und Stufen-Verfeinerung bleiben aktiv.
|
||||
"""
|
||||
if not (rematch_guard or "").strip():
|
||||
return set(slot_indices)
|
||||
return {
|
||||
idx
|
||||
for idx in slot_indices
|
||||
if str(rematch_reasons.get(idx) or "").strip() != _UNFILLED_REMATCH_REASON
|
||||
}
|
||||
|
||||
|
||||
def filter_rematch_slot_indices(
|
||||
steps: Sequence[Mapping[str, Any]],
|
||||
slot_indices: Set[int],
|
||||
|
|
@ -372,6 +393,7 @@ def prune_stripped_after_rematch(
|
|||
|
||||
|
||||
__all__ = [
|
||||
"apply_rematch_catalog_guard",
|
||||
"collect_rematch_slot_indices",
|
||||
"filter_rematch_slot_indices",
|
||||
"prune_stripped_after_rematch",
|
||||
|
|
|
|||
|
|
@ -1244,6 +1244,8 @@ def run_progression_roadmap_pipeline(
|
|||
resolved.target_state,
|
||||
),
|
||||
primary_topic=goal_analysis.primary_topic,
|
||||
cur=cur,
|
||||
catalog=catalog,
|
||||
)
|
||||
|
||||
heuristic_specs = build_stage_specs(
|
||||
|
|
|
|||
|
|
@ -34,6 +34,27 @@ _PLANNING_PROMPT_VARIABLE_PROVIDERS: tuple[PlanningPromptVariableProvider, ...]
|
|||
)
|
||||
|
||||
|
||||
def catalog_exercise_hints_block(
|
||||
cur,
|
||||
catalog: Optional[ProgressionPlanningCatalogContext] = None,
|
||||
*,
|
||||
slug: Optional[str] = None,
|
||||
) -> str:
|
||||
"""Kombinierte hints_on_exercise aus aktivem Katalog-Kontext (Gap-Fill / Intent)."""
|
||||
if cur is None or catalog is None:
|
||||
return ""
|
||||
from catalog_prompt_slots import CATALOG_KINDS, placeholder_key
|
||||
|
||||
merged = merge_planning_prompt_variables(cur, {}, catalog=catalog, slug=slug)
|
||||
lines: list[str] = []
|
||||
for cfg in CATALOG_KINDS:
|
||||
key = placeholder_key(cfg.kind, "hints_on_exercise")
|
||||
text = (merged.get(key) or "").strip()
|
||||
if text:
|
||||
lines.append(f"{cfg.label_de}: {text}")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def merge_planning_prompt_variables(
|
||||
cur,
|
||||
base_variables: Mapping[str, str],
|
||||
|
|
@ -113,6 +134,7 @@ def planning_prompt_placeholder_catalog() -> dict:
|
|||
|
||||
|
||||
__all__ = [
|
||||
"catalog_exercise_hints_block",
|
||||
"merge_planning_prompt_variables",
|
||||
"planning_prompt_placeholder_catalog",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -83,6 +83,20 @@ def test_strip_off_topic_steps_from_path():
|
|||
assert [s["exercise_id"] for s in out] == [1, 2, 4]
|
||||
|
||||
|
||||
def test_build_gap_fill_goal_text_includes_catalog_exercise_hints():
|
||||
from planning_exercise_semantics import build_semantic_brief
|
||||
|
||||
brief = build_semantic_brief("Mae Geri")
|
||||
text = build_gap_fill_goal_text(
|
||||
goal_query="Mae Geri lernen",
|
||||
brief=brief,
|
||||
spec={"source": "roadmap_unfilled", "phase": "grundlage", "title_hint": "Stand"},
|
||||
catalog_exercise_hints="Primärfokus: Kihon und Partnerübungen mit Technikbezug.",
|
||||
)
|
||||
assert "Katalog-Hinweise Übungsanlage" in text
|
||||
assert "Kihon" in text
|
||||
|
||||
|
||||
def test_build_gap_fill_goal_text_includes_topic():
|
||||
brief = build_semantic_brief("Mae Geri Perfektion")
|
||||
text = build_gap_fill_goal_text(
|
||||
|
|
|
|||
|
|
@ -336,3 +336,29 @@ def test_filter_rematch_skips_preserved_slots():
|
|||
off_topic_steps=[],
|
||||
)
|
||||
assert filtered == {1}
|
||||
|
||||
|
||||
def test_apply_rematch_catalog_guard_skips_unfilled_only():
|
||||
from planning_path_rematch import apply_rematch_catalog_guard
|
||||
|
||||
indices = {0, 1, 2}
|
||||
reasons = {
|
||||
0: "QS-Tier-1",
|
||||
1: "Keine passende Übung für Roadmap-Stufe",
|
||||
2: "Passt nicht zur Haupttechnik",
|
||||
}
|
||||
filtered = apply_rematch_catalog_guard(
|
||||
indices,
|
||||
reasons,
|
||||
rematch_guard="Keine leeren Slots erzwingen.",
|
||||
)
|
||||
assert filtered == {0, 2}
|
||||
|
||||
|
||||
def test_apply_rematch_catalog_guard_inactive_without_guard():
|
||||
from planning_path_rematch import apply_rematch_catalog_guard
|
||||
|
||||
indices = {1}
|
||||
reasons = {1: "Keine passende Übung für Roadmap-Stufe"}
|
||||
assert apply_rematch_catalog_guard(indices, reasons, rematch_guard=None) == {1}
|
||||
assert apply_rematch_catalog_guard(indices, reasons, rematch_guard="") == {1}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
# Shinkan Jinkendo Version Information
|
||||
|
||||
APP_VERSION = "0.8.237"
|
||||
APP_VERSION = "0.8.238"
|
||||
BUILD_DATE = "2026-05-22"
|
||||
DB_SCHEMA_VERSION = "20260607094"
|
||||
DB_SCHEMA_VERSION = "20260607095"
|
||||
|
||||
MODULE_VERSIONS = {
|
||||
"legal_documents": "1.4.0", # Admin: Live-Vorschau pro Abschnitt + modale Vollvorschau (Editor + Dokumentenliste)
|
||||
|
|
@ -53,11 +53,20 @@ MODULE_VERSIONS = {
|
|||
}
|
||||
|
||||
CHANGELOG = [
|
||||
{
|
||||
"version": "0.8.238",
|
||||
"date": "2026-05-22",
|
||||
"changes": [
|
||||
"Planungs-KI H1.5: rematch_guard im Auto-Rematch (kein Rematch nur wegen leerer Roadmap-Stufe).",
|
||||
"hints_on_exercise in Gap-Fill, Übungs-KI-Kontext und intent_context_json (Roadmap-Pipeline).",
|
||||
"Migration 095: keine Migrations-Seeds in catalog_prompt_slots (Prod-sichere Katalogstruktur).",
|
||||
],
|
||||
},
|
||||
{
|
||||
"version": "0.8.237",
|
||||
"date": "2026-05-22",
|
||||
"changes": [
|
||||
"Migration 094: catalog_prompt_slots vollständig befüllt (Karate, SV, alle Trainingsstile/Zielgruppen).",
|
||||
"Migration 094/095: Katalog-Prompt-Slots ohne Migrations-Seeds — Inhalte Admin oder Laufzeit-Fallback.",
|
||||
"catalog_slot_fallbacks: Namens-Fallback bis Admin-Override — gleiche Qualität wie H1-Registry.",
|
||||
],
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Shinkan Jinkendo – Entwicklungsstand & Handover
|
||||
|
||||
**Stand:** 2026-05-22 (F15 Graph-Match & getrennte Pfad-QS, lokal nach **0.8.233**)
|
||||
**App-Version / DB-Schema:** App **`0.8.233`** (Planungs-KI F11–F14, Katalog-Kontext); **F15** siehe §2.8 — DB unverändert (`DB_SCHEMA_VERSION`, Migration **088**).
|
||||
**Stand:** 2026-05-22 (Katalog-Prompt-Slots H2/H1.5, **0.8.238**)
|
||||
**App-Version / DB-Schema:** App **`0.8.238`**; DB Migration **095** (keine Slot-Seeds; Prod-sichere Katalogstruktur).
|
||||
|
||||
Diese Datei ist die **Einstiegs-Doku für neue Chat-Sessions**: Anforderungen im Detail stehen in `.claude/docs/` (siehe unten); hier der **implementierte Stand**, **Medien-Meilenstein** und **sinnvolle nächste Schritte**.
|
||||
|
||||
|
|
@ -115,7 +115,8 @@ Das Schema ist gegenüber dem Code zurück: Migration **`022_skills_schema_compl
|
|||
| **F13** | **`planning_catalog_context`** (Fokus/Stil/TT/ZG) im Match + Graph-Artefakt | ✅ **0.8.233** |
|
||||
| **F14** | **`ProgressionGraphEditor`** — Slot-UI + Planungskontext-Dropdowns | ✅ **0.8.233** |
|
||||
| **F15** | Unified Slot-Review (Match-Dialog), getrennte Pfad-QS, `findings_stale` | ✅ lokal (nach 0.8.233) |
|
||||
| **H1** | Katalog-Prompt-Snippets (modulare LLM-Anweisungen) | 🔲 Spec **`docs/architecture/PLANNING_CATALOG_PROMPT_SNIPPETS.md`** |
|
||||
| **H1–H2.1** | Katalog-Prompt-Slots (DB, Resolver, Admin-UI, granulare Prompts) | ✅ **0.8.234–238** — Spec **`docs/architecture/PLANNING_CATALOG_PROMPT_SNIPPETS.md`** |
|
||||
| **H1.5** | `rematch_guard`, `hints_on_exercise` in Rematch/Gap-Fill/Intent | ✅ **0.8.238** |
|
||||
|
||||
**Architektur (verbindlich):** Drei Schichten — (1) **Katalog-Dimensionen** (DB, jetzt im Match verdrahtet; **H1:** zusätzlich Prompt-Snippets), (2) **Technik-Disambiguierung** (Code, nur bei `topic_type=technique`), (3) **Didaktik** (Roadmap + LLM-QS, nicht im Vokabular). Progressionsgraph = **Roadmap-first**, **keine Gruppenanalyse**. Bestehender Graph = **leichter Nachfolger-Bias** ab Schritt 2. Trainingsplanung = **eigene Pipeline** (Phase G) — Wiederverwendung der Bausteine, siehe Ist-Doku §16.
|
||||
|
||||
|
|
@ -148,7 +149,7 @@ Das Schema ist gegenüber dem Code zurück: Migration **`022_skills_schema_compl
|
|||
5. Phase D′ — Auto KI-Gap-Fill bei persistent leeren Slots
|
||||
6. **Trainingsplanung Phase G** — Gruppenkontext-Pack, Scopes `training_section` / `framework_slot` (Ist-Doku §16)
|
||||
7. Technik-Katalog konfigurierbar (Backlog)
|
||||
8. **H1** — Katalog-Prompt-Snippets (modulare LLM-Anweisungen)
|
||||
8. **H1–H2.1** — Katalog-Prompt-Slots ✅ · **Prompt-Editor-Überarbeitung** → eigene Session
|
||||
|
||||
#### Übungs-KI Formular / Schnellanlage (Stand **0.8.171**)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Planungs-KI — Katalog-Prompt-Slots (Snippets)
|
||||
|
||||
**Stand:** 2026-05-22
|
||||
**Status:** **H2** umgesetzt (0.8.235) · **H2.1** Admin-UI + granulare Prompts (0.8.236)
|
||||
**Status:** **H2.1** umgesetzt (0.8.236) · **H1.5** Rematch-Guard + Gap-Fill-Hints (0.8.238) · **Prompt-Editor-Überarbeitung** bewusst ausgelagert
|
||||
**Bezüge:** `PLANNING_PROGRESSION_GRAPH_KI.md` §4.4 · `AI_PROMPT_TARGET_ARCHITECTURE.md` §2.4 · `planning_catalog_context.py` · `catalog_prompt_slots.py`
|
||||
|
||||
---
|
||||
|
|
@ -173,12 +173,12 @@ Hardcodierte `SNIPPET_REGISTRY` — Proof of Concept für `catalog_guidance_bloc
|
|||
|
||||
- [x] Slot-Editor an Fokusbereich / Trainingsstil / Zielgruppe / Stilrichtung (`CatalogPromptSlotsEditor`, Stammdaten-Katalog)
|
||||
- [x] Prompt-Templates mit granularen Platzhaltern (Migration 093)
|
||||
- [ ] Platzhalter-Hilfe im KI-Prompt-Editor (erweitert)
|
||||
- [ ] **Prompt-Editor komplett überarbeiten** (Platzhalter-Hilfe, UX) — **eigene Session**
|
||||
|
||||
### H1.5
|
||||
### H1.5 ✓ (0.8.238)
|
||||
|
||||
- [ ] `rematch_guard` im Rematch-Loop
|
||||
- [ ] Intent-Prompts + Gap-Fill: `hints_on_exercise`
|
||||
- [x] `rematch_guard` im Rematch-Loop (`apply_rematch_catalog_guard`)
|
||||
- [x] Gap-Fill + Intent: `hints_on_exercise` via `catalog_exercise_hints_block` / `intent_context_json`
|
||||
|
||||
### H3 — Trainingsplanung (Phase G)
|
||||
|
||||
|
|
@ -211,6 +211,7 @@ Hardcodierte `SNIPPET_REGISTRY` — Proof of Concept für `catalog_guidance_bloc
|
|||
|
||||
| Datum | Änderung |
|
||||
|-------|----------|
|
||||
| 2026-05-22 | **H1.5** (0.8.238): rematch_guard; hints_on_exercise in Gap-Fill/Intent; Migration 095 ohne Seeds |
|
||||
| 2026-05-22 | **H2.1** (0.8.236): Admin-UI `CatalogPromptSlotsEditor`; Migration 093 granulare Prompt-Templates |
|
||||
| 2026-05-22 | **H2** (0.8.235): Slot-Typ-Register + `catalog_prompt_slots` DB, granulare Platzhalter, Admin-API |
|
||||
| 2026-05-22 | Konzept §4–§8: zwei Ebenen Slot-Typ vs. Slot-Wert; Platzhalter `{kind}_{slot_key}` |
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user