Системные узлы
Справочник по всем встроенным системным узлам — управление потоком, преобразование данных и утилиты — с описанием входов, выходов и рабочим примером для каждого.
Системные узлы
Системные узлы — это встроенные строительные блоки, которые Triggo предоставляет рядом с действиями коннекторов; те самые элементы, что формируют данные, маршрутизируют поток управления, вызывают ИИ-модели или приостанавливают выполнение без обращения к стороннему API. Тянитесь за системным узлом, когда вам нужно не «вызвать сервис X», а «решить, в какую ветвь пойти», «сделать это один раз на каждый заказ», «свернуть список в сумму» или «подождать до завтрашнего утра». Действия коннекторов отвечают за глаголы конкретных интеграций; системные узлы — за грамматику, которая их соединяет.
На этой странице описан каждый системный узел, доступный сейчас в подборщике узлов. Три типа из ранних черновиков продукта — обобщённый системный узел HTTP, узел Transform и узел Delay — не являются системными узлами в Triggo: HTTP живёт в отдельном коннекторе http-request, Delay покрывается узлом Ожидание ниже, а устаревший тип transform теперь отклоняется при сохранении (используйте Код или шаблоны сопоставления полей вместо него).
Поле Name
У инспектора каждого узла есть поле Name, которое переопределяет заголовок по умолчанию в карточке на холсте. Поле доступно на всех системных узлах, а также на действиях, триггерах, узлах Код и ИИ-модель, и оно чисто косметическое — меняет то, что показывает заголовок на холсте, но не то, как узел адресуется в сопоставлениях полей (там по-прежнему используется id узла). Применяйте его, чтобы превратить Условие в Только оплаченные? или Установить значения в Собрать строку Sheets, когда холст становится плотным.
Где их найти — подборщик узлов
В подборщике узлов системные узлы живут в разделе Логика, разбитые на три подкатегории, зеркально отражающие разделы этой страницы:
- Поток — Условие, Переключатель, Цикл, Объединение, Ожидание, Остановить с ошибкой. Они меняют, какие узлы выполняются, в каком порядке и приостанавливается ли запуск.
- Данные — Установить значения, Фильтр, Агрегация, Разбить массив. Они преобразуют полезную нагрузку без обращения к внешним сервисам.
- Утилиты — Код, ИИ-модель, RAG Поиск, Пропуск, Ответ на вебхук. Смешанный набор универсальных инструментов, которые не вписываются в Поток или Данные.
Все 15 типов объявлены в LOGIC_TYPES в и сгруппированы для отображения через LOGIC_CATEGORIES в.
Поток
Узлы потока решают, какие узлы выполняются и когда. Они ветвят DAG, обходят элементы списков, снова соединяют параллельные ветви, приостанавливают запуск или останавливают его с пользовательской ошибкой.
Условие
Двустороннее ветвление: вычисляет группу условий по данным выше по потоку и направляет выполнение по ветви true или ветви false.
Входы (config):
combinator—"AND" | "OR", обязательный. Как комбинироватьconditions.conditions[]— обязательный, минимум один. Каждая запись —{ id, field, operator, value? }.field— шаблонный путь к данным выше по потоку (например,trigger.payment.status);operator— один из 23 поддерживаемых операторов, покрывающих сравнения по тексту, числам, булевым значениям, существованию, спискам и датам.caseSensitive— опциональный булев флаг для текстовых операторов.label— опциональный отображаемый заголовок.
Модель ветвления: у узла Условие есть два source handle — true и false. Исходящие связи выставляют sourceHandle в одно из этих значений; движок помечает каждую связь непринятой ветви как пропущенную, а каждый нижестоящий узел этой ветви записывает в журнал как step_skipped.
Выход: { branch: "true" | "false", matched: <детали по каждому условию> }.
Пример: направить оплаченные заказы в ветвь исполнения, а всё остальное — в ветвь «требует проверки».
Триггер: новый заказ
→ Условие (field: "trigger.status", operator: TEXT_EXACTLY_MATCHES, value: "paid")
├── true → Google Sheets · Append row (журнал исполнения)
└── false → Slack · Post message (канал needs-review)Полный каталог операторов — в: полный набор — 8 текстовых, 6 числовых, 2 булевых, 2 на существование, 4 на списки и 1 оператор даты.
Переключатель
N-сторонний маршрутизатор: вычисляет ветви по порядку и берёт первую совпавшую. Используйте, когда true/false Условия недостаточно — например, чтобы маршрутизировать по стране, тарифу или имени события.
Входы (config):
mode—"value" | "expression", обязательный.- Режим
"value": разрешаетmatchFieldпо данным выше по потоку и сравнивает сvalueкаждой ветви через строгое равенство строк. - Режим
"expression": разрешает шаблонexpressionкаждой ветви и берёт первый истинный результат. matchField— опциональный; обязательный в режимеvalue.branches[]— обязательный, минимум один. Каждый —{ id, name, value?, expression? }.fallback— обязательный булев. Еслиtrue, несоответствующий запуск берёт handlefallback; еслиfalse, ничего не берётся и выход помечается"no_match".label— опциональный.
Модель ветвления: по одному source handle на ветвь (branch_<id>) плюс опциональный handle fallback. Невыбранные ветви отсекаются так же, как у Условия — их связи помечаются как пропущенные, а нижестоящие узлы журналируются как step_skipped.
Выход: { taken: "branch_<id>" | "fallback" | "no_match" }.
Пример: маршрутизация по тарифу подписки.
Триггер: новая регистрация
→ Переключатель (mode: value, matchField: "trigger.plan")
├── branch "pro" → Email · Send onboarding template A
├── branch "business" → Email · Send onboarding template B
└── fallback → Email · Send onboarding template FreeЦикл
Итерирует bodyExecutor по массиву выше по потоку, запуская тело цикла как подграф один раз на каждый элемент. По умолчанию итерации последовательны.
Входы (config):
items— обязательная шаблонная строка, разрешающаяся в массив, например{{trigger.orders}}.itemVariable— опциональный, по умолчанию"item"; идентификатор, по которому узлы тела ссылаются на значение текущей итерации.batchSize— опциональное целое ≥ 1. Элементы обрабатываются порциями такого размера; элементы внутри порции отправляются вместе, но исполнитель считает итерации последовательными — не полагайтесь на параллельное выполнение. По умолчанию1(полностью последовательно).label— опциональный.
Тело: у узла есть прикреплённый подграф тела, нарисованный на холсте. Движок запускает этот подграф один раз на элемент; выход тела собирается в iterations. Вложенные циклы ограничены MAX_LOOP_NESTING_DEPTH. При первой же ошибке в итерации цикл останавливается (fail-fast), и узел падает.
Выход: { iterations: [{ index, item, output }], totalItems: number }.
Пример: добавить по одной строке в Google Sheets на каждый заказ в пачке вебхука.
Триггер: Inbound webhook (orders.batch)
→ Цикл (items: "{{trigger.orders}}", batchSize: 5)
body: Google Sheets · Append row ({{item.id}}, {{item.total}})
→ Slack · Post "{{loop.totalItems}} orders logged"Объединение
Join с ожиданием всех: держит, пока каждая входящая связь не завершится, упадёт или не будет пропущена, затем отдаёт один объект, ключи которого — targetHandle каждой связи. Используйте, чтобы снова свести параллельные ветви после Переключателя или параллельного fan-out.
Входы (config):
mode— зафиксировано как"wait_for_all".inputs— обязательное целое от 2 до 8. Заявленная степень fan-in (холст использует это, чтобы отрисовать нужное число target handles).label— опциональный.
Выход: { [targetHandle]: <выход выше по потоку или null> }. Связь, чья ветвь была отсечена (пропущена), отдаёт null для своего handle вместо того, чтобы блокировать объединение.
Пример: после Переключателя с ветвями pro и business, которые по-разному обогащают данные, объединить результаты перед общим шагом «отправить приветственное письмо».
Переключатель
├── branch "pro" → HubSpot · Enrich contact ─┐
└── branch "business" → HubSpot · Enrich account ─┤→ Объединение (inputs: 2) → Gmail · Send welcomeОжидание
Приостанавливает запуск на заданную длительность или до конкретной временной метки. Внутри исполнитель ставит задачу отложенного возобновления через WaitService, пишет в журнал событие step_waiting, переводит статус запуска в "waiting" и выходит из цикла исполнителя. Отдельный воркер забирает задачу, когда подходит время, и возобновляет DAG с преемников узла Ожидание.
Входы (config):
mode—"duration" | "until_time", обязательный.- Режим
"duration":amount(положительное число) иunit("seconds" | "minutes" | "hours" | "days"). Ограничено 30 днями. - Режим
"until_time":until, ISO-8601 строка даты-времени. label— опциональный.
Выход: { resumeAt: <ISO-8601> } (пишется в журнал; нижестоящие узлы обычно не читают выход Ожидания).
Пример: отправить follow-up через 24 часа после регистрации.
Триггер: новая регистрация
→ Ожидание (duration: 24 hours)
→ Gmail · Send follow-up emailОстановить с ошибкой
Останавливает текущую ветвь с пользовательской ошибкой. Используйте, чтобы явно завершить запуск неуспешно, когда данные выше по потоку указывают на недопустимый или неподдерживаемый случай (например, полезная нагрузка вебхука неожиданной формы).
Входы (config):
errorMessage— обязательная шаблонная строка. Разрешается по выходам выше по потоку перед тем, как быть брошенной.errorCode— опциональный, по умолчанию"WORKFLOW_STOPPED". Записывается в событие падения в журнале для удобной фильтрации.label— опциональный.
Выход: нет — узел бросает WorkflowStoppedError, движок записывает событие step_failed, и запуск падает так же, как и при сбое действия (включая учёт авто-паузы). Сочетайте с узлом Условие выше по потоку, когда хотите остановиться только в конкретных случаях.
Пример:
Условие (operator: DOES_NOT_EXIST, field: "trigger.customer.email")
└── true → Остановить с ошибкой (errorMessage: "Missing customer email on {{trigger.id}}", errorCode: "MISSING_EMAIL")Данные
Узлы данных переформируют полезную нагрузку, текущую сквозь workflow, без обращения к внешним сервисам. Они не ветвят и не приостанавливают — просто читают значение выше по потоку и отдают новое.
Установить значения
Присваивает одно или несколько именованных значений к полезной нагрузке. Представьте себе мини-маппер: полезно, чтобы именовать промежуточные значения, приводить типы или добавлять вычисленные поля перед тем, как передать данные действию коннектора.
Входы (config):
assignments[]— обязательный, минимум один. Каждое —{ id, key, value, type? }.keyдолжен соответствовать^[a-zA-Z_][a-zA-Z0-9_]*$.value— шаблонная строка, разрешаемая по выходам всех узлов выше по потоку.type— один из"string" | "number" | "boolean" | "json"; разрешённое значение приводится соответственно ("json"парсит строковое значение как JSON, либо пропускает не-строки как есть).- Приведение к булеву строгое: только литерал
trueили строка"true"даютtrue. Любое другое значение — включая1,"1","yes"и"True"— становитсяfalse. includeInputFields— опциональный булев, по умолчаниюfalse. Когдаtrue, выход стартует как поверхностный клон выхода первого узла выше по потоку, затем поверх накладываются назначения.label— опциональный.
Выход: простой объект { [key]: <приведённое значение>,... }, опционально предварительно заполненный из выше по потоку.
Пример: вычислить отображаемое имя и сумму в младших единицах перед добавлением в Sheets.
HTTP enrich customer
→ Установить значения
assignments:
- key: "display_name", value: "{{http.firstName}} {{http.lastName}}", type: string
- key: "total_cents", value: "{{trigger.total}}", type: number
→ Google Sheets · Append row (display_name, total_cents)Фильтр
Фильтрует массив выше по потоку, вычисляя набор условий для каждого элемента.
Входы (config):
items— обязательный шаблон, разрешающийся в массив, например{{trigger.orders}}.combinator—"AND" | "OR", обязательный.conditions[]— обязательный, минимум один. Та же форма{ id, field, operator, value? }, что и у узла Условие; вычисляется по каждому элементу массива как по собственному контексту.label— опциональный.
Выход: { items: <оставленные>[], kept: number, dropped: number }. Бросает, если items не разрешается в массив.
Пример: оставить только оплаченные заказы для последующей обработки.
Триггер: orders batch
→ Фильтр (items: "{{trigger.orders}}", combinator: AND,
conditions: [{ field: "status", operator: TEXT_EXACTLY_MATCHES, value: "paid" }])
→ Цикл (items: "{{filter.items}}")
body: Google Sheets · Append rowАгрегация
Прогоняет одну или несколько операций агрегации по массиву выше по потоку. Опционально предварительно группирует по ключу.
Входы (config):
items— обязательный шаблон, разрешающийся в массив.operations[]— обязательный, минимум одна. Каждая операция —{ id, key, op, field?, separator? }:op∈count | sum | avg | min | max | concat | collect | first | last.fieldобязателен везде, кромеcount | first | last | collect(там опционален).separatorприменяется кconcat(по умолчанию"").key— имя выходного свойства.groupBy— опциональный. Если задан, выход становится{ groups: { [bucketKey]: { [opKey]: value } } }с ключамиString(item[groupBy] ?? "_null").label— опциональный.
Выход: плоский { [opKey]: value,... }, когда groupBy не задан, либо { groups: {... } }, когда задан. Крайние случаи пустого входа: min/max → null, avg → 0, first/last → null.
Пример: суммарная выручка и число заказов по стране из выхода Фильтра.
Фильтр (paid orders)
→ Агрегация
items: "{{filter.items}}"
groupBy: "country"
operations:
- key: "count", op: count
- key: "revenue", op: sum, field: "total"
→ Slack · Post JSON-formatted summaryРазбить массив
Разворачивает вложенный массив, чтобы нижестоящие узлы могли работать с ним как с основной полезной нагрузкой, опционально сохраняя соседние поля исходного объекта.
Входы (config):
items— обязательный шаблон, разрешающийся в массив, например{{fetch.response.orders}}.includeParent— опциональный булев, по умолчаниюfalse. Когдаtrue, раскладывает выход исходного узла в результат (с опущенным полем исходного массива, чтобы массив не присутствовал под двумя ключами).itemKey— опциональный, по умолчанию"item". Зарезервирован для будущего режима multi-emit по элементу; сейчас Разбить массив отдаёт один объединённый выход.label— опциональный.
Выход: { items, count } (когда includeParent: false) или {...parentFields, items, count }, когда includeParent: true.
Пример: постраничный HTTP-ответ возвращает { page, pageSize, orders: [...] }. Разбить массив позволяет Циклу итерировать orders, сохраняя page доступным дальше по потоку.
HTTP · GET /orders?page=1
→ Разбить массив (items: "{{http.body.orders}}", includeParent: true)
→ Цикл (items: "{{split.items}}")
body: Google Sheets · Append row ({{item.id}}, page={{split.page}})Утилиты
Всё остальное. Узлы общего назначения для встроенного кода, вызовов ИИ-моделей, генерации с расширенным поиском, проходной маршрутизации и синхронных ответов на вебхуки.
Код
Запускает пользовательский JavaScript или TypeScript в песочнице isolated-vm. Типы снимаются нативным stripTypeScriptTypes из Node перед вычислением. Используйте Код, когда изменение формы слишком сложно для узла Установить значения или нужно напрямую вызвать утилиты стандартной библиотеки (JSON, Date, методы массивов).
Входы (config):
code— обязательная строка. Должна экспортировать именованную функцию-вход (по соглашениюfunction run(inputs, utils) {... }; также поддерживаетсяexport default function name(...)).fieldMappings— опциональный объект. Каждый ключ становится свойством аргументаinputs; каждое значение — шаблонное выражение, разрешаемое по выходам всех узлов выше по потоку.
Выход: то, что возвращает функция-вход. Непойманные ошибки журналируются с кодом CODE_EXECUTION_FAILED. Вывод console.log захватывается и логируется на стороне сервера (не включается в выход узла).
Пример: вычислить ключ идемпотентности SHA-256 из трёх полей триггера.
// code
function run(inputs) {
const raw = `${inputs.orderId}:${inputs.customerId}:${inputs.total}`;
return { idempotencyKey: raw, length: raw.length };
}
// fieldMappings:
// orderId: "{{trigger.id}}"
// customerId: "{{trigger.customer.id}}"
// total: "{{trigger.total}}"Подробности — в Справочнике Code Node по семантике и лимитам песочницы.
ИИ-модель
Генерирует текст через провайдера ИИ-модели. Интерполирует шаблон промпта, собранный из данных выше по потоку, и вызывает Anthropic или OpenAI через Vercel AI SDK.
Входы (config): валидируются LlmNodeConfigSchema в.
provider—"anthropic" | "openai", обязательный.model— строка ID модели, обязательный.userPromptTemplate— обязательный. Плейсхолдеры{{path.to.field}}разрешаются по разрешённому входу шага.systemPrompt— опциональный, либо используйтеpresetдля выбора встроенного системного промпта изLLM_PRESETS.temperature,maxTokens— опциональные.
Каждый вызов обёрнут в 60-секундный таймаут и скомбинирован с сигналом прерывания уровня pipeline. Сбои журналируются с кодом LLM_TIMEOUT или LLM_FAILED.
Выход: { text, usage: { inputTokens, outputTokens }, model, provider, durationMs }.
Пример: кратко изложить тикет поддержки в одном предложении.
Zendesk trigger
→ ИИ-модель (provider: anthropic, model: claude-sonnet,
userPromptTemplate: "Summarize in one sentence: {{trigger.description}}")
→ Slack · Post "Ticket {{trigger.id}}: {{llm.text}}"RAG Поиск
Генерация с расширенным поиском: берёт запрос из данных выше по потоку, строит эмбеддинг, опрашивает сконфигурированное векторное хранилище по HTTP и синтезирует ответ с помощью ИИ-модели, используя найденные документы как контекст.
Входы (config): валидируются ragRetrieveConfigSchema в — единый источник истины, который используют и исполнитель, и инспектор канваса.
query— обязательный шаблонный стринг, например"{{trigger.question}}".vectorStoreUrl— обязательныйhttp(s)URL.bodyTemplate— обязательный шаблон тела запроса, отправляемого в векторное хранилище; поддерживает{{embedding}}и{{topK}}.responsePath— опциональный, по умолчанию"result". Где искать список документов в ответе.documentContentField— опциональный, по умолчанию"payload.text". Поле на каждом найденном документе, где лежит его текст.headers— опциональныйRecord<string, string>.topK— опциональное целое 1–100, по умолчанию5.embeddingConnectionId,embeddingModel— провайдер эмбеддингов; модель по умолчанию"text-embedding-3-small".llmSystemPrompt,llmModel,maxTokens— конфиг синтезирующей ИИ-модели;maxTokensпо умолчанию1024.continueOnFailure— опциональный булев, по умолчаниюfalse. Когдаtrue, ошибки порождают событиеstep_failed_continuedс частичным выходом вместо остановки.
Выход: { query, retrievedDocuments, documentCount, llmResponse, model, embeddingModel, durationMs }.
Пример: ответить на входящий вопрос поддержки из векторного хранилища продуктовых документов.
Inbound webhook (question)
→ RAG Поиск
query: "{{trigger.question}}"
vectorStoreUrl: https://vector.example/search
topK: 5
→ Ответ на вебхук (body: "{{rag.llmResponse}}")Пропуск
Проходной узел. Копирует выход первого узла выше по потоку в свой собственный выход и помечает себя завершённым.
Входы (config):
label— опциональный.
Выход: выход первого узла выше по потоку, либо null, если входящей связи нет или тот выход отсутствует.
Используйте для якорей компоновки на холсте, узлов-заглушек во время разработки или как точку объединения, когда нужен именованный шаг в журнале без побочных эффектов.
Ответ на вебхук
Пишет HTTP-ответ для запуска pipeline, инициированного вебхуком в режиме respond-node. Запуск создаётся входящим HTTP-запросом; этот узел публикует реальный ответ обратно в ожидающий API-процесс (через Redis pub/sub), так что клиент получает настоящий синхронный ответ вместо обобщённого 202 Accepted.
Входы (config):
mode—"json" | "text" | "no_body", обязательный.statusCode— целое 100–599, по умолчанию200.headers— опциональныйRecord<string, string>.body— опциональная шаблонная строка. В режиме"json"должна разрешаться в валидный JSON; в режиме"text"отправляется как есть; в режиме"no_body"игнорируется.label— опциональный.
Предусловия: триггерный вебхук должен быть настроен в режиме respond-node (и httpRequestId, и synchronousHandlerId присутствуют на запуске). Если их нет, обработчик бросает respond_to_webhook: no pending HTTP response.
Выход: { responded: true, statusCode }.
Пример: эндпоинт синхронной валидации.
Inbound webhook (respond-node mode)
→ Условие (field: "trigger.body.email", operator: EXISTS)
├── true → Ответ на вебхук (mode: json, statusCode: 200,
│ body: "{\"ok\":true,\"id\":\"{{trigger.body.email}}\"}")
└── false → Ответ на вебхук (mode: json, statusCode: 400,
body: "{\"ok\":false,\"error\":\"email required\"}")Смотрите также
- Сопоставление полей — как разрешаются шаблоны
{{node.field}}и как распространяетсяundefined, когда данных выше по потоку нет. - Обработка ошибок — повторы,
continueOnFailure, предохранитель, авто-пауза и как узел Остановить с ошибкой с ними взаимодействует. - Передача данных между шагами — как выходы узлов становятся входами для нижестоящих узлов.
- Справочник Code Node — более глубокий справочник по песочнице Code.