Всем привет! В прошлой статье я рассказывал про ребрендинг моего плагина для Obsidian, Companion и MCP. Но спустя какое-то время я поймал себя на достаточно неприятной мысли: поменять название, причесать отдельные функции и добавить ещё несколько возможностей недостаточно, чтобы получился действительно интересный продукт. Функций становилось всё больше, а ответа на довольно простой вопрос всё ещё не было:

Зачем обычному пользователю вообще нужен именно мой плагин?

Ответить на него самому себе оказалось сложнее, чем я думал. Поэтому вместо того, чтобы придумать очередную кнопку, я решил посмотреть, как развивались другие популярные плагины Obsidian, особенно в AI-нише. Мне хотелось понять, есть ли какой-то момент, после которого проект внезапно начинает расти, и можно ли этот момент вообще повторить.

Я искал момент взрывного роста, а нашёл немного другое

В первую очередь я смотрел на Copilot и Claudian. Copilot для меня вообще был одним из первых источников вдохновения: именно после его установки я когда-то задумался, почему бы не сделать собственный инструмент для работы с заметками и LLM.

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

Спойлер: нашёл. Только пользы от этого оказалось примерно ноль.

Я пытался связать скачок то с новой функцией, то с очередным релизом, то с изменением интерфейса, но в итоге пришёл к намного более банальному выводу. По моему ощущению, самые заметные плагины в нужный момент просто очень хорошо отвечали на один понятный вопрос пользователя.

Я не хочу утверждать, что Copilot был буквально первым плагином с AI-чатом или Claudian первым агентным плагином в истории Obsidian. Это уже надо было бы отдельно доказывать. Мне интереснее другое. Copilot долго воспринимался как понятный способ работать с LLM прямо внутри Obsidian. У Claudian центральная идея ещё заметнее: дать агенту работать прямо внутри vault, читать и изменять файлы, искать по ним, запускать инструменты и выполнять несколько действий подряд.

То есть человек открывает описание и довольно быстро понимает, зачем этот инструмент существует.

И вот тут мне стало немного грустно смотреть на собственный плагин.

Плагин, в котором было всё, кроме ответа на вопрос «зачем»

Я решил на какое-то время забыть, что сам его разработчик, и просто открыть Veynrel как пользователь.

И внезапно увидел примерно следующее: одна панель делает одно, вторая другое, где-то генерация MOC, где-то флешкарты, где-то работа с LLM, где-то обработка текста, где-то поиск сироток, где-то семантические функции. Каждая вещь по отдельности имела смысл, но вместе всё выглядело как набор маленьких инструментов, которые случайно оказались установлены под одним названием.

Причина понятна. Над плагином я потихоньку работаю уже почти полгода, и разработка шла примерно по принципу: увидел полезный паттерн работы с Obsidian, добавил. Увидел ещё один, добавил и его.

В какой-то момент там появились поиск заметок-сирот, генерация системы тегов, создание MOC, флешкарты, запросы к LLM по заметке, улучшение и сокращение текста, атомизация больших заметок и ещё много всего. Это была условная первая фаза разработки. Я пытался собрать в одном месте практики, которыми люди уже пользуются при ведении базы знаний.

Потом, читая наш любимый Хабр, я наткнулся на статью про embeddings и векторные базы данных. Тема меня довольно быстро затянула, потому что она решала проблему, которая у моего подхода уже начинала появляться: ключевые слова, теги и названия заметок далеко не всегда нормально описывают их смысл. Так началась вторая фаза. В плагине появился семантический индекс, поиск похожих заметок, кластеризация и другие функции, которые уже смотрели не только на явные связи между файлами.

Функционала стало ещё больше, но центральной идеи всё ещё не появилось.

Попытка собрать всё в одну точку

Тогда я решил начать с интерфейса.

Если я хочу, чтобы человек воспринимал Veynrel не как 5 разных плагинов, разбросанных по хранилищу, а как один продукт, у него должна быть одна понятная точка входа. Так появилась общая страница Health, которая постепенно превратилась в своего рода панель состояния всего хранилища.

Главная страница Health после локальной проверки: Pulse, рекомендация и четыре области состояния хранилища.
Главная страница Health после локальной проверки: Pulse, рекомендация и четыре области состояния хранилища.
Подробнее о состоянии хранилища
Более подробные данные по Structure, Connections, Recall и Knowledge после проверки хранилища.
Более подробные данные по Structure, Connections, Recall и Knowledge после проверки хранилища.

