RAGRAF — локальная база знаний о коде: индекс репозитория, который отвечает LLM-агенту фиксированными операциями: где определён символ, кто его вызывает, что сломается при правке, какие колонки есть в таблице. Каждый факт приходит со ссылкой файл:строка, с пометкой происхождения (stated — доказано анализатором, derived — эвристика) и с указанием ревизии индекса, по которой он получен. Граф, полнотекстовый индекс и векторы лежат в трёх файлах SQLite внутри каталога .ragraf/ самого проекта — ни сервера, ни облака, ни подписки.

Ниже — разбор продукта целиком: какая проблема решается, что именно он умеет (52 операции фасада, 59 инструментов MCP, разбор деревом на 29 языках), почему его ответам можно верить, где границы, а затем установка и настройка по шагам — от чистой машины до первого запроса агента. Всё проверяемое: версия 0.3.0, команды и числа взяты из репозитория и с сайта продукта, а не пересказаны по памяти.
TL;DR: что это и как проверить за десять минут
- Задача. Больше половины отказов агента в чужом коде — это не анализ, а навигация: найти файл, найти строку. RAGRAF выносит навигацию из модели во внешний индекс, который отвечает фактом, а не списком кандидатов.
- Форма ответа. 52 фиксированные операции (
symbol,callers,impact,slice,schema,write_check,policy_trace,search…) и 59 инструментов MCP. Языка запросов к графу нет намеренно: по замеру LLMxCPG (USENIX Security ’25) базовые модели дают валидный запрос к графу кода в 19–132 случаях из 1278. - Доказательство в каждом факте.
path:line,provenance,confidence,index_rev. Эвристические рёбра помечены отдельно и в один блок с доказанными не смешиваются. - Устаревание видно, а не скрыто. Ревизия иммутабельна, правки копятся в append-only журнале расхождений, затронутые факты получают
staleв самом ответе, сторож контрольных сумм переиндексирует при расхождении. - Локально и без инфраструктуры. Три файла SQLite в
.ragraf/проекта, эмбеддинги считаются на месте (модель 384 измерения, ~240 МБ, без torch и без облака). Удалили каталог — удалили индекс. - Экономия измерена. На 14 навигационных задачах реального PHP-портала: 14 вызовов против 28 (реалистично 43), 0 полных чтений файлов против 14, попадание в файл-цель с первого запроса 92%.
- Десять минут на проверку — после установки:
ragraf ensure C:\work\myproject # найти/создать корпус, пересобрать отставший индекс
ragraf -p <конфиг> status # ревизия, покрытие, деградация, размер журнала
ragraf -p <конфиг> query symbol --name corpus_hash
ragraf -p <конфиг> query write_check --table doc_payments --columns "amount,AuthorUserID"
ragraf -p <конфиг> check # разошёлся ли индекс с кодом
Если symbol вернул путь и строки, write_check назвал ближайшие существующие колонки, а check сказал, что суммы совпадают, — индекс работает. Дальше можно подключать агента.
Проблема: агент ищет место вместо того, чтобы думать
Три симптома, знакомые каждому, кто поручал модели правку в существующем проекте.
Не находит место. rg по имени функции даёт десятки кандидатов, и нужный — не первый. В замерах VulnGym в цепочках длиннее девяти шагов 56,5% провалов приходится именно на навигацию: в 26,8% агент не нашёл файл, в 29,7% нашёл файл, но не строку. Это отказы не про качество анализа, а про поиск.
Не знает схему базы. Колонку назвали «как переменную в соседнем модуле» — и узнали об этом при первом сохранении: SQLSTATE[42S22] Unknown column. Схему из миграций нужно проверять до запуска кода, а не после.
Не знает, что устарел. Индекс построили в понедельник, во вторник переименовали функцию, в среду агент получил честный, доказанный и уже неверный ответ. Отличить «такой функции нет» от «она появилась после индексации» по обычному RAG-ответу невозможно, а решение уже принято.
Свободный язык запросов к графу эту проблему не решает: он её перекладывает на модель. Именно поэтому в RAGRAF нет Cypher и CPGQL — только фиксированный набор операций.
Что такое RAGRAF
Три хранилища на корпус, в каталоге .ragraf/ внутри индексируемого проекта:
| Файл | Что внутри |
|---|---|
graph.sqlite |
узлы (класс, функция, метод), рёбра (вызовы, наследование, импорты) с доказательствами, схема БД, маршруты |
lexvec.sqlite |
лексика FTS5 (BM25) и векторы vec0 в одном файле, плюс сырой текст чанков — фрагменты выдаются без чтения диска |
control.sqlite |
ревизии, append-only журнал расхождений, архивы переиндексаций, телеметрия обращений |
flowchart LR P["Проект:
код, миграции, документы"] --> I["Индексация"] I --> G[("graph.sqlite
узлы и рёбра
с доказательствами")] I --> V[("lexvec.sqlite
BM25 + векторы")] I --> C[("control.sqlite
ревизии, дрейф,
телеметрия")] G --> AG["Агент
MCP или CLI"] V --> AG C --> AG AG -- правка кода --> P P -. расхождение идёт
в журнал, а не в индекс .-> C
Индексация — шесть стадий, каждая называется в отчёте и в метриках прогона: scan (обход, sha256 каждого файла, контрольная сумма корпуса) → ast (tree-sitter: символы, вызовы, SQL-литералы) → load → resolve (разрешение имён: точное совпадение, затем имя+файл, затем честный отказ) → schema (таблицы и колонки из миграций, без подключения к СУБД) → cpg (межпроцедурные вызовы через Joern, если он есть) → lexvec (токены и векторы; неизменённые чанки переиспользуются по хешу).
flowchart LR
subgraph IDX["Индексация — одна команда"]
direction TB
S["scan
sha256 каждого файла"] --> A["ast
tree-sitter: символы, вызовы, SQL"]
A --> L["load"]
L --> R["resolve
разрешение имён"]
R --> SC["schema
таблицы из миграций"]
SC --> CPG["cpg
Joern, опционально"]
CPG --> LV["lexvec
FTS5 + векторы 384"]
end
LV --> REV[("ревизия
corpus_hash")]
subgraph ANS["Ответ на каждый запрос"]
direction TB
Q["операция агента"] --> G{"сторож:
corpus_hash совпал?"}
G -- да --> F["факты со ссылкой path:line"]
G -- нет --> ST["stale_warnings
+ запись в журнал"]
F --> ENV["конверт: index_rev,
capability, drift, limits"]
ST --> ENV
end
REV --> GОтвет на каждый запрос собирается в конверт — и конверт здесь важнее результата:
{
"op": "impact",
"index_rev": "rev-0007",
"capability": "full",
"drift": { "files": 3, "symbols": 7, "entries": 4 },
"provenance": "index",
"limits": ["статический анализ; динамика не покрыта"],
"stale_warnings": [
{ "path": "src/Pipeline/PipelineEngine.php", "reason": "modify после rev-0007", "suggest": "live_read" }
],
"result": ["…"]
}
Три решения, каждое выведено из измеренного дефекта, а не из вкуса:
- Индекс неизменяем в пределах ревизии. Факты разных моментов времени не смешиваются в одном ответе.
- Агенту не даётся язык запросов. Он выбирает, что спросить, а не как.
- Ребро без доказательства не считается фактом. Иначе оно выглядит достоверным только потому, что пришло «из базы».
Возможности: 52 операции вместо свободного поиска
Навигация и структура
| Операция | На какой вопрос отвечает |
|---|---|
search |
где об этом написано: гибридно, BM25 + вектор, с выдачей текста фрагментов |
search_multi |
где встречается сразу несколько строк; какие файлы — копии друг друга |
duplicates |
какие файлы дублируют друг друга и чем доказана активность главного |
symbol |
где определён символ: вид, файл, строки, сигнатура, контракт параметров из docblock |
references |
где ещё используется |
code |
тела символов — только по явному запросу, а не «на всякий случай» |
repo_map |
карта репозитория в заданном бюджете символов |
entrypoints |
точки входа: маршруты, HTTP-обработчики, CLI, endpoint’ы разметки |
dead_code |
файлы и подпрограммы без ссылок и вне достижимости |
neighbors |
есть ли готовая реализация в соседних корпусах того же стека |
live_read |
актуальный файл с выделением отличий от индекса |
catalog |
реестр корпусов: что проиндексировано и с какими суммами |
route |
куда идти за ответом: какой корпус отвечает и почему (без цитирования) |
diff |
что изменилось между ревизиями: файлы, символы, рёбра, регрессии |
Граф и влияние правки
| Операция | На какой вопрос отвечает |
|---|---|
callers / callees |
кто вызывает / кого вызывает; --mode hard только доказанное, all — с эвристикой |
impact |
что затронет изменение: транзитивный конус, доказанные и эвристические связи раздельно |
slice |
структурный срез: узлы, рёбра, строки — без тел функций (нужен слой Joern) |
routes |
маршруты приложения и их обработчики |
policy_trace |
кто и по какому условию получает право; красный флаг — default_behavior: allow |
class_usage |
класс оформления: где описан, где применён, что используется без правила |
Данные, схема и живая база
| Операция | На какой вопрос отвечает |
|---|---|
schema |
что в таблице по миграциям проекта |
table_usage |
кто читает и кто пишет таблицу (--mode write — только запись) |
write_check |
можно ли так записать: сверка плана записи со схемой до запуска SQL |
db_schema / db_table |
что в живой базе: таблицы, колонки, типы, индексы, ключи |
db_relations |
связи таблицы: stated (внешний ключ, достоверность 1.0) и derived (вывод по имени, 0.4) раздельно |
db_sample |
пример строк; персональные колонки закрыты, а не выброшены |
db_drift / db_cross |
что изменилось в структуре между ревизиями; сверка «код ↔ база» |
Свежесть, дрейф, качество
| Операция | На какой вопрос отвечает |
|---|---|
freshness |
актуален ли индекс: сверка контрольной суммы корпуса |
index_status |
ревизия, покрытие, деградация, размер журнала расхождений |
drift_report / drift_ack |
журнал расхождений индекса и кода; подтверждение разобранных записей |
calibrate |
калибровка порогов переиндексации по фактическому накоплению правок |
quality_suite / quality_diff / quality_prune |
контур качества: прогон проверок индекса, сравнение прогонов, прореживание журнала прогонов с копией и подтверждением |
Учёт и след решений
| Операция | На какой вопрос отвечает |
|---|---|
usage |
телеметрия обращений и оценка эффекта: saved_calls, hit_rate, avg_ms, misses, stale_served |
task_stats |
отчёт по задаче пользователя: обращения к индексу, факты, доля проверяемых ссылок |
task_set |
привязать вызовы к метке задачи |
gate |
решение по вызову инструмента: allow / ask / deny |
note / notes |
записать и прочитать заметку о решении — знание между сессиями |
Ещё девять операций — экспорт в SIEM, единый вход, офлайн-лицензия, аттестат поставки и общий индекс команды — поставляются отдельными сборками: в открытой сборке Community их файлов нет, и вызов отвечает отказом с названной причиной, а не пустым результатом. Для обычной работы над кодом они не нужны.
Отдельно стоит write_check: он сверяет план записи (INSERT/UPDATE) со схемой и при ошибке называет ближайшие существующие колонки. Замер на пилоте: 44 таблицы и 453 колонки, 14 проверок за одну доработку, четыре дефекта предотвращены до запуска кода.
Языки, форматы и документы
29 языков разбираются деревом — то есть дают узлы и рёбра графа. У шестнадцати карта узлов описана профилем: PHP, Python, JavaScript, TypeScript, Java, C#, C, C++, Ruby, Lua, Go, Perl, bash, PowerShell, CSS, HTML. У тринадцати (Rust, Kotlin, Swift, Scala, Dart, R, F#, OCaml, Solidity, Fortran, MATLAB, Elm, Lisp) она берётся из tags-запроса самой грамматики: имена узлов у них не совпадают с привычными, и профиль на каждый язык означал бы выдумывание имён. Каждый язык проверяется на пробном файле — минимум один найденный символ.
Ещё 40 подключаются текстом: файлы находятся поиском, но узлов не дают. Это Julia, Elixir, Erlang, Haskell, Clojure, Groovy, Visual Basic, Pascal/Delphi, Objective-C, Zig, Nim, Crystal, Tcl, Prolog, Ada, Verilog, VHDL, ассемблер, HCL/Terraform, Dockerfile, Makefile, CMake, SCSS, Less, Vue, Svelte, GraphQL, Protobuf, SQL, batch, Markdown, RST, YAML, JSON, TOML, INI, XML, обычный текст и язык document.
Документы индексируются целиком, включая старые версии офисных форматов: Word (.docx, .docm, .dotx, .doc — собственный разбор OLE), Excel (.xlsx, .xlsm, .xltx, .xls), PowerPoint (.pptx, .pptm, .ppsx, .potx, .ppt), PDF, OpenDocument (.odt, .ods, .odp, .odg), EPUB, RTF, письма .eml и .msg, файлы WPS. Форматы без разборщика не выдают себя за пустой документ — они помечаются причиной.
Всего в реестре форматов 84 записи и 186 расширений, плюс четыре формата, опознаваемых по имени файла: Dockerfile, Containerfile, Makefile, CMakeLists.txt. Что индексировать — выбирается в панели, вкладка «Форматы»; значение * означает «все, включая добавленные позже».
Свежесть и дрейф: почему индексу можно верить
Индекс — это кэш, и кэш, который устарел молча, хуже отсутствия кэша. RAGRAF не обещает, что индекс всегда свежий: при активной работе это недостижимо. Он обещает, что устаревание всегда видно. Четыре механизма:
1. Иммутабельная ревизия. corpus_hash = sha1(отсортированный список (path, sha256)) фиксирует точное состояние корпуса. Внутри ревизии факты не правятся и не дописываются — только новая ревизия. Следствие: любой ответ воспроизводим, index_rev однозначно определяет набор фактов.
2. Append-only журнал расхождений. Пока агент работает, код правится — и каждая правка регистрируется журналом, а не индексом: entry_id, rev, change (create/modify/delete), path, old_sha256/new_sha256, затронутые узлы и символы, dedupe_key с уникальным индексом (повторная регистрация того же события не создаёт дубль). Журнал не удаляется и не перезаписывается.
3. Сторож контрольных сумм в каждом запросе. corpus_hash пересчитывается или сверяется по кэшу состояния и сравнивается с ревизией; расхождение дополняет журнал и попадает в конверт: drift и stale_warnings с полем suggest — live_read (перечитать файл) или reindex.
4. Архив и отчёт «что изменилось в знаниях». При переиндексации журнал не исчезает: он архивируется вместе с ревизией, которая его закрыла, и превращается в отчёт — какие файлы разошлись, какие символы затронуты, что индекс узнал заново. Вопрос «что изменилось в знаниях о коде между понедельником и средой» получает ответ не по git-истории, а по истории фактов.
flowchart LR
Q["Запрос агента"] --> H{"corpus_hash
совпал?"}
H -- да --> OK["Ответ: drift.files = 0"]
H -- нет --> W{"затронут ли
запрошенный символ?"}
W -- нет --> OK2["Ответ + drift в конверте"]
W -- да --> ST["Метка stale + suggest:
live_read или reindex"]
ST --> CAP{"операция возможна
в текущем режиме?"}
CAP -- да --> OK3["Ответ с пометкой stale"]
CAP -- нет --> REF["Отказ: код 3,
причина в конверте"]В ответе агент видит одно из трёх состояний, и четвёртого нет:
| Состояние | Что видит агент |
|---|---|
| Свежо | drift.files = 0, предупреждений нет — обычный ответ |
| Разошлось | stale_warnings со suggest и метки stale у затронутых фактов: «функция найдена, но её файл правился после ревизии — перечитай» |
| Невозможно | отказ с причиной (код 3): операция несовместима с текущим режимом |
Чего механика не делает — и это тоже часть контракта: не следит за файлами в реальном времени (файлового вотчера нет, сторож срабатывает на запросе или по команде); не пересобирает CPG-слой инкрементально (у Joern её нет — после правок, затрагивающих межпроцедурные связи, slice честнее пересобрать целиком); не защищает от правок самого каталога .ragraf/ — это ответственность файловой системы, поэтому каталог и не попадает в репозиторий.
Замеры: что меняется в цифрах
Замер на реальном проекте — PHP-портал, 14 навигационных задач, артефакты прогонов лежат в bench/results/:
| Показатель | Без индекса | С индексом |
|---|---|---|
| Вызовов инструментов на 14 задач | 28 (реалистично 43) | 14 |
| Среднее на задачу | 2,0 / 3,1 | 1,0 |
| Кандидатов к просмотру | до 34 файлов | 1–5 точных |
| Файлов, прочитанных целиком | 14 (минимум) | 0 |
| Попадание в файл-цель с первого запроса | — | 92% (12 из 13) |
Проверяемость: факты со ссылкой файл:строка |
— | 100% |
Второй замер — журнал телеметрии доработки подсистемы согласований на том же портале: 46 обращений к операциям за всю доработку, 0 полных чтений файлов, 0 ошибок выполнения, 14 вызовов write_check перед SQL-записями, четыре дефекта предотвращены до появления кода. Доминирующий класс пойманного — «работает, но не так»: страница открывается, исключений нет, а отклонённый документ оказывается тупиком, потому что маршрут согласования обрывается. Ни компилятор, ни тест «страница открывается» такое не ловят; ловят policy_trace и routes.
Честная оговорка, которую обычно опускают: выигрыш в 2–3 раза относится к задачам локализации. В цикле «прочитал → изменил → проверил» экономии по числу вызовов почти нет — там выигрыш в другом: правки не ломают индекс, а спорный SQL проверяется до запуска.
Ориентиры по времени на 323-файловом PHP-корпусе (процессор): полная индексация 71 с, из них 64 с — построение графа свойств кода через Joern; без Joern — единицы секунд. Первая индексация с векторами дополнительно скачивает модель (~240 МБ, один раз).
Почему это круто
- Проверяемость вместо доверия. Ответ без
path:line— догадка; в RAGRAF догадка помечена как догадка. Проверить факт можно в редакторе, а не в вере к модели. Это меняет жанр: агент не «объясняет», а ссылается. - Он отказывается отвечать, когда не имеет права.
capability=degradedиlimitsв конверте — это редкое свойство: инструмент сообщает о своей неполноте раньше, чем её заметит человек. - Ноль инфраструктуры. Три файла SQLite рядом с кодом, переносится копированием каталога, удаляется
rm -rf .ragraf. Ни демонов, ни Docker, ни внешней БД, ни API-ключей. - Данные не уходят. Индексация, эмбеддинги и ответы — локально; единственный сетевой трафик: PyPI при установке и разовая загрузка модели. Файлы из
secrets_excludeне открываются вовсе. - Целый класс дефектов исчезает до запуска.
write_check— 3 мс против ошибки драйвера в рантайме,impact— против «а кто ещё это вызывает?». - Один контракт для любого агента. MCP по stdio: 59 инструментов, одна форма конверта, одна телеметрия. Подключение к AutoClaw и DeepSeek Harness отлажено замерами сессий; к средам без MCP-клиента — через CLI и навык.
- Видно, кто работает, а кто grep-ит. Телеметрия пишет
agentиagent_kind, поэтому «агент игнорирует индекс» — это строка в отчёте, а не догадка. Лечится регламентом, а не заменой модели. - Документы в том же индексе. PDF, DOCX, XLSX, PPTX, ODT, письма
.eml— «где это описано в ТЗ» перестаёт быть отдельной задачей.
Установка
| Что | Значение |
|---|---|
| Python | 3.13 или новее (проверено на 3.14) |
| Права администратора | не нужны, всё ставится в пользовательский каталог |
| Проверенная платформа | Windows + PowerShell 7.6 |
Общий путь через pip |
Linux и macOS — не верифицирован разработчиком сквозным прогоном |
| Диск | ~2,5 ГБ на окружение; индекс — порядка 1 МБ на 1–2 файла исходников |
| Опционально | JDK 21 + Joern (~5–6 ГБ) для межпроцедурного слоя; GPU — для эмбеддингов |
| Сеть | PyPI при установке и один раз ~240 МБ модели эмбеддингов |
Лицензия — Business Source License 1.1: исходный код открыт, использование внутри организации разрешено, предоставлять продукт третьим лицам как управляемый сервис — нельзя; через четыре года после выпуска версия переходит на Apache-2.0. Java и Joern ставятся отдельно и остаются под Apache-2.0.
Windows: установщик одной командой
iwr -OutFile install.ps1 https://raw.githubusercontent.com/paulmann/RAGRAF/main/install.ps1
pwsh -NoLogo -NoProfile -File .\install.ps1
Установщик проверяет окружение (PowerShell 7.6+, Python 3.13+, место на диске, доступность GitHub), спрашивает каталог установки (по умолчанию C:\RAGRAF), получает пакет (git clone, а без git — архив ветки), ставит виртуальное окружение и зависимости тем же путём, что CI продукта, и в конце печатает инструкцию по запуску панели. Полезные ключи:
pwsh -File .\install.ps1 -InstallDir C:\RAGRAF -Yes # без диалогов
pwsh -File .\install.ps1 -SourceDir D:\dist\RAGRAF # из локальной копии, без сети
pwsh -File .\install.ps1 -WithTruststore # сеть с TLS-инспекцией
pwsh -File .\install.ps1 -SkipDeps -SkipClients # только развернуть дерево
pwsh -File .\install.ps1 -SummaryJson C:\temp\install.json # машиночитаемый итог
Чего он осознанно не делает: не пишет настройки в AutoClaw (тот принимает MCP-серверы только через свой интерфейс, а файлового способа регистрации нет) — вместо этого печатает точные значения для формы «Add tool»; не индексирует корпуса; не ставит Java и Joern; не перезаписывает найденную установку без согласия.
Из дерева проекта
git clone https://github.com/paulmann/RAGRAF.git
cd RAGRAF
pwsh -NoLogo -NoProfile -File scripts/install.ps1
Виртуальное окружение создаётся в ~/.ragraf/venv, транскрипт пишется в ~/.ragraf/logs/install-<время>.log, каждый шаг заканчивается проверкой, а итог печатается таблицей. Ключи этого установщика: -Project (сразу подготовить корпус), -Index и -NoVectors (индексация, с моделью или без), -Venv, -Python, -DependenciesOnly, -WithTruststore, -Force, -MinFreeGb, -TimeoutScale, -SummaryJson.
pwsh -File scripts/install.ps1 -Project C:\work\myproject -Index
pwsh -File scripts/install.ps1 -Project C:\work\myproject -Index -NoVectors
Коды возврата установщика — контракт: 0 успех, 1 ошибка установки, 2 неверные аргументы, 3 неподходящее окружение.
Общий путь через pip
git clone https://github.com/paulmann/RAGRAF.git
cd RAGRAF
python -m venv .venv
. .venv/bin/activate # Windows: .venv\Scripts\Activate.ps1
pip install -e .
ragraf --version
Без установки пакета продукт тоже работает — через обёртку, которая сама добавляет src/ в путь:
python tools/ragraf.py --version
Слой Joern, если нужен slice
Для межпроцедурного графа нужны JDK 21 и Joern. Без них всё работает: индекс строится, поиск и структура доступны, slice отказывает с причиной.
pwsh -File scripts/install-joern.ps1
Архив кладётся в .tools/downloads/, скрипт идемпотентен, распакованный комплект занимает около 5 ГБ.
Настройка корпуса
Первый корпус и что делает ensure
ragraf ensure C:\work\myproject --json
ensure — команда «приведи в порядок»: находит существующий корпус, для незнакомого каталога создаёт конфигурацию, при расхождении контрольных сумм переиндексирует. Статус в ответе: actual, reindexed, created, not_a_project (каталог не похож на проект — корпус не создаётся) или error с причиной. Индекс ложится в <проект>/.ragraf/, и каталог надо добавить в .gitignore того репозитория:
echo ".ragraf/" >> .gitignore
git check-ignore -v .ragraf # правило действует
Если корпус нужен вручную и по шагам:
ragraf init C:\work\myproject # осмотреть каталог и собрать конфигурацию, напечатать путь
ragraf -p <конфиг> register # зарегистрировать разработку в реестре
ragraf -p <конфиг> index # полная индексация
Файл конфигурации
Конфигурация корпуса — YAML. init заполняет её по фактическому содержимому каталога; ниже — рабочий пример с пояснениями:
project: myproject
root: C:/work/myproject
languages: [php, javascript, markdown] # языки, файлы которых попадают в индекс
include: # что индексировать
- "**/*.php"
- "**/*.js"
- "**/*.md"
exclude: # ДОБАВЛЯЕТСЯ к базовому списку, не заменяет его
- "**/node_modules/**"
- "**/build/**"
secrets_exclude: # не индексируются и не читаются вовсе
- "**/*.local.php"
- "**/.env"
- "**/*.pem"
db:
enabled: false # схема БД из migrations/*.sql
embedding:
model: sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2
dim: 384
batch_size: 64
use_gpu: false
device: auto # auto | cpu | cuda:0
chunking:
by_symbol: true
max_chunk_lines: 120
min_chunk_lines: 3
index:
retain_revs: 3 # сколько ревизий хранить
batch_size: 2000
joern_enabled: true # нет Joern — capability=degraded
joern_server: true
joern_server_port: 9500
joern_heap_gb: 8
reindex_after_changes: 5 # быстрая переиндексация по накоплению правок
full_reindex_after_changes: 20 # полный пересбор с графом свойств
reindex_interval_minutes: 15 # индекс старше 15 минут — полный пересбор
Плотность правок и пороги — не украшение: при полном пересборе с графом свойств индексация кратно дороже, поэтому до 5 правок считается быстрая переиндексация, от 20 — полная. Эти пороги видны в ответе любой операции полем accumulation вместе с возрастом индекса.
Что индексировать и что исключать
Базовый список исключений (28 шаблонов) применяется всегда и не заменяется списком из конфига: история и зависимости (.git, node_modules, vendor, bower_components), служебные каталоги (.ragraf, .zwork, .pi, .venv, __pycache__, кеши), выход сборки (dist, build, target), образцы имён (*_bak*, *backup*, *.min.js). Правила из конфига добавляются к нему.
flowchart LR
F["Файлы проекта"] --> X{"под exclude или
под базовыми 28 правилами?"}
X -- да --> SKIP["не индексируются"]
X -- нет --> SEC{"в secrets_exclude?"}
SEC -- да --> NOREAD["файл не читается вовсе"]
SEC -- нет --> LANG{"язык в списке
languages?"}
LANG -- "дерево: 29 языков" --> GRAPH["узлы и рёбра графа"]
LANG -- "текст: 40 языков" --> LEX["FTS5 + векторы"]
GRAPH --> LEXТри вещи, которые нельзя исключать «на всякий случай»: bin, cache, obj, logs, publish — по имени назначение неразличимо. В одном проекте bin — шесть рабочих PHP-скриптов, в другом — выход сборки. Такие каталоги исключаются в конфиге конкретного корпуса, где назначение видно.
Сопоставление шаблонов регистрозависимо (**/cache/** не поймает src/Cache/), поэтому после правки исключений проверяйте, сколько файлов выпало, — ошибка в списке молча выкидывает рабочий код из индекса.
Векторы и GPU
Векторы считаются локально моделью paraphrase-multilingual-MiniLM-L12-v2 (384 измерения) через fastembed и ONNX Runtime — без torch и без облака. Модель скачивается один раз в ~/.ragraf/models (каталог переопределяется RAGRAF_MODEL_CACHE). Индексация без векторов: ragraf -p <конфиг> index --no-vectors — остаётся лексический поиск BM25.
GPU ускоряет только кодирование текста в векторы, и включается по порогу окупаемости: подготовка видеокарты занимает десятки секунд разово, поэтому на малом объёме она проигрывает. Настройки: use_gpu, gpu_min_texts (по умолчанию 1800 текстов), gpu_batch_size, в конфиге корпуса — device. Для графовых расчётов есть отдельный порог graph_gpu_min_nodes (5000 вершин). Какой путь выбран и почему — пишется в метаданные прогона, а не подразумевается.
Настройки панели
Часть параметров живёт не в конфиге корпуса, а в настройках продукта (51 ключ, правится в панели): пороги переиндексации и хранения ревизий, index_formats (все 84 формата), dedupe_enabled/dedupe_scope/dedupe_mode/dedupe_keep (схлопывание побайтовых копий документации — по умолчанию выключено, потому что меняет состав корпуса и сдвигает все числа отчётов), ragraf_mode (cli, mcp-stdio, mcp-http, both), блок secrets_* для хранилища секретов. Изменяющие запросы требуют токен, который печатается при запуске панели (или задаётся RAGRAF_ADMIN_TOKEN).
Подключение к агенту (MCP)
Конфигурация сервера
{
"mcpServers": {
"ragraf": {
"command": "python",
"args": [
"C:/RAGRAF/tools/ragraf-mcp.py",
"--project", "C:/RAGRAF/config/mycorpus.yaml",
"--agent", "my-agent",
"--agent-kind", "external"
]
}
}
}
--project указывает корпус. --agent и --agent-kind нужны только для раздельной статистики: без них вызовы попадут в журнал как unknown. Клиенты, которые запускают сервер без --project, переключают корпус на лету инструментом ragraf_use_project. Значения --agent-kind: autoclaw (среда автора, расширенная атрибуция), external (сторонний агент), internal.
Сервер отдаёт 59 инструментов ragraf_* по stdio (JSON-RPC) — это те же операции, что в CLI, с теми же конвертами. Проверить, что сервер поднялся и отвечает:
sequenceDiagram autonumber participant A as Агент participant M as ragraf-mcp.py
stdio, JSON-RPC participant F as Фасад: 52 операции participant S as .ragraf
три SQLite A->>M: initialize M-->>A: 59 инструментов ragraf_* A->>M: ragraf_symbol name=corpus_hash M->>F: op symbol F->>S: граф + сверка corpus_hash S-->>F: узлы, рёбра, свежесть F-->>M: конверт: index_rev, capability, drift, limits M-->>A: факт + path:line A->>M: ragraf_write_check table=Users columns=... M-->>A: unknown_columns + ближайшие имена
python tools/ragraf-mcp.py --project config/mycorpus.yaml --agent probe --agent-kind external
ragraf -p config/mycorpus.yaml usage
Регламент для агента
Инструменты без регламента не работают — агент возвращается к grep. Регламент живёт там, где агент читает правила: в AGENTS.md проекта или в пользовательских правилах клиента. Блок ниже переносится как есть.
# Регламент работы с RAGRAF
1. Навигация — только через ragraf_symbol / ragraf_search. Полный файл — только
ragraf_live_read и только по факту, найденному в индексе.
2. Проверяй конверт, а не только result: stale_warnings → перечитай затронутое
(live_read); capability=degraded → не планируй задачи со slice.
3. Перед любой записью в БД — ragraf_write_check. Всегда, без исключений.
4. Перед правкой символа — ragraf_impact --depth 3.
5. «Не найдено» — валидный ответ: сообщай limits из конверта, не додумывай.
Проверка и телеметрия
ragraf -p <конфиг> usage # сводка по всем агентам
ragraf -p <конфиг> usage --agent my-agent # только этот агент
ragraf -p <конфиг> usage --task <метка> # только эта задача
ragraf -p <конфиг> query task_stats --task <метка>
Телеметрия пишет, кто именно вызвал операцию, — поэтому видно, соблюдается ли правило первого шага: ensure и обращения к операциям попадают в сводку наравне друг с другом. Задача помечается меткой (--task или переменная RAGRAF_TASK), и тогда task_stats отдаёт готовый отчёт: обращения, факты, доля фактов со ссылкой файл:строка, объём ответов и оценку того, сколько чтения файлов они заменили.
Первые запросы: шпаргалка
# где определено и как выглядит сигнатура
ragraf -p cfg.yaml query symbol --name corpus_hash
# кто вызывает (только доказанное) и что затронет правка
ragraf -p cfg.yaml query callers --name audit_log --mode hard
ragraf -p cfg.yaml query impact --target "Facade::query" --depth 3
# гибридный поиск с выдачей текста фрагментов — без второго вызова
ragraf -p cfg.yaml query search "порог переиндексации" --with-text --limit 8
# схема и проверка плана записи ДО выполнения SQL
ragraf -p cfg.yaml query schema --table doc_payments
ragraf -p cfg.yaml query write_check --table doc_payments --columns "amount,AuthorUserID"
# кто получает право и по какому условию
ragraf -p cfg.yaml query policy_trace acl_can_act
# свежесть и «что изменилось в знаниях»
ragraf -p cfg.yaml check
ragraf -p cfg.yaml query drift_report
Порядок флагов — самая частая ошибка: глобальные -p/--json ставятся до подкоманды (ragraf -p cfg query search …), собственные ключи подкоманды — после неё. Исключение — ensure: у неё свой --json, он идёт после (ragraf ensure <каталог> --json).
Коды возврата:
| Код | Значение |
|---|---|
0 |
успех; для selftest — «отчёт сформирован», даже если компоненты отсутствуют |
1 |
ошибка аргументов или корпуса: флаг после подкоманды, нет конфига, корпус не найден |
2 |
внутренняя ошибка: повреждённое хранилище, баг |
3 |
операция невозможна в этом состоянии: slice при capability=degraded |
Код 3 — не «ничего не найдено». «Не найдено» — успешный ответ с пустым result и объявленными limits. Различие принципиально, и на нём держится вся честность отчётов агента.
Админ-панель
python tools/ragraf-admin.py --no-browser
Открывается на 127.0.0.1:8777 и слушает только этот адрес. Показывает корпуса и состояние их индексов, размеры трёх хранилищ, качество индекса (чанки сверх бюджета, обрезанные сигнатуры, доступность векторного поиска), статистику обращений и настройки порогов. Отдельные страницы: /sources — разрешённые корни источников (добавление, скан, включение, удаление; содержимое файлов не читается, секретоносные видны только числом и правилом) и /value — польза за 30 дней, сложенная по корпусам.
Токен мутаций печатается при старте в консоль; его можно задать заранее переменной RAGRAF_ADMIN_TOKEN. Панель не является обязательной: всё то же делается командами, а сама панель наружу не доступна.
Подводные камни
- Флаг после подкоманды.
ragraf query search -p cfg.yaml— ошибка «не распознан аргумент», код 1. Глобальные ключи идут первыми. selftestвсегда возвращает 0. Это отчёт по 11 компонентам, а не гейт: читать надо тело отчёта, аjoern/javaв нём опциональны.certificate verify failedпри скачивании модели. Корпоративный TLS-прокси:install.ps1 -WithTruststoreилиpip install truststore.sliceотвечает кодом 3. Нет Joern —capability=degraded. Либоscripts/install-joern.ps1, либо жить без срезов: остальные операции работают..ragrafпопал в git. Каталог индекса — производные данные на сотни мегабайт; правило.ragraf/в корневом.gitignoreобязательно до первой индексации, а не после.- Ожидание «индекс сам заметит». Файлового вотчера нет: сторож срабатывает на запросе или по
check. При длинной сессии переиндексация — отдельное действие, иdrift_reportпокажет, что изменилось. reindexпосле правок в межпроцедурных связях. Joern не умеет инкрементальную пересборку: слой CPG пересобирается целиком, иначеimpact/sliceопираются на старые рёбра.- Расширение списка исключений без замера. Ошибка в маске молча выбрасывает рабочий код из индекса; проверяйте число файлов до и после.
- Включённое схлопывание копий меняет числа.
dedupe_enabledпо умолчанию выключено; после включения состав корпуса и все отчёты сдвигаются — это надо делать осознанно и один раз. - Путь через
pipна Linux и macOS не проверен разработчиком. На Windows прогон сквозной; на других платформах сначала прогоняйтеselftestи первыйensureна тестовом каталоге.
Чек-лист внедрения
- [ ] Python 3.13+ в PATH, PowerShell 7.6+ на Windows; прав администратора не требуется.
- [ ] Установка завершилась кодом 0,
ragraf --versionотвечает0.3.0. - [ ]
.ragraf/добавлен в.gitignoreтого репозитория, который индексируется. - [ ]
ensureпо каталогу проекта вернулactual,reindexedилиcreated— неerror. - [ ]
statusпоказываетcapabilityи покрытие;degraded— известное состояние, а не поломка. - [ ]
query symbolпо известному имени возвращаетpath:line;searchнаходит фразу из README проекта. - [ ]
secrets_excludeзаполнен:.env, ключи, локальные конфиги не индексируются. - [ ] MCP-сервер зарегистрирован в клиенте и отдаёт инструменты; регламент добавлен в правила агента.
- [ ]
usage --agent <имя>показывает вызовы агента, а неunknown. - [ ] Панель открывается на
127.0.0.1:8777, токен мутаций не хранится в репозитории.
FAQ
Чем это отличается от обычного RAG по коду?
Тремя обязательствами. Индекс иммутабелен в пределах ревизии, а расхождения с кодом копятся в append-only журнале: устаревший факт помечается stale в каждом ответе, а не выдаётся молча за актуальный. Каждый факт несёт доказательство — path:line, provenance (stated/derived), confidence. Вместо свободного retrieval-ответа — фиксированные операции с явными limits и состоянием capability.
Нужен ли сервер, облако или подписка?
Нет. RAGRAF — локальный инструмент: индексация и ответы идут на вашей машине, состояние лежит в трёх файлах SQLite внутри проекта. MCP-сервер — процесс stdio, который запускает ваш агент. Единственный внешний компонент — Joern с сервером на localhost во время индексации CPG, и он останавливается автоматически.
Что попадает наружу?
Ничего, кроме установки из PyPI и разовой загрузки модели эмбеддингов. Ни API-ключей, ни отправки кода: secrets_exclude означает, что файл не открывается вовсе, а телеметрия хранит хеш аргумента и короткий дайджест вместо полного текста запросов.
Что будет без Java и Joern?
Продукт работает: индекс строится, поиск, символы, схема и все операции чтения доступны. Недоступен межпроцедурный слой — slice и часть impact по межпроцедурным связям; capability при этом degraded, и отказ называет причину. Захотели слой — scripts/install-joern.ps1, около 5 ГБ.
Можно ли добавить свою операцию или запрос к графу?
Языка запросов к графу нет и не планируется: это архитектурное решение, основанное на замере (19–132 валидных CPGQL-запроса из 1278 у базовых моделей). Набор из 52 операций фиксирован; расширение обсуждается в issues репозитория.
Сколько места занимает индекс и сколько строится?
Окружение ~2,5 ГБ (ONNX Runtime, tree-sitter, зависимости). Сам индекс — порядка 1 МБ на 1–2 файла исходников; на 323-файловом PHP-корпусе полная индексация с CPG заняла 71 с, из них 64 с — Joern; без него — единицы секунд.
Итог
RAGRAF решает узкую, но дорогую задачу: убрать из модели ту работу, которую она делает плохо, — поиск места в незнакомом коде. Взамен агент получает фиксированный контракт из 52 операций, три файла SQLite рядом с кодом и обязанность смотреть на конверт ответа: ревизия, состояние возможностей, расхождения, границы. Числа на пилоте скромные на вид и важные по сути: один вызов вместо двух-трёх, ноль полных чтений файлов, 92% попаданий с первого запроса, 100% фактов со ссылкой на строку.
Главное же свойство продукта — не скорость, а право отказаться отвечать. Агент, который говорит «этот файл правился после ревизии, перечитай», полезнее агента, который уверенно называет функцию, удалённую во вторник.
Цитата: Индекс кода — это не ускорение поиска, а перенос части работы из модели во внешнюю структуру, которая знает, чего она не знает.
Связанные материалы
- RAGRAF — сайт продукта — возможности, устройство, доказательства замеров, вики и эмулятор панели.
- Репозиторий RAGRAF — исходный код,
README.ru.md, примеры конфигурации,scripts/install.ps1. - Релиз v0.3.0 — версия, на которой проверены команды этой статьи.
- ARCHITECTURE.md — устройство и контракты — хранилища, стадии индексации, конверт ответа.
- INTEGRATION.md — подключение к агентам — MCP, режимы, телеметрия.
- WinSecHunter 2.1.0 — пример инструмента с контрактом кодов возврата и разделом для агента.
- Bash WP-CLI Update — как выглядит разбор продукта целиком: режимы, гарантии, восстановление.
- RPMBUILD в ALT Linux — сборка и упаковка на Linux, если ставите RAGRAF вне Windows.
LLM Summary
- Тема: RAGRAF — локальная графово-векторная база знаний о коде для LLM-агентов; разобрана версия 0.3.0 (25.10.2026).
- Что делает: индексирует репозиторий и отвечает фиксированными операциями (
symbol,callers,impact,slice,schema,write_check,policy_trace,search) фактами со ссылкойфайл:строка, сprovenance,confidenceиindex_rev. - Масштаб: 52 операции фасада, 59 инструментов MCP по stdio, 21 подкоманда CLI, 29 языков разбором деревом, ещё 40 текстом, 84 формата в реестре.
- Хранение: три файла SQLite в
.ragraf/внутри проекта —graph.sqlite,lexvec.sqlite(FTS5 + векторы 384),control.sqlite(ревизии, журнал расхождений, телеметрия); внешней инфраструктуры нет. - Свежесть: иммутабельная ревизия (
corpus_hash), append-only журнал расхождений сdedupe_key, сторож контрольных сумм в каждом запросе,stale_warningsсsuggest, архив иdrift_report. - Замеры (PHP-портал, 14 навигационных задач): 28 (реалистично 43) → 14 вызовов; 2,0–3,1 → 1,0 на задачу; 34 → 1–5 кандидатов; 14 → 0 полных чтений файлов; 92% попаданий с первого запроса; 100% фактов со ссылкой.
- Установка: Windows —
iwrкорневогоinstall.ps1илиscripts/install.ps1(ключи-Project,-Index,-NoVectors,-WithTruststore); общий путь —python -m venv+pip install -e .; Joern опционально (scripts/install-joern.ps1, ~5 ГБ). - Настройка:
ensure/init/register/index; конфигурация корпуса (languages, include, exclude, secrets_exclude, embedding, chunking, index); базовые 28 исключений применяются всегда;.ragraf/— в.gitignore; MCP-серверtools/ragraf-mcp.pyс--project,--agent,--agent-kind; панельtools/ragraf-admin.pyна 127.0.0.1:8777. - Границы: статический анализ (динамика не покрыта), нет файлового вотчера, Joern не пересобирается инкрементально,
sliceтребует CPG, pip-путь на Linux/macOS разработчиком сквозным прогоном не проверен. - Ссылки: https://ragraf.ru/ru/, https://github.com/paulmann/RAGRAF, https://github.com/paulmann/RAGRAF/releases/tag/v0.3.0
Промт для агента (установка и настройка)
Готовый промт: скопируйте блок целиком и отдайте ассистенту. Шаги и команды взяты из README.ru.md репозитория и с сайта продукта, а регламент — из раздела «Регламент для агента» этой статьи: его нужно положить в AGENTS.md вашего агента, иначе он вернётся к grep.
Задача: поставить RAGRAF 0.3.0, проиндексировать один проект, подключить индекс к агенту
и доказать замерами, что агент работает через индекс, а не через grep.
Материалы:
- сайт продукта: https://ragraf.ru/ru/
- вики: установка (https://ragraf.ru/ru/wiki/getting-started/), CLI
(https://ragraf.ru/ru/wiki/cli/), свежесть и дрейф
(https://ragraf.ru/ru/wiki/freshness-and-drift/)
- репозиторий: https://github.com/paulmann/RAGRAF
- установка и требования: README.ru.md, раздел «Установка»
1. Установка (Windows, PowerShell 7.6+; Python 3.13+ должен быть в PATH):
iwr -OutFile install.ps1 https://raw.githubusercontent.com/paulmann/RAGRAF/main/install.ps1
pwsh -NoLogo -NoProfile -File .\install.ps1 -Yes
Альтернатива из дерева проекта:
git clone https://github.com/paulmann/RAGRAF.git
cd RAGRAF
pwsh -NoLogo -NoProfile -File scripts/install.ps1 -Project C:\work\myproject -Index
Без модели эмбеддингов (только лексический поиск): добавить -NoVectors.
Сеть с TLS-инспекцией: -WithTruststore.
2. Корпус:
ragraf ensure C:\work\myproject
Ожидаемые статусы: actual, reindexed или created. Любой другой — остановиться.
Добавить .ragraf/ в .gitignore индексируемого репозитория.
3. Настройка (файл, который напечатала команда init или ensure):
- languages/include: убедиться, что .ps1, .sh, .py, .cs, .pl попали (иначе файлы не индексируются);
- exclude: служебные каталоги проекта (не добавлять в общий список bin/cache/logs);
- secrets_exclude: .env, ключи, локальные конфиги — файлы не читаются вовсе;
- index.reindex_after_changes (5), full_reindex_after_changes (20),
reindex_interval_minutes (15) — оставить по умолчанию до первых замеров;
- joern_enabled: false, если JDK 21 и Joern не ставились (тогда slice будет отказывать с причиной).
4. Подключение к агенту (MCP, stdio):
команда: python
аргументы: C:/RAGRAF/tools/ragraf-mcp.py --project <конфиг корпуса>
--agent <имя агента> --agent-kind external
В правила агента добавить регламент: навигация только через symbol/search, полный
файл только через live_read по найденному факту, проверка конверта (stale_warnings,
capability), write_check перед любой записью в БД, impact --depth 3 перед правкой,
«не найдено» — валидный ответ.
5. Проверка (что считать успехом):
ragraf --version → 0.3.0
ragraf -p <конфиг> status → capability, покрытие, размер журнала
ragraf -p <конфиг> query symbol --name <известный символ> → path:line
ragraf -p <конфиг> query search "<фраза из README>" → фрагменты с путями
ragraf -p <конфиг> check → суммы совпадают
ragraf -p <конфиг> usage --agent <имя> → вызовы атрибутированы агенту
python tools/ragraf-admin.py --no-browser → панель на 127.0.0.1:8777
Ограничения:
- глобальные флаги -p и --json ставятся ДО подкоманды (исключение: ragraf ensure <dir> --json);
- selftest всегда возвращает код 0 — читать тело отчёта, а не код;
- код 3 у slice означает отсутствие CPG, а не «ничего не найдено»;
- не менять пороги переиндексации и не включать схлопывание копий до первых замеров;
- если команда падает — остановиться и показать команду, вывод и код возврата.