# Romance Novel Pipeline

> Конвейер художественного романа (dark/mafia/forbidden romance): библия персонажей, посценный синопсис, валидация глав против канона, чистка ИИ-стиля, иллюстрации. Триггеры: «напиши роман», «мафия-романс», «книга с иллюстрациями». НЕ нон-фикшн→book-post.

- Skill: `jhamidun/romance-novel-pipeline` (Agent Skill)
- Install (CLI): `npx skillmds@latest add jhamidun/romance-novel-pipeline`
- Raw SKILL.md: https://api.skillmd.com/api/skills/jhamidun/romance-novel-pipeline/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: JHamidun (https://skillmd.com/u/jhamidun)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/jhamidun/romance-novel-pipeline

---


# Romance Novel Pipeline — конвейер жанрового романа

Методология длинной художественной формы (150-200k знаков) с иллюстрациями.
Решает три болезни ИИ-прозы на дистанции: **персонаж плывёт** (глаза меняли цвет
трижды), **стиль скатывается в рубленые фразы и тире**, **факты мира противоречат сами себе**.

Лечится не «пиши лучше», а конструкцией: сначала канон (Bible), потом посценный план,
и только потом текст — каждая глава проверяется против канона автоматически.

## Что понадобится

| Нужно | Платно? | Где взять |
|---|---|---|
| Модель для текста | по подписке | она у тебя уже есть |
| Иллюстрации | **да, по расходу** | навык `image-generation` (Gemini Image по `GOOGLE_API_KEY` — ключ бесплатно в Google AI Studio, генерация тарифицируется за картинку). 25-30 иллюстраций на книгу — считай бюджет заранее |
| Сборка `.docx` | нет | `pip install python-docx`; либо PDF через навык `pdf`, либо конвертация навыком `file-converter` |

Отдельной команды-запускалки у навыка нет: опиши задачу словами
(«собери библию персонажей для мафия-романса про X»), фазы идут по порядку ниже.

## Когда использовать

- Создание полноценного романа (150-200k знаков)
- Dark Romance / Mafia Romance / Forbidden Romance и смежные жанры
- Книги с иллюстрациями

Нон-фикшн — не сюда: там источники важнее сюжета, см. `book-post`.

## 5-фазный пайплайн

### ФАЗА 1: BIBLE (канон)

Создать файл `{book_name}_bible.md`. Это единственный источник истины: всё, чего в нём нет,
в тексте появляться не должно.

```markdown
# BIBLE: {Название книги}

## ПЕРСОНАЖИ

### Главная героиня
- **Имя:** полное имя (как называют друзья, как называет герой)
- **Возраст:** точный
- **Внешность:** цвет волос, глаз, рост, телосложение, особые приметы
- **Характер:** 3-5 ключевых черт
- **Бэкграунд:** семья, образование, работа, травмы
- **Цель:** чего хочет
- **Страх:** чего боится
- **Секрет:** что скрывает

### Главный герой
[Аналогично]

### Антагонист
[Аналогично]

### Второстепенные персонажи
[Краткое описание каждого]

## ОТНОШЕНИЯ
- Героиня → Герой: как относится, как меняется
- Герой → Героиня: как относится, как меняется
- Герой ↔ Антагонист: история конфликта

## ЛОКАЦИИ
- Основная локация: детальное описание
- Дополнительные локации

## ТАЙМЛАЙН
- Когда происходит действие
- Сколько длится история
- Ключевые даты

## ФАКТЫ И ПРАВИЛА МИРА
- Криминальная структура
- Правила организации
- Важные детали мира

## ТЕРМИНОЛОГИЯ
- Как герои называют друг друга
- Специфические термины
```

### ФАЗА 2: SYNOPSIS (посценный план)

Создать файл `{book_name}_synopsis.md`:

```markdown
# SYNOPSIS: {Название книги}

## СТРУКТУРА: 18 глав (~9000 знаков каждая)

### АКТ 1: Завязка (главы 1-6)
**Цель акта:** знакомство, первый конфликт, искра между героями

#### Глава 1: {Название}
- **POV:** от кого
- **Локация:** где
- **Время:** когда
- **События:**
  1. Конкретное событие 1
  2. Конкретное событие 2
  3. ...
- **Эмоциональная дуга:** от какого состояния к какому
- **Крючок в конце:** чем заканчивается

[Аналогично для каждой главы]

### АКТ 2: Развитие (главы 7-12)
**Цель акта:** сближение, раскрытие секретов, нарастание угрозы

### АКТ 3: Кульминация (главы 13-18)
**Цель акта:** кризис, разлука, воссоединение, HEA
```

