Enhance Planning Intent Context and Stage Specification Finalization
All checks were successful
Deploy Development / deploy (push) Successful in 42s
Test Suite / pytest-backend (push) Successful in 44s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 13s
Test Suite / k6 /health Baseline (push) Successful in 34s
Test Suite / playwright-tests (push) Successful in 1m13s
All checks were successful
Deploy Development / deploy (push) Successful in 42s
Test Suite / pytest-backend (push) Successful in 44s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 13s
Test Suite / k6 /health Baseline (push) Successful in 34s
Test Suite / playwright-tests (push) Successful in 1m13s
- Introduced `intent_context` and `semantic_brief` parameters in `try_llm_stage_specs` to improve context handling for stage specifications. - Updated `build_goal_analysis` to extract explicit exclusions from goal queries, enhancing constraint management. - Enhanced `roadmap_context_from_override` to enrich semantic briefs with path constraints and finalize stage specifications with intent context. - Incremented application version to reflect these updates.
This commit is contained in:
parent
3c12363b8f
commit
4ef3f00e6b
|
|
@ -0,0 +1,67 @@
|
|||
-- Migration 089: Planungs-Intent — Zielanalyse + Stufenspecs (anti_patterns, success_criteria)
|
||||
|
||||
UPDATE ai_prompts SET
|
||||
description = 'Phase A: Ist-/Soll, Erfolgskriterien und explizite Ausschlüsse (ohne Gruppenkontext).',
|
||||
template = $t$Du bist Assistent für Kampfsport-Trainer und analysierst eine Anfrage für einen Progressionsgraphen.
|
||||
|
||||
Anfrage: {{goal_query}}
|
||||
Semantic Brief: {{semantic_brief_json}}
|
||||
|
||||
Wichtig:
|
||||
- Keine Gruppenanalyse — nur didaktischer Pfad für die Technik/das Thema.
|
||||
- Explizite Negationen aus der Anfrage (ohne/kein/nicht …) in constraints.excluded_themes übernehmen — nicht raten.
|
||||
- success_criteria: messbar, für späteres Übungs-Matching (Titel + Kurzbeschreibung + Übungsziel).
|
||||
|
||||
Antworte NUR mit JSON:
|
||||
{
|
||||
"primary_topic": "Hauptthema",
|
||||
"start_assumption": "Voraussetzungen für den Einstieg",
|
||||
"target_state": "Konkreter Zielzustand der Progression",
|
||||
"success_criteria": ["messbare Kriterien entlang des Pfads"],
|
||||
"constraints": {
|
||||
"partner_required": false,
|
||||
"excluded_themes": ["wörtliche Negationen, z. B. keine Kumite-Anwendung"],
|
||||
"trainer_notes": "optional: Fokus aus Ergänzungen"
|
||||
}
|
||||
}$t$,
|
||||
default_template = template
|
||||
WHERE slug = 'planning_progression_goal_analysis';
|
||||
|
||||
UPDATE ai_prompts SET
|
||||
description = 'Phase C: Belastung, Übungstyp, Erfolgskriterien und anti_patterns je Major Step — für Retrieval-Matching.',
|
||||
template = $t$Du bist Assistent für Kampfsport-Trainer und spezifizierst didaktische Stufen eines Progressionsgraphen.
|
||||
|
||||
Anfrage: {{goal_query}}
|
||||
Zielanalyse: {{goal_analysis_json}}
|
||||
Major Steps: {{major_steps_json}}
|
||||
Planungs-Intent (Pfadweite Regeln): {{intent_context_json}}
|
||||
Semantic Brief: {{semantic_brief_json}}
|
||||
|
||||
Aufgabe je Major Step — Felder für automatisches Übungs-Matching (nicht nur Titel):
|
||||
- learning_goal: messbares Stufen-Lernziel (was die Übung bringen soll)
|
||||
- load_profile: z. B. koordination, präzision, kraft, athletik
|
||||
- exercise_type: kihon_einzel | partner_drill | kombination | kraft_auxiliary
|
||||
- success_criteria: 2–4 prüfbare Kriterien an Kurzbeschreibung + Übungsziel (nicht nur Technikname im Titel)
|
||||
- anti_patterns: 2–5 Dinge, die für diese Stufe unpassend sind
|
||||
|
||||
Regeln:
|
||||
1. Jede explicit_exclusions / excluded_themes aus intent_context und Zielanalyse MUSS in anti_patterns jeder Stufe vorkommen (umformuliert ok).
|
||||
2. Keine neuen Ausschlüsse erfinden, die nicht in Anfrage/Intent/Zielanalyse stehen.
|
||||
3. success_criteria Pfad-weit + stufenspezifisch kombinieren.
|
||||
4. partner_drill nur wenn Partner/Kumite nicht ausgeschlossen ist.
|
||||
|
||||
Antworte NUR mit JSON:
|
||||
{
|
||||
"stage_specs": [
|
||||
{
|
||||
"major_step_index": 0,
|
||||
"learning_goal": "…",
|
||||
"load_profile": ["koordination"],
|
||||
"exercise_type": "kihon_einzel",
|
||||
"success_criteria": ["…"],
|
||||
"anti_patterns": ["…"]
|
||||
}
|
||||
]
|
||||
}$t$,
|
||||
default_template = template
|
||||
WHERE slug = 'planning_progression_stage_spec';
|
||||
216
backend/planning_intent_context.py
Normal file
216
backend/planning_intent_context.py
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
"""
|
||||
Gemeinsame Intent-Anreicherung für Planungs-Retrieval.
|
||||
|
||||
Progressionsgraph (Roadmap stage_specs) und später Trainingsplanung
|
||||
(Abschnitt/Slot) nutzen dieselben Bausteine:
|
||||
Intent-Kontext bauen → Specs finalisieren → Matching-Gates.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Any, Dict, List, Mapping, Optional, Sequence
|
||||
|
||||
from planning_exercise_semantics import (
|
||||
PlanningSemanticBrief,
|
||||
resolve_path_anti_patterns,
|
||||
)
|
||||
|
||||
_NEGATION_CLAUSE_RE = re.compile(
|
||||
r"\b(?:ohne|kein(?:e|en|er|em)?|nicht)\s+[^,.;\n]+",
|
||||
flags=re.IGNORECASE,
|
||||
)
|
||||
|
||||
|
||||
def extract_explicit_exclusions(*texts: Optional[str]) -> List[str]:
|
||||
"""Lesbare Negationsklauseln aus Freitext (ohne Themen-Raten)."""
|
||||
out: List[str] = []
|
||||
for raw in texts:
|
||||
s = (raw or "").strip()
|
||||
if not s:
|
||||
continue
|
||||
for m in _NEGATION_CLAUSE_RE.finditer(s):
|
||||
clause = m.group(0).strip().rstrip(".,;")
|
||||
if clause and clause.lower() not in {x.lower() for x in out}:
|
||||
out.append(clause[:220])
|
||||
return out[:12]
|
||||
|
||||
|
||||
@dataclass
|
||||
class PlanningIntentContext:
|
||||
"""Pfad-/Abschnittsweiter Planungs-Intent — domänenneutral."""
|
||||
|
||||
source_query: str = ""
|
||||
primary_topic: str = ""
|
||||
path_anti_patterns: List[str] = field(default_factory=list)
|
||||
path_success_criteria: List[str] = field(default_factory=list)
|
||||
explicit_exclusions: List[str] = field(default_factory=list)
|
||||
context_notes: str = ""
|
||||
|
||||
def to_api_dict(self) -> Dict[str, Any]:
|
||||
return {
|
||||
"source_query": self.source_query,
|
||||
"primary_topic": self.primary_topic,
|
||||
"path_anti_patterns": self.path_anti_patterns[:16],
|
||||
"path_success_criteria": self.path_success_criteria[:10],
|
||||
"explicit_exclusions": self.explicit_exclusions[:10],
|
||||
"context_notes": self.context_notes[:1200] or None,
|
||||
}
|
||||
|
||||
|
||||
def build_planning_intent_context(
|
||||
goal_query: str,
|
||||
*,
|
||||
semantic_brief: Optional[PlanningSemanticBrief] = None,
|
||||
goal_analysis: Optional[Mapping[str, Any]] = None,
|
||||
extra_context: Optional[str] = None,
|
||||
primary_topic: Optional[str] = None,
|
||||
) -> PlanningIntentContext:
|
||||
"""Intent aus Anfrage, Zielanalyse und optionalem Kontext — ohne Sonderregeln pro Thema."""
|
||||
ga = dict(goal_analysis or {})
|
||||
notes_parts = [extra_context or ""]
|
||||
constraints = ga.get("constraints") if isinstance(ga.get("constraints"), dict) else {}
|
||||
if isinstance(constraints, dict):
|
||||
trainer_notes = str(constraints.get("trainer_notes") or "").strip()
|
||||
if trainer_notes:
|
||||
notes_parts.append(trainer_notes)
|
||||
|
||||
combined_notes = " ".join(p.strip() for p in notes_parts if p and p.strip())
|
||||
explicit = extract_explicit_exclusions(goal_query, combined_notes or None)
|
||||
ga_excluded = constraints.get("excluded_themes") if isinstance(constraints, dict) else None
|
||||
if isinstance(ga_excluded, list):
|
||||
for item in ga_excluded:
|
||||
s = str(item or "").strip()
|
||||
if s and s.lower() not in {x.lower() for x in explicit}:
|
||||
explicit.append(s[:220])
|
||||
|
||||
path_anti = resolve_path_anti_patterns(
|
||||
goal_query,
|
||||
semantic_brief=semantic_brief,
|
||||
extra_context=combined_notes or None,
|
||||
)
|
||||
path_success: List[str] = []
|
||||
for item in ga.get("success_criteria") or []:
|
||||
s = str(item or "").strip()
|
||||
if s and s not in path_success:
|
||||
path_success.append(s[:240])
|
||||
target = str(ga.get("target_state") or "").strip()
|
||||
if target and len(target) >= 8:
|
||||
line = f"Zielzustand erreichbar: {target[:200]}"
|
||||
if line not in path_success:
|
||||
path_success.append(line)
|
||||
|
||||
topic = (primary_topic or ga.get("primary_topic") or "").strip()
|
||||
if semantic_brief and not topic:
|
||||
topic = (semantic_brief.primary_topic or "").strip()
|
||||
|
||||
return PlanningIntentContext(
|
||||
source_query=(goal_query or "").strip(),
|
||||
primary_topic=topic,
|
||||
path_anti_patterns=path_anti,
|
||||
path_success_criteria=path_success,
|
||||
explicit_exclusions=explicit,
|
||||
context_notes=combined_notes[:1200],
|
||||
)
|
||||
|
||||
|
||||
def _dedupe_preserve(items: Sequence[str], *, limit: int = 14) -> List[str]:
|
||||
out: List[str] = []
|
||||
seen: set[str] = set()
|
||||
for raw in items:
|
||||
s = str(raw or "").strip()
|
||||
if not s:
|
||||
continue
|
||||
key = s.lower()
|
||||
if key in seen:
|
||||
continue
|
||||
seen.add(key)
|
||||
out.append(s[:240])
|
||||
if len(out) >= limit:
|
||||
break
|
||||
return out
|
||||
|
||||
|
||||
def finalize_stage_spec_artifact(
|
||||
spec: "StageSpecArtifact",
|
||||
*,
|
||||
major_step: Optional["MajorStep"] = None,
|
||||
intent: PlanningIntentContext,
|
||||
) -> "StageSpecArtifact":
|
||||
"""Pfad-Intent in eine Stufenspezifikation mergen (LLM oder heuristisch)."""
|
||||
from planning_progression_roadmap import MajorStep, StageSpecArtifact
|
||||
|
||||
learning_goal = (spec.learning_goal or (major_step.learning_goal if major_step else "")).strip()
|
||||
phase = (major_step.phase if major_step else "").strip().lower()
|
||||
|
||||
anti = _dedupe_preserve(
|
||||
[
|
||||
*(spec.anti_patterns or []),
|
||||
*intent.explicit_exclusions,
|
||||
*intent.path_anti_patterns,
|
||||
],
|
||||
limit=14,
|
||||
)
|
||||
success = _dedupe_preserve(
|
||||
[
|
||||
*(spec.success_criteria or []),
|
||||
*intent.path_success_criteria,
|
||||
(
|
||||
f"Übung liefert messbar: {learning_goal[:160]}"
|
||||
if learning_goal
|
||||
else ""
|
||||
),
|
||||
(
|
||||
f"Kurzbeschreibung und Übungsziel passen zur Phase {phase}"
|
||||
if phase
|
||||
else "Kurzbeschreibung und Übungsziel passen zum Stufen-Lernziel"
|
||||
),
|
||||
],
|
||||
limit=8,
|
||||
)
|
||||
|
||||
idx = spec.major_step_index
|
||||
if major_step is not None:
|
||||
idx = major_step.index
|
||||
|
||||
return StageSpecArtifact(
|
||||
major_step_index=idx,
|
||||
learning_goal=learning_goal,
|
||||
load_profile=list(spec.load_profile or []),
|
||||
exercise_type=(spec.exercise_type or "").strip(),
|
||||
success_criteria=success,
|
||||
anti_patterns=anti,
|
||||
)
|
||||
|
||||
|
||||
def finalize_stage_specs_with_intent(
|
||||
specs: Sequence["StageSpecArtifact"],
|
||||
major_steps: Sequence["MajorStep"],
|
||||
*,
|
||||
intent: PlanningIntentContext,
|
||||
fallback_specs: Optional[Sequence["StageSpecArtifact"]] = None,
|
||||
) -> List["StageSpecArtifact"]:
|
||||
"""Alle Stufen mit gleichem Pfad-Intent anreichern; fehlende Indizes aus Fallback."""
|
||||
from planning_progression_roadmap import MajorStep, StageSpecArtifact
|
||||
|
||||
by_idx = {int(s.major_step_index): s for s in specs}
|
||||
fallback_by_idx = {int(s.major_step_index): s for s in (fallback_specs or [])}
|
||||
out: List[StageSpecArtifact] = []
|
||||
for major in major_steps:
|
||||
raw = by_idx.get(major.index) or fallback_by_idx.get(major.index)
|
||||
if raw is None:
|
||||
raw = StageSpecArtifact(
|
||||
major_step_index=major.index,
|
||||
learning_goal=major.learning_goal,
|
||||
)
|
||||
out.append(finalize_stage_spec_artifact(raw, major_step=major, intent=intent))
|
||||
return out
|
||||
|
||||
|
||||
__all__ = [
|
||||
"PlanningIntentContext",
|
||||
"build_planning_intent_context",
|
||||
"extract_explicit_exclusions",
|
||||
"finalize_stage_spec_artifact",
|
||||
"finalize_stage_specs_with_intent",
|
||||
]
|
||||
|
|
@ -298,6 +298,8 @@ def try_llm_stage_specs(
|
|||
goal_query: str,
|
||||
goal_analysis: GoalAnalysisArtifact,
|
||||
major_steps: Sequence[MajorStep],
|
||||
intent_context: Optional[Mapping[str, Any]] = None,
|
||||
semantic_brief: Optional[PlanningSemanticBrief] = None,
|
||||
) -> Tuple[Optional[List[StageSpecArtifact]], bool]:
|
||||
obj = _run_prompt_json(
|
||||
cur,
|
||||
|
|
@ -306,6 +308,11 @@ def try_llm_stage_specs(
|
|||
"goal_query": goal_query or "",
|
||||
"goal_analysis_json": json.dumps(goal_analysis.model_dump(), ensure_ascii=False),
|
||||
"major_steps_json": json.dumps([m.model_dump() for m in major_steps], ensure_ascii=False),
|
||||
"intent_context_json": json.dumps(dict(intent_context or {}), ensure_ascii=False),
|
||||
"semantic_brief_json": json.dumps(
|
||||
brief_to_summary_dict(semantic_brief) if semantic_brief else {},
|
||||
ensure_ascii=False,
|
||||
),
|
||||
},
|
||||
)
|
||||
if not obj:
|
||||
|
|
@ -522,9 +529,14 @@ def build_goal_analysis(
|
|||
if notes.strip():
|
||||
criteria.append(f"Berücksichtigung: {notes.strip()[:200]}")
|
||||
|
||||
from planning_intent_context import extract_explicit_exclusions
|
||||
|
||||
constraints: Dict[str, Any] = {"partner_required": False, "group_analysis": False}
|
||||
if notes.strip():
|
||||
constraints["trainer_notes"] = notes.strip()[:500]
|
||||
excluded = extract_explicit_exclusions(goal_query, notes or None)
|
||||
if excluded:
|
||||
constraints["excluded_themes"] = excluded
|
||||
|
||||
return GoalAnalysisArtifact(
|
||||
primary_topic=topic,
|
||||
|
|
@ -955,6 +967,44 @@ def roadmap_context_from_override(
|
|||
semantic_brief=semantic_brief,
|
||||
)
|
||||
|
||||
from planning_exercise_semantics import enrich_brief_with_path_constraints
|
||||
from planning_intent_context import (
|
||||
build_planning_intent_context,
|
||||
finalize_stage_specs_with_intent,
|
||||
)
|
||||
|
||||
enriched_brief = enrich_brief_with_path_constraints(
|
||||
semantic_brief,
|
||||
goal_query.strip(),
|
||||
extra_context=_merge_roadmap_notes(
|
||||
structured.roadmap_notes if structured else None,
|
||||
structured.start_situation if structured else None,
|
||||
structured.target_state if structured else None,
|
||||
),
|
||||
)
|
||||
intent = build_planning_intent_context(
|
||||
goal_query.strip(),
|
||||
semantic_brief=enriched_brief,
|
||||
goal_analysis=goal_analysis.model_dump(),
|
||||
extra_context=_merge_roadmap_notes(
|
||||
structured.roadmap_notes if structured else None,
|
||||
structured.start_situation if structured else None,
|
||||
structured.target_state if structured else None,
|
||||
),
|
||||
primary_topic=goal_analysis.primary_topic,
|
||||
)
|
||||
stage_specs = finalize_stage_specs_with_intent(
|
||||
stage_specs,
|
||||
majors,
|
||||
intent=intent,
|
||||
fallback_specs=build_stage_specs(
|
||||
majors,
|
||||
goal_analysis=goal_analysis,
|
||||
goal_query=goal_query.strip(),
|
||||
semantic_brief=enriched_brief,
|
||||
),
|
||||
)
|
||||
|
||||
return ProgressionRoadmapContext(
|
||||
goal_query=goal_query.strip(),
|
||||
max_steps=effective_max,
|
||||
|
|
@ -1122,24 +1172,59 @@ def run_progression_roadmap_pipeline(
|
|||
)
|
||||
ctx.roadmap = roadmap
|
||||
|
||||
stage_specs = build_stage_specs(
|
||||
from planning_exercise_semantics import enrich_brief_with_path_constraints
|
||||
from planning_intent_context import (
|
||||
build_planning_intent_context,
|
||||
finalize_stage_specs_with_intent,
|
||||
)
|
||||
|
||||
brief = enrich_brief_with_path_constraints(
|
||||
brief,
|
||||
goal_query,
|
||||
extra_context=_merge_roadmap_notes(
|
||||
resolved.roadmap_notes,
|
||||
resolved.start_situation,
|
||||
resolved.target_state,
|
||||
),
|
||||
)
|
||||
intent = build_planning_intent_context(
|
||||
goal_query,
|
||||
semantic_brief=brief,
|
||||
goal_analysis=goal_analysis.model_dump(),
|
||||
extra_context=_merge_roadmap_notes(
|
||||
resolved.roadmap_notes,
|
||||
resolved.start_situation,
|
||||
resolved.target_state,
|
||||
),
|
||||
primary_topic=goal_analysis.primary_topic,
|
||||
)
|
||||
|
||||
heuristic_specs = build_stage_specs(
|
||||
roadmap.major_steps,
|
||||
goal_analysis=goal_analysis,
|
||||
goal_query=goal_query,
|
||||
semantic_brief=brief,
|
||||
)
|
||||
stage_specs = list(heuristic_specs)
|
||||
if include_llm_roadmap and cur is not None:
|
||||
llm_specs, spec_ok = try_llm_stage_specs(
|
||||
cur,
|
||||
goal_query=llm_goal_query,
|
||||
goal_analysis=goal_analysis,
|
||||
major_steps=roadmap.major_steps,
|
||||
intent_context=intent.to_api_dict(),
|
||||
semantic_brief=brief,
|
||||
)
|
||||
if spec_ok and llm_specs:
|
||||
stage_specs = llm_specs
|
||||
stage_specs = list(llm_specs)
|
||||
ctx.llm_stage_spec_applied = True
|
||||
ctx.prompt_slugs.append(PROMPT_SLUG_STAGE_SPEC)
|
||||
ctx.stage_specs = stage_specs
|
||||
ctx.stage_specs = finalize_stage_specs_with_intent(
|
||||
stage_specs,
|
||||
roadmap.major_steps,
|
||||
intent=intent,
|
||||
fallback_specs=heuristic_specs,
|
||||
)
|
||||
|
||||
if ctx.llm_goal_analysis_applied or ctx.llm_roadmap_applied or ctx.llm_stage_spec_applied:
|
||||
ctx.pipeline_phase = "roadmap_v1_llm"
|
||||
|
|
|
|||
58
backend/tests/test_planning_intent_context.py
Normal file
58
backend/tests/test_planning_intent_context.py
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
"""Tests gemeinsames Planungs-Intent-Modul (Progressionsgraph → Trainingsplanung)."""
|
||||
from planning_intent_context import (
|
||||
build_planning_intent_context,
|
||||
extract_explicit_exclusions,
|
||||
finalize_stage_specs_with_intent,
|
||||
)
|
||||
from planning_progression_roadmap import (
|
||||
MajorStep,
|
||||
StageSpecArtifact,
|
||||
build_goal_analysis,
|
||||
build_stage_specs,
|
||||
run_progression_roadmap_pipeline,
|
||||
)
|
||||
from planning_exercise_semantics import build_semantic_brief, enrich_brief_with_path_constraints
|
||||
|
||||
|
||||
def test_extract_explicit_exclusions_parses_negations():
|
||||
q = "gesprungener Mawashi Geri, keine Kumite-Anwendung gewünscht"
|
||||
out = extract_explicit_exclusions(q)
|
||||
assert any("kumite" in x.lower() for x in out)
|
||||
|
||||
|
||||
def test_build_planning_intent_context_includes_path_anti():
|
||||
q = "Mawashi Geri Sprungkraft, keine Kumite-Anwendung"
|
||||
brief = enrich_brief_with_path_constraints(build_semantic_brief(q), q)
|
||||
ga = build_goal_analysis(q, brief)
|
||||
intent = build_planning_intent_context(q, semantic_brief=brief, goal_analysis=ga.model_dump())
|
||||
assert intent.explicit_exclusions
|
||||
assert any("kumite" in a for a in intent.path_anti_patterns)
|
||||
|
||||
|
||||
def test_finalize_stage_specs_merges_intent_into_each_stage():
|
||||
q = "gesprungener Mawashi Geri, keine Kumite-Anwendung"
|
||||
brief = enrich_brief_with_path_constraints(build_semantic_brief(q), q)
|
||||
ga = build_goal_analysis(q, brief)
|
||||
intent = build_planning_intent_context(q, semantic_brief=brief, goal_analysis=ga.model_dump())
|
||||
majors = [
|
||||
MajorStep(index=0, phase="grundlage", learning_goal="Grundtechnik Mawashi", consolidates=["m1"]),
|
||||
MajorStep(index=1, phase="vertiefung", learning_goal="Sprungkoordination", consolidates=["m2"]),
|
||||
]
|
||||
raw_specs = [
|
||||
StageSpecArtifact(major_step_index=0, learning_goal=majors[0].learning_goal),
|
||||
StageSpecArtifact(major_step_index=1, learning_goal=majors[1].learning_goal),
|
||||
]
|
||||
finalized = finalize_stage_specs_with_intent(raw_specs, majors, intent=intent)
|
||||
assert len(finalized) == 2
|
||||
for spec in finalized:
|
||||
assert spec.success_criteria
|
||||
assert any("kumite" in a.lower() for a in spec.anti_patterns)
|
||||
assert any("messbar" in c.lower() or "übungsziel" in c.lower() for c in spec.success_criteria)
|
||||
|
||||
|
||||
def test_pipeline_stage_specs_carry_exclusions_without_llm():
|
||||
q = "gesprungener Mawashi Geri Sprungphase, keine Kumite-Anwendung"
|
||||
ctx = run_progression_roadmap_pipeline(q, max_steps=3, include_llm_roadmap=False)
|
||||
assert len(ctx.stage_specs) == 3
|
||||
for spec in ctx.stage_specs:
|
||||
assert any("kumite" in a.lower() for a in (spec.anti_patterns or []))
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
# Shinkan Jinkendo Version Information
|
||||
|
||||
APP_VERSION = "0.8.222"
|
||||
APP_VERSION = "0.8.223"
|
||||
BUILD_DATE = "2026-06-07"
|
||||
DB_SCHEMA_VERSION = "20260607088"
|
||||
DB_SCHEMA_VERSION = "20260607089"
|
||||
|
||||
MODULE_VERSIONS = {
|
||||
"legal_documents": "1.4.0", # Admin: Live-Vorschau pro Abschnitt + modale Vollvorschau (Editor + Dokumentenliste)
|
||||
|
|
@ -38,7 +38,7 @@ MODULE_VERSIONS = {
|
|||
"skill_profiles": "1.0.0", # Phase 3: gewichtetes Fähigkeiten-Profil + skill-discovery/suggestions
|
||||
"methods": "0.1.0",
|
||||
"exercises": "2.37.1", # KI-Endpoints: feature_usage nach ai_calls consume
|
||||
"planning_exercise_suggest": "0.22.0", # skill_expectations, planning_roadmap persist (088), stage_specs override
|
||||
"planning_exercise_suggest": "0.23.0", # planning_intent_context, finalize stage_specs, Prompt 089
|
||||
"training_units": "0.4.0", # POST .../publish-to-framework: Ablauf aus geplanter Einheit → Rahmen-Slot-Blueprint
|
||||
"training_programs": "0.1.0",
|
||||
"planning": "0.15.0", # Vorlagen: Strukturvorschau, Bearbeiten inkl. Split-Sessions + Beschreibung
|
||||
|
|
@ -53,6 +53,14 @@ MODULE_VERSIONS = {
|
|||
}
|
||||
|
||||
CHANGELOG = [
|
||||
{
|
||||
"version": "0.8.223",
|
||||
"date": "2026-06-07",
|
||||
"changes": [
|
||||
"planning_intent_context: gemeinsamer Intent für anti_patterns/success_criteria (Phase G-ready).",
|
||||
"Migration 089: LLM-Prompts Zielanalyse + stage_spec mit Intent-Kontext; finalize nach Pipeline.",
|
||||
],
|
||||
},
|
||||
{
|
||||
"version": "0.8.222",
|
||||
"date": "2026-06-07",
|
||||
|
|
|
|||
|
|
@ -225,7 +225,23 @@ Validierung: `progression_graph_planning_artifact.py` · Tests: `test_progressio
|
|||
|
||||
---
|
||||
|
||||
## 8. Fähigkeiten-Scoring-Anbindung
|
||||
## 8. Planungs-Intent (gemeinsam mit Trainingsplanung)
|
||||
|
||||
Modul: **`planning_intent_context.py`** — domänenneutral, wiederverwendbar in Phase G.
|
||||
|
||||
| Baustein | Progressionsgraph heute | Trainingsplanung (Phase G) |
|
||||
|----------|-------------------------|----------------------------|
|
||||
| `build_planning_intent_context` | Aus `goal_query`, Zielanalyse, Notizen | Aus `section_guidance`, Slot, Einheit |
|
||||
| `explicit_exclusions` | Negationen (`ohne/kein/nicht …`) | gleich |
|
||||
| `path_anti_patterns` | → jede `stage_spec.anti_patterns` | → Abschnitts-/Slot-Brief |
|
||||
| `path_success_criteria` | → `success_criteria` + Matching | → Slot-Erwartung |
|
||||
| `finalize_stage_specs_with_intent` | Nach heuristischer/LLM-Roadmap | Analog für Sektionen |
|
||||
|
||||
LLM: Migration **089** — Prompts `planning_progression_goal_analysis` + `planning_progression_stage_spec` erhalten `intent_context_json`; Ausschlüsse nicht erfinden, nur aus Anfrage übernehmen.
|
||||
|
||||
Matching: `anti_patterns` + `success_criteria` → `build_stage_match_brief` → Retrieval-Gate (Titel + Summary + Goal).
|
||||
|
||||
## 9. Fähigkeiten-Scoring-Anbindung
|
||||
|
||||
Modul: `planning_skill_expectations.py`
|
||||
|
||||
|
|
@ -245,7 +261,7 @@ Integration:
|
|||
|
||||
---
|
||||
|
||||
## 9. KI-Lücken (Gap-Fill)
|
||||
## 10. KI-Lücken (Gap-Fill)
|
||||
|
||||
Flow:
|
||||
1. `roadmap_unfilled` / QA-Lücken → `gap_fill_offers`
|
||||
|
|
@ -257,7 +273,7 @@ Kontext-Helfer: `frontend/src/utils/planningContextForExerciseAi.js`
|
|||
|
||||
---
|
||||
|
||||
## 10. Implementierungsstände (Phasen)
|
||||
## 11. Implementierungsstände (Phasen)
|
||||
|
||||
| Phase | Inhalt | Status | Version |
|
||||
|-------|--------|--------|---------|
|
||||
|
|
@ -275,7 +291,7 @@ Kontext-Helfer: `frontend/src/utils/planningContextForExerciseAi.js`
|
|||
|
||||
---
|
||||
|
||||
## 11. Offenes Backlog (priorisiert)
|
||||
## 12. Offenes Backlog (priorisiert)
|
||||
|
||||
1. **UI-Überarbeitung** — Wizard mit 4 Schritten, progressive disclosure (Briefing unten)
|
||||
2. **Graph-Erweiterungsmodus** — Start ab gewähltem Knoten / letzter Sequenz
|
||||
|
|
@ -292,13 +308,14 @@ Kern: Wizard ① Ziel & Start/Ziel → ② Roadmap → ③ Match → ④ Lücken
|
|||
|
||||
---
|
||||
|
||||
## 12. Tests
|
||||
## 13. Tests
|
||||
|
||||
| Datei | Abdeckung |
|
||||
|-------|-----------|
|
||||
| `test_planning_progression_roadmap.py` | Roadmap-Pipeline, Override, Start/Ziel |
|
||||
| `test_planning_exercise_path_builder.py` | Pfad-Annotierung, Skill-Expectations auf Steps |
|
||||
| `test_planning_skill_expectations.py` | Skill-Erwartungen, Scopes |
|
||||
| `test_planning_intent_context.py` | Intent-Kontext, finalize stage_specs |
|
||||
| `test_planning_exercise_path_ai_fill.py` | Gap-Fill, `expected_skills` in goal text |
|
||||
| `test_planning_exercise_form_context.py` | `planning_context`, Gap-Snapshot |
|
||||
| `test_progression_graph_planning_artifact.py` | JSONB-Artefakt-Validierung |
|
||||
|
|
@ -306,7 +323,7 @@ Kern: Wizard ① Ziel & Start/Ziel → ② Roadmap → ③ Match → ④ Lücken
|
|||
|
||||
---
|
||||
|
||||
## 13. Dokumenten-Index (Drift vermeiden)
|
||||
## 14. Dokumenten-Index (Drift vermeiden)
|
||||
|
||||
| Frage | Primäre Quelle |
|
||||
|-------|----------------|
|
||||
|
|
@ -322,7 +339,7 @@ Kern: Wizard ① Ziel & Start/Ziel → ② Roadmap → ③ Match → ④ Lücken
|
|||
|
||||
---
|
||||
|
||||
## 14. Changelog (Dokument)
|
||||
## 15. Changelog (Dokument)
|
||||
|
||||
| Datum | Änderung |
|
||||
|-------|----------|
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user