Workflows
Workflow — это визуальная карта того, что ваш агент должен делать шаг за шагом — кто говорит первым, что спрашивать, когда ветвиться, когда переводить звонок и когда передавать его другому агенту. Вы строите её на холсте, размещая ноды и соединяя их.
Используйте workflow, когда одного промпта недостаточно — когда у звонка есть чёткие этапы («сначала квалифицировать, потом собрать детали, потом записать или перевести»), когда нужна детерминированная маршрутизация по ответам звонящего (а не «AI обычно справится»), или когда один агент должен передать часть работы специалисту.
Prompt — это одна длинная инструкция. Агент читает её один раз и использует на протяжении всего звонка. Workflow — это много маленьких инструкций, по одной на ноду, с явными переходами между ними. Граф управляет звонком — агент каждой ноды владеет только своим куском.
Если ваш агент уже хорошо работает с одним промптом, workflow вам не нужен. Переходите на workflows, когда начинаете накапливать в промпте правила «если X, то Y», и они перестают надёжно работать.
Редактор
Редактор workflow живёт в отдельной вкладке на странице агента. Вы видите холст с нодой Start и нодой End по умолчанию. Вы добавляете ноды из тулбара, перетаскиваете их на холст и соединяете рёбрами.
Правая панель меняется в зависимости от того, что вы выбрали:
- Клик по ноде — открывается её панель настроек (промпт, поля, переменные для извлечения и т. д.).
- Клик по ребру — открываются настройки его триггера (AI decides / Condition / Always).
- Панель Variables (кнопка в тулбаре) перечисляет каждую переменную, которую workflow объявляет или использует, с переходом в один клик к ноде, которая её устанавливает или читает.
Запустите Test Call в браузере прямо из редактора — светящийся светлячок отслеживает живую позицию, чтобы вы видели, какая именно нода активна в каждый момент.
Undo / Redo (v2.8) — редактор хранит историю до 20 шагов, так что вы можете откатить неверный ход (или повторить его) без пересборки. Удаление ноды сначала запрашивает подтверждение (v2.8), так что вы не потеряете шаг из-за случайного клика.
Вам не обязательно начинать с пустого холста. Точка входа «build as workflow» создаёт workflow для агента и сразу бросает вас в билдер — одно действие создаёт workflow и открывает редактор, готовый к добавлению нод.
Workflows имеют три состояния:
- Draft — редактируемый, ещё не в проде. Используйте Test Call, чтобы его опробовать.
- Active — в проде для реальных звонков. Агент использует этот workflow со следующего звонка.
- Template — переиспользуемый чертёж, никогда не привязанный напрямую к агенту. Можно инстанцировать его на несколько агентов.
Ноды
Каждая нода — это шаг в разговоре. Разные типы нод делают разные вещи — одни говорят, другие слушают, третьи маршрутизируют, четвёртые запускают инструменты.
Agent
Нода Agent — сердце этапа workflow. Когда граф доходит до ноды Agent, этот агент берёт слово и ведёт разговор, пока что-то не запустит переход.
Вы настраиваете личность и задачу ноды Agent прямо на холсте (вкладка Prompt открывается по клику на ноду) — это промпт для этого куска разговора, а не для всего звонка. Также можно прикреплять базы знаний и инструменты по каждой ноде.
Greeting — открывающая фраза. Если это входная нода (первая остановка после Start), это первое, что слышит звонящий.
Когда граф управляет звонком, промпт активной ноды накладывается поверх базового промпта агента (того, что во вкладке Model). Граф владеет личностью и задачей на каждом этапе, а базовые инструкции агента остаются на месте как фундамент.
Subagent
Subagent — это помощник, который работает инлайн — он забирает разговор для сфокусированной задачи, а затем передаёт результат обратно тому, кто его вызвал.
Subagents хороши, когда кусок работы переиспользуем или ощущается отдельным: «проверить личность звонящего», «собрать адрес доставки», «квалифицировать лид методом SPIN». Родительский агент вызывает subagent, тот делает своё дело и сообщает результат, родитель продолжает с того места, где остановился.
Ноды Subagent имеют те же вкладки, что и нода Agent (Prompt, Knowledge, Tools, Actions) — редактор автоматически создаёт за вас скрытого backing-агента. Вы не видите этого скрытого агента в своём основном списке агентов, и он не считается в лимит агентов вашего тарифа.
Return variable — поле, куда сохранить результат subagent (например, identity_verified, lead_score).
Кто говорит первым
По умолчанию subagent начинает свой ход с реплики — он приветствует или задаёт первый вопрос в тот момент, когда берёт слово. Два переключателя меняют это поведение:
- Wait for the caller (don't speak first) — subagent молчит при входе и отвечает только после того, как звонящий что-то скажет. Включайте это, когда subagent — первая остановка в звонке и вы хотите, чтобы начал звонящий (настоящее «Алло?»), а не агент — или посреди звонка, когда предыдущий шаг уже всё сказал и subagent должен просто слушать ответ.
- Silent step (don't speak) — subagent вообще не говорит. Он читает разговор на текущий момент, принимает решение о результате и сразу передаёт контроль обратно. Используйте его как невидимый маршрутизатор: «прочитай, о чём спросил звонящий, и реши, какой это отдел» — ни слова не произносится, только решение, по которому ветвится граф.
Allowed result values
Обычно subagent завершается тем значением, которое счёл подходящим. Если вы собираетесь ветвиться по этому результату рёбрами Condition, зафиксируйте его на закрытом списке допустимых значений:
- Перечислите каждое допустимое значение в Allowed result values (например,
sales,support,billing). - Subagent обязан завершиться ровно одним из них — он не может придумать новое.
- Свяжите каждое значение с ребром Condition (
result is "sales"→ …) для чистой, предсказуемой маршрутизации.
Это надёжный способ заставить subagent решать и маршрутизировать: результат гарантированно совпадёт с одной из ваших ветвей.
Subagent передаёт обратно единственный результат, когда закончил. Подключайте то, что происходит дальше, рёбрами Condition или Always по этому результату — выход subagent не использует рёбра «AI decides». Именно поэтому Allowed result values + рёбра Condition — рекомендованный паттерн для маршрутизирующего subagent.
Message
Нода Message произносит фиксированную фразу — без хода AI, без импровизации. Используйте её для переходов («Одну минуту, перевожу вас…»), обязательных правовых уведомлений или заранее заданных подтверждений, где важны конкретные слова.
Text — что сказать. Поддерживает токены {{variable}} для подстановки собранных значений, например "Спасибо, {{first_name}}, я записал вас на {{appointment_time}}."
Нода Message забирает ход — агент ничего не добавит поверх.
Gather input
Нода Gather input задаёт один вопрос и захватывает ответ звонящего в переменную.
Prompt — вопрос, который нужно задать. Поддерживает {{tokens}}.
Variable — имя поля, куда сохранить ответ.
Max silence (v2.8) — потолок на каждый вопрос: сколько ждать ответа звонящего, прежде чем двигаться дальше. Задавайте его для каждой ноды Gather input, чтобы быстрый вопрос «да/нет» мог истечь по таймауту раньше, чем тот, где звонящего просят продиктовать длинный номер счёта.
Используйте её всегда, когда нужно конкретное значение перед продолжением — email, номер счёта, число человек, категория проблемы. Следующий ход звонящего становится значением; на нём можно ветвиться ребром Condition сразу после.
Update state
Нода Update state записывает одно или несколько значений в переменные workflow, ничего не говоря и не спрашивая. Используйте её для предустановки значений по умолчанию, отметки флагов прогресса или записи значений, вычисленных из предыдущих шагов.
Variable / Value — одна пара ключ-значение. More variables (optional) — дополнительные пары ключ-значение.
Как ключи, так и значения поддерживают {{tokens}}, так что можно составлять новые значения из существующих.
Condition
Нода Condition — это нода только для маршрутизации: она не говорит, не спрашивает, а просто отправляет звонок по той исходящей ветви, которая совпала.
Само условие живёт на каждом исходящем ребре, а не на ноде — см. рёбра Condition ниже.
Используйте ноду Condition, когда хотите чёткую точку «fan-out» в своём графе: много ветвей, зависящих от значения одной переменной. По сравнению с условиями на рёбрах Agent-нод, выделенная нода Condition делает логику маршрутизации визуально очевидной.
Tool
Нода Tool запускает один из подключённых инструментов агента как шаг графа — не давая AI никакого права голоса в том, вызывать ли его. Используйте её, когда вам нужен факт, а не разговор: «найди этого клиента», «проверь остаток на складе», «GET текущей погоды».
Можно либо:
- Выбрать один из существующих инструментов агента (поиск по календарю, поиск по базе знаний, конкретное действие интеграции), либо
- Сконфигурировать inline HTTP-вызов прямо на ноде (URL, метод, заголовки, параметры, body).
Ответ инструмента становится доступен всем последующим нодам. Если ответ — JSON-объект, каждое поле верхнего уровня становится отдельной переменной — так что ответ вроде {"is_known": true, "tier": "gold"} запишет для вас и is_known, и tier, чтобы вы могли на них ветвиться.
Каждое текстовое поле на ноде Tool — URL, значения заголовков, body — поддерживает {{tokens}}. Воткните переменную в путь URL, заголовок или тело запроса без написания кода.
Если вызов инструмента упал (HTTP-ошибка, таймаут, сеть), нода логирует сбой и граф идёт дальше — без данных. Спланируйте последующие условия так, чтобы обработать случай «нет значения».
Knowledge (RAG)
Нода Knowledge (RAG) — это вариант Tool, сфокусированный на поиске по базе знаний. Выберите одну из баз знаний агента, напишите поисковый запрос (с {{tokens}}, если хотите), и нода выполнит поиск как шаг графа.
Используйте её, когда хотите, чтобы workflow что-то нашёл до того, как заговорит нода Agent — например, подтянуть самый недавний заказ клиента, чтобы следующая нода Agent могла упомянуть его, не спрашивая. Найденный контент попадает в переменную, которую можно подставлять в промпты и сообщения.
Integration
Нода Integration — это сфокусированный вариант Tool для подключённых внешних сервисов — Google Calendar, Outlook, HubSpot. Вы выбираете провайдера и конкретное действие (например, check_availability, create_event), задаёте параметры, и нода детерминированно вызывает интеграцию.
Если интеграция не подключена у этого агента, нода проходит насквозь, ничего не делая — ваш граф продолжается. Проверяйте последующие условия на случай «не сработало».
Phone transfer
Нода Phone transfer переводит звонок (warm transfer) на внешний телефонный номер. После успешного перевода звонок покинул ваш workflow — ваш агент больше не на линии.
Number — пункт назначения в международном формате с ведущим + (например, +15551234567). Поддерживает {{tokens}}, так что пункт назначения может прийти из более раннего шага (например, ответ на «с каким офисом вас соединить?»).
Если перевод не проходит (неверный номер, не отвечают, телефонная связь не настроена), граф откатывается и текущий агент остаётся на линии. Разговор продолжается как обычно — агент может извиниться и попробовать другой путь.
End call
Нода End call завершает разговор — агент произносит настроенную прощальную фразу, затем кладёт трубку. Используйте её как чистый терминал для веток «всё сделано» или для ветки «мы не можем помочь, извините» из сложного для обработки Condition.
Не требует конфигурации, кроме подведённого к ней ребра.
Переиспользование workflow (Sub-workflow)
Нода Sub-workflow зарезервирована для встраивания другого workflow как под-графа. Эта нода есть в редакторе, но ещё не полностью подключена — граф проходит её насквозь, ничего не делая. Не полагайтесь на неё для продакшен-звонков. Мы включим её, как только выпустим переиспользуемые под-графы.
Рёбра (Connections)
Вы соединяете одну ноду со следующей через ребро. Триггер ребра решает, когда срабатывает переход.
Есть три триггера:
| Триггер | Кто решает | Когда срабатывает |
|---|---|---|
| AI decides | AI | Когда разговор делает это естественным следующим шагом. AI получает «инструмент перехода», который он может вызвать. |
| Condition | Рантайм | Когда переменная совпадает с правилом, которое вы написали. Оценивается до того, как AI ответит. |
| Always | Рантайм | Безусловно — в тот момент, когда источниковая нода завершилась. Без ввода звонящего, без участия AI. |
AI decides (по интенту)
Используйте AI decides, когда только AI может определить, когда переключаться — «если звонящий спрашивает про цены, переведи на агента продаж», «если это звучит как жалоба, запусти subagent извинений».
Вы пишете короткое описание интента на ребре (например, «Звонящий спрашивает про цены или хочет проапгрейдиться»). AI видит это как инструмент, помеченный этим интентом, и решает, вызывать ли его, на основе разговора. Если существует несколько AI-decides рёбер, AI выбирает максимум одно за ход.
Condition (детерминированное ветвление)
Используйте Condition, когда хотите, чтобы граф — а не AI — решал на основе известного факта: «если lead_score is more than 7, отправь на closer; иначе — вежливо заверши звонок».
Conditions оцениваются до того, как AI ответит, так что они всегда побеждают AI в этом ходе. AI не может переопределить совпавшее условие.
Вы можете строить условие из этих операторов:
| Оператор | Значение | Пример |
|---|---|---|
| is | точное совпадение (регистронезависимо для текста) | intent is "billing" |
| is not | не совпадает | status is not "active" |
| contains | совпадение по подстроке (регистронезависимо) | feedback contains "broken" |
| is more than | численное > | lead_score is more than 7 |
| is less than | численное < | wait_minutes is less than 5 |
Вы также можете комбинировать условия через All (каждое должно совпасть — AND) или Any (хотя бы одно должно совпасть — OR), и вкладывать их. Так что правило вроде «звонящий известен и его tier — gold или premium» становится:
All:
- is_known is true
- Any:
- tier is gold
- tier is premium
Если вы ссылаетесь на переменную, которая ещё не была установлена (например, AI её так и не собрал), условия на ней всегда не выполняются — они не выдают ошибку, они просто не совпадают. Так что loyalty_tier is "gold" вернёт false, если loyalty_tier никогда не устанавливался. Проектируйте свои ветки так, чтобы обрабатывать это — обычно через ветку else (см. ниже).
Ветка else — когда ни одно условие не совпало
Если у вас есть несколько Condition-рёбер, выходящих из ноды, и ни одно из них не совпадает, звонок проваливается на не-Condition ребро самого низкого приоритета (Always или AI-decides ребро). Если fallback нет вообще, AI возвращает себе контроль и может продолжать свободно.
Распространённый паттерн: несколько Conditions для хорошо известных путей плюс Always ребро на catch-all (сказать что-нибудь через Message, затем End call или перевести).
Always (auto-advance)
Используйте Always, когда следующий шаг безусловный — без решения, без ввода звонящего. Два естественных применения:
- Цепочка шагов: Message → Update state → Message → Tool → Condition. Каждый шаг завершается, и ребро Always сразу продвигает дальше.
- Авто-продвижение после того, как агент закончил говорить: нода Agent с исходящим ребром Always означает «как только AI перестанет говорить, двигайся дальше» — без необходимости ответа звонящего. Полезно для монологовых потоков, например агента-презентера, который произносит текст, а затем переходит к следующей секции.
Condition и Always (детерминированные) всегда побеждают AI decides. Если Condition совпало, инструменты перехода AI игнорируются в этом ходе. Именно это позволяет вам писать правила, которые AI не может обойти.
Порядок веток
Когда из одной ноды выходит несколько рёбер, редактор позволяет задать приоритет, перетаскивая строки вверх или вниз. Ветки с более низким приоритетом проверяются первыми. Это важно только тогда, когда две ветки могут сработать на одном и том же ходу — обычно два Condition-ребра с пересекающимися правилами. Побеждает первая совпавшая ветка.
Перенос контекста — сколько помнит следующий агент
У каждого ребра есть настройка Carry context, которая контролирует, сколько транскрипта разговора видит следующая нода:
- Full transcript (по умолчанию) — следующий агент видит всё, что было сказано до этого.
- Summary only — платформа суммирует предыдущий разговор и передаёт следующему агенту короткое резюме.
- Nothing — следующий агент начинает с нуля, без предыдущего транскрипта.
Используйте Summary only, когда передаёте дело специализированному агенту, которому не нужно каждое слово — это экономит токены и держит нового агента сфокусированным. Используйте Nothing для веток, где персона нового агента должна быть отдельной (например, опрос об удовлетворённости в конце звонка).
Переменные
Переменные — это память workflow. Большинство вещей в workflow — это либо установка переменной, либо её чтение.
Объявление переменных
Панель Variables (кнопка в тулбаре) показывает каждую переменную workflow с шагами, которые её устанавливают или читают. Клик по переменной — переход к сеттеру; это самый быстрый способ найти, где что-то идёт не так.
Вы можете объявить переменную со значением по умолчанию в панели — полезно для флагов вроде escalated=false, которые должны существовать с самого начала, чтобы ваши условия не сваливались тихо на «отсутствии».
Встроенные переменные звонка
Несколько переменных заполняются за вас в самом начале каждого звонка — вы их не объявляете и не устанавливаете, они просто доступны для чтения:
| Переменная | Что содержит |
|---|---|
{{call_from}} | Номер телефона звонящего |
{{call_to}} | Номер, на который позвонили (ваш номер) |
{{call_direction}} | inbound или outbound |
Поскольку они существуют с первого мгновения, по ним можно ветвиться на самой первой ноде — например, Condition по call_from, чтобы поприветствовать известного VIP по имени, маршрутизировать по коду страны или отправить неизвестные номера по пути верификации, прежде чем произойдёт что-либо ещё.
Как переменные устанавливаются
| Источник | Пример |
|---|---|
| Значения по умолчанию (объявленные в панели Variables) | escalated = false, установленное до начала звонка |
| Gather input | Голосовой ответ звонящего захватывается в переменную ноды |
| Update state | Вы записываете значение (литерал или составленное из {{tokens}}) напрямую |
| Результат Tool / Integration | Весь ответ сохраняется под именем ноды; если ответ — JSON-объект, каждое поле верхнего уровня тоже становится отдельной переменной |
| Return из Subagent | То, чем subagent завершается, сохраняется в return-переменную ноды subagent |
Чтение переменных — {{tokens}}
Везде, где можно вписать текст на ноде — промпт Agent, текст Message, промпт Gather, URL / body / headers в Tool, номер в Phone transfer, значение в Update state, значения условий — можно воткнуть {{variable}}, и оно подставится в рантайме.
Пути с точкой тоже работают для вложенных данных — например, если Tool-ответ был {"customer": {"name": "Anna", "tier": "gold"}}, можно читать {{customer.name}} или {{customer.tier}}.
Если токен ссылается на несуществующую переменную, литеральный текст {{name}} оставляется на месте (он не очищается). Это намеренно — так сломанные шаблоны легко заметить в test call, вместо того чтобы тихо их проглатывать.
Распространённые паттерны
Квалификация → Ветвление → Маршрутизация
Start
└─ Agent (greeting + qualify)
└─ Gather input (intent)
└─ Condition: intent == "sales" → Agent (sales)
└─ Condition: intent == "support" → Subagent (triage)
└─ Always (catch-all) → Message "Let me connect you" → Phone transfer
Поиск → Персонализация
Start
└─ Tool (CRM lookup, writes is_known, name, tier)
└─ Condition: is_known == true → Agent (warm greeting with {{name}})
└─ Always (else) → Agent (cold greeting)
Переиспользуемый хелпер (side-trip Subagent)
Agent (main conversation)
└─ AI decides: "Caller mentions an address" → Subagent (address collector)
└─ on finish: result = full_address
└─ Agent continues with {{full_address}}
Что заблокировано во время workflow-звонка
Некоторые значения по умолчанию меняются, когда звонок управляется графом:
- Язык заблокирован на том, что использует первый агент workflow. AI не может сменить язык посреди звонка. Это предотвращает уход модели не на тот язык на шумном ходу.
- Основной промпт агента заменяется промптом активной ноды. Это правило «граф владеет персоной» — см. warning под Agent.
- Первое сообщение приходит из greeting входной ноды, а не из поля «Begin Message» агента.
Защита от циклов — бюджет шагов
У каждого workflow есть максимальное число переходов на звонок (по умолчанию 25). Каждый раз, когда граф переходит к новой ноде, счётчик уменьшается на единицу. Когда он доходит до нуля, workflow перестаёт прыгать между нодами, чтобы предотвратить неуправляемые циклы.
Вы можете выбрать запасную ноду для этого случая (обычно Phone transfer или End call) — звонок отклоняется туда, как только бюджет исчерпан. Иначе AI возвращает себе контроль, и граф фактически замораживается на оставшуюся часть звонка.
Об этом очень редко приходится думать — это страховочная сетка для графов, которые случайно зациклились. Если вы попали в неё при обычном использовании, у вас, скорее всего, где-то цикл.
Тестирование
Кнопка Test Call в редакторе открывает браузерный звонок с текущим draft-ом workflow. Пока звонок идёт, светящийся светлячок движется между нодами на холсте, чтобы вы видели, где именно сейчас звонок. Редактор заблокирован во время test call, чтобы правки не рассинхронизировались с живым состоянием.
Используйте Test Call, чтобы проверить:
- Ваши условия совпадают со значениями, которых вы ожидаете (следите, не проскакивает ли светлячок мимо ветки, которая, как вы думали, должна сработать).
- Ваши токены подставляются правильно (прислушайтесь к зависшему
{{name}}, если значение ещё не было установлено). - Сбои инструментов деградируют изящно (ваш fallback-маршрут действительно срабатывает).
Режимы сбоев, о которых стоит знать
| Ситуация | Что происходит |
|---|---|
| Вызов Tool / Integration упал | Граф продолжается без данных. Ваши последующие условия должны обрабатывать отсутствующую переменную. |
| Phone transfer упал (неверный номер, не отвечают, телефонная связь не настроена) | Граф откатывается, текущий агент остаётся на линии, и разговор продолжается как обычно. |
| Subagent не запустился | Побочный заход отменяется, родительский агент остаётся, и произносится короткая фраза о сбое. |
{{missing_variable}} в тексте | Оставляется литералом в выводе — легко заметить во время test call. |
| Нода Sub-workflow | Сейчас проходит насквозь, ничего не делая. Не полагайтесь на неё пока. |
| Бюджет шагов исчерпан | Отклоняется на запасную ноду, если она задана, иначе AI возвращает себе контроль. |
Когда НЕ использовать Workflow
Workflows мощны, но они не правильный ответ для каждого агента. Останьтесь с одним промптом, когда:
- Разговор открытый («отвечать на вопросы звонящего о наших продуктах»).
- У вас нет чёткой логики ветвления — большинство решений — это суждения, с которыми AI хорошо справляется.
- Вы бы написали одну ноду Agent и одну End call — это просто промпт с лишними шагами.
Переходите на workflow, когда:
- Вы можете набросать звонок как блок-схему с тремя или более чёткими этапами.
- Вам нужно хотя бы одно детерминированное ветвление по переменной (Condition).
- Вы хотите переиспользовать кусок разговора между несколькими агентами (subagent).
- Вы уже накапливаете правила «если звонящий говорит X, то делай Y» в промпте, и они ненадёжны.