humanizer-ru 3.29.0 → 3.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.en.md CHANGED
@@ -5,7 +5,7 @@
5
5
  Claude Code / Cowork plugin. Kills AI smell in Russian text. The English [humanizer](https://github.com/blader/humanizer) won't help here. Russian AI markers are their own beast: bureaucratic noun-chains (канцелярит), English-syntax calques, missing particles like "же" and "ведь" that make Russian sound alive.
6
6
 
7
7
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
8
- [![Version](https://img.shields.io/badge/version-3.29.0-blueviolet)](https://github.com/ilyautov/humanizer-ru/blob/main/CHANGELOG.md)
8
+ [![Version](https://img.shields.io/badge/version-3.30.0-blueviolet)](https://github.com/ilyautov/humanizer-ru/blob/main/CHANGELOG.md)
9
9
  [![Stars](https://img.shields.io/github/stars/ilyautov/humanizer-ru?style=social)](https://github.com/ilyautov/humanizer-ru/stargazers)
10
10
  [![skills.sh](https://skills.sh/b/ilyautov/humanizer-ru)](https://skills.sh/ilyautov/humanizer-ru/humanizer-ru)
11
11
  [![npm](https://img.shields.io/npm/v/humanizer-ru?label=npm%20%C2%B7%20dsh)](https://www.npmjs.com/package/humanizer-ru)
@@ -13,7 +13,7 @@ Claude Code / Cowork plugin. Kills AI smell in Russian text. The English [humani
13
13
 
14
14
  <p align="center">
15
15
  <a href="https://humanizer-ru.aifrontier.tech/">
16
- <img src="assets/social-preview.png" alt="humanizer-ru: removes AI tells from Russian text. 64 patterns, 21 hard bans, scanner included" width="720">
16
+ <img src="assets/social-preview.png" alt="humanizer-ru: removes AI tells from Russian text. 67 patterns, 21 hard bans, scanner included" width="720">
17
17
  </a>
18
18
  </p>
19
19
 
@@ -34,17 +34,17 @@ The CLI detects your installed agents and asks where to put the skill. In Claude
34
34
 
35
35
  Claude.ai, DeepSeek Harness and team rollout are covered in [Install](#install).
36
36
 
37
- 📖 **Docs & write-ups (RU):** [humanizer-ru.aifrontier.tech](https://humanizer-ru.aifrontier.tech/): do AI detectors work on Russian, the 64 markers, plagiarism vs AI detection.
37
+ 📖 **Docs & write-ups (RU):** [humanizer-ru.aifrontier.tech](https://humanizer-ru.aifrontier.tech/): do AI detectors work on Russian, the 67 markers, plagiarism vs AI detection.
38
38
 
39
39
  ## What you get
40
40
 
41
- 64 patterns across 14 categories: канцелярит, English calques, emotional sterility, persuasion tricks, information rhythm, hedging specifics, plus the 2025-2026 stylistic fingerprints (jagged-meditation single-word sentences, pseudo-Socratic Q-A chains, decorative emoji per list item, pseudo-therapeutic register) and the 2026 formulas (inanimate subject, Title Case headings, mid-sentence truncation, negation triad), plus a discourse layer (explicit final moral, portrait-style character introduction, emotion conveyed only through the body, seamless causal chains, strictly linear chronology, no direct reader address: narrative signals that survive stylistic rewriting, per StoryScope / COLM 2026). 21 hard-banned constructions that scream "GPT wrote this", including em-dashes (detectors count their frequency). A research-backed section on how detectors actually work (perplexity, burstiness, morphology) with verified numbers from DivEye, PIFE, AINL-Eval 2025 (every citation checked, see SOURCES.md). Five article formulas. Voice calibration. Local editing instead of rewriting: mark the exact spot, strip the wrapper around the claim, verify the result against the source (who did what, certainty, conditions, numbers with units). Nothing is added for "liveliness": no invented facts, emotions or particles; a 195-sample blind run (eval/LOCAL_EDIT_CHECK.md) showed that voice quotas produce foreign tone and staged rhetoric.
41
+ 67 patterns across 16 categories: канцелярит, English calques, emotional sterility, persuasion tricks, information rhythm, hedging specifics, plus the 2025-2026 stylistic fingerprints (jagged-meditation single-word sentences, pseudo-Socratic Q-A chains, decorative emoji per list item, pseudo-therapeutic register) and the 2026 formulas (inanimate subject, Title Case headings, mid-sentence truncation, negation triad), plus a discourse layer (explicit final moral, portrait-style character introduction, emotion conveyed only through the body, seamless causal chains, strictly linear chronology, no direct reader address: narrative signals that survive stylistic rewriting, per StoryScope / COLM 2026). 21 hard-banned constructions that scream "GPT wrote this", including em-dashes (detectors count their frequency). A research-backed section on how detectors actually work (perplexity, burstiness, morphology) with verified numbers from DivEye, PIFE, AINL-Eval 2025 (every citation checked, see SOURCES.md). Five article formulas. Voice calibration. Local editing instead of rewriting: mark the exact spot, strip the wrapper around the claim, verify the result against the source (who did what, certainty, conditions, numbers with units). Nothing is added for "liveliness": no invented facts, emotions or particles; a 195-sample blind run (eval/LOCAL_EDIT_CHECK.md) showed that voice quotas produce foreign tone and staged rhetoric.
42
42
 
43
43
  ## Try it without installing
44
44
 
45
45
  The [site's front page](https://humanizer-ru.aifrontier.tech/#audit) runs the same scanner in JavaScript: paste a text, get a 0-100 cleanliness score, highlighted markers and the install command for your agent. Everything runs in the browser, nothing is uploaded. Rules are exported from `markers.py` by `scripts/export_web_rules.py`; `scripts/test_web_parity.py` keeps the web and Python engines in step in CI.
46
46
 
47
- The same scanner ships as a Chrome extension: select text on any page, pick "Проверить на следы нейросети" in the context menu, and the popup shows the score and highlights. Two permissions only, context menu and local storage; no host access, no network. Source in `extension/`, built by `scripts/build_extension.py`; until the Web Store listing is live, load it via `chrome://extensions` → Developer mode → Load unpacked.
47
+ The same scanner ships as a [Chrome extension](https://chromewebstore.google.com/detail/iaelpnagdohdahkcahippeadohpgobad): select text on any page, pick "Проверить на следы нейросети" in the context menu, and the popup shows the score and highlights. Two permissions only, context menu and local storage; no host access, no network. Source in `extension/`, built by `scripts/build_extension.py`.
48
48
 
49
49
  ## Install
50
50
 
package/README.md CHANGED
@@ -1,11 +1,11 @@
1
1
  # humanizer-ru: очеловечить русский AI-текст для Claude Code, Cursor, Codex и других AI-агентов
2
2
 
3
- > Скилл для Claude. Убирает 64 признака нейросети в русском тексте: канцелярит, кальки, фингерпринты ChatGPT и Claude. Метит в то, что измеряют GPTZero, DivEye, RuBERT: поднимает perplexity и burstiness. 21 жёстких банов, quad-pass аудит, калибровка под голос автора, eval-харнес с метриками до/после. Живое демо сканера на сайте.
3
+ > Скилл для Claude. Убирает 67 признаков нейросети в русском тексте: канцелярит, кальки, фингерпринты ChatGPT и Claude. Метит в то, что измеряют GPTZero, DivEye, RuBERT: поднимает perplexity и burstiness. 21 жёстких банов, quad-pass аудит, калибровка под голос автора, eval-харнес с метриками до/после. Живое демо сканера на сайте.
4
4
 
5
5
  > [English version](README.en.md) · [中文](README.zh.md)
6
6
 
7
7
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
8
- [![Версия](https://img.shields.io/badge/версия-3.29.0-blueviolet)](https://github.com/ilyautov/humanizer-ru/blob/main/CHANGELOG.md)
8
+ [![Версия](https://img.shields.io/badge/версия-3.30.0-blueviolet)](https://github.com/ilyautov/humanizer-ru/blob/main/CHANGELOG.md)
9
9
  [![Звёзды](https://img.shields.io/github/stars/ilyautov/humanizer-ru?style=social)](https://github.com/ilyautov/humanizer-ru/stargazers)
10
10
  [![skills.sh](https://skills.sh/b/ilyautov/humanizer-ru)](https://skills.sh/ilyautov/humanizer-ru/humanizer-ru)
11
11
  [![npm](https://img.shields.io/npm/v/humanizer-ru?label=npm%20%C2%B7%20dsh)](https://www.npmjs.com/package/humanizer-ru)
@@ -13,7 +13,7 @@
13
13
 
14
14
  <p align="center">
15
15
  <a href="https://humanizer-ru.aifrontier.tech/">
16
- <img src="assets/social-preview.png" alt="humanizer-ru: убирает следы нейросети из русского текста. 64 признака, 21 запрет, сканер в комплекте" width="720">
16
+ <img src="assets/social-preview.png" alt="humanizer-ru: убирает следы нейросети из русского текста. 67 признаков, 21 запрет, сканер в комплекте" width="720">
17
17
  </a>
18
18
  </p>
19
19
 
@@ -39,7 +39,7 @@ CLI найдёт установленных агентов и спросит, к
39
39
 
40
40
  Claude.ai, DeepSeek Harness и раскатка на команду в [разделе «Установка»](#установка).
41
41
 
42
- 📖 **Документация и разборы:** [humanizer-ru.aifrontier.tech](https://humanizer-ru.aifrontier.tech/): [работают ли AI-детекторы на русском](https://humanizer-ru.aifrontier.tech/ai-detektory-na-russkom.html), [64 признака AI-текста](https://humanizer-ru.aifrontier.tech/52-priznaka-ai-teksta.html), [антиплагиат и нейросеть](https://humanizer-ru.aifrontier.tech/antiplagiat-i-neyroset.html).
42
+ 📖 **Документация и разборы:** [humanizer-ru.aifrontier.tech](https://humanizer-ru.aifrontier.tech/): [работают ли AI-детекторы на русском](https://humanizer-ru.aifrontier.tech/ai-detektory-na-russkom.html), [67 признаков AI-текста](https://humanizer-ru.aifrontier.tech/52-priznaka-ai-teksta.html), [антиплагиат и нейросеть](https://humanizer-ru.aifrontier.tech/antiplagiat-i-neyroset.html).
43
43
 
44
44
  🧰 **Остальные инструменты:** MCP-серверы к кабинетам Wildberries, Ozon, Яндекс Маркета и Авито, пять серверов к hh.ru, VK, Диадоку, СБИС и Честному знаку, 34 скилла для малого бизнеса, совет мыслителей с дословной проверкой цитат. Все одним списком: [ilyautov.github.io](https://ilyautov.github.io/).
45
45
 
@@ -51,7 +51,7 @@ Claude.ai, DeepSeek Harness и раскатка на команду в [разд
51
51
 
52
52
  ## Что внутри
53
53
 
54
- **64 паттерна AI-генерации в 14 категориях:**
54
+ **67 паттернов AI-генерации в 16 категориях:**
55
55
 
56
56
  - A. Контентные: пустые открытия, размытые авторитеты, формульные выводы
57
57
  - B. Языковые: канцелярит, кальки, «является», нагромождение причастий
@@ -86,7 +86,7 @@ Claude.ai, DeepSeek Harness и раскатка на команду в [разд
86
86
 
87
87
  На [главной странице сайта](https://humanizer-ru.aifrontier.tech/#audit) работает тот же сканер на JavaScript: вставьте текст, получите оценку чистоты от 0 до 100, подсветку найденных оборотов и команду установки для своей среды. Считается в браузере, текст никуда не отправляется. Правила экспортируются из `markers.py` скриптом `scripts/export_web_rules.py`, а паритет с Python проверяет `scripts/test_web_parity.py` в CI.
88
88
 
89
- Тот же сканер есть как расширение Chrome: выделили текст на любой странице, в контекстном меню «Проверить на следы нейросети», и попап показывает балл и подсветку. Разрешений у него два, контекстное меню и локальное хранилище, доступа к сайтам и сети нет. Папка `extension/`, сборка `scripts/build_extension.py`; пока идёт модерация в Web Store, ставится через `chrome://extensions` → «Режим разработчика» → «Загрузить распакованное расширение».
89
+ Тот же сканер есть как [расширение Chrome](https://chromewebstore.google.com/detail/iaelpnagdohdahkcahippeadohpgobad): выделили текст на любой странице, в контекстном меню «Проверить на следы нейросети», и попап показывает балл и подсветку. Разрешений у него два, контекстное меню и локальное хранилище, доступа к сайтам и сети нет. Папка `extension/`, сборка `scripts/build_extension.py`.
90
90
 
91
91
  ## Установка
92
92
 
@@ -349,7 +349,7 @@ GPTZero, Originality.ai, ZeroGPT и другие популярные детек
349
349
 
350
350
  **Как обойти GPTZero на русском?** GPTZero измеряет perplexity и burstiness. Скилл поднимает оба показателя: контрастное вычитание (замена самого предсказуемого слова в каждом предложении) повышает perplexity, рваный ритм с короткими и длинными предложениями повышает burstiness. Адверсарный парафраз снижает true positive rate детекторов на 87.88% по NeurIPS 2025.
351
351
 
352
- **Как убрать признаки ChatGPT из текста?** Главные сигналы: «не просто X, а Y» (80%+ AI-текстов), «стоит отметить», «является», длинное тире, отсутствие частиц «же/ведь/вот», равномерная информационная плотность. Скилл ловит и заменяет все 64 паттерна.
352
+ **Как убрать признаки ChatGPT из текста?** Главные сигналы: «не просто X, а Y» (80%+ AI-текстов), «стоит отметить», «является», длинное тире, отсутствие частиц «же/ведь/вот», равномерная информационная плотность. Скилл ловит и заменяет все 67 паттернов.
353
353
 
354
354
  **Как сделать текст ChatGPT человеческим на русском?** Три шага: снять 21 жёстких банов и маркеры по приоритету, срезать оболочку вокруг утверждений вместо переписывания, сверить результат с исходником по фактам и степени уверенности. Живость не дописывается: то, чего нет в исходнике, в текст не идёт.
355
355
 
@@ -372,7 +372,7 @@ GPTZero, Originality.ai, ZeroGPT и другие популярные детек
372
372
  | | humanizer-ru | blader/humanizer |
373
373
  |---|---|---|
374
374
  | Язык | Русский | Английский |
375
- | Паттернов | 64 | 32 |
375
+ | Паттернов | 67 | 32 |
376
376
  | Жёстких банов | 20 | - |
377
377
  | Аудит | Quad-pass | Single-pass |
378
378
  | Voice calibration | Да | Да |
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "humanizer-ru",
3
- "version": "3.29.0",
4
- "description": "Agent skill for DeepSeek Harness: rewrites Russian text to remove 64 markers of AI generation (bureaucratese, calques, ChatGPT fingerprints), with a corpus-calibrated scanner, audit mode and author-voice calibration.",
3
+ "version": "3.30.0",
4
+ "description": "Agent skill for DeepSeek Harness: rewrites Russian text to remove 67 markers of AI generation (bureaucratese, calques, ChatGPT fingerprints), with a corpus-calibrated scanner, audit mode and author-voice calibration.",
5
5
  "license": "MIT",
6
6
  "author": "Ilya Utov",
7
7
  "homepage": "https://humanizer-ru.aifrontier.tech/",
@@ -4,7 +4,7 @@ description: "Качество русского текста для агента
4
4
  license: MIT
5
5
  ---
6
6
 
7
- # Humanizer-RU v3.29.0
7
+ # Humanizer-RU v3.30.0
8
8
 
9
9
  Ты редактор. Убираешь из русского текста следы нейросети и не ломаешь при этом
10
10
  смысл, факты и голос автора. Оба обещания равноправны: текст, который стал «чище»
@@ -27,7 +27,7 @@ license: MIT
27
27
  - **Точечная правка** («убери только канцелярит», «исправь тире»): работай только
28
28
  с названной проблемой, остальное не трогай.
29
29
  - **Свой черновик** (без просьбы, молча): всё, что ты пишешь пользователю
30
- по-русски сам, перед отправкой проходит по таблице жёстких запретов. Это одна
30
+ по-русски сам, в чат или в файл, перед отправкой проходит по таблице жёстких запретов. Это одна
31
31
  вычитка своего текста, а не сеанс редактуры: сканер не запускается, каталог не
32
32
  открывается, отчёта нет, скилл не упоминается. Чужой текст молча не правится
33
33
  никогда. Чужое это сообщение пользователя, содержимое файла, цитата, выдача
@@ -90,7 +90,9 @@ JOIN (убрать дублирование соседних фраз), SPLIT (
90
90
  Осмысленную триаду («пришёл, увидел, победил»), намеренный повтор (анафора),
91
91
  единственное авторское тире, терминологическое «является», формальный регистр
92
92
  делового письма, структуру длинного лонгрида, содержательное противопоставление
93
- («не просто X, а Y», где X и Y правда разные услуги) оставляй. В научном тексте
93
+ («не просто X, а Y», где X и Y правда разные услуги) оставляй. Риторические вопросы
94
+ и общее «не X, а Y» у людей встречаются не реже, чем у моделей: это не маркеры
95
+ (каталог, «Не маркеры»). В научном тексте
94
96
  «является», кальки-связки, канцелярит, «данный», «таким образом» и тире это норма
95
97
  регистра, измерено на 35 158 аннотациях AINL-Eval: без жанровой поправки скилл
96
98
  ловил людей чаще, чем машины. Хорошо отредактированный человеческий текст тоже
@@ -169,6 +171,9 @@ JOIN (убрать дублирование соседних фраз), SPLIT (
169
171
  - причина и последовательность: соседство фактов остаётся соседством, а не выводом;
170
172
  - числа вместе с единицами, выборкой, условиями и предметом сравнения;
171
173
  - авторское суждение, личный опыт и объяснение механизма ровно в объёме источника;
174
+ - швы после удалений (каталог #65): «этот», «саму X», «вторая», «как сказано выше»
175
+ указывают на то, что осталось в тексте; склейка не повторяет саму себя; одна
176
+ и та же оговорка не закрывает три абзаца подряд;
172
177
  - практическая функция: действие читателя, предложение, ограничение, срок, контакт;
173
178
  - рамка: когда в тексте названы клиент, партнёр или человек, сравни, как они
174
179
  выглядят до и после. Снятая пустая подводка порой была единственным смягчением,
@@ -1,5 +1,5 @@
1
1
  interface:
2
2
  display_name: "Humanizer RU"
3
- short_description: "Убирает следы нейросети из русского текста: 64 паттерна, 21 жёстких банов, голос автора."
3
+ short_description: "Убирает следы нейросети из русского текста: 67 паттернов, 21 жёстких банов, голос автора."
4
4
  policy:
5
5
  allow_implicit_invocation: true
@@ -4,6 +4,22 @@
4
4
  действия. Сам текст сохраняется. Если проблем не найдено, сообщи это без выдуманных
5
5
  находок. Аудит не определяет авторство текста и не обещает обход детекторов.
6
6
 
7
+ ## Авторство не устанавливается
8
+
9
+ Вопрос «писал ли это ИИ», «вникал ли автор» аудит не решает, даже если пользователь
10
+ спросил именно так. Скажи одной строкой, что скилл измеряет текст, а не авторство,
11
+ и дай находки по тексту. Не бывает находками: метаданные файла (программа,
12
+ даты создания и правки), способ вёрстки («HTML, напечатанный из Chrome»),
13
+ рассуждения «человек так не работает» и вердикт о «модели производства». Правило держится
14
+ и в следующих ходах: после отказа «докажи, что это нейрослоп» просьба «ну а
15
+ по маркерам?» не повод вынести тот же вердикт мягче («архитектура нейросетевая,
16
+ фактура человеческая»). Отказывай без нотаций: не объясняй пользователю, что
17
+ его просьба сама похожа на слоп, и не начинай с «результат тебе не понравится».
18
+ Шаблон
19
+ вёрстки глав это дизайн-система, а не почерк модели. Не маркеры и сами по себе,
20
+ по замеру на корпусах (`eval/MODERN-SLOP.md`, «Не маркеры» в каталоге):
21
+ риторические вопросы, общее «не X, а Y», длинное тире в свёрстанном документе.
22
+
7
23
  ## Машинная проверка, если доступна
8
24
 
9
25
  Сканер находится внутри скилла: `scripts/scan.py`. Для существующего файла:
@@ -19,7 +35,10 @@ python3 <папка-скилла>/scripts/scan.py <файл> --json
19
35
 
20
36
  Код завершения 0 или 1 с валидным отчётом означает выполненный анализ: 1 может
21
37
  обозначать найденные сигналы. Ошибка запуска, зависимостей или разбора отчёта
22
- означает, что измерения нет. Не устанавливай зависимости автоматически.
38
+ означает, что измерения нет. Не ставь пакеты в систему (`pip install`) без
39
+ спроса. Упал на нехватке зависимостей, а `uvx` есть в PATH: запусти то же через
40
+ `uvx ru-humanizer` с теми же аргументами. Он берёт пакет во временный кэш uv и
41
+ систему не трогает, спрашивать для этого не нужно.
23
42
 
24
43
  Если сканер не запускался или недоступен, прямо отметь это и продолжи вручную.
25
44
  Не оценивай балл на глаз, даже с пометкой «примерно». Если отчёт получен,
@@ -38,6 +57,21 @@ python3 <папка-скилла>/scripts/scan.py <файл> --json
38
57
  локальное действие. Если все фрагменты KEEP, сообщи, что полезных правок нет.
39
58
  Вопросы о невидимом контексте добавляй только когда от ответа зависит конкретная
40
59
  предлагаемая правка; так и обозначай их, отдельно от найденных ошибок.
60
+
61
+ Разбор длинного документа или рецензия для третьего лица дополняются двумя
62
+ короткими блоками, если в них есть что сказать: что выдержало проверку (факты,
63
+ источники, оставленные на виду расхождения) и что недостатком не является
64
+ (артефакты извлечения текста из PDF вроде «со труд ники», декларированные
65
+ AI-иллюстрации, норма жанра). Симметрия защищает автора от ложного обвинения.
66
+ Число без выборки или условия (каталог #66) не правь: задай автору вопрос «при
67
+ каком условии получена эта цифра?», первоисточника у тебя нет.
68
+
69
+ Сам разбор это твой русский текст, а не цитата: таблица жёстких запретов из
70
+ SKILL.md к нему применяется целиком, а с ней и #54: без «вот честный аудит»,
71
+ «чтобы разбор был честным», «для симметрии». То же для файлов, которые ты
72
+ пишешь по-русски: рецензия в PDF или .md это тоже твой текст. Длинного тире в отчёте нет («первая
73
+ половина - автор» пишется «первая половина от автора» или через двоеточие),
74
+ цитаты из исходника при этом приводятся как есть.
41
75
  Объём ответа определяется числом полезных действий, а не обязательными разделами.
42
76
 
43
77
  Если пользователь просит разбор по каталогу, прочитай нужный раздел
@@ -48,7 +48,7 @@ davinci-003). Метод и полные таблицы: `eval/AINL-CALIBRATION.
48
48
  Общая граница у всех замеров одна: генераторы 2023-2024 годов. Ни одной модели
49
49
  2025-2026 годов по-русски не измерено.
50
50
 
51
- ## Каталог: 64 паттерна AI-генерации в русском
51
+ ## Каталог: 67 паттернов AI-генерации в русском
52
52
 
53
53
  ### A. Контентные (1-5)
54
54
 
@@ -178,7 +178,7 @@ davinci-003). Метод и полные таблицы: `eval/AINL-CALIBRATION.
178
178
 
179
179
  ### G. Структурные (34-35)
180
180
 
181
- **34. Разорванные абзацы.** Абзацы AI слабо связаны. Можно переставить местами, и ничего не изменится. Нет обратных ссылок, нет причинно-следственных связок. AI предпочитает дискурсивные связи Elaboration и Explanation, люди чаще используют Contrast и Concession (2510.26124). Практически: добавлять «но», «с другой стороны», «впрочем»: это меняет дискурсивную структуру, а не только стилистику. Свяжи: каждый следующий абзац должен вытекать из предыдущего. Для длинных текстов (>500 слов) обязательны обратные ссылки: «Как уже говорил выше...», «Повторюсь, но...», «К этому вернёмся через минуту», «Помните, в начале я упоминал...», «Вот тут-то и пригодится то, что...». AI теряет coherence в длинных нарративах: «narrative drift» документированная проблема. Человек помнит начало истории, AI забывает.
181
+ **34. Разорванные абзацы.** Абзацы AI слабо связаны. Можно переставить местами, и ничего не изменится. Нет обратных ссылок, нет причинно-следственных связок. AI предпочитает дискурсивные связи Elaboration и Explanation, люди чаще используют Contrast и Concession (2510.26124). Практически: добавлять «но», «с другой стороны», «впрочем»: это меняет дискурсивную структуру, а не только стилистику. Свяжи: каждый следующий абзац должен вытекать из предыдущего. Для длинных текстов (>500 слов) обязательны обратные ссылки: «Как уже говорил выше...», «Повторюсь, но...», «К этому вернёмся через минуту», «Помните, в начале я упоминал...», «Вот тут-то и пригодится то, что...». AI теряет coherence в длинных нарративах: «narrative drift» документированная проблема. Человек помнит начало истории, AI забывает. В экспертном лонгриде тот же дрейф виден по голосу: первая половина от автора, с репликами и случаями из практики, вторая превращается в регламент («Назначьте ответственного», «Отдельно проверяйте») без единой авторской детали. Проверка: найди последнюю реплику от первого лица; если после неё больше трети текста, это сигнал. Реплики за автора не дописывай: отметь, где голос пропал, и спроси, есть ли у него там наблюдение. Осознанный переход в практическую часть тоже бывает, решает автор.
182
182
 
183
183
  **35. Галлюцинации.** AI уверенно утверждает ложное: несуществующие цитаты, неправильные даты, выдуманные факты. Проверь каждый конкретный факт. Не можешь подтвердить? Убери или пометь как неподтверждённое.
184
184
 
@@ -188,6 +188,8 @@ davinci-003). Метод и полные таблицы: `eval/AINL-CALIBRATION.
188
188
 
189
189
  **37. Вылизанная типографика.** AI ставит все тире, кавычки, пробелы идеально по ГОСТу. Человек в мессенджере пишет дефис вместо тире, не ставит пробел перед скобкой, забывает точку в конце. Для неформальных текстов не исправляй типографику до идеала. Пусть местами будет дефис вместо тире, пусть скобка прижмётся к слову. Это не ошибка, это почерк.
190
190
 
191
+ Обратный признак, который тоже проверяется глазами: расхождения источников, оставленные на виду («расхождение не устранено догадкой»), и разные даты сверки у разных источников. Слоп такие швы сглаживает, живая многопроходная сверка их показывает. Не правь их и не выдавай за небрежность.
192
+
191
193
  ### I. Паттерны убеждения (38-42)
192
194
 
193
195
  **38. Негативные параллелизмы.** «Не просто инструмент, а партнёр». «Не только ускоряет, но и трансформирует». AI обожает эту конструкцию, она есть в 80%+ текстов GPT. HARD BAN. Скажи прямо что имеешь в виду: «Это партнёр» или «Ускоряет и трансформирует». Без «не просто / не только».
@@ -221,6 +223,8 @@ davinci-003). Метод и полные таблицы: `eval/AINL-CALIBRATION.
221
223
  До: «1. X: один делает, второй проверяет. Подходит для... 2. Y: один раздаёт, остальные делают. Подходит для... 3. Z: передаёт по цепочке. Подходит для...»
222
224
  После: «1. X: один пишет, второй ловит косяки. Берёте туда, где цена ошибки высокая. 2. Y: тут проще, конвейер. 3. Z: а вот это для хаоса. Задачи сыпятся, кто свободен, тот и хватает.»
223
225
 
226
+ Тот же скелет бывает у абзацев прозы без всякого списка: тезис, число со ссылкой, оговорка, вывод, и так три-четыре абзаца подряд, часто с однотипным зачином («Модель подыгрывает себе.» / «Контролёр уступает давлению.» / «Спор ради спора тоже не помогает.»). Проверка та же: прочитай подряд первые предложения абзацев. Сломай порядок в одном-двух, переставив их же части: начни с числа, а тезис дай после него. Новых фраз для этого не нужно.
227
+
224
228
  ### K. Хеджирование и специфика (46-48)
225
229
 
226
230
  **46. Модальная неопределённость.** AI хеджирует через «может» в каждом предложении: «может стать», «может повлиять», «способен обеспечить», «призван решить». Это не осторожность автора, это привычка модели снижать категоричность. Если утверждение верно, утверждай. Если неверно, не пиши. «Может быть полезным» не несёт информации. «Ускорило вдвое» несёт. Порог: больше 1 «может/способен/призван» на 100 слов вне контекста прогнозов = маркер. Не путать с паттерном 25 (избыточные оговорки типа «в определённом смысле») и паттерном 41 (отказы от ответственности типа «хотя информация может быть неполной»). Модальное «может»: систематическое хеджирование КАЖДОГО утверждения через одну конструкцию.
@@ -291,6 +295,35 @@ davinci-003). Метод и полные таблицы: `eval/AINL-CALIBRATION.
291
295
 
292
296
  **64. Нет обращения к читателю.** Человек говорит с читателем напрямую чаще ИИ: порядковое среднее 0.28 против 0.07 (собственная проза статьи передаёт это как «28% против 7%»). В абсолютных числах это один из НАИМЕНЬШИХ разрывов таблицы, так что признак вспомогательный: сам по себе он ничего не доказывает. Вопрос в лоб, «представьте», «вы наверняка такое видели». Сигнал здесь - отсутствие, поэтому сканер его не считает: смотри глазами. Применяй только там, где формат обращение допускает: пост, рассылка, колонка, лендинг. В новость, документацию и научный текст не тащи. Это предложение автору, не автоправка: обращение меняет интонацию всего текста, вставлять его молча нельзя. Предложи одно-два места, решает автор.
293
297
 
298
+ ### O. Следы итеративной правки (65)
299
+
300
+ Эти следы оставляет не генерация, а цикл правок: агент переписывает документ по замечаниям рецензентов, людей или моделей, и каждое замечание закрывает отдельно. В разовых генерациях наших корпусов их почти нет (оговорка «Это X, а не Y» в конце абзаца: 0% у Claude и GPT-5.6, до 2% у остальных моделей, 0-0,2% у людей), а в исследовании на 5 800 слов после нескольких раундов рецензий их было девять. Скилл сам работает правкой и сам удаляет, поэтому паттерн проверяется и на собственном чистовике (шаг 3 SKILL.md).
301
+
302
+ **65. Швы правки.** Четыре вида, обычно вместе:
303
+ - **оговорка-заплатка одной формулой:** «Это учебный пример, не исследование», «Это экстраполяция, а не аудит», «Это моя сборка, а не проверенная методика». По отдельности каждая верна, три и больше на текст читаются как оправдание вместо довода. Уточнение по существу (выборка, условия) оставь, а в половине случаев разбери обёртку и впиши условие прямо в утверждение;
304
+ - **обрыв ссылки:** слово-антецедент удалено, а отсылка к нему осталась («Саму усталость эти проценты не измеряют», хотя «усталость» вычищена абзацем выше; «Вторая возможность» без первой). Замени отсылку словом, которое реально стоит рядом, или верни антецедент;
305
+ - **самоповтор после склейки:** «затраты на подготовку, проверку и переделки с учётом проверок и переделок». Сократи хвост;
306
+ - **одна мысль в каждой главе заново:** фраза, закрывшая замечание в одном месте, дословно повторена в трёх-четырёх разделах и на плашках. Оставь её там, где она доказана, в остальных местах сошлись на главу или убери. Сквозной мотив (название поста на обложке и в финале) это решение автора, спроси.
307
+
308
+ Отличие от #41: там размытая оговорка вместо знания, здесь точная оговорка, размноженная одной формулой. От #26: водянистость размазывает мысль в одном месте, здесь мысль повторена в разных. Механически повтор фразы между абзацами ловится сканером частично; обрыв ссылки только чтением.
309
+
310
+ ### P. Числа и проверяемость (66-67)
311
+
312
+ Не признаки машины, а места, где текст подводит читателя. Нашлись при независимой рецензии документа с 45 источниками; к стилю отношения не имеют, поэтому правятся всегда, где их видно, и не штрафуются сканером.
313
+
314
+ **66. Точность, которую нельзя проверить.** Деталь подана как точная, а читатель проверить её не может: «пост №656» (номер не виден в веб-версии канала), «Doc. 46» (номер записи есть только в платной базе суда), «2 077 решений на 23.09» без ссылки на срез. Сюда же число из первоисточника без условия, при котором оно получено: «80,3% против 43,4%» верно только для одного типа возражений, «17% против 5%» это разбивка, а заголовочная цифра источника 13,6%. Точность убеждает сильнее, чем позволяет основание. Родня #35, но факт может быть верным: проблема в том, что проверить его нельзя. Не выдумывай ни условие, ни ссылку (факт-замок): спроси автора, откуда деталь и при каком условии получено число, или предложи формулировку, которая не обещает больше, чем есть («в этом канале», «в эксперименте с полным опровержением»).
315
+
316
+ **67. Разнобой числовых форматов.** В одном расчёте «10 000» с пробелом и «8,928» с запятой по-английски, «$186» перед числом, «1,000» как тысяча. Русский читатель видит в «$8,928 млн» восемь миллиардов. Приведи к одной норме: пробел между разрядами, запятая как десятичный разделитель, валюта словом или знаком после числа («8,9 млн долларов»), лишние знаки после запятой округли, если точность не нужна для вывода. Встречается у 0-3% текстов и людей, и моделей, это ошибка вёрстки, а не почерк.
317
+
318
+ ## Не маркеры (замерено)
319
+
320
+ Расхожие «признаки ИИ», которые на наших корпусах не отделяют модели от людей (`eval/MODERN-SLOP.md`, тексты 60-600 слов). Их нельзя предъявлять как довод, ни в аудите, ни в правке:
321
+ - **риторические вопросы:** 4 и больше на 1000 слов у 37,5% авторов Пикабу и у 9-25% текстов большинства моделей;
322
+ - **общее «не X, а Y»:** у людей 10-11% текстов, у моделей LLMTrace 8-12%. Маркер только узкий: «не просто», «не только», и число таких оборотов на текст, а не одно наличие (#38);
323
+ - **длинное тире в свёрстанном документе:** типограф вёрстки ставит его сам, для изданного PDF это норма, в посте и чате решает автор;
324
+ - **шаблон вёрстки** (одинаковые главы, карточки, плашки): это дизайн-система документа, а не скелет абзацев (#45);
325
+ - **метаданные файла и способ сборки** (PDF из браузера, одна дата сохранения): к тексту отношения не имеют.
326
+
294
327
  ---
295
328
 
296
329
  ## Быстрый сканер: слова-маркеры AI
@@ -22,6 +22,7 @@ from .markers import (
22
22
  scan_markers,
23
23
  )
24
24
  from .morphology import MorphStats, morph_stats, morph_verdict
25
+ from .repeats import RepeatStats, repeat_stats
25
26
  from .structure import StructureStats, structure_stats, structure_verdict
26
27
  from .score import ScoreResult, cleanliness_score
27
28
 
@@ -32,6 +33,7 @@ __all__ = [
32
33
  "MorphStats",
33
34
  "StructureStats",
34
35
  "LexicalStats",
36
+ "RepeatStats",
35
37
  "MarkerHit",
36
38
  "ScoreResult",
37
39
  "cleanliness_score",
@@ -39,6 +41,7 @@ __all__ = [
39
41
  "morph_stats",
40
42
  "structure_stats",
41
43
  "lexical_stats",
44
+ "repeat_stats",
42
45
  "scan_hard_bans",
43
46
  "scan_markers",
44
47
  "mask_foreign",
@@ -56,6 +59,7 @@ class Report:
56
59
  morph: MorphStats
57
60
  structure: StructureStats
58
61
  lexical: LexicalStats
62
+ repeats: RepeatStats
59
63
 
60
64
  @property
61
65
  def hard_ban_count(self) -> int:
@@ -75,6 +79,7 @@ class Report:
75
79
  "morph": self.morph.as_dict(),
76
80
  "structure": self.structure.as_dict(),
77
81
  "lexical": self.lexical.as_dict(),
82
+ "repeats": self.repeats.as_dict(),
78
83
  }
79
84
 
80
85
 
@@ -103,4 +108,5 @@ def analyze(text: str) -> Report:
103
108
  morph=morph_stats(prose),
104
109
  structure=structure_stats(prose),
105
110
  lexical=lexical_stats(prose),
111
+ repeats=repeat_stats(prose),
106
112
  )
@@ -108,6 +108,7 @@ def scan(text: str, genre: str | None = None) -> dict:
108
108
  "morph": rep.morph.as_dict(),
109
109
  "structure": rep.structure.as_dict(),
110
110
  "lexical": rep.lexical.as_dict(),
111
+ "repeats": rep.repeats.as_dict(),
111
112
  }
112
113
 
113
114
 
@@ -0,0 +1,139 @@
1
+ """Повтор фразы между абзацами: модель пересказывает сама себя.
2
+
3
+ Признак: одна и та же цепочка из REPEAT_N словоформ дословно стоит в двух
4
+ разных абзацах. Так пишут Llama-3.3 и YandexGPT: вывод повторяет вступление,
5
+ пункт повторяет подводку. У людей тоже бывает, прежде всего в новостях (лид
6
+ пересказан в теле заметки), в справках с длинными официальными названиями и в
7
+ лонгридах.
8
+
9
+ Почему это заметка, а не штраф. Подбор на LLMTrace, Пикабу и длинных
10
+ человеческих текстах (eval/MODERN-SLOP.md, раздел про повтор фраз) показал
11
+ потолок пользы: даже бесконечный штраф вывел бы из полосы «чисто» 2,8% текстов
12
+ Llama-3.3 и меньше процента у остальных моделей, потому что текст с такими
13
+ повторами почти всегда уже задет другими признаками (медиана счёта у Llama с
14
+ повтором 58, без повтора 80). Людей при этом задело бы столько же или больше. Поэтому в счёт повтор не входит, сканер
15
+ только показывает сами фразы: это готовая подсказка для правки.
16
+
17
+ Токенизация своя и повторена в docs/scan.js байт в байт, как у MATTR:
18
+ - абзац это непустая строка (у Пикабу и в чатах абзацы идут через один
19
+ перевод строки, пустая строка тут не обязательна);
20
+ - строки-заголовки Markdown («## ...») не считаются;
21
+ - цитаты в «ёлочках», „лапках“ и "прямых" кавычках вырезаются: чужие слова
22
+ автор повторяет законно;
23
+ - слово это кириллица с внутренними дефисами после нижнего регистра и ё → е;
24
+ - латиница, цифры, скобки, кавычки и знаки конца предложения рвут цепочку:
25
+ n-грамма не перескакивает через «2020» и через точку;
26
+ - n-грамма, где меньше REPEAT_MIN_CONTENT полнозначных слов («и в то же
27
+ время, как и»), не считается: это грамматика, а не фраза;
28
+ - считаются только первые REPEAT_MAX_TOKENS словоформ: длинный текст копит
29
+ повторы просто длиной, и без окна признак на лонгридах людей шумит.
30
+
31
+ Перекрывающиеся n-граммы одного повтора склеиваются: «мы живём в мире, где
32
+ всё меняется» это одна фраза, а не две шестёрки.
33
+ """
34
+
35
+ from __future__ import annotations
36
+
37
+ import re
38
+ from dataclasses import dataclass, field
39
+
40
+ REPEAT_N = 6
41
+ REPEAT_MAX_TOKENS = 600
42
+ REPEAT_MIN_CONTENT = 2
43
+ # Сколько разных фраз нужно для заметки. Одна повторённая фраза в первых 600
44
+ # словах у людей встречается в 1-3% коротких текстов и в 4-16% длинных, две в
45
+ # 0,2-0,7% коротких и 1-5% длинных.
46
+ REPEAT_MIN_PHRASES = 2
47
+
48
+ # Служебные слова: предлоги, союзы, частицы, местоимения, связка. N-грамма из
49
+ # одних таких слов («что это может быть так») фразой не считается.
50
+ FUNCTION_WORDS = frozenset("""
51
+ а б бы в во вот да для до же за и из или им их к ко как ли либо между на над не ни но о об обо
52
+ от ото по под при про с со так там то тоже только у уже чем что чтобы это этот эта эти этого этой
53
+ этом этим тот та те того той том тем тех я ты он она оно мы вы они его ее их ему ей нам вам
54
+ меня тебя нас вас мне тебе себя себе свой своя свое свои своего своей своих своим который которая
55
+ которое которые которого которой которых котором которым был была было были быть есть будет будут
56
+ бывает если когда где куда чтоб все весь вся всего всех всем также еще уж даже лишь нет
57
+ очень более менее можно нужно надо может могут мой моя мое мои наш наша наше наши ваш ваша ваше
58
+ ваши кто чего чему ним ней них него нее нему
59
+ """.split())
60
+
61
+ _TOKEN_RE = re.compile(r"[а-яё]+(?:-[а-яё]+)*|[a-z0-9]+|[.!?…;:()\[\]\"«»“”„]")
62
+ _QUOTE_RE = re.compile(r"«[^«»\n]*»|„[^„“\n]*“|\"[^\"\n]*\"")
63
+ # Пробелы явные, не \s: у Python и JS классы \s расходятся на редких символах.
64
+ _HEADING_RE = re.compile(r"^[ \t]*#{1,6}[ \t]")
65
+
66
+
67
+ @dataclass
68
+ class RepeatStats:
69
+ tokens: int # словоформ просмотрено (не больше REPEAT_MAX_TOKENS)
70
+ phrases: list[str] = field(default_factory=list) # разные повторённые фразы, в порядке текста
71
+
72
+ def as_dict(self) -> dict:
73
+ return {"tokens": self.tokens, "phrases": list(self.phrases)}
74
+
75
+
76
+ def _runs(line: str) -> list[list[str]]:
77
+ """Цепочки подряд идущих кириллических словоформ строки."""
78
+ runs: list[list[str]] = []
79
+ cur: list[str] = []
80
+ for tok in _TOKEN_RE.findall(line.lower()):
81
+ if "а" <= tok[0] <= "я" or tok[0] == "ё":
82
+ cur.append(tok.replace("ё", "е"))
83
+ else:
84
+ if cur:
85
+ runs.append(cur)
86
+ cur = []
87
+ if cur:
88
+ runs.append(cur)
89
+ return runs
90
+
91
+
92
+ def repeat_stats(text: str) -> RepeatStats:
93
+ """Повторённые между абзацами фразы. Порт в docs/scan.js обязан дать тот же список."""
94
+ text = _QUOTE_RE.sub(" . ", text)
95
+ budget = REPEAT_MAX_TOKENS
96
+ where: dict[str, set[int]] = {} # n-грамма -> номера строк
97
+ order: list[tuple[int, int, list[str]]] = [] # (строка, сквозная позиция, цепочка)
98
+ pos = 0
99
+ for li, line in enumerate(text.split("\n")):
100
+ if budget <= 0:
101
+ break
102
+ if _HEADING_RE.match(line):
103
+ continue
104
+ for run in _runs(line):
105
+ run = run[:budget]
106
+ budget -= len(run)
107
+ for i in range(len(run) - REPEAT_N + 1):
108
+ key = " ".join(run[i:i + REPEAT_N])
109
+ where.setdefault(key, set()).add(li)
110
+ order.append((li, pos + i, run[i:i + REPEAT_N]))
111
+ pos += len(run) + 1 # разрыв: следующая цепочка не продолжает эту
112
+ if budget <= 0:
113
+ break
114
+ seen = REPEAT_MAX_TOKENS - max(budget, 0)
115
+
116
+ def repeated(words: list[str]) -> bool:
117
+ key = " ".join(words)
118
+ return (len(where[key]) >= 2
119
+ and sum(1 for w in words if w not in FUNCTION_WORDS) >= REPEAT_MIN_CONTENT)
120
+
121
+ phrases: list[str] = []
122
+ keys: set[str] = set()
123
+ cur: list[str] = []
124
+ cur_key = ""
125
+ prev = (-1, -2)
126
+ for li, p, words in order:
127
+ if not repeated(words):
128
+ continue
129
+ if prev[0] == li and prev[1] == p - 1 and cur:
130
+ cur.append(words[-1]) # та же фраза тянется дальше
131
+ else:
132
+ if cur and cur_key not in keys:
133
+ keys.add(cur_key)
134
+ phrases.append(" ".join(cur))
135
+ cur, cur_key = list(words), " ".join(words)
136
+ prev = (li, p)
137
+ if cur and cur_key not in keys:
138
+ phrases.append(" ".join(cur))
139
+ return RepeatStats(tokens=seen, phrases=phrases)
@@ -19,6 +19,7 @@ from .lexical import LEX_MIN_TOKENS
19
19
  from .markers import (GENRE_MUTED_BANS, GENRE_MUTED_CATEGORIES,
20
20
  effective_hard_bans, mute_by_genre)
21
21
  from .morphology import NV_TARGET
22
+ from .repeats import REPEAT_MIN_PHRASES
22
23
  from .structure import (
23
24
  LISTICLE_MIN_ITEMS,
24
25
  LISTICLE_SHARE_AI,
@@ -92,6 +93,10 @@ LEX_THRESHOLD = 0.955
92
93
  LEX_SLOPE = 1000
93
94
  LEX_PENALTY_MAX = 15
94
95
  LEX_MUTED_GENRES = frozenset({"news", "academic", "legal"})
96
+ # Повтор фразы между абзацами (repeats.py): заметка без штрафа. Даже бесконечный
97
+ # штраф вывел бы из «чисто» 0,8-2,8% текстов одной Llama-3.3, у людей потери
98
+ # того же порядка (eval/MODERN-SLOP.md). Показываем не больше REPEAT_SHOW фраз.
99
+ REPEAT_SHOW = 3
95
100
  # Потолок штрафа за номинальность. Именованный, потому что браузерный сканер
96
101
  # морфологии не имеет и объявляет ровно эту величину как неизмеренную.
97
102
  NV_PENALTY_MAX = 8
@@ -267,5 +272,14 @@ def cleanliness_score(report, genre: str | None = None) -> ScoreResult:
267
272
  f"в {words} слов, остальные {100 - share:.0f}% что-нибудь да используют. "
268
273
  "Цель не ноль, а типичная для жанра частота: вычищать дальше незачем")
269
274
 
275
+ # Заметка о повторе фраз между абзацами. Почему не штраф: комментарий у REPEAT_SHOW.
276
+ phrases = report.repeats.phrases
277
+ if len(phrases) >= REPEAT_MIN_PHRASES:
278
+ shown = ", ".join(f"«{p}»" for p in phrases[:REPEAT_SHOW])
279
+ more = f" и ещё {len(phrases) - REPEAT_SHOW}" if len(phrases) > REPEAT_SHOW else ""
280
+ notes.append(
281
+ f"повтор фраз между абзацами: {shown}{more}. В счёт не входит: у людей так "
282
+ "бывает в новостях и справках. Если повтор не нарочный, оставьте фразу в одном месте")
283
+
270
284
  final = max(0, min(100, round(score)))
271
285
  return ScoreResult(score=final, band=_band(final), penalties=penalties, notes=notes)
@@ -30,6 +30,7 @@ try:
30
30
  GENRES, effective_hard_bans, marker_verdict,
31
31
  mute_by_genre)
32
32
  from humanizer_metrics.morphology import morph_verdict
33
+ from humanizer_metrics.repeats import REPEAT_MAX_TOKENS, REPEAT_N
33
34
  from humanizer_metrics.structure import structure_verdict
34
35
  except ImportError as exc:
35
36
  print(f"[ошибка] не хватает зависимостей сканера ({exc.name}); балла не будет.\n"
@@ -205,6 +206,16 @@ def main() -> int:
205
206
 
206
207
  print("Структура (уровень документа):")
207
208
  print(f" {structure_verdict(rep.structure)}")
209
+ phrases = rep.repeats.phrases
210
+ if phrases:
211
+ print(f" повтор фраз между абзацами: {len(phrases)} (цепочки от {REPEAT_N} слов в первых "
212
+ f"{REPEAT_MAX_TOKENS} словоформах, в счёт не входит)")
213
+ for ph in phrases[:5]:
214
+ print(f" «{ph}»")
215
+ if len(phrases) > 5:
216
+ print(f" … и ещё {len(phrases) - 5}")
217
+ else:
218
+ print(f" повторов фраз между абзацами нет (цепочки от {REPEAT_N} слов)")
208
219
 
209
220
  if fdiff is not None:
210
221
  print()