Перейти к содержимому
RAGRAF

Проблематика: почему агенту нельзя доверять память о коде

Три измеренных симптома слепого агента, данные VulnGym и LLMxCPG, и пять анти-требований, из которых выросла архитектура RAGRAF.

Три симптома, которые знает каждый #

Кто поручал LLM-агенту правку в существующем проекте, видел это своими глазами.

Симптом 1 — агент не находит место #

Он уверенно отвечает про функцию, но проверка показывает: функция в другом файле, а описанная — одноимённая. В репозитории на 392 файла grep по имени даёт десятки совпадений, и нужное — не первое.

Измерение на пилотном корпусе: rg находил файлы, но не локализовал — 34 кандидата на SuperAdmin, 44 на z_e(, 64 на namespace BatteryService. Найти файл легко; найти место в файле — нет.

Симптом 2 — агент не знает схему БД #

Колонку назвали «как переменную в соседнем модуле» — AuthorUserID. Такой колонки нет. Это выясняется в рантайме, а не при чтении:

Text
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'AuthorUserID' in 'field list'

В журналах колонка называлась InitiatorID, а UpdatedBy существовала ровно в одной таблице из пяти. Код был уже написан, когда ошибка проявилась. Именно этот случай породил операцию write_check — сверку плана записи со схемой до запуска.

Симптом 3 — агент не знает, что устарел #

Самый неприятный. Индекс построили, код потом правили — агент получает факты о состоянии, которого уже нет. Отличить «такой функции нет» от «функция появилась после индексации» по ответу невозможно. Решение принимается по этому ответу, и оно неверно.

Что говорят измерения, а не мнения #

Навигация — доминирующая причина провалов #

Исследование VulnGym (цитируется в статье Positive Technologies о графах в аудите кода): в задачах, где цепочка от источника до стока длиннее девяти шагов, лучший агент находил 22,58% уязвимостей, а в 56,5% провалов он либо вообще не находил нужный файл (26,8%), либо находил файл, но не строку (29,7%).

Больше половины отказов — не про анализ. Про навигацию. Именно её RAGRAF выносит из модели во внешний индекс.

Свободные запросы к графу — генератор ошибок #

LLMxCPG (USENIX Security '25) измерил, как базовые модели генерируют CPGQL-запросы к графу кода:

МодельВалидных запросов
Базовая, конфигурация A19 из 1278
Базовая, конфигурация B132 из 1278
Дообученная генерировать запросы1278 из 1278

Дообученная модель идеальна — потому что её учили именно этому. Произвольного агента никто не учил. Отсюда архитектурное решение P4: языка запросов нет вовсе, только фиксированные операции. Подробнее — в архитектуре.

Смежная литература, на которую опирается дизайн #

  • ALIBI — инъекции в комментариях подавляют обнаружение в >90% случаев → свободный текст хранится отдельно от структурных фактов (text_chunk против node/edge).
  • «Words Speak…» — формулировка сдвигает вердикты на 33,2% → нейтральность формулировок ответов — требование (ФТ-23).
  • Repoformer — право не искать даёт до 70% ускорения → честное «не найдено» лучше приблизительного ответа (принцип P8).
  • LCIP (arXiv:1512.07435) — измеренный потолок impact-анализа: precision 69% / recall 79% / F1 55% (Java, мутационная симуляция). Используется только как ориентир порядка величины для метрики M2 — никогда как «цель для отчёта».
  • AdaRepair-Mem — «больше памяти ≠ лучше» → журнал расхождений архивируется и дедуплицируется, а не копится вечно.

Почему существующие инструменты не закрывают задачу #

АльтернативаЧто даётЧего не хватает
rg/grep + чтение файловуниверсальность, ноль настройкилокализация (34–64 кандидата); нет структуры; нет понятия свежести; каждое чтение жжёт контекст
LSP / индексы IDEточные определения и ссылкисервер на каждый язык; нет связей код↔схема БД↔маршруты; не рассчитан на агента; нет контракта provenance/staleness
Обычный RAG (чанки+эмбеддинги)семантический поискнет «кто кого вызывает»; нет фактов схемы; молча устаревает после правок; сходство ≠ структура
Графовая БД + Cypher/CPGQLполная выразительностьмодель не пишет валидных запросов (19–132/1278); сервер СУБД; факты без контракта доказательств
Joern/CodeQL напрямуюнастоящий CPG, taint-анализвес JVM/CodeQL; снова язык запросов; нет журнала свежести; нет фиксированного контракта для агента

Позиция RAGRAF: взять структурную точность графа и семантический охват векторов, обернуть в фиксированный, проверяемый, знающий свою свежесть контракт из 22 операций и хранить всё в файлах SQLite — ноль сервисов. Опциональный слой Joern добавляет межпроцедурную достижимость, а его отсутствие сообщается, а не прячется.

Пять анти-требований: проблему, которую индекс не имеет права создать #

Индекс — это кэш. Кэш, который врёт, хуже отсутствия кэша:

  1. Молчаливое устаревание — факты прошлой недели выдаются за текущие. → Иммутабельная ревизия + append-only журнал расхождений + stale_warnings в каждом конверте + авто-реиндекс по контрольной сумме. Механика — в статье свежесть и дрейф.
  2. Галлюцинированные связи внутри индекса — правдоподобное ребро «потому что из базы». → Каждое ребро несёт evidence path:line, provenance (stated/derived) и confidence; эвристические рёбра — отдельный вид и отдельный блок ответа.
  3. Непроверяемые ответы — агент не может проверить полученное. → 100% фактов о коде с path:line; метрика M6 подтверждает ссылки автоматически.
  4. Маскарада возможностей — «slice вернул пусто» против «slice в этом режиме невозможен». → capability: full|degraded в каждом конверте; отказ с кодом 3 и причиной; молчаливая деградация запрещена конструктивно.
  5. Утечка секретов в индекс — значения из конфигов и .env в векторах. → secrets_exclude — исключение, а не маскировка: файлы не открываются вовсе; телеметрия хранит хеши аргументов и 60-символьный дайджест.

Эти пять анти-требований сформировали архитектуру сильнее, чем любая фича. Каждое отображено в проверяемый механизм — см. архитектуру и протоколы приёмки (метрики M5/M6, тест M3-T2).

Что это дало на практике #

Одна реальная доработка (раздел документооборота /agreements/: журналы платежей, договоров, отпусков), 46 обращений к операциям, 0 ошибок выполнения:

РезультатЧисло
Дефектов найдено/предотвращено в разрабатываемой подсистеме9 (3 из них — обрывы маршрута согласования и неработающая защита полей)
Дефектов самого RAGRAF найдено той же работой13 (5 невидимы синтетическим тестам: «код работал правильно, а измерял себя неправильно»)
Предотвращено до написания кода4 (один — операцией schema, остальные — write_check/policy_trace)
Доминирующий класс дефектов«работает, но не так» — 8 из 16: страница открывается, исключений нет, а отклонённый документ — тупик, и любой сотрудник может отметить платёж оплаченным

Ни компилятор, ни тест «страница открывается» такой класс не ловят. Ловят — точная схема в индексе, статическая трассировка прав и проверки под реальной ролью. Полные разборы — в статьях замеры и границы и перспективы.

Границы: чем RAGRAF не является #

  • Не переписыватель кода. RAGRAF отвечает; правит агент. Автономного патчинга нет.
  • Не сканер уязвимостей. Taint-анализ не цель релиза (слой CPG делает его возможным в будущем — см. перспективы).
  • Не сервис. Ни демонов, ни сетевых эндпоинтов, кроме loopback-панели. Три файла SQLite в <project>/.ragraf/.
  • Не оракул. Каждый ответ перечисляет свои limits («статический анализ; динамика не покрыта»), а честная общая проверяемость по всем операциям — 24,4%, потому что у фактов схемы не существует номеров строк. Мы не дорисовываем ссылки для красоты отчёта.

Дальше: архитектура — три файла SQLite и ноль инфраструктуры →