### ФАЗА 3: WRITING + VALIDATION

**Правила написания:**

1. **Длина главы:** 8000-10000 знаков
2. **POV:** первое лицо (героиня), одна глава от героя (15 или 16)
3. **Темп:** чередовать action/reflection
4. **Диалоги:** 30-40% текста

**СТОП-СЛОВА (избегать):**

```
- "—" (тире) в начале абзацев → заменить на нормальные предложения
- Короткие рубленые фразы подряд → объединять в сложные
- "Он/Она" в начале каждого предложения → варьировать
- Клише: "сердце замерло", "дыхание перехватило" → 1 раз на главу максимум
- Повторение слов в соседних предложениях
```

**Валидация после каждой главы.** Скрипт даёт только механическую часть (стиль и упоминания);
смысловые противоречия с каноном по-прежнему ловит читающий агент — но пусть он ловит их
на пяти сомнительных местах, а не на всей главе.

```python
# validate_chapter.py — stdlib only, запускать после каждой главы
import re

# Короткое = 6 слов и меньше. Порог подобран под русскую прозу: ниже него
# фраза почти всегда «рубленая», выше — уже нормальное простое предложение.
SHORT_WORDS = 6
CLICHES = ["сердце замерло", "дыхание перехватило", "по спине пробежал холодок",
           "воздух стал густым", "время остановилось"]


def split_sentences(text: str) -> list[str]:
    """Грубая нарезка на предложения. Точность здесь не нужна — нужен стабильный счётчик."""
    parts = re.split(r'(?<=[.!?…])\s+', text.replace('\n', ' '))
    return [p.strip() for p in parts if p.strip()]


def count_sentences(text: str) -> int:
    return len(split_sentences(text))


def count_short_sentences(text: str, max_words: int = SHORT_WORDS) -> int:
    return sum(1 for s in split_sentences(text) if len(s.split()) <= max_words)


def unknown_proper_nouns(text: str, bible_text: str) -> list[str]:
    """Слова с заглавной НЕ в начале предложения — почти всегда имена и топонимы.
    Два фильтра, оба обязательны, оба проверены на живом тексте:
      - первое слово предложения пропускаем, иначе в отчёт попадёт каждое «Она» и «Нет»;
      - слово после тире тоже пропускаем — в диалоге тире открывает реплику, то есть
        это опять начало фразы. Без второго фильтра в романе с 40% диалогов шум
        возвращается почти полностью."""
    found = set()
    for s in split_sentences(text):
        words = s.split()
        prev = None
        for i, raw in enumerate(words):
            w = raw.strip('«»""'(),.!?:;—-')
            starts_phrase = (i == 0) or (prev in ('—', '–', '-'))
            if not starts_phrase and re.fullmatch(r'[А-ЯЁ][а-яё]{2,}', w) and w not in bible_text:
                found.add(w)
            prev = raw
    return sorted(found)


def validate_chapter(chapter_text: str, bible_text: str, characters: list[str]) -> list[str]:
    errors = []

    # 1. Имена: персонаж, которого нет в Bible, — это выдуманный на ходу персонаж
    mentioned = {n for n in characters if n in chapter_text}
    if not mentioned:
        errors.append("В главе не упомянут ни один персонаж из Bible — проверь имена")
    unknown = unknown_proper_nouns(chapter_text, bible_text)
    if unknown:
        errors.append("Имена/топонимы вне Bible: " + ", ".join(unknown)
                      + " (либо внеси в канон, либо убери)")

    # 2. Стиль: тире в начале абзацев
    dash_starts = sum(1 for ln in chapter_text.split('\n') if ln.lstrip().startswith('—'))
    if dash_starts > 10:
        errors.append(f"Слишком много абзацев, начатых с тире: {dash_starts}")

    # 3. Стиль: доля рубленых фраз
    total = count_sentences(chapter_text)
    short = count_short_sentences(chapter_text)
    if total and short / total > 0.30:
        errors.append(f"Слишком много коротких предложений: {short}/{total} ({short / total:.0%})")

    # 4. Клише
    for c in CLICHES:
        n = chapter_text.lower().count(c)
        if n > 1:
            errors.append(f"Клише «{c}» повторено {n} раз (норма — 1 на главу)")

    # 5. Объём
    if not 8000 <= len(chapter_text) <= 10000:
        errors.append(f"Объём главы {len(chapter_text)} знаков вне коридора 8000-10000")

    return errors


if __name__ == "__main__":
    import sys
    chapter = open(sys.argv[1], encoding="utf-8").read()
    bible = open(sys.argv[2], encoding="utf-8").read()
    names = sys.argv[3:]           # имена персонажей из Bible
    problems = validate_chapter(chapter, bible, names)
    print("OK" if not problems else "\n".join("- " + p for p in problems))
```

