Архитектура решения

Схема сопоставления и общий процесс
Сначала мы строим таблицу соответствия методов на основе пакетной выгрузки конфигурации в файлы.
Таблица содержит три стороны:
- метод конфигурации до обновления;
- метод расширения конфигурации до обновления;
- метод конфигурации после обновления.
Это позволяет рассматривать каждый метод отдельно и на выходе получать обновлённый метод расширения. Неважно, как реализовано расширение — через события или переопределение методов. Алгоритм обработки един для всех случаев.
Основные сценарии обновления
- Метод не изменён в обновлённой конфигурации: ничего делать не нужно.
- Метод удалён в обновлённой конфигурации: сложный случай для автоматизации. Мы удаляем метод из расширения, затем вручную анализируем изменения и переносим доработки, если они всё ещё актуальны.
- Метод изменён в обновлённой конфигурации: здесь мы применяем автоматизацию — используем Git и GPT.
Методы с аннотациями Перед, После и Вместо с ПродолжитьВызов обычно требуют лишь обновления параметров вызова, а не всего тела. В остальных случаях обновляем код целиком.
Алгоритм обработки изменённых методов
- Предварительная обработка: очищаем тела методов, синхронизируем параметры и вызовы ПродолжитьВызов.
- Трёхстороннее слияние через Git: в большинстве случаев проходит успешно. Ошибки редки и выявляются на этапе проверки конфигурации или тестирования.
- Разрешение конфликтов с помощью GPT: в конфликт попадают только действительно спорные места. Иногда одного метода недостаточно для принятия решения, иногда доработку невозможно перенести технически. Поэтому GPT — ассистент, а не замена программиста. При корректной настройке промпта удалось автоматизировать разрешение большинства конфликтов.
Использование Git для трёхстороннего слияния
Исходно мы рассматривали несколько инструментов для трёхстороннего слияния, но по итогу остановились на Git:
- KDiff3 — слишком «мягкий» алгоритм. Может объединить изменения без конфликта, даже если одна сторона удалила блок, а другая его изменила. Кроме того, не поддерживает полноценный консольный режим.
- GNU Diff3 — не умеет корректно игнорировать пробелы, из-за чего создаёт множество лишних конфликтов.
- Git — оптимален: строг в конфликтных случаях и гибко настраивается.
В Git мы включили трёхсторонний стиль конфликтов — двух сторон иногда недостаточно: git config merge.conflictstyle diff3.
После появления конфликтов заменяем метки на читаемые с помощью регулярных выражений:
"(?<=^<<<<<<< ).*" -> "THEIRS"
"(?<=^\|\|\|\|\|\|\| ).*" -> "BASE"
"(?<=^>>>>>>> ).*" -> "OURS"
Подключение GPT к процессу разрешения конфликтов
Настройка параметров модели
Для автоматического разрешения конфликтов мы экспериментировали с моделями и промптами. Выводы практические, но не универсальные — многое зависит от модели, задачи и промпта.
Модель можно развернуть как локально, так и на внешнем сервисе — важно лишь обеспечить стабильный доступ и достаточную производительность. В нашем случае система интегрирована напрямую, без необходимости обращаться к внешним API.
Параметры моделей
Задача требует точности, не творчества. Наилучшие рабочие значения в наших экспериментах:
- top_p = 0.3 — ограничивает набор вероятных токенов,
- temperature = 0.1 — регулирует случайность выбора.
Значения ниже приводили к зацикливанию и малой гибкости, выше — к лишней вариативности.
Обычные vs Reasoning-модели
Reasoning-модели медленнее и полезны, когда нужен краткий вывод в ответе. Когда у моделей есть возможность расписать решение перед выдачей ответа, обычные модели показали лучший результат.
Цельный запрос vs Цепочка запросов
Разделение задачи на несколько запросов ухудшало качество: модель «теряла» контекст и делала странные допущения. Лучший результат — одно целостное обращение, где модель последовательно анализирует конфликт и выдаёт решение в одном ответе.
Подробный vs Краткий промпт
Избыточная детализация вредна: модель тонет в мелочах. Оптимально — краткий, формализованный промпт с минимальным набором структурированных входных данных. Практические правила для входных данных:
- Не передавать метод целиком — достаточно конфликтного фрагмента и нескольких строк контекста до/после. Большие куски кода повышают риск галлюцинаций и опечаток.
- Явно показывать построчные различия (мы использовали SequenceMatcher в Python). Без явного указания отличий модель чаще упускает изменения или находит несуществующие.
Промпт и структура данных
После серии итераций мы пришли к рабочему шаблону промпта, который показал наилучший результат на тестовой выборке. Он обеспечивает стабильную логику, чёткое следование правилам и предсказуемость ответа.
Ты опытный разработчик 1С. Перед тобой блок кода с git-конфликтами (трёхстороннее слияние). А также список различий, полученных в результате сравнения.
## Стороны конфликта:
BASE - Исходная (базовая) версия конфигурации от 1С до изменений.
THEIRS - Новая версия от вендора (1С) — изменения, которые пришли с обновлением.
OURS - Наши кастомные изменения в конфигурации.
## Основные принципы:
1. **Безопасность прежде всего** - не допускать нерабочий код
2. **Максимальное сохранение изменений** - где возможно объединять
3. **Чёткие приоритеты** - минимум неоднозначностей
4. **Сохранение комментариев** - если комментарий находится в блоке, который остаётся — комментарий тоже должен остаться.
5. **Никаких изменений вне конфликтных маркеров** - все строки, не затронутые конфликтами, должны быть сохранены в исходном виде, включая регистр символов, пунктуацию и пустые строки.
## Правила разрешения конфликтов:
- Удаления - ВСЕГДА удаляем (имеет высший приоритет). Даже если другая сторона изменила логический блок.
- Изменения и Добавления - ВСЕГДА сохраняем, если не мешают удаления.
### Особые случаи (в остальных случаях действуй по общим правилам):
1. **Используются инструкции препроцессора #Удаление и #КонецУдаления или #Вставка и #КонецВставки** - значит OURS - это не исходный код конфигурации, а расширение, надстройка над BASE.
- В этом случае сначала не обращай внимание на OURS, возьми целиком код из THEIRS.
- Если это #Удаление и #КонецУдаления - то в коде THEIRS вставь только инструкции препроцессора, чтобы в них остался аналогичный логический блок, который мы исходно отмечали.
- Если это #Вставка и #КонецВставки - то в коде THEIRS вставь целиком с #Вставка до #КонецВставки, который мы исходно добавляли.
- Если код в блоках #Удаление и #КонецУдаления и #Вставка и #КонецВставки дублирует логику в THIERS, то доработка избыточна - берем THEIRS без инструкций препроцессора.
2. **Конфликт изменений метаданных текущего метода** - имя метода берем из OURS, параметры метода берем из THEIRS, но переносим туда все доработки OURS, если это возможно.
## Алгоритм действий (действуй по шагам для каждого конфликта отдельно):
1. Выпиши подробно, какие логические блоки изменений были в OURS и THEIRS относительно BASE согласно различиям. ВАЖНО! В различиях не указано где удаление и добавление, а где изменение - твоя задача это определить.
- Сравни OURS и BASE. Выпиши информацию по каждому отличающемуся блоку (Изменения, Удаления, Добавления).
- Сравни THEIRS и BASE. Выпиши информацию по каждому отличающемуся блоку (Изменения, Удаления, Добавления).
2. Определи в каком порядке должны быть вставлены отличающиеся логические блоки. Если порядок не играет роли - ориентируйся на строки кода до и после конфликта.
3. Рассуди, какой должен получиться результат после разрешения конфликта.
## Примеры
### Удаление + изменение
BASE: function init() {}
OURS: (удалили)
THEIRS: function init() { update(); } U94; Игнорируем
RESULT: (нет функции)
### Объединение полей
BASE: { x: 1 }
OURS: { x: 1, y: 2 }
THEIRS: { x: 1, z: 3 }
RESULT: { x: 1, y: 2, z: 3 }
### Конфликт изменений
BASE: timeout: 100 + 100
OURS: timeout: 100 + 200
THEIRS: timeout: 500 + 100
RESULT: timeout: 500 + 200
# Ответ:
- Выполни подробный анализ КАЖДОГО конфликта (`<<<<<<< THEIERS`, `||||||| BASE` `=======`, `>>>>>>> OURS`) согласно алгоритму действий.
- ВАЖНО! В самом конце предоставь полный блок кода после разрешения конфликтов в блоке ```bsl (```bsl\n``` - для пустого результата).
# Исходные данные:
## Блок кода:
```bsl
{{ code }}```
{% for conflict in conflicts %}
## КОНФЛИКТ {{ loop.index }}
Различия OURS и BASE:
```text
{{ conflict.diff_ours_base }}```
Различия THEIRS и BASE:
```text
{{ conflict.diff_theirs_base }}```
{% endfor %}
Промпт делится на несколько логических частей. Каждая из которых добавлена для конкретной цели:
1. Контекст задачи
Короткое вступление («Ты опытный разработчик 1С…») задаёт роль и контекст модели. Это помогает ограничить стиль ответа и направить поведение в нужное русло. Без этого модель склонна к «творчеству» или выдаче комментариев вне кода.
2. Описание сторон конфликта (BASE / THEIRS / OURS)
Определяет термины, чтобы модель не путала источники изменений. Это снижает риск логических ошибок при слиянии.
3. Принципы и правила разрешения конфликтов
Набор строгих инструкций, формирующих приоритеты при объединении изменений: сохранение корректности кода, приоритет удалений, сохранение комментариев и неизменённых строк. Эти правила позволяют добиться одинакового поведения модели на разных наборах данных.
4. Особые случаи
Обработка препроцессорных инструкций (#Удаление, #Вставка) и конфликтов в сигнатуре методов. Эти ситуации часто ломают автоматическое слияние, поэтому мы ввели отдельные указания, как действовать.
5. Алгоритм действий
Подробный пошаговый план, по которому модель должна работать с каждым конфликтом:
- выделить различия между версиями,
- определить тип изменений (удаление, добавление, изменение),
- составить итоговый результат. Это помогает избежать типичных «догадок» модели и заставляет её рассуждать последовательно.
6. Примеры
Минимальные примеры обеспечивают лучшее понимание задачи моделью на примерах с разными типами конфликтов. Модель, увидев шаблон, склонна повторять структуру рассуждений, а не просто «угадывать» результат.
7. Формат вывода
Жёсткое требование выдать результат в виде блока bsl гарантирует в большинстве случаев предсказуемый формат в ответе. Это позволяет парсить результат без дополнительной фильтрации.
8. Подстановка данных в шаблон
В шаблоне предусмотрены динамические блоки. Для заполнения шаблона мы использовали шаблонизатор jinja2 в Python, но в 1С можно реализовать аналогичный алгоритм.
• code — блок кода с конфликтами и несколькими строками контекста.
• loop.index — порядковый номер конфликта (удобно при логировании).
• conflict.diff_ours_base — различия между базовой конфигурацией и расширением до обновления.
• conflict.diff_theirs_base — различия между базовой конфигурацией и обновлённой конфигурацией.
Эти данные передаются модели в структурированном виде, чтобы она могла сопоставлять различия построчно. В результате модель анализирует только необходимый контекст, не теряя фокус.
Пример работы
Для демонстрации процесса возьмём конкретный метод конфигурации, который изменился в конфигурации после обновления и был заменен в расширении.
Конфигурация до обновления (BASE)
Процедура ПодразделениеПриИзменении(Элемент)
ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(Список, "Подразделение", Подразделение,,, ЗначениеЗаполнено(Подразделение));
ОбновитьСписокСостоянияНаСервере();
КонецПроцедуры
Конфигурация после обновления (THEIRS)
Процедура ПодразделениеПриИзменении(Элемент)
ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(Список, "Подразделение", Подразделение,,, ЗначениеЗаполнено(Подразделение));
ОбновитьСписокСостоянияНаКлиенте();
КонецПроцедуры
Расширение конфигурации до обновления (OURS)
Процедура Расширение_ПодразделениеПриИзмененииВместо(Элемент)
ОтборыСписковКлиентСервер.ИзменитьЭлементОтбораСписка(Список, "Подразделение", Подразделение, ЗначениеЗаполнено(Подразделение), ВидСравненияКомпоновкиДанных.ВИерархии);
ОбновитьСписокСостоянияНаСервере();
КонецПроцедуры
Работа Git
Git получил на вход все три стороны (BASE, THEIRS, OURS) и выполнил стандартное трёхстороннее слияние.
На этом этапе мы получили файл с конфликтами, который не может быть применён напрямую в конфигурацию, но может послужить входными данными для GPT:
Процедура Расширение_ПодразделениеПриИзмененииВместо(Элемент)
<<<<<<< THEIRS
ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(Список, "Подразделение", Подразделение,,, ЗначениеЗаполнено(Подразделение));
ОбновитьСписокСостоянияНаКлиенте();
||||||| BASE
ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(Список, "Подразделение", Подразделение,,, ЗначениеЗаполнено(Подразделение));
ОбновитьСписокСостоянияНаСервере();
=======
ОтборыСписковКлиентСервер.ИзменитьЭлементОтбораСписка(Список, "Подразделение", Подразделение, ЗначениеЗаполнено(Подразделение), ВидСравненияКомпоновкиДанных.ВИерархии);
ОбновитьСписокСостоянияНаСервере();
>>>>>>> OURS
КонецПроцедуры
Работа GPT
GPT получили на вход промпт основанный на шаблоне:
//Начало шаблона промпта
# Исходные данные:
## Блок кода:
```bsl
Процедура Расширение_ПодразделениеПриИзмененииВместо(Элемент)
<<<<<<< THEIRS
ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(Список, "Подразделение", Подразделение,,, ЗначениеЗаполнено(Подразделение));
ОбновитьСписокСостоянияНаКлиенте();
||||||| BASE
ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(Список, "Подразделение", Подразделение,,, ЗначениеЗаполнено(Подразделение));
ОбновитьСписокСостоянияНаСервере();
=======
ОтборыСписковКлиентСервер.ИзменитьЭлементОтбораСписка(Список, "Подразделение", Подразделение, ЗначениеЗаполнено(Подразделение), ВидСравненияКомпоновкиДанных.ВИерархии);
ОбновитьСписокСостоянияНаСервере();
>>>>>>> OURS
КонецПроцедуры```
## КОНФЛИКТ 1
Различия OURS и BASE:
```text
- ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(Список, "Подразделение", Подразделение,,, ЗначениеЗаполнено(Подразделение));
+ ОтборыСписковКлиентСервер.ИзменитьЭлементОтбораСписка(Список, "Подразделение", Подразделение, ЗначениеЗаполнено(Подразделение), ВидСравненияКомпоновкиДанных.ВИерархии);
...
Различия THEIRS и BASE:
...
- ОбновитьСписокСостоянияНаСервере();
+ ОбновитьСписокСостоянияНаКлиенте();
На основе этого промпта GPT выполнила пошаговый анализ каждого конфликта и предложила итоговый вариант кода без конфликтных маркеров:
Анализирую конфликт по шагам:
1. Анализ изменений:
- OURS vs BASE:
* Изменение: Заменили вызов `ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка` на `ОтборыСписковКлиентСервер.ИзменитьЭлементОтбораСписка` с изменёнными параметрами
* Оставили без изменений вызов `ОбновитьСписокСостоянияНаСервере()`
- THEIRS vs BASE:
* Изменили вызов `ОбновитьСписокСостоянияНаСервере()` на `ОбновитьСписокСостоянияНаКлиенте()`
* Оставили без изменений вызов `ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка`
2. Порядок изменений:
- Оба изменения независимы и могут быть объединены
3. Результат:
- Сохраняем изменённый вызов установки отбора из OURS
- Сохраняем изменённый вызов обновления списка из THEIRS
Итоговый код:
```bsl
Процедура Расширение_ПодразделениеПриИзмененииВместо(Элемент)
ОтборыСписковКлиентСервер.ИзменитьЭлементОтбораСписка(Список, "Подразделение", Подразделение, ЗначениеЗаполнено(Подразделение), ВидСравненияКомпоновкиДанных.ВИерархии);
ОбновитьСписокСостоянияНаКлиенте();
КонецПроцедуры
Результат можно получить регулярным выражением из блока bsl в конце ответа модели.
Проверка результата
Корректность обновления мы проверяем через сравнение результатов Git и GPT. Визуально сверяем различия между слиянием Git и кодом, полученным от GPT. Это позволяет быстро выявить участки, где модель могла допустить ошибки.

Для сложных случаев дополнительно используем двухстороннее попарное сравнение методов, без участия Git. Такая проверка применяется в случаях, когда сравнения с Git недостаточно.
Результаты GPT-обновления 1С на реальных проектах
Для оценки эффективности мы взяли несколько крупных проектов обновления.
Статистика общая:
- Всего методов — 3632.
- Не требовали обновления — 2447.
- Удалены — 130.
- Изменены — 1055.
Статистика изменённых методов:
- Автоматически обновлены Git — 720.
- Переданы в GPT — 335.
- Успешно обновлены — 202 (60%).
- Ошибка — 133 (40%).
Таким образом, использование GPT позволило сократить количество ручных правок с 335 до 133.
Все результаты GPT проходили сверку, однако в простых случаях сравнение с конфликтами Git занимало считанные минуты.
Выводы и рекомендации
- Git уверенно справляется с большинством изменений, но не избавляет от конфликтов.
- GPT существенно снижает объём ручной работы при их разрешении — более половины конфликтов удалось обработать автоматически.
- Полной автономности ждать не стоит: модель не заменяет разработчика, но становится надёжным инструментом, особенно при хорошо выстроенном промпте и чётких правилах обработки.
Вступайте в нашу телеграмм-группу Инфостарт