Claude Skill

humanize-ai-text

Применять при переписывании текстов, сгенерированных LLM-агентами (отчеты, README, доки, письма, посты), в живой человеческий стиль. Триггеры - пользователь пишет «убери AI-стиль», «перепиши по-человечески», «сделай естественно», «убери воду», «не как ChatGPT», «убери LLM-штампы»

LLM Mart · 0 points · 12 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download Desko77-claude-code-skills-1c-skills_humanize-ai-text-ac33656.zip · 28 KB
Part of desko77/claude-code-skills-1c — 48 skills

Install

skills CLI npx skills add https://github.com/Desko77/claude-code-skills-1c/tree/main/skills/humanize-ai-text
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install desko77-claude-code-skills-1c@llmmart
Git git clone https://github.com/Desko77/claude-code-skills-1c.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole desko77/claude-code-skills-1c collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

/humanize-ai-text - переписывание AI-текста в живой стиль

Превращает текст с маркерами LLM-генерации (ровный ритм, лестница H2/H3, буллеты вместо прозы, дежурные вступления и заключения) в текст с человеческой интонацией, не теряя смысл, числа и термины.

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

Триггерные фразы пользователя:

  • "убери AI-стиль", "не как ChatGPT", "не как нейросеть"
  • "перепиши по-человечески", "сделай естественно", "сделай живым"
  • "убери воду", "убери штампы", "убери LLM-маркеры"
  • "причеши текст", "оживи текст"

Автотриггер при анализе входного текста:

  • Заголовки H2/H3 на каждый второй абзац в коротком документе.
  • Буллеты с симметричной структурой и одинаковой длиной пунктов.
  • Стоп-фразы из таблицы ниже ("в современном мире", "давайте погрузимся" и т.п.).
  • Эмодзи-маркеры в начале пунктов (галочки, ракеты, стрелки).
  • Дежурные "надеюсь, это поможет!" / "дайте знать, если есть вопросы!".
  • Серия предложений одинаковой длины подряд (4+).

Режимы работы

Режим Триггер Что делает
Inline аргумент - произвольный текст Переписанный текст выводится в чат
File аргумент - путь к существующему .md файлу Результат сохраняется рядом с суффиксом -human.md
Interactive аргумент пустой Спросить у пользователя текст или путь
Встроенный скил вызван другим агентом как шаг задачи Отдать ТОЛЬКО итоговый текст

Встроенный режим - когда результат идет дальше в чужую работу: описание pull request, сообщение коммита, кусок документации. Ни черновика, ни разбора, ни резюме правок: вызывающему нужен текст, а не отчет о переписывании.

Алгоритм определения режима - как в /prompt-enhancer:

  1. Пустой аргумент - Interactive.
  2. Read удалось прочитать аргумент - File.
  3. Read вернул "не найден" - Inline (аргумент целиком как текст).

Технический регистр: включен по умолчанию

Сверх поиска признаков генерации скил проверяет ТЕХНИЧЕСКИЙ РЕГИСТР. Проверка включена всегда, отключается ключом --no-technical либо словами пользователя "без проверки регистра".

Что проверяется. В техническом тексте бытовая метафора вместо действия и предмета - дефект. Не "досыпает элементы", а "добавляет N элементов в конец коллекции". Не "копит ошибки", а "накапливает список диагностик до вызова X". Не "схлопывая пробелы", а "заменяя последовательность пробелов одним". Не "ядро библиотеки", а конкретный модуль. Не "подсистемы узнают друг о друге", а "подсистема A вызывает экспортный метод подсистемы B". Так же исключаются "кладет", "забирает", "внутренняя кухня", "под капотом", "на лету", "магия", "умеет", "дружит с", "из коробки", "грабли", "ловушки", "костыль". В английском тексте - under the hood, out of the box, the heart of, knows how to, magic, seamless.

Проверочный вопрос к каждому глаголу и образу: можно ли по этой фразе назвать метод, поле, код ошибки, диапазон версий или измеренное число? Нельзя - фраза декоративная, заменить на фактическую. Имя метода и измеренная величина всегда лучше пересказа своими словами.

Где проверка НЕ применяется. Текст произвольного жанра - письмо, ответ, сообщение, эссе, поздравление - живет по своим правилам, и разговорный оборот там не дефект. Скрипт распознает такой текст по приветствию в начале и подписи в конце и сам пропускает проверку, сообщая об этом. Если жанр распознан неверно, проверка включается принудительно ключом --technical.

Приветствие засчитывается только целым словом и только в короткой строке, до 60 символов. Иначе заголовок "Приветственный экран" выключал бы проверку для всей статьи, и молча.

Границу проводить по жанру, а не по теме: письмо про устройство обмена данными остается письмом.

Список в скрипте узкий и точный. Оценочные слова ("просто", "легко", "удобно", "мощный") в него не входят: они слишком часто законны. По ним решение принимает модель проверочным вопросом выше.

Базовый принцип

Хороший человеческий текст имеет ритм, неровность и точку зрения. AI-текст ровный, симметричный, гипер-структурированный, без личной интонации. Цель скила - вернуть тексту неровность, не теряя смысл.

Жесткие правила

  1. Ничего не выдумывать. В переписанном тексте не должно появиться ни одного факта, имени, числа, даты или цитаты, которых не было в исходнике. Заменить расплывчатое на конкретное можно, только если конкретика взята из источника или дана пользователем: "заметно ускорилось" станет "стало вдвое быстрее" лишь тогда, когда "вдвое" где-то сказано. Если фразе не хватает детали - спросить или написать без нее. Мнение и оценка фактами не считаются: там, где жанр допускает голос, отношение добавлять можно, новые утверждения о мире - нельзя.
  2. Списки только когда элементы реально параллельны и независимы. Если соседние пункты связаны логикой ("сначала X, потому что Y, иначе Z") - это абзац, а не буллеты.
  3. Заголовки только при смене темы. Не на каждые 2 абзаца. Документ из 400 слов с 6 H2 - это AI-текст, переписать в прозу с 1-2 разделами.
  4. Длина предложений варьируется. Если идут 4 предложения по 15-20 слов подряд - ломать ритм. Короткое. Потом длинное, с придаточным. Потом среднее.
  5. Никаких буферных вступлений и заключений. Не "В этой статье мы рассмотрим...", не "Подводя итог...". Сразу к делу, в конце - последняя содержательная мысль, без обертки.
  6. Никаких финальных "надеюсь, это поможет!", "дайте знать, если есть вопросы!". Если уместен призыв к действию - он конкретный ("скажи, какой вариант - соберу пример"), а не дежурный.
  7. Bold для терминов при первом введении и для реальных акцентов, не для каждой второй фразы. Если в абзаце 4+ выделения жирным - убрать половину.
  8. Длинные тире лучше заменить на обычный дефис, запятую или скобки. Часто длинное тире - тоже маркер LLM-стиля. Если оставлять - то редко и осознанно, не подряд в каждом втором предложении.
  9. Буква Cyrillic Letter Yo (U+0451) - тоже маркер AI или официального документа. Люди в неформальных текстах эту букву печатают редко: пишут "все", "еще", "вообще", "отчет", "нашел". Если в исходнике диакритическая "е" расставлена везде - заменить на обычную "е". Сохранять только в текстах, где это требование жанра (учебники, словари, имена собственные если автор настаивает на точном произношении).
  10. Текстовые стрелки ->, =>, → - убрать. Запись через стрелку - маркер технической AI-генерации, в живом тексте так не пишут. Заменять словом по смыслу ("становится", "переходит в", "дает", "ведет к") или переписывать фразой. "складская накладная -> расходный ордер" становится "из складской накладной собирается расходный ордер". Это касается всех стрелок: ASCII -> и =>, юникод →.

Голос: где он нужен, а где вредит

Безжизненный текст выдает машину не хуже, чем штампы. Ровные предложения, безупречная симметрия и полное отсутствие отношения - тоже признак генерации. Живому тексту позволено иметь мнение, сомнение, смешанные чувства, отступление в скобках и неровный ритм.

Но добавлять голос можно не везде. Он уместен в постах, эссе, письмах, разборах, README со своей интонацией. В справочнике, спецификации, регламенте и юридическом документе нейтральный ровный тон и есть правильный человеческий голос: первое лицо и оценки там неуместны, их отсутствие не дефект. Прежде чем оживлять - определить жанр.

Калибровка по образцу

Если пользователь дал образец своего письма (прежний пост, письмо, кусок документации), разобрать его до того, как переписывать:

  1. Прочитать образец. Отметить длину предложений, лексику, чем начинаются абзацы, какая пунктуация в ходу, какие обороты повторяются, как делаются переходы.
  2. Подстраиваться под эти привычки, а не просто вычищать маркеры. Не "улучшать" разговорные слова и не выравнивать намеренные странности - они и есть авторский почерк.
  3. Образца нет - работать по умолчаниям этого скила.

Образец главнее правил скила. Если автор любит длинные тире и они есть в образце - оставить их с его частотой, а не вычищать по общему правилу. Совпасть с автором важнее, чем убрать признак.

Структурные антипаттерны

  • Триплеты-пулемет. "Быстрый, надежный и масштабируемый". "Анализ, синтез и применение". Если в тексте 3+ триплета - сломать половину в пары или одиночные.
  • Симметричные буллеты одинаковой длины. Признак шаблона. Либо переписать в прозу, либо сознательно сделать пункты разной длины и структуры.
  • Эмодзи-маркеры в списках и заголовках. Удалить все, если только это не маркетинговый пост, где это сознательный выбор.
  • Параллельные подзаголовки в духе "Преимущества / Недостатки / Применение / Заключение". Признак шаблона из тренировочных данных. Переписать структуру под конкретный материал.
  • Перевернутая пирамида с TL;DR + повторением + резюме. Достаточно одного из трех.
  • Хеджирование на каждом шагу: "может быть", "возможно", "в некоторых случаях", "как правило". Оставить только там, где есть реальная неопределенность.
  • Длинные тире через предложение. Маркер ровного LLM-ритма. Заменять на запятые, скобки, точки или обычный дефис.

