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

Свежесть и дрейф: как индекс не даёт себе врать

Иммутабельные ревизии, append-only журнал расхождений, сторож контрольных сумм, метки stale и архивы «что изменилось в знаниях» — главная гарантия RAGRAF.

Проблема, которую решает механика #

Индекс — это кэш. Кэш, который устарел молча, хуже отсутствия кэша: агент принимает решения по фактам о состоянии кода, которого уже нет. Классический сценарий провала:

  1. Индекс построили в понедельник.
  2. Во вторник разработчик переименовал функцию и удалил таблицу.
  3. В среду агент спрашивает «кто вызывает старую функцию» — получает честный, доказанный, устаревший ответ и строит на нём правку.

RAGRAF не обещает «индекс всегда свежий» — это дорого и недостижимо при активной работе. Он обещает другое: устаревание всегда видимо. Ответ либо подтверждает свежесть, либо перечисляет, что разошлось, либо отказывается отвечать. Молчаливой выдачи прошлого за настоящее в архитектуре нет (анти-требование №1, принцип P2).

Четыре механизма #

1. Иммутабельная ревизия #

Индекс строится снапшотом: corpus_hash = sha1(отсортированный список (path, sha256)) фиксирует точное состояние корпуса. Внутри ревизии факты не правятся и не дописываются — только новая ревизия. Следствие: любой ответ воспроизводим — index_rev в конверте однозначно определяет набор фактов.

2. Append-only журнал расхождений (drift ledger) #

Пока агент работает, код правится. Каждая правка регистрируется журналом, а не индексом:

ПолеНазначение
entry_id, revсвязь с ревизией, по которой живёт журнал
changecreate / modify / delete — что случилось с файлом
path, detected_by, detected_atгде, кем замечено (guard / явный вызов), когда
old_sha256, new_sha256точные версии содержимого
affected_nodes, affected_symbolsкакие узлы и символы затронуты
dedupe_key (UNIQUE)повторная регистрация того же события не создаёт дублей

Журнал — единственное место, где живут изменения между ревизиями. Он не удаляется и не перезаписывается: история расхождений — часть аудиторского следа продукта.

3. Сторож контрольных сумм (guard) #

Дешёвая проверка, встроенная в каждый запрос: corpus_hash пересчитывается (или сверяется по кэшу состояния) и сравнивается с ревизией индекса. Расхождение → журнал дополняется, а конверт ответа получает:

JSON
"drift": { "files": 3, "symbols": 7, "entries": 4 },
"stale_warnings": [
  { "path": "src/Pipeline/PipelineEngine.php",
    "reason": "modify после rev-0007",
    "suggest": "live_read" }
]

Политика действия настраивается (index.reindex_on_drift): warn (пометить и отдать), auto (переиндексировать прозрачно), fail (отказать — для строгих конвейеров). Факты, затронутые журналом, помечаются stale в самих результатах — устаревшее нельзя перепутать с отсутствующим: это разные ответы.

4. Архив журнала и отчёт «что изменилось в знаниях» #

При переиндексации журнал не исчезает бесследно — он архивируется в drift_archive вместе с ревизией, которая его закрыла, и превращается в человекочитаемый отчёт: какие файлы разошлись, какие символы были затронуты, что индекс «узнал заново». Это редкая для RAG-систем возможность: ответить на вопрос «что изменилось в знаниях о коде между понедельником и средой» — не по git-истории, а по истории фактов. Дедупликация и архивирование вместо бесконечного накопления — прямой вывод из AdaRepair-Mem («больше памяти ≠ лучше»).

Как это выглядит в ответах #

Три честных состояния вместо одного «ответа»:

СостояниеЧто видит агентПример
Свежоdrift.files = 0, предупреждений нетобычный ответ
Разошлосьstale_warnings со suggest + метки stale у затронутых фактов«функция найдена, но её файл правился после ревизии — перечитай live_read»
Невозможноотказ с причиной (код 3)журнал противоречит состоянию, требуемая операция несовместима с режимом

suggest обязателен: предупреждение без инструкции — половина пользы. Варианты: live_read (прочитать файл напрямую — всегда доступно), reindex (пересобрать ревизию).

Проверено протоколами, а не обещанием #

Механика защищена приёмочными тестами, зафиксированными в docs/architecture.md §8.5:

  • M5-T2 — журнал не теряет события при параллельных правках одного файла: dedupe_key и порядок регистрации проверены сценарием.
  • M5-T3 — устаревший факт не может быть выдан за свежий ни одной операцией: сквозная проверка по всем операциям, читающим граф.
  • M5-T4 — архив журнала восстанавливает полную картину расхождений закрытой ревизии.
  • M6 — 100% фактов о коде имеют проверяемые path:line (скрипт верификации ссылается на реально извлечённые артефакты).

Границы механики #

Честность требует назвать, чего механика не делает:

  • Не отслеживает изменения внутри ревизии автоматически в реальном времени — guard срабатывает на запросе или по команде; файловый вотчер не поставляется (перспектива, см. future).
  • Не чинит CPG-слой по дельте — Joern не имеет инкрементальной пересборки; после правок, затрагивающих межпроцедурные связи, slice честнее пересобрать целиком.
  • corpus_hash — хеш списка файлов, а не дерева каталогов — переименование каталога без изменения файлов даёт тот же хеш содержимого файлов, но разные пути; guard сравнивает и пути.
  • Не защищает от правок самого .ragraf/ — файлы индекса лежат рядом с кодом; их целостность — ответственность файловой системы и владельца (поэтому .ragraf/ добавляется в .gitignore, а не в репозиторий).

Почему это WOW #

Сравните с обычным RAG: чанки переиндексируются «когда-нибудь», поиск возвращает фрагменты без понятия возраста, и ни один ответ не говорит «я основан на вчерашнем коде». RAGRAF делает свежесть частью контракта каждого ответа — drift, stale_warnings, index_rev и capability присутствуют всегда, даже когда всё хорошо. Агент (и человек, читающий его отчёт) всегда знает, насколько можно доверять факту. Это и есть разница между «поиском по коду» и базой знаний.


Дальше: справочник 22 операций — что спрашивать и как читать ответы →