moat

Хватит защищать свой ров документами (3/5)

Ров, который держится на регламентах, начинает гнить в тот день, когда в команду приходит новичок или кто-то правит код в три часа ночи. У меня на каждом коммите работают две проверки: одна роняет сборку, если в код вернулось запрещённое, вторая роняет wiki, если из неё выпал добытый урок. Обе запечатаны в одной HMAC-цепочке.

8 мин чтения

Большинство AI-продуктов защищают свой ров регламентами, а регламенты тихо гниют в тот день, когда в команду приходит новичок или кто-то правит код в три часа ночи. Я защищаю свой двумя жёсткими проверками, и обе идут на каждом коммите. Первая роняет сборку, если я всё-таки протащил в продукт то, что клялся никогда не строить. Вторая роняет wiki, если добытый кровью урок стало нельзя найти запросом или он начал противоречить сам себе. Обе запечатаны в общей HMAC-цепочке. Так получается единственный ров, который щёлкает только в одну сторону: каждый отказ и каждый урок делают систему строго труднее откатить назад и строго полезнее, чем вчера.

На каждом коммите у меня запускаются два скрипта.

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

Две зеркальные колонки над общей HMAC-цепочкой: слева код проходит через grep, который ищет запрещённое, пока сборка не упадёт; справа wiki проходит через lint, который ищет недостающее, пока не сломается целостность; события с обеих сторон пишутся в один и тот же журнал, где подделка сразу заметна.

Второй обходит wiki и задаёт обратный вопрос: чего здесь нет, хотя должно быть? Утверждение без источника. Страница о понятии, на которую никто не ссылается. Два текста, написанные с разницей в полгода и спорящие друг с другом. Первый скрипт не даёт системе превратиться в CVE. Второй не даёт ей забыть то, что она уже поняла.

Весь lodos построен вокруг одного правила. Каждый значимый отказ живёт в сборке как grep. Каждое значимое знание живёт как версионируемая страница, к которой можно обратиться запросом.

Обе половины лежат внутри HMAC-цепочки, которая громко рвётся при первой же попытке подправить что-то задним числом. Запишите то, что отказываетесь добавлять. Запишите то, что отказываетесь забывать. И заставьте сборку не выпускать релиз, пока нет обоих списков. Большинство AI-продуктов записывают, какими хотят быть, и защищают это регламентами. Кто-то записывает, какими отказывается становиться, и защищает это кодом. Совсем немногие записывают ещё и то, что отказываются забывать, и защищают это через wiki, которая заваливает собственную проверку целостности, стоит одной странице уплыть в сторону. Только у последних ров прирастает сам, как сложный процент.

Слева ров на регламентах: письменное обещание проходит через новичка в команде, через рефакторинг и через тихий дрейф, никто ничего не замечает, и кривая доверия сползает к нулю. Справа ров-храповик: grep и lint громко падают на каждом коммите, и за те же двенадцать месяцев кривая доверия поднимается ступенями.

Что разрушается, а что нет?

Ров на регламентах умирает тихо. «Мы никогда не пишем секреты в логи». «Вывод каждого инструмента у нас изолирован». «Мы проверяем данные до того, как их увидит модель». Такие фразы живут в документах SOC 2 и в день написания честны. Потом новичок добавляет отладочный вывод. Рефакторинг прячет очистку данных за флагом. Фраза в документе остаётся прежней, система ведёт себя иначе. Ничего не падает. Никто не замечает.

Ров на знании рушится ещё быстрее. В два часа ночи основатель наконец понимает, почему ушёл клиент, кидает мысль в Slack и через неделю о ней не помнит. Урока нет нигде, где его можно было бы найти запросом. Через три месяца та же история повторяется, и за тот же урок он платит второй раз. Это самая дорогая потеря, потому что о ней никто так и не узнаёт.

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

Цикл из трёх операций: загрузка превращает сырые источники в черновик страницы, который сначала проходит через окно с построчным сравнением и только потом становится каноническим; запрос собирает ответ со ссылками на источники, и такой ответ можно сохранить обратно страницей; проверка ищет страницы-сироты, утверждения без источников и противоречия. Ниже перечислены три отличия lodos: зашифрованное хранилище на SQLite, право агента только предлагать и журнал, запечатанный HMAC-цепочкой.

Подход Karpathy, переведённый в продукт

Половина про знание придумана не мной. Andrej Karpathy описал wiki для языковой модели: три операции и одно утверждение. Wiki здесь работает как исполняемый файл, сырые источники как исходный код, модель как компилятор, lint как тесты, а запросы как выполнение программы.

Я взял этот подход целиком и осознанно поменял три вещи.

  • Хранилище. У Karpathy это папка markdown-файлов под git. У меня SQLite и хранилище, зашифрованное посекционно. Я меняю совместимость на конфиденциальность. Чистый экспорт в markdown никуда не делся, так что Obsidian всегда под рукой.
  • Свобода агента. Karpathy разрешает модели писать напрямую. У меня любая правка идёт через окно сравнения, где я её одобряю. Канонической страницы агент не касается вообще. На каждое одобрение уходит от пяти до десяти секунд, зато агент не может тихо переписать моё поведение.
  • Аудит. Karpathy ведёт обычный markdown-лог. Я веду HMAC-цепочку, подписанную производным ключом от мастер-пароля. Подделанный журнал всплывает разрывом цепочки при следующем запуске.

Это не спор о философии. Это перевод личной практики в коммерческий продукт: дух сохранён, а цена уже такая, какую платит покупатель, которому нужен комплаенс.

Обе половины щёлкают только вперёд

Сторона отказов и сторона знания устроены одинаково. Одна ищет то, чего быть не должно никогда. Вторая ищет пропажу того, что быть обязано. Обе проверяют отсутствие, обе падают громко, обе запечатаны в одной HMAC-цепочке.

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

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

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

Плата за такой ров и есть его смысл

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

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

То, что я получаю взамен, не влезает в рекламный текст, зато отлично звучит в разговоре с покупателем из регулируемой отрасли. Такой покупатель слышит «мы не можем» совсем не так, как «мы обещаем, что не будем». Специалист по комплаенсу смотрит на HMAC-цепочку, где подделка сразу заметна, иначе, чем на markdown-лог. А основатель, который читает wiki сегодня, на самом деле пишет её для себя же через полгода. Ни до кого из этих людей маркетинговым обещанием не достучаться.

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

Пять деталей, которые можно украсть

Ни одна из них не требует остальных. Вместе они складываются в общий подход.

  1. Декларативный движок автоматизаций, в котором нет вычислителя выражений ни в каком виде.
  2. Хранилище из двух частей, устроенное так, что модель физически не может вернуть секрет в открытом виде.
  3. Скрипт инвариантов на этапе сборки, каждая проверка которого доказана намеренным нарушением.
  4. Wiki с тремя операциями Karpathy, журнал которой обёрнут в HMAC-цепочку.
  5. Цикл обучения, в котором агент только предлагает и никогда не пишет собственное поведение.

Порядок внедрения любой. Общее у них ровно одно: запишите, что отказываетесь выпускать и что отказываетесь забывать, поставьте на оба списка проверку в сборке и убедитесь, что проверки живые, попробовав нарушить их нарочно. Дальше начинается обычная инженерия.

Следующий цикл из шести шагов: агент слушает разговор, замечает пробел в своих навыках и готовит предложение; предложение упирается в окно с построчным сравнением, отмеченное как единственный проход; там человек одобряет или отклоняет, и только после этого правка становится канонической и уходит в релиз, а само предложение и решение по нему попадают в HMAC-цепочку.

Что дальше?

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

Записаться в лист ожидания

moatwikiбезопасностьarchitecture

Запусти свою компанию на собственной машине.

Записывайся в лист ожидания и получи доступ первым, когда lodos откроется.