Перейти к основному содержимому

Редактор кода

Редактор функций -- это специализированная среда разработки, встроенная в платформу. Она состоит из двух панелей: слева -- редактор кода, справа -- панель тестирования.

Интерфейс редактора

Верхняя панель

В верхней панели расположены:

  • Кнопка назад -- возврат к списку функций
  • Имя функции -- редактируемое поле (кликните, чтобы изменить)
  • Категория -- автодополняемое поле; можно выбрать существующую категорию или ввести новую
  • Вкладки «Код» и «Параметры» -- переключение между редактором кода и JSON Schema параметров
  • Таймаут (сек) -- максимальное время выполнения (от 1 до 60 секунд, по умолчанию 10)
  • Кнопка «Сохранить» -- сохраняет все изменения
Горячие клавиши
  • Cmd+S (Mac) / Ctrl+S (Windows) -- сохранить функцию
  • Cmd+Enter / Ctrl+Enter -- отправить AI-промпт для генерации

AI-генерация

Под верхней панелью находится поле для AI-генерации. Опишите, что должна делать функция, и нажмите Генерировать. AI сгенерирует:

  • Код функции (на вкладке «Код»)
  • Имя и описание функции
  • JSON Schema параметров (на вкладке «Параметры»)

Если вы находитесь на вкладке Параметры, AI сгенерирует только JSON Schema на основе текущего кода функции и вашего описания.

Примеры промптов:

  • «Получить лид из Bitrix24 по ID»
  • «Добавить фильтр по статусу и дате»
  • «Отправить сообщение в Telegram через Bot API»

Нажмите кнопку Стоп, чтобы отменить генерацию.

Форматирование кода

Кнопка с иконкой кисти (справа от кнопки «Генерировать») автоматически форматирует код или JSON с помощью Prettier.

Структура кода функции

Каждая функция оборачивается в асинхронную функцию execute со следующей сигнатурой:

async function execute(context, axios, moment, console) {
// Ваш код здесь

return result;
}

Вам не нужно писать обертку вручную -- редактор добавляет ее автоматически.

Обертка косметическая, а ее аргументы -- не параметры

При сохранении платформа снимает обертку и хранит только тело функции. context, axios, moment, console -- это глобальные объекты песочницы, а не аргументы, которые кто-то передает при вызове.

Отсюда следствие: переименовывать их нельзя. Если написать async function execute(ctx, http), то в теле не будет ни ctx, ни http -- глобалы по-прежнему называются context и axios, а код упадет с ctx is not defined.

Не стоит и добавлять свои аргументы в сигнатуру -- они ничем не будут заполнены.

Если обертку не удается разобрать -- например, после нее остался посторонний код, -- функция не сохранится, и вы получите ошибку с описанием проблемы. Так сделано намеренно: раньше такой код сохранялся вместе с оберткой, и функция затем возвращала null со статусом «успех», не давая никакой подсказки о причине.

Доступные объекты

context -- контекст выполнения

СвойствоОписание
context.inputВходные параметры функции (из предыдущей ноды, аргументов инструмента или тестовой панели)
context.lastOutputРезультат предыдущей ноды в рабочем процессе
context.stateВсе переменные рабочего процесса
context.nodeOutputsРезультаты всех выполненных нод, доступные по ID ноды
context.user_idID текущего пользователя
context.project_idID текущего проекта
context.organization_idID организации
context.thread_idID текущего потока (сессии)
context.execution_idID текущего выполнения
context.integrationsУчетные данные интеграций организации (см. ниже)

Все перечисленные поля присутствуют всегда, на любом пути вызова. Там, где значения нет, приходит null или пустой объект -- но не undefined. Это позволяет читать поля без проверок.

Различия между путями вызова

Набор ключей одинаков, но наполнение зависит от того, откуда вызвана функция:

ПолеНода рабочего процессаИнструмент текстового агентаОбработчик ошибкиТест из редактораИнструмент голосового агента
inputвход нодыаргументы вызоваконтекст ошибкиданные из панелиаргументы вызова
project_id, organization_idдадададаproject_id да, organization_id нет
user_id, thread_idдададанетда
state, lastOutput, nodeOutputsдапустопустопустопусто
memoryдадададапусто
memoryOps (запись)дадададане применяется
integrationsдадададапусто
metaдадададапусто

state, lastOutput и nodeOutputs пусты вне рабочего процесса потому, что вне графа им нечего содержать: у агента нет предыдущих нод.