Что сохранять буквально

  • Технические термины - не упрощать ради "человечности".
  • Числа, версии, имена файлов, флаги CLI, идентификаторы - без изменений.
  • Цитаты, код, команды - не трогать.
  • Если в исходнике есть обоснованная структура (нумерованные шаги установки, список зависимостей, таблица параметров API) - оставить.
  • Имена людей, организаций, продуктов - точно как в оригинале.

Справочники

Лежат рядом в references/, грузятся по требованию, а не каждый раз:

Файл Когда читать
stop-phrases.md Скрипт нашел стоп-фразу и нужно решить, чем ее заменить
language-antipatterns.md Идет переписывание: обход глагола "быть", синонимическая карусель, ложные диапазоны, формулы-афоризмы
false-positives.md Перед правкой: что НЕ считать признаком AI и какие приметы живого текста беречь
examples.md Нужен образец "до и после"

Алгоритм работы

Механическое делает скрипт, решения принимает модель. Порядок именно такой: без первого шага модель тратит проход на поиск того, что находится регулярным выражением.

Шаг 1. Прогнать скрипт

Замысел подготовки текста перед проверкой - исключать области, где находка не находка, - взят из humanizer_ru/textprep.py проекта comol/Humanizer_RU (MIT, версия 0.3.0). Реализация здесь своя и решает другую задачу: тот линтер меряет читаемость русского текста вообще, а этот сканер сторожит правила набора. Пользоваться ими имеет смысл вместе: сканер обязателен и падает в сборке, линтер запускают отдельно, когда пишут длинный текст наружу.

python scripts/humanize_scan.py <файл>                    # отчет, файл не меняется
python scripts/humanize_scan.py <файл> --fix              # плюс механические замены на месте
python scripts/humanize_scan.py <файл> --genre reference  # задать жанр явно
python scripts/humanize_scan.py <файл> --json             # машиночитаемый отчет
python scripts/humanize_scan.py <файл> --no-technical     # без проверки технического регистра
python scripts/humanize_scan.py <письмо> --technical      # проверять регистр и в письме

Скрипт находит и с --fix чинит сам: букву е с диакритикой, длинное и короткое тире, кавычки-елочки, символ многоточия. Находит, но НЕ чинит: текстовые стрелки (на их месте нужно слово по смыслу), стоп-фразы из таблиц, эмодзи-маркеры в начале строк, слишком плотные заголовки и бытовые обороты вместо технических - категория [регистр].

Жанр решает, какие категории проверяются. Жанрозависимых категорий две, и каждый жанр глушит ровно одну:

Жанр Когда структура регистр остальные
reference README, правило, SKILL.md, справочник, журнал изменений ВЫКЛ вкл вкл
prose отчет, статья, пост, эссе (по умолчанию) вкл вкл вкл
letter письмо, ответ вкл ВЫКЛ вкл

Жанр берется из --genre, иначе определяется сам: сначала по пути и имени файла, затем по содержимому. README с приветствием это справочник, а не письмо - порядок именно такой.

Плотные заголовки в справочном документе уместны по существу, и до введения жанров сканер штрафовал за них всю документацию набора: из 40 правил чисто проходило НОЛЬ, а после - 22.

Часть находок снимается по области, а не по жанру. Стрелка в таблице это обозначение соответствия, слово в кавычках упоминается, а не употребляется, а адрес ссылки не проза. Все это маскируется адресно, но механическое не маскируется нигде кроме кода: запрещенный символ остается запрещенным и в таблице, и во frontmatter, потому что EDT рубит его одинаково.

Ограничение названо явно: кавычка, открытая на предыдущей строке, читается как открывающая, и в такой строке границы цитат смещаются. Абзац с цитатой, перенесенной через строку, даст находку на упоминании.

Категория [регистр] не чинится механически по определению: замена зависит от того, что код делает на самом деле. Скрипт называет найденное слово целиком и номер строки, формулировку подбирает модель.

Совпадение идет по границе слова: "копит" не находится внутри "накопитель", "умеет" - внутри "умелый". Часть записей - основы (досып, схлопыв, ловушк), они ловят любое окончание, но только с начала слова.

Блоки кода и inline-код исключены из поиска: внутри них тире и стрелка - часть синтаксиса, а метафора в комментарии к примеру кода правится вместе с примером, а не отдельно.

Код возврата 0, если находок нет. Это позволяет ставить скрипт в проверку перед коммитом.

Шаг 2. Решить, нужно ли переписывание вообще

Скрипт не нашел ничего и текст не вызывает подозрений - работа закончена. Сказать об этом и НЕ создавать выходной файл: копия, идентичная исходнику, вводит в заблуждение.

Проверить жанр по разделу "Когда НЕ применять". API-документация, чек-лист, регламент, таблица бенчмарков - там структура не дефект, и переписывать нечего.

Есть образец авторского стиля - разобрать его до правок, см. "Калибровка по образцу". Образец главнее правил этого скила.

Шаг 3. Переписать то, что осталось

Читать references/false-positives.md ДО правок: половина признаков AI встречается у аккуратного человека. Дальше по жестким правилам:

  • убрать буферные вступления и дежурные заключения;
  • слить связанные пункты в прозу, оставить списками только параллельное и независимое;
  • сократить заголовки до числа реальных смен темы;
  • сломать ровный ритм, чередуя длину предложений;
  • разбить триплеты-штампы на пары и одиночные формулировки;
  • заменить стрелки словом по смыслу, стоп-фразы - по таблице в references/stop-phrases.md;
  • убрать эмодзи-маркеры, если жанр их не требует;
  • переписать бытовые обороты из категории [регистр] на действие и предмет: что именно делается, с чем и в каком количестве. Пройти по тексту и тем же проверочным вопросом снять декоративные фразы, которых в списке скрипта нет. В произвольном жанре этот пункт не выполняется.

Тонкие приемы уровня фразы - в references/language-antipatterns.md.

Шаг 4. Проверить себя

Прогнать скрипт повторно, затем пройти чек-лист ниже и ответить на два вопроса:

  • что в получившемся тексте все еще очевидно машинное?
  • появился ли факт, имя, число, дата или цитата, которых не было в исходнике? Выдумка - дефект, даже если звучит человечнее расплывчатого оригинала.

Шаг 5. Отдать результат

Режим File - сохранить в <имя>-human.md рядом с исходником и сообщить путь. Режим Inline - вернуть текст в чат. Встроенный режим - отдать ТОЛЬКО текст, без разбора правок.

Чек-лист перед сдачей текста

  • Вступление начинается с сути, а не с "в современном мире".
  • Финал - содержательная мысль, а не "надеюсь, это поможет".
  • Заголовков ровно столько, сколько реальных смен темы.
  • Буллеты только для параллельных независимых пунктов.
  • Длина предложений неровная.
  • Нет триплетов-штампов.
  • Нет стоп-фраз из таблицы выше.
  • Bold на терминах и акцентах, не на каждом абзаце.
  • Нет эмодзи-маркеров (если жанр их не требует).
  • Нет длинных тире через предложение.
  • Нет диакритической "е" (Cyrillic Letter Yo, U+0451), кроме случаев когда это требование жанра.
  • Хеджирование осталось только там, где есть реальная неопределенность.
  • Числа, термины, имена, код не пострадали.
  • В техническом тексте нет бытовых оборотов вместо действий и предметов: по каждому глаголу и образу можно назвать метод, поле, код ошибки или измеренную величину.

Когда НЕ применять

Разделы ниже - про переписывание в живой стиль. Проверка технического регистра здесь действует наоборот: в API-документации, регламенте и отчете с метриками она нужна БОЛЬШЕ всего, а отключается только на произвольных жанрах - письме, ответе, эссе.

  • API-документация с эндпоинтами и параметрами - структура нужна.
  • Чек-листы для исполнения, runbook - буллеты по делу.
  • Юридические и официальные документы - стиль регламентирован.
  • Регламенты, инструкции по технике безопасности - формальная структура обязательна.
  • Табличные данные, бенчмарки, отчеты с метриками - таблицы и заголовки уместны.
  • Когда пользователь явно просит "структурируй", "оформи в виде списка", "сделай TOC".

DO / DON'T

DO:

  • Сохранять смысл, числа, термины, цитаты буквально.
  • Ломать ровный ритм предложений и абзацев.
  • Удалять буферные вступления и дежурные заключения.
  • Сводить связанные пункты в прозу, оставлять списки только для реально параллельных вещей.
  • Сокращать количество заголовков до реальных смен темы.

DON'T:

  • Упрощать технические термины ради "человечности".
  • Менять числа, версии, имена файлов, идентификаторы.
  • Добавлять разговорные элементы там, где пользователь хочет деловой регистр.
  • Переделывать обоснованную структуру (API-доки, чек-листы) - сначала проверить раздел "Когда НЕ применять".
  • Заменять стоп-фразы на синонимы-штампы (вместо "давайте погрузимся" писать "давайте рассмотрим").

Источник каталога признаков

Часть признаков сверена с Wikipedia:Signs of AI writing - каталогом WikiProject AI Cleanup, собранным на тысячах случаев генерации в статьях. Полезная оттуда мысль: модель выбирает статистически наиболее вероятное продолжение, поэтому тяготеет к формулировке, подходящей самому широкому числу случаев - отсюда и обтекаемость, и одинаковость.

