Что мы научимся делать:

1. Введение. Почему пиксели врут, а код — нет
Визуализировать технические концепции — боль. Рисовать схемы в PowerPoint долго, а кодить анимации в Manim вручную — это часы отладки ради 10 секунд видео.
Обычные ИИ-генераторы здесь не помогут. Они создают красивую картинку, но галлюцинируют в деталях: синусоида идет кривыми волнами, оси координат «плывут», а формулы превращаются в инопланетные руны. Диффузионные модели угадывают пиксели, но не знают законов физики.
Инженерам нужна не «атмосфера» науки, а точность. Поэтому мы будем генерировать не видеоряд, а исполняемый код.
Идея проста: разделяем ответственность.
-
LLM (Qwen-CLI): пишет код сцены.
-
Движок (Manim Community): исполняет этот код.
В таком пайплайне нет места галлюцинациям. Если код валиден, то график строится математически точно, а не превращается в руны.
2. Архитектура
Самая большая проблема при генерации кода нейросетями — они не могут запустить его у себя в голове. Модель может написать идеально выглядящий скрипт, перепутав один аргумент в функции, и всё упадет.
Поэтому мы строим не прямую линию (Prompt → Video), а кольцо Self-Correction (самоисправления). Если Manim ругается на ошибку, мы не сдаемся, а скармливаем эту ошибку обратно нейросети.
Выглядит это так:

Как это работает по шагам:
-
Генерация: Мы отправляем промпт (например, «Анимируй синусоиду») в
qwen-cli. -
Парсинг: Вытаскиваем Python-код из ответа и сохраняем во временный файл.
-
Исполнение: Пытаемся запустить рендер через
subprocess. -
Развилка:
-
Успех: Если exit code равен 0, значит, видео готово. Отдаем файл пользователю.
-
Провал: Если скрипт упал, мы перехватываем
stderr(текст ошибки).
-
-
Исправление: Формируем новый запрос: «Ты написал код, но он вызвал ошибку
{Traceback}. Вот твой код. Исправь его». И цикл повторяется.
Почему это надежнее аналогов?
Здесь работает принцип детерминизма. В обычных генераторах видео нейросеть предсказывает цвет следующего пикселя. Она может «забыть», что у треугольника три угла, или начать искривлять прямые линии, потому что «так красивее».
В нашем пайплайне нейросеть генерирует логику.
Если код скомпилировался и выполнился:
-
Прямая будет математически прямой.
-
Уравнение
будет написано именно так, а не иероглифами.
-
Длительность анимации будет ровно такой, как вы указали.
Мы меняем «правдоподобность» на «точность» (которая нужна в инженерии). Галлюцинации отсеиваются на этапе компиляции: код либо работает правильно, либо мы заставляем модель его переписывать, пока он не заработает.
3. Настройка окружения
Мы будем запускать всё на «живой» системе, без контейнеров. Это даст максимальную производительность, но потребует аккуратной подготовки зависимостей.
Шаг 1. Движок рендера (Manim + LaTeX)
Сердце Manim — это визуализация формул. Для этого он использует LaTeX.
Тут правило простое: не пытайтесь экономить место, ставя урезанные версии. Рано или поздно скрипт споткнется об отсутствующий пакет шрифтов или символов.
Поэтому:
-
LaTeX: Ставим полный дистрибутив.
-
Manim: Собственно, сама библиотека.
pip install manim
Шаг 2. Qwen CLI
Для генерации кода мы будем использовать официальный CLI-инструмент qwen-code. Он написан на JS, поэтому нам понадобится Node.js.
-
Убедитесь, что у вас стоит Node.js версии 20 или выше.
-
Установите CLI глобально через npm:
npm install -g @qwen-code/qwen-code -
Проверьте, что всё взлетело:
qwen --version
После установки утилита попросит вас настроить доступ. Вам понадобится API-ключ (обычно настраивается через команду конфигурации или переменную окружения, следуйте инструкции в терминале).
4. Этап 1: Промпт
Существует несколько версий (оригинальная от 3Blue1Brown, версия Community, старые версии), и синтаксис в них отличается. Если просто попросить нейросеть «напиши код на Manim», она может смешать методы 2019 и 2026 года, выдав нерабочий франкенштейн-скрипт.
Наша задача — задать жесткие рамки через System Prompt. Мы должны превратить Qwen в Senior Manim Developer.
Вот правила, которые мы «вшиваем» в каждый запрос:
-
Только код. Никаких объяснений «Вот ваш скрипт…». Нам нужно чистое содержимое файла.
-
Шаблон. Обязательный импорт
from manim import *и структура класса. -
Изоляция. Запрет на использование внешних файлов (
.jpg,.svg). Всё, что есть на экране, должно быть нарисовано кодом (геометрические примитивы, LaTeX). Это гарантирует, что скрипт не упадет с ошибкойFileNotFound.
Системный промпт (System Prompt)
Вот инструкция, которую мы отправляем в Qwen перед любой задачей пользователя:
You are a Python expert specializing in the Manim Community library.
Your task is to write a complete, runnable Python script.
RULES:
1. Output ONLY the Python code. No markdown, no explanations.
2. Start with `from manim import *`.
3. Create a class named `GenScene` inheriting from `Scene`.
4. Use `def construct(self):` method.
5. Use ONLY built-in Manim shapes/text (no external image files).
6. Ensure valid LaTeX syntax for math.
5. Этап 2: Python скрипт
Теперь самое интересное. У нас есть нейросеть (Qwen), которая умеет писать код, и движок (Manim), который умеет его исполнять. Но они не знают о существовании друг друга.
Нам нужен скрипт на Python, который будет управлять этим процессом.
Проблема CLI
Мы используем qwen-cli. Это прекрасный инструмент для человека: он рисует красивые прогресс-бары, подсвечивает синтаксис, поддерживает диалог. Но для автоматизации это кошмар.
Если просто вызвать его через subprocess, произойдет следующее:
-
Процесс зависнет, ожидая ввода пользователя.
-
В выводе (
stdout) будет куча мусора: управляющие символы очистки экрана[2J, коды цветовx1b[31mи анимация спиннера загрузки.
Нам нужно превратить интерактивного чат-бота в послушный генератор кода. Для этого применим технику Headless Wrapper.
Хак №1: Эмуляция ввода
Мы не будем ждать приглашения ко вводу. Мы отправим в stdin процесса сразу всю последовательность действий:
Текст промпта -> Enter -> Команда /quit -> Enter.
Это заставит CLI запуститься, отработать запрос и сразу завершиться.
Хак №2: Regex
Python не сможет скомпилировать код, если в середине строки будет стоять невидимый символ смены цвета консоли. Поэтому весь вывод мы прогоняем через регулярное выражение, вырезающее ANSI-последовательности.
Логика Self-Correction
Это фича нашего скрипта. Мы не просто запускаем код, мы следим за результатом.
-
Генерация: Получаем код от Qwen.
-
Рендер: Запускаем
manim -qm scene.py. -
Проверка: Смотрим на
returncode.-
0— Успех. -
!= 0— Ошибка.
-
-
Исправления: Если ошибка произошла, мы берем текст ошибки (Python Traceback), склеиваем его с неработающим кодом и отправляем обратно в Qwen с припиской: “Fix this error”.
Итоговый код
Вот полный скрипт, который соединяет всё воедино. Он не требует установки тяжелых библиотек для LLM, только стандартные subprocess, re и sys.
Полный код скрипта-оркестратора (main.py) — Нажмите, чтобы развернуть
import subprocess
import sys
import re
import os
# --- КОНФИГУРАЦИЯ ---
MAX_RETRIES = 3 # Количество попыток авто-исправления
OUTPUT_FILE = "scene.py"
SCENE_NAME = "GenScene" # Должно совпадать с именем класса в System Prompt
# System Prompt задает роль и ограничения
SYSTEM_PROMPT = """
You are a Python expert specializing in the Manim Community library.
Your task is to write a complete, runnable Python script.
RULES:
1. Output ONLY the Python code. No markdown, no explanations.
2. Start with `from manim import *`.
3. Create a class named `GenScene` inheriting from `Scene`.
4. Use `def construct(self):` method.
5. Use ONLY built-in Manim shapes/text (no external image files).
6. Ensure valid LaTeX syntax for math.
"""
def strip_ansi(text):
"""
Чистим вывод от цветов и управляющих символов консоли,
которые генерирует интерактивный CLI.
"""
ansi_escape = re.compile(r'x1B(?:[@-Z\-_]|[[0-?]*[ -/]*[@-~])')
return ansi_escape.sub('', text)
def call_qwen(prompt_text):
"""
Вызывает qwen-cli в 'немом' режиме.
"""
full_prompt = f"{SYSTEM_PROMPT}nnUSER REQUEST: {prompt_text}"
# Эмуляция диалога: отправляем промпт и сразу команду выхода
input_payload = f"{full_prompt}n/quitn"
# На Windows npm создает .cmd обертки
command = "qwen.cmd" if sys.platform == "win32" else "qwen"
try:
# shell=False безопаснее, но требует правильных путей в PATH
result = subprocess.run(
[command],
input=input_payload,
capture_output=True,
text=True,
encoding='utf-8', # Node.js обычно отдает UTF-8
check=False
)
# Возвращаем чистый текст без мусора
return strip_ansi(result.stdout)
except FileNotFoundError:
print("❌ Ошибка: Команда 'qwen' не найдена. Установите: npm install -g @qwen-code/qwen-code")
sys.exit(1)
def extract_code(raw_text):
"""Вытаскивает Python-код из ответа модели."""
if not raw_text: return ""
# Ищем блок кода в маркдауне
match = re.search(r'```pythons*(.*?)```', raw_text, re.DOTALL)
if match: return match.group(1)
# Fallback: если модель вернула код без маркдауна
if "from manim import" in raw_text:
start = raw_text.find("from manim import")
return raw_text[start:]
return raw_text
def run_manim():
"""Запускает рендер и возвращает статус успеха + лог."""
print(" ...Рендер видео (Manim)...")
cmd = [sys.executable, "-m", "manim", "-qm", OUTPUT_FILE, SCENE_NAME]
# Скрываем всплывающее окно консоли на Windows
startupinfo = subprocess.STARTUPINFO() if sys.platform == "win32" else None
result = subprocess.run(
cmd, capture_output=True, text=True, encoding='utf-8', startupinfo=startupinfo
)
return (result.returncode == 0, result.stderr)
def main():
if len(sys.argv) < 2:
print(f"Usage: python {sys.argv[0]} "Описание сцены"")
return
user_topic = sys.argv[1]
print(f"🎬 Генерация сцены: '{user_topic}'")
current_prompt = user_topic
for attempt in range(1, MAX_RETRIES + 1):
print(f"n--- Попытка {attempt} / {MAX_RETRIES} ---")
# 1. Запрос к LLM
print(" ...Ждем код от Qwen...")
raw_out = call_qwen(current_prompt)
code = extract_code(raw_out)
if not code.strip():
print("❌ Модель не вернула код. Попробуйте переформулировать запрос.")
break
# Сохраняем промежуточный файл (полезно для отладки)
with open(OUTPUT_FILE, "w", encoding="utf-8") as f:
f.write(code)
# 2. Попытка рендера
success, err = run_manim()
if success:
print(f"✅ УСПЕХ! Видео сгенерировано.")
print(f"Ищите результат в папке media/videos/{OUTPUT_FILE[:-3]}")
break
else:
print(f"❌ Ошибка рендера.")
# Берем последние 5 строк ошибки, чтобы не засорять промпт
clean_err = "n".join(err.splitlines()[-5:])
print(f" Log: {clean_err}")
# 3. Формируем запрос на исправление
current_prompt = (
f"The previous code for '{user_topic}' failed to render.n"
f"Error message:n{clean_err}nn"
f"Failed code:n{code}nn"
f"Fix the error and return ONLY the corrected Python code."
)
if __name__ == "__main__":
main()
Как это выглядит в консоли
PS D:project_worldreact_anim> python .main.py 'сделай анимацию линейной функции'
🎬 Генерация сцены: 'сделай анимацию линейной функции'
--- Попытка 1 / 3 ---
...Ждем код от Qwen...
...Рендер видео (Manim)...
✅ УСПЕХ! Видео сгенерировано.
Ищите результат в папке media/videos/scene

6. Демонстрация
Вернемся к задаче подготовки учебных материалов. Я решил проверить, сможет ли наш «ИИ ассистент» сгенерировать понятные анимации для базовых слайдов, понимая команды на русском языке.
Кейс А: Математика (Графики функций)
Базовая задача для любого школьного курса. Рисовать это вручную в PowerPoint — долго.
Мой промпт:
“Нарисуй оси координат. Построй график синуса синим цветом и график косинуса красным цветом. Подпиши графики.”
Результат:

Что под капотом: Модель с первого раза поняла, что нужно использовать
Axes,plotиget_graph_labelдля подписей. Анимация отрисовки (Create) была добавлена автоматически.
Кейс Б: Геометрия (Трансформация фигур)
Manim славится своими плавными переходами (Transform). Проверим, как модель справляется с морфингом.
Мой промпт:
“Покажи белый квадрат в центре экрана. Затем плавно преврати его в красный круг. Добавь текст ‘Трансформация’ сверху.”
Результат:

Кейс В: Физика (Свободное падение)
Простейшая симуляция гравитации.
Мой промпт:
“Нарисуй мячик, который падает сверху вниз и ударяется о ‘пол’ (линию внизу). Сделай анимацию ускорения при падении.”
Результат:

Кейс С (Сложный уровень): Архитектура ЭВМ (Как работает процессор)
Здесь мы просим нейросеть не просто подвигать геометрические фигуры, а смоделировать логический процесс: цикл выборки и исполнения команды (Fetch-Decode-Execute).
Это сложная сцена: нужно нарисовать блоки (CPU, RAM), соединить их и синхронизировать перемещение «битов» с изменением текста .
Мой промпт:
“Нарисуй схему работы компьютера.
Слева — блок RAM (оперативная память) с ячейками. В одной ячейке написано ‘LOAD 42’.
Справа — блок CPU (процессор) с регистром внутри.Анимация:
Данные ‘LOAD 42’ вылетают из RAM и по стрелке (шине) летят в CPU.
Процессор загорается (обработка).
В регистре процессора появляется число 42.”
Результат:

7. Заключение и точки роста
То, что мы написали выше — это только фундамент. Скрипт занимает всего 139 строк, но он решает фундаментальную проблему генеративного ИИ: галлюцинации в точных данных.
Что можно улучшить (Roadmap):
-
RAG (Retrieval-Augmented Generation). Самая большая проблема — Qwen учился на коде из GitHub, который мог устареть. Manim Community активно обновляется. Идея: Подключить векторную базу данных с актуальной документацией Manim, чтобы модель подглядывала в свежие примеры перед написанием кода.
-
Озвучка (TTS). Видео без звука — это скучно. Идея: Попросить LLM генерировать не только код сцены, но и текст закадрового голоса (
voiceover). Затем прогнать его через Edge-TTS или ElevenLabs и наложить аудиодорожку прямо средствами Manim. -
Многоагентность. Сейчас одна модель делает всё. Идея: Разделить роли. «Архитектор» придумывает сценарий, «Кодер» пишет Python, а «Критик» (Vision-модель) смотрит кадры из видео и говорит: «Текст наезжает на график, исправь координаты».
Итог
Мы собрали «Text-to-Video для инженеров» за один вечер. Без обучения собственных моделей, без дорогих подписок и без GPU-ферм (если использовать qwen-cli).
Анонсы новых статей, полезные материалы, а так же если в процессе у вас возникнут сложности, обсудить их или задать вопрос по этой статье можно в моём Telegram-сообществе. Смело заходите, если что-то пойдет не так, — постараемся разобраться вместе.
Мы заменили вероятность на логику. Вместо того чтобы гадать, как выглядит график , наша нейросеть просто вызывает функцию
Axes.plot(lambda x: x**2). И это работает.
Автор: enamored_poc


