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

Не то чтобы я часто балуюсь текстовым нейрослопом, но сама идея того, что твой текст могут им объявить чисто случайно или в следствии того, что ты отредактировал иишкой его часть, меня не сильно радует. Поэтому в этой статье мы разберемся, как собственно эти метки работают, и как с ними бороться.
Как работают текстовые ИИ-вотермарки
Поначалу, когда я впервые услышал о текстовых вотермарках, я подумал, что LLM просто вставляет в текст всякие скрытые символы или там длинные тире, но этот метод донельзя дубовый и очевидно, что обходится на раз-два. На самом деле это работает так.
LLM это авторегрессионная вероятностная модель. На каждом шаге она выбирает следующий токен из своего большого словаря (обычно от десятков до сотен тысяч токенов) опираясь на изначальный промпт и предыдущие сгенерированные ею токены. Сначала модель отбирает топ-K наиболее вероятных токенов, а затем выбирает среди них один единственный с учетом его вероятности. Вотермаркинг же смещает распределение вероятностей токенов: слова, которые обычно редко встречаются в тексте, начинают появляться чаще, и наоборот.

Для человека заметить разницу практически невозможно, но для машины, знающей реальное распределение слов/токенов, это не составит труда.

Собственно вот видео, в котором объясняется, как работает вотермаркинг в Gemini. Claude работает точно так же.
В видео это объяснено довольно поверхностно, поэтому объясню немного детальнее.
У нас есть фиксированный секретный ключ K. При генерации текста смотрим на последние m токенов, и на основании ключа K и этих токенов при помощи хэш функции разбиваем словарь на две половины, условно красную и зеленую. И следующий токен выбирает только из зеленой части. Дальше переходим к выбору следующего токена. Поскольку набор из m последних токенов у нас теперь другой, окраска словаря при выборе следующего токена у нас также будет совершенно другая, поэтому не возникнет такой ситуации, что каждый раз выбираются только одни и те же токены.
А при проверке текста, зная ключ и зная предыдущие токены, просто смотрим, из какой половины раскраски происходит наш текущий токен - зеленой или красной. И вот, если раз за разом на протяжении 10-20-30 токенов стабильно получается так, что токен всегда из зеленой части, очень похоже на то, что текст сгенерирован. Причем даже если какие-то слова в тексте менять, что-то выкидывать, добавлять и т.д., все равно останется множество подпоследовательностей, в которых эти зеленые токены будут идти подряд один за другим. Поэтому от вотермарки так сложно избавиться.
Как их обходить
Вполне очевидно, что придется либо жестко редактировать либо переписывать текст другими словами. Что предлагаю я.
Для начала предобработать исходный текст, чтобы удалить самые очевидные признаки сгенерированного ИИ контента: длинные тире, символы нулевой ширины, нестандартные пробелы и т. д.
Следующие три шага направлены на борьбу с самой сутью вотермаркинга - специфическим распределением токенов. Вот как это работает:
Отправить текст в переводчик (например, Google Переводчик) и перевести его на промежуточный язык
Перевести текст обратно с промежуточного языка на исходный
При необходимости переписать текст с помощью LLM, которая гарантированно не добавляет водяные знаки в свои ответы (например, модели с открытыми весами вроде DeepSeek-v4). Это поможет сделать итоговый текст более читаемым (если его качество снизилось в результате двойного перевода) и удалить оставшиеся следы вотермаркинга, если они каким-то образом сохранились
Вдохновившись этой идеей, я разработал простое приложение, которое автоматизирует этот процесс. В нём можно выбрать, какие именно шаги использовать, и настроить каждый из них отдельно (например, выбрать конкретный переводчик или модель LLM для перефразирования).
Оно создано на основе Streamlit и LangChain, GUI выглядит вот так:

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

Проект есть на гитхабе, лежит по этой ссылке. И как всегда, предложения по доработке и исправлению багов приветствуются.