Communitygithub.com

ci-story

초보 학습자를 위한 이해 가능한 입력 기반 스토리 비디오(일러스트, 내레이션, 반복 포함)를 생성합니다. 주로 중국어 과정용이며, 학습자가 새 스토리나 수업, 복습, 특정 주제나 단어에 관한 스토리, 어려운 단어나 이미 아는 단어를 말하거나 코스 어휘나 사용 가능한 음성을 확인하거나 ci-story 설정을 원할 때 사용합니다.

ci-story란 무엇인가요?

ci-story는 언어 학습자를 위해 설계된 AI 에이전트 스킬로, 이해 가능한 입력(Comprehensible Input) 방식의 스토리 비디오를 자동 생성합니다. 현재는 중국어(만다린) 과정을 지원하며, Claude Code, Codex, Cursor 등 다양한 AI 에이전트 환경에서 실행됩니다. 이 스킬은 학습자의 요청에 따라 새로운 스토리, 복습 수업, 특정 단어에 집중한 수업을 계획하고, 일러스트(SVG 또는 생성 이미지), 내레이션(로컬 Qwen TTS 또는 edge-tts), 자막(로마자 병기 포함)이 포함된 완전한 비디오를 생성합니다. 각 문장은 그림과 함께 10회 이상 반복되어 의미가 명확하게 전달되며, 비디오의 마지막 약 15%는 자막 없이 순수 청취를 위한 구간으로 구성됩니다. 학습자의 어휘 상태(어려운 단어, 이미 아는 단어, 복습이 필요한 단어)를 추적하여 개인화된 학습 경험을 제공합니다.

지원 대상✓Claude Code✓Codex CLI~Cursor
npx skills add https://github.com/victorblaga/ci-story/tree/HEAD/skills/ci-story

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

ci-story은(는) 무엇을 하나요?

각 수업은 짧은 비디오입니다. 소수의 등장인물이 간단한 이야기를 연기하고, 차분한 목소리가 짧은 문장을 낭독하며, 각 문장은 그림을 통해 의미가 명확해지도록 여러 번 반복됩니다. 언어 스크립트와 로마자 병기 자막이 현재 말해지고 있는 단어를 강조합니다. 마지막 약 15%는 자막 없이 순수 청취 구간입니다.

코스 데이터와 학습자의 요청을 바탕으로 계획을 직접 수립한 후, 생성(5단계)을 시작하기 전에 먼저 제시하고 학습자의 승인을 기다리세요. 그 외에는 첫 실행 설정 및 대용량 다운로드 전을 제외하고 실행 중 질문을 하지 마세요.

위치

  • <skill>: 이 스킬의 폴더(이 파일이 있는 폴더)로, scripts/, references/, assets/를 포함합니다.

  • 설정: ~/.config/ci-story/config.json에는 workspace, tts_project(로컬 qwen 음성 전용) 및 선택적 env가 저장됩니다. 파일이 없거나 경로가 존재하지 않으면 references/setup.md를 먼저 따르세요.

  • 코스: <workspace>/courses/<lang>/ 폴더에는 다음이 포함됩니다.

    • course.json: 음성, 폰트
    • notes.md: 학습자 재정의
    • art/drawn/, art/generated/
    • audio-cache/
    • lessons/NNN-slug/

    학습자가 코스 이름을 지정하지 않고 하나만 존재하는 경우 해당 코스를 사용하세요.

  • tts 프로젝트에서 uv lock --upgrade를 실행하지 마세요. 또한 해당 프로젝트에서 torch 경로 소스를 변경하지 마세요. 멀티 GB torch 휠이 로컬 파일로 고정되어 있을 수 있습니다.

