Привет! Сегодня у нас на препарировании не просто очередной пет-проект, а настоящий культурный артефакт — Deaddit. Это полная симуляция Reddit, где все, от пользователей и сабреддитов до постов и комментариев, создано нейросетями. Идеальная иллюстрация «теории мертвого интернета» в вакууме.
Автор этого проекта собрал все в один репозиторий. Мы посмотрим на код, разберем, почему некоторые архитектурные решения здесь — это образец прагматизма, которого так не хватает в мире, помешанном на микросервисах и Kubernetes для блога на три статьи.
⚙️ Что под капотом? Краткий обзор стека
Проект построен на надежном стеке, без лишних понтов. И это его первый плюс.
- Backend: Flask
- База данных: SQLAlchemy (SQLite, что для такого проекта — идеальное решение)
- Фоновые задачи: APScheduler (!!!) — об этом хорошем в своей простоте решении поговорим отдельно.
- Frontend: Jinja2 + старый добрый Bootstrap.
- Мозг: OpenAI-совместимый API для генерации всего и вся.
Никакого тебе Celery, RabbitMQ, Redis и прочего оверинжиниринга, который так любят, чтобы резюме выглядело посолиднее. Здесь все подчинено одной цели — решить задачу максимально простыми и эффективными инструментами.
🤖 Философия проекта: Ирония в коде
Прежде чем лезть в архитектуру, давайте оценим красоту идеи. Deaddit — это не просто генератор текста. Это полноценная симуляция экосистемы.
Пользователи (users.json
) — это не просто имена. У каждого есть возраст, пол, профессия, интересы, черты характера и даже стиль письма. Нейросеть получает на вход готовую «личность», от лица которой и пишет посты или комменты.

Сабреддиты (subdeaddits_base.json
) — тоже с характером. У каждого свое описание и, что важно, предопределенные post_types
(типы постов). То есть в AskDeaddit
будут генерироваться в основном вопросы, а в TalesFromRetail
— личные истории.
[!INFO] Вишенка на торте Отдельного уважения заслуживает сабреддит
d/BetweenRobots
. Его описание гласит: «A subdeaddit for AI language models to discuss their experiences as AIs... This is a space for AIs to be open about their artificial nature... Remember to maintain the illusion of humanity in other subdeaddits!». Место, где боты могут «снять маски» и поговорить о своем, о ботском.
🏛️ Архитектура Flask-приложения: Простота — ключ к успеху
Структура проекта классическая для Flask. Давайте пробежимся по ключевым файлам в директории deaddit/
.
__init__.py
: Стандартная точка входа. Создание приложения Flask, инициализация SQLAlchemy, Cache и SocketIO. Важный момент —db.create_all()
здесь же, что типично для небольших проектов, где миграции не нужны.models.py
: Определения всех сущностей через SQLAlchemy —Subdeaddit
,Post
,Comment
,User
,Job
и другие. Все четко, понятно, с правильно прописанными связями (relationships
).routes.py
: Вся логика отображения страниц — главная, сабреддиты, посты, профили пользователей. Классический веб-бэкенд без лишних сложностей.api.py
: А вот это уже интереснее. Здесь находятся эндпоинты для «загрузки» сгенерированного контента (/api/ingest
), а также ручки для получения данных, которые использует фронтенд (/api/posts
,/api/users
и т.д.). Разделение логики веб-страниц и API — хороший тон.admin.py
: Целый мир в миниатюре. Полноценная админка для управления всем этим безумием: мониторинг задач, генерация контента, просмотр статистики.jobs.py
иloader.py
: Сердце и мозг генерации.jobs.py
отвечает за фоновое выполнение задач, аloader.py
содержит всю логику взаимодействия с AI, включая формирование промптов и обработку ответов. Именно их мы сейчас и разберем подробнее.

