Починка spintax-шаблонов
Починка — это найти дефект → применить один канонический рецепт его класса → перепроверить. Никогда не свободное переписывание. И она соблюдает порядок, которому учила серия: структура, затем переменные, затем синонимы — на уровне синонимов нельзя починить то, что принадлежит уровню переменных.
Почему шаблоны ломаются при рендере, а не в исходнике
validate() судит шаблон. Шаблон может быть безупречен и всё равно выдавать сломанный текст, потому что дефект живёт в комбинации выборов: два соседних слота случайно выбирают одно слово, существительное из одного слота встречает относительное местоимение из другого и они расходятся в роде, пустая ветка оставляет после себя голые скобки. Ничего из этого не видно в исходнике, и ничего из этого не переживает чтение исходника человеком — оно появляется только в части рендеров.
Поэтому починка начинается с отрендеренных вариантов, а не с шаблона. Операция Lint в n8n-ноде существует ровно для этого: она судит рендер, никогда не шаблон, и на реальном пуле она насчитала 2% чистых из 200 рендеров на первом проходе и 100% после того, как починили слоты, на которые она указала. Самая частая находка в том пуле — существительное из одного слота рядом с местоимением из другого: сюжет, в которой попало в 18% статей, прежде чем кто-то заметил.
Практическое следствие: если читать только шаблон, дефект уедет в продакшен. Рендерьте десятки вариантов, прогоняйте их линтом и чините то, на что он указывает.
Золотое правило: чинить по рецепту, а не по наитию
У дефекта есть класс, и у каждого класса — один канонический способ починки. Свободная правка — человеком в спешке или моделью, которой сказали «почини» — ломает грамматику, пока чинит грамматику. Модель, которую попросили починить дополнение, который, охотно подставит синоним другого рода и вернёт расширение, который: местоимение, о котором её спрашивали, по-прежнему неверно, а новая ветка уже выдумана. Получив вместо этого именованный рецепт — зависимое слово внутрь каждой ветки — она делает одно преобразование и останавливается.
Вторая половина правила — порядок. Гайд по грамматике формулирует его для авторства: «Не добавляйте {a|b}, пока не зафиксированы permutations, переменные и форма предложения». Для починки он читается так: структура → переменные → синонимы. Дефект, который идёт от существительного, повторённого в трёх падежах, — проблема уровня переменных; латать синонимы вокруг него значит починить один рендер и оставить сломанным следующий.
Две починки, в этом порядке
- Починка синтаксиса — по диагностике
validate(). У каждой есть строка, колонка и код; модели показывают пронумерованный шаблон и велят менять как можно меньше, оставив всё остальное байт-в-байт. Такой промпт собирает@spintax/authoring-prompt(buildRepairPrompt()), n8n-нода отдаёт его как Build Repair Prompt, а кнопка Fix в Spintax Studio делает тот же раунд. Если этот шаг находит ошибки — он первый: шаблон, который не парсится, нечего рендерить и нечего линтить. - Починка по рендер-линту — по находкам на отрендеренных вариантах. Находка несёт код, проблемный фрагмент и сообщение, но не позицию в шаблоне. Этот гайд — про этот шаг: сопоставить код рецепту, найти ветки, породившие фрагмент, применить рецепт, перерендерить, перелинтить.
Каталог «дефект → рецепт»
Для каждого класса ниже названо, кто его находит — линт, по коду, или вы, читая сэмплы, — и дан один рецепт с «до» и «после». Рендеры «до» — настоящий вывод движка; сообщения линта процитированы в том виде, в каком их печатает линт n8n-ноды. Грамматика за каждым рецептом объяснена один раз, в гайде по грамматически безопасной синонимизации; ссылки ведут в точную секцию.
Артикль или определитель (английский)
Находит: вы, по сэмплам — машинно не проверяется. Симптом: a encrypted connection. Рецепт: втянуть определитель внутрь ветки.
a {secure|encrypted} connection
{a secure|an encrypted} connection
Разобрано в секции об английском согласовании.
Подлежащее и сказуемое, род и число
Находит: вы, по сэмплам. Симптом: the team respond; по-русски сайт удобна, команда доступны. Рецепт: связать подлежащее и сказуемое в одной ветке. Два независимых слота согласовать нельзя никогда; один слот, несущий обе половины, — всегда.
{the team|all members} {responds|respond}
{the team responds|all members respond}
{сайт|платформа} {удобен|удобна}
{сайт удобен|платформа удобна}
Разобрано в секциях о согласовании и «Род и число».
Зависимое слово: относительное местоимение (русский)
Находит: линт, код agreement.relative. Он смотрит не более чем на два слова назад от каждой формы который, угадывает род существительного по окончанию и молчит, когда окончание двусмысленно (путь, тень), вместо того чтобы гадать. Симптом — ровно так, как печатает линт:
"дополнение … который" — neuter noun with a masculine relative pronoun
Рецепт A: зависимое слово внутрь каждой ветки — в той форме, которая нужна этой ветке.
{дополнение|плагин}, который ускоряет сайт
{дополнение, которое|плагин, который} ускоряет сайт
На 40 рендерах линт пометил 18 у первого шаблона и ни одного у второго.
Рецепт B: вынести существительное в #def одного рода, когда оно повторяется. Возьмите синонимы одного рода, бросок делается один раз, и каждое последующее упоминание согласовано по построению:
#def %Ext% = {дополнение|расширение}
%Ext%, которое ускоряет сайт
Тоже 40 из 40 чистых. Условие, которое идёт вместе с рецептом B: он держится только пока существительное стоит в одном падеже. Объявленный отдельно %ExtGen% с %Ext% не коррелирует — два определения это два броска, — поэтому существительному, которому нужно несколько падежей, положена лестница падежных семей: синонимы, склоняющиеся одинаково, с окончанием снаружи определения, или одна основа в #def, на которую ссылается каждая падежная форма.
Предлог + падеж (русский, немецкий)
Находит: вы, по сэмплам — падеж внутри слота линт не судит. Симптом: после первом заказе, für dem Team. Рецепт: предлог уезжает в ветку вместе со своей управляемой формой.
{после|при} первом заказе
{после первого заказа|при первом заказе}
{mit|für} dem Team
{mit dem Team|für das Team}
Когда одно и то же существительное управляется в нескольких местах, альтернатива — падежная переменная, %OrderGen%, %OrderLoc%, объявленная семьёй. Разобрано в секции «Предлог + управляемая форма».
Повторённый корень
Находит: линт, код repeat.word. Он сканирует окно в шесть слов — то есть ловит повтор на расстоянии до пяти слов — и сравнивает основы, так что вопрос / вопросов считаются одним словом. Шесть — число измеренное: при девяти 82% попаданий были обычной связностью текста, при шести каждое попадание было настоящим. Симптом, как печатается:
"сразу" and "сразу" repeat 4 words apart
Рецепт: корень только в одном слоте. Его несёт либо перечисление, либо фиксированный текст, никогда оба; когда повторяется сущность — вынести её в #def и упомянуть один раз.
{Сразу|Немедленно} после установки сайт сразу ускоряется
{Сразу|Немедленно} после установки сайт начинает отвечать быстрее
18 из 40 рендеров помечены до, ни одного после. Английская форма та же — «Коллизия повторяющихся слов» показывает которым нужно получить… могут получить.
Стык, которого линт не видит. Перестановка делает соседями любые два блока, поэтому заголовок и вступление после него могут делить корень через границу блока. Линт намеренно не флагит повторы «заголовок — тело» (именно там жила «обычная связность»), так что стыки держит дисциплина в источнике — разведите словарь на границе — или линт-проход по целому собранному шаблону.
Регистр и однородность слота
Находит: вы, по сэмплам. Симптом: формальная ветка рядом с разговорной или нарративная связка, меняющая регистр посреди абзаца (Следовательно / Ну и вот). Рецепт: один регистр на слот; связки зафиксировать. Убрать чужую ветку, а не переписывать слот. Правила — в «Правилах синонимизации».
Пунктуация, оставшаяся внутри ветки
Находит: линт, код punctuation.empty-pair. Симптом: empty brackets or quotes — необязательная ветка оставила за собой свою обёртку. Рецепт: перенести пунктуацию в ту ветку, которой она нужна.
Works in any browser ({fast|})
Works in any browser{ (fast)|}
18 из 40 рендеров чистых до, 40 из 40 после.
У той же семьи есть ещё три кода — punctuation.double-space, punctuation.space-before и punctuation.duplicated, — и от движка вы их почти не увидите: его пост-процессинг сам нормализует двойной пробел и пробел перед запятой прямо в рендере. Это сеть для текста, собранного вне движка, или для хоста, который рендерит с выключенным пост-процессингом. Рецепт тот же: пунктуация уходит внутрь ветки, которой принадлежит.
Переменные: самая сильная починка
У половины каталога выше есть лекарство на уровне переменных, и оно бьёт любую заплатку на уровне синонимов, потому что убирает саму комбинацию, в которой жил дефект. Метод:
- Выносить до синонимизации. Склоняемая сущность, которая повторяется по тексту, сначала уходит в
#def; только потом проход синонимизации идёт по тексту вокруг неё. В обратном порядке инлайн-замена синонима молча меняет форму, с которой согласовано остальное предложение. В кампании wpps-ru одна статья прошла от 52 чистых рендеров из 200 до 200 из 200 на одном этом шаге:#def %Ext% = {дополнение|расширение}, оба среднего рода, и инлайн-синонимы её больше не трогали. - Называть формы.
%PascalCase%для именительного, суффикс для каждого используемого падежа —%OrderGen%,%OrderDat%,%OrderLoc%— по конвенции гайда о переменных. - Держать семью на одном броске — единственный способ, которым движок согласует формы. Синонимы, склоняющиеся одинаково, с окончанием снаружи определения (
#def %e% = {магазин|сайт|проект}→для %e%а); или одна основа в#def, на которую ссылается каждая падежная форма (#def %s% = {посетител|гост},#def %VisitorsGen% = %s%ей); или вся фраза в одном перечислении. Два независимых определения — два броска, и они не коррелируют никогда — ни на#set, ни на#def. #def, а не#set, для всего, в чём два упоминания должны совпасть.#setперебрасывает при каждой ссылке и годится там, где вариативность нужна;#defбросает один раз за рендер.
Когда не спинить
Некоторые участки не чинят — их вынимают из перечисления целиком. Имена собственные, бренды, цены, URL и юридические формулировки никогда не живут внутри ветки. Как и существительное с длинным хвостом зависимых — прилагательное, причастие и придаточное, все согласованные с ним, — потому что каждой ветке пришлось бы нести весь хвост. Варьируйте текст вокруг них, а их держите фиксированными.
Починка с помощью LLM
Модель чинит хорошо, когда ей дают четыре вещи и отказывают в пятой: шаблон, конкретный дефект с фрагментом, названный рецепт к применению и указание применить только его — и никакой лицензии что-либо улучшать. Запрос никогда не звучит как «почини это»; он звучит как «примени рецепт X к участку, породившему Y». Промпт ниже — форма, которая выдержала реальный материал.
You are repairing a spintax template. Do NOT rewrite freely and do NOT improve the wording.
A lint pass over rendered variants found this defect:
code: <agreement.relative | repeat.word | punctuation.empty-pair | …>
fragment: "<the offending rendered text>"
message: "<the lint message>"
Apply exactly ONE recipe from https://spintax.net/docs/repairing-spintax.md
recipe: <its name, e.g. "bind the governed word into each branch">
to the branch or branches that produced the fragment — and to nothing else.
Rules:
- Keep meaning, structure, variables and every other branch byte-for-byte unchanged.
- Do not add words, clauses, prepositions or relative pronouns the template does not
already have.
- If the recipe cannot be applied without changing meaning, leave the template as it is
and reply: unfixable
- Return only the corrected template — no explanation, no quotes, no code fences.
Две привычки из той же кампании держат модель в рамках. Substitution-only разметка, когда модель размечает готовый текст: ей можно заменять слова и короткие обороты и нельзя вводить союзы, придаточные, предлоги и относительные местоимения, которых не было в источнике — выдуманный который и выдуманный предлог были источником большинства дефектов согласования и падежа, и правило их убрало. И линт-проход на каждом размеченном абзаце, потому что даже substitution-only сносит: один абзац вернулся как {Совпадает|Совпадает ли} — careful, no. {Совпадает ли|…} — английская «мысль вслух» протекла в русскую ветку плюс дубль корня. Петля это поймала; человек бы не заметил.
Ещё два указателя для модели: девять ошибок, которые модели делают в spintax, и фрагмент грамматического аудита в гайде по грамматике — авторская половина этого промпта.
Автоматизация починки
Для пайплайна форма, которая довела три long-form шаблона до линт-чистых 200 из 200, выглядит так, на один абзац:
draft = markup(concrete_text) # substitution-only
repeat up to K:
errors = validate(draft)
if errors: draft = repair(draft, errors); continue # синтаксис: модель чинит спаны
if any(lint(render(draft, seed_i)) for i in 1..N):
draft = markup(concrete_text); continue # грязно: заново, не «свободно чинить»
return draft # чисто
fail loud
Читайте асимметрию: ошибку синтаксиса модель чинит по точным спанам; находка линта либо отправляет абзац на перегенерацию, либо — в полной версии петли — чинится применением именованного рецепта из каталога к участку. Петля никогда не позволяет модели чинить линт-дефект свободно. При N = 8 рендерах на абзац и K = 3 попытках второй шаблон кампании прошёл от 95 чистых из 200 до 200 из 200.
В n8n та же петля — четыре ноды: Validate направляет элемент в Valid или Invalid, Build Repair Prompt превращает выход Invalid в промпт-починку для вашей LLM-ноды (ограничьте цикл, двух попыток достаточно), Render Many набирает выборку, а Lint направляет её в Clean или Defective — выборка чиста, только когда чист каждый вытянутый документ. Те же правила работают и как обычная функция над любым текстом — пайплайн кампании гонял собственную их копию, ту самую, из которой портирован линт ноды.
Три вещи, которые нужно знать до подключения:
- Провенанс строите вы. Находка линта несёт отрендеренный фрагмент, а не позицию в шаблоне. Чтобы применить рецепт, нужно найти ветку, породившую фрагмент — искать слова фрагмента по шаблону, ветка за веткой, — и именно этот кусок обвязки в итоге пишет каждый инструмент починки.
- Выборка вероятностна. Восемь рендеров на абзац ловят частые комбинации; редкая всплывает на объёме. Финальные ворота по собранному шаблону — 200 рендеров, каждый через линт — остаются сетью, и операция выборки у линта сделана ровно под это (50 рендеров по умолчанию, до 500).
- Стыки остаются за человеком. Поабзацная петля не видит повтор через границу блоков; их держит проход по целому шаблону или дисциплина источника, как выше.
Шпаргалка
| Дефект | Находит | Рецепт | Разобрано |
|---|---|---|---|
| Артикль / определитель (EN) | вы, по сэмплам | втянуть определитель внутрь ветки | согласование |
| Подлежащее и сказуемое, род и число | вы, по сэмплам | связать подлежащее и сказуемое в одной ветке | согласование, род и число |
| Относительное местоимение не согласовано (RU) | линт agreement.relative | зависимое слово внутрь каждой ветки; или вынести существительное в #def одного рода | падежные семьи |
| Предлог + падеж (RU, DE) | вы, по сэмплам | предлог уезжает в ветку вместе с формой; или падежная переменная | предлог + падеж |
| Глагольное управление, квантификаторы (RU) | вы, по сэмплам | связать глагол и дополнение / квантификатор и существительное в одной ветке | управление, квантификаторы |
| Повторённый корень | линт repeat.word | корень только в одном слоте; сущность — в #def и одно упоминание | коллизия |
| Регистр / однородность слота | вы, по сэмплам | один регистр на слот; связки зафиксировать | правила |
| Пустая пара, лишний пробел, сдвоенный знак | линт punctuation.empty-pair, punctuation.double-space, punctuation.space-before, punctuation.duplicated | перенести пунктуацию в ту ветку, которой она принадлежит | эта страница |
Одна строка на память: найти, назвать рецепт, применить, перепроверить. Если рецепта для того, что вы видите, нет — дефект в структуре или в переменных, и чинится он в серии, которая идёт до этого гайда.