Все статьи

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

Автоматический рефакторинг BSL-кода с помощью Claude 3.7 Sonnet и MCP-сервера Графон

Почему Claude 3.7 Sonnet «слепнет» на реальных конфигурациях 1С

Claude 3.7 Sonnet — сильная модель для кода, но её контекстное окно (200k токенов) конечно. Типовая ERP или УТ — это гигабайты XML-метаданных и сотни тысяч строк BSL. Даже один общий модуль вроде ОбщегоНазначения или УправлениеЗапасами — это десятки тысяч строк, а рефакторинг почти никогда не локален: правка метода в модуле менеджера тянет за собой подписки на события, вызовы из форм, обращения к регистрам и запросы.

Если скормить агенту «весь проект» или папку модулей целиком, происходит предсказуемое:

  • Переполнение контекста. Модули обрезаются посередине, модель теряет сигнатуры и начинает «дорисовывать» несуществующие методы.
  • 10-кратный перерасход токенов. Агент читает десятки нерелевантных файлов на каждую итерацию.
  • Галлюцинации. Claude уверенно вызывает ПолучитьЦенуНоменклатурыНаДату() там, где в конфигурации такого метода нет и никогда не было.
  • Потеря связей. Формальные параметры подписок, экспортные методы, обращения к реквизитам — всё это не видно без графа зависимостей.

Именно эту проблему решает Графон — локальный инструмент и MCP-сервер для конфигураций 1С:Предприятие.

Архитектура: локальный граф Графона + Claude 3.7 Sonnet

[Выгрузка конфигурации 1С: XML + BSL]
              │ индексация (полная / инкрементальная)
              ▼
[Графон: граф метаданных, вызовов, подписок, запросов]
              │ MCP (stdio / localhost)
              ▼
[Claude Code | Cursor | Windsurf] ──► Claude 3.7 Sonnet

Что именно строит Графон

| Слой графа | Узлы | Рёбра |
|---|---|---|
| Метаданные | Справочники, документы, регистры, формы | Владелец → реквизит, ТЧ → реквизит |
| Код | Модули, процедуры, функции | Кто вызывает кого, экспорт/локальный |
| События | Подписки, ПередЗаписью, ПриПроведении | Обработчик ↔ объект-источник |
| Запросы | Тексты запросов, таблицы | Запрос → таблица метаданных |
| Формы | Элементы, обработчики, команды | Форма → модуль → процедура |

Построение индексируется один раз. Для ERP первичный проход — десятки минут, дальше — инкрементально, секунды на изменённый файл.

Как это выглядит со стороны агента

Агент не читает файлы — он вызывает инструменты Графона через MCP.

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "grafon_find_routine",
    "arguments": {
      "name": "ПересчитатьСуммы",
      "includeCallers": true,
      "includeCallees": true,
      "depth": 2
    }
  }
}

Ответ — компактный срез, а не файлы:

{
  "routine": "Документ.РеализацияТоваровУслуг.МодульОбъекта.ПересчитатьСуммы",
  "signature": "Процедура ПересчитатьСуммы(Документ) Экспорт",
  "callers": [
    "ОбщийМодуль.РеализацияТоваровУслугСервер.ПровестиДокумент",
    "Подписка.ПередЗаписьюРеализацияТоваровУслуг.ОбработчикПередЗаписью"
  ],
  "callees": ["ОбщегоНазначения.СообщитьПользователю"],
  "touched_metadata": [
    "РегистрСведений.ЦеныНоменклатуры",
    "Документ.РеализацияТоваровУслуг.Товары"
  ],
  "tokens_estimate": 1840
}

Второй ключевой вызов — получение точного среза модулей и проверка подписок:

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "grafon_get_context_slice",
    "arguments": {
      "roots": ["Документ.РеализацияТоваровУслуг.МодульОбъекта.ПересчитатьСуммы"],
      "include_metadata": ["РегистрСведений.ЦеныНоменклатуры"],
      "include_event_subscriptions": true,
      "max_tokens": 12000
    }
  }
}

Агент получает ровно те сигнатуры, тела методов и описания метаданных, которые нужны для правки. Ничего лишнего.

Сценарий 1. Устранение «запроса в цикле»

Классическая проблема производительности. Агент находит её так: grafon_find_query_usage по таблице РегистрСведений.ЦеныНоменклатуры, затем grafon_get_context_slice по найденному методу.

Было:

Процедура ПересчитатьСуммы(Документ) Экспорт
    Для Каждого СтрокаТЧ Из Документ.Товары Цикл
        Запрос = Новый Запрос;
        Запрос.Текст =
        "ВЫБРАТЬ ПЕРВЫЕ 1
        |    Цены.Цена КАК Цена
        |ИЗ
        |    РегистрСведений.ЦеныНоменклатуры КАК Цены
        |ГДЕ
        |    Цены.Номенклатура = &Номенклатура
        |    И Цены.Период <= &Период
        |УПОРЯДОЧИТЬ ПО
        |    Цены.Период УБЫВ";
        Запрос.УстановитьПараметр("Номенклатура", СтрокаТЧ.Номенклатура);
        Запрос.УстановитьПараметр("Период", Документ.Дата);

Выборка = Запрос.Выполнить().Выбрать();
Если Выборка.Следующий() Тогда
СтрокаТЧ.Цена = Выборка.Цена;
КонецЕсли;
СтрокаТЧ.Сумма = СтрокаТЧ.Количество * СтрокаТЧ.Цена;
КонецЦикла;
КонецПроцедуры

Стало (после рефакторинга агентом):

Процедура ПересчитатьСуммы(Документ) Экспорт
    СписокНоменклатуры = Новый Массив;
    Для Каждого СтрокаТЧ Из Документ.Товары Цикл
        СписокНоменклатуры.Добавить(СтрокаТЧ.Номенклатура);
    КонецЦикла;

Если СписокНоменклатуры.Количество() = 0 Тогда
Возврат;
КонецЕсли;

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Цены.Номенклатура КАК Номенклатура,
| Цены.Цена КАК Цена
|ИЗ
| РегистрСведений.ЦеныНоменклатуры КАК Цены
|ГДЕ
| Цены.Номенклатура В (&СписокНоменклатуры)
| И Цены.Период <= &Период
| И Цены.Период В
| (ВЫБРАТЬ
| МАКСИМУМ(Последние.Период)
| ИЗ
| РегистрСведений.ЦеныНоменклатуры КАК Последние
| ГДЕ
| Последние.Номенклатура = Цены.Номенклатура
| И Последние.Период <= &Период)";
Запрос.УстановитьПараметр("СписокНоменклатуры", СписокНоменклатуры);
Запрос.УстановитьПараметр("Период", Документ.Дата);

СоответствиеЦен = Новый Соответствие;
Для Каждого СтрокаЦены Из Запрос.Выполнить().Выгрузить() Цикл
СоответствиеЦен.Вставить(СтрокаЦены.Номенклатура, СтрокаЦены.Цена);
КонецЦикла;

Для Каждого СтрокаТЧ Из Документ.Товары Цикл
Цена = СоответствиеЦен[СтрокаТЧ.Номенклатура];
Если Цена <> Неопределено Тогда
СтрокаТЧ.Цена = Цена;
КонецЕсли;
СтрокаТЧ.Сумма = СтрокаТЧ.Количество * СтрокаТЧ.Цена;
КонецЦикла;
КонецПроцедуры

Что здесь критично: агент не меняет контракт процедуры (сигнатура и экспортность сохранены) и не трогает подписки, потому что Графон отдал список вызывающих. Без графа модель с высокой вероятностью переписала бы метод в локальный или поменяла набор параметров.

Сценарий 2. Распутывание подписок на события

Подписки — главная зона галлюцинаций. Агент спрашивает:

{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "tools/call",
  "params": {
    "name": "grafon_get_event_subscriptions",
    "arguments": {
      "source": "Документ.РеализацияТоваровУслуг",
      "event": "ПередЗаписью"
    }
  }
}

И получает точный список обработчиков с модулями и порядком. Дальше — рефакторинг с гарантией, что ни один обработчик не остался висеть на удалённом методе.

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

Замеры на выгрузке ERP (≈ 220 тыс. строк BSL, 41 тыс. файлов XML):

| Метрика | Без Графона | С Графоном |
|---|---|---|
| Токенов контекста на задачу | 150 000 – 220 000 | 8 000 – 20 000 |
| Итераций агента | 9 – 14 | 3 – 5 |
| Время до готового diff | 2 – 4 часа | 15 – 40 минут |
| Ссылки на несуществующие методы | 5 – 12 | 0 – 1 |
| Правки, ломающие подписки | 1 – 3 | 0 |
| Ревью | со сплошным чтением | по карте вызовов |

| Тип задачи | Токенов без Графона | Токенов с Графоном | Экономия |
|---|---|---|---|
| Извлечение общего метода | 210 000 | 18 000 | 11,7× |
| Оптимизация запроса в цикле | 165 000 | 12 000 | 13,8× |
| Переименование экспортного метода | 240 000 | 21 000 | 11,4× |
| Ревизия подписок объекта | 180 000 | 9 500 | 18,9× |

Как формулировать задачу агенту

Рабочий шаблон промпта для Claude Code / Cursor:

  1. Контекст добывай через Графон, файлы не читай. Явно укажи: «используй MCP-инструменты grafon_*, не открывай модули напрямую».
  2. Границы правки. Перечисли неизменяемые контракты: экспортные методы, сигнатуры подписок, реквизиты метаданных.
  3. Проверка перед правкой. Требуй сначала отчёт grafon_find_routine со списком вызывающих, потом diff.
  4. Проверка после правки. Требуй повторный вызов графа и подтверждение, что все вызывающие по-прежнему разрешаются.
  5. Один метод — один патч. Не давай агенту «отрефактори всё».

Ограничения и границы применимости

  • Графон строит граф по выгрузке конфигурации. Держите её актуальной: инкрементальная индексация дешёвая, но не мгновенная.
  • Динамические вызовы через Вычислить() и Выполнить() в графе не видны — их нужно помечать вручную и запрещать агенту считать их «мёртвым кодом».
  • Объекты, расширяющие чужие модули (.Расширение), требуют отдельной ветки графа.
  • Модель остаётся моделью: финальное ревью 1С-разработчиком обязательно. Графон убирает галлюцинации по связям, но не по бизнес-логике.

Чеклист внедрения

  1. Выгрузить конфигурацию и расширения в файловую структуру XML.
  2. Запустить индексацию Графона, дождаться построения графа.
  3. Подключить MCP-сервер в Claude Code, Cursor или Windsurf (stdio-транспорт).
  4. Прогнать пилотную задачу: один метод, обязательный grafon_find_routine до и после.
  5. Сравнить токены и время с историей «ручных» рефакторингов, зафиксировать регламент.
  6. Добавить в CI проверку: после правки BSL — повторная индексация и валидация разрешимости вызовов.

Автоматический рефакторинг BSL перестаёт быть лотереей ровно в тот момент, когда у агента появляется точный граф связей вместо всего проекта в контексте. Claude 3.7 Sonnet даёт качество рассуждений, Графон — точность входа. Вместе это 10-кратная экономия токенов и предсказуемый diff.