- Семантическая близость только ищет кандидатов
- Наличие цитаты не доказывает весь тезис
- Рекомендация должна стать ограниченным планом
- Diff показывает, что именно будет записано
- Свежесть файла проверяется непосредственно перед записью
- Undo не должен стирать последующую правку
- Что показал прогон настоящей модели
- Где заканчивается гарантия
- Практический порядок проверки
В тестовом хранилище Veynrel есть две заметки с одинаковым названием Cache lifetime. Одна лежит в Study/Caching/Cache lifetime.md, другая в Study/Systems/Cache lifetime.md. Для Obsidian это два разных файла. Поэтому совет «добавить ссылку» уже требует решений: какой файл менять, куда вставлять ссылку и подтверждает ли исходный текст вывод модели.
Автор плагина Veynrel Ziverpup описал этот путь в инженерном разборе на Хабре. Это не рецензируемое исследование и не независимый аудит Obsidian. Мы рассматриваем статью вместе с тестами и адаптером записи из зафиксированной версии репозитория, commit 79fabc93bd946f0287b431721126efee0646f588. Вопрос здесь практический: какие барьеры нужны, чтобы рекомендация LLM не превратилась в ошибочную или устаревшую правку Markdown?
Семантическая близость только ищет кандидатов
Veynrel сравнивает числовые векторы, построенные для фрагментов заметок. Это помогает найти тексты, где одна тема описана разными словами. Но сходство 0,95 не означает 95-процентную вероятность дубликата. Это мера близости в выбранном векторном пространстве.
Автор поэтому называет результат поиска кандидатом. Он подсказывает, какие заметки стоит открыть, но не разрешает автоматически объединить их, удалить повтор или записать ссылку. Перед отправкой текста внешнему провайдеру плагин показывает подготовленный запрос и требует отдельного согласия. Локальный Ollama меняет место обработки, но не отменяет необходимость сообщить пользователю, какие данные будут переданы.
Эти этапы нельзя смешивать. Поиск отвечает на вопрос «какие заметки могут быть связаны?». Решение о правке отвечает на другой вопрос: «какое действие допустимо над конкретным файлом?». Высокая похожесть сама по себе не является разрешением на запись.
Наличие цитаты не доказывает весь тезис
Модель может вернуть тезис и цитаты, которые якобы его подтверждают. Но сгенерированный фрагмент может быть перефразирован, отнесён не к той заметке или вообще отсутствовать в исходном файле. В Veynrel для этого создан отдельный каталог свидетельств.
Локальный код заранее читает сохранённые файлы и формирует точные фрагменты. Каждый получает сторону, A или B, диапазон и идентификатор вроде nonce:A:0. В запрос передаются полные тексты заметок и каталог разрешённых фрагментов. В тесте отдельно проверяется, что ограничение каталога не обрезает сам документ.
После ответа модель не может прислать произвольную цитату. Она должна сослаться на идентификатор из текущего каталога. Код сам находит фрагмент и подставляет его содержимое. Неизвестный ID, ссылка на другую сторону, устаревший каталог, неверная версия схемы и лишние поля приводят к отказу.
Тест relationshipEvidence.test.ts показывает и более строгие случаи. Если фрагмент повторяется, система сообщает о неоднозначности. Если изменены регистр, пунктуация или Markdown-разметка, строка не считается точным совпадением. Проверка не пытается угадать, что имела в виду модель.
Но этот барьер имеет ясный предел. Он подтверждает наличие фрагмента в нужной заметке, а не достаточность фрагмента для всего тезиса. В одном из описанных прогонов тезис включал просроченное значение и пример на пять минут, тогда как привязанная цитата подтверждала только первое.
Это близко к различию между формальной проверкой и содержательной обоснованностью, которое разобрано в статье о границах формальной проверки доказательств. Валидатор может установить, что фрагмент существует, но не решить за человека, следует ли из него весь тезис.
Рекомендация должна стать ограниченным планом
Даже согласованная рекомендация не должна исполняться напрямую из JSON модели. Для двух файлов с одинаковым именем запись [[Cache lifetime]] может разрешиться не в тот объект. Нужно выбрать исходную и целевую заметку, направление ссылки и конкретный диапазон текста.
В Veynrel отдельный планировщик переводит разрешённый тип рекомендации в параметры операции. Для обычной ссылки он использует диапазон документа, выбранный пользователем, а не приблизительный поиск по цитате модели. План содержит путь к исходному файлу, путь к цели, исходный и предлагаемый Markdown, диапазон изменения и сведения о целевой заметке.
Тесты в changePlan.test.ts проверяют несколько важных свойств:
- ссылка разрешается в конкретный путь, а не только в имя файла;
- изменяется выбранное вхождение, а соседний контекст сохраняется;
- неоднозначный диапазон не превращается в удаление или замену;
- защищённые структуры, включая frontmatter, блоки кода, списки, таблицы, ссылки и встраивания, блокируют операцию;
- для операции без изменений не создаются ни diff, ни запись в журнале, ни запись в файл.
Перед применением адаптер ещё раз проверяет, куда Obsidian разрешает целевую ссылку. Если путь, метаданные или расположение файла изменились, прежний план считают устаревшим.
Diff показывает, что именно будет записано
Пользователь должен видеть полный исходный текст, предлагаемую версию и конкретный diff. Подтверждение относится именно к этому плану. Нажатие кнопки «добавить ссылку» не должно означать согласие на любые другие правки, которые способна предложить модель.
Так интерфейс ограничивает масштаб операции. В тесте на замену одного абзаца сохраняются пример из соседнего абзаца и последующий текст. Если разметка Markdown не позволяет однозначно определить диапазон, система отказывается от изменения.
Свежесть файла проверяется непосредственно перед записью
Между просмотром diff и нажатием кнопки пользователь может изменить заметку. Если исполнитель просто запишет заранее подготовленную версию after, ручная правка исчезнет.
Адаптер Veynrel перечитывает заметки и метаданные перед применением. Он проверяет существование и идентичность файлов, размер, время изменения, структуру Markdown, разрешение целевой ссылки и соответствие нового плана текущим данным. В obsidianSafeApply.ts эта проверка повторяется внутри Vault.process(), то есть в операции изменения файла.
Главное условие простое: текущий текст должен в точности совпадать с текстом, на основе которого построили план. В коде это выражено сравнением current === before. «Почти такой же» Markdown и нормализация пробелов здесь не подходят. При несовпадении операция завершается конфликтом и не перезаписывает новую версию.
Отдельно проверяются открытые редакторы. В комментарии адаптера сказано: «Для ожидающих сохранения редактора блокировок нет: в первой версии нужно закрыть затронутые представления». Даже совпадающий буфер может иметь сохранение в очереди, а публичные API Obsidian не дают плагину надёжного способа остановить его. Поэтому текущая реализация предпочитает отказ и требует закрыть вкладки с затронутыми заметками.
Undo не должен стирать последующую правку
Безусловное восстановление старого текста опасно. После добавления ссылки пользователь может дописать абзац, а затем запросить отмену. Если плагин просто восстановит before, исчезнет и ссылка, и новая правка человека.
Журнал Veynrel хранит исходное и ожидаемое содержимое, контрольные суммы и данные для восстановления. Перед отменой система проверяет, что файл всё ещё точно равен версии after, записанной плагином. Проверяются также идентичность файла, отсутствие конфликтующего журнала и состояние редакторов. Только после этого допускается переход от after к before.
Если после применения заметка изменилась, Undo блокируется. Плагин не пытается объединить две версии, а отказывается удалять правку, о которой ничего не знает.
Такая же осторожность нужна после сбоя. Файл мог сохраниться, а отметка об успешном применении в журнале не успела записаться. После перезапуска система сопоставляет журнал с фактическим содержимым и показывает восстановленное состояние. Она не должна автоматически повторять запись, если неизвестно, что уже произошло.
Что показал прогон настоящей модели
24 октября 2026 года автор провёл десять последовательных запросов к DeepSeek через OpenRouter. Использовались Obsidian 1.13.7, Veynrel 2.2.1, производственный запрос, температура 0, ограничение ответа 8192 токена и отключённое рассуждение. Все пары заметок были синтетическими, а список кандидатов составили из фиксированных искусственных векторов. Поэтому прогон проверял языковую модель и интеграцию, но не качество векторных представлений и не поиск в реальном хранилище.
Все десять ответов были корректным JSON и прошли проверку схемы и ссылок на фрагменты. В пяти случаях модель предложила добавить ссылку, в одном предложила рассмотреть объединение, ещё в четырёх рекомендовала ручной разбор. Предварительную проверку прошли шесть рекомендаций. Полностью провести применение и отмену автор смог только для одного сценария.
Техническая валидность не означала убедительность всех выводов. Сам автор счёл семь рекомендаций уместными, а три спорными. Это оценка одного человека, причём разработчика плагина, поэтому она не является независимой метрикой качества модели. В одном ответе по двум заметкам Cache lifetime цитата подтверждала только часть составного тезиса.
Таков фактический предел эксперимента: десять ответов прошли технические фильтры, но это не доказывает смысловую правильность рекомендаций. Тесты проверяли устойчивость конкретной реализации к заданным ошибкам, а не надёжность LLM в целом.
Где заканчивается гарантия
Veynrel не превращает операции над несколькими Markdown-файлами в единую атомарную транзакцию. Если первый файл уже записан, а изменение второго завершается ошибкой, журнал помогает обнаружить частично применённую операцию, но не делает файловую систему атомарной.
Плагин также не контролирует одновременно внешние редакторы, сторонние плагины, синхронизацию и все окна Obsidian. Управляемые сбои в тестах показывают, как реализация ведёт себя в отдельных сценариях, но не являются статистикой пользовательских инцидентов.
Эти ограничения относятся к конкретному исполнителю безопасного применения в Veynrel 2.2.1. Их нельзя автоматически распространять на все генеративные действия плагина, другие плагины Obsidian или произвольные скрипты, меняющие Markdown.
Практический порядок проверки
- Считать семантическое сходство способом найти кандидатов, а не основанием для правки.
- До отправки текста показать пользователю точный запрос и получить согласие.
- Подготовить каталог фрагментов и принимать только существующие ID, относящиеся к нужной стороне.
- Отдельно проверять наличие цитаты и её достаточность для тезиса.
- Преобразовать рекомендацию в ограниченный план с выбранным направлением, диапазоном и целевым путём.
- Отказываться от операции при неоднозначной разметке, неизвестном файле, конфликтующей ссылке или устаревших метаданных.
- Показать diff и запросить отдельное подтверждение.
- Непосредственно перед записью перечитать файлы, проверить редакторы и сверить точное содержимое.
- Вести журнал и разрешать Undo только при неизменённом ожидаемом состоянии.
- При конфликте остановиться, сохранив новую правку пользователя.
Вывод автора Veynrel можно передать коротко: модель предлагает, код проверяет, человек выбирает, а исполнитель вправе отказаться. Наша интерпретация строже. Безопасность зависит не от одной проверки, а от цепочки независимых барьеров. Наличие цитаты не делает рекомендацию истинной, diff не защищает файл от последующих изменений, а журнал не превращает многодокументную операцию в атомарную.
Для заметок Obsidian это означает простое правило: рекомендация LLM должна заканчиваться проверяемым планом, а не записью в файл. Если структура неоднозначна или данные устарели, корректным результатом может быть отказ от изменений.
Источники
- Ziverpup. «Найти похожие заметки в Obsidian оказалось проще, чем безопасно их изменить». Хабр. Авторский инженерный разбор, не рецензируемая научная публикация. Полный текст.
- Репозиторий Veynrel, commit
79fabc93bd946f0287b431721126efee0646f588: проверка свидетельств, построение плана, безопасное применение и Undo, адаптер записи в Obsidian.

Фото: SHVETS production / Pexels