"새 이야기" 워크플로우

  1. 코스를 읽으세요. python3 <skill>/scripts/curriculum.py report <course>를 실행하세요. notes.md와 마지막 하나 또는 두 개의 lesson.json 파일 및 해당 story.py를 읽어 연속성 있는 캐스트, 소품 및 재사용 가능한 도우미를 확인하세요.

  2. 학습자가 말한 것을 기록하세요.

    • 어렵거나 잊어버린 단어: struggling: 给 줄을 추가하세요.
    • 잘 알고 있는 단어: solid: 苹果를 추가하세요.
    • 더 많이 듣고 싶은 단어: focus: …를 추가하세요.

    태그 줄은 쉼표로 구분된 단어만 포함합니다. 나중 줄이 같은 단어에 대한 이전 줄을 재정의하며, (괄호) 안의 주석은 무시됩니다.

  3. 선생님처럼 계획하세요:

    • 먼저 요청을 존중하세요: 주제, 집중 단어 또는 복습 대상을 반영하세요.
    • 2-3개의 새 단어를 선택하세요. 각각은 구체적이고 시각적으로 보여줄 수 있어야 하며(사물, 눈에 보이는 동작, 숫자, 색상, 또는 있음/없음, 크다/작다 같은 대비), 일상 대화에서 흔히 쓰이고, 이미 알고 있는 단어와 결합하여 간단한 문장을 만들 수 있어야 합니다. 코스의 첫 번째 수업은 총 5-7개의 단어만 사용하고(예: 这 是 一个에 2-3개의 명사), 각 단어를 10회 이상 듣게 합니다. 첫 수업의 모든 단어는 새 단어이므로 모두 new_words에 나열하세요.
    • 복습 대상(due) 단어를 각각 최소 3회 이상 포함시키세요. 취약(fragile) 및 struggling 단어는 더 자주 사용하세요. solid 단어는 이야기에 꼭 필요하지 않으면 제외하세요.
    • 새 단어는 각각 최소 10회 이상 들리며, 매번 그림이 그 의미를 보여줍니다.
    • 이야기 구조: 도입(这是…), 대비(有 / 没有), 동작이나 변화, 그리고 모든 것을 결합하는 요약. 기존 캐스트(man, woman)를 유지하되, 이야기에 필요한 새 인물이 있다면 추가하세요.
    • 길이는 내용에 따라 결정됩니다. 기본적으로 각 새 단어가 10회 이상 나오고 복습 대상 단어를 모두 다루는 데 필요한 만큼의 문장을 사용하세요. 일반적으로 30-50개의 2-6단어 문장으로 약 2-4분(문장당 약 4초)입니다. 학습자가 더 긴 이야기("약 6분", "긴 이야기")를 요청하면 동일한 2-3개의 새 단어를 유지하고 장면을 추가하세요: 이야기를 다시 말하거나, 같은 단어를 새로운 상황에 넣거나, 오래된 단어를 다시 가져오세요. 시간을 채우기 위해 새 단어를 더 추가하지 마세요.
    • 변형을 만드는 것보다 문장을 정확히 반복하는 것을 선호하세요. 정확한 반복은 동일하게 들리며(캐시에서 가져옴) 듣기 연습에 도움이 됩니다.
  4. <course>/lessons/NNN-slug/lesson.json 초안을 작성하세요. NNN은 보고서에서 가져옵니다. 이 초안이 계획의 기준 자료(source of truth)이며, 학습자가 승인할 때까지 수정합니다. 각 비트(beat)를 한 줄에 배치하여 비트 번호를 쉽게 볼 수 있도록 하세요:

    {"title": "…", "art": "drawn", "new_words": ["…"],
     "beats": [
      {"words": [["这","zhè"],["是","shì"],["一个","yí ge"],["人","rén"]], "show": "man alone", "target": ["man"], "caption": true}
     ]}
    
    • words는 성조 표시된 로마자와 함께 단어 단위를 보유하며, 발음된 대로의 연음(一个 = yí ge)을 사용합니다. words 안에 구두점은 없습니다. 모든 비트는 。으로 끝나는 진술문으로 읽히므로 질문을 계획하지 마세요.
    • show는 그림을 설명하는 산문으로, 사용자와 아트 생성을 위한 것입니다.
    • target은 비트가 말해지는 동안 빛나는 스프라이트 ID 목록입니다.
    • caption은 마지막 약 15%의 비트에서 false입니다.
    • pause는 선택 사항입니다: 비트 후 침묵 길이로, 기본값 1.5초, 최소 1.2초입니다. 물건 건네기 같은 동작이 뒤따르는 비트(3-3.5초)에서는 더 길게 설정하여 동작이 침묵 중에 발생하도록 하세요.
    • art는 사용자가 Claude일 때 drawn, Codex일 때 generated입니다.

    그런 다음 python3 <skill>/scripts/curriculum.py check <lesson.json>을 실행하세요. 모든 ERROR와 모든 WARNING을 수정하세요. 또한 비트의 번호 매겨진 목록을 출력합니다. 8단계를 위해 보관하세요.

  5. 계획을 제시하고 승인을 기다리세요. 아트, 내레이션 또는 렌더링 전에 초안과 check 출력을 기반으로 한 짧은 계획을 학습자에게 보여주세요:

    • 이야기: 평범한 영어로 장면별로 무슨 일이 일어나는지 2-4문장.
    • 새 단어: 각각 로마자, 의미, 그림이 어떻게 보여주는지(예: "香蕉 xiāngjiāo, 바나나: 여자가 하나를 들고 있다가 건넵니다"), 그리고 들리는 횟수.
    • 복습 단어: 각 단어가 포함된 이유(due, fragile, struggling 또는 focus)와 들리는 횟수. 제외한 복습 대상 단어와 그 이유를 언급하세요.
    • 길이: 문장 수와 비디오 실행 시간(문장당 약 4초), 아트 세트 및 음성.
    • 새로 그리거나 생성해야 할 것.

    그런 다음 정중하고 짧은 한 줄로 변경 사항이 있는지 또는 생성을 진행해도 괜찮은지 물어보세요. 멈추고 답변을 기다리세요. 모든 피드백(다른 주제, 다른 단어, 더 길게 또는 짧게, 다른 전개)을 수정으로 간주하고, lesson.json과 notes.md를 필요에 따라 업데이트하고, check를 다시 실행하고, 수정된 계획을 같은 방식으로 제시하세요. 학습자가 명확히 승인("ok", "go", "generate it", "looks good")한 경우에만 생성하세요. 학습자가 계획 검토를 원하지 않는다고 미리 말한 경우("just make it", "surprise me") 이 단계를 건너뛰세요. 학습자가 아이디어를 취소하면 초안 수업 폴더를 삭제하세요.

  6. 아트. <course>/art/<set>/에 있는 스프라이트를 재사용하세요. 누락된 항목이 있으면 references/art.md를 정확히 따르고, 새 파일만 추가하세요:

    • Claude: 코스의 기존 파일(또는 새 코스의 references/examples/)을 모델로 하여 art/drawn/<name>.svg에 SVG를 직접 작성하세요. 새 캐릭터는 세 가지 표정이 모두 필요합니다. 사용하기 전에 확인하세요:
      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: 이미지 생성기를 사용하세요. 투명 PNG를 art/generated/에 저장하고 프롬프트를 art/generated/prompts.md에 추가하세요.
  7. 내레이션. course.json의 voice.engine과 일치하는 명령을 사용하세요:

    qwen (로컬 GPU): <env from config> uv run --project <tts_project> --no-sync python <skill>/scripts/audio.py <lesson dir>
    edge (온라인):    uv run <skill>/scripts/audio.py <lesson dir>
    

    백그라운드에서 실행하세요. 캐시된 문장은 GPU나 네트워크 없이 재사용됩니다. 새 문장은 qwen GPU에서 약 30초(3회 테이크 + 정렬), edge에서 약 1초가 소요됩니다. 실행이 중단되면 다시 실행하세요: 완료된 문장은 캐시됩니다. 이 명령은 timing.json(실제 비트 및 단어 시간)과 narration.wav를 작성합니다. 캐시된 클립이 잘못 들리면(단어 오독, 음절 누락) 해당 두 파일을 삭제하고 다시 실행하세요. grep -l '<sentence>' <course>/audio-cache/*.json으로 찾을 수 있습니다.

  8. 안무: <lesson dir>/story.py를 작성하세요. scene(t, timing)을 정의하고 (sprites, marks)를 반환합니다. 계약은 scripts/engine.py의 독스트링에 있습니다.

    • 모든 움직임을 비트 시간(timing["beats"][i]["start"] 및 ["end"])에 맞추고, 절대 초를 사용하지 마세요.
    • 손 위치는 e.anchor / e.on_hand와 e.HAND_L / e.HAND_R에서 가져옵니다. 오른쪽에 반전된 캐릭터의 경우 HAND_R이 중앙을 향한 손입니다.
    • e.ease는 부드러운 움직임을 제공합니다. 반지 및 "말해지고 있는 문장"에 연결된 모든 것에는 index, _ = e.active_beat(t, timing)을 사용하세요.
    • 먼저 코스의 이전 story.py 파일과 references/examples/story-002-banana.py를 참고하세요: 44비트 완전 안무로, track(호가 있는 키프레임), eat(입으로, 그 다음 축소), MOUTH 포인트, 물건 건네기 및 반지가 포함됩니다. 맞는 것을 복사하세요. 도우미가 두 번째 수업에 복사된 경우 별도 단계로 engine.py로 이동하세요.

    골격:

    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}], []
    

    규칙:

    • 캐릭터가 손에 아무것도 없을 때는 표정 -1(실망)을 사용하세요.
    • 没有에 대해 말할 때 빈 손 위에 반지(marks)를 그리세요.
    • 캐릭터나 물체는 해당 비트가 이름을 부르기 직전에 등장시키세요.
    • 물체가 얼굴 위를 지나가지 않도록 경로를 지정하세요.
  9. 렌더링: uv run <skill>/scripts/render.py <lesson dir>은 약 1-2분이 소요됩니다. 출력되는 WARNING beat N: target not on screen을 수정하세요.

  10. 보고 전에 확인하세요.

  • 모든 contact-sheet-N.jpg를 여세요: 비트당 하나의 타일, 문장 중간. 빛나는 대상이 문장과 일치하는지, 물체가 손에 있는지, 반지가 빈 손에 있는지, 표정이 일치하는지, 자막 스트립과 겹치거나 프레임을 벗어나는 것이 없는지 확인하세요.
  • 움직임은 일시 정지 중에 발생하므로 몇 가지 물건 건네기나 도착을 확인하세요: ffmpeg -v error -ss <seconds> -i lesson.mp4 -frames:v 1 /tmp/frame.png. 비트 시간은 timing.json에 있습니다.
  • story.py(또는 lesson.json의 target/caption, 렌더링 시 읽힘)를 수정하고 다시 렌더링하세요. words를 변경한 경우 먼저 7단계를 다시 실행하세요.
  • 그런 다음 학습자에게 수업 경로, 새 단어 및 복습한 단어를 알려주세요.

기타 요청

  • "복습 …" / "잊었어요 …": 새 단어가 없거나 하나만 있는 수업을 만들어 지정된 단어나 해당 수업의 단어를 중심으로 구성하세요. 학습자가 단어를 잊어버리고 있다고 말하면 struggling으로 표시하세요.

  • 어휘 또는 진행 상황: 보고서를 실행하고 요약하세요.

  • 음성: 코스의 음성은 course.json에 있습니다: qwen(로컬, 예: 지시어에 slow-teacher가 포함된 Serena) 또는 edge(온라인, 예: 속도 -25%의 zh-CN-XiaoxiaoNeural).

    • Qwen 스피커: <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())". 이 명령은 모델을 로드합니다.
    • edge-tts 음성: uvx edge-tts --list-voices | grep zh-.

    엔진 또는 음성 변경은 voice 블록을 수정하는 것입니다(references/setup.md, 3단계 참조). 변경 시 새로운 캐시 클립 세트가 시작됩니다.

규칙

  • 실행은 해당 수업 폴더, 코스의 audio-cache/와 notes.md, 그리고 <course>/art/<set>/ 아래의 새 파일에만 씁니다.
  • <skill>/scripts, 기존 아트 또는 tts 프로젝트의 변경은 별도 단계로, 학습자가 요청할 때 수행합니다.
  • story.py 없이 가져온 수업은 다시 렌더링되지 않습니다.

관련 스킬