Дубль по именам собственным даст ложные срабатывания на топонимах и марках — это осознанно:
дешевле один раз внести «Милан» в Bible, чем на 14-й главе обнаружить город, которого
в романе не было.

### ФАЗА 4: STYLISTIC POLISH

**Автоматические исправления:**

1. **Тире → предложения**
   - "— Да. — Нет." → "Да, — ответила она. — Нет."

2. **Рубленые фразы → сложные**
   - "Он встал. Подошёл к окну. Закурил." → "Он встал и, подойдя к окну, закурил."

3. **Повторы → синонимы**
   - Использовать тезаурус для замены повторяющихся слов

4. **Ритм:**
   - Короткие предложения для напряжения
   - Длинные для описаний и размышлений
   - Чередование!

Для общей чистки от ИИ-почерка есть отдельные навыки: `de-ai-ify` (клише и штампы) и
`ru-text` (канцелярит и типографика). Художественный текст ими правь выборочно —
они настроены на нон-фикшн и могут срезать намеренную стилизацию.

### ФАЗА 5: FORMATTING + ILLUSTRATIONS

**Документ:**

- Обложка
- Титульная страница
- Аннотация
- Содержание (опционально)
- Главы с иллюстрациями
- «КОНЕЦ»

Сборка: `python-docx` для `.docx`, навык `pdf` — для PDF, навык `file-converter` —
если исходник в markdown и нужен другой формат.

**Иллюстрации (25-30 штук)** — через навык `image-generation`:

- Обложка: герои вместе, атмосфера жанра
- По 1-2 на главу: ключевые сцены
- Консистентность персонажей держится на том, что описание берётся **дословно из Bible**,
  одним и тем же блоком текста в каждом промпте. Перефразировал — получил другого человека.

**Промпт-шаблон:**

```
Generate image: [описание сцены].
[ГЕРОИНЯ_DESC]: [детальное описание из Bible — копировать дословно]
[ГЕРОЙ_DESC]: [детальное описание из Bible — копировать дословно]
Style: cinematic photorealistic, dramatic lighting, movie poster quality,
8K, ultra detailed, professional photography, dark moody atmosphere,
romantic thriller aesthetic
```

## Структура файлов

Корень выбираешь сам — например `./novels/` в рабочем каталоге или `~/novels/`.

```
novels/
└── {book_name}/
    ├── {book_name}_bible.md
    ├── {book_name}_synopsis.md
    ├── {book_name}_part1.txt
    ├── {book_name}_part2.txt
    ├── {book_name}_part3.txt
    ├── {book_name}_full.txt
    ├── images/
    │   ├── 00_cover.jpg
    │   ├── 01_scene.jpg
    │   └── ...
    └── {book_name}_final.docx
```

## Чеклист качества

- [ ] Bible заполнена полностью
- [ ] Synopsis содержит все главы с деталями
- [ ] Каждая глава 8000-10000 знаков
- [ ] `validate_chapter.py` по каждой главе отдал OK
- [ ] Стилистическая полировка (тире < 10 на главу)
- [ ] Общий объём 150000-180000 знаков
- [ ] Иллюстрации сгенерированы и встроены
- [ ] Документ собран

## Примеры жанровых особенностей

### Mafia Romance

- Герой: глава клана/синдиката
- Конфликт: враждующие семьи или внутреннее предательство
- Danger + Possession

### Forbidden Romance

- Запретные отношения (разница в статусе/возрасте/положении)
- Guilt + Desire
- Секретность

### Bodyguard Romance

- Она защищает его (или наоборот)
- Профессионализм vs Чувства
- Proximity + Danger