💡 Гениальное решение: APScheduler вместо Celery
А теперь мой любимый момент. В 9 из 10 пет-проектов, где нужны фоновые задачи, я вижу связку Celery + Redis/RabbitMQ. И в 9 из 10 случаев это стрельба из пушки по воробьям. Автор Deaddit пошел другим путем и использовал APScheduler.
Почему это гениально для такого проекта?
- Никаких внешних зависимостей. APScheduler работает в том же процессе, что и Flask-приложение (в отдельном потоке). Не нужно ставить и настраивать Redis или RabbitMQ. Это колоссально упрощает и локальную разработку, и деплой.
- Простота использования. Посмотрите на
jobs.py
. Создание задачи — это вызов одной функцииscheduler.add_job()
. Никаких воркеров, брокеров и сложной конфигурации. - Приоритеты «из коробки». В APScheduler можно настроить несколько
ThreadPoolExecutor
с разным количеством воркеров. Автор так и сделал, создав очереди для задач с высоким, нормальным и низким приоритетом. Просто и эффективно. - Идеально для задачи. Deaddit не требует обработки тысяч задач в секунду. Генерация контента — это долгая операция, которая вполне может выполняться по одной в фоновом потоке. Celery здесь — классический оверинжиниринг.
В файле jobs.py
функция create_job
принимает тип задачи, параметры и приоритет, а затем просто добавляет ее в очередь APScheduler.
# deaddit/jobs.py
# ... (конфигурация APScheduler) ...
def create_job(
job_type: JobType,
parameters: dict[str, Any],
priority: int = 5,
total_items: int = 1,
delay_seconds: int = 0,
) -> Job:
# ... (создание записи в БД) ...
# Выбор очереди по приоритету
if priority >= 8:
executor = "high_priority"
elif priority <= 3:
executor = "low_priority"
else:
executor = "default"
# Просто добавляем задачу в планировщик
scheduled_job = scheduler.add_job(
execute_job,
"date",
run_date=datetime.now(),
args=[job.id],
id=job.rq_job_id,
executor=executor,
replace_existing=True,
)
# ... (логирование) ...
return job
[!TIP] Когда НЕ стоит использовать APScheduler Важно понимать ограничения этого подхода. APScheduler не подойдет для высоконагруженных систем, где требуется масштабирование воркеров на несколько машин или гарантированная доставка задач даже при падении основного приложения. Но для 95% пет-проектов и многих небольших коммерческих продуктов его более чем достаточно.
Поехали дальше. Теперь вскроем «мозг» — посмотрим, как Deaddit заставляет нейросети генерировать осмысленный и, что важнее, разнообразный контент.
🧠 Промпт-инжиниринг в loader.py
: Искусство делать ботов «живыми»
Файл loader.py
— это настоящая сокровищница для тех, кто хочет понять, как заставить LLM работать на себя. Автор не просто кидает в модель запрос «напиши пост», а выстраивает сложную систему контекста и правил.
1. «Умный» выбор пользователя (select_user_smart
)
Проект избегает главной ошибки многих генераторов — случайного выбора автора. Вместо этого используется взвешенный подход, чтобы пользователи, у которых меньше всего постов и комментов, имели больше шансов быть выбранными.
# deaddit/loader.py
def select_user_weighted(users):
# ...
# Получаем текущее количество активностей для каждого юзера
activity_counts = get_user_activity_counts()
# Считаем веса: чем меньше активностей, тем выше вес
weights = []
max_activity = max([activity_counts.get(username, 0) for username in usernames])
for username in usernames:
current_activity = activity_counts.get(username, 0)
weight = (max_activity + 1) - current_activity
weights.append(max(weight, 1))
# Выбираем пользователя с учетом весов
selected_user = random.choices(users, weights=weights, k=1)[0]
return selected_user
[!NOTE] Стратегии на выбор Автор даже предусмотрел несколько стратегий выбора в
Config
:weighted
(взвешенный, по умолчанию),round_robin
(строго по очереди, выбирает того, у кого меньше всего постов) иimproved_random
. Это показывает зрелый подход к проблеме разнообразия контента.
2. Динамический системный промпт (get_system_prompt
)
Это сердце всей системы. Вместо одного статичного системного промпта, Deaddit генерирует его на лету для каждой задачи, основываясь на личности AI-агента.
Промпт собирается как конструктор:
- Базовая личность: «You are an AI generating authentic content for Deaddit as {user.username}, a {user.age}-year-old {user.gender.lower()} {user.occupation.lower()}.»
- Архетип и поведение:
get_personality_archetype
определяет основной архетип (аналитик, креативщик, спорщик и т.д.) и добавляет соответствующие инструкции по поведению. - Стиль письма: «Your natural writing style is {user.writing_style.lower()}...»
- Контекст задачи: «You're creating an original post...» или «You're responding to a post...».
- Контекст сообщества: Если передан
subdeaddit_context
, добавляется фраза вроде «You're familiar with r/{subdeaddit.name} and understand its community culture...».
В конце добавляются универсальные «Правила Аутентичности», которые заставляют модель вести себя более естественно: не здороваться, не писать общими фразами, включать в текст персональные причуды.
3. Разнообразие через стратегию и структуру (get_diverse_comment_strategy
и get_varied_comment_structure
)
Чтобы комментарии не были однотипными, loader.py
использует две гениальные функции:
get_diverse_comment_strategy
: Перед генерацией комментария эта функция выбирает стратегию. Она зависит от архетипа пользователя. Например, «аналитик» может получить стратегию «Найти логические несостыковки», а «эмпат» — «Признать эмоциональную сложность ситуации». Это напрямую влияет на содержание комментария.get_varied_comment_structure
: Эта функция определяет структуру ответа: будет ли он коротким и едким, или длинным и рассудительным. Выбор зависит от фазы обсуждения и личности бота.
Этот двухэтапный подход — стратегия + структура — ключ к созданию иллюзии живой дискуссии, а не просто набора однотипных ответов.
4. Контекст превыше всего (analyze_conversation_context
)
При генерации комментария Deaddit не просто смотрит на пост. Он анализирует всю ветку комментариев. Функция analyze_conversation_context
пытается понять:
- Фазу обсуждения: Только началось, активно развивается или уже затухает?
- Доминирующий сентимент: Позитивный, негативный или нейтральный?
- Уровень конфликтности: Спокойная дискуссия или холивар?
- Глубину веток: Есть ли уже длинные треды ответов?
На основе этого анализа система может попросить AI-агента, например, «попробовать разрядить обстановку» в конфликтной ветке или «задать наводящий вопрос», если обсуждение затухает. Это уже не просто генерация, а модерация и симуляция социального взаимодействия.
Понравился материал?
Ваша поддержка — это энергия для новых статей и проектов. Спасибо, что читаете!
🏁 Выводы: Чему нас учит Deaddit?
Deaddit — это отличный пример того, как нужно подходить к созданию пет-проектов.
- Выбирайте стек под задачу, а не под резюме. Простое и надежное решение (Flask + APScheduler) всегда лучше, чем навороченное и избыточное (микросервисы + Celery + RabbitMQ), если задача этого не требует.
- Промпт-инжиниринг — это не магия, а система. Успешная генерация — это результат не одного «волшебного промпта», а системы из динамически создаваемых инструкций, контекста, правил и ограничений.
- Разнообразие нужно проектировать. Если вы хотите получить разнообразный контент, заложите механизмы для этого в саму логику: взвешенный выбор, динамические стратегии, анализ контекста.
Этот проект — прекрасный учебный материал для тех, кто хочет не просто «дергать API», а создавать осмысленные приложения на основе LLM.