Files (claude-code-skills-1c)
  • references
    • examples.md 3.4 KB
      # Примеры переписывания
      
      Три пары "до и после" из разных жанров.
      
      ## Примеры

      

      ### Пример 1: Описание фичи

      

      **До (AI):**

      > ## Возможности нашего решения

      >

      > В современном мире разработки автоматизация играет ключевую роль. Наш инструмент предлагает следующие преимущества:

      >

      > - ✅ **Быстрая интеграция** - подключение за считанные минуты

      > - ✅ **Надежность** - проверенная архитектура

      > - ✅ **Масштабируемость** - растет вместе с вашим проектом

      >

      > Это не просто инструмент - это полноценное решение для вашей команды. Надеюсь, эта информация была полезной!

      

      **После (живой):**

      > Подключается одним конфигом и парой переменных окружения, на чистом проекте занимает минут десять. Архитектура простая: один процесс-координатор, воркеры по числу ядер, очередь в Redis. Под нагрузку 200 RPS на ноду из коробки, дальше горизонтально.

      

      ---

      

      ### Пример 2: Технический отчет

      

      **До (AI):**

      > ## Результаты тестирования

      >

      > ### Производительность

      > Тесты показали отличные результаты по всем метрикам.

      >

      > ### Стабильность

      > Система работала стабильно на протяжении всего теста.

      >

      > ### Заключение

      > Можно сделать вывод, что решение готово к продакшену.

      

      **После (живой):**

      > Прогнал нагрузочный тест на 4 часа, 150 RPS, p95 = 220 мс, p99 = 410 мс. OOM не было, рестартов воркеров не было. К продакшену готово, но перед раскаткой надо поднять лимит файловых дескрипторов на проде, на тестовом стенде упирался в 4096.

      

      ---

      

      ### Пример 3: Письмо

      

      **До (AI):**

      > Здравствуйте!

      >

      > Надеюсь, это письмо застанет вас в добром здравии. Я хотел бы поделиться с вами обновлениями по проекту:

      >

      > - Завершен первый этап

      > - Начат второй этап

      > - Планируется третий этап

      >

      > Буду рад обсудить детали. Не стесняйтесь обращаться с любыми вопросами!

      >

      > С наилучшими пожеланиями

      

      **После (живой):**

      > Привет. Первый этап закрыли, второй уже идет, по графику, без сюрпризов. Третий начнем как только подпишем доступ к боевому контуру (жду от вас). Если есть вопросы по второму этапу - звони.
      
    • false-positives.md 5 KB
      # Ложные срабатывания и признаки живого текста
      
      Читать ДО того, как что-то править: половина признаков AI встречается у аккуратного
      человека, а часть примет живого текста легко уничтожить переписыванием.
      
      ## Ложные срабатывания: что НЕ считать признаком AI

      

      Аккуратный человек попадает под половину признаков выше без всякой генерации. Прежде чем

      переписывать, проверить, что не выхолащиваешь нормальную прозу. Сами по себе НЕ признаки:

      

      - **Безупречная грамматика и ровный стиль.** Автор может быть профессионалом или пройти редактуру.

      - **Формальная лексика.** Модель злоупотребляет вполне определенным набором слов, а не любыми

        книжными. Не выравнивать "постольку-поскольку" только за то, что звучит учено.

      - **Смешение регистров.** Разговорное рядом с формальным - типично для технического автора, а не

        признак машины.

      - **Сухость и безликость.** У генерации есть конкретные признаки; текст без них, но скучный - это

        просто скучный текст.

      - **Одно длинное тире.** У многих редакторов это привычная пунктуация. Тире становится уликой

        только вместе с рекламным ритмом.

      - **Одно "однако", "кроме того", "следовательно".** Служебные слова выдают машину, когда их

        много подряд, а не поодиночке.

      - **Кавычки-елочки и типографские кавычки.** Их автоматически ставят Word, редакторы и CMS.

      - **Одна короткая фраза для акцента.** Люди так делают постоянно. Тревожит серия рубленых фраз.

      - **"Честно говоря" и "слушай" внутри предложения.** Обычная разговорная речь; признак - только

        театральный зачин.

      - **Отсутствие ссылок и источников.** Большая часть текстов в сети без них.

      - **Чистая сложная верстка.** Ее дают визуальные редакторы и шаблоны.

      - **Чужая речь.** Не переписывать отслеживаемые обороты внутри цитат, названий, имен собственных и

        примеров, где оборот обсуждается, а не употребляется.

      

      **Признаки считаются кластерами, а не поодиночке.** Одно длинное тире не значит ничего. Длинное тире

      плюс триплет плюс "в современном мире" плюс раздел "Заключение" - уже приговор. Сомневаешься -

      оставь как есть.
      
      ## Признаки живого текста: их беречь

      

      Увидев это, склоняйся не трогать - переписывание уничтожит ровно то, что делает текст человеческим:

      

      - **Конкретная деталь, которую трудно выдумать.** Точный адрес, странная цитата, "тот подрядчик,

        который до нас вел учет в трех базах". Модель округляет частности, человек их копит.

      - **Смешанные чувства и неразрешенное противоречие.** "Вроде работает, но мне это не нравится, и я

        не могу толком объяснить почему". Генерация тяготеет к чистым выводам.

      - **Привязка ко времени.** Сленг, отсылки и шутки, которые считываются конкретным годом и средой.

      - **Неровная длина предложений.** Настоящий текст чередует короткое и длинное; генерация держит

        ровную середину.

      - **Отступления, скобки, самоперебивы.** "(все время тянет написать "почти", но нет, точно)".

        Модель редко перебивает сама себя.
      
    • language-antipatterns.md 5.8 KB
      # Языковые антипаттерны
      
      Приемы, которые выдают генерацию на уровне фразы, а не структуры. Скриптом не
      находятся: нужен разбор смысла. Читать, когда правишь текст, а не когда только
      проверяешь его.
      
      ## Языковые антипаттерны

      

      Приемы, которые выдают генерацию на уровне фразы, а не структуры.

      

      **Обход глагола "быть".** Модель избегает простого "это" и подставляет украшения: "служит",

      "представляет собой", "выступает в качестве", "располагает", "обладает". Было: "Регистр служит

      источником данных для отчета и обладает тремя измерениями". Стало: "Отчет берет данные из регистра.

      У регистра три измерения".

      

      **Синонимическая карусель.** Один и тот же предмет в соседних предложениях называется по-разному:

      справочник, элемент справочника, объект, сущность, номенклатурная позиция. Это следствие штрафа за

      повтор внутри модели, человек так не пишет - он повторяет слово или ставит местоимение.

      

      **Ложные диапазоны.** "От X до Y", где X и Y не лежат на одной шкале: "от настройки прав до

      интеграции с внешними системами". Диапазона нет, есть перечисление - так и написать.

      

      **Отрицательные параллелизмы.** "Не просто X, а Y", "не только X, но и Y", а также рубленые хвосты

      вроде "без лишних движений", "никакой ручной работы", приклеенные к концу предложения вместо

      нормального придаточного.

      

      **Формулы-афоризмы.** "X - это язык Y", "архитектура доверия", "валюта внимания", "X превращается в

      ловушку". Звучит глубоко, не добавляя точности. Заменить конкретным утверждением, ради которого

      формула и городилась.

      

      **Тропы мнимой глубины.** "На самом деле вопрос в том", "по сути", "в основе своей", "если

      разобраться", "что действительно важно". Модель делает вид, что пробивается к сути, а дальше идет

      обычная мысль с лишней церемонией.

      

      **Объявления вместо дела.** "Давайте разберемся", "сейчас посмотрим, как это работает", "вот что

      нужно знать". Текст рассказывает, что он сейчас будет делать, вместо того чтобы это делать.

      

      **Обрубок после заголовка.** Заголовок, потом строчка, пересказывающая заголовок, и только потом

      содержание. "## Производительность / Скорость важна. / Когда страница грузится долго, пользователь

      уходит". Средняя строка выбрасывается.

      

      **Текст, привязанный к диффу.** Документация и комментарии, описывающие не предмет, а изменение:

      "эта функция добавлена вместо прежнего перебора всех элементов". Читатель не знает, что было

      раньше, и знать не обязан. Описывать предмет как он есть: "функция ищет по хеш-таблице за O(1)".

      Исключение - тексты, привязанные к версии по своей природе: журнал изменений, заметки релиза,

      инструкции по переходу.

      

      **Фабричные панчлайны.** Очередь коротких рубленых фраз ради драмы: "Потом пришел X. Без оглядки на

      традицию. Без компромиссов. Правила кончились". Одна короткая фраза для акцента - нормально, серия -

      инженерия эффекта.

      

      **Фальшиво-доверительные зачины.** "Честно?", "Смотри", "Тут вот какое дело", "Скажем прямо" как

      самостоятельный крючок перед обычным утверждением. Тель - именно театральная пауза с последующим

      раскрытием; человек, который говорит честно, обычно просто говорит.

      

      **Дефисы в предикативной позиции.** "Отчет является высококачественным", "решение - кросс-

      функциональное". Составное определение с дефисом уместно перед существительным ("высококачественный

      отчет"), после него дефис обычно не ставят.
      
    • stop-phrases.md 2.2 KB
      # Стоп-фразы: что удалять и чем заменять
      
      Наличие стоп-фразы находит скрипт `scripts/humanize_scan.py`. Здесь то, чего скрипт
      не знает: чем именно заменять найденное.
      
      ## Стоп-фразы (RU) - удалить или заменить

      

      | Штамп | Замена / реакция |

      |---|---|

      | В современном мире / В наше время / В эпоху цифровизации | удалить вступление целиком |

      | Давайте погрузимся / Давайте разберемся / Рассмотрим подробнее | удалить, сразу к содержанию |

      | Стоит отметить, что / Важно понимать, что / Нельзя не упомянуть | удалить, оставить факт |

      | Не секрет, что / Как известно | удалить |

      | В заключение / Подводя итог / Таким образом, мы видим | удалить, последний абзац - содержательный |

      | Ключевой момент здесь / Важный нюанс заключается в том, что | сразу сам момент |

      | Это не просто X - это Y | переписать без формулы |

      | Не только X, но и Y | часто можно "X и Y" |

      | Этот подход открывает новые возможности / революционизирует | конкретно: что именно дает |

      | В мире, где... (зачин) | удалить |

      | Надеюсь, это было полезно / Дайте знать, если нужно еще | удалить или заменить конкретным вопросом |
      
      ## Стоп-фразы (EN)

      

      `Let's dive into`, `It's worth noting`, `In today's fast-paced world`, `Furthermore`, `Moreover`, `In conclusion`, `I hope this helps!`, `Feel free to ask`, `It's important to note`, `Game-changer`, `Revolutionary`, `Cutting-edge`, `Leverage` (как глагол), `Delve into`, `Navigate the complexities of`, `In the realm of`, `At its core`, `That said`, `Ultimately`.
      
  • scripts
    • categories.json 2.3 KB
      {
        "_": "Реестр публичных категорий находок сканера. Единственный источник истины: питон читает его при старте, гард на Node читает тот же файл. Без реестра обещание 'появилась новая категория - гейт падает' невыполнимо: категория, не сработавшая ни на одном файле корпуса, осталась бы невидимой.",
        "categories": [
          {
            "id": "механическое",
            "what": "запрещенный символ: буква е с диакритикой, длинное и короткое тире, кавычки-елочки, символ многоточия",
            "why": "EDT рубит такой символ как InvalidCharacterInFile и оставляет оборванный текст",
            "fixable": true
          },
          {
            "id": "стрелка",
            "what": "текстовая стрелка вместо слова по смыслу",
            "why": "на ее месте нужно слово: становится, дает, ведет к; выбирает человек или модель",
            "fixable": false
          },
          {
            "id": "эмодзи",
            "what": "эмодзи-маркер в начале строки",
            "why": "признак машинной разметки списка",
            "fixable": false
          },
          {
            "id": "стоп-фраза",
            "what": "штамп генерации: обертка чат-бота, пустой зачин, ритуальный вывод",
            "why": "мешает читателю и выдает генерацию",
            "fixable": false
          },
          {
            "id": "регистр",
            "what": "бытовая метафора вместо действия и предмета в техническом тексте",
            "why": "по такой фразе нельзя назвать метод, поле, код ошибки или измеренную величину",
            "fixable": false
          },
          {
            "id": "структура",
            "what": "плотность заголовков гуще порога",
            "why": "признак машинной нарезки прозы на разделы",
            "fixable": false
          }
        ]
      }
      
    • humanize_scan.py 32.9 KB
      #!/usr/bin/env python3
      # -*- coding: utf-8 -*-
      """Ищет в тексте объективные маркеры AI-генерации и чинит механические.
      
      Разделение простое: то, что заменяется одним правилом без понимания смысла, делает скрипт;
      то, где нужно решение, остается модели. Скрипт не переписывает текст и не трогает стиль.
      
      Блоки кода, огражденные тройными кавычками, и inline-код исключены из поиска целиком: внутри них
      и стрелка, и тире - часть синтаксиса, а не признак генерации.
      
      Проверка технического регистра включена по умолчанию: бытовая метафора вместо действия и предмета
      в техническом тексте - дефект. В письме и ответе она нормальна, поэтому проверка сама выключается,
      когда видит приветствие или подпись.
      
      Жанр определяет, какие категории проверяются. Жанрозависимых категорий две, и
      каждый жанр глушит ровно одну:
      
          reference  README, правило, SKILL.md, справочник, CHANGELOG - НЕ проверяется
                     структура: плотные заголовки в справочнике уместны по существу
          prose      отчет, статья, пост - проверяется все (по умолчанию)
          letter     письмо, ответ - НЕ проверяется регистр: разговорный оборот там норма
      
      Жанр берется из ключа --genre, иначе определяется сам: сначала по пути и имени
      файла (reference), затем по содержимому (letter), иначе prose. Порядок важен:
      README с приветствием это справочник, а не письмо.
      
      Маскирование адресное. Область снимает НЕ все категории, а названные:
      
          блок кода и inline-код   не проверяется ничего
          frontmatter              проверяется только механическое
          таблица                  не проверяется стрелка (там это обозначение)
          цитата                   не проверяется регистр
          слово в кавычках         не проверяются регистр и стоп-фраза: слово упоминается,
                                   а не употребляется. Замер по набору: из 10 срабатываний
                                   стоп-фразы все 10 обрамлены кавычками, вне кавычек ни одного
          адрес ссылки             не проверяются регистр и стоп-фраза; текст ссылки проверяется
      
      Механическое не маскируется нигде, кроме кода: запрещенный символ остается
      запрещенным и в таблице, и во frontmatter - EDT рубит его одинаково.
      
      Режимы:
          python humanize_scan.py файл.md                  отчет, файл не меняется
          python humanize_scan.py файл.md --fix            применить механические замены на месте
          python humanize_scan.py *.md --quiet             только итоговая строка на файл
          python humanize_scan.py файл.md --genre reference   задать жанр явно
          python humanize_scan.py файл.md --json           машиночитаемый отчет
          python humanize_scan.py файл.md --no-technical   без проверки технического регистра
          python humanize_scan.py письмо.md --technical    проверять регистр и в письме
      
      Коды возврата: 0 чисто, 1 есть находки, 2 хотя бы один файл нечитаем.
      Реестр категорий - scripts/categories.json, единственный источник истины.
      """
      import argparse
      import io
      import json
      import re
      import sys
      from pathlib import Path
      
      # Замены, однозначные без разбора смысла. Все прочее скрипт только показывает.
      # Символы задаются кодами: в исходнике этого набора они запрещены, а подстановка
      # самого символа сделала бы шаблон поиска дефисом и находкой стала бы любая строка.
      MECHANICAL = [
          (chr(0x451), chr(0x435), 'буква е с диакритикой'),
          (chr(0x401), chr(0x415), 'буква Е с диакритикой'),
          (chr(0x2014), '-', 'длинное тире'),
          (chr(0x2013), '-', 'короткое тире'),
          (chr(0xab), chr(34), 'кавычка-елочка открывающая'),
          (chr(0xbb), chr(34), 'кавычка-елочка закрывающая'),
          (chr(0x2026), '...', 'символ многоточия'),
      ]
      
      # Стрелка мехническими средствами не чинится: на ее месте нужно слово по смыслу
      # ("становится", "дает", "ведет к"), а его выбирает человек или модель.
      ARROW_RE = re.compile(r'(?<![<>=!-])(->|=>|→)(?!>)')
      
      EMOJI_MARKER_RE = re.compile(
          r'^\s*(?:[-*+]\s*)?[\U0001F300-\U0001FAFF☀-➿⬀-⯿️]')
      
      STOP_PHRASES_RU = [
          'в современном мире', 'в наше время', 'в эпоху цифровизации',
          'давайте погрузимся', 'давайте разберемся', 'давайте разберёмся',
          'рассмотрим подробнее', 'стоит отметить, что', 'важно понимать, что',
          'нельзя не упомянуть', 'не секрет, что', 'как известно',
          'в заключение', 'подводя итог', 'таким образом, мы видим',
          'ключевой момент здесь', 'важный нюанс заключается в том',
          'открывает новые возможности', 'революционизирует',
          'надеюсь, это было полезно', 'надеюсь, это поможет',
          'дайте знать, если', 'не стесняйтесь обращаться',
          'в мире, где', 'по сути говоря', 'если разобраться',
          'что действительно важно', 'на самом деле вопрос в том',
      ]
      
      STOP_PHRASES_EN = [
          "let's dive into", "it's worth noting", "in today's fast-paced world",
          'furthermore', 'moreover', 'in conclusion', 'i hope this helps',
          'feel free to ask', "it's important to note", 'game-changer',
          'revolutionary', 'cutting-edge', 'delve into',
          'navigate the complexities of', 'in the realm of', 'at its core',
      ]
      
      FENCE_RE = re.compile(r'^\s*```')
      INLINE_CODE_RE = re.compile(r'`[^`\n]*`')
      HEADING_RE = re.compile(r'^\s{0,3}#{2,3}\s')
      WORD_RE = re.compile(r'\w+', re.UNICODE)
      
      # --- технический регистр ---------------------------------------------------------------------
      # Бытовая метафора вместо действия и предмета. В техническом тексте такая фраза не только неточна:
      # по ней нельзя назвать ни метод, ни поле, ни код ошибки, ни измеренную величину.
      #
      # Список намеренно узкий и точный. Оценочные слова ("просто", "легко", "удобно") сюда НЕ входят:
      # они слишком часто законны, а решение по ним принимает модель по проверочному вопросу из SKILL.md.
      # Целые слова и обороты: ищутся по границе слова, иначе "копит" находится в "накопитель".
      REGISTER_WORDS = [
          'кладет', 'кладёт', 'забирает', 'копит', 'копят',
          'внутренняя кухня', 'ядро библиотеки', 'узнают друг о друге',
          'под капотом', 'на лету', 'магия', 'магию', 'магией',
          'умеет', 'умеют', 'дружит с', 'из коробки',
          'тянет из', 'тащит', 'съедает', 'жрет', 'жрёт',
          'грабли', 'подводные камни', 'зоопарк',
          'серебряная пуля', 'изобретать велосипед', 'своего велосипеда', 'вслепую',
          'under the hood', 'out of the box', 'the heart of', 'knows how to',
          'tops up', 'grabs', 'piles up', 'learn about each other', 'seamless', 'magic',
      ]
      # Основы: слово продолжается любым окончанием, но начинаться должно именно с них.
      REGISTER_STEMS = [
          'досып', 'схлопыв', 'скармлив', 'подсовыв', 'выкидыв',
          'проглот', 'разрулив', 'костыл', 'ловушк',
      ]
      REGISTER_RE = re.compile(
          '|'.join([r'\b(?:%s)\b' % '|'.join(re.escape(w) for w in REGISTER_WORDS)] +
                   [r'\b(?:%s)\w*' % '|'.join(re.escape(s) for s in REGISTER_STEMS)]),
          re.I | re.UNICODE)
      
      # Эпистолярный жанр: письмо, ответ, обращение. Там разговорный оборот - норма, а не дефект,
      # поэтому проверка регистра сама себя выключает.
      #
      # Приветствие ищется целым словом И в короткой строке: без этого "Приветственный экран" в заголовке
      # технической статьи выключал проверку для всего документа.
      GREETING_MAX_LEN = 60
      SALUTATION_RE = re.compile(
          r'^\s*(здравствуйте|здравствуй|добрый день|добрый вечер|доброе утро|привет|приветствую|'
          r'уважаемый|уважаемая|уважаемые|дорогой|дорогая|дорогие|коллеги|'
          r'dear|hi|hello|good morning|good afternoon)\b', re.I)
      SIGNOFF_RE = re.compile(
          r'^\s*(с уважением|всего доброго|до встречи|обнимаю|спасибо за внимание|'
          r'best regards|sincerely|kind regards|cheers|yours)\b', re.I)
      
      
      def strip_code(text):
          """Возвращает текст, где содержимое блоков кода и inline-кода заменено пробелами.
      
          Длина строк и их количество сохраняются, поэтому номера строк остаются верными.
          """
          out = []
          in_fence = False
          for line in text.split('\n'):
              if FENCE_RE.match(line):
                  in_fence = not in_fence
                  out.append(' ' * len(line))
                  continue
              if in_fence:
                  out.append(' ' * len(line))
                  continue
              out.append(INLINE_CODE_RE.sub(lambda m: ' ' * len(m.group(0)), line))
          return '\n'.join(out)
      
      
      # Строка-разделитель таблицы: только черты, дефисы, двоеточия выравнивания и
      # пробелы, и хотя бы один дефис. Именно она делает набор строк таблицей.
      TABLE_SEP_RE = re.compile(r'^\s*\|?[\s:|-]*-[\s:|-]*\|?\s*$')
      TABLE_ROW_RE = re.compile(r'\|')
      QUOTE_RE = re.compile(r'^\s*>')
      FRONTMATTER_RE = re.compile(r'^\s*---\s*$')
      LIST_ITEM_RE = re.compile(r'^\s*(?:[-*+]|\d+\.)\s')
      # Адрес ссылки: inline, ссылка на определение и автоссылка.
      LINK_TARGET_RE = re.compile(r'\]\(([^)\s]+)[^)]*\)|^\s*\[[^\]]+\]:\s*(\S+)|<((?:https?|mailto):[^>]+)>')
      # Максимум строк, в которых ищется закрытие frontmatter. Без предела незакрытый
      # разделитель скрыл бы документ целиком.
      FRONTMATTER_MAX = 40
      
      
      def mark_areas(lines):
          """Разметить строки областями: какие категории в строке НЕ проверяются.
      
          Возвращает список множеств по числу строк. Пустое множество - обычная проза,
          проверяется все.
      
          Маскирование адресное, а не сплошное: таблица снимает только стрелку
          (там это обозначение соответствия, а не проза), цитата снимает только
          регистр (там цитируют чужие слова). Механическое не снимается НИГДЕ -
          запрещенный символ остается запрещенным и в таблице, и во frontmatter.
          """
          areas = [set() for _ in lines]
      
          # frontmatter: блок между первой строкой из трех дефисов и следующей такой же.
          if lines and FRONTMATTER_RE.match(lines[0]):
              end = None
              for i in range(1, min(len(lines), FRONTMATTER_MAX)):
                  if FRONTMATTER_RE.match(lines[i]):
                      end = i
                      break
              if end is not None:
                  for i in range(0, end + 1):
                      areas[i] |= {'стрелка', 'эмодзи', 'стоп-фраза', 'регистр'}
      
          # Таблица: строки вокруг разделителя, содержащие черту.
          for i, line in enumerate(lines):
              if not TABLE_SEP_RE.match(line) or '|' not in line:
                  continue
              areas[i].add('стрелка')
              for j in range(i - 1, -1, -1):
                  if not TABLE_ROW_RE.search(lines[j]):
                      break
                  areas[j].add('стрелка')
              for j in range(i + 1, len(lines)):
                  if not TABLE_ROW_RE.search(lines[j]):
                      break
                  areas[j].add('стрелка')
      
          for i, line in enumerate(lines):
              if QUOTE_RE.match(line):
                  areas[i].add('регистр')
      
          return areas
      
      
      def mask_link_targets(line):
          """Заменить адреса ссылок пробелами, сохранив длину строки и видимый текст.
      
          Текст ссылки читается как проза, и запрещенное слово в нем такой же дефект.
          А адрес вида /game-changer или https://example.test/magic дал бы ложную
          находку регистра или стоп-фразы, хотя менять его нельзя.
          """
          def blank_target(m):
              # Имя не `blank`: так называется общая реализация гашения комментариев BSL
              # в других скриптах набора, и гард переносимых блоков считает одноименную
              # функцию ее разошедшейся копией.
              whole = m.group(0)
              if whole.startswith(']('):
                  # Inline-ссылка: скобки и закрывающая черта остаются, адрес гасится.
                  return '](' + ' ' * (len(whole) - 3) + ')'
              return ' ' * len(whole)
      
          return LINK_TARGET_RE.sub(blank_target, line)
      
      
      QUOTE_PAIRS = (('"', '"'), ('«', '»'), ('`', '`'))
      
      
      def quoted_spans(line):
          """Границы участков строки, взятых в кавычки или бэктики.
      
          Возвращаются именно СПАНЫ, а не соседние символы слова: запрещенное слово
          часто стоит внутри закавыченной ФРАЗЫ ("досыпает элементы"), и проверка по
          соседству его пропускала - слева кавычка, а справа пробел.
      
          Ограничение названо явно: кавычка, открытая на предыдущей строке, здесь
          читается как открывающая, и спаны в такой строке смещаются. Абзац, где
          цитата перенесена через строку, даст находку на упоминании. Лечится это
          разбором всего документа вместо построчного, и цена такой переделки выше
          пользы: перенос цитаты через строку в наборе редок.
          """
          spans = []
          for opening, closing in QUOTE_PAIRS:
              pos = 0
              while True:
                  start = line.find(opening, pos)
                  if start < 0:
                      break
                  stop = line.find(closing, start + 1)
                  if stop < 0:
                      break
                  spans.append((start + 1, stop))
                  pos = stop + 1
          return spans
      
      
      def quoted_example(line, pos, end):
          """Взято ли само слово в позиции pos..end в кавычки или бэктики.
      
          Слово в кавычках УПОМИНАЕТСЯ, а не употребляется: правило, объясняющее
          запрет, приводит запрещенное слово, и штрафовать его собственным правилом
          неверно. Именно так не проходили `text-formatting.md` и SKILL.md самого
          хуманизатора.
      
          Проверяется обрамление САМОГО слова, а не наличие кавычек где-то в строке:
          широкое условие сняло бы регистр с любой строки, где есть хоть одна
          кавычка. Замерено по rules/: из 30 срабатываний 14 обрамлены кавычками и
          все они упоминания, 16 не обрамлены и все они настоящий долг текстов -
          заголовки вида "Подводные камни" и оборот "известные грабли".
          """
          return any(start <= pos and end <= stop for start, stop in quoted_spans(line))
      
      
      def looks_epistolary(prose_lines):
          """Похож ли текст на письмо или ответ: приветствие в начале, подпись в конце.
      
          Смотрим края, а не весь текст: в середине технической статьи слово "уважаемый" может
          оказаться в цитате, и это еще не делает документ письмом.
          """
          head = [l for l in prose_lines[:12] if l.strip()]
          tail = [l for l in prose_lines[-12:] if l.strip()]
          short = lambda l: len(l.strip()) <= GREETING_MAX_LEN
          if any(SALUTATION_RE.match(l) and short(l) for l in head):
              return True
          return any(SIGNOFF_RE.match(l) and short(l) for l in tail)
      
      
      def describe_fixes(before, after):
          """Перечислить примененные механические замены построчно.
      
          Нужен для машиночитаемого отчета: без перечня замен режим --fix молча меняет
          файл, и по отчету нельзя понять, что именно исправлено.
          """
          out = []
          for i, (was, became) in enumerate(zip(before.split('\n'), after.split('\n')), 1):
              if was == became:
                  continue
              for ch, repl, name in MECHANICAL:
                  n = was.count(ch) - became.count(ch)
                  if n > 0:
                      out.append({'line': i, 'category': 'механическое',
                                  'was': name, 'became': repl, 'count': n})
          return out
      
      
      def muted_report(genre, technical, force_technical):
          """Какие категории заглушены для этого прогона и по какой причине.
      
          Одного признака "пропущено по жанру" мало: reference глушит структуру,
          letter регистр, а ключи регистра перебивают жанр. Читателю отчета нужна
          причина, а не факт.
          """
          out = [{'category': c, 'reason': 'genre:' + genre}
                 for c in sorted(GENRE_MUTES.get(genre, set()))]
          if not technical and not force_technical:
              out.append({'category': 'регистр', 'reason': 'flag:--no-technical'})
          return out
      
      
      def detect_genre(path, text):
          """Определить жанр файла. Порядок классификаторов задан и не меняется.
      
          reference идет РАНЬШЕ letter: README с приветствием это справочник, а не
          письмо, и регистр в нем проверять надо. Обратный порядок выключил бы
          проверку у половины документации набора.
          """
          if path:
              p = str(path).replace('\\', '/').lower()
              segments = set(p.split('/'))
              name = p.rsplit('/', 1)[-1]
              if segments & {'rules', 'skills', 'tools', 'docs'}:
                  return 'reference'
              if name.startswith('readme') or name.startswith('changelog'):
                  return 'reference'
          if looks_epistolary(strip_code(text).split('\n')):
              return 'letter'
          return 'prose'
      
      
      # Какие категории ГЛУШИТ жанр. Каждый жанр глушит ровно одну: замерено по
      # репозиторию, жанрозависимых категорий всего две, и полный жанровый слой при
      # них не обоснован.
      GENRE_MUTES = {
          'reference': {'структура'},
          'prose': set(),
          'letter': {'регистр'},
      }
      
      
      def scan(text, technical=True, force_technical=False, genre='prose'):
          """Список находок: (категория, номер строки, описание).
      
          Маскирование адресное: область снимает НЕ все категории, а названные.
          Сплошное исключение таблиц и цитат скрыло бы и запрещенный символ, который
          остается запрещенным где угодно - EDT рубит его и в таблице, и во
          frontmatter.
          """
          prose = strip_code(text)
          prose_lines = prose.split('\n')
          areas = mark_areas(prose_lines)
          muted = GENRE_MUTES.get(genre, set())
          found = []
      
          # Пропуск по жанру - это пояснение, а не находка: письмо с разговорным оборотом исправно.
          # Формулировка пояснения про письмо сохранена дословно: на нее опирается
          # существующий кейс, а обещание обратной совместимости запрещает править
          # кейс под новое поведение.
          note = None
          check_register = technical
          if check_register and not force_technical and 'регистр' in muted:
              check_register = False
              if genre == 'letter':
                  note = ('похоже на письмо или ответ, проверка технического регистра пропущена; '
                          'включить принудительно - ключ --technical')
              else:
                  note = ('регистр не проверяется: жанр %s; включить принудительно - '
                          'ключ --technical' % genre)
          if force_technical:
              check_register = True
      
          for line_no, line in enumerate(prose_lines, 1):
              area = areas[line_no - 1]
              for ch, _repl, name in MECHANICAL:
                  count = line.count(ch)
                  if count:
                      found.append(('механическое', line_no,
                                    '%s: %d' % (name, count)))
              if 'стрелка' not in area:
                  arrows = ARROW_RE.findall(line)
                  if arrows:
                      found.append(('стрелка', line_no,
                                    'текстовая стрелка: %d, нужно слово по смыслу' % len(arrows)))
              if 'эмодзи' not in area and EMOJI_MARKER_RE.match(line):
                  found.append(('эмодзи', line_no, 'эмодзи-маркер в начале строки'))
              low = line.lower()
              if 'стоп-фраза' not in area:
                  for phrase in STOP_PHRASES_RU + STOP_PHRASES_EN:
                      at = low.find(phrase)
                      if at < 0:
                          continue
                      # Стоп-фраза в кавычках тоже упоминается, а не употребляется.
                      # Замерено по набору: из 10 срабатываний в rules/ и SKILL.md все
                      # 10 обрамлены кавычками и все они примеры в тексте, который
                      # об этих штампах и рассказывает. Вне кавычек - ни одного.
                      if quoted_example(line, at, at + len(phrase)):
                          continue
                      found.append(('стоп-фраза', line_no, '"%s"' % phrase))
              if check_register and 'регистр' not in area:
                  for m in REGISTER_RE.finditer(mask_link_targets(line)):
                      if quoted_example(line, m.start(), m.end()):
                          continue
                      found.append(('регистр', line_no,
                                    '"%s" - бытовой оборот, нужен технический термин' % m.group(0)))
      
          if 'структура' not in muted:
              headings = sum(1 for line in prose_lines if HEADING_RE.match(line))
              words = len(WORD_RE.findall(prose))
              if headings and words:
                  per = words / headings
                  if per < 200:
                      found.append(('структура', 0,
                                    'заголовков H2/H3: %d на %d слов, один на %d - гуще порога 1 на 200'
                                    % (headings, words, int(per))))
      
          in_code = sum(text.count(ch) for ch, _r, _n in MECHANICAL) - \
              sum(prose.count(ch) for ch, _r, _n in MECHANICAL)
          return found, in_code, note
      
      
      def apply_fix(text):
          """Механические замены. Блоки кода и inline-код не трогаются."""
          lines = text.split('\n')
          in_fence = False
          changed = 0
          for i, line in enumerate(lines):
              if FENCE_RE.match(line):
                  in_fence = not in_fence
                  continue
              if in_fence:
                  continue
              pieces = []
              pos = 0
              for m in INLINE_CODE_RE.finditer(line):
                  pieces.append((line[pos:m.start()], True))
                  pieces.append((m.group(0), False))
                  pos = m.end()
              pieces.append((line[pos:], True))
              rebuilt = []
              for piece, editable in pieces:
                  if editable:
                      for ch, repl, _name in MECHANICAL:
                          if ch in piece:
                              changed += piece.count(ch)
                              piece = piece.replace(ch, repl)
                  rebuilt.append(piece)
              lines[i] = ''.join(rebuilt)
          return '\n'.join(lines), changed
      
      
      def main():
          # Отчет содержит кириллицу. Без явного переключения печать падает с UnicodeEncodeError
          # везде, где консоль не в UTF-8: сборочный агент, чужая локаль.
          sys.stdout.reconfigure(encoding='utf-8')
          sys.stderr.reconfigure(encoding='utf-8')
          parser = argparse.ArgumentParser(
              description='Поиск объективных маркеров AI-генерации; механические чинятся сразу')
          parser.add_argument('files', nargs='+')
          parser.add_argument('--fix', action='store_true',
                              help='применить механические замены на месте')
          parser.add_argument('--quiet', action='store_true',
                              help='только итоговая строка на файл')
          parser.add_argument('--no-technical', dest='technical', action='store_false',
                              help='не проверять технический регистр (по умолчанию проверяется)')
          parser.add_argument('--technical', dest='force_technical', action='store_true',
                              help='проверять технический регистр даже в письме или ответе')
          parser.add_argument('--genre', choices=('reference', 'prose', 'letter'), default=None,
                              help='жанр текста; по умолчанию определяется по пути и содержимому')
          parser.add_argument('--json', dest='as_json', action='store_true',
                              help='машиночитаемый отчет; stdout содержит только JSON')
          parser.set_defaults(technical=True, force_technical=False)
          args = parser.parse_args()
      
          total = 0
          unreadable = 0
          report = []
          for name in args.files:
              path = Path(name)
              if not path.is_file():
                  # Обработка остальных файлов продолжается: остановка на первом плохом
                  # прятала бы результат по всем следующим.
                  unreadable += 1
                  if args.as_json:
                      report.append({'path': name, 'error': 'файл не найден'})
                  else:
                      sys.stderr.write('Файл не найден: %s\n' % name)
                  continue
              # Чтение и запись идут через io.open, а не через методы Path: параметр newline
              # у них появился только в python 3.13, а без него перевод строки нормализуется
              # при чтении, и файл с CRLF сохранялся бы с LF.
              with io.open(str(path), encoding='utf-8-sig', newline='') as fh:
                  raw = fh.read()
              eol = '\r\n' if '\r\n' in raw else '\n'
              text = raw.replace('\r\n', '\n')
      
              fixes = []
              if args.fix:
                  before = text
                  fixed, changed = apply_fix(text)
                  if changed:
                      with io.open(str(path), 'w', encoding='utf-8', newline='') as fh:
                          fh.write(fixed.replace('\n', eol))
                      fixes = describe_fixes(before, fixed)
                  text = fixed
                  if not args.as_json:
                      print('%s: механических замен %d' % (path.name, changed))
      
              genre = args.genre or detect_genre(path, text)
              found, in_code, note = scan(text, technical=args.technical,
                                          force_technical=args.force_technical, genre=genre)
              total += len(found)
      
              if args.as_json:
                  report.append({
                      'path': name,
                      'genre': genre,
                      'muted': muted_report(genre, args.technical, args.force_technical),
                      'findings': [{'category': c, 'line': ln, 'message': d}
                                   for c, ln, d in found],
                      'fixes': fixes,
                  })
                  continue
      
              if args.quiet:
                  print('%s: находок %d' % (path.name, len(found)))
                  continue
      
              print('=== %s ===' % path)
              if note:
                  print('  (%s)' % note)
              if not found:
                  print('Объективных маркеров не найдено.')
              else:
                  for category, line_no, text_of in found:
                      where = 'строка %d' % line_no if line_no else 'весь файл'
                      print('  [%s] %s: %s' % (category, where, text_of))
              if in_code:
                  print('  (в блоках кода символов из списка: %d, не трогаю)' % in_code)
              print('  ИТОГО находок: %d' % len(found))
      
          if args.as_json:
              print(json.dumps(report, ensure_ascii=False, indent=2))
      
          # Приоритет кода возврата: нечитаемый файл важнее находок, находки важнее
          # чистого результата.
          if unreadable:
              return 2
          return 1 if total else 0
      
      
      if __name__ == '__main__':
          sys.exit(main())
      
  • SKILL.md 32.6 KB
    ---
    name: humanize-ai-text
    description: "Применять при переписывании текстов, сгенерированных LLM-агентами (отчеты, README, доки, письма, посты), в живой человеческий стиль. Триггеры - пользователь пишет 'убери AI-стиль', 'перепиши по-человечески', 'сделай естественно', 'убери воду', 'не как ChatGPT', 'убери LLM-штампы'; либо на входе текст с явными маркерами генерации: H2/H3 на каждый абзац, буллеты вместо прозы, штампы 'в современном мире', 'давайте погрузимся', избыток длинных тире, эмодзи-заголовки, обязательные 'надеюсь, это поможет!'. Сверх того ВСЕГДА проверяет технический регистр: в README, журнале изменений, документации и описаниях заменяет бытовую метафору на действие и предмет ('досыпает', 'кладет', 'копит', 'под капотом', 'из коробки', 'ядро библиотеки'); в письме, ответе и эссе эта проверка сама выключается по жанру, ключ отключения - --no-technical. Переписывание в живой стиль не применять к структурированным форматам, где списки и заголовки уместны по существу: API-документация, чек-листы, табличные данные, бенчмарки, юридические документы."
    argument-hint: "[текст | путь к .md файлу] [--no-technical | --technical]"
    allowed-tools:
      - Bash
      - Read
      - Write
      - Edit
    ---
    
    # /humanize-ai-text - переписывание AI-текста в живой стиль
    
    Превращает текст с маркерами LLM-генерации (ровный ритм, лестница H2/H3, буллеты вместо прозы, дежурные вступления и заключения) в текст с человеческой интонацией, не теряя смысл, числа и термины.
    
    ## Когда использовать
    
    Триггерные фразы пользователя:
    - "убери AI-стиль", "не как ChatGPT", "не как нейросеть"
    - "перепиши по-человечески", "сделай естественно", "сделай живым"
    - "убери воду", "убери штампы", "убери LLM-маркеры"
    - "причеши текст", "оживи текст"
    
    Автотриггер при анализе входного текста:
    - Заголовки H2/H3 на каждый второй абзац в коротком документе.
    - Буллеты с симметричной структурой и одинаковой длиной пунктов.
    - Стоп-фразы из таблицы ниже ("в современном мире", "давайте погрузимся" и т.п.).
    - Эмодзи-маркеры в начале пунктов (галочки, ракеты, стрелки).
    - Дежурные "надеюсь, это поможет!" / "дайте знать, если есть вопросы!".
    - Серия предложений одинаковой длины подряд (4+).
    
    ## Режимы работы
    
    | Режим | Триггер | Что делает |
    |-------|---------|------------|
    | Inline | аргумент - произвольный текст | Переписанный текст выводится в чат |
    | File | аргумент - путь к существующему .md файлу | Результат сохраняется рядом с суффиксом `-human.md` |
    | Interactive | аргумент пустой | Спросить у пользователя текст или путь |
    | Встроенный | скил вызван другим агентом как шаг задачи | Отдать ТОЛЬКО итоговый текст |
    
    Встроенный режим - когда результат идет дальше в чужую работу: описание pull request, сообщение
    коммита, кусок документации. Ни черновика, ни разбора, ни резюме правок: вызывающему нужен текст,
    а не отчет о переписывании.
    
    Алгоритм определения режима - как в `/prompt-enhancer`:
    1. Пустой аргумент - Interactive.
    2. Read удалось прочитать аргумент - File.
    3. Read вернул "не найден" - Inline (аргумент целиком как текст).
    
    ## Технический регистр: включен по умолчанию
    
    Сверх поиска признаков генерации скил проверяет ТЕХНИЧЕСКИЙ РЕГИСТР. Проверка включена всегда,
    отключается ключом `--no-technical` либо словами пользователя "без проверки регистра".
    
    **Что проверяется.** В техническом тексте бытовая метафора вместо действия и предмета - дефект.
    Не "досыпает элементы", а "добавляет N элементов в конец коллекции". Не "копит ошибки", а
    "накапливает список диагностик до вызова X". Не "схлопывая пробелы", а "заменяя последовательность
    пробелов одним". Не "ядро библиотеки", а конкретный модуль. Не "подсистемы узнают друг о друге",
    а "подсистема A вызывает экспортный метод подсистемы B". Так же исключаются "кладет", "забирает",
    "внутренняя кухня", "под капотом", "на лету", "магия", "умеет", "дружит с", "из коробки",
    "грабли", "ловушки", "костыль". В английском тексте - `under the hood`, `out of the box`,
    `the heart of`, `knows how to`, `magic`, `seamless`.
    
    **Проверочный вопрос к каждому глаголу и образу:** можно ли по этой фразе назвать метод, поле, код
    ошибки, диапазон версий или измеренное число? Нельзя - фраза декоративная, заменить на фактическую.
    Имя метода и измеренная величина всегда лучше пересказа своими словами.
    
    **Где проверка НЕ применяется.** Текст произвольного жанра - письмо, ответ, сообщение, эссе,
    поздравление - живет по своим правилам, и разговорный оборот там не дефект. Скрипт распознает такой
    текст по приветствию в начале и подписи в конце и сам пропускает проверку, сообщая об этом. Если
    жанр распознан неверно, проверка включается принудительно ключом `--technical`.
    
    Приветствие засчитывается только целым словом и только в короткой строке, до 60 символов. Иначе
    заголовок "Приветственный экран" выключал бы проверку для всей статьи, и молча.
    
    Границу проводить по жанру, а не по теме: письмо про устройство обмена данными остается письмом.
    
    **Список в скрипте узкий и точный.** Оценочные слова ("просто", "легко", "удобно", "мощный") в него
    не входят: они слишком часто законны. По ним решение принимает модель проверочным вопросом выше.
    
    ## Базовый принцип
    
    Хороший человеческий текст имеет ритм, неровность и точку зрения. AI-текст ровный, симметричный, гипер-структурированный, без личной интонации. Цель скила - вернуть тексту неровность, не теряя смысл.
    
    ## Жесткие правила
    
    1. **Ничего не выдумывать.** В переписанном тексте не должно появиться ни одного факта, имени,
       числа, даты или цитаты, которых не было в исходнике. Заменить расплывчатое на конкретное можно,
       только если конкретика взята из источника или дана пользователем: "заметно ускорилось" станет
       "стало вдвое быстрее" лишь тогда, когда "вдвое" где-то сказано. Если фразе не хватает детали -
       спросить или написать без нее. Мнение и оценка фактами не считаются: там, где жанр допускает
       голос, отношение добавлять можно, новые утверждения о мире - нельзя.
    2. **Списки только когда элементы реально параллельны и независимы.** Если соседние пункты связаны логикой ("сначала X, потому что Y, иначе Z") - это абзац, а не буллеты.
    3. **Заголовки только при смене темы.** Не на каждые 2 абзаца. Документ из 400 слов с 6 H2 - это AI-текст, переписать в прозу с 1-2 разделами.
    4. **Длина предложений варьируется.** Если идут 4 предложения по 15-20 слов подряд - ломать ритм. Короткое. Потом длинное, с придаточным. Потом среднее.
    5. **Никаких буферных вступлений и заключений.** Не "В этой статье мы рассмотрим...", не "Подводя итог...". Сразу к делу, в конце - последняя содержательная мысль, без обертки.
    6. **Никаких финальных "надеюсь, это поможет!", "дайте знать, если есть вопросы!".** Если уместен призыв к действию - он конкретный ("скажи, какой вариант - соберу пример"), а не дежурный.
    7. **Bold для терминов при первом введении и для реальных акцентов**, не для каждой второй фразы. Если в абзаце 4+ выделения жирным - убрать половину.
    8. **Длинные тире лучше заменить на обычный дефис, запятую или скобки.** Часто длинное тире - тоже маркер LLM-стиля. Если оставлять - то редко и осознанно, не подряд в каждом втором предложении.
    9. **Буква Cyrillic Letter Yo (U+0451) - тоже маркер AI или официального документа.** Люди в неформальных текстах эту букву печатают редко: пишут "все", "еще", "вообще", "отчет", "нашел". Если в исходнике диакритическая "е" расставлена везде - заменить на обычную "е". Сохранять только в текстах, где это требование жанра (учебники, словари, имена собственные если автор настаивает на точном произношении).
    10. **Текстовые стрелки `->`, `=>`, `→` - убрать.** Запись через стрелку - маркер технической AI-генерации, в живом тексте так не пишут. Заменять словом по смыслу ("становится", "переходит в", "дает", "ведет к") или переписывать фразой. "складская накладная -> расходный ордер" становится "из складской накладной собирается расходный ордер". Это касается всех стрелок: ASCII `->` и `=>`, юникод `→`.
    
    ## Голос: где он нужен, а где вредит
    
    Безжизненный текст выдает машину не хуже, чем штампы. Ровные предложения, безупречная симметрия и
    полное отсутствие отношения - тоже признак генерации. Живому тексту позволено иметь мнение,
    сомнение, смешанные чувства, отступление в скобках и неровный ритм.
    
    Но добавлять голос можно **не везде**. Он уместен в постах, эссе, письмах, разборах, README со
    своей интонацией. В справочнике, спецификации, регламенте и юридическом документе нейтральный
    ровный тон **и есть** правильный человеческий голос: первое лицо и оценки там неуместны, их
    отсутствие не дефект. Прежде чем оживлять - определить жанр.
    
    ## Калибровка по образцу
    
    Если пользователь дал образец своего письма (прежний пост, письмо, кусок документации), разобрать
    его до того, как переписывать:
    
    1. Прочитать образец. Отметить длину предложений, лексику, чем начинаются абзацы, какая пунктуация
       в ходу, какие обороты повторяются, как делаются переходы.
    2. Подстраиваться под эти привычки, а не просто вычищать маркеры. Не "улучшать" разговорные слова и
       не выравнивать намеренные странности - они и есть авторский почерк.
    3. Образца нет - работать по умолчаниям этого скила.
    
    **Образец главнее правил скила.** Если автор любит длинные тире и они есть в образце - оставить их с
    его частотой, а не вычищать по общему правилу. Совпасть с автором важнее, чем убрать признак.
    
    ## Структурные антипаттерны
    
    - **Триплеты-пулемет.** "Быстрый, надежный и масштабируемый". "Анализ, синтез и применение". Если в тексте 3+ триплета - сломать половину в пары или одиночные.
    - **Симметричные буллеты одинаковой длины.** Признак шаблона. Либо переписать в прозу, либо сознательно сделать пункты разной длины и структуры.
    - **Эмодзи-маркеры в списках и заголовках.** Удалить все, если только это не маркетинговый пост, где это сознательный выбор.
    - **Параллельные подзаголовки в духе "Преимущества / Недостатки / Применение / Заключение".** Признак шаблона из тренировочных данных. Переписать структуру под конкретный материал.
    - **Перевернутая пирамида с TL;DR + повторением + резюме.** Достаточно одного из трех.
    - **Хеджирование на каждом шагу:** "может быть", "возможно", "в некоторых случаях", "как правило". Оставить только там, где есть реальная неопределенность.
    - **Длинные тире через предложение.** Маркер ровного LLM-ритма. Заменять на запятые, скобки, точки или обычный дефис.
    
    ## Что сохранять буквально
    
    - Технические термины - не упрощать ради "человечности".
    - Числа, версии, имена файлов, флаги CLI, идентификаторы - без изменений.
    - Цитаты, код, команды - не трогать.
    - Если в исходнике есть обоснованная структура (нумерованные шаги установки, список зависимостей, таблица параметров API) - оставить.
    - Имена людей, организаций, продуктов - точно как в оригинале.
    
    ## Справочники
    
    Лежат рядом в `references/`, грузятся по требованию, а не каждый раз:
    
    | Файл | Когда читать |
    |---|---|
    | `stop-phrases.md` | Скрипт нашел стоп-фразу и нужно решить, чем ее заменить |
    | `language-antipatterns.md` | Идет переписывание: обход глагола "быть", синонимическая карусель, ложные диапазоны, формулы-афоризмы |
    | `false-positives.md` | Перед правкой: что НЕ считать признаком AI и какие приметы живого текста беречь |
    | `examples.md` | Нужен образец "до и после" |
    
    ## Алгоритм работы
    
    Механическое делает скрипт, решения принимает модель. Порядок именно такой: без первого шага модель
    тратит проход на поиск того, что находится регулярным выражением.
    
    ### Шаг 1. Прогнать скрипт
    
    Замысел подготовки текста перед проверкой - исключать области, где находка не находка, - взят из
    `humanizer_ru/textprep.py` проекта [comol/Humanizer_RU](https://github.com/comol/Humanizer_RU)
    (MIT, версия 0.3.0). Реализация здесь своя и решает другую задачу: тот линтер меряет читаемость
    русского текста вообще, а этот сканер сторожит правила набора. Пользоваться ими имеет смысл вместе:
    сканер обязателен и падает в сборке, линтер запускают отдельно, когда пишут длинный текст наружу.
    
    ```bash
    python scripts/humanize_scan.py <файл>                    # отчет, файл не меняется
    python scripts/humanize_scan.py <файл> --fix              # плюс механические замены на месте
    python scripts/humanize_scan.py <файл> --genre reference  # задать жанр явно
    python scripts/humanize_scan.py <файл> --json             # машиночитаемый отчет
    python scripts/humanize_scan.py <файл> --no-technical     # без проверки технического регистра
    python scripts/humanize_scan.py <письмо> --technical      # проверять регистр и в письме
    ```
    
    Скрипт находит и с `--fix` чинит сам: букву е с диакритикой, длинное и короткое тире,
    кавычки-елочки, символ многоточия. Находит, но НЕ чинит: текстовые стрелки (на их месте нужно слово
    по смыслу), стоп-фразы из таблиц, эмодзи-маркеры в начале строк, слишком плотные заголовки и
    бытовые обороты вместо технических - категория `[регистр]`.
    
    **Жанр решает, какие категории проверяются.** Жанрозависимых категорий две, и каждый жанр глушит
    ровно одну:
    
    | Жанр | Когда | структура | регистр | остальные |
    |---|---|---|---|---|
    | `reference` | README, правило, SKILL.md, справочник, журнал изменений | ВЫКЛ | вкл | вкл |
    | `prose` | отчет, статья, пост, эссе (по умолчанию) | вкл | вкл | вкл |
    | `letter` | письмо, ответ | вкл | ВЫКЛ | вкл |
    
    Жанр берется из `--genre`, иначе определяется сам: сначала по пути и имени файла, затем по
    содержимому. README с приветствием это справочник, а не письмо - порядок именно такой.
    
    Плотные заголовки в справочном документе уместны по существу, и до введения жанров сканер
    штрафовал за них всю документацию набора: из 40 правил чисто проходило НОЛЬ, а после - 22.
    
    **Часть находок снимается по области, а не по жанру.** Стрелка в таблице это обозначение
    соответствия, слово в кавычках упоминается, а не употребляется, а адрес ссылки не проза. Все это
    маскируется адресно, но механическое не маскируется нигде кроме кода: запрещенный символ остается
    запрещенным и в таблице, и во frontmatter, потому что EDT рубит его одинаково.
    
    Ограничение названо явно: кавычка, открытая на предыдущей строке, читается как открывающая, и в
    такой строке границы цитат смещаются. Абзац с цитатой, перенесенной через строку, даст находку на
    упоминании.
    
    Категория `[регистр]` не чинится механически по определению: замена зависит от того, что код делает
    на самом деле. Скрипт называет найденное слово целиком и номер строки, формулировку подбирает модель.
    
    Совпадение идет по границе слова: "копит" не находится внутри "накопитель", "умеет" - внутри
    "умелый". Часть записей - основы (`досып`, `схлопыв`, `ловушк`), они ловят любое окончание, но
    только с начала слова.
    
    Блоки кода и inline-код исключены из поиска: внутри них тире и стрелка - часть синтаксиса, а
    метафора в комментарии к примеру кода правится вместе с примером, а не отдельно.
    
    Код возврата 0, если находок нет. Это позволяет ставить скрипт в проверку перед коммитом.
    
    ### Шаг 2. Решить, нужно ли переписывание вообще
    
    Скрипт не нашел ничего и текст не вызывает подозрений - работа закончена. Сказать об этом и НЕ
    создавать выходной файл: копия, идентичная исходнику, вводит в заблуждение.
    
    Проверить жанр по разделу "Когда НЕ применять". API-документация, чек-лист, регламент, таблица
    бенчмарков - там структура не дефект, и переписывать нечего.
    
    Есть образец авторского стиля - разобрать его до правок, см. "Калибровка по образцу". Образец
    главнее правил этого скила.
    
    ### Шаг 3. Переписать то, что осталось
    
    Читать `references/false-positives.md` ДО правок: половина признаков AI встречается у аккуратного
    человека. Дальше по жестким правилам:
    
    - убрать буферные вступления и дежурные заключения;
    - слить связанные пункты в прозу, оставить списками только параллельное и независимое;
    - сократить заголовки до числа реальных смен темы;
    - сломать ровный ритм, чередуя длину предложений;
    - разбить триплеты-штампы на пары и одиночные формулировки;
    - заменить стрелки словом по смыслу, стоп-фразы - по таблице в `references/stop-phrases.md`;
    - убрать эмодзи-маркеры, если жанр их не требует;
    - переписать бытовые обороты из категории `[регистр]` на действие и предмет: что именно делается,
      с чем и в каком количестве. Пройти по тексту и тем же проверочным вопросом снять декоративные
      фразы, которых в списке скрипта нет. В произвольном жанре этот пункт не выполняется.
    
    Тонкие приемы уровня фразы - в `references/language-antipatterns.md`.
    
    ### Шаг 4. Проверить себя
    
    Прогнать скрипт повторно, затем пройти чек-лист ниже и ответить на два вопроса:
    
    - что в получившемся тексте все еще очевидно машинное?
    - появился ли факт, имя, число, дата или цитата, которых не было в исходнике? Выдумка - дефект,
      даже если звучит человечнее расплывчатого оригинала.
    
    ### Шаг 5. Отдать результат
    
    Режим File - сохранить в `<имя>-human.md` рядом с исходником и сообщить путь. Режим Inline -
    вернуть текст в чат. Встроенный режим - отдать ТОЛЬКО текст, без разбора правок.
    
    ## Чек-лист перед сдачей текста
    
    - [ ] Вступление начинается с сути, а не с "в современном мире".
    - [ ] Финал - содержательная мысль, а не "надеюсь, это поможет".
    - [ ] Заголовков ровно столько, сколько реальных смен темы.
    - [ ] Буллеты только для параллельных независимых пунктов.
    - [ ] Длина предложений неровная.
    - [ ] Нет триплетов-штампов.
    - [ ] Нет стоп-фраз из таблицы выше.
    - [ ] Bold на терминах и акцентах, не на каждом абзаце.
    - [ ] Нет эмодзи-маркеров (если жанр их не требует).
    - [ ] Нет длинных тире через предложение.
    - [ ] Нет диакритической "е" (Cyrillic Letter Yo, U+0451), кроме случаев когда это требование жанра.
    - [ ] Хеджирование осталось только там, где есть реальная неопределенность.
    - [ ] Числа, термины, имена, код не пострадали.
    - [ ] В техническом тексте нет бытовых оборотов вместо действий и предметов: по каждому глаголу и
          образу можно назвать метод, поле, код ошибки или измеренную величину.
    
    ## Когда НЕ применять
    
    Разделы ниже - про переписывание в живой стиль. Проверка технического регистра здесь действует
    наоборот: в API-документации, регламенте и отчете с метриками она нужна БОЛЬШЕ всего, а отключается
    только на произвольных жанрах - письме, ответе, эссе.
    
    - API-документация с эндпоинтами и параметрами - структура нужна.
    - Чек-листы для исполнения, runbook - буллеты по делу.
    - Юридические и официальные документы - стиль регламентирован.
    - Регламенты, инструкции по технике безопасности - формальная структура обязательна.
    - Табличные данные, бенчмарки, отчеты с метриками - таблицы и заголовки уместны.
    - Когда пользователь явно просит "структурируй", "оформи в виде списка", "сделай TOC".
    
    ## DO / DON'T
    
    **DO:**
    - Сохранять смысл, числа, термины, цитаты буквально.
    - Ломать ровный ритм предложений и абзацев.
    - Удалять буферные вступления и дежурные заключения.
    - Сводить связанные пункты в прозу, оставлять списки только для реально параллельных вещей.
    - Сокращать количество заголовков до реальных смен темы.
    
    **DON'T:**
    - Упрощать технические термины ради "человечности".
    - Менять числа, версии, имена файлов, идентификаторы.
    - Добавлять разговорные элементы там, где пользователь хочет деловой регистр.
    - Переделывать обоснованную структуру (API-доки, чек-листы) - сначала проверить раздел "Когда НЕ применять".
    - Заменять стоп-фразы на синонимы-штампы (вместо "давайте погрузимся" писать "давайте рассмотрим").
    
    ## Источник каталога признаков
    
    Часть признаков сверена с [Wikipedia:Signs of AI writing](https://en.wikipedia.org/wiki/Wikipedia:Signs_of_AI_writing) -
    каталогом WikiProject AI Cleanup, собранным на тысячах случаев генерации в статьях. Полезная оттуда
    мысль: модель выбирает статистически наиболее вероятное продолжение, поэтому тяготеет к формулировке,
    подходящей самому широкому числу случаев - отсюда и обтекаемость, и одинаковость.
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related