Перейти к основному содержимому
CodeAlive 3.0: от context engine к агенту для исследования кода
CodeAlive

Ко всем статьям

GraphRAG по коду: когда граф вызовов действительно помогает агенту

Мы много вложили в точный граф кода: вызовы, ссылки на типы, наследование и реализации интерфейсов, и у каждой связи есть точное место в исходнике. На корпусах, где эталон строит компилятор, точность графа около 0,98 (сравнение с открытыми индексаторами).

Потом мы отдали граф нашему исследовательскому агенту и измерили, что изменилось. На репозитории среднего размера почти ничего. На большом и более сложном граф помог заметнее. А главной проблемой оказалось не построить граф, а добиться, чтобы агент им пользовался. Здесь многое зависит от модели.

Как мы мерили

Мы взяли RepoContextBench v2: 40 вопросов по двум зафиксированным репозиториям, ответ на каждый нужно подтвердить кодом.

ТрекРепозиторийРазмерВопросов
LiteMicrosoft Agent Framework~680 тыс. строк на C# и Python20
HardSapling (система контроля версий от Meta)~1,6 млн строк на Rust, Python, C++ и TypeScript20

Во всех конфигурациях работал один и тот же исследовательский агент CodeAlive на одном индексе, оценивал один и тот же судья (Muse Spark 1.3, два голоса на задачу). Менялось только то, как граф связей попадал к модели. В большинстве прогонов отвечал DeepSeek V4.1 Flash. Шкала от 0 до 100.

Сразу оговорка. Hard отличается от Lite не только размером: вопросы там сложнее, языки другие. Поэтому корректно говорить «на большой и более сложной кодовой базе граф помогает сильнее», а не «прирост дал именно размер». Повтор одного и того же прогона сдвигал оценку примерно на ±2 пункта, всё, что меньше, мы считаем шумом.

Мы дали агенту инструмент для графа, и он его ни разу не вызвал

Сначала граф был отдельным инструментом, get_artifact_relationships. Он возвращает вызывающих и вызываемых, реализации и ссылки для символа.

У DeepSeek V4.1 Flash этот инструмент был доступен во всех 40 задачах. Он не вызвал его ни разу. Когда мы убрали инструмент совсем, оценка не упала: 62,2 без него против 59,4 с ним, в пределах шума.

Тогда мы стали подталкивать модель к графу:

Что пробовалиВызовов графа
Инструкция «сначала граф» в системном промпте0 из 20 задач
Подсказка следующего шага в выдаче инструмента0 из 6 точек решения
Переписанное описание, инструмент первым в списке0 из 6
Few-shot примеры с графом0 из 6
Повышенный reasoning0 из 6
Всё сразу1 из 6
Раздельные find_callers / find_callees3 из 4, но только на прямых вопросах «кто вызывает X»

Почему так, стало понятно из рассуждений модели в этих точках. Ей нужны литеральные факты: сигнатуры, имена свойств, ключевые слова. Поиск и чтение файлов дают ровно это, поэтому граф она даже не рассматривает. А практические вопросы чаще звучат как «как работает X», а не «кто вызывает X». Подсказки в результатах инструментов хорошо работали у нас для другого поведения, но это не изменили.

Всё зависит от модели

Те же задачи на других моделях:

МодельВызовов графа, LiteВызовов графа, Hard
DeepSeek V4.1 Flash0–10–1
Qwen 3.8 27B00
GPT-5.6 Luna315

Более сильная модель сама пошла в граф, причём на большом репозитории в пять раз чаще. Для Luna наличие инструмента дало около 4–5 пунктов на обоих треках. Это примерно два уровня шума, и по одному прогону на конфигурацию.

Если агент не спрашивает, кладём граф туда, где он и так читает

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

Граф модель теперь видела почти в каждой задаче. Оставалось понять, помогает ли он:

МетрикаLite, без → с графомHard, без → с графом
Найдено нужных файлов81,2 → 84,478,3 → 82,4
Найдено фрагментов-доказательств60,3 → 61,064,2 → 69,6
Утверждений, подкреплённых кодом65,6 → 64,655,9 → 60,8
Оценка ответа73,9 → 74,055,5 → 56,7

На Lite разница в пределах шума. На Hard агент собрал заметно больше нужного кода, плюс 4–5 пунктов по каждой метрике поиска. Итоговый ответ вырос меньше, на 1,2 пункта, это ниже нашего порога шума.

Лишний контекст мешает

Ещё мы попробовали добавить к графу точные позиции каждого вызова. Выглядело полезно: агент мог бы сразу перейти к нужной строке.

Стало хуже: минус 6,2 пункта на Hard и минус 3,5 на всех 40 задачах. Агенту показали 2 276 позиций вызовов, и он не процитировал ни одной. Они заняли место в контексте и ничем не помогли.

Что мы из этого вынесли

Прежде чем сравнивать оценки, посчитайте вызовы графа в трассах. Инструмент, который модель не вызывает, помочь не может, а по списку доступных инструментов этого не видно.

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

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

И делайте повторы. Шум между прогонами у нас ±2 пункта, и дважды промежуточный результат на 6–11 задачах показывал лидера, который проигрывал на полном наборе.

Гораздо сильнее графа на результат повлияла сама обвязка агента. На той же модели harness CodeAlive набрал на 18,7 пункта больше, чем универсальный агент для кода: Одна модель, разные harness: CodeAlive против OpenCode.

БенчмаркиContext EngineeringАгенты для кода

Новые статьи

Следите за работой, а не за маркетинговой воронкой

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

Дайте агентам всю кодовую базу

Проиндексируйте первый репозиторий за минуты — или попробуйте демо без регистрации.