# Рекурсивное улучшение поддержки языков: шлифуем интеллектуальный анализ кода на ~280 языках

Canonical source: https://maguyva.ai/ru/blog/autonomous-quality-loops-language-grind

License: text under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/); code samples under [MIT-0](https://opensource.org/license/mit-0); the Maguyva name, logos, brand assets and other code are not covered. Details: [maguyva.ai/license](https://maguyva.ai/license).

Date: 2026-05-26 · Author: Maguyva Team

> Мы поддерживаем интеллектуальный анализ кода для ~280 языков. Ни один человек не может вручную проверить это. Поэтому мы построили цикл рекурсивного улучшения поддержки языков — выборочная проверка, LLM в роли судьи, исправление одной вещи, повторная валидация — и прогоняем его флотом изолированных агентов, пока извлечение не станет по-настоящему верным, а не просто «зелёным».

> *Цифры в этом посте отражают состояние системы на момент публикации (май 2026). Актуальные цифры смотрите на нашей [странице команды](/ru/team).*

Maguyva извлекает символы, ссылки и граф зависимостей из исходного кода примерно на 280 языках. Каждый язык — это собственный обработчик tree-sitter: запросы, эвристики, пограничные случаи, — и каждый обработчик может по-своему ошибаться в тонких деталях. Вызов метода, выданный как чтение. Функция, приписанная не тому охватывающему скоупу. Связь, которой на самом деле нет.

Это нельзя проверить вручную. Ни одна команда не в силах прочитать вывод извлечения по 280 грамматикам и заметить плохие рёбра графа. Поэтому интересный вопрос не «верно ли наше извлечение» — а «как *узнать*, что оно неверно, на такой ширине охвата, без человека в каждом цикле». Наш ответ — рекурсивное улучшение поддержки языков: цикл контроля качества под управлением языковых агентов и LLM в роли судьи, а также одно правило, которое мы усваиваем снова и снова: зелёный — не значит верный.

## Зелёный — не значит верный

У каждого языка есть набор фикстур, и контроль перед релизом оценивает его по пяти измерениям — точность, структурная целостность, полнота, качество и производительность. Язык становится **ЗЕЛЁНЫМ** только тогда, когда на его фикстурах precision ≥ 0,95, recall ≥ 0,99 и F1 ≥ 0,97, при не менее чем 20 ожидаемых рёбрах для статистической уверенности. Ниже этого — ЖЁЛТЫЙ или КРАСНЫЙ, и такой язык не отправляется в релиз.

Этот контроль необходим, но недостаточен. Фикстуры проверяются на фикстурах, которые *написали мы сами*. Они кодируют случаи, о которых мы уже подумали. Обработчик может быть безупречен на своих фикстурах и всё равно ломать паттерн, который встречается только в реальном коде — идиому макроса, метод с ограничениями обобщённых типов, языковую особенность, для которой никто не написал фикстуру. ЗЕЛЁНЫЙ означает, что фикстуры проходят. Это не значит, что реальный репозиторий извлекается чисто. Поэтому циклу приходится оставлять фикстуры позади и проверять реальный код.

## Внутренний цикл: выборочная проверка, суд, исправление, доказательство

Основной цикл работает по одному языку за раз:

```text
        ┌──────────────────────────────────────────────────────────┐
        │                                                          │
        ▼                                                          │
  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 заняло бы вечность, поэтому внутренний цикл обёрнут во внешний, который прогоняет многие языки параллельно.

```text
   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 языках «верно» нельзя просто заявить; это нужно непрерывно *заслуживать* на реальном коде. Этот цикл — способ, которым мы это заслуживаем: автономный цикл выборочной проверки и исправления, который относится к собственным метрикам с подозрением, доказывает каждое изменение и превращает каждое исправление в защиту от следующей регрессии. Это не эффектно. Это работа, которая позволяет нам сказать «мы поддерживаем ваш язык» и иметь это в виду. Зелёный — легко. Верный — заслужено.
