Рекурсивное самосовершенствование языков: шлифуем интеллектуальный анализ кода на ~280 языках
> Мы поддерживаем интеллектуальный анализ кода для ~280 языков. Ни один человек не может вручную проверить это. Поэтому мы построили цикл рекурсивного самосовершенствования языков — выборочная проверка, LLM в роли судьи, исправление одной вещи, повторная валидация — и прогоняем его флотом изолированных агентов, пока извлечение не станет по-настоящему верным, а не просто «зелёным».
Цифры в этом посте отражают состояние системы на момент публикации (май 2026). Актуальные цифры смотрите на нашей странице команды.
Maguyva извлекает символы, ссылки и граф зависимостей из исходного кода примерно на 280 языках. Каждый язык — это собственный обработчик tree-sitter: запросы, эвристики, пограничные случаи, — и каждый обработчик может быть по-своему тонко неправ. Вызов метода, выданный как чтение. Функция, приписанная не тому охватывающему скоупу. Связь, которой на самом деле нет.
Это нельзя проверить вручную. Ни одна команда не в силах прочитать вывод извлечения по 280 грамматикам и заметить плохие рёбра графа. Поэтому интересный вопрос не «верно ли наше извлечение» — а «как узнать, что оно неверно, на такой ширине охвата, без человека в каждом цикле». Наш ответ — рекурсивное самосовершенствование языков: цикл качества, движимый языковыми агентами, LLM в роли судьи и одно правило, которое мы постоянно переучиваем: зелёный — не значит верный.
Зелёный — не значит верный
У каждого языка есть набор фикстур, и релизный гейт оценивает его по пяти измерениям — точность, структурная целостность, полнота, качество и производительность. Язык становится ЗЕЛЁНЫМ только тогда, когда на его фикстурах precision ≥ 0,95, recall ≥ 0,99 и F1 ≥ 0,97, при не менее чем 20 ожидаемых рёбрах для статистической уверенности. Ниже этого — ЖЁЛТЫЙ или КРАСНЫЙ, и такой язык не отправляется в релиз.
Этот гейт необходим, но недостаточен. Фикстуры проверяются на фикстурах, которые написали мы сами. Они кодируют случаи, о которых мы уже подумали. Обработчик может быть безупречен на своих фикстурах и всё равно ломать паттерн, который встречается только в реальном коде — идиому макроса, метод с обобщёнными границами, языковую особенность, для которой никто не написал фикстуру. ЗЕЛЁНЫЙ означает, что фикстуры проходят. Это не значит, что реальный репозиторий извлекается чисто. Поэтому циклу приходится оставлять фикстуры позади и смотреть в дикую природу.
Внутренний цикл: выборочная проверка, суд, исправление, доказательство
Основной цикл работает по одному языку за раз:
┌──────────────────────────────────────────────────────────┐
│ │
▼ │
1. corpus run ── clone real-world repos, extract relationships │
│ │
▼ │
2. spot-check 100 edges (seed 42, then seed 123 to cross-check) │
│ │
▼ │
3. LLM judge classifies every sampled edge: │
CORRECT · FALSE_POSITIVE · TYPE_ERROR · │
SCOPE_ERROR · METADATA_ERROR │
│ │
▼ │
4. fix ONE thing — handler .py, .scm query, or config │
│ │
▼ │
5. re-validate — F1 + re-classify + manifest diff (no regressions)│
│ │
better? ──no──► revert, try a different fix ────────────────────┤
│ yes │
▼ │
6. promote the fix into fixtures (a permanent regression guard) ──┘
Есть несколько вещей, которые заставляют это работать, а не буксовать.
Судья — это агент, а не вызов API. Когда мы говорим «LLM в роли судьи», мы имеем в виду, что сам языковой агент читает каждое отобранное ребро графа на фоне реального исходного кода и классифицирует его по фиксированному рубрикатору из пяти категорий: это ребро верно, это ложное срабатывание, правильная связь с неверным типом, привязано не к тому скоупу или несёт неверные метаданные? Этот рубрикатор — вся суть игры: цифра «23% ошибок» ничего не значит, пока вы не знаете, реальные ли это дефекты или судья считает неверно.
Исправь одну вещь, затем докажи это. Каждая итерация меняет ровно один редактируемый актив, затем перезапускается на фиксированном стенде и сохраняет изменение, только если F1 улучшился, а повторная классификация выглядит лучше. Если нет — откат. Никаких пакетов спекулятивных правок, никаких «должно стать лучше». Изменение либо заслуживает своего места, либо исчезает. А когда исправление приживается, оно продвигается в набор фикстур — чтобы исправленный баг никогда тихо не вернулся. Именно этот шаг продвижения делает цикл рекурсивным, а не просто повторяющимся: каждый проход укрепляет эталон, по которому проверяется следующий проход.
Урок, который мы постоянно переучиваем: метрики завышают проблему
Вот ловушка, в которую мы вляпались прямиком. Вторичные метрики корпуса — как часто у извлечённой цели нет разрешимого символа, сколько символов выглядят «осиротевшими» и так далее — сильно завышают проблему. По большей части это артефакты парадигмы, а не баги.
Самый чистый пример: llvm однажды показал 73% доли «источник без символа» и был заклеймён катастрофическим. Мы копнули глубже. Реальная точность составляла 98,5%. «Отсутствующие символы» почти все оказались легитимными внешними ссылками — вызовами в стандартную библиотеку, во фреймворки, в код, живущий за пределами репозитория. Метрика измеряла свойство самого языка, а не дефект обработчика. Такие языки, как Zig, COBOL и Odin, показывают 65–70% доли «сирот» и при этом полностью корректны; у COBOL не было ни одной реальной ошибки.
Если бы мы позволили этим цифрам управлять работой, мы бы потратили недели на «исправление» обработчиков, которые уже были правильными, и игнорировали языки с тихими, но реальными багами. Вывод прямолинеен: агрегированные метрики в лучшем случае — грубый сигнал для триажа. Настоящий сигнал качества — это выборочная проверка с классификацией рёбер графа: просмотр реальных рёбер в реальных репозиториях и вынесение суждения по каждому из них. Данные важнее интуиции, но только когда вы знаете, какие данные говорят правду.
Внешний цикл: флот, а не марафон
Обрабатывать по одному языку за раз на всех 280 заняло бы вечность, поэтому внутренний цикл обёрнут во внешний, который прогоняет многие языки параллельно.
pick a wave of near-GREEN / high-error languages
│
▼
fan out 10–15 agents, each ISOLATED in its own git worktree,
each grinding ONE language, committing to its own branch
│
▼
orchestrator integrates serially: file-scoped apply, then
`manifest diff` — any cross-language regression blocks the batch
│
▼
gated push (reviewed and confirmed) ──► rotate to the next wave
Каждый агент работает в одноразовом дереве (worktree), чтобы агенты не мешали друг другу. Оркестратор интегрирует их исправления по одному, каждое за проверкой регрессии по полному манифесту: изменение, которое помогает своему языку, но незаметно ломает три других, не проходит. Интеграция проходит через гейт и подтверждается прежде, чем что-либо будет отправлено — гейт регрессии решает, что безопасно, но что отправляется в релиз, по-прежнему решает человек. Затем пул переключается на следующий набор языков, и всё повторяется снова.
Что всё ещё несовершенно
Судья может ошибаться, и у этой ошибки есть направление: агент, работающий со слишком малым контекстом, завышает число проблем. В одной партии восемь языков были помечены с 5–20% ошибок; при проверке лишь один оказался реальным багом конкретного языка — остальное были ошибки судьи из-за незнания собственной семантики языка (одна строка исходника, легитимно порождающая несколько рёбер, привязки параметров, смоделированные как присваивания, языки с одной ссылкой на идентификатор). Именно поэтому мы делаем выборку с двумя случайными затравками и перекрёстно сверяем результаты, и именно поэтому расхождение между судьёй и фикстурами считается самым интересным сигналом, а не окончательным вердиктом — иногда неправа именно фикстура.
Мы также честны насчёт цели. Цель — ноль реальных ошибок, и точка, — но «ноль» — это направление, к которому мы шлифуем язык за языком, а не галочка, которую можно поставить раз и навсегда. Всегда найдётся ещё один репозиторий с ещё одной идиомой.
Заслужено, а не заявлено
Всё, что Maguyva делает для агента — находит символ, прослеживает зависимость, отвечает на вопрос со ссылкой на код, — опирается на то, что лежащее в основе извлечение верно. На 280 языках «верно» нельзя просто заявить; это нужно непрерывно заслуживать на реальном коде. Этот цикл — способ, которым мы это заслуживаем: автономный цикл выборочной проверки и исправления, который относится к собственным метрикам с подозрением, доказывает каждое изменение и превращает каждое исправление в защиту от следующей регрессии. Это не эффектно. Это работа, которая позволяет нам сказать «мы поддерживаем ваш язык» и иметь это в виду. Зелёный — легко. Верный — заслужено.
Похожие материалы
Ещё из журнала разработки Maguyva
Почему мы обновили поиск по коду до voyage-4-large_
Мы перевели эмбеддинги кода на voyage-4-large — модель, которая сейчас возглавляет публичный рейтинг RTEB по поиску кода. Честная версия: какой компромисс мы принимаем, что мы на самом деле индексируем и почему платим за премиальные эмбеддинги.
Мультимодальный фьюжн-поиск: выбор правильного ретривера для каждого запроса_
Запрос вроде «где определён parseConfig» требует другого поиска, чем «как работает аутентификация». Maguyva классифицирует намерение, соответствующим образом взвешивает четыре модальности поиска и сливает результаты с помощью взвешенного Reciprocal Rank Fusion.
Наблюдаемость агентов: хуки, Alloy и Grafana_
Мы подключили Claude Code и Codex к единому стеку Grafana через OpenTelemetry и Alloy, а затем с помощью трасс и логов находили и устраняли проблемы в поведении агентов прямо у источника.