Изначально всё это было достаточно текстовым. Но так как для меня, как пользователя, очень важна визуальная составляющая, потом появился вариант, который вы видите сейчас: своеобразный дашборд состояния хранилища.

Я пытался визуально связать его со «здоровьем второго мозга» (очень часто слышу данное словосочетание про Obsidian в последнее время). В результате получил пульс, поэтому теперь временами выглядит так, будто мы лечим не второй мозг, а второе сердце. Но от визуальной составляющей отказываться уже не хотелось, так как она стала центральной символикой плагина.

Самое важное для меня оказалось в том, что теперь разрозненные функции получили общий контекст. На главной странице есть четыре направления: Structure, Connections, Recall и Knowledge. То есть вместо списка инструментов пользователь сначала видит, что вообще происходит с его хранилищем, а уже потом решает, куда идти дальше.

Нулевая фаза: вообще без нейронок

Ещё одна вещь, которую я понял во время переделки, это насколько плохим может быть первый опыт пользователя, когда приложение сразу встречает его словами «введите API key, выберите provider, модель, endpoint…».

Человеку, который уже не чайник, а хотя бы самовар, всё это проблем не доставит. Но мне хотелось, чтобы Veynrel был полезен ещё до настройки первой модели.

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

Часть Health работает полностью локально. Можно найти сломанные ссылки и заметки-сироты, посмотреть структуру Markdown-связей и построить Vault Topology (один из моих любимых фрагментов новой общей страницы), не отправляя содержимое заметок никакой модели.

Vault Topology: карта реальных Markdown-связей между 500 заметками. Здесь embeddings и LLM вообще не используются.
Vault Topology: карта реальных Markdown-связей между 500 заметками. Здесь embeddings и LLM вообще не используются.

Причём топологическая карта строится именно по реальным Markdown-ссылкам. Никаких embeddings там нет. Узлы — это заметки, связи — это реальные ссылки между ними. Отдельно считаются компоненты графа, входящие и исходящие связи, сироты и так называемые connectors, то есть точки сочленения, удаление которых разбивает компонент графа на части.

И после того как пользователь получил базовый пакет возможностей, он может перейти на новый уровень.

#Recall и зачем я вообще полез в интервальное повторение

Отдельной частью этой новой концепции стал Recall.

Когда я начал разбираться в интервальном повторении, мне понравилась идея, что база знаний должна помогать не только хранить информацию, но и что-то из неё реально помнить. Тут я наткнулся на FSRS.

Само интервальное повторение, конечно, далеко не новая идея: сам ею пользовался, ещё когда готовился к экзаменам в вузе. Интересным для меня оказался именно современный FSRS, который моделирует память через difficulty, stability и retrievability. У него есть optimizer, который может подбирать параметры под историю повторений пользователя.

Встроенный Recall: после показа ответа FSRS предлагает четыре оценки и сразу показывает следующий интервал повторения.
Встроенный Recall: после показа ответа FSRS предлагает четыре оценки и сразу показывает следующий интервал повторения.

В Veynrel я встроил собственный локальный FSRS-6 scheduler, чтобы не ставить отдельный плагин для карточек. Карточки можно найти в заметках, начать повторение и оценивать ответ через Again, Hard, Good и Easy.

Нейронка для самого расписания повторений при этом вообще не требуется. LLM может помочь создать карточки, но решение о том, когда показать их снова, уже принимает локальный scheduler.

Первая фаза: подключаем LLM

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

Возвращаются практически все функции, которые были в ранних версиях плагина: работа с выделенным текстом, генерация и обработка заметок, MOC, batch processing, Ask your Vault и другие инструменты.

Массовая обработка заметок: сначала выбираются фильтры, затем действие, которое будет применено к выбранным файлам.
Массовая обработка заметок: сначала выбираются фильтры, затем действие, которое будет применено к выбранным файлам.
Остальные инструменты Veynrel
Часть инструментов, которые остались доступны отдельно от основной страницы Health
Часть инструментов, которые остались доступны отдельно от основной страницы Health

Но теперь я стараюсь воспринимать LLM как дополнительный уровень анализа, а не как очередную кнопку «написать с помощью ИИ». Например, так появился раздел Knowledge.

Knowledge Health не проверяет правдивость заметок, он просто подсказывает, какие из них выглядят недоработанными и к каким, возможно, стоит вернуться.

При глубоком анализе модель классифицирует заметки, а в Findings попадают только те, которые выглядят как draft. При этом право выбора остаётся за пользователем: это всего лишь рекомендация. Короткая заметка вполне может быть законченной и полезной, а идеально оформленная простыня всё равно может содержать ерунду.

Для меня этот момент важен ещё и потому, что он довольно хорошо выражает то, к чему я в итоге хотел привести весь плагин. Не делать что-то автоматически за пользователя, а показывать место, на которое стоит обратить внимание, и оставлять решение человеку.

Вторая фаза: подключаем embeddings

А вот после подключения embedding-модели открывается уже семантическая часть, которую я раньше считал главной фишкой всего плагина.

Самая заметная новая штука здесь, конечно же, Global Semantic Map.

В ранней идее у меня было что-то намного ближе к обычному «разбросаем точки по окружности и покажем группы». Но когда начал делать это нормально, пришлось сначала определить, что вообще означает положение точки на такой карте.

Сейчас каждая заметка представляется не одним случайным числом на экране. У заметки могут быть несколько chunks и, соответственно, несколько embedding-векторов. Для карты они собираются в единое нормализованное представление заметки.

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

Размер точки тоже не взят с потолка. Он отражает semantic connectedness, то есть насколько в среднем заметка близка к своим ближайшим семантическим соседям.

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

Global Semantic Map: выбранная заметка, её положение относительно semantic core и ближайшие по смыслу соседи.
Global Semantic Map: выбранная заметка, её положение относительно semantic core и ближайшие по смыслу соседи.
Перецентрирование карты и семантическое окружение
Ту же карту можно перестроить относительно конкретной заметки и посмотреть всё пространство уже с её точки зрения.
Ту же карту можно перестроить относительно конкретной заметки и посмотреть всё пространство уже с её точки зрения.
Semantic Neighborhood: выбранная заметка и десять ближайших к ней заметок из существующего семантического индекса.
Semantic Neighborhood: выбранная заметка и десять ближайших к ней заметок из существующего семантического индекса.

При этом открытие карты не вызывает embedding API заново. Она использует уже построенный индекс.

В текущей реализации я специально ограничил карту 500 подходящими документами. Для них сравнивается каждая пара, после чего для каждой заметки сохраняются только ближайшие кандидаты. Это пока обычный точный расчёт, без HNSW и другой approximate nearest neighbor магии. Для моего реального vault такого масштаба пока вполне хватает.

А вот как карта выглядит на моём реальном хранилище:

А так Global Semantic Map выглядит уже на моём реальном хранилище, а не на специально подготовленных демонстрационных данных.
А так Global Semantic Map выглядит уже на моём реальном хранилище, а не на специально подготовленных демонстрационных данных.

Отдельная группа справа оказалась в основном моими личными заметками, переживаниями и впечатлениями, которые я какое-то время начал записывать просто для себя.

И это, наверное, один из моментов, ради которых мне вообще нравится заниматься такими инструментами. Я вручную не создавал категорию «вот здесь мои мысли о себе». Она просто появилась из содержания самих заметок.

Если будет интересно, потом могу отдельно написать, как сейчас устроено моё собственное хранилище: какие директории, теги и плагины я использую. За время работы с Obsidian там уже образовалась довольно конкретная система (многим это будет даже интереснее, чем плагин).

Что в итоге изменилось на самом деле

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

Раньше я смотрел на Veynrel примерно так: вот генерация MOC, вот AI-команды, вот semantic search, вот flashcards, вот поиск сироток. Сейчас я пытаюсь смотреть на него как на один workspace вокруг состояния базы знаний.

Сначала человек может вообще ничего не подключать и посмотреть на локальную структуру vault.

Потом, если ему нужна работа с содержанием заметок, он подключает LLM.

Если нужен поиск уже не по ссылкам и словам, а по смыслу, появляется semantic layer с embeddings.

А Recall при этом вообще может жить локально своей жизнью.

Я не знаю, является ли именно эта концепция тем самым ответом, который я искал, когда начал смотреть на Copilot и Claudian. Возможно, через месяц опять открою плагин как пользователь и скажу себе: «ну нет, опять не то». С моим проектом такое уже происходило далеко не один раз. Из-за этого мне и нужна ваша критика, об этом ниже.

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

И это уже намного лучше, чем «у меня есть двадцать инструментов для использования AI».

Немного про критику

Наверное, это и есть главная причина, зачем я вообще решил написать эту статью.

Когда долго работаешь над своим проектом, очень легко начать воспринимать собственные решения как очевидные. Добавил функцию, сам понял, зачем она нужна, сам ей воспользовался, значит всё хорошо.

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

Поэтому мне сейчас особенно интересна критика.

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

Как говорится, в споре рождается истина. Главное, чтобы сам спор в итоге не оказался единственным готовым продуктом.