Как создать визуальный конструктор рабочих процессов с открытым исходным кодом для ИИ-агентов в 2026 году
TL;DR
Визуальный конструктор рабочего процесса для AI-агентов — это холст, состоящий из связанных узлов, плюс движок, который выполняет их в порядке зависимости. Холст — это проблема пользовательского интерфейса (перетаскивание, соединение, позиционирование); движок — это графовая проблема (топологическая сортировка направленного ациклического графа, или DAG).
React Flow (опубликованный как пакет @xyflow/react, текущая версия 12.11.3, лицензия MIT) является стандартной открытой библиотекой для половины холста. Он отображает узлы и рёбра, обрабатывает перетаскивание и масштабирование, и предоставляет хуки для чтения и изменения состояния графа.
Вторая половина исполнения — определение того, что выполняется, в каком порядке и с какими входными данными — не является тем, что предоставляет библиотека холста. Это должно быть написано как небольшой, отдельный движок: сортировать граф, выполнять обработчик каждого узла и передавать выходные данные каждого узла в узлы, которые от него соединяются.
Узел, которому нужны живые веб-данные (узел "fetch" или "crawl"), нуждается в реальном слое выборки за ним, а не просто в вызове fetch() — страницы, рендеримые с помощью JavaScript, и защита от ботов будут тихо ломать ручной HTTP-запрос задолго до того, как остальная часть рабочего процесса сработает.
Движок исполнения этой статьи на самом деле был запущен, а не просто описан: граф из четырех узлов (триггер → ползунок → подсказка → вывод) был топологически отсортирован и выполнен от начала до конца в этой среде, с реальными захваченными выходными данными, показанными ниже.
Ручной конструктор рабочих процессов — это реальный, осуществимый проект выходного дня с двумя проверенными открытыми библиотеками (React Flow плюс топологическая сортировка), но он не поставляется с никакими операционными функциями (аутентификация, повторные попытки, ведение журналов на уровне узлов, версионирование), которые нужны инструменту для продакшена — эта статья явно заявляет об этом ограничении, а не обходится им стороной.
Попробуйте Nstproxy - Начните бесплатный тест сегодня
Введение: что на самом деле означает "создание своего собственного конструктора рабочего процесса агентов"
Конструктор рабочего процесса AI-агента представляет собой, структурно, две отдельные системы, использующие один пользовательский интерфейс: холст, на котором человек располагает и соединяет узлы, и движок, который считывает этот граф и на самом деле выполняет его. Интерес к инструментам стиля "открытого конструктора агентов" возрос наряду с платформами искусственного интеллекта без кода и с малым количеством кода, но большинство того, что показывается по этой фразе, — это либо хостируемый продукт без видимого кода, либо короткая демонстрация, которая рисует красивый холст и никогда на самом деле ничего не запускает. Эта статья создает реальную вещь: холст, использующий открытую библиотеку React Flow, и небольшой движок исполнения в порядке зависимости, соединенный с узлом для выборки живых данных, при этом движок исполнения действительно запущен, а его вывод захвачен ниже.
Две вещи намеренно находятся вне сферы охвата. Во-первых, это не пошаговое руководство по какому-либо конкретному коммерческому или открытому продукту рабочего процесса — оно обучает основному шаблону с помощью нейтральной библиотеки, чтобы результат был вашим для расширения. Во-вторых, "AI" в узле здесь означает, что обработчик узла вызывает языковую модель или инструмент; сам графовый движок не имеет мнения о том, что узел делает внутри, что именно делает его многоразовым для автоматизации без AI тоже.
Установка: React Flow и минимальный проект
React Flow поставляется как @xyflow/react на npm. На момент написания опубликованная версия — 12.11.3 (подтверждено напрямую в реестре npm и установлено чисто с помощью обычной команды npm install в свежем проекте во время проверки для этой статьи), и пакет лицензирован по MIT в соответствии с репозиторием xyflow на GitHub.
Сгенерируйте минимальный проект Vite + React, затем добавьте библиотеку:
Та версия статична — массивы никогда не меняются после рендеринга. Реальному строителю необходимо, чтобы холст реагировал на перетаскивания и новые соединения, для чего и нужны useNodesState и useEdgesState. Каждый хук возвращает текущий массив, сеттер и обработчик изменений, который подключается напрямую к свойствам onNodesChange / onEdgesChange компонента <ReactFlow>:
Документация React Flow отмечает, что эти хуки предназначены для прототипирования контролируемого потока, и что большему строителю следует перейти к специализированному хранилищу состояния (в частности, упоминается Zustand) как только состояние узлов и рёбер необходимо будет разделить с компонентом холста — что делает настоящий движок выполнения, поскольку ему требуется читать тот же граф, который отображает холст.
Базовая реализация: тип узла для действия агента
Типы узлов по умолчанию (input, default, output) отображают только метку. Рабочий процесс агента требует типов узлов, которые несут конфигурацию — URL для шага получения, шаблон подсказки для шага модели — и открывают точки соединения, к которым могут подключаться другие узлы. Паттерн пользовательского узла в React Flow — это простой компонент React, зарегистрированный в карте nodeTypes:
Узел с обоими ручками target (слева) и source (справа) может находиться в середине цепочки — он получает соединение от вышестоящего узла и отправляет свой собственный вывод вниз по течению. Узлу-спусковику нужна будет только ручка source; терминальному узлу вывода понадобится только ручка target.
Расширенные паттерны: движок выполнения, который на самом деле запускает граф
Холст производит лишь два массива — узлы и рёбра. Превращение этого в работающий поток означает ответ на один вопрос, на который библиотека UI не отвечает: в каком порядке выполняются узлы? Ответом является топологическая сортировка — посещать каждый узел только после того, как все узлы, которые подают в него, уже были выполнены. Это теория графов, а не React, поэтому это было написано и выполнено как простой скрипт для этой статьи, а не оставлено в виде диаграммы.
Граф протестирован: узел-спусковик, узел-обходчик, который получает страницу, узел-подсказка, который обобщает то, что вернул узел-обходчик, и узел вывода.
functiontopoSort(nodeList, edgeList){const indegree =newMap(nodeList.map((n)=>[n.id,0]));const adjacency =newMap(nodeList.map((n)=>[n.id,[]]));for(const edge of edgeList){ adjacency.get(edge.source).push(edge.target); indegree.set(edge.target, indegree.get(edge.target)+1);}const queue = nodeList.filter((n)=> indegree.get(n.id)===0).map((n)=> n.id);const order =[];while(queue.length){const id = queue.shift(); order.push(id);for(const next of adjacency.get(id)){ indegree.set(next, indegree.get(next)-1);if(indegree.get(next)===0) queue.push(next);}}if(order.length!== nodeList.length){thrownewError('Граф содержит цикл — рабочий процесс должен быть ДАГ');}return order;}
Проверка на цикл важнее, чем кажется: интерфейс холста с радостью позволит кому-то перетащить ребро, создающее петлю (узел A подает узлу B, который подает узлу A), и без этой проверки движок либо зависнет, либо незаметно сбросит узлы вместо того, чтобы сообщить человеку, который построил рабочий процесс, что не так.
С порядком выполнения в руках, запуск рабочего процесса представляет собой цикл, который вызывает обработчик каждого узла и передаёт выводы в входы вдоль рёбер, указывающих на него:
asyncfunctionrun(nodes, edges){const order =topoSort(nodes, edges);const nodesById =newMap(nodes.map((n)=>[n.id, n]));const outputs =newMap();const log =[];for(const id of order){const node = nodesById.get(id);const incoming = edges.filter((e)=> e.target=== id).map((e)=> outputs.get(e.source));let result;switch(node.type){case'trigger': result ='запуск-начат';break;case'crawl': result =awaitrunCrawlNode(node);break;case'prompt': result =runPromptNode(node, incoming[0]);break;case'output': result = incoming[0];break;default:thrownewError(`Неизвестный тип узла: ${node.type}`);} outputs.set(id, result); log.push({node: id,type: node.type,output: result });}return{ order, log };}
runCrawlNode это место, где ручной вызов fetch() к произвольному URL сталкивается с проблемами: множество реальных страниц отображаются на стороне клиента, блокируют запросы без отпечатка браузера или требуют прокси, чтобы к ним можно было надежно обратиться. Для проверки выполнения этой статьи тот обработчик вызывал Nstproxy CrawlPOST /api/v1/crawl/scrape конечную точку (документированную на docs.nstproxy.com/docs/crawl) — которая возвращает отрендеренный Markdown вместо необработанного HTML — так что задача узла обхода свелась к распаковке ответа, а не к повторной реализации браузера:
asyncfunctionrunCrawlNode(node){const response =awaitfetch('https://api.nstproxy.com/api/v1/crawl/scrape',{method:'POST',headers:{'x-api-key': process.env.NSTPROXY_API_KEY,'Content-Type':'application/json'},body:JSON.stringify({url: node.data.url,formats:['markdown'],onlyMainContent:true}),});const envelope =await response.json();if(envelope.err)thrownewError(envelope.msg||'запрос на обход не удался');const inner = envelope.data;if(!inner.success)thrownewError(inner.status||'обход не завершен');return inner.data.markdown;}
Эта среда не имеет доступа к исходящему сетевому трафику к произвольным доменам и не имеет действующего ключа API Nstproxy, поэтому выполнение проверки ниже заменяет локальный HTTP-фиктив, который возвращает точно такой же вложенный ответный конверт, который специфицирует собственная документация Nstproxy Crawl (внешние code/err/msg/data, внутренние data.success/status, нагрузка страницы на data.data.markdown) вместо реальной конечной точки — это раскрывается здесь, а не представляется как реальный вызов API. Логика распаковки сама по себе (проверка err, затем success, затем чтение data.data.markdown) — это именно то, что выполняется по отношению к реальной конечной точке; только целевой транспорт изменился для этого теста.
Рабочий пример: выполнение четырехузлового графа
Граф, использованный для этого выполнения: start (триггер) → fetch (узел обхода с целью страницы продукта Nstproxy Crawl) → prompt (резюмирует все, что вернул узел обхода) → output. Он был выполнен с помощью Node.js (v22.22.2) в этой среде, с fetch нацеленным на локальный сервер фиктив, выступающий в качестве настоящего API Nstproxy Crawl по вышеупомянутой причине.
Захваченный вывод:
{"order":["start","fetch","prompt","output"],"log":[{"node":"start","type":"trigger","output":"запуск-начат"},{"node":"fetch","type":"crawl","output":"# Nstproxy Crawl\n\nNstproxy Crawl превращает URL в чистый Markdown, JSON или снимок экрана с помощью одного вызова API, при этом встроена рендеринг на JavaScript и доступ через прокси."},{"node":"prompt","type":"prompt","output":"Подытожьте эту страницу в одном предложении:\n\n# Nstproxy Crawl\n\nNstproxy Crawl превращает URL в чистый Markdown, JSON или снимок экрана с помощью одного вызова API, при этом встроена рендеринг на JavaScript и доступ через прокси.\n\n[имитационный вывод модели] Nstproxy Crawl — одно предложение, сгенерированное из полученной страницы."},{"node":"output","type":"output","output":"Подытожьте эту страницу в одном предложении:\n\n# Nstproxy Crawl\n\nNstproxy Crawl превращает URL в чистый Markdown, JSON или снимок экрана с помощью одного вызова API, при этом встроена рендеринг на JavaScript и доступ через прокси.\n\n[имитационный вывод модели] Nstproxy Crawl — одно предложение, сгенерированное из полученной страницы."}]}
Массив order подтверждает, что топологическая сортировка разместила каждый узел после его зависимостей, а массив log показывает, как вывод каждого узла передается в следующий — Markdown узла обхода стал входом узла подсказки, а (сымитированное) резюме узла подсказки стало финальным выводом. Вызов модели узла подсказки является детерминированной заменой, а не реальным вызовом API LLM, раскрытым так же, как и замена обхода выше; замена реального вызова модели означает вызов SDK любого поставщика внутри runPromptNode вместо моковой строки.
Быстрый взгляд
Каждый узел "fetch" в рабочем процессе, подобном этому, в конечном итоге попадает на страницу, которая требует рендеринга JavaScript или блокирует обычный HTTP-запрос — Nstproxy Crawl обрабатывает этот уровень с одним вызовом API вместо самописного браузера.
Сборка выходного устройства плюс движок топологической сортировки доказывает, что основной шаблон работает, но ему не хватает всего, что нужно инструменту рабочего процесса в производственной среде вокруг этого ядра. Нет уровня постоянства — граф в этой статье существует в памяти для одного запуска, и ничего не сохраняется между сеансами. Нет повторной попытки или обработки частичного сбоя — если запрос узла обхода не удается, весь запуск завершает работу вместо того, чтобы пытаться снова или направлять к ветке с ошибкой. Нет журнала выполнения для каждого узла, отображаемого обратно в пользовательский интерфейс холста, нет возможности приостановить и проверить состояние во время выполнения, нет аутентификации или контроля доступа для нескольких пользователей и нет версионирования сохранённого рабочего процесса. Также нет защиты от обработчика узлов, который безопасно выполняет произвольный код, предоставленный пользователем — любой узел типа "пользовательский код" в реальном продукте нуждается в среде выполнения в песочнице, которую простой оператор switch в этой статье не предоставляет. Никакая из этих задач не является обязанностью React Flow; это настоящая инженерная работа по превращению этого шаблона в инструмент, на который другие люди могут полагаться. Если уровень получения данных узла обхода является той частью, которая передается на аутсорсинг, а не создается вручную, имеет смысл проверить цены на размещенный API на основе ожидаемого объема вызовов, прежде чем добавлять шаг получения в рабочий процесс.
Устранение неполадок
Холст отображается без стилей или с перекрывающимися узлами. Это почти всегда означает, что импорт @xyflow/react/dist/style.css был пропущен — React Flow позиционирует узлы с абсолютными координатами, которые зависят от его собственной базовой таблицы стилей.
Рабочий процесс с циклом зависает или молча отбрасывает узлы. Это цикл, на который ссылается функция topoSort, упомянутая выше — пользовательский интерфейс холста не имеет встроенного способа предотвратить создание пользователем ребра, создающего цикл, поэтому движок должен явно проверять его, а не предполагать, что каждый граф, построенный пользователем, является допустимым DAG.
Пользовательский узел не принимает подключения. Проверьте, что компонент узла включает в себя Handle с правильным type (source или target) и что он действительно отображается — настраиваемый компонент узла, который пропускает <Handle>, визуально отображается, но никогда не может быть подключен к другому узлу.
Узел "fetch" работает для некоторых URL-адресов и не работает для других. Обычно это проблема рендеринга JavaScript или обнаружения ботов, а не ошибка в коде узла — простой вызов fetch() видит только необработанный HTML, который возвращает сервер, а не то, что браузер будет рендерить после выполнения скриптов страницы, именно поэтому узел обхода в этой статье обращается к API, учитывающему рендеринг, вместо того чтобы напрямую извлекать URL.
Заключение
Создание сборщика рабочих процессов для AI делится на два проверенных куска: React Flow (@xyflow/react, версия 12.11.3, лицензия MIT) для холста и небольшого самописного движка топологической сортировки для выполнения — ни один из них не зависит от другого, и именно поэтому их можно разрабатывать и тестировать отдельно, как это сделано в этой статье. Результат — это реальный, работоспособный шаблон, а не диаграмма, проверенный с помощью фактического графа из четырех узлов, выполненного от начала до конца. Чем он не является, так это готовым продуктом — постоянство, повторные попытки, работа в песочнице и контроль доступа все еще требуют реальной инженерной работы, наложенной на это ядро. Для получения информации о слое получения данных, используемом в примере, см. пост о запуске Nstproxy Crawl.
Часто задаваемые вопросы
В: Мне нужен именно React Flow, или я могу использовать другую библиотеку?
React Flow (@xyflow/react) — это библиотека, используемая и проверенная в этой статье, но это не единственный вариант — Svelte Flow (аналогичная библиотека от той же команды для Svelte) охватывает проекты, не использующие React, и любая библиотека холста, которая предоставляет позиции узлов, ребра и способ регистрации пользовательских компонентов, может выполнять ту же роль. Описанный здесь движок выполнения совершенно независим от того, какая библиотека холста его рендерит, поскольку он только потребляет простые массивы узлов и ребер.
В: Должен ли движок выполнения работать в браузере?
Нет — и для большинства реальных рабочих процессов это не должно быть так. Холст работает в браузере, так что человек может редактировать граф, но выполнение рабочего процесса (особенно с ключами API или длительными шагами) должно происходить на сервере. Движок, показанный в этой статье, это обычный JavaScript без зависимости от браузера, поэтому он может работать в Node.js точно так же, как тестировалось здесь, или внутри любой серверной среды, поддерживающей fetch.
В: Как остановить кого-то от создания рабочего процесса, который зацикливается навсегда?
Отклоните его до выполнения. Функция topoSort в этой статье выбрасывает исключение, когда отсортированный порядок не включает каждую ноду, что и происходит, когда существует цикл — поймайте эту ошибку в пользовательском интерфейсе холста и скажите человеку, какие ноды вовлечены, вместо того чтобы позволить движку зависнуть.
В: Может ли нода вызывать более одной внешней службы?
Да — обработчик ноды это просто функция; он может делать столько вызовов, сколько ему нужно, прежде чем вернуть свои выводы. Единственное ограничение, которое накладывает движок, состоит в том, что обработчик ноды получает только выходные данные нод с ребром, указывающим на него, поэтому любой узел, который нуждается в данных из двух вышестоящих источников, нуждается в двух входящих ребрах.
В: Какой самый быстрый способ добавить вызов реальной языковой модели вместо макета узла запроса?
Замените тело runPromptNode на вызов того SDK провайдера модели, который используется, передавая выходные данные входящей ноды в качестве контекста и возвращая ответ модели в виде строки — остальная часть движка (сортивка, передача вывода, логирование) не нуждается в изменениях, поскольку ей важно только то, что обработчик ноды возвращает значение.
В: Почему узел обхода вызывает API, а не просто использует fetch() напрямую?
Обычный вызов fetch() получает только тот HTML, который сервер отправляет до выполнения любого клиентского JavaScript, и многие страницы рендерят свой фактический контент позже — API, осведомленный о рендеринге, выполняет страницу так, как это сделал бы браузер, прежде чем вернуть Markdown или HTML, что и является основой работы узла обхода в примере этой статьи.
Создайте настоящий открытый визуальный конструктор рабочих процессов для агентов ИИ: холст React Flow в сочетании с проверенным движком выполнения топологической сортировки, протестированным от и до с реальным захваченным результатом.
Ivy Lin
Aug. 24th 2026
110M+ реальных IP с 99.9% успешных доступов
Средний отклик ~0.5с для задач высокой конкуренции
Всего от $0.1/GB
Мгновенный доступ к премиальным residential, datacenter, IPv6 и ISP пулам.