Короткий ответ: что переживает водяной знак текста от копирования
Универсального текстового водяного знака, который сохраняется после любой редакторской правки, очистки, перевода и парафраза LLM, нет. Невидимые Unicode-символы обычно подходят для обнаружения почти неизменной копии. Лексические маркеры, основанные на выборе слов, могут пережить отдельные правки, но зависят от формулировок. Смысловые маркеры работают на уровне статистики и структуры, поэтому дают вероятностный сигнал и требуют калибровки детектора.
При обычном копипасте лучше всего сохраняется маркер, встроенный в исходную строку. После преобразования HTML в plain text, фильтрации непечатаемых символов или явного удаления zero-width-кодов Unicode-сигнал пропадает. Замена синонимов, перестановка предложений и сокращение текста ослабляют лексический слой. Перевод и переписывание через LLM обычно меняют символы, слова и синтаксис сразу, поэтому низкоуровневые маркеры теряются.
Водяной знак помогает обнаружить связь текста с размеченной версией или определённым процессом генерации. Он не останавливает копирование, не устанавливает автора автоматически и не заменяет исходные файлы, хеши, журналы обработки и юридическую оценку. Технический результат проверки нужно формулировать точно: маркер найден, текст совпадает с размеченной версией, либо детектор оценил вероятность происхождения.
Какая стойкость нужна именно в вашем сценарии
Сначала нужно описать канал обработки и предполагаемого нарушителя. Случайный копипаст, редакторская правка и намеренная очистка требуют разных защитных слоёв.
- Случайное копирование. Пользователь переносит текст из редактора в CMS или документ без намерения удалить служебные символы. Здесь может хватить Unicode-маркера и проверки исходной строки.
- Редактура человеком. Автор исправляет опечатки, меняет кавычки, сокращает предложения и переставляет абзацы. Символьный маркер часто исчезает, а лексический сигнал сохраняется только в незатронутых местах.
- Автоматическая очистка. Пайплайн удаляет управляющие и форматирующие символы, нормализует пробелы, переводит HTML в plain text или сериализует текст в другом формате. Zero-width-коды нужно считать уязвимым слоем.
- Намеренная атака. Нарушитель знает о маркере и применяет фильтр, синонимайзер, переводчик или LLM-парафраз. В таком сценарии детектор проверяют на false positive и false negative, а не на единичном удачном примере.
Для короткого сообщения доступно мало позиций, в которые можно распределить сигнал. Одна замена слова или удаление предложения может уничтожить значительную часть признаков. В статье из нескольких абзацев статистическая проверка получает больше наблюдений, но длинный материал проходит через большее число редакторских операций.
Главный вывод без маркетинговых обещаний
Водяной знак стоит считать одним слоем контроля происхождения. Для неизменного текста он может дать сильный технический признак. Для переписанного текста он превращается в оценку вероятности, а после полного пересказа может исчезнуть совсем.
Если задача связана с авторскими правами, сохраняйте исходный материал, дату создания, историю версий, хеши и логи доступа. Маркер помогает связать копию с записью в системе, но сам по себе не отвечает на вопросы о том, кто создал текст, кто им владел и какие права действовали в конкретной юрисдикции.
Похожую границу между техническим детектированием и выводами об источнике хорошо видно в разборе водяных знаков Claude: маркировка может сигнализировать о происхождении генерации, но её стойкость зависит от последующей обработки текста.
Что именно доказывает текстовый водяной знак
Проверка водяного знака состоит из четырёх частей: исходный текст, способ добавления маркера, процедура обнаружения и журнал преобразований. Если один элемент потерян, результат становится сложнее воспроизвести и интерпретировать.
Маркер, совпадение и доказательство происхождения
Фраза «маркер найден» означает, что детектор увидел ожидаемую последовательность символов, слов или статистических признаков. Это самый узкий и технически точный вывод.
Фраза «текст похож на размеченный источник» описывает результат сравнения. Здесь нужно указать алгоритм, версию исходного текста, порог похожести и список проверенных преобразований. Семантическое сходство не доказывает наличие именно вашего маркера: похожую формулировку мог создать другой автор.
Фраза «авторство подтверждено» требует дополнительных данных. Понадобятся исходные версии, надёжная фиксация времени, сведения о доступе к документу, история правок и правила конкретной правовой системы. Один положительный ответ детектора не заменяет эту цепочку.
Почему положительный результат не равен юридическому выводу
Юридическая сила технического сигнала зависит от способа его получения, сохранности исходных данных, воспроизводимости проверки и применимого законодательства. Один и тот же маркер может иметь разный вес в споре о публикации, в корпоративном аудите и в расследовании утечки.
Техническая система может показать, что копия содержит служебные символы, совпадает с конкретной версией или демонстрирует статистический профиль. Она не сообщает автоматически, кто вставил маркер и кто написал видимый текст. Скрытый идентификатор тоже не равен подтверждённой личности.
Для правового спора нужна консультация специалиста по конкретной юрисдикции. В статье можно описать технические ограничения, но нельзя заменить ими правовую оценку.
Какие данные стоит сохранять вместе с размеченным текстом
- исходную строку в оригинальной кодировке и нормализованную копию;
- версию алгоритма, словаря или модели;
- параметры встраивания, включая позицию и длину маркера;
- дату создания и идентификатор документа;
- криптографический хеш исходного материала;
- результат детектора, порог и confidence;
- журнал операций: копирование, очистка, перевод, сокращение, генерация;
- версии текста до и после каждого преобразования.
Хеш полезен для точного сравнения версии. Он меняется после одного символа, поэтому его нужно дополнять версионированием и журналом правок. Внутрь водяного знака лучше помещать непрозрачный идентификатор, а не адрес электронной почты, имя пользователя или другой чувствительный атрибут.
Три семейства текстовых водяных знаков
Различие между подходами определяется местом хранения информации. Unicode-маркер находится в кодовых точках строки, лексический маркер в выборе слов, смысловой сигнал в распределении токенов, структуре и статистических свойствах текста.
Невидимые Unicode-символы
Zero-width space, zero-width non-joiner, zero-width joiner и другие символы категории Cf не занимают видимого места. Их можно добавить между символами или в границах слов. Приложение, которое сохраняет исходную строку без фильтрации, может перенести такой сигнал вместе с текстом.
Преимущество подхода, отсутствие видимой правки. Ограничение, сильная зависимость от формата и очистки. Нормализация Unicode не обязана удалять каждый служебный символ, зато фильтр категорий, конвертация в plain text или регулярное удаление zero-width-кодов уничтожают сигнал предсказуемо.
Ключевые замены слов
Лексический маркер использует контролируемый выбор между вариантами: быстро и оперативно, проверить и верифицировать, сначала описать причину и сначала привести результат. Водяной знак можно кодировать последовательностью выбранных вариантов.
Такой слой не зависит от сохранения невидимых символов. Человек видит обычные слова, но редактор может заменить их ради стиля или точности. Техническая документация, короткое сообщение и текст с жёсткой терминологией дают мало допустимых вариантов, поэтому лексический маркер может ухудшить качество или стать заметным.
Смысловые маркеры на основе генерации
Смысловой подход задаёт ограничения во время генерации: разрешённые группы токенов, повторяемую структуру, распределение вариантов или другие статистические признаки. Детектор проверяет, чаще ли наблюдаемые признаки встречаются в размеченном тексте, чем ожидалось бы без маркера.
Чем выше уровень абстракции, тем труднее отделить маркер от обычного авторского стиля. Структура абзацев, частотность связок и выбор терминов встречаются в естественных текстах без специальной маркировки. После сильного парафраза сигнал может ослабнуть или смешаться с особенностями новой модели.
Подходы к маркировке, которые применяются для AI-текста, обсуждаются в отдельном разборе водяных знаков Anthropic. Для разработчика ключевой вопрос остаётся прежним: какие преобразования должен пережить сигнал и как система измеряет ошибки.
Невидимый Unicode-маркер: простой код, хрупкий сигнал
Учебный пример ниже кодирует короткий идентификатор в биты и заменяет нули и единицы двумя невидимыми символами. Начальная и конечная кодовые точки ограничивают полезную нагрузку. Это каркас для эксперимента, не промышленная защита.
Как встроить и извлечь маркер в Python
from base64 import urlsafe_b64decode, urlsafe_b64encode
ZERO = chr(0x200B)
ONE = chr(0x200C)
START = chr(0x2060)
END = chr(0x2061)
def encode_marker(value: str) -> str:
payload = urlsafe_b64encode(value.encode('utf-8')).decode('ascii')
bits = ''.join(f'{byte:08b}' for byte in payload.encode('ascii'))
body = ''.join(ONE if bit == '1' else ZERO for bit in bits)
return START + body + END
def embed(text: str, marker: str, position=None) -> str:
if position is None:
position = len(text) // 2
if not 0 <= position <= len(text):
raise ValueError('position is outside the text')
return text[:position] + marker + text[position:]
def extract(text: str):
start = text.find(START)
end = text.find(END, start + 1)
if start < 0 or end < 0:
return None
body = text[start + 1:end]
if not body or any(ch not in (ZERO, ONE) for ch in body):
return None
bits = ''.join('1' if ch == ONE else '0' for ch in body)
if len(bits) % 8:
return None
encoded = bytes(
int(bits[i:i + 8], 2)
for i in range(0, len(bits), 8)
).decode('ascii')
padding = '=' * (-len(encoded) % 4)
return urlsafe_b64decode((encoded + padding).encode('ascii')).decode('utf-8')
def detect(text: str) -> bool:
return extract(text) is not None
marker = encode_marker('doc-2026-09-06-v1')
marked = embed('Текст для эксперимента.', marker)
assert detect(marked)
assert extract(marked) == 'doc-2026-09-06-v1'
В примере Base64 нужен только для удобного представления полезной нагрузки. Он не шифрует данные. Любой, кто извлечёт символы и увидит формат, сможет декодировать идентификатор.
Промышленная схема должна проверять версию формата, длину поля и контрольную сумму. Если маркер содержит идентификатор пользователя, лучше использовать случайный идентификатор или значение, полученное через HMAC. Водяной знак не должен раскрывать персональные данные при обычном копировании.
Что происходит при копировании и форматировании
Поведение зависит от канала. Буфер обмена в одном приложении может сохранить последовательность кодовых точек. Вставка в HTML может пройти через парсер, который превратит текст в узлы и удалит неизвестные или служебные символы. Markdown-редактор может оставить исходную строку, а экспорт в plain text может отфильтровать её.
Проверяйте каждый канал отдельно:
- скопируйте размеченную строку внутри одного приложения;
- перенесите её в другой редактор;
- вставьте текст в HTML и извлеките его из DOM;
- сохраните в Markdown и plain text;
- загрузите файл и скачайте его повторно;
- сравните кодовые точки, длину строки, хеш и результат
detect.
Визуальное совпадение недостаточно. Две строки могут выглядеть одинаково, но иметь разный состав Unicode. В Python полезно сравнивать repr(text), список значений ord и хеш UTF-8-представления.
Почему очистка текста удаляет такой водяной знак
Очистка часто удаляет символы категории Cf, потому что они воспринимаются как форматирующие или управляющие. Пример фильтра:
import hashlib
import unicodedata
def remove_format_chars(text: str) -> str:
return ''.join(
ch for ch in text
if unicodedata.category(ch) != 'Cf'
)
def snapshot(label: str, text: str) -> dict:
return {
'label': label,
'length': len(text),
'sha256': hashlib.sha256(text.encode('utf-8')).hexdigest(),
'has_marker': detect(text),
}
clean = remove_format_chars(marked)
print(snapshot('before-cleanup', marked))
print(snapshot('after-cleanup', clean))
Фильтр удаляет больше, чем четыре символа из учебного примера. В некоторых языках и системах форматирующие кодовые точки участвуют в отображении текста, поэтому бездумная очистка способна изменить визуальное представление или поиск. Любой фильтр нужно прогнать на реальном наборе документов.
Операция unicodedata.normalize тоже должна присутствовать в тестах, но её результат нельзя заранее подменять предположением. NFC и NFKC преобразуют разные сочетания Unicode по своим правилам, а удаление служебных символов часто выполняет отдельный фильтр. Снимок до и после каждого шага показывает, где именно исчез сигнал.
Ключевые замены слов: маркер, который видит редактор
Лексический маркер хранится в видимых словах. Он переживает фильтр Unicode и перенос между форматами, если сами слова сохраняются. Редактор, переводчик и LLM работают как раз с этим слоем, поэтому стойкость зависит от характера изменений.
Как выбрать слова-кандидаты
Кандидат должен удовлетворять нескольким условиям: подходить по части речи, сохранять согласование, не менять терминологический смысл и звучать естественно в конкретном предложении. Частотные общеупотребительные слова дают больше позиций, но чаще встречаются случайно. Редкие технические термины снижают случайные совпадения, зато почти не допускают вариантов.
Учитывайте форму слова. Пара вариантов быстро и оперативно подходит для ограниченного контекста, но не заменяет все формы слова быстрый. В русском языке изменение падежа, числа или рода может сделать автоматическую подстановку грамматически неверной.
Для технической документации лучше хранить кандидатов вместе с частью речи, допустимой морфологией, областью применения и причиной выбора. Если вариант требует ручного решения, алгоритм должен пропустить позицию, а не вставлять сомнительный синоним.
Как реализовать выбор и детектирование в Python
Ниже показана упрощённая схема. Она работает с точными словоформами и поэтому намеренно не решает задачу морфологического анализа.
import re
LEXICON_VERSION = 'ru-tech-v1'
LEXICON = {
'быстро': ('быстро', 'оперативно'),
'проверить': ('проверить', 'верифицировать'),
}
def apply_lexical_marker(text: str, bits: str) -> str:
bit_index = 0
def replace(match):
nonlocal bit_index
word = match.group(0)
options = LEXICON.get(word.lower())
if options is None or bit_index >= len(bits):
return word
chosen = options[int(bits[bit_index])]
bit_index += 1
if word[:1].isupper():
chosen = chosen.capitalize()
return chosen
return re.sub(r'\b[А-Яа-яЁё-]+\b', replace, text)
def extract_lexical_bits(text: str) -> str:
reverse = {
variant: str(bit)
for source, options in LEXICON.items()
for bit, variant in enumerate(options)
}
tokens = re.findall(r'\b[А-Яа-яЁё-]+\b', text.lower())
return ''.join(reverse[token] for token in tokens if token in reverse)
source = 'Сервис быстро проверит входные данные.'
marked = apply_lexical_marker(source, '01')
print(LEXICON_VERSION, marked)
print(extract_lexical_bits(marked))
В прикладной системе словарь нужно хранить с версией и контрольным хешем. Детектор должен знать, какие позиции считались кандидатами, какие варианты были допустимы и сколько позиций могло исчезнуть. Иначе последовательность битов нельзя корректно сопоставить с исходным текстом.
Пример не учитывает формы проверит и данные, пунктуацию рядом со словом и омонимию. Для русского текста пригодится отдельный слой морфологического анализа, но его подключение не отменяет ручной проверки качества. Словарь с двумя вариантами может дать случайное совпадение в обычной статье.
Что ломает лексический маркер
- Синонимайзинг. Автоматическая замена быстро на оперативно или обратная замена меняет закодированный бит.
- Морфологическая правка. Изменение формы слова убирает точное совпадение, если детектор не умеет сводить словоформы к леммам.
- Перестановка предложений. Набор слов может сохраниться, но последовательность маркера изменится.
- Сокращение. Удаление нескольких предложений уменьшает число наблюдений и может убрать критическую часть последовательности.
- Перевод. Исходные слова исчезают даже при точной передаче смысла.
- Парафраз LLM. Модель меняет лексику, синтаксис, связки и длину текста, поэтому точный лексический сигнал обычно теряется.
Увеличение количества замен не гарантирует рост стойкости. Если каждое второе слово выглядит необычно, читатель или редактор заметит стиль, а качество текста снизится. Сильный лексический слой часто конфликтует с естественностью.
Смысловые маркеры и генерация: больше устойчивости, меньше определенности
Смысловая маркировка пытается перенести сигнал выше уровня отдельных кодовых точек и слов. При генерации модель получает правило выбора, а детектор анализирует результат. Такой подход ближе к watermarking для LLM, но его нельзя оценивать как обычный скрытый идентификатор.
Где находится сигнал: слова, токены и структура
- Конкретные слова. Генератор предпочитает одну группу синонимов или связок. Такой признак проще объяснить, но легче стереть.
- Токены. На каждом шаге выбирается подмножество токенов по секретному правилу или псевдослучайному состоянию. Детектор анализирует статистическое отклонение от ожидаемого распределения.
- Структура. Генерация может чаще использовать заданное число смысловых блоков, порядок аргументов или шаблон переходов. Такой сигнал менее привязан к символам, но сильнее смешивается с авторским стилем и задачей.
- Смысловые элементы. Сохраняются определённые факты, отношения или формулировки на уровне идеи. Чем сильнее обобщение, тем труднее отделить специальный маркер от естественного содержания.
Низкоуровневые признаки легче проверять точно. Высокоуровневые признаки потенциально переживают больше правок, но дают больше ложных совпадений. Поэтому рост абстракции не означает автоматический рост надёжности.
Почему детектор работает вероятностно
Детектор обычно считает признаки и сравнивает наблюдаемую статистику с базовой гипотезой. Порог определяет, когда результат считается положительным. Низкий порог повышает шанс найти слабый маркер, но увеличивает false positive. Высокий порог уменьшает ложные срабатывания, но пропускает часть размеченных текстов.
Precision отвечает на вопрос, какая доля положительных ответов действительно относится к размеченным текстам. Recall показывает, какую долю размеченных текстов детектор нашёл. Эти метрики нужно считать на положительном и отрицательном наборе, а не на одном известном документе.
Короткий текст даёт мало наблюдений. Один токен, выбранный случайно, способен заметно изменить итоговый score. Длинный текст стабилизирует статистику, но не спасает сигнал после полного переписывания.
def simple_score(token_ids, allowed_tokens):
if not token_ids:
return 0.0
hits = sum(
token in allowed_tokens[index % len(allowed_tokens)]
for index, token in enumerate(token_ids)
)
return hits / len(token_ids)
def classify(score: float, threshold: float) -> bool:
return score >= threshold
Эта функция показывает форму расчёта, но не готовый детектор для LLM. В реальной схеме разрешённое множество зависит от контекста, токенизатора, вероятностей модели и секретного правила. Для калибровки нужны немаркированные тексты, размеченные тексты и набор преобразований.
По этой причине детектор не должен возвращать формулировку «авторство доказано». Корректнее вернуть score, порог, версию модели, длину обработанного текста и предупреждение о неполной наблюдаемости.
Зависимость от модели и режима генерации
Сигнал зависит от модели, токенизатора, языка, температуры, top-p, системного промпта и длины ответа. Изменение токенизатора меняет границы элементов, на которых строился статистический тест. Изменение температуры меняет распределение вариантов. Новый системный промпт может изменить стиль и частотность слов.
Детектор, настроенный на одну модель, нельзя автоматически переносить на другую. Даже одна модель с разными параметрами генерации может дать разные профили. Перевод на другой язык добавляет ещё один слой неопределённости.
В публикациях о маркировке AI-текста встречаются схемы с ограничениями выбора токенов и вероятностной проверкой. Для прикладной системы важнее повторяемый протокол: зафиксировать версию модели, токенизатор, настройки, промпт и длину ответа, затем повторить тест после каждого изменения.
Копирование, правки, очистка и парафраз: карта отказов
Стойкость удобнее оценивать как последовательность преобразований. Каждая операция затрагивает свой слой: кодовые точки, слова, порядок, смысл или статистику.
| Преобразование | Unicode-маркер | Лексический маркер | Смысловой маркер |
|---|---|---|---|
| Копирование строки без изменений | Часто сохраняется, если канал переносит исходные кодовые точки | Сохраняется | Сохраняется вместе с текстом |
| HTML, Markdown или plain text | Зависит от парсера и экспорта | Обычно сохраняется | Сохраняется как часть текста |
| Замена пробелов и кавычек | Может сохраниться, если служебные символы не затронуты | Обычно сохраняется | Чаще сохраняется, но score может измениться |
| Фильтр непечатаемых символов | Обычно теряется | Сохраняется | Сохраняется, если содержание не изменилось |
| Замена синонимов и редактура | Может сохраниться только случайно | Ослабевает | Зависит от масштаба правок |
| Перевод | Теряется | Теряется | Может сохраниться частично, но требует отдельного теста |
| Парафраз через LLM | Теряется | Обычно теряется | Сигнал может ослабнуть, исказиться или исчезнуть |
Обычный копипаст без изменений
При копировании важны три представления: байты файла, последовательность Unicode-кодовых точек и видимый текст. Хеш байтов изменится после смены кодировки или перевода строк, даже если символы выглядят одинаково. Хеш UTF-8-строки позволяет отдельно контролировать содержимое, а проверка ord показывает скрытые кодовые точки.
Для HTML нужно проверять исходный HTML, текст после парсинга и результат сериализации. Для Markdown добавьте проверку после удаления форматирования. Для файла сохраните контрольные суммы до загрузки и после скачивания. Один успешный тест в редакторе не описывает поведение CMS, мессенджера или браузерного буфера.
Unicode-маркер подходит для такого сценария, если канал находится под вашим контролем и очистка не удаляет категорию Cf. В противном случае лучше опираться на хеши, версионирование и серверный журнал.
Редактура человеком и автоматическое форматирование
Исправление кавычек, тире, регистра, пробелов и переносов строк меняет строковое представление. Unicode-маркер внутри текста может пережить часть таких операций, но перестановка фрагментов, вставка нового абзаца или удаление символов рядом с маркером меняют контекст.
Лексический маркер чувствительнее к словам. Редактор может заменить термин, изменить порядок слов, сократить повтор или убрать связку, которая несла один бит. Если детектор допускает пропуски, он должен хранить карту позиций и минимальное число совпадений. Иначе одна удалённая фраза сдвинет всю последовательность.
Смысловой детектор может распознать близкий текст после лёгкой редакторской правки, но это будет статистический вывод. Проверка семантического сходства показывает близость содержания, а не сохранение конкретного водяного знака.
Очистка и нормализация
Типичная цепочка очистки включает удаление HTML, декодирование сущностей, замену пробелов, нормализацию Unicode, фильтрацию управляющих кодов, сериализацию и повторный парсинг. Сигнал может исчезнуть на любом шаге.
Фиксируйте отдельную версию после каждой операции. Пример структуры журнала:
steps = [
('source', source_text),
('html_to_text', html_to_text),
('normalized_nfc', normalized_text),
('without_format_chars', clean_text),
]
for label, value in steps:
print(snapshot(label, value))
Такой журнал показывает, сохранились ли длина, хеш и результат детектора. Если маркер исчез после очистки, это технический факт для конкретного фильтра. Нельзя переносить его на все библиотеки и форматы без повторной проверки.
Перевод и парафраз через LLM
Перевод меняет алфавит, словарь, морфологию и порядок слов. Даже перевод без смысловых ошибок разрушает Unicode- и лексический слой. Семантическое обнаружение может увидеть близкое содержание, но на результат влияют язык, длина, модель и степень свободы переводчика.
LLM-парафраз действует ещё шире. Модель может объединить два предложения, удалить пример, заменить термин, изменить структуру абзацев и добавить собственное пояснение. После такого процесса исходный текст уже не совпадает с размеченной строкой, а смысловой score требует отдельной валидации.
При работе с переводом полезно учитывать не только маркировку, но и изоляцию инструкций, разметку кода и контроль структуры входных данных. Практические проблемы таких пайплайнов разобраны в материале про сбои AI-перевода. Защита процесса обработки не сохраняет водяной знак автоматически, но помогает не потерять сведения о версиях и преобразованиях.
Как проверить стойкость водяного знака в Python
Честная оценка начинается с тестового набора и заканчивается таблицей результатов. Без этого фраза «маркер переживает парафраз» означает лишь, что один текст прошёл через один сценарий.
Сформировать набор исходных текстов
Разделите материалы по длине, языку и стилю. Для первой серии подойдут:
- короткие сообщения в одно предложение;
- ответы LLM длиной в несколько абзацев;
- технические инструкции с кодом и терминами;
- статьи с несколькими смысловыми блоками;
- документация с таблицами, списками и заголовками.
Для каждого текста сохраните исходную версию, количество символов, количество токенов, язык, стиль, наличие терминов и видимые ограничения. Код в документации нужно тестировать отдельно: комментарии допускают лексические варианты, а синтаксис программы почти не допускает свободного выбора.
Создайте положительный набор с маркером и отрицательный набор без маркера. Отрицательные тексты должны быть похожи по длине и тематике, иначе precision будет отражать различия набора, а не качество детектора.
Пропустить текст через контролируемые преобразования
Задайте последовательности операций с отдельной записью каждой версии:
- копирование без изменений;
- перевод HTML в plain text;
- нормализация NFC и NFKC;
- замена пробелов, кавычек и переносов строк;
- удаление символов категории
Cf; - ручная имитация редакторских правок;
- перестановка предложений и сокращение;
- перевод на другой язык и обратный перевод;
- парафраз через одну или несколько LLM.
Для LLM сохраняйте модель, токенизатор, системный промпт, пользовательский промпт, температуру, top-p, максимальную длину и дату запуска. Повторный запрос может дать другой текст, поэтому один результат не описывает весь режим генерации.
def evaluate(detector, marked_versions, clean_versions):
marked_hits = sum(detector(text) for text in marked_versions)
clean_hits = sum(detector(text) for text in clean_versions)
recall = marked_hits / len(marked_versions) if marked_versions else 0.0
precision = (
marked_hits / (marked_hits + clean_hits)
if marked_hits + clean_hits else 0.0
)
return {
'recall': recall,
'precision': precision,
'marked_count': len(marked_versions),
'clean_count': len(clean_versions),
}
Для Unicode- и лексического подходов детектор может возвращать булево значение. Для смысловой схемы лучше сохранять числовой score, порог и решение до порога. Так можно построить несколько вариантов классификации без повторной обработки текста.
Какие метрики считать
- Recall. Доля размеченных версий, где маркер обнаружен.
- Precision. Доля корректных положительных ответов среди всех положительных ответов.
- False positive rate. Доля чистых текстов, ошибочно признанных размеченными.
- Доля испорченных текстов. Сколько документов получили заметные изменения смысла, грамматики или стиля при добавлении маркера.
- Изменение длины. Особенно полезно для Unicode- и лексических схем.
- Качество перевода и смысловая эквивалентность. Эти показатели нужны, если тест включает перевод или LLM-парафраз.
- Стоимость проверки. Учитывайте время, память, число обращений к модели и ручной ревью.
Для коротких текстов полезно показывать доверительный интервал или хотя бы размер выборки. Один найденный маркер среди двух документов не позволяет сравнить методы. Отдельно измеряйте качество текста, потому что высокая стойкость, купленная ценой неестественных формулировок, может сделать систему непригодной.
Как оформить честный вывод
Вывод привязывайте к условиям: русский язык, диапазон длины, версия словаря, модель, параметры генерации и конкретная цепочка обработки. Формулировка «в этой серии после очистки категории Cf Unicode-сигнал не обнаруживался» точнее, чем «Unicode-маркеры всегда удаляются очисткой».
Отсутствие маркера после одного преобразования не доказывает универсальную неустойчивость. Другой формат, расположение полезной нагрузки или алгоритм коррекции ошибок может дать другой результат. Симметрично, один успешный пример не доказывает стойкость к любой редактуре.
Добавляйте в отчёт исходные тексты, версии, параметры и хеши. Тогда повторная проверка сможет отделить изменение алгоритма от изменения канала.
Как выбрать метод для короткого и длинного текста
Выбор определяется длиной, допустимостью изменения формулировок, контролем над каналом и моделью угроз. Одинаковая схема не подходит для копирования статьи в CMS и перевода короткого ответа LLM.
Короткие сообщения и ответы LLM
В одном предложении мало позиций для статистического сигнала и мало слов-кандидатов для лексической схемы. Одна редакторская замена может убрать половину доступных признаков. Смысловой детектор получает слишком мало наблюдений, поэтому порог становится нестабильным.
Для короткой неизменной строки практичнее сочетать внешний хеш с компактным Unicode-маркером, если канал сохраняет кодовые точки. Для короткого текста, который будут редактировать, лучше считать водяной знак вспомогательным признаком и хранить исходную версию на сервере.
Если ответ LLM часто переводят или переписывают, маркер в тексте не должен быть единственным способом контроля происхождения. Нужны логи запроса, версия модели, параметры генерации и идентификатор результата.
Длинные статьи и документация
Длинный материал даёт больше места для распределённых признаков. Можно проверять отдельные фрагменты, использовать несколько независимых участков и связывать их с версией документа. Это помогает анализировать частично скопированный текст.
Распределение по всему документу требует контроля версий. Если редактор удалил третий раздел, детектор должен понимать, что исчез фрагмент, а не считать весь документ чистым. Сохраняйте маркеры позиций, границы абзацев и идентификаторы блоков.
Длина не гарантирует стойкость после полного переписывания. Статья может стать новым текстом с тем же содержанием, но без исходной последовательности слов и статистического профиля.
Когда имеет смысл комбинировать маркеры
Гибридная схема оправдана, когда у слоёв разные задачи:
- Unicode помогает находить неизменную или почти неизменную копию;
- лексический слой связывает текст с конкретным словарём и версией;
- статистический слой проверяет более сильные изменения в длинном материале;
- хеш и журнал фиксируют исходную версию независимо от видимого текста.
Комбинация увеличивает сложность детектора. Нужно синхронно версионировать все правила, хранить результаты каждого слоя и отдельно разбирать конфликтующие ответы. Если все маркеры зависят от одной и той же части текста, общий отказ останется общим.
Когда водяной знак лучше не использовать
Откажитесь от него как от основного механизма, если текст состоит из нескольких слов, всегда проходит перевод, подвергается агрессивной очистке или регулярно переписывается LLM. В таких условиях маркер даст слабый сигнал и создаст ложное чувство контроля.
В системах с жёсткими требованиями к происхождению полезнее сочетать контроль доступа, журналирование, версионирование, хеширование и цифровую подпись. Водяной знак можно оставить дополнительным способом связать опубликованную копию с исходной записью.
Ограничения, безопасность и практические альтернативы
Водяной знак работает в предположениях о канале и поведении нарушителя. Как только эти предположения меняются, меняется и полезность маркера.
Удаление маркера и атаки на детектор
Нарушитель, который знает о схеме, может удалить Unicode-символы, заменить слова, сократить текст, переставить фрагменты, перевести материал или сгенерировать новый вариант. Чем проще описан детектор, тем легче проверить, какие признаки он ищет.
Для Unicode-схемы достаточно фильтра кодовых точек. Для лексического слоя подходит замена вариантов и изменение порядка. Для смыслового детектора работают сокращение контекста, перевод, повторная генерация и изменение стиля. Ни один из этих шагов не гарантирует удаления конкретного маркера во всех схемах, но каждый должен входить в тестовую модель угроз.
Секретное правило выбора токенов усложняет целевую атаку, но не устраняет ложные срабатывания и не делает детектор независимым от модели. Доступ к API проверки тоже нужно защищать, иначе злоумышленник сможет подбирать текст по ответам детектора.
Конфликты с качеством, доступностью и приватностью
Невидимый символ способен влиять на поиск, сравнение строк, токенизацию, индексацию и экспорт. Поведение зависит от программного стека, поэтому проверяйте маркер в браузере, базе данных, поисковом индексе и редакторе, которые используются в вашем продукте.
Лексические замены могут ухудшить читаемость, нарушить терминологию или изменить юридически значимую формулировку. Смысловые признаки могут ошибочно принять авторский стиль за маркировку. Для пользователей с ограничениями доступности проблемы возникают там, где вспомогательные технологии или конвертеры по-разному обрабатывают служебные кодовые точки.
Не храните в маркере открытые персональные данные. Скрытое поле может попасть в публичную копию, логи, резервные файлы и историю буфера обмена. Используйте непрозрачный идентификатор, ограничивайте срок хранения и описывайте назначение обработки данных.
Что использовать вместе с водяным знаком
- Версионирование. Показывает, какая редакция существовала и какие изменения появились позже.
- Хеширование. Позволяет точно сравнить сохранённую строку или файл с копией.
- Серверные журналы. Фиксируют выдачу, загрузку, пользователя, время и операцию, если журнал защищён от незаметного редактирования.
- Контроль доступа. Уменьшает число каналов, через которые исходный документ может утечь.
- Цифровая подпись. Связывает данные с ключом подписанта и проверяет целостность подписанной версии.
- Хранение исходных документов. Даёт материал для сравнения и последующей экспертизы.
Хеш проверяет целостность, подпись связывает документ с ключом, журнал показывает события, контроль доступа ограничивает выдачу, а водяной знак помогает найти связь после копирования. Эти слои решают разные задачи и не заменяют друг друга.
Если нужен анализ того, кто написал текст, не смешивайте его с оценкой качества. Детекторы AI-текста легко меняют результат после синонимайзинга и смены параметров генерации. Подход с проверкой связности и фактической нагрузки описан в материале об автоматизации детекции слопа: оценка полезности текста отвечает на другой вопрос и не подтверждает происхождение.
Итог: какой подход выбрать для конкретной задачи
Если важен факт копирования без изменений
Используйте Unicode-маркер как дополнительный сигнал и сравнивайте текст с исходной версией. До запуска проверьте конкретный буфер обмена, редактор, CMS, формат файла и фильтры. Хеш UTF-8-строки и журнал выдачи дадут более воспроизводимую основу для сравнения.
Не храните чувствительные сведения в открытом виде. Маркер может исчезнуть после plain text-экспорта или фильтра категории Cf, даже если видимый текст не изменился.
Если текст будут редактировать или переписывать
Для лёгкой редакторской правки можно добавить лексический слой, если текст длинный, словарь качественный, а допустимые варианты проверены человеком. Для длинных материалов пригодится статистический или структурный слой, но его нужно настраивать на конкретную модель, язык и длину.
После перевода и LLM-парафраза низкоуровневые маркеры обычно теряются. Семантическая проверка может показать близость содержания, но её результат нельзя выдавать за надёжное подтверждение происхождения. Считайте recall, precision, false positive и false negative на собственном наборе преобразований.
Если нужна доказательная цепочка
Сохраняйте исходный текст, хеши, версии алгоритма, параметры генерации или добавления маркера, журналы обработки и результаты детектора. Для каждой копии фиксируйте канал и список преобразований.
Техническая запись «маркер найден» становится полезнее, когда её можно связать с конкретной версией и воспроизвести на том же наборе данных. Правовую оценку определяют контекст, сохранность доказательств и применимое право, а не один найденный символ или положительный score.
Практическое правило простое: сначала опишите преобразования, которые текст должен пережить, затем выберите слой маркера и проведите тест на реальном канале. Для неизменной копии подойдёт Unicode и хеш. Для длинного текста после лёгких правок можно добавить лексические или статистические признаки. Для перевода и полного парафраза рассчитывайте на журнал происхождения, версии и контроль доступа, а не на обещание неразрушаемого водяного знака.