← Назад Harupa
2026-07-04RU EN UA BY PL

My Architect, часть 10: память о репозитории — код-граф, которому запрещено верить на слово

Это десятая статья серии про My Architect и продолжение девятой — истории скила recursive-context, который учит агента дисциплине работы с данными больше контекстного окна. На этот раз: четыре релиза за один день (v1.14.0 → v1.16.1), которые дали агенту персистентный граф кода, и контролируемый эксперимент, который измерил его реальную пользу вместо пересказа чужого маркетинга. Все числа наших прогонов — из телеметрии реальных запусков 2026-07-03; факты о Graphify — из его README и живой установки; оценки помечены явно. Ничего не додумано.

Пролог. Агент с амнезией

У LLM-агента нет памяти между сессиями. Каждый новый разговор о твоём репозитории начинается с чистого листа: агент снова grep'ает, снова читает те же файлы, снова строит в голове ту же карту — и выбрасывает её в конце сессии. Мы это измерили на собственном репозитории: сбор фактов о хорошо знакомой кодовой базе стоил ~77 тысяч токенов и 20 вызовов инструментов — и так каждую сессию, по кругу.

Отдельная беда — вопросы вида «что сломается, если я поменяю сигнатуру этой функции?». Ответ требует знать всех потребителей символа. Grep находит их, но за цену полного прохода; а главное — у агента нет места, где это знание могло бы пережить сессию.

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

Глава 1. Не строить своё: Graphify

Инструмент для персистентной памяти о коде уже существует — Graphify (open source, MIT, ~77k звёзд): парсит код tree-sitter'ом полностью локально (36 языков, без API-вызовов и телеметрии), складывает граф символов и связей в graphify-out/graph.json, рядом кладёт интерактивную HTML-карту и готовый текстовый отчёт о репозитории. Свежесть индекса поддерживают git-хуки; для командной работы есть merge-driver, чтобы граф можно было коммитить без конфликтов.

Мы сознательно не стали строить свой граф-движок. Вместо этого научили наш скил decomposition-дисциплины распознавать лежащий в репозитории индекс и правильно им пользоваться. Вся фича — это markdown-справочник с правилами плюс несколько строк-указателей: у плагина не появилось ни одной жёсткой зависимости. Нет графа — всё работает как раньше; нет графа, а задача тяжёлая — агент один раз предложит владельцу поставить и собрать (и сделает это сам после «да»; молчаливые установки запрещены).

Насколько это дёшево на практике: на нашем репозитории (351 файл) сборка графа заняла секунды и дала 2655 узлов, 4948 рёбер и 153 автоматически найденных кластера — 6.1 МБ на диске, ноль API-затрат. Один запрос после этого выглядит так:

$ graphify explain "getBlockers"
Node: getBlockers()   Source: src/hierarchy/model.ts L419   Degree: 9
  <-- NodePropertiesPanel.tsx [imports]
  <-- validator.ts [imports]
  <-- validateHierarchy() [calls]
  ...

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

Глава 2. Правило, ради которого всё затевалось

Индекс — это кэш, а кэш умеет врать: он отстаёт от кода между пересборками. Поэтому в центре фичи не команды, а три правила:

  1. Граф — навигация и кандидаты, НЕ источник фактов. Ответ графа говорит, КУДА смотреть; фактом становится только то, что подтверждено живым файлом. Контракт фактов ({утверждение, путь-доказательство, уверенность}) не изменился ни на йоту.
  2. Freshness-check обязателен. Первым делом агент сравнивает время сборки индекса с временем последнего коммита. Протух — сказать вслух, предложить владельцу пересборку (а если git-хук не стоит — один раз предложить и хук, чтобы протухание не повторялось каждую сессию), кандидатов помечать «по устаревшему индексу».
  3. Verify — экономно, регионами. Кандидат приходит с путь:строка — проверка читает ±30 строк вокруг, а не файл целиком. Без этого правила проверка съедает всё, что граф сэкономил на поиске.

Глава 3. Как мы это тестировали

Скил — это текст, который должен менять поведение модели. Текст не скомпилируешь и юнит-тестом не покроешь, поэтому проверка — слоёная, и каждый слой ловит свой класс ошибок. Железное правило всех релизов: тесты пишутся до правок, и сначала фиксируется «как агент ошибается без них».

Слой 1 — триггеры: загрузится ли скил сам. Судьи видят только имя и описание скила (ровно то, что видит Claude Code при выборе) плюс запрос; три независимых судьи на кейс; в наборе — позитивы на двух языках, коварные почти-попадания («переименуй переменную» — похоже на impact-вопрос, но скил должен молчать) и кросс-проверка, что новый скил не ворует триггеры соседнего. Финальный прогон: 30/30 голосов ровно по ключу.

