Все статьи

28 сентября 2026 г.

BSL Language Server vs граф зависимостей 1С: что реально кормить ИИ-агенту

Две парадигмы: язык и связи

Когда тимлид впервые подключает Claude Code или Cursor к конфигурации 1С, возникает один и тот же вопрос: «У меня же уже стоит BSL Language Server — зачем ещё какой-то граф зависимостей?». Ответ короткий: BSL LS отвечает на вопрос «правильно ли написан код», а граф зависимостей — на вопрос «какой код вообще относится к задаче». Для LLM-агента второе важнее в разы, потому что контекстное окно — это дефицитный ресурс, а не абстракция.

Что умеет BSL Language Server

BSL LS — это LSP-сервер и набор диагностик для языка BSL. Он умеет:

  • парсить модули и строить AST;
  • выдавать 100+ диагностик (неиспользуемые переменные, Ссылка.ПолучитьОбъект() в цикле, неправильные Попытка/Исключение, запросы в цикле);
  • поддерживать LSP-примитивы: textDocument/definition, references, hover, documentSymbol, callHierarchy;
  • форматировать код и работать как quality gate в CI.

Для человека в IDE это отлично. Для агента — почти бесполезно в чистом виде. Причины ниже.

Почему LSP ≠ контекст для LLM

  1. LSP поточечный, а не пакетный. На запрос textDocument/definition вы получите одно определение, а не замыкание вызовов. Агенту же нужны 5–7 уровней: ПриЗаписиНаСервере → РассчитатьСуммуСНДС → ОбщегоНазначения.ЗначениеРеквизитаОбъекта → подписка ПередЗаписью в другом общем модуле.
  2. N round-trip вместо одного запроса. Каждый переход — отдельный вызов MCP/IDE-моста, каждый ответ снова улетает в контекст. 20 переходов — это 20 × 2–4 КБ служебных данных.
  3. documentSymbol на общем модуле УТ 11 — это тысячи символов. Агент получает оглавление вместо нужного абзаца.
  4. LSP не знает метаданных. BSL LS видит код, но не видит, что Документ.РеализацияТоваровУслуг входит в подсистему, что на реквизит СуммаСНДС завязан отчёт СКД, а на таблицу — регистр накопления. Метаданные и код в LSP живут раздельно.
  5. Нет запросов как сущности. Текст запроса — это строка. Связь «поле запроса → реквизит метаданных → тип» LSP не строит.

Что такое граф зависимостей 1С

Графон индексирует выгрузку конфигурации и строит ориентированный граф, где узлы — это:

  • объекты метаданных (Документ, Справочник, Регистр, ОбщийМодуль, ПодпискаНаСобытие, Форма);
  • процедуры и функции (МодульОбъекта.ПриЗаписиНаСервере);
  • реквизиты и измерения, табличные части, поля форм;
  • тексты запросов и их поля;
  • подписки на события и их обработчики.

Рёбра — это вызывает, переопределяет, обрабатывает событие, читает поле, пишет реквизит, используется в запросе, обрабатывает заполнение. Всё это лежит локально, обычно в SQLite/embedded-графе, и отдаётся агенту через MCP.

Архитектура Графона

Выгрузка XML (ERP, УТ, ЗУП)
        │
        ▼
Парсер XML + AST модулей BSL
        │
        ▼
Индексатор (граф: узлы + рёбра)
        │
        ▼
MCP-сервер (localhost)
        │
        ├──► Claude Code
        ├──► Cursor
        └──► Windsurf / Roo Code

MCP-запросы: как это выглядит

Агент не «грепает» репозиторий, а делает пакетный запрос:

{
  "tool": "grafon_pack_context",
  "arguments": {
    "entry_point": "Документ.РеализацияТоваровУслуг.МодульОбъекта.ПриЗаписиНаСервере",
    "depth": 2,
    "include": ["callers", "subscriptions", "queries", "metadata_fields"],
    "budget_tokens": 12000
  }
}

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

{
  "nodes": [
    { "id": "Документ.РеализацияТоваровУслуг.ПриЗаписиНаСервере", "kind": "procedure", "lines": 74 },
    { "id": "ОбщийМодуль.СуммыНДС.РассчитатьСуммуСНДС", "kind": "function", "signature": "РассчитатьСуммуСНДС(Товары)" },
    { "id": "Подписка.ПередЗаписьюРеализации", "kind": "subscription", "handler": "ОбщийМодуль.КонтрольОстатков.ПередЗаписьюРеализации" }
  ],
  "edges": [
    "ПриЗаписиНаСервере -> РассчитатьСуммуСНДС",
    "ПриЗаписиНаСервере -> ЗарегистрироватьИзменения",
    "Подписка.ПередЗаписьюРеализации -> КонтрольОстатков.ПередЗаписьюРеализации"
  ],
  "queries": [
    { "owner": "ПриЗаписиНаСервере", "tables": ["Документ.РеализацияТоваровУслуг.Товары"], "fields": ["Номенклатура", "Сумма"] }
  ]
}

Другой типовой вызов — анализ влияния перед изменением реквизита:

{
  "tool": "grafon_impact_analysis",
  "arguments": {
    "target": "Справочник.Номенклатура.Реквизит.СтавкаНДС",
    "edge_kinds": ["reads", "writes", "query_field", "form_attribute"],
    "max_nodes": 200
  }
}

BSL-код, который агент получает целиком вместо 3000 строк

Без графа агент читает модуль документа (часто 2500–4000 строк) и общий модуль КонтрольОстатков (иногда 8000+ строк). С графом — только этот фрагмент:

Процедура ПриЗаписиНаСервере(Отказ, РежимЗаписи, РежимПроведения)
    СуммаСНДС = РассчитатьСуммуСНДС(Объект.Товары);
    Объект.СуммаСНДС = СуммаСНДС;
    // Граф подсказал: есть подписка ПередЗаписью, изменяющая Объект
    ЗарегистрироватьИзменения(Объект.Ссылка, "Запись");
КонецПроцедуры

Функция РассчитатьСуммуСНДС(Товары)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Товары.Номенклатура КАК Номенклатура,
| СУММА(Товары.Сумма) КАК Сумма,
| Товары.СтавкаНДС КАК СтавкаНДС
|ИЗ
| Документ.РеализацияТоваровУслуг.Товары КАК Товары
|ГДЕ
| Товары.Ссылка = &Ссылка
|СГРУППИРОВАТЬ ПО
| Товары.Номенклатура,
| Товары.СтавкаНДС";
Запрос.УстановитьПараметр("Ссылка", Ссылка);
Выборка = Запрос.Выполнить().Выбрать();
СуммаСНДС = 0;
Пока Выборка.Следующий() Цикл
СуммаСНДС = СуммаСНДС + Выборка.Сумма * Выборка.СтавкаНДС / 100;
КонецЦикла;
Возврат СуммаСНДС;
КонецФункции

Агент сразу видит, что СтавкаНДС — это реквизит табличной части, и что на итог влияет подписка. Без графа он бы нашёл функцию после 6–8 grep-итераций и, вероятно, выдумал несуществующий метод НДССумма().

Сравнение: с Графоном и без

| Сценарий | Подход | Токены | Время | Галлюцинации |
|---|---|---|---|---|
| Доработать контроль остатков при записи реализации | grep + чтение файлов | ~186 000 | 9–14 мин | 3–5 несуществующих методов |
| То же | Графон через MCP | ~14 500 | 35–50 сек | 0 |
| Рефакторинг общего модуля на 8 000 строк | чтение модуля целиком | ~64 000 | 4–6 мин | путаница одноимённых функций |
| То же | Графон + call hierarchy | ~9 200 | 25 сек | 0 |
| Анализ влияния изменения реквизита | ручной поиск ссылок | не сходится, агент «сдаётся» | 15+ мин | 40 % пропущенных точек |
| То же | grafon_impact_analysis | ~6 000 | 12 сек | 0 |

Итого по бюджету: экономия токенов 8–12×, времени — 6–15×.

Практические сценарии

  1. Фикс бага в типовой. Агент получает только цепочку кнопка формы → команда → серверная процедура → запрос → регистр. Остальные 700 модулей не грузятся.
  2. Генерация нового общего модуля. Графон отдаёт список существующих похожих функций, чтобы агент не создал дубль ОбщегоНазначения.ЗначениеРеквизитаОбъекта.
  3. Миграция на новую версию. Граф показывает все места, где используется изменённый метод БСП.
  4. Аудит подписок. Один MCP-запрос отдаёт все подписки на ПередЗаписью с обработчиками — типичная задача при разборе «почему документ пишется дважды».
  5. Ревью pull request от агента. В контекст подтягиваются вызывающие и вызываемые, а не весь модуль.

Гибридная схема: LSP + граф

Не нужно выбирать одно. Рабочая конфигурация для команды:

  • BSL Language Server — pre-commit и CI: диагностики, форматирование, порог по ошибкам.
  • Графон (MCP) — рантайм ИИ-агента: сборка контекста, impact analysis, поиск подписок и запросов.
  • .cursorrules / CLAUDE.md — правило: «перед чтением любого модуля вызови grafon_pack_context».

Антипаттерны

  • Отдавать агенту documentSymbol по общему модулю — это не контекст, а оглавление.
  • Полагаться на grep по XML-выгрузке: структура каталогов у ERP и УТ различается, regex ломается.
  • Грузить формы целиком: Form.xml на 5 МБ съедает окно за один ход.
  • Игнорировать подписки: в 1С логика часто живёт не в модуле объекта.

Вывод

BSL Language Server — это компилятор совести разработчика: он скажет, что код плохой. Граф зависимостей — это карта местности: он скажет, какой именно код относится к задаче. Для ИИ-кодинга в 1С критичен второй пункт, потому что ограничение — не качество модели, а объём контекста. Графон закрывает эту проблему на уровне MCP: один запрос — точный срез замыкания вызовов, метаданных, подписок и запросов, минус 10× токенов и почти нулевые галлюцинации. BSL LS при этом остаётся на своём месте — в CI, где он и должен быть.