Lektionsinhalte erstellen¶
Dieser Leitfaden beschreibt Schritt für Schritt, wie man ein neues Lektionsset für den Adaptive-Learner-Content-Loader aufsetzt. Wer ein Sprach- oder Themenset bauen möchte — für den Eigengebrauch oder als Beitrag zum öffentlichen Content-Pool — sollte ihn vor der ersten Lektion einmal komplett durchlesen.
Was ist ein Content-Set?¶
Ein Content-Set ist ein versioniertes Bündel von Lektionen,
das ein Nutzer über die Set-Browser-Seite (/content)
herunterladen kann. Das Content-Loader-Plugin (v1.27.0) übernimmt
Discovery, Download, Caching und Versionsabgleich in beiden
Speichermodi.
Ein Set hat drei Ebenen:
- Root-Manifest (
manifest.yaml) — listet jedes Set des Repos. Wird vom Set Browser für den Quell-Katalog gelesen. - Set-Manifest (
sets/{set-id}/manifest.yaml) — Schwester des Root-Manifests, listet die Lektions-Dateien des konkreten Sets. - Lektionsdateien (
sets/{set-id}/lessons/NN-slug.json) — eine JSON-Datei pro Lektion, bei jedem Download gegen Schema v1.0 validiert.
Die mit Adaptive Learner ausgelieferten Pilot-Sets liegen im separaten
Content-Repo astrapi69/adaptive-learner-content
(als Geschwister-Checkout ../adaptive-learner-content ausgecheckt und
vom Build über frontend/scripts/copy-bundled-content.mjs gebündelt)
und eignen sich gut als Vorlage.
Sprachpaare (v1.44.0)¶
Jedes Content-Set deklariert das Sprach-PAAR, das es vermittelt:
target_language— was der Lernende LERNT (z. B.fr).source_language— was der Lernende bereits SPRICHT, also die Sprache, in der die Karten-back-Felder,notesund der Theorie-Text geschrieben sind (z. B.de).
Genau das macht "Französisch für Englischsprachige" zu einem
anderen Set als "Französisch für Deutschsprachige": gleiches Ziel
(fr), andere Ausgangssprache (en vs. de), andere
Erklärsprache. Ein Lernender sieht nur Sets, deren
source_language zu einer von ihm gesprochenen Sprache passt
(App-Sprache plus optionale Zusatzsprachen in Einstellungen →
Lernen).
Set-IDs kodieren das Paar als {ziel}-{niveau}-from-{quelle}
(z. B. fr-a1-from-de), und jedes Set deklariert einen path,
der auf sein Ausgangssprach-Verzeichnis zeigt (sets/de/fr-a1).
Ein Set trägt außerdem title (in der Ausgangssprache, was der
Lernende liest) und title_native (in der Zielsprache, als
Zweittitel).
Beide Codes müssen ISO-639-1 (zwei Buchstaben) sein, und
source_language muss sich von target_language unterscheiden.
Sets vor v1.2 ohne diese Felder laden weiterhin: der alte
language-Schlüssel wird als target_language akzeptiert, und
source_language fällt auf en zurück.
Verzeichnislayout¶
Der Baum ist nach AUSGANGSSPRACHE, dann Ziel+Niveau organisiert:
mein-content-repo/
manifest.yaml # Root: listet jedes Set (mit path + Paar)
sets/
de/ # Ausgangssprache: Deutsch
fr-a1/ # Ziel Französisch, Niveau A1 -> ID fr-a1-from-de
manifest.yaml # Set: listet die Lektionen
lessons/
01-begruessung.json
...
assets/ # optionale Bilder / Audio
en/ # Ausgangssprache: Englisch
fr-a1/ # -> ID fr-a1-from-en
...
Manifest-Format¶
Beide Manifest-Dateien (Root + Set) verwenden die gleiche Form
mit schema_version: '1.0'. Pflichtfelder:
schema_version: '1.0'
name: Mein Englisch-B1-Set
description: >-
Optionale Langbeschreibung.
sets:
- id: language-en-b1 # slug-sicher, eindeutig
title: Englisch B1 (Fortgeschrittene)
language: en # BCP-47 (z.B. en, fr, zh-Hans)
level: B1 # CEFR für Sprachen, frei für andere Domänen
version: '1.0.0' # Semver — pro Set-Release erhöht
lesson_count: 12
domain: language # 'language' / 'math' / 'programming' / ...
description: >-
Optionale Set-Beschreibung.
tags:
- intermediate
- business
metadata:
author: Dein Name
license: CC-BY-SA-4.0 # oder die Lizenz deiner Wahl
Das Set-Manifest listet zusätzlich jede Lektionsdatei:
Der Content-Loader iteriert metadata.lessons in der gegebenen
Reihenfolge; die Dateinamen auf der Festplatte sind irrelevant —
nur die Manifest-Reihenfolge zählt.
Lektionsschema (v1.0)¶
Jede Lektion ist eine einzelne JSON-Datei. Top-Level-Struktur:
{
"id": "01-greetings",
"title": "Begrüßungen",
"description": "Optionale 1-2-Satz-Zusammenfassung.",
"estimated_minutes": 12,
"cards": [ ... ],
"steps": [ ... ]
}
Cards¶
Eine Card ist die kleinste lernbare Einheit — typischerweise ein einzelner Begriff oder ein Konzept. Jede Card hat eine stabile id (aus Übungen referenziert) und ein front/back-Paar:
{
"id": "art-le",
"front": "le",
"back": "der (männlich Singular)",
"notes": "Vor konsonantenanfangenden männlichen Substantiven. **le chat**, **le livre**.",
"tags": ["article", "definite"]
}
notes akzeptiert Markdown. Nutze sie für Ausspracheregeln,
Falsche-Freunde-Warnungen, Ausnahme-Hinweise — alles, was die
Langzeitspeicherung verbessert. tags steuern das SRS-Filtering.
Steps¶
Eine Lektion ist eine Schritt-für-Schritt-Sequenz, jeder Schritt entweder THEORY (ein Markdown-Block) oder EXERCISE (eine der vier Übungstypen):
{
"id": "intro",
"type": "theory",
"title": "Warum Artikel wichtig sind",
"body": "# Artikel im Französischen\n\nJedes französische Nomen hat ein Geschlecht..."
}
Ein Theorie-Step kann optional einen Beispiel-Link tragen (Schema v1.4, additiv — bestehende Lektionen bleiben ohne ihn gültig). Wenn vorhanden, rendert der Viewer darunter einen Button zum Öffnen des Beispiels:
{
"id": "intro",
"type": "theory",
"body": "Die Korrelation misst den Zusammenhang...",
"example_url": "https://example.com/correlation-visualizer",
"example_label": "Interaktive Visualisierung"
}
example_url(optional): muss einehttp(s)-URL sein.example_label(optional): der Link-Text; leer wird zu einem lokalisierten „Beispiel ansehen".
Oder eine Übung:
{
"id": "ex-match-greetings",
"type": "exercise",
"title": "Begrüßungen zuordnen",
"exercise": {
"id": "ex-match-greetings",
"type": "matching",
"prompt": "Ordne jede Begrüßung ihrer Übersetzung zu.",
"card_ids": ["bonjour", "salut"],
"pairs": [
{"left": "Bonjour", "right": "Hallo"},
{"left": "Salut", "right": "Hi"}
]
}
}
Übungstyp-Referenz¶
matching¶
Drag-pair-Übung. Der Renderer mischt vor der Anzeige.
{
"id": "ex-id",
"type": "matching",
"prompt": "Ordne jedem französischen Nomen seinen Artikel zu.",
"card_ids": ["noun-1", "noun-2"],
"pairs": [
{"left": "chat", "right": "le"},
{"left": "chaise", "right": "la"}
]
}
Jedes Pair muss genau zwei Schlüssel haben: left + right.
picture_choice¶
Multiple-Choice mit Bildern. ≥ 2 Bilder, genau eines als richtig markiert.
{
"id": "ex-id",
"type": "picture_choice",
"prompt": "Welche Begrüßung passt zum Abend?",
"card_ids": ["card-1"],
"images": [
{"src": "assets/img/morning.png", "label": "Bonjour"},
{"src": "assets/img/evening.png", "label": "Bonsoir", "is_correct": "true"}
],
"hint": "Optionaler Markdown-Tipp auf Knopfdruck.",
"distractors": ["Bonjour"]
}
Wichtig: is_correct ist ein String "true", kein JSON-Boolean.
Zeigt der src-Pfad auf eine nicht vorhandene Datei, fällt der
Renderer auf das label zurück — picture_choice funktioniert
also auch ohne Illustrations-Assets.
free_text¶
Antwort eintippen. Der Renderer matched erst exakt, dann Levenshtein-tolerant.
{
"id": "ex-id",
"type": "free_text",
"prompt": "Wie sagt man 'Danke' auf Französisch?",
"card_ids": ["card-merci"],
"accept": ["Merci", "merci", "MERCI"],
"hint": "Beginnt mit M.",
"distractors": ["Bonjour", "Salut"]
}
accept[0] ist die kanonische Antwort, die bei einem falschen
Versuch angezeigt wird. Liste ≥ 3 Varianten auf, um Groß/Klein-
schreibung + Interpunktion abzudecken; Whitespace wird vom
Renderer normalisiert.
word_tiles¶
Kacheln in die richtige Reihenfolge bringen. Der Renderer mischt vor der Anzeige.
{
"id": "ex-id",
"type": "word_tiles",
"prompt": "Bring die Kacheln in die Reihenfolge: Ich sehe eine Katze.",
"card_ids": ["card-1"],
"tiles": ["Je", "vois", "un", "chat"],
"hint": "Gleiche Wortreihenfolge wie im Deutschen."
}
Falls mehrere Wortreihenfolgen korrekt sind, ergänze
accept_orderings:
Jede Reihenfolge ist eine Permutation der Tile-Indizes.
cloze (Phase 52 / v1.35.0 — Schema 1.1)¶
Lückentext mit sichtbaren ___-Markern im Satz. Jeder ___
entspricht einem Eintrag in blanks[] (Zuordnung von links nach
rechts; der Loader prüft sentence.count("___") ==
len(blanks)).
{
"id": "ex-id",
"type": "cloze",
"prompt": "Setze den unbestimmten Artikel ein.",
"card_ids": ["art-un", "noun-chat"],
"sentence": "Je vois ___ chat dans le jardin.",
"blanks": [
{
"accept": ["un"],
"hint": "männlicher unbestimmter Artikel",
"placeholder": "?"
}
],
"cloze_mode": "type",
"distractors": ["le", "la", "les"],
"hint": "*un* ist der männliche unbestimmte Artikel."
}
Render-Modi — pro Übung über cloze_mode gesetzt:
"type"(Standard, wenn nicht gesetzt): pro Lücke ein<input>. Validiert mit demselben NFC + Levenshtein-≤-1- Matcher wie free-text, sodass Autorinnen nur semantische Varianten auflisten müssen (keine Tippfehler)."select": pro Lücke ein<select>. Optionen ausaccept[0]+distractorsder Übung, pro Lücke mit stabilem Seed gemischt. Erfordert nicht-leeredistractors— der Schema-Validator weistcloze_mode: "select"ohne sie ab.
Mehrere Lücken pro Cloze sind unterstützt: jeder ___ im
Satz wird der Reihe nach auf den nächsten Eintrag in blanks
abgebildet. Jede Lücke kann eigenen Hint + Placeholder +
Accept-Liste haben. Das Element-SRS fächert pro Lücke einen
ElementAttempt auf — wer Lücke A fließend füllt, aber Lücke B
ständig verfehlt, bekommt eine lückengranulare Mastery-
Verfolgung.
Token-Rollen auf Cards (Phase 52I / v1.35.0) — optionale Card-Metadaten, mit denen der Cloze-Generator zur Laufzeit (Review-Sessions + die Korrektur-Runde am Lektionsende) eine semantisch bedeutsame Lücke wählen kann:
{
"id": "art-un",
"front": "un chat",
"back": "eine Katze",
"tags": ["article"],
"token_roles": [
{"token": "un", "role": "article"}
]
}
Geschlossene Enum von Rollen: article / verb / noun /
adjective / preposition / gender_marker / tense_marker.
Eine Rolle hinzuzufügen ist ein Minor-Schema-Version-Bump —
nicht inline erweitern.
Übungsrichtung (v1.46.0 / EXP-018)¶
Jede Übung akzeptiert ein optionales Feld direction, das angibt,
in welche Richtung die Lernenden die Karte üben:
target_to_source(Standard) — REZEPTIV: die Zielsprache wird gezeigt, die Quellsprache wird erkannt (leichter).source_to_target— PRODUKTIV: die Quellsprache wird gezeigt, die Zielsprache wird produziert (schwerer).both/random— überlässt dem Renderer / adaptiven Generator die Wahl einer konkreten Richtung pro Versuch.
{
"type": "matching",
"direction": "source_to_target",
"card_ids": ["bonjour"],
"pairs": [{ "left": "Bonjour", "right": "Guten Tag" }]
}
Das Feld ist additiv — das Schema bleibt bei Version 1.2, und
Lektionen ohne direction verhalten sich genau wie zuvor
(rezeptiv). Das SRS verfolgt die Beherrschung pro Richtung: eine
rezeptiv gemeisterte Karte ist noch nicht produktiv gemeistert.
Cloze-Übungen sind kontextgebunden und ignorieren direction. Für
eine Schwierigkeitsprogression hält man frühe Lektionen rezeptiv
und führt source_to_target in späteren Lektionen ein (genau das
macht der gebündelte Pilot-Inhalt).
Annotationen für den adaptiven Lektions-Generator (v1.36.0+)¶
Der adaptive Lektions-Generator aus Phase 53
(/adaptive-lesson/:setId, F-114) kombiniert die vorhandenen
Übungen neu, um die spezifischen Schwächen der Lernenden
gezielt zu adressieren. Der Generator funktioniert ohne
zusätzliche Annotationen, zwei Felder machen ihn jedoch
deutlich smarter:
- Breitere
token_roles-Abdeckung auf Karten. Der Generator nutzttoken_roles, um: - Semantisch sinnvolle Lücken zu wählen, wenn aus Fehlern Cloze-Varianten erzeugt werden (bereits in v1.35.0)
- Fehler als
article_gender/verb_conjugationzu klassifizieren, für die "Übungsschwerpunkt"-Chips im Dashboard (53E) - ALTERNATIVE Übungen zu finden, die dasselbe Element
testen, wenn die ursprüngliche Übung falsch war (53D
Variations-Logik — findet Kandidaten, deren Karte einen
passenden
token_roles-Eintrag hat)
Füge JEDEN Karten, die eine eigene grammatische Einheit
lehrt (Artikel, konjugierte Verbformen, geschlechtsbezogene
Substantive), einen token_roles-Eintrag hinzu. Kosten:
ein zusätzlicher JSON-Eintrag pro Karte; Nutzen: deutlich
reichhaltigere adaptive Generierung.
- Karten-Tags wie
tags: ["article", "masculine"]werden vom Fehler-Klassifizierer als Fallback gelesen, wenntoken_rolesfehlt. Sie ersetzen nichttoken_roles— sie sind eine günstige Halbweg-Annotation.
Was wir noch NICHT brauchen (auf einen zukünftigen Schema-Bump verschoben):
related_cards-Querverweise zwischen Karten aus verschiedenen Lektionen- Schwierigkeits-Ratings pro Übung (der Generator schätzt
Schwierigkeit aktuell aus
exercise.typeab) - Pro-Karte Beispielsätze in
notes, parsebar als alternative Cloze-Kontexte (der Cloze-Generator nutzt ausschließlichfront)
Faustregel: füge token_roles zu jeder Karte hinzu, die einen
grammatischen Token lehrt. Das ist die mit Abstand
wirkungsvollste Autoren-Gewohnheit für das adaptive System.
Assets (Bilder, die ein Set mitbringt) — v1.37.0+¶
Picture-Choice-Übungen und Karten-Cover-Bilder kommen aus zwei Quellen: 1. Autoren-Asset-Dateien, im Set-Manifest deklariert und neben dem Lektions-JSON ausgeliefert 2. Platzhalter-SVGs, vom Runtime erzeugt, wenn kein Asset existiert (Farbtafeln für Farbwörter, große Ziffern für Zahlen, Avatar-Stil für alles andere)
Wenn du ein Set ohne Assets veröffentlichst, funktioniert Picture-Choice trotzdem — der Platzhalter-SVG-Generator deckt Farben + Zahlen automatisch ab und fällt für alles andere auf einen deterministischen Avatar zurück.
Verzeichnis-Layout¶
Innerhalb des Set-Verzeichnisses liegen Assets unter
assets/:
sets/
language-fr-a1/
manifest.yaml
lessons/
01-greetings.json
02-numbers.json
...
assets/
img/
chat.png
chien.png
oiseau.png
Manifest-Deklaration¶
Jedes Asset muss im Set-Manifest deklariert werden, damit der Downloader weiß, was er holen soll:
sets:
- id: language-fr-a1
title: French A1
language: fr
level: A1
version: '1.0.0'
lesson_count: 10
assets:
- path: img/chat.png
size_kb: 45
- path: img/chien.png
size_kb: 38
Der path ist relativ zum assets/-Verzeichnis des Sets
(NICHT zum Lektions-JSON). Im Lektions-JSON referenzieren
Picture-Choice-Übungen Assets MIT dem assets/-Präfix:
{
"type": "picture_choice",
"prompt": "Welches ist 'chat'?",
"images": [
{"src": "assets/img/chat.png", "label": "Katze", "is_correct": "true"},
{"src": "assets/img/chien.png", "label": "Hund"}
]
}
Das Frontend entfernt den assets/-Präfix automatisch beim
Aufruf des Asset-Resolvers, sodass das Lektions-JSON in der
für Autoren intuitiven Form bleibt.
Größen- + Format-Limits¶
- Pro-Asset-Limit: 500 KiB. Der Manifest-Validator weist
Assets ab, deren deklariertes
size_kbdieses Limit überschreitet. Der Downloader weist auch Assets ab, deren tatsächliche Bytegröße die Deklaration um mehr als 10% überschreitet — hält das Manifest ehrlich. - Pro-Set Soft-Limit: 10 MiB Gesamtgröße. Der Validator warnt, lehnt aber nicht ab.
- Akzeptierte Formate:
.png/.jpg/.jpeg/.webp/.svg. Kein GIF (animierte Inhalte lenken ab), kein BMP (keine Kompression). Für Fotos bevorzugt WebP — deutlich kleiner als PNG bei vergleichbarer Qualität. Für Icons + Diagramme bevorzugt SVG — skaliert sauber + winzige Dateigröße.
Größen-Empfehlungen¶
Picture-Choice-Kacheln werden bis maximal 150x150 px auf dem
Desktop und 100x100 px auf Mobile gerendert (object-fit:
contain). Quellbilder mit 300x300 px liefern auf Retina-
Bildschirmen das beste Ergebnis ohne unnötigen Datenbedarf.
PNGs über 150 KiB sehen selten besser aus als ein gut
komprimiertes WebP halber Größe.
Wann der Runtime-Platzhalter ausreicht¶
Drei Lektionsarten, bei denen der Runtime-Platzhalter so gut ist, dass Autoren-Bilder keinen Lerngewinn bringen:
- Farb-Lektionen (
rouge/rojo/rot/red): der Platzhalter-Generator erzeugt eine farbige Hex-Kachel passend zum Farbnamen. Autoren-Kacheln sind redundant. - Zahlen-Lektionen (
7/42/1492): der Platzhalter rendert die Ziffern groß + zentriert. Autoren-Bilder hätten nur bei nicht-arabischen Ziffernsystemen Sinn. - Abstrakte Konzepte ohne offensichtliche visuelle
Darstellung (
patience,liberté): der Avatar-Platzhalter liefert einen klaren visuellen Anker, ohne eine umstrittene Icon-Wahl zu erzwingen.
Für alles andere (Tiere, Objekte, Essen, Orte, Körperteile) helfen Autoren-Bilder messbar bei Erkennen + Erinnern.
Qualitäts-Checkliste¶
Vor dem PR für eine neue Lektion prüfen:
- [ ] 3-5 Theorie-Schritte + 8-12 Übungen pro Lektion
- [ ] Mindestens 3 Übungstypen vertreten (matching, picture-choice, free-text, word-tiles oder cloze — cloze ab v1.35.0)
- [ ] Theorie-Schritte ≤ 200 Wörter je Schritt
- [ ] Free-Text-Übungen: ≥ 3 Akzept-Varianten + ≥ 3 Distraktoren
- [ ] Word-Tiles: ≥ 3 Kacheln je Übung
- [ ] estimated_minutes: 10-15 (realistisch, nicht idealisiert)
- [ ] Distraktoren sind falsch-aber-plausibel — semantisch verwandt, nie zufällig
- [ ] Card-Notes liefern echten Mehrwert (Aussprache, falsche Freunde, Ausnahme-Flag)
- [ ] Progressive Struktur: spätere Konzepte bauen auf früheren im selben Set auf
- [ ] Kulturelle Genauigkeit: realer Sprachgebrauch, nicht nur Lehrbuch-Floskeln
- [ ] Schema-Validierung: die Lektion lädt sauber via
dict_to_lesson()(siehe Lokales Testen) - [ ] Card-ID-Integrität: jedes
exercise.card_ids[i]existiert incards[]der Lektion - [ ] Sprachpaar:
target_language+source_languagegesetzt (ISO 639-1, verschieden),title_nativevorhanden
Validierung (zwei Ebenen, v1.44.0)¶
Inhalte werden durch zwei Validierungsebenen mit den GLEICHEN Prüfungen abgesichert:
- In der App, vor dem Teilen. Beim Teilen über Meine Lektionen → Für die Community bereitstellen läuft zuerst eine regelbasierte Prüfung (immer, ohne KI). Sie erzwingt die Mindestwerte unten; ein Set darunter kann nicht geteilt werden. Besteht es und ist ein KI-Schlüssel konfiguriert, kann der Lernende OPTIONAL eine ergänzende KI-Prüfung starten (Übersetzungsgenauigkeit, Distraktor-Plausibilität, Grammatik, Niveau, kulturelle Sensibilität, Natürlichkeit). Der KI-Schritt ist nie automatisch, erfordert ausdrückliche Zustimmung (der Lektionsinhalt wird an den konfigurierten Anbieter gesendet) und blockiert das Teilen nie — die regelbasierte Prüfung ist das Tor.
- In der CI des Content-Repos. Ein Pull Request an
astrapi69/adaptive-learner-contentführtscripts/validate_content.pyaus (gespiegelt unterdocs/ci/adaptive-learner-content/) und prüft jedes Set mit denselben Regeln, damit ein manueller PR das Tor nicht umgeht.
Qualitäts-Mindestwerte (hartes Tor): ≥ 5 Übungen pro Lektion, ≥ 2 Übungstypen, ≥ 1 Theorie-Schritt, Free-Text ≥ 2 akzeptierte Antworten + Distraktoren, Matching ≥ 3 Paare, Picture-Choice mit Distraktoren, keine leeren Karten-Vorder-/Rückseiten und (bei nicht-lateinischen Ausgangsschriften) Karten-Rückseiten in der Ausgangsschrift. Das sind Mindestwerte, keine Ziele — die Checkliste oben verlangt mehr.
Lokales Testen¶
Der Schema-Validator des Content-Loaders läuft im Rahmen von
make test. Eine einzelne Lektion von Hand validieren:
cd plugins/adaptive-learner-plugin-content-loader
poetry run python -c "
import json, sys
from adaptive_learner_content_loader.schema import dict_to_lesson
path = '../adaptive-learner-content/sets/en/fr-a1/lessons/01-greetings.json'
with open(path) as f:
lesson = dict_to_lesson(json.load(f))
print(f'OK: {lesson.id} — {len(lesson.cards)} Cards, {len(lesson.steps)} Steps')
"
Alle Lektionen eines Content-Repos auf einmal validieren — mit dem Validator des Content-Repos (dasselbe Skript, das dessen CI bei jedem PR ausführt):
Er findet jedes Set unter sets/{source}/{target-level}/ und prüft das
Schema plus die Qualitäts-Mindestwerte (≥5 Übungen, ≥2 Übungstypen, ≥1
Theorieschritt, Freitext-Akzepte + Distraktoren, Matching-Paare, keine
leeren Karten, Karten-ID-Integrität). Neue Lektionen werden automatisch
erkannt — keine Test-Änderung nötig.
PR-Workflow¶
Sobald dein Set fertig ist:
- Öffne einen PR gegen das Haupt-Repo (für Sets, die mit der App ausgeliefert werden sollen), ODER
- Lege ein eigenes Content-Repo unter deinem GitHub-Account an
und konfiguriere den Content-Loader über
backend/config/plugins/content-loader.yaml(unterdefault_sources).
Der Content-Loader unterstützt jedes öffentliche GitHub-Repo als
Quelle. Private Repos benötigen ein Personal Access Token, das
über die Drei-Schichten-Schlüsselverwaltung gesetzt wird
(~/.config/adaptive_learner/secrets.yaml).
Häufige Stolperfallen¶
Card-ID-Verweise: Jeder card_ids-Eintrag in einer Übung
muss in cards[] der Lektion existieren. Kopierst du eine Übung
zwischen Lektionen und vergisst die zugehörige Card mitzunehmen,
schlägt die Validierung fehl.
Slug-sichere IDs: Alle IDs (Lesson, Card, Step, Exercise)
müssen ^[a-z0-9]+(-[a-z0-9]+)*$ matchen. Keine Unterstriche,
keine Apostrophe, keine Großbuchstaben, keine führenden/abschließenden
Bindestriche.
is_correct: "true": Es ist ein String, kein JSON-Boolean.
Das Schema verlangt explizit "true", weil die picture_choice-
Felder intern als dict[str, str] modelliert sind.
Zusätzliche Felder: Jedes Modell hat extra="forbid". Ein
nicht-dokumentiertes Feld führt zur Ablehnung der gesamten
Lektion. Halte dich an die dokumentierten Felder.
Theory-Body: Theory-Steps benötigen ein nicht-leeres
body-Feld (Markdown). Exercise-Steps dürfen kein body tragen
— nutze stattdessen den prompt der Übung.
Referenz: die Pilot-Sets¶
Die beiden mit Adaptive Learner ausgelieferten Sets sind die kanonischen Referenzen:
sets/en/fr-a1/— Französisch A1 für Englischsprachige (10 Lektionen, ~2 Stunden);sets/de/fr-a1/ist das deutschsprachige Pilot-Set.sets/en/es-a1/+sets/de/es-a1/— Spanisch A1 (15 Lektionen je Quellsprache), imadaptive-learner-content-Repo.
Beide folgen den in diesem Leitfaden beschriebenen Konventionen. Eine vollständige Lektion durchzulesen ist der schnellste Weg, die Struktur zu verinnerlichen.
Weg zur Community-Beteiligung (v1.42.0)¶
Du musst Lektionen nicht von Grund auf von Hand erstellen. Der schnellste Weg, etwas beizutragen, ist, eine Lektion in der App zu erstellen und zu teilen:
- Importiere einen Chat und analysiere ihn, dann Als Offline-Lektion speichern (oder beende eine adaptive Lektion und Diese Lektion speichern?). Die Lektion erscheint unter Meine Lektionen im Set-Browser.
- Klicke bei „Meine Lektionen" auf Als Content-Set
exportieren, um ein Content-Set als
.zipherunterzuladen (Manifest + Lektionen). Exporte enthalten nur den Lektionsinhalt — keinen Fortschritt, keine Fehlerhistorie, nichts Persönliches. - Klicke auf Für die Community bereitstellen, um einen
vorausgefüllten Pull Request im Inhalts-Repository zu öffnen
— die Lektions-JSON wird am richtigen Pfad im Baum committet,
kein
.zip-Anhang nötig. - Die CI des Repos validiert den PR automatisch; ein Maintainer
prüft die Lektion, bringt das Manifest (id, title, language,
level, tags) in Einklang mit den obigen Konventionen und führt
ihn unter
sets/zusammen. Nach dem Merge können alle sie aus dem Set-Browser herunterladen.
Das ist der soziale Weg: Die Prüfung ist manuell (ein Maintainer kuratiert jede Ergänzung — nichts wird automatisch veröffentlicht), und der gesamte Ablauf braucht nur GitHub. Erzeugte Lektionen werden bereits gegen das Schema validiert, sodass eine beigetragene Lektion meist nur etwas Manifest-Feinschliff braucht.
Teilen-Assistent, Variationen und Autoren-Credit (Phase 64)¶
Eine Lektion aus Meine Lektionen zu teilen öffnet einen vierstufigen Assistenten, statt direkt zu GitHub zu springen:
- Vorschau + Platzierung. Die App berechnet genau, wo die
Lektion im Baum landet (
sets/{quelle}/{ziel}-{niveau}/) und einen automatisch nummerierten Dateinamen ({nn}-{slug}.json, die nächste Nummer nach den bestehenden Lektionen). Ein ganz neues Paar + Niveau zeigt "Neues Set! Du bist der Erste." - Duplikat-Prüfung. Die Lektion wird mit den bereits in diesem Pfad vorhandenen Lektionen verglichen (Karten- und Übungs-Überschneidung — beratend, niemals blockierend). Wenn etwas Ähnliches existiert, kannst du:
- Als Variation teilen — die Lektion wird mit
variation_of: "{original_id}"plus einer optionalenvariation_notemarkiert ("Wie unterscheidet sich deine Version?"). - Nur die neuen Übungen vorschlagen (bei Beinahe- Duplikaten) — der Assistent extrahiert genau die Übungen, die dem Original fehlen, samt der zugehörigen Karten, als Ergänzungs-Variation.
- Qualitäts-Zusammenfassung. Die Befunde des regelbasierten Validators (plus die optionale KI-Prüfung); Warnungen werden angezeigt, blockieren aber nie.
- Teilen + Feiern. Ein Klick öffnet den GitHub-Pull-Request (Datei-Editor bei kleinen Lektionen, Upload-Seite bei großen), und die App bedankt sich mit einer kleinen Feier.
Variations- und Credit-Felder (Schema 1.3, alle optional)¶
{
"variation_of": "10-passe-compose",
"variation_note": "Mehr Übungen zur Angleichung",
"contributed_by": "Maria S.",
"contributed_at": "2026-06-01T14:30:00Z"
}
Alle vier sind additiv und optional; Lektionen ohne sie
verhalten sich genau wie zuvor. contributed_by wird gesetzt,
wenn der Autor beim Teilen den Credit aktiviert (ein Feld "Dein
Name (optional)", das lokal für das nächste Mal gemerkt wird).
Ist es vorhanden, zeigt der Viewer eine dezente Zeile
"Bereitgestellt von {name}" unter dem Titel, und der
Pull-Request-Text führt den Autor in seiner Metadaten-Tabelle auf.
Beitrags-Historie und Lücken¶
Geteilte Lektionen werden lokal gemerkt (kein Konto nötig) unter Meine Beiträge mit einem Zähler und einer Community-Beitragende-Auszeichnung ab fünf geteilten Lektionen. Der Set-Browser zeigt außerdem Fehlende Lektionen — ermutigende Vorschläge für das nächste CEFR-Niveau eines bestehenden Paars oder eine Zielsprache, die für eine Ausgangssprache existiert, für eine andere aber fehlt ("Kannst du helfen?").
Verwandte Seiten¶
- Lektionen erstellen — Überblick — Einstieg + Lektions-Creator in der App
- Buchempfehlungen —
books.yamlpro Domäne pflegen - Mehrere Content-Repositories — eigenes Repo verbinden