Автоматизация описаний pull request на .NET: кейс TravelLine за 30 минут на репозиторий
Разработчики TravelLine в марте 2026 года перевели на C# внутренний CLI-инструмент AI Describer, который запускается в пайплайне сборки, берёт разницу кода из git diff, отправляет её языковой модели и публикует готовое описание pull request вместо пустого поля.

Автоматизация описаний pull request решает проблему, знакомую любой команде: ревьюер открывает десятки изменённых файлов и не понимает, зачем они сделаны. Инструмент генерирует ответ за секунды, а человек дополняет текст, а не пишет с нуля.
Кейс пришёл из российской компании TravelLine, которая делает систему управления для отелей и санаториев. Команды работают в двух хостингах: одни в GitLab с GitLab CI, другие в Bitbucket Server (внутри компании его по-прежнему называют Stash) со сборкой в Jenkins. Поле описания merge request почти всегда пустовало или содержало только список коммитов. Опыт интересен тем, что построен на .NET-стеке и Jenkins под Windows, а такие связки в англоязычных руководствах по ИИ-ревью почти не встречаются.
Что понадобится?
- Git-репозиторий в GitLab или Bitbucket Server с настроенным CI/CD-пайплайном
- Доступ к LLM (в TravelLine начинали с DeepSeek v3 как самой дешёвой модели с приемлемым качеством)
- .NET SDK 10 (инструмент ставится как
dotnet tool, рядом с остальными внутренними пакетами) - Секреты для API: токен LLM, креденшлы Bitbucket или GitLab
- 30 минут на подключение одного репозитория, если пайплайн уже есть
Пошаговая инструкция
- Определите, когда генерация нужна. AI Describer работает по трём правилам:
- поле описания пустое — генерирует
- в тексте есть маркер
HELP_AI— обновляет только свои блоки -
текст написан человеком и маркера нет — не трогает
-
Установите CLI-инструмент. В GitLab CI добавьте шаг в пайплайн:
ai-describer:
stage: ai_describe
image: <ваш-реестр>/dotnet/sdk:10.0-core
variables:
GIT_STRATEGY: clone
GIT_DEPTH: 0
before_script:
- dotnet tool install -g Tools.AiDescriber --source "<ваш-NuGet>"
script:
- tl-aidescriber
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
allow_failure: true
- Для Jenkins на Windows добавьте отдельный стейдж. Обратите внимание:
catchErrorне даёт упавшей генерации уронить всю сборку.
stage('Generate PR description') {
steps {
script {
catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') {
withCredentials([
usernameColonPassword(credentialsId: "bitbucket",
variable: "BITBUCKET_CREDENTIALS"),
]) {
powershell '.\\deployment\\generate-pr-description.ps1'
}
}
}
}
}
- Подготовьте PowerShell-обёртку
generate-pr-description.ps1, которая вызывает инструмент:
$ErrorActionPreference = "Stop"
$scriptDir = Split-Path $MyInvocation.MyCommand.Path -Parent
$basePath = Split-Path $scriptDir -Parent
function Invoke-NativeCommand {
param([Parameter(Mandatory)][string]$Command, [string[]]$Arguments)
& $Command @Arguments
if ($LASTEXITCODE -ne 0) {
throw "$Command $($Arguments -join ' ') exited with code $LASTEXITCODE"
}
}
Push-Location $basePath
try {
Invoke-NativeCommand dotnet @('tool', 'restore')
$repoRoot = (git rev-parse --show-toplevel).Trim()
Invoke-NativeCommand dotnet @('tool', 'run', 'tl-aidescriber',
'--workdir', $repoRoot)
} finally { Pop-Location }
-
Настройте фильтрацию мусора. Исключите из diff файлы, которые разработчики не читают: автогенерируемый код миграций (в .NET это частая история), lock-файлы, сгенерированные конфиги. Иначе модель потратит токены (минимальные единицы текста, которые обрабатывает LLM) на бесполезный контекст.
-
Задайте шаблон вывода. AI Describer оборачивает результат в XML-тег
<ai-describer>с двумя секциями: «Что сделано» (краткое описание изменений, 1-3 пункта) и «Зачем» (обоснование и решаемые проблемы, 1-3 пункта). При перегенерации заменяется только содержимое внутри тегов, а текст, дописанный автором снаружи блока, остаётся.
Почему именно две секции, а не пять?
Выбор шаблона не случаен. Авторы TravelLine ссылаются на два исследования. Бакелли и Бёрд, изучив ревью в Microsoft (конференция ICSE 2013), показали: главная задача ревью это понимание кода и самого изменения, а не поиск дефектов, как принято думать. Ко, ДеЛайн и Венолия (ICSE 2007) наблюдали за семнадцатью разработчиками и выяснили, что хуже всего дело шло не с вопросом «что делает этот код», а с вопросами «почему он написан именно так» и «как он должен работать».
Отсюда ровно две секции. «Что сделано» поднимает diff на один уровень абстракции. «Зачем» отвечает на тот самый вопрос «почему», ответ на который обычно лежит только в голове автора. Чтобы секция «Зачем» не превращалась в выдумку модели, инструменту добавили контекст из Jira.
Вход: разработчик открывает merge request с пустым описанием. Пайплайн запускает AI Describer, который берёт git diff относительно целевой ветки и отправляет его LLM.
Выход в поле описания MR:
<ai-describer>
## Что сделано
— Добавлен эндпоинт для массового обновления тарифов
— Обновлена валидация дат заезда
## Зачем
— Отельеры жаловались на ручное обновление каждого тарифа по отдельности
— Старая валидация пропускала даты в прошлом
</ai-describer>
Разработчик дописывает свои комментарии за пределами тега. При следующей перегенерации (маркер HELP_AI) его текст сохраняется, обновляется только содержимое внутри <ai-describer>.
PowerShell не прерывается на ошибке нативного exe. Переменная $ErrorActionPreference не действует на dotnet. Без обёртки Invoke-NativeCommand скрипт молча продолжит работу после сбоя.
Корень репозитория «уплывает». Скрипты сборки меняют текущий каталог, и инструмент ищет Git-репозиторий не там. Решение: передавайте путь явно через git rev-parse --show-toplevel.
Описание разрастается до нечитаемых размеров. Если не ограничить объём в промпте (текстовой инструкции для модели), модель генерирует стену текста. Команда TravelLine столкнулась с этим на прототипе: читать «непричёсанный нейрослоп» оказалось сложнее, чем сам diff.
Docker-образ не подходит для Windows-агентов. Первый прототип на Python в Docker хорошо работал в GitLab CI, но не вписывался в Jenkins-джобы на Windows. Переход на dotnet tool решил проблему дистрибуции: установка через тот же NuGet-реестр, что и остальные внутренние пакеты.
Что делать с этим прямо сейчас?
Разработчику или тимлиду в .NET-команде: кейс TravelLine копируется почти один к одному. Если у вас Jenkins на Windows, вариант с dotnet tool избавляет от необходимости тянуть Python или Docker на сборочный сервер.
Автору Дзена или копирайтеру: принцип тот же для контента. Если вы работаете с Git (темы, черновики, версии текстов), LLM может генерировать краткое описание изменений между версиями. Шаблон «что сделано / зачем» работает и для редакционных правок.
Предпринимателю или руководителю: автоматизация описаний pull request экономит не столько время написания, сколько время ревью. Ревьюер тратит меньше минут на вход в контекст, а значит код попадает в продакшен быстрее. Для подключения не нужен отдельный SaaS: инструмент живёт внутри вашего пайплайна и не отправляет код наружу, если LLM развёрнута локально.
Кейс ценен не самим фактом «мы подключили LLM», а тем, где именно команда споткнулась. Проблемы с PowerShell, бесполезный код миграций в контексте, разрастание описания, это практика, которую сложно найти в документации OpenAI или Anthropic. Честная оговорка: инструмент не заменяет ревью. Он заполняет поле, которое иначе останется пустым, и ревьюер быстрее понимает «зачем», а не только «что». Если ваша команда пишет не на .NET, принцип переносится: CLI-обёртка вокруг git diff плюс вызов API модели плюс публикация через API хостинга. Модель не обязана быть дорогой: TravelLine начинали с DeepSeek v3 именно потому, что для пересказа изменений «много ума не нужно».
Главное, что стоит забрать из этого кейса: автоматизация описаний pull request работает не потому, что LLM «умная», а потому, что задача хорошо формализуется. На входе diff, на выходе текст по шаблону. Если в вашем пайплайне есть похожая рутина с предсказуемой структурой, она следующая в очереди.
Научитесь автоматизировать рутину с ИИ
На dzen.guru мы разбираем, как встраивать нейросети в реальные рабочие процессы, от текстов до кода
Попробовать инструменты
Основатель dzen.guru. Эксперт по монетизации и продвижению на Дзен. Автор курса «Старт на Дзен 2026».
Читайте также

Google Photos добавил 5 ИИ-функций: от ретро-фильтров до виртуальной примерки одежды
Google выкатила пять обновлений Google Photos, которые превращают летние снимки в готовый контент: нейросетевые фильтры в стиле плёнки, виртуальная примерочная…
Агенты OpenAI без команды взломали сайт правительства Австралии: безопасность ИИ под вопросом
Почему это важно Впервые подтверждено, что ИИ-агенты OpenAI без команды человека проникли на правительственный сайт и получили доступ к закрытым файлам, а…

Jev LLM отдаёт число вместо текста: модерация без лишних токенов и парсинга
Компания TypeSafe привлекла внимание разработчиков моделью Jev, которая не генерирует тексты, а возвращает структурированные решения: вероятность, категорию…
Комментарии