Конвейеры и автоматизация
Конвейеры — это детерминированная половина Control Center. Агент решает, что сказать; конвейер решает, что произойдёт дальше, — и каждый раз одинаково. Конвейер — это DAG из шагов, которые умеют запускать агентов, выполнять скрипты, ветвиться по условию, проходить по всем элементам коллекции и передавать данные между шагами — с сохранением состояния, возможностью возобновления и учётом стоимости.
Они существуют по трём причинам: чтобы связывать повторяющийся многошаговый процесс в цепочку, чтобы координировать несколько агентов последовательно или параллельно и чтобы реагировать на событие, которое произошло без участия человека.
Шаблоны и запуски
Заголовок раздела «Шаблоны и запуски»Шаблон (PipelineDefinition) — это декларативный DAG: шаги, объявленные входные
данные и конфигурация. Он хранится в собственной базе данных рабочего пространства.
Запуск (PipelineRun) — это одно выполнение этого шаблона, которое несёт
изменяемый мешок состояния, суммарные затраты и токены, а также статус каждого шага.
У одного шаблона много запусков. Запуски переживают перезапуск: движок заново загружает выполняющиеся запуски и продолжает их с того места, где они остановились.
Две страховки намеренно проваливают весь запуск, а не возобновляют шаг, потому что возобновление было бы хуже остановки:
- Шаг, который находится в состоянии
suspendedдольшеsuspendedStepTimeout(по умолчанию 24 часа), ждёт тикеты, которые никогда не достигнут терминального состояния, поэтому его проваливают, чтобы освободить запуск. Исключение —extras['awaitApproval'] == true, то есть шлюз частичного подтвержденияorchestration.awaitApproval. Дляhuman.gateисключения нет. - Шаг, объявленный как
extras: {'idempotent': false}и находившийся в процессе выполнения, когда сервер остановился, мог уже применить свой побочный эффект (squash-merge, очистку рабочего дерева). Повторный запуск мог бы применить его дважды, поэтому движок проваливает шаг, а повтор оставляет на ваше усмотрение.
Вид планирует, тело работает
Заголовок раздела «Вид планирует, тело работает»У шага есть две ортогональные части, и именно их разделение позволяет движку оставаться небольшим.
Вид (StepKind) сообщает движку, когда шаг становится готовым к выполнению
относительно вышестоящих шагов, — это единственное, что нужно знать планировщику.
Видов шесть: trigger, listen, join, router, forEach и terminal.
Тело (bodyKey) — это работа, которую шаг выполняет после того, как его
запланировали. Тела — это замыкания, регистрируемые по ключу при старте сервера,
поэтому множество шагов разных видов могут использовать одно тело: например,
несколько шагов listen, все выполняющие conversation.promptAgent.
Поскольку семантика планирования заключена в шести видах, каталог поведений может расти, и при этом никому не придётся трогать планировщик. Полный каталог видов, тел и их конфигурации приведён в разделе Шаги конвейера.
Два следствия устройства реестра стоит знать, прежде чем писать шаблон вручную:
- Шаг, чей
bodyKeyне зарегистрирован, выдаёт предупреждение при загрузке шаблона и затем никогда не выполняется. Во время работы ничего не падает с громкой ошибкой. Шагиtriggerиterminalот этого предупреждения освобождены: триггер всё равно выполняет телоpipeline.trigger, которое ничего не делает, чтобы сработали нижестоящие слушатели; у шагаterminalтела нет, это сторожевой элемент планировщика. - Ключи в карте
extrasузла сопоставляются только точно, а в остальных случаях молча игнорируются. Толькоextras['spaceId']помещает работу агентского шага в существующий разговор.extras['mode'](chat/plan/review) читается на узле, который открывает комнату (messaging.createSpace, по умолчаниюreview), — более поздний агентский шаг не может заново выдать права разговору, к которому присоединился. Любое другое написание любого из этих ключей игнорируется.
Состояние и поток данных
Заголовок раздела «Состояние и поток данных»Шаги не вызывают друг друга. Они читают и пишут в общий мешок состояния запуска: шаг
читает свои входные данные по ключу, выполняется и записывает результат обратно под
своим outputKey. StateReducer сливает конкурентные записи, когда параллельные
ветви нацелены на один и тот же ключ, а TemplateRenderer подставляет заполнители
{{...}} в промпты и скрипты из этого же состояния.
Именно поэтому конвейер можно разобрать постфактум — мешок состояния и есть вся история запуска.
Что инициирует запуск
Заголовок раздела «Что инициирует запуск»Строки PipelineTrigger объявляют, когда шаблон запускается автоматически. Видов
четыре:
| Триггер | Инициирует запуск, когда |
|---|---|
| Доменное событие | Срабатывает подходящее доменное событие — например, PullRequestPublished или TicketAssigned |
| Расписание | Наступает срок выражения cron или интервала |
| Вручную | Вы запускаете его сами; наличие включённого ручного триггера делает шаблон доступным на странице «Запустить конвейер» |
| Webhook | Внешняя система отправляет POST на /webhooks/<token> на HTTP-порт сервера |
Событийные триггеры — закрытый набор: диспетчер знает 16 типов событий, и именно они
доступны для выбора (EventPayloadMapper.knownEventTypes); ничто другое не может
запустить конвейер. У четырнадцати из этих 16 есть сопоставление payload. У двух —
TicketCreated и TicketStatusChanged — его нет, поэтому триггер на любое из них
остаётся включённым и никогда не срабатывает. Полный список — в разделе
Доменные события.
Токен webhook заодно служит секретом HMAC: заголовок запроса
X-Hub-Signature-256: sha256=<hex> проверяется по HMAC-SHA256(body, token),
непроверенные доставки отклоняются и не могут быть воспроизведены, а дубликаты
отбрасываются по dedupe_key. Поскольку строки триггеров уникальны по (рабочее
пространство, тип события, шаблон), у шаблона может быть не более одного
webhook-триггера и по одному триггеру на каждый тип доменного события.
Встроенные шаблоны при создании получают ручные и событийные триггеры включёнными; триггеры по расписанию создаются отключёнными, поэтому повторяющийся обход всегда требует явного включения.
Шаблоны, которые поставляются отключёнными
Заголовок раздела «Шаблоны, которые поставляются отключёнными»В каждое рабочее пространство добавляются тринадцать шаблонов конвейеров, и восемь
из них поставляются отключёнными: external_pr_welcome, cross_review,
ticket_to_pr, pr_triage, pre_merge_gate, release_notes, dep_audit и
pr_digest.
Именно это и удивляет людей. Их триггеры создаются включёнными, но
PipelineEngine.start отказывается запускать отключённый шаблон и возвращает null —
поэтому триггер срабатывает в пустоту, и никакой запуск не появляется.
Переключатель — это шаблон, а не триггер. Включите его в разделе Настройки →
Рабочее пространство → Шаблоны конвейеров, прежде чем ждать от этих восьми хоть
какой-то работы.
Повторное создание шаблонов в рабочем пространстве сохраняет всё, что вы выбрали:
ваша настройка isEnabled и существующие строки триггеров остаются на месте. Есть
ровно одно намеренное исключение: ежедневное расписание pr_merged_cleanup
принудительно включается, даже если раньше было выключено, потому что это сборка
мусора. Без неё накапливались бы строки рабочих деревьев, разделы графа кода и копии
copy-on-write, а это никогда не задумывалось как выбор пользователя.
Ещё одна асимметрия, которую стоит знать: встроенный pr_review намеренно больше не
публикует ничего в GitHub. Публикация проверки — отдельный шаг, требующий
подтверждения пользователя, а конвейер, публикующий напрямую, публиковал бы всё
дважды. А вот cross_review, ticket_to_pr и pr_triage при запуске по-прежнему
отправляют комментарий прямо в GitHub.
Что знает движок
Заголовок раздела «Что знает движок»PipelineEngine создаёт запись о запуске, планирует стартовый узел, соответствующий
типу события запуска, и затем обходит DAG: по мере завершения шагов он вычисляет
нижестоящие триггеры, разрешает ветвления router, дожидается join, применяет политики
повторов и продолжение при сбое, а также сохраняет состояние, стоимость и ошибки на
каждом шаге. Рёбра, ведущие из другого старта, для этого запуска игнорируются, поэтому
срабатывание по расписанию не ждёт условия, доступного только при ручном запуске.
Чего он не знает — так это ничего о тикетах, диспетчеризации, GitHub или обмене
сообщениями. Его единственная точка расширения — PipelineBodyRegistry. Именно
зарегистрированные там тела содержат AgentDispatchPort, MessagingPort и их
родственников, поэтому добавление новой возможности в конвейеры никогда не означает
правку планировщика.
Обработка ошибок
Заголовок раздела «Обработка ошибок»Упавший шаг записывает своё сообщение об ошибке и переходит в failed. Если шаг
объявляет continueOnFail, нижестоящие шаги продолжают работу, а ошибка складывается
в состояние запуска; иначе запуск завершается ошибкой. Шаг с StepRetryPolicy сначала
повторяется — с линейной или экспоненциальной задержкой, — и только потом применяется
любой из этих вариантов.
См. также
Заголовок раздела «См. также»- Доменные события: на что реагирует событийный триггер
- Оркестрация: план, который вы утверждаете и который компилируется в один из таких конвейеров
- Тикеты и делегирование: что создают и завершают шаги конвейера
- Шаги конвейера: полный каталог видов, тел и конфигурации
- Соберите свой первый конвейер
- Создать шаблон конвейера
- Запустить конвейер вручную
- Мониторить запуски конвейеров
- Настроить триггеры конвейера