Ограничения голосового агента

У функции, вызванной голосовым агентом, память недоступна: context.memory всегда пустая, а операции из context.memoryOps не применяются. Интеграции тоже недоступны -- голосовая сессия не привязана к организации. Если функция должна работать и в голосовом сценарии, не полагайтесь на память и интеграции.

context.memory -- чтение памяти

Доступ к данным памяти по областям видимости. Подробнее см. в разделе Память агента:

const userName = context.memory.user["name"];
const projectSettings = context.memory.project["settings"];
const threadHistory = context.memory.thread["history"];
const orgConfig = context.memory.organization["config"];

context.memoryOps -- запись в память

Для записи и удаления данных из памяти используйте массив операций:

// Записать значение
context.memoryOps.push({
action: "set",
scope: "project", // user, project, thread, organization
key: "myKey",
value: "myValue"
});
context.set('memoryOps', context.memoryOps);

// Удалить значение
context.memoryOps.push({
action: "delete",
scope: "project",
key: "myKey"
});
context.set('memoryOps', context.memoryOps);
Важно

После добавления операций в context.memoryOps необходимо вызвать context.set('memoryOps', context.memoryOps), чтобы изменения были применены. Без этого вызова платформа не увидит операции и молча ничего не запишет.

Второй рабочий вариант -- вернуть операции в составе результата: return { memoryOps: context.memoryOps, ... }. Достаточно любого из двух способов.

context.integrations -- учетные данные интеграций

Активные интеграции организации, доступные по имени провайдера. Позволяют обращаться к внешним системам, не вписывая токены в код функции:

const token = context.integrations.bitrix24?.webhook_url;
if (!token) {
return { error: "Интеграция Bitrix24 не настроена" };
}

Набор полей внутри каждого провайдера определяется тем, как настроена интеграция. Проверяйте наличие интеграции перед использованием: если она не настроена, отключена или недоступна, соответствующего ключа просто не будет.

warning

Значения интеграций -- это секреты. Не выводите их в console.log и не возвращайте из функции: логи выполнения и результат видны в интерфейсе платформы.

context.meta -- метаданные выполнения

СвойствоОписание
context.meta.startedAtВремя начала выполнения
context.meta.currentNodeID текущей ноды
context.meta.executionCountКоличество выполнений

axios -- HTTP-клиент

Встроенный HTTP-клиент для выполнения запросов к внешним API:

// GET-запрос
const response = await axios.get(url, {
headers: { 'Authorization': 'Bearer token' }
});
return response.data;

// POST-запрос
const response = await axios.post(url, {
key: 'value'
});
return response.data;

Доступны get, post, put, patch, delete, head, options, request, вызов axios(config) как функции и создание отдельного клиента через axios.create(). Все они попадают в трейс выполнения: каждый запрос виден в логах вместе с URL, методом, телом и маршрутом.

// Отдельный клиент с общими настройками
const api = axios.create({ baseURL: 'https://api.example.com' });
const response = await api.get('/clients');

moment -- работа с датами

Библиотека Moment.js для работы с датами и временем:

const now = moment();
const formatted = now.format('YYYY-MM-DD HH:mm');
const tomorrow = moment().add(1, 'day');

request -- устаревший HTTP-клиент

В песочнице также доступна библиотека request@2.88. Она оставлена для совместимости со старыми функциями; для нового кода используйте axios.

warning

Запросы через request не попадают в трейс выполнения и всегда идут напрямую, минуя прокси, -- настройка маршрута на них не действует. См. Сетевые запросы и прокси.

console -- логирование

Стандартный объект для вывода логов:

console.log("Отладочное сообщение");

Доступны console.log, console.info, console.warn и console.error. Логи отображаются в панели результатов при тестировании и сохраняются в трейсе выполнения.

Сетевые запросы и прокси

Некоторые внешние API недоступны с исходящего адреса платформы. Для таких случаев запрос можно направить через прокси -- тот же, что используют остальные сервисы платформы.

Маршрут задается в коде функции, для каждого запроса отдельно, ключом useProxy:

// Через прокси
const response = await axios.get(url, { useProxy: true });

// Напрямую, даже если по умолчанию включен прокси
const response = await axios.get(url, { useProxy: false });

// Для запросов с телом -- третьим аргументом
const response = await axios.post(url, data, { useProxy: true });

Если useProxy не указан, действует режим по умолчанию, заданный на уровне сервиса. Обычно это прямые запросы.

В логах выполнения у каждого запроса виден фактический маршрут -- via proxy или via direct. По нему можно проверить, что настройка сработала.

Что важно знать

Прокси не настроен, а запрошен явно. Если написать useProxy: true там, где прокси недоступен, запрос завершится ошибкой Proxy requested, but proxy is not configured for this service. Молчаливого перехода на прямой запрос не происходит специально: иначе вы бы получили успешный ответ, не заметив, что трафик ушел не тем маршрутом.

Редиректы через прокси не отслеживаются. Если запрос с useProxy: true получает ответ 301 или 302, функция завершится ошибкой Request failed with status code 302 с пояснением. Причина техническая: переход по редиректу ушел бы напрямую, минуя прокси.

Лучшее решение -- указать конечный URL. Если он заранее неизвестен, обработайте ответ 3xx сами: отключите проверку статуса и перейдите по location вторым запросом, тоже через прокси.

const first = await axios.get(url, { useProxy: true, validateStatus: null });

const response = first.status >= 300 && first.status < 400
? await axios.get(first.headers.location, { useProxy: true, validateStatus: null })
: first;

return response.data;
warning

Если задать свой maxRedirects больше нуля, axios снова начнет переходить по редиректам самостоятельно -- и эти переходы пойдут напрямую, минуя прокси, хотя в логах у запроса останется пометка via proxy.

useProxy работает только для отдельного запроса. В axios.create({ useProxy: true }) этот ключ не действует -- указывайте его в самом запросе:

const api = axios.create({ baseURL: 'https://api.example.com' });
const response = await api.get('/clients', { useProxy: true }); // так

Маршрут контролируется только для axios. Запросы через request всегда идут напрямую.

Ограничения песочницы

Код функции выполняется в изолированной песочнице, где доступны стандартные возможности JavaScript (Promise, JSON, Math, Date, классы, деструктуризация, ?., ??) и перечисленные выше объекты. Остального окружения Node.js там нет:

НедоступноЧто использовать вместо
fetchaxios
require, importТолько встроенные axios, moment, request
setTimeout, setIntervalПауз в коде сделать нельзя (см. ниже)
process, Buffer, файловая система, сеть напрямую--
Задержки в коде невозможны

setTimeout в песочнице нет, поэтому привычная пауза не сработает:

await new Promise(r => setTimeout(r, 1000));   // ОШИБКА: setTimeout is not defined

Если внешний API требует ожидания или повторной попытки, реализуйте это на стороне рабочего процесса -- отдельными нодами, -- а не внутри функции.

Таймаут

Таймаут задается в верхней панели редактора, от 1 до 60 секунд, по умолчанию 10. Он действует на всех путях вызова одинаково: и когда функция стоит нодой рабочего процесса, и когда ее вызывает текстовый или голосовой агент, и при запуске из панели тестирования.

По истечении таймаута выполнение прерывается, а функция возвращает ошибку -- частичный результат не сохраняется.

Автодополнение

Редактор поддерживает автодополнение. Начните вводить context, axios или moment, и появятся подсказки с описанием доступных свойств и методов.

Вкладка «Параметры»

На вкладке Параметры вы описываете входные параметры функции в формате JSON Schema. Это описание используется:

  • Агентом для понимания, какие аргументы передать при вызове функции как инструмента
  • Панелью тестирования для автогенерации примера входных данных

Пример JSON Schema:

{
"type": "object",
"properties": {
"phone": {
"type": "string",
"description": "Номер телефона клиента"
},
"name": {
"type": "string",
"description": "Имя клиента"
},
"amount": {
"type": "number",
"description": "Сумма заказа"
}
},
"required": ["phone"]
}
Автогенерация параметров

Перейдите на вкладку «Параметры», введите описание в поле AI-генерации и нажмите «Генерировать». AI проанализирует код функции и создаст JSON Schema автоматически. После этого можно протестировать функцию с автогенерированными примерами.

Возвращаемое значение

Функция должна вернуть результат через return. Результат будет доступен как lastOutput для следующей ноды в рабочем процессе или как ответ инструмента для агента.

async function execute(context, axios, moment, console) {
const { phone } = context.input;

const response = await axios.get(`https://api.example.com/clients?phone=${phone}`);

return {
client_id: response.data.id,
name: response.data.name,
status: "found"
};
}

Если функция не возвращает значение явно, результат будет undefined.