Sub-workflows как tools для AI Agent в n8n: контракт, безопасность и production-паттерн ¶
Обновлено: 2026-05-29
Короткий ответ ¶
Sub-workflows как tools — лучший способ дать AI Agent в n8n доступ к реальным действиям, не превращая агента в бесконтрольный API-клиент. Каждый tool должен быть отдельным workflow с узким назначением, строгой input schema, проверкой прав, идемпотентностью, нормализованным output, логами и понятными ошибками. Агент выбирает tool, но tool сам проверяет, можно ли выполнить действие. Это главное различие между production-автоматизацией и небезопасным “пусть модель вызовет API”.
Зачем выносить tools в sub-workflows ¶
AI Agent хорошо выбирает действие по смыслу, но плохо подходит для хранения бизнес-правил. Если подключить к нему общий HTTP Request Tool с доступом к CRM, агент может сформировать опасный запрос. Sub-workflow позволяет спрятать сложность: агент видит простой инструмент create_support_ticket, а внутри workflow есть validation, mapping, credentials, retry, idempotency и error handling.
Преимущества:
- один tool можно использовать в нескольких агентах;
- credentials не раскрываются агенту;
- вход и выход стандартизированы;
- можно тестировать tool отдельно;
- проще включить human approval;
- проще логировать и откатывать действия.
Правило одного действия ¶
Один sub-workflow tool должен делать одну бизнес-операцию. Не создавайте tool “работа с CRM”. Создайте:
find_customer_by_email;create_lead;update_deal_stage;create_support_ticket;get_order_status;draft_customer_reply.
Чем уже tool, тем проще написать schema и проверить права. Агенту легче выбрать правильный tool, а ревьюеру легче понять последствия.
Input schema ¶
Tool должен принимать только нужные поля. Не передавайте в sub-workflow весь chat history или весь email thread. До tool должен дойти очищенный, минимальный, валидируемый JSON.
{
"customer_email": "client@example.com",
"subject": "Webhook перестал принимать заявки",
"description": "После обновления n8n production URL возвращает 404.",
"priority": "high",
"source": "telegram",
"trace_id": "tg_777000_20260529",
"idempotency_key": "ticket_tg_777000_20260529_001"
}
Минимально проверяйте:
- required fields;
- типы данных;
- enum values;
- длину текста;
- отсутствие secrets;
- роль вызывающего;
- idempotency key.
Если вход невалиден, tool должен вернуть structured error, а не падать.
Output contract ¶
Хороший output одинаков для успешных и неуспешных операций.
{
"status": "success",
"action": "create_support_ticket",
"object_id": "T-2042",
"human_message": "Тикет T-2042 создан и назначен в technical_support.",
"retryable": false,
"audit_id": "audit_91a",
"errors": []
}
Для ошибки:
{
"status": "error",
"action": "create_support_ticket",
"error_code": "CUSTOMER_NOT_FOUND",
"human_message": "Не удалось найти клиента по email. Запросите email или создайте лид.",
"retryable": false,
"audit_id": "audit_91b"
}
Не возвращайте агенту сырой HTML, stack trace, токены, внутренние URL админки и длинные API-ответы. Сырые данные можно хранить в логах с redaction, но Agent должен получить нормализованный результат.
Idempotency ¶
AI workflow может повторить tool call из-за retry, timeout, повторного сообщения или ошибки сети. Без идемпотентности вы получите дубли тикетов, лидов, писем или платежных действий.
Паттерн:
- Сформировать
idempotency_keyиз business context. - Проверить таблицу
tool_runsдо действия. - Если ключ уже был выполнен успешно, вернуть прежний result.
- Если был failure и retryable=true, повторить безопасно.
- Если действие создаёт внешний объект, сохранить external_id.
Пример таблицы:
create table tool_runs (
id bigserial primary key,
idempotency_key text unique not null,
tool_name text not null,
input_hash text not null,
status text not null,
external_id text,
result_json jsonb,
created_at timestamptz default now()
);
Permissions ¶
Agent не должен решать права. Он может передать user_role, но sub-workflow должен сам проверить, разрешена ли операция. Например, viewer может читать статус заказа, но не менять сделку. operator может создавать тикет, но не делать возврат. admin может подтверждать опасные действия, но всё равно через audit.
Схема проверки:
const role = $json.user_role;
const action = 'update_deal_stage';
const allowed = {
viewer: ['get_order_status'],
operator: ['get_order_status', 'create_support_ticket'],
manager: ['get_order_status', 'create_support_ticket', 'update_deal_stage']
};
if (!allowed[role]?.includes(action)) {
return [{ json: { status: 'error', error_code: 'PERMISSION_DENIED', retryable: false } }];
}
return [{ json: $json }];
Human approval для tools ¶
Sub-workflow и approval хорошо сочетаются. Agent предлагает tool call, policy checker решает, нужен ли approval, ревьюер видит arguments, после approve запускается sub-workflow. Для опасных tools не давайте агенту прямой обход approval.
Approval нужен для:
- create/update/delete во внешних системах;
- отправки email/SMS/Telegram клиенту;
- финансовых операций;
- изменений access rights;
- массовых действий;
- обработки sensitive data.
Logging и observability ¶
Логируйте каждый tool call:
trace_id;parent_execution_id;tool_execution_id;tool_name;input_hash;user_id/role;idempotency_key;status;error_code;external_id;duration_ms;approval_id.
Для AI tools особенно важно связывать parent execution и sub-execution. Иначе при инциденте вы не поймёте, какой пользовательский запрос создал внешний объект.
Тестирование sub-workflow tool ¶
Тестируйте tool как API:
- happy path;
- missing required fields;
- invalid enum;
- permission denied;
- duplicate idempotency key;
- API timeout;
- API 429;
- downstream 500;
- partial success;
- human approval denied;
- sensitive input.
Для каждого теста проверьте не только result, но и отсутствие side effects. Например, повторный запрос не должен создать второй тикет.
Типовые ошибки ¶
- Tool принимает произвольный JSON без schema.
- Tool называется слишком широко:
crm_tool,api_tool,database_tool. - Agent получает credentials или raw API details.
- Нет idempotency, появляются дубли.
- Ошибки возвращаются как stack trace.
- Sub-workflow не проверяет права, доверяет prompt.
- Output отличается от запуска к запуску.
- Нет связи parent execution → sub-execution.