Слой 2 — поведение: соблюдается ли дисциплина. Исполнитель читает скил с диска и пишет план по сценарию; независимый грейдер построчно сверяет с заранее записанными утверждениями. Каждый сценарий бьёт в конкретный способ сломаться: свежий граф → запросы до grep, но факты по файлам; протухший → заметить и не доверять; графа нет → предложить один раз и не ставить молча.

Слой 3 — злое ревью самого текста. Отдельный ревьюер не читает скил вежливо — он его исполняет: запускает шелл-сниппеты на граничных входах, поднимает docker, сверяет каждое имя команды с README апстрима.

Слой 4 — live-прогоны: настоящая установка (по согласию), настоящий граф, настоящий агент на настоящем репозитории.

Слой 5 — контролируемый эксперимент (глава 5): когда качественные проверки пройдены, остаётся вопрос «а сколько это стоит?» — и на него отвечает только A/B с телеметрией.

Глава 4. Что поймали до эксперимента. Четыре истории

Ловушка условного роутинга. Мы добавили ветку «графа нет → предложи владельцу собрать» — а ревью показало, что она недостижима: все указатели, ведущие агента в справочник графа, начинались со слов «если граф ЕСТЬ». Агент без графа просто не открыл бы файл с предложением. Классическая ошибка: новая ветка есть, но все дороги к ней гейтятся на состояние, при котором она не нужна.

Баг, который жил бы только на Linux. Сниппет freshness-проверки использовал macOS-синтаксис stat с фолбэком на GNU-вариант. Ревьюер поднял docker и показал: у GNU stat тот же флаг значит другое — команда «успешно» печатает мусор, и гейт вечно отвечает STALE. На любой Linux-машине graph-first не включился бы никогда, тихо.

Апстрим знает лучше. Мы советовали прятать graphify-out/ в .gitignore — а README Graphify рекомендует командам граф коммитить (общая карта; конфликты решает их merge-driver). Наш совет молча выбрасывал командный сценарий; теперь предложение честно даёт выбор. Заодно выяснилось, что PyPI-пакет называется graphifyy — с двумя «y».

Исполнитель, который не поверил условию задачи. Сценарий поведенческого теста утверждал «граф свежий» — агент проверил mtime по-настоящему, обнаружил, что граф его реального окружения протух (наши же коммиты успели его обогнать), и обработал расхождение по рецепту. Дисциплина «не доверяй, проверяй» сработала даже против текста задания.

Отдельным заходом мы провели аудит «до чего агент не додумается сам» — и закрыли три пробела: impact-вопросы получили собственный вход в скил (раньше «что сломается, если поменяю X?» вне трекаемой задачи не вело к графу вообще), готовый отчёт GRAPH_REPORT.md теперь читается первым при аудите (сводка уже посчитана — глупо собирать её запросами), а при протухшем графе без хука агент предлагает хук.

Глава 5. Эксперимент: меряем сами, а не цитируем маркетинг

Дизайн. Незнакомый обеим группам крупный репозиторий — NestJS, 2125 файлов (на порядок больше нашего). Два идентичных клона: плечо A — с графом (12309 узлов, 22895 рёбер, 738 кластеров; собран tree-sitter'ом за секунды, $0), плечо B — без. Три класса вопросов, промпты идентичны с точностью до пути, про граф — ни слова (плечо A обязано найти его само по дисциплине скила), одна модель (Sonnet), один контракт вывода (≥8 фактов с путями + честное покрытие + журнал всех вызовов). Метрики — токены и вызовы из телеметрии, качество — количество фактов и выборочная сверка путей.

Результаты:

Класс вопросаA (граф)B (без графа)Дельта
Impact: «потребители ModuleRef, что сломается при смене get()»71.4k токенов · 26 вызовов · соло · 12 фактов71.9k · 31 вызов · соло · 14 фактовпаритет (A: −16% вызовов и времени)
Понимание: «HTTP-request lifecycle end-to-end»75.9k · соло · 13 фактов56.1k сверху + 205.8k в скрытых суб-агентах (измерено) ≈ 262k · 21 фактграф дешевле в ~3.5× (−71%)
Карта: «packages/core: модули, API, риски»≈445k (8 суб-агентов, оценка*) · 24 факта≈500k (9 суб-агентов, оценка*) · 25 фактов~паритет

\* Оценка вложенной стоимости откалибрована на единственной точно измеренной ячейке (Q2-B: 205 759 токенов на 5 агентов) и помечена как оценка; направление погрешности неизвестно.

Вывод 1: экономия настоящая, но селективная. На классе «понять подсистему» — измеренные −71%: не потому что запросы графа дешевле grep, а потому что граф сразу сузил корпус, и один агент справился там, где плечо B было вынуждено молча развернуть пятерых. На impact-классе экономия — ноль (grep по буквальному имени символа дёшев даже на 2125 файлах; выигрыш графа там — полнота и скорость, не токены). На полном аудите — ноль (оркестрация нужна обоим; граф даёт готовое партиционирование и кандидатов, не скидку). Универсального множителя «90%» не существует — существует класс задач.

Вывод 2, методологический — скрытая оркестрация. По верхним цифрам плечо B на «понимании» выглядело дешевле (56k против 76k) — пока мы не вскрыли в его транскриптах 205.8k токенов вложенных агентов. Красивые сравнения «агент с инструментом против агента без» систематически недооценивают плечо, которое тихо делегирует. Честный замер обязан считать всё дерево агентов — подозреваем, что немалая часть популярных цифр этим не занимается.

Дисциплина недоверия к графу отработала и внутри эксперимента: плечо A на impact-вопросе отсеяло ложноположительных кандидатов графа (barrel-импорты) живым grep'ом, а на аудите верифицировало циклы из графа и два ложных отбросило. Граф давал наводки — фактами становилось только подтверждённое.

Бонус, которого не заказывали: аудит-агенты плеча B нашли в ядре NestJS два похожих на настоящие бага (присваивание в Map через индекс, которое никогда не читается; return вместо continue, молча обрезающий регистрацию middleware) — незапланированное подтверждение, что глубина аудита у дисциплины настоящая.

Ограничения — честно: n=1 на ячейку (без дисперсии), один репозиторий, один тир модели; вложенная стоимость двух ячеек оценена по калибровке на единственной точно измеренной (и помечена как оценка); плечи сами выбирали стратегию — это фича дизайна (измеряем систему целиком), но она смешивает «граф» с «решением не оркестрировать»; долгосрочная амортизация повторных сессий этим экспериментом не измерялась — она остаётся гипотезой с сильным априори (~77k «налога на переизучение» за сессию против одноразовой бесплатной сборки).

Глава 6. Было → стало

БылоСтало
Знание репо между сессиямине переживает сессию; ~77k токенов на переизучениеграф собирается за секунды один раз; кандидаты — мгновенные бесплатные запросы
«Кто зовёт X / что сломается?»полный grep-проход; вне трекаемой задачи агент про граф не зналсамостоятельный вход: graphify affected "X" → verify регионами ±30 строк — включая indirect-связи, которых grep не видит
Понимание подсистемы на большом репоскрытый fan-out суб-агентов, ~262k токеноводин агент с графом, ~76k — −71%, измерено
Аудит репозиторияразведка с нуляGRAPH_REPORT.md (готовые хабы, кластеры, неожиданные связи) читается первым
Доверие к индексуfreshness-гейт; протух → вслух + предложение пересборки и хука; факты — только по живым файлам
Установкапо явному «да» владельца: агент предлагает один раз, ставит и собирает сам; отказ уважается всю сессию

Эпилог

Формула фичи: существующий инструмент (Graphify) + дисциплина обращения с ним (наш скил) + слоёные тесты, которые не верят никому — ни автору текста, ни красивому сниппету, ни условию собственного сценария, ни, как выяснилось в главе 5, верхней строчке чужого бенчмарка. Из всех существенных находок четырёх релизов ни одна не была найдена «вычиткой» — все принесло исполнение: судьи, грейдеры, docker, живые прогоны и телеметрия эксперимента.

Осознанно отложено: триаж pull request'ов через граф (у нас нет PR-процесса — инструмент без потребителя не делаем), петля памяти запросов save-result/reflect (отдельный разговор) и повторение эксперимента с n>1 и вторым репозиторием — если селективная экономия подтвердится и там, у «90% хайпа» появится честная замена: «−70% на классе „понимание“, ноль на остальных».

Как попробовать

Код-граф едет в плагине my-architect начиная с v1.14.0 (актуальная — v1.16.1). Руками звать не нужно: скил сам распознаёт граф в репозитории, а на тяжёлой задаче без графа — сам предложит поставить.

  1. Заведи аккаунт на my-architect.app, возьми токен на странице API Keys и экспортируй его в той же сессии, где запускаешь Claude Code:

``bash export MCP_API_KEY=mcp_ВАШ_ТОКЕН ``

  1. Добавь маркетплейс и поставь плагин:

`` /plugin marketplace add d7561985/my-architect-marketplace /plugin install my-architect@my-architect-marketplace ``

  1. Граф можно собрать и руками, не дожидаясь предложения агента:

``bash pip install graphifyy && graphify build . ``

  1. Уже стоит плагин? /plugin marketplace update my-architect-marketplace/plugin update my-architect.

---

Факты и ссылки