Communitygithub.com

ci-story

Erstelle anfängergerechte ‚Comprehensible-Input‘-Story-Videos (illustriert, vertont, mit starker Wiederholung) für einen Sprachkurs – derzeit für Mandarin-Chinesisch.

Was ist ci-story?

ci-story ist ein Skill, der speziell für Mandarin-Chinesisch-Lernende kurze, stark wiederholende Videos produziert. Der Schwierigkeitsgrad ist bewusst für absolute Anfänger ausgelegt. Der Skill analysiert den Lernfortschritt und erstellt basierend auf dem Kursplan und den expliziten Wünschen des Lernenden (z. B. zu einem bestimmten Thema oder mit bestimmten schwierigen Wörtern) eine Videogeschichte. Dabei werden 2–3 neue, konkrete und darstellbare Wörter eingeführt und jedes neue Wort mindestens 10-mal wiederholt; fällige Wiederholungswörter werden mindestens drei Mal eingebaut. Der Workflow umfasst das Auslesen des Kurses, die Planung der Lektion, das Zeichnen von SVG-Sprites (durch Claude) oder das Generieren von PNG-Bildern (durch Codex), die Vertonung (lokal mit Qwen3TTS oder online mit edge-tts) und das Rendern des Videos. Der Skill unterstützt sowohl Claude (für handgezeichnete SVG-Sprites) als auch Codex (für generierte Grafiken) und arbeitet mit einem lokalen GPU-basierten TTS-Modell oder einem Online-Dienst. Besonders wichtig: Vor der Generierung wird dem Lernenden immer ein Plane vorgestellt, den er freigeben oder ablehnen kann – erst nach expliziter Zustimmung wird das Video erstellt.

Funktioniert mit✓Claude Code✓Codex CLI~Cursor
npx skills add https://github.com/victorblaga/ci-story/tree/HEAD/skills/ci-story

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Was macht ci-story?

Jede Lektion ist ein kurzes Video. Eine kleine Besetzung spielt eine einfache Geschichte, während eine ruhige Stimme kurze Sätze spricht, und jeder Satz wird mehrfach wiederholt, wobei das Bild die Bedeutung offensichtlich macht. Untertitel in der Schrift der Sprache plus Romanisierung heben das gesprochene Wort hervor. Die letzten etwa 15 % laufen ohne Untertitel, zum reinen Hören.

Erarbeite den Plan selbst aus den Kursdaten und der Anfrage des Lernenden, präsentiere ihn dann und warte auf die Zustimmung des Lernenden, bevor du etwas generierst (Schritt 5). Abgesehen davon stellst du während des Laufs keine Fragen, außer beim erstmaligen Einrichten und vor großen Downloads.

Orte

  • <skill>: der Ordner dieses Skills (der Ordner, der diese Datei enthält), mit scripts/, references/ und assets/.

  • Konfiguration: ~/.config/ci-story/config.json enthält workspace, tts_project (nur für eine lokale Qwen-Stimme) und optional env. Fehlt sie oder existiert ein Pfad darin nicht, folge zuerst references/setup.md.

  • Ein Kurs: <workspace>/courses/<lang>/, mit:

    • course.json: Stimme, Schriftarten
    • notes.md: Lerner-Überschreibungen
    • art/drawn/, art/generated/
    • audio-cache/
    • lessons/NNN-slug/

    Wenn der Lernende keinen Kurs nennt und nur einer existiert, verwende diesen.

  • Führe niemals uv lock --upgrade im TTS-Projekt aus und ändere niemals eine Torch-Pfadquelle dort. Ein Multi-GB-Torch-Wheel kann als lokale Datei gepinnt sein.

Arbeitsablauf für „eine neue Geschichte“

  1. Lies den Kurs. Führe python3 <skill>/scripts/curriculum.py report <course> aus. Lies notes.md sowie die letzten ein oder zwei lesson.json-Dateien und ihre story.py, um Kontinuität der Besetzung, Requisiten und Helfer zu gewährleisten, die du wiederverwenden kannst.

  2. Notiere, was der Lernende gesagt hat.

    • Ein Wort, das schwer fällt oder vergessen wurde: Füge eine Zeile struggling: 给 hinzu.
    • Ein Wort, das sie gut kennen: Füge solid: 苹果 hinzu.
    • Wörter, von denen sie mehr möchten: Füge focus: … hinzu.

    Tag-Zeilen enthalten nur Wörter, durch Komma getrennt. Eine spätere Zeile überschreibt eine frühere für dasselbe Wort, und Bemerkungen in (Klammern) werden ignoriert.

  3. Plane wie ein Lehrer:

    • Beachte zuerst die Anfrage: ihr Thema, Fokus-Wörter oder Wiederholungsziel.
    • Wähle 2–3 neue Wörter. Jedes muss konkret und darstellbar sein (ein Gegenstand, eine sichtbare Handlung, eine Zahl, eine Farbe oder ein Gegensatz wie haben/nicht haben oder groß/klein), im alltäglichen Sprachgebrauch üblich und mit bekannten Wörtern zu einfachen Sätzen kombinierbar. Die erste Lektion eines Kurses verwendet insgesamt nur 5–7 Wörter (z. B. 这 是 一个 plus 2–3 Nomen), jedes mindestens 10 Mal gehört. Da in einer ersten Lektion jedes Wort neu ist, liste sie alle in new_words.
    • Baue jedes fällige Wort mindestens 3 Mal ein. Verwende fragile- und struggling-Wörter häufiger. Lasse solid-Wörter weg, es sei denn, die Geschichte braucht sie.
    • Jedes neue Wort wird mindestens 10 Mal gehört, jedes Mal mit dem Bild, das seine Bedeutung zeigt.
    • Geschichtsform: Einführung (这是…), Gegensatz (有 / 没有), eine Handlung oder Veränderung, dann eine Zusammenfassung, die alles kombiniert. Behalte die bestehende Besetzung (man, woman) bei, es sei denn, die Geschichte braucht jemand Neues.
    • Die Länge ergibt sich aus dem Inhalt. Standardmäßig verwende so viele Sätze, wie nötig sind, um jedes neue Wort 10+ Mal zu hören und die fälligen Wörter abzudecken. Das sind normalerweise 30–50 Sätze mit 2–6 Wörtern, etwa 2–4 Minuten (ca. 4 s pro Satz). Wenn der Lernende um eine längere Geschichte bittet („etwa 6 Minuten“, „eine lange“), behalte dieselben 2–3 neuen Wörter und füge Szenen hinzu: Erzähle die Geschichte nach, setze dieselben Wörter in eine neue Situation und bringe ältere Wörter zurück. Füge niemals neue Wörter hinzu, um die Zeit zu füllen.
    • Bevorzuge die exakte Wiederholung eines Satzes gegenüber dem Erfinden einer Variante. Exakte Wiederholungen klingen identisch (sie kommen aus dem Cache) und helfen beim Hören.
  4. Erstelle den Entwurf <course>/lessons/NNN-slug/lesson.json>. NNN stammt aus dem Bericht. Dieser Entwurf ist die Quelle der Wahrheit des Plans, und du wirst ihn überarbeiten, bis der Lernende ihn genehmigt. Setze jeden Beat in eine Zeile, damit Beat-Nummern leicht zu sehen sind:

    {"title": "…", "art": "drawn", "new_words": ["…"],
     "beats": [
      {"words": [["这","zhè"],["是","shì"],["一个","yí ge"],["人","rén"]], "show": "man alone", "target": ["man"], "caption": true}
     ]}
    
    • words enthält die Worteinheiten mit tonmarkierter Romanisierung unter Verwendung des Sandhi, wie gesprochen (一个 = yí ge). Keine Satzzeichen innerhalb von words. Jeder Beat wird als Aussage gelesen, die mit 。 endet, also plane keine Fragen.
    • show ist Prosa, die das Bild beschreibt, für dich und für die Kunst.
    • target listet die Sprite-IDs auf, die leuchten, während der Beat gesprochen wird.
    • caption ist für etwa die letzten 15 % der Beats auf false gesetzt.
    • pause ist optional: die Stille nach dem Beat, Standard 1,5 s, Minimum 1,2. Mache sie länger (3–3,5 s) nach einem Beat, dem eine Bewegung folgt, z. B. eine Übergabe, damit die Bewegung in der Stille stattfindet.
    • art ist drawn, wenn du Claude bist, und generated, wenn du Codex bist.

    Führe dann python3 <skill>/scripts/curriculum.py check <lesson.json> aus. Behebe jeden ERROR und jede WARNING. Es gibt auch eine nummerierte Liste der Beats aus; behalte sie für Schritt 8.

  5. Präsentiere den Plan und warte auf die Freigabe. Bevor du Kunst, Erzählung oder Rendering erstellst, zeige dem Lernenden einen kurzen Plan, der aus dem Entwurf und der check-Ausgabe erstellt wurde:

    • Geschichte: zwei bis vier Sätze darüber, was passiert, Szene für Szene, in einfachem Englisch.
    • Neue Wörter, jedes mit seiner Romanisierung, Bedeutung und wie das Bild es zeigt (z. B. „香蕉 xiāngjiāo, Banane: die Frau hält eine, dann gibt sie sie weiter“) und wie oft es gehört wird.
    • Wiederholungswörter, mit dem Grund, warum jedes enthalten ist (fällig, fragil, kämpfend oder Fokus) und wie oft es gehört wird. Erwähne jedes fällige Wort, das du weggelassen hast, und warum.
    • Länge: die Anzahl der Sätze und wie lange das Video ungefähr läuft (ca. 4 s pro Satz), plus die Kunstsammlung und die Stimme.
    • Alles, was neu zu zeichnen oder zu generieren ist.

    Frage dann höflich und in einer kurzen Zeile, ob sie Änderungen wünschen oder ob du weitermachen sollst. Halte inne und warte auf ihre Antwort. Behandle jedes Feedback (ein anderes Thema, andere Wörter, länger oder kürzer, eine andere Wendung) als Überarbeitung: aktualisiere lesson.json und notes.md nach Bedarf, führe check erneut aus und präsentiere den überarbeiteten Plan auf dieselbe Weise. Generiere erst, wenn der Lernende klar zustimmt („ok“, „los“, „generiere es“, „sieht gut aus“). Wenn sie von vornherein gesagt haben, dass sie den Plan nicht überprüfen möchten („mach einfach“, „überrasch mich“), überspringe diesen Schritt. Wenn sie die Idee fallen lassen, lösche den Entwurfs-Lektion-Ordner.

  6. Kunst. Verwende die Sprites im <course>/art/<set>/ wieder. Für alles Fehlende folge genau references/art.md und füge dann nur neue Dateien hinzu:

    • Claude: Schreibe SVG von Hand in art/drawn/<name>.svg, modelliert nach den vorhandenen Dateien des Kurses (oder references/examples/ in einem neuen Kurs). Eine neue Figur benötigt alle drei Ausdrücke. Sieh es dir an, bevor du es verwendest:
      uv run --no-project --with 'cairosvg>=2.7,<3' python -c "import cairosvg,sys; cairosvg.svg2png(url=sys.argv[1], write_to=sys.argv[2], output_width=512)" <svg> /tmp/preview.png
      
    • Codex: Verwende den Bildgenerator. Speichere transparente PNGs in art/generated/ und hänge den Prompt an art/generated/prompts.md an.
  7. Vertonung. Verwende den Befehl, der zu voice.engine in course.json passt:

    qwen (lokale GPU): <env from config> uv run --project <tts_project> --no-sync python <skill>/scripts/audio.py <lesson dir>
    edge (online):    uv run <skill>/scripts/audio.py <lesson dir>
    

    Führe ihn im Hintergrund aus. Zwischengespeicherte Sätze werden ohne GPU oder Netzwerk wiederverwendet. Neue brauchen mit qwen auf einer GPU etwa 30 s pro Stück (3 Takes plus Ausrichtung) oder mit edge etwa eine Sekunde. Wenn der Lauf unterbrochen wird, führe ihn erneut aus: fertige Sätze sind zwischengespeichert. Dies schreibt timing.json (echte Beat- und Wortzeiten) und narration.wav. Wenn ein zwischengespeicherter Clip falsch klingt (ein falsch gelesenes Wort, eine ausgelassene Silbe), lösche seine beiden Dateien und führe ihn erneut aus; grep -l '<sentence>' <course>/audio-cache/*.json findet sie.

  8. Choreografie: Schreibe <lesson dir>/story.py. Es definiert scene(t, timing) und gibt (sprites, marks) zurück. Der Vertrag steht im Docstring von scripts/engine.py.

    • Binde alle Bewegungen an Beat-Zeiten (timing["beats"][i]["start"] und ["end"]), niemals an buchstäbliche Sekunden.
    • Handpositionen werden von e.anchor / e.on_hand mit e.HAND_L / e.HAND_R bezogen. Beim gespiegelten rechten Charakter ist HAND_R die Hand zur Mitte hin.
    • e.ease sorgt für sanfte Bewegungen. Für Ringe und alles, was an „den Satz, der gerade gesprochen wird“ gebunden ist, verwende index, _ = e.active_beat(t, timing).
    • Sieh dir zuerst die früheren story.py-Dateien des Kurses an und references/examples/story-002-banana.py: eine vollständige 44-Beat-Choreografie mit track (Keyframes mit Bögen), eat (zum Mund, dann schrumpfen), einem MOUTH-Punkt, Übergaben und Ringen. Kopiere, was passt. Wenn ein Helfer in eine zweite Lektion kopiert wurde, verschiebe ihn in einem separaten Schritt in engine.py.

    Ein Gerüst:

    import engine as e
    
    def scene(t, timing):
        s = [beat["start"] for beat in timing["beats"]]
        man = {"id": "man", "sprite": "man-0", "x": e.LEFT_X, "y": e.CHARACTER_Y, "h": e.CHARACTER_H}
        woman = {"id": "woman", "sprite": "woman-0", "x": e.RIGHT_X, "y": e.CHARACTER_Y,
                 "h": e.CHARACTER_H, "flip": True, "opacity": e.ease((t - s[2] + 0.8) / 0.8)}
        ax, ay = e.on_hand(man, e.HAND_R, 84)
        return [man, woman, {"id": "apple", "sprite": "apple", "x": ax, "y": ay, "h": 84}], []
    

    Konventionen:

    • Verwende den Ausdruck -1 (enttäuscht), während eine Figur leerhändig ist.
    • Zeichne einen Ring (marks) über eine leere Hand, während 没有 darüber gesagt wird.
    • Bringe eine Figur oder ein Objekt kurz vor dem Beat herein, der sie benennt.
    • Leite Objekte so, dass sie nie über ein Gesicht fahren.
  9. Rendern: uv run <skill>/scripts/render.py <lesson dir> dauert etwa 1–2 Minuten. Behebe jedes WARNING beat N: target not on screen, das es ausgibt.

  10. Überprüfen vor dem Bericht.

  • Öffne jedes contact-sheet-N.jpg: eine Kachel pro Beat, in der Satzmitte. Überprüfe, dass das leuchtende Ziel zum Satz passt, Objekte in den Händen sitzen, Ringe auf leeren Händen sitzen, Gesichter passen und nichts den Untertitelstreifen überlappt oder den Rahmen verlässt.
  • Bewegungen finden in den Pausen statt, überprüfe daher ein paar Übergaben oder Ankünfte: ffmpeg -v error -ss <seconds> -i lesson.mp4 -frames:v 1 /tmp/frame.png. Beat-Zeiten stehen in timing.json.
  • Korrigiere story.py (oder target/caption in lesson.json; beides wird zum Renderzeitpunkt gelesen) und rendere neu. Wenn du words änderst, führe zuerst Schritt 7 erneut aus.
  • Teile dem Lernenden dann den Lektionspfad, die neuen Wörter und die wiederholten Wörter mit.

Andere Anfragen

  • „Wiederhole …“ / „Ich habe … vergessen“: Erstelle eine Lektion ohne neue Wörter oder mit einem, aufgebaut um die genannten Wörter oder die Wörter dieser Lektion. Markiere sie als struggling, wenn der Lernende sagt, dass sie ihnen entfallen.

  • Wortschatz oder Fortschritt: Führe den Bericht aus und fasse ihn zusammen.

  • Stimmen: Die Stimme des Kurses steht in course.json: entweder qwen (lokal, z. B. Serena mit einer Langsam-Lehrer-Anweisung) oder edge (online, z. B. zh-CN-XiaoxiaoNeural mit Rate -25%).

    • Qwen-Sprecher: <env> uv run --project <tts_project> --no-sync python -c "from qwen_tts import Qwen3TTSModel as M; print(M.from_pretrained('Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice').get_supported_speakers())". Dies lädt das Modell.
    • edge-tts-Stimmen: uvx edge-tts --list-voices | grep zh-.

    Das Wechseln der Engine oder Stimme ist eine Bearbeitung des voice-Blocks (siehe references/setup.md, Schritt 3). Es startet einen neuen Satz zwischengespeicherter Clips.

Regeln

  • Ein Lauf schreibt nur in seinen Lektionsordner, das audio-cache/ des Kurses und notes.md, plus neue Dateien unter <course>/art/<set>/.
  • Änderungen an <skill>/scripts, vorhandener Kunst oder dem TTS-Projekt sind ein separater Schritt, der nur auf Anfrage des Lernenden durchgeführt wird.
  • Importierte Lektionen ohne story.py werden niemals neu gerendert.

Verwandte Skills