30 сентября 2026 г.
Анализ влияния изменений в общем модуле 1С через граф связей: от grep до MCP-ассистента
Почему правка общего модуля — это всегда минное поле
Любой архитектор 1С знает это чувство: нужно переименовать параметр в ОбщегоНазначения.ЗначениеРеквизитаОбъекта или вынести часть логики из ОбщегоНазначенияКлиентСервер. Метод вызывается из 400 мест, из них 60 — в расширениях, 15 — в подписках на события, а 3 — в динамически формируемых именах через Выполнить().
Стандартный путь разработчика выглядит так:
- Поиск по конфигурации через
Ctrl+Shift+Fпо имени метода. - Ручная фильтрация ложных срабатываний (комментарии, строковые литералы, одноимённые методы другого модуля).
- Открытие каждого модуля, чтобы понять контекст вызова.
- Надежда, что ничего не забыл.
Проблема в том, что типичная ERP или УТ — это гигабайты XML и сотни тысяч строк BSL. Grep находит совпадения, но не различает:
- вызов метода из другого общего модуля;
- вызов через
ОбщегоНазначения.ВызватьМетодПоИмени(); - подписку на событие
ПередЗаписью, которая косвенно зависит от вашего метода; - использование в запросе через вычисляемое поле;
- вызов из формы, который «спрятан» в
&НаКлиенте.
Что именно должен показывать граф связей
Граф зависимостей конфигурации 1С — это ориентированный мультиграф, где узлы — это сущности метаданных и кода, а рёбра — типизированные связи между ними.
| Тип узла | Пример | Тип ребра |
|---|---|---|
| Общий модуль | ОбщегоНазначения | экспортирует метод |
| Процедура/функция | ЗначениеРеквизитаОбъекта | вызывает |
| Подписка на событие | ПередЗаписью_ОбработкаПроведения | обрабатывает событие объекта |
| Форма | Документ.РеализацияТоваровУслуг.Форма.ФормаДокумента | использует модуль |
| Запрос | текстовый ресурс ТекстЗапроса | обращается к таблице |
| Расширение | Расш1_ОбщегоНазначения | переопределяет метод |
Без графа вы видите плоский список совпадений. С графом вы видите транзитивное замыкание — цепочку «кто вызывает → чем вызывается → на что влияет».
Архитектура Графона: локальный разбор + MCP
Графон работает полностью локально, без отправки кода конфигурации в облако:
┌──────────────────────────┐
│ Выгрузка конфигурации │ XML + BSL
│ (ERP, УТ, ЗУП, КА) │
└───────────┬──────────────┘
│ индексация
▼
┌──────────────────────────┐
│ Парсер метаданных и BSL │ сигнатуры, вызовы,
│ + резолвер ссылок │ подписки, запросы
└───────────┬──────────────┘
▼
┌──────────────────────────┐
│ Граф зависимостей │ SQLite / in-memory
│ (узлы, рёбра, атрибуты) │
└───────────┬──────────────┘
│ MCP (stdio / HTTP)
▼
┌──────────────────────────┐
│ Claude Code / Cursor / │ получает только срез
│ Windsurf / Roo Code │ связанных файлов
└──────────────────────────┘
Ключевая идея: ИИ-агент не читает всю конфигурацию. Он обращается к MCP-серверу Графона и получает ровно тот подграф, который нужен для задачи.
Практический сценарий: меняем сигнатуру метода
Допустим, в общем модуле ОбщегоНазначения есть функция:
// Возвращает значение реквизита объекта по имени.
//
// Параметры:
// Ссылка - ДокументСсылка, СправочникСсылка - ссылка на объект
// ИмяРеквизита - Строка - имя реквизита
// Контекст - Структура - дополнительный контекст (устаревший параметр)
//
// Возвращаемое значение:
// Произвольный
//
Функция ЗначениеРеквизитаОбъекта(Ссылка, ИмяРеквизита, Контекст = Неопределено) Экспорт
Если Контекст <> Неопределено Тогда
// Устаревшая ветка, оставлена для совместимости с 2019 годом
Возврат ЗначениеРеквизитаОбъектаУстаревший(Ссылка, ИмяРеквизита, Контекст);
КонецЕсли;
Возврат ОбщегоНазначенияКлиентСервер.ЗначениеРеквизитаОбъекта(Ссылка, ИмяРеквизита);
КонецФункции
Мы хотим убрать третий параметр. Первый шаг — запрос к Графону через MCP:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "analyze_impact",
"arguments": {
"symbol": "ОбщегоНазначения.ЗначениеРеквизитаОбъекта",
"direction": "callers",
"depth": 3,
"includeExtensions": true,
"includeSubscriptions": true,
"includeDynamicCalls": true
}
}
}
Графон возвращает структурированный ответ:
{
"symbol": "ОбщегоНазначения.ЗначениеРеквизитаОбъекта",
"signature_changed": true,
"callers": [
{
"module": "Документ.РеализацияТоваровУслуг.МодульОбъекта",
"method": "ПередЗаписью",
"line": 214,
"uses_param": "Контекст",
"risk": "high"
},
{
"module": "ОбщийМодуль.Расш1_ОбщегоНазначения",
"method": "ЗначениеРеквизитаОбъекта",
"line": 8,
"risk": "extension_override"
},
{
"module": "Подписка.ПередЗаписью_КонтрольОстатков",
"method": "Обработчик",
"line": 57,
"uses_param": null,
"risk": "medium"
}
],
"dynamic_calls": [
"ОбщийМодуль.Мониторинг.ВызватьМетодПоИмени(\"ОбщегоНазначения\", \"ЗначениеРеквизитаОбъекта\", ...)"
],
"total_callers": 412,
"callers_using_removed_param": 63
}
Вместо 412 открытых модулей и ручного анализа вы получаете точный список из 63 мест, где реально используется удаляемый параметр. Остальные 349 — безопасны.
Шаг 2. Получаем минимальный срез кода
Вместо того чтобы скармливать агенту весь модуль ОбщегоНазначения (4 500 строк, ~60 000 токенов), запрашиваем только нужные сигнатуры:
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "get_context_slice",
"arguments": {
"symbols": ["ОбщегоНазначения.ЗначениеРеквизитаОбъекта"],
"includeCallers": true,
"callersLimit": 63,
"maxTokens": 12000
}
}
}
Шаг 3. Автоматическая правка и проверка
После правки агент может запросить обратную проверку:
{
"name": "verify_refactor",
"arguments": {
"removed_param": "Контекст",
"symbol": "ОбщегоНазначения.ЗначениеРеквизитаОбъекта"
}
}
Графон вернёт список оставшихся вызовов с тремя аргументами — ни один не будет забыт.
Сравнение: с Графоном и без
| Метрика | Без Графона (ИИ-агент + полный контекст) | С Графоном (MCP-срез) |
|---|---|---|
| Токенов на анализ ERP | ~480 000 | ~11 500 |
| Время первичного анализа | 12–18 минут | 40–70 секунд |
| Ошибок «метод не найден» / галлюцинаций | 8–15 за сессию | 0–1 |
| Пропущенных вызовов в расширениях | 10–30 % | 0 % |
| Стоимость одной итерации рефакторинга | $3–7 | $0,20–0,45 |
| Учёт подписок на события | вручную | автоматически |
| Динамические вызовы (Выполнить, ВызватьМетодПоИмени) | не находятся | находятся |
Разница в расходе токенов достигает 10× — именно за счёт того, что агент не «читает всю конфигурацию», а получает граф-срез.
Подписки, формы и запросы — невидимая часть айсберга
Самое опасное при изменении общего модуля — косвенные зависимости. Графон строит их через отдельные рёбра графа:
// Подписка на событие: ПередЗаписью документа РеализацияТоваровУслуг
Процедура Расш1_ПередЗаписью(Источник, Отказ, РежимЗаписи, РежимПроведения) Экспорт
// Косвенная зависимость: метод общего модуля вызывается внутри обработчика
Остатки = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(
Источник.Ссылка, "Организация", Неопределено);
Если Остатки = Неопределено Тогда
Отказ = Истина;
КонецЕсли;
КонецПроцедуры
MCP-запрос find_subscriptions с параметром usesSymbol покажет все подписки, чьи обработчики (прямо или через цепочку из N вызовов) зависят от изменяемого метода. Это то, что grep принципиально не умеет.
Аналогично для запросов:
{
"name": "find_query_usages",
"arguments": {
"module": "ОбщегоНазначения",
"symbol": "ЗначениеРеквизитаОбъекта"
}
}
Обратный анализ: «кто вызывает» и «кого вызывает»
Граф двунаправленный. Помимо analyze_impact (direction: callers), полезен get_callees — что сам метод тянет за собой. Это критично, когда нужно понять, можно ли вынести логику без разрыва цепочки:
Функция ЗначениеРеквизитаОбъекта(Ссылка, ИмяРеквизита) Экспорт
// дерево вызовов, которое вернёт Графон:
// → ОбщегоНазначенияКлиентСервер.ЗначениеРеквизитаОбъекта
// → Метаданные.НайтиПоПолномуИмени (платформенный)
// → ОбщегоНазначенияПовтИсп.КэшРеквизитов
// → ПовторноеИспользование.КэшРеквизитов
КонецФункции
Интеграция с Claude Code и Cursor
Графон подключается как MCP-сервер одной строкой в конфиг агента:
{
"mcpServers": {
"grafon": {
"command": "grafon",
"args": ["mcp", "--config", "./grafon.config.json"],
"env": {
"GRAFON_CONF_PATH": "/data/erp_3_1"
}
}
}
}
После этого в промпте достаточно написать:
«Проанализируй влияние удаления параметраКонтекстизОбщегоНазначения.ЗначениеРеквизитаОбъекта, найди все вызовы и предложи патч».
Агент сам вызовет analyze_impact, get_context_slice и verify_refactor, не загружая конфигурацию целиком.
Чек-лист перед изменением общего модуля
- Получить список прямых вызовов (
analyze_impact, depth=1). - Расширить глубину до 3 — найти транзитивные зависимости.
- Проверить расширения (
includeExtensions: true). - Проверить подписки на события (
includeSubscriptions: true). - Проверить динамические вызовы (
includeDynamicCalls: true). - Сопоставить изменения в
ОбщийМодульПовтИспи кэшах. - Выполнить
verify_refactorпосле правки.
Итоги
Анализ влияния изменений в общем модуле 1С через граф связей превращает самую рискованную операцию в предсказуемую. Графон строит этот граф локально, а через MCP отдаёт ИИ-агенту ровно тот срез, который нужен: сигнатуры, вызовы, подписки, запросы. Результат — до 10× меньше токенов, скорость анализа в десятки раз выше и отсутствие пропущенных вызовов, из-за которых обычно и падают релизы ERP и УТ.