Как исполняется граф

Движок событийный. Он не выстраивает ноды в очередь заранее и не читает холст слева направо. Сам он запускает ровно одну ноду — entry (внутри тела composite — его входной порт). Всё остальное запускает входящее ребро.

Ребро — это триггер

Разные провода делают разное:

  • exec — только порядок: запускает следующую ноду, не передавая значение;
  • execute_data — запускает и передаёт значение одним ребром, самый частый случай;
  • data — только значение: цель его прочитает, но ребро её не запустит;
  • link_llm / link_memory / link_extension — ссылка на конфигурацию (модель, память, инструмент), к порядку исполнения отношения не имеет.

Отсюда главное следствие: нода, до которой не доходит ни одно exec- или execute_data-ребро, не выполнится — сколько бы шаблонов на неё ни ссылалось. Запись {{ nodes.<id>.output }} читает значение, но не запускает ноду.

Слияние веток: по умолчанию ждём всех

Нода с несколькими входящими зависимостями запускается, когда сработали все — это обычный AND-join. Так же ведут себя параллельные ветви: независимые цепочки исполняются одновременно, а количество одновременно работающих нод ограничено тарифом.

Ветвление убивает рёбра

if и switch выбирают ровно одну ногу. Невыбранные рёбра помечаются мёртвыми — по ним уже никогда ничего не придёт. Нода, у которой мертвы все входы, тоже становится мёртвой и передаёт это дальше: так пропускается целая ветка. Почему конкретная нода не выполнялась, видно в её карточке запуска — см. статусы и skip_reason.

OR-join: один вход сработал, остальные мертвы

Когда обе ветви if сходятся в одну ноду, ждать «всех» бессмысленно: второй вход не придёт никогда. Такая нода запускается, как только сработал хотя бы один вход, а все оставшиеся мертвы.

Если / Иначе
Ответ LLM
Шаблон
Выход
  • Исполнение + данные
Ветви исключают друг друга: сработает одна, exit запустится по OR-join.

Нода выполняется один раз и получает значение той ветви, которая сработала; значений остальных веток у неё нет. Валидатор говорит об этом предупреждением wire.andjoin_partial_payload — это подсказка, а не ошибка: обычно именно такого поведения и ждут.

Барьер: для веток, которые идут одновременно

Если ветви друг друга не исключают, а действительно работают параллельно, вести их в один вход нельзя: они гонятся за него, и значение выигрывает та, что пришла последней. Валидатор отбивает это ошибкой wire.fanin_needs_barrier.

Правильная форма — wait_all: каждая ветвь идёт в его вход barrier_in, дальше граф продолжается с его выхода.

Ответ LLM
HTTP-запрос
Дождаться всех
Выход
  • Исполнение + данные
Параллельные ветви сводятся барьером, а не общим входным портом.

Барьер ждёт, пока каждый вход разрешится: либо сработает, либо окажется мёртвым. Поэтому пропущенная ветка switch его не блокирует. Как объединить значения, решает поле merge_mode (first, last, merge).

Чистая data-нога

Ребро data передаёт значение, но ноду не запускает. Из этого правила есть одно исключение: если у цели нет никакой другой зависимости планировщика, а источник сам управляем, движок поднимает такое ребро в планировщик — и цель запускается вслед за источником. Именно поэтому работает самая естественная форма раздельных портов: code_javascript[data] → exit.

Если же поднимать нечего и exit не достижим ни по одному exec-пути, запуск закончился бы пустым результатом. Валидатор отбивает такой граф ошибкой exec.exit_no_exec_trigger ещё до старта.

Ошибка в одной ветви

Упавшая нода не отменяет остальные ветви — они доигрывают до конца. Что будет с её собственным продолжением, решает наличие ветки on_error; итоговый статус запуска подводится после того, как все ветви остановились.