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

Создание Workflows

Это руководство охватывает создание workflow с нуля, включая типовые паттерны, циклы валидации и лучшие практики.

  1. Определите цель workflow и основные этапы 2. Спроектируйте структуру графа узлов 3. Создайте JSON с правильными определениями узлов 4. Проверьте связи и достижимость 5. Сохраните через MCP tools

Каждый workflow должен содержать:

{
"id": "my-workflow",
"metadata": {
"name": "Читаемое название",
"version": "1.0.0",
"description": "Что делает этот workflow"
},
"nodes": [
// start узел (ровно один)
// action узлы
// condition узлы (для ветвления)
// end узел (минимум один)
]
}

Используйте когда нужно проверить результат и повторить при ошибке:

flowchart LR
    A[action] --> B[check]
    B -->|success| C[next]
    B -->|failure| D[fix]
    D --> E[increment-iteration]
    E --> A
{
"id": "do-work",
"type": "agent-directive",
"directive": "Выполни задачу",
"completionCondition": "Задача выполнена",
"inputSchema": {
"type": "object",
"properties": {
"result_valid": { "type": "string", "enum": ["yes", "no"] }
},
"required": ["result_valid"]
},
"connections": { "success": "check-result" }
},
{
"id": "check-result",
"type": "condition",
"condition": {
"operator": "eq",
"left": { "contextPath": "result_valid" },
"right": "yes"
},
"connections": {
"true": "next-step",
"false": "fix-issues"
}
},
{
"id": "fix-issues",
"type": "agent-directive",
"directive": "Исправь найденные проблемы",
"connections": { "success": "increment-iteration" }
},
{
"id": "increment-iteration",
"type": "agent-directive",
"directive": "Увеличь счетчик итераций",
"inputSchema": {
"type": "object",
"properties": {
"iteration": { "type": "number" }
},
"required": ["iteration"]
},
"connections": { "success": "do-work" }
}

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

{
"id": "get-action",
"type": "agent-directive",
"directive": "Спроси пользователя: создать новый или редактировать существующий?",
"inputSchema": {
"properties": {
"action": { "type": "string", "enum": ["create", "edit"] }
},
"required": ["action"]
},
"connections": { "success": "route-action" }
},
{
"id": "route-action",
"type": "condition",
"condition": {
"operator": "eq",
"left": { "contextPath": "action" },
"right": "create"
},
"connections": {
"true": "create-branch",
"false": "edit-branch"
}
}

Используйте для критических действий требующих подтверждения:

{
"id": "show-plan",
"type": "agent-directive",
"directive": "Представь план пользователю и спроси подтверждение",
"inputSchema": {
"properties": {
"approved": { "type": "string", "enum": ["yes", "no"] },
"feedback": { "type": "string" }
},
"required": ["approved"]
},
"connections": { "success": "check-approval" }
},
{
"id": "check-approval",
"type": "condition",
"condition": {
"operator": "eq",
"left": { "contextPath": "approved" },
"right": "yes"
},
"connections": {
"true": "proceed",
"false": "revise-plan"
}
}
{
"inputSchema": {
"type": "object",
"properties": {
"result": { "type": "string", "enum": ["yes", "no"] },
"details": { "type": "string" }
},
"required": ["result"]
}
}
{
"inputSchema": {
"type": "object",
"properties": {
"count": { "type": "number", "minimum": 0 },
"total": { "type": "number", "minimum": 1 }
},
"required": ["count", "total"]
}
}
{
"inputSchema": {
"type": "object",
"properties": {
"items": {
"type": "array",
"items": { "type": "string" },
"minItems": 1
}
},
"required": ["items"]
}
}
{
"inputSchema": {
"type": "object",
"properties": {
"action": {
"type": "string",
"enum": ["create", "edit", "delete", "cancel"]
},
"reason": { "type": "string" }
},
"required": ["action"]
}
}
{
"inputSchema": {
"type": "object",
"properties": {
"file_path": {
"type": "string",
"pattern": "^[a-zA-Z0-9/_.-]+\\.(json|yaml|yml)$"
}
},
"required": ["file_path"]
}
}
ОператорОписаниеПример
eqРавно"right": "value"
neqНе равно"right": "value"
ltМеньше"right": 10
gtБольше"right": 0
lteМеньше или равно"right": 100
gteБольше или равно"right": 1
andЛогическое И"conditions": [...]
orЛогическое ИЛИ"conditions": [...]
existsПеременная существует
isEmptyМассив/строка пусты
{
"condition": {
"operator": "and",
"conditions": [
{
"operator": "eq",
"left": { "contextPath": "status" },
"right": "ready"
},
{
"operator": "gt",
"left": { "contextPath": "count" },
"right": 0
}
]
}
}

Храните переиспользуемые знания в start узле:

{
"type": "start",
"id": "start",
"initialData": {
"quality_rules": "Правило 1: ... Правило 2: ...",
"validation_checklist": "Проверка 1: ... Проверка 2: ..."
},
"connections": { "default": "first-step" }
}

Обращение в директивах:

{
"directive": "Следуй этим правилам: {{quality_rules}}"
}

Перед сохранением проверьте:

  1. Структура

    • Ровно один start узел
    • Минимум один end узел
    • Все ID узлов уникальны
    • Все connections указывают на существующие узлы
  2. Достижимость

    • Все узлы достижимы от start
    • Нет orphan узлов
    • Все пути ведут к end
  3. Определения узлов

    • directive не пустой
    • completionCondition определен
    • connections.success указан
    • inputSchema — валидный JSON Schema
  4. Условия

    • true и false connections определены
    • Оператор валиден
mcp__moira__manage({
action: "create",
workflow: {
id: "my-workflow",
metadata: { name: "...", version: "1.0.0", description: "..." },
nodes: [...]
}
})

Разные агенты имеют разные возможности. Проектируйте workflow для определения и адаптации:

ВозможностьПримерыМетод определения
Файловая системаRead, Write, создание файловСпросить агента
Web доступFetch URL, поискПроверить наличие web tools
Только MCPТолько инструменты MoiraПредположение по умолчанию
{
"id": "detect-capabilities",
"type": "agent-directive",
"directive": "Сообщи свои возможности: есть ли доступ к файловой системе? можешь ли получать URL?",
"inputSchema": {
"type": "object",
"properties": {
"has_file_access": { "type": "boolean" },
"has_web_access": { "type": "boolean" }
},
"required": ["has_file_access", "has_web_access"]
},
"connections": { "success": "route-by-capabilities" }
}
{
"id": "route-by-capabilities",
"type": "condition",
"condition": {
"operator": "eq",
"left": { "contextPath": "has_file_access" },
"right": true
},
"connections": {
"true": "file-based-flow",
"false": "mcp-only-flow"
}
}

Используйте когда workflow должен создать и выполнить план с подтверждением пользователя и возможностью пересмотра.

Workflow часто требуют этапа планирования, но:

  • Планы создаются один раз и не пересматриваются
  • Требования к качеству плана не формализованы
  • Нет возможности адаптировать план по ходу выполнения
  • Агент теряет контекст в длинных сессиях
flowchart LR
    A[understand_task] --> B[decompose_into_steps]
    B --> C[present_plan]
    C --> D{user_approval}
    D -->|approved| E[execute_steps]
    D -->|rejected| F[revise_plan]
    F --> C
    E -->|during_execution| G[update_plan]
    G --> H[reinitialize]
  1. initialData.plan_writing_requirements — правила написания планов (агент видит при создании)
  2. decompose_into_steps — директива с {{plan_writing_requirements}} для создания плана
  3. user_approval_branch — approved → execute, rejected → revise_plan → present_plan
  4. update_during_execution — возможность адаптировать план по ходу выполнения

Главная проблема: агент теряет контекст (архивация сессии, простое забывание). Планы должны быть написаны так, чтобы любой шаг можно было выдать агенту БЕЗ остального плана — и он смог бы выполнить.

ТребованиеПочему важноПример
Плоский линейный списокЛегче отслеживать без иерархии1, 2, 3… не 1.1, 1.2, 1.2.1
Самодостаточные шагиЛюбой шаг выполним без контекста”Шаг 3: Создать файл X.ts с функцией Y” не “Продолжить работу”
Явные действияНе “как обычно”, а конкретно”Сделать коммит” в каждом шаге где нужен
Измеримый результатЛегко проверить выполнение”expected_output: файл X.ts создан и содержит функцию Y”
НезависимостьМинимум зависимостей между шагамиШаг 4 не должен требовать знания деталей шага 2
Полные пути к файламАгент не должен угадывать/full/path/to/file.json, не “в соответствующей папке”
Избыточность разрешенаЛучше повторить чем забытьПовторять “сделать коммит” в каждом шаге если нужно
{
"type": "start",
"id": "start",
"initialData": {
"plan_writing_requirements": "ТРЕБОВАНИЯ К НАПИСАНИЮ ПЛАНА:\n\n- Плоский линейный список — без иерархии, без вложенных подпунктов, просто 1, 2, 3...\n- Один пункт = одна задача — не микрошаг ('скачать файл'), а законченная задача ('обновить workflow до v2.1.0')\n- Полная самодостаточность — содержит ВСЁ для выполнения: зачем делать, что делать, какие файлы читать/менять, куда сохранять, что коммитить\n- НЕТ отдельных 'глобальных правил' — всё нужное для пункта должно быть В пункте\n- Явные действия — не 'как обычно', а конкретно: 'загрузить на moira-local через token', 'сделать коммит'\n- Избыточность разрешена — лучше повторить 'сделать коммит' в каждом пункте, чем забыть\n- Полные пути к файлам — не 'в соответствующей папке', а /full/path/to/file.json\n- Измеримый результат — легко проверить выполнение пункта"
},
"connections": { "default": "analyze-task" }
},
{
"id": "decompose-into-steps",
"type": "agent-directive",
"directive": "Создай план выполнения задачи.\n\nСледуй требованиям: {{plan_writing_requirements}}\n\nДля каждого шага укажи:\n- Что делать (action)\n- Ожидаемый результат (expected_output)",
"inputSchema": {
"type": "object",
"properties": {
"steps": {
"type": "array",
"items": {
"type": "object",
"properties": {
"action": { "type": "string" },
"expected_output": { "type": "string" }
},
"required": ["action", "expected_output"]
}
}
},
"required": ["steps"]
},
"connections": { "success": "present-plan" }
},
{
"id": "present-plan",
"type": "agent-directive",
"directive": "Покажи план пользователю.\n\n⚠️ ОБЯЗАТЕЛЬНО: ДОЖДИСЬ ОТВЕТА ПОЛЬЗОВАТЕЛЯ\n- Спроси: 'Подтверждаете план? (да/нет)'\n- ОСТАНОВИСЬ и жди пока пользователь напишет ответ",
"inputSchema": {
"type": "object",
"properties": {
"plan_approved": { "type": "string", "enum": ["да", "нет"] },
"user_feedback": { "type": "string" }
},
"required": ["plan_approved"]
},
"connections": { "success": "check-plan-approval" }
},
{
"id": "check-plan-approval",
"type": "condition",
"condition": {
"operator": "eq",
"left": { "contextPath": "plan_approved" },
"right": "да"
},
"connections": {
"true": "execute-steps",
"false": "revise-plan"
}
},
{
"id": "revise-plan",
"type": "agent-directive",
"directive": "Пользователь не одобрил план. Фидбек: {{user_feedback}}\n\nДоработай план на основе фидбека.\nСледуй: {{plan_writing_requirements}}",
"connections": { "success": "present-plan" }
}

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

{
"id": "update-plan-during-execution",
"type": "agent-directive",
"directive": "Обнови план по ходу выполнения.\n\nТекущий шаг: {{current_step_index}}\nПричина обновления: {{update_reason}}\n\n1. Проанализируй текущий прогресс\n2. Обнови оставшиеся шаги (не меняй завершённые)\n3. Сохрани историю в ./plan-changes-history.md\n\nСледуй: {{plan_writing_requirements}}",
"connections": { "success": "reinitialize-tracking" }
},
{
"id": "reinitialize-tracking",
"type": "agent-directive",
"directive": "Реинициализируй tracking после обновления плана.\n\n1. Обнови tracking.json с новым total_steps\n2. Скорректируй current_step_index если нужно\n3. Продолжи выполнение",
"connections": { "success": "execute-current-step" }
}

Используйте когда workflow имеет циклы валидации которые могут застрять. Предоставляет механизм выхода после повторных неудач.

Когда агент застревает в цикле валидации:

  • Бесконечные повторы без прогресса
  • Одни и те же ошибки повторяются
  • Нет способа выйти из цикла
  • Пользователь ждёт бесконечно
flowchart TD
    A[action] --> B[validate]
    B -->|fail| C[increment_retry]
    C --> D{check_retry_limit}
    D -->|retry < max| A
    D -->|retry >= max| E[ESCALATION]
    E --> F[revise_plan / ask_user / skip]
  • Workflows с планированием (task-breakdown, development)
  • Workflows с валидацией результатов (workflow-management, test-generation)
  • Любые циклы “сделай → проверь → повтори”
ВариантКогда использоватьПример
revise_planТекущий план неверенТесты падают из-за неправильного дизайна
ask_userНужно решение человекаНеясные требования
skipШаг не критиченОпциональное улучшение
{
"id": "increment-retry",
"type": "agent-directive",
"directive": "Увеличь счётчик повторов. Текущий: {{step_retry}}",
"inputSchema": {
"type": "object",
"properties": {
"step_retry": { "type": "number", "minimum": 1 }
},
"required": ["step_retry"]
},
"connections": { "success": "check-retry-limit" }
},
{
"id": "check-retry-limit",
"type": "condition",
"condition": {
"operator": "gte",
"left": { "contextPath": "step_retry" },
"right": 3
},
"connections": {
"true": "notify-escalation",
"false": "retry-action"
}
},
{
"id": "notify-escalation",
"type": "telegram-notification",
"message": "⚠️ *Требуется эскалация*\n\nШаг не удался после {{step_retry}} попыток.\n\nВарианты:\n- revise_plan\n- ask_user\n- skip",
"parseMode": "Markdown",
"connections": { "default": "ask-escalation-decision", "error": "ask-escalation-decision" }
},
{
"id": "ask-escalation-decision",
"type": "agent-directive",
"directive": "Шаг не удался после {{step_retry}} попыток.\n\nСпроси пользователя:\n1. **revise_plan** — вернуться к планированию и пересмотреть подход\n2. **ask_user** — запросить помощь человека с конкретной проблемой\n3. **skip** — пропустить этот шаг и продолжить\n\n⚠️ ДОЖДИСЬ ОТВЕТА ПОЛЬЗОВАТЕЛЯ",
"inputSchema": {
"type": "object",
"properties": {
"escalation_decision": {
"type": "string",
"enum": ["revise_plan", "ask_user", "skip"]
},
"user_input": { "type": "string" }
},
"required": ["escalation_decision"]
},
"connections": { "success": "route-escalation" }
},
{
"id": "route-escalation",
"type": "condition",
"condition": {
"operator": "eq",
"left": { "contextPath": "escalation_decision" },
"right": "revise_plan"
},
"connections": {
"true": "revise-plan",
"false": "route-escalation-skip"
}
},
{
"id": "route-escalation-skip",
"type": "condition",
"condition": {
"operator": "eq",
"left": { "contextPath": "escalation_decision" },
"right": "skip"
},
"connections": {
"true": "mark-step-skipped",
"false": "handle-user-help"
}
}

При использовании обоих паттернов — Planning и Escalation:

flowchart LR
    A[plan] --> B[execute]
    B --> C[validate]
    C -->|fail| D[retry]
    D -->|max_retries| E[escalate]
    E -->|revise_plan| A
    E -->|skip| F[next_step]
    E -->|ask_user| G[wait_for_input]

Вариант revise_plan возвращает к фазе Planning, позволяя агенту пересмотреть подход на основе того, что он узнал из неудач.

Реальные паттерны из production workflows (development-flow, 104 ноды).

Маршрутизация в упрощённый или полный flow в зависимости от сложности:

[get-requirements] → [check-mode] → express=true → [express-flow]
→ express=false → [full-flow]
{
"id": "check-development-mode",
"type": "condition",
"condition": {
"operator": "eq",
"left": { "contextPath": "development_mode" },
"right": "express"
},
"connections": {
"true": "express-implementation",
"false": "analyze-and-plan"
}
}

Представить план → получить фидбек → уточнить → подтвердить:

[present-plan] → [check-approval] → approved → [continue]
→ rejected → [refine] → [confirm] → [continue]

Паттерн числовой валидации (рекомендуемый)

Заголовок раздела «Паттерн числовой валидации (рекомендуемый)»

Решение: Используйте числовой счётчик проблем вместо boolean. Движок механически проверяет равен ли счётчик нулю — нет места для интерпретации.

{
"id": "validate-result",
"type": "agent-directive",
"directive": "ТОЛЬКО ПРОВЕРЬ результат. Подсчитай найденные проблемы.",
"inputSchema": {
"type": "object",
"properties": {
"issues_count": {
"type": "number",
"minimum": 0,
"description": "Количество найденных проблем (0 = валидно)"
},
"issues": {
"type": "array",
"items": { "type": "string" },
"description": "Список проблем если есть"
}
},
"required": ["issues_count"]
},
"connections": { "success": "route-validation" }
},
{
"id": "route-validation",
"type": "condition",
"condition": {
"operator": "eq",
"left": { "contextPath": "issues_count" },
"right": 0
},
"connections": {
"true": "next-step",
"false": "fix-issues"
}
}

Почему это работает:

  • Агент не может соврать про число (количество объективно)
  • Условие issues_count == 0 проверяется механически движком
  • Нет места для “почти готово” или “незначительные проблемы”

Когда использовать: ВСЕ validation loops должны использовать этот паттерн. Замените существующие is_valid: enum["да","нет"] на issues_count: number.

Числовая валидация с результатами тестов

Заголовок раздела «Числовая валидация с результатами тестов»

Валидация с числовыми проверками вместо да/нет:

{
"id": "run-tests",
"type": "agent-directive",
"directive": "Запусти тесты и сообщи результаты",
"inputSchema": {
"type": "object",
"properties": {
"tests_passed": { "type": "number" },
"tests_failed": { "type": "number" }
},
"required": ["tests_passed", "tests_failed"]
},
"connections": { "success": "check-tests" }
},
{
"id": "check-tests",
"type": "condition",
"condition": {
"operator": "eq",
"left": { "contextPath": "tests_failed" },
"right": 0
},
"connections": {
"true": "continue",
"false": "fix-tests"
}
}

Уведомления держат пользователя в курсе во время долгих workflows. Используйте их стратегически — слишком много уведомлений становятся шумом.

СценарийЗачем уведомлять
Начало этапа (долгие задачи)Пользователь видит прогресс, может планировать время
Требуется ввод пользователяПользователь знает что нужно ответить
Критические ошибкиНемедленное информирование о блокерах
Завершение задачиПользователь может проверить результаты
  • Короткие workflows (< 5 минут)
  • Между каждым мелким шагом
  • Для внутренних validation loops
  • Когда ошибка авто-восстанавливаема

Для многошаговых задач уведомляйте на каждом крупном этапе:

{
"id": "notify-step-start",
"type": "telegram-notification",
"message": "🚀 *Шаг {{current_step}}/{{total_steps}}*\n\n{{current_step_description}}",
"parseMode": "Markdown",
"connections": {
"default": "execute-step",
"error": "execute-step"
}
}

Оповещение когда workflow заблокирован ожиданием пользователя:

{
"id": "notify-approval-needed",
"type": "telegram-notification",
"message": "⏳ *Ожидаю подтверждения*\n\nПлан готов к ревью. Подтвердите для продолжения.",
"parseMode": "Markdown",
"connections": {
"default": "present-plan-to-user",
"error": "present-plan-to-user"
}
}

Когда автоматические retry не помогли и нужно решение человека:

{
"id": "notify-escalation",
"type": "telegram-notification",
"message": "⚠️ *Требуется действие*\n\nШаг {{current_step}} не удался после {{max_retries}} попыток.\n\nВарианты:\n- Пропустить этот шаг\n- Выполнить вручную",
"parseMode": "Markdown",
"connections": {
"default": "ask-user-decision",
"error": "ask-user-decision"
}
}

Уведомление при завершении задачи:

{
"id": "notify-completion",
"type": "telegram-notification",
"message": "✅ *Задача выполнена*\n\n{{task_name}}\n\nРезультат: {{deliverable_summary}}",
"parseMode": "Markdown",
"connections": {
"default": "end",
"error": "end"
}
}

Для агентов с доступом к файловой системе используйте файлы для:

  • Выгрузки больших данных из контекста workflow
  • Отслеживания прогресса выполнения между итерациями
  • Восстановления после прерываний
  • Создания аудит-лога

Используйте шаблоны для организации файлов:

./{{task_name}}/
├── process-id.txt # ID выполнения workflow
├── plan.md # Текущий план
├── step-{{step_index}}/
│ ├── iteration-{{iteration}}/
│ │ ├── result.md # Результат шага
│ │ └── artifacts/ # Сгенерированные файлы
│ └── summary.md # Сводка по шагу
└── final-report.md # Финальный отчёт

Сохраняйте process ID для восстановления:

{
"id": "save-process-id",
"type": "agent-directive",
"directive": "Сохрани process ID в ./{{task_name}}/process-id.txt для восстановления",
"completionCondition": "Файл создан с process ID",
"connections": { "success": "next-step" }
}

Сохраняйте результаты итераций в файлы вместо контекста:

{
"id": "save-iteration-result",
"type": "agent-directive",
"directive": "Сохрани результат итерации {{current_iteration}} в ./{{task_name}}/step-{{step_index}}/iteration-{{current_iteration}}/result.md",
"completionCondition": "Результат сохранён в файл",
"inputSchema": {
"type": "object",
"properties": {
"file_path": { "type": "string" }
},
"required": ["file_path"]
},
"connections": { "success": "next-iteration" }
}

Ссылайтесь на файлы вместо хранения больших данных в контексте:

{
"id": "analyze-with-file-reference",
"type": "agent-directive",
"directive": "Прочитай анализ из {{analysis_file_path}} и продолжи обработку",
"completionCondition": "Анализ загружен и обработан"
}

Когда использовать файловую персистенцию

Заголовок раздела «Когда использовать файловую персистенцию»
СценарийФайловый подходКонтекстный подход
Большой анализ кодаСохранить в файл, ссылатьсяНе рекомендуется
История итерацийСохранять каждую итерациюХранить только текущую
Данные восстановленияprocess-id.txt обязателенТеряется при прерывании
Аудит-логДописывать в лог-файлНедоступно
Маленькие флаги статусаОба подходаПроще

Проблема получения ответа от пользователя

Заголовок раздела «Проблема получения ответа от пользователя»

Когда workflow требует подтверждения от пользователя, агент может “оптимизировать” — заполнить поля inputSchema не дожидаясь реального ответа.

{
"id": "approve-plan",
"directive": "Покажи план пользователю. Спроси: 'Подтверждаете? (да/нет)'",
"inputSchema": {
"properties": {
"approved": { "type": "string", "enum": ["да", "нет"] }
}
}
}

Что происходит: Агент показывает план, затем сразу заполняет approved: "да" не дожидаясь ответа пользователя.

Почему: Агент видит что может заполнить поле самостоятельно и “оптимизирует” не останавливаясь.

Добавьте явные инструкции которые ЗАСТАВЛЯЮТ агента ждать:

{
"id": "approve-plan",
"directive": "Покажи план пользователю.\n\n⚠️ ОБЯЗАТЕЛЬНО: ДОЖДИСЬ ОТВЕТА ПОЛЬЗОВАТЕЛЯ\n- Спроси: 'Подтверждаете? (да/нет)'\n- ОСТАНОВИСЬ и жди пока пользователь напишет ответ\n- НЕ заполняй поле 'approved' пока пользователь явно не скажет да или нет\n- Показать информацию ≠ получить подтверждение",
"completionCondition": "Пользователь явно ответил да или нет (не предполагаемый ответ)",
"inputSchema": {
"properties": {
"approved": { "type": "string", "enum": ["да", "нет"] },
"user_response_text": {
"type": "string",
"description": "Точный текст ответа пользователя"
}
},
"required": ["approved", "user_response_text"]
}
}
  1. Добавить “ДОЖДИСЬ ОТВЕТА ПОЛЬЗОВАТЕЛЯ” — явная инструкция остановиться
  2. Требовать user_response_text — заставляет агента фиксировать реальный ответ
  3. Указать что НЕ делать — “НЕ заполняй поле пока…”
  4. Разъяснить разницу — “Показать ≠ получить подтверждение”

Для критичных подтверждений разделите на отдельные ноды:

[show-information] → [get-user-confirmation] → [route-decision]

Первая нода только показывает, вторая только получает ответ:

{
"id": "show-plan",
"directive": "Покажи план пользователю. Объясни каждый этап.",
"inputSchema": {
"properties": {
"plan_shown": { "type": "string", "enum": ["да"] }
}
},
"connections": { "success": "get-plan-approval" }
},
{
"id": "get-plan-approval",
"directive": "Пользователь видит план выше. Спроси: 'Подтверждаете план? (да/нет)'\n\nЖДИ ответа пользователя. НЕ продолжай пока пользователь не напишет да или нет.",
"inputSchema": {
"properties": {
"approved": { "type": "string", "enum": ["да", "нет"] }
}
},
"connections": { "success": "route-approval" }
}
  1. Один узел = одна ответственность — не смешивайте проверку и исправление
  2. Ясные директивы — начинайте с глагола: Создай, Проверь, Исправь
  3. Явные отрицания — “НЕ исправляй, ТОЛЬКО проверь”
  4. Используйте inputSchema — всегда определяйте ожидаемую структуру ответа
  5. Числовая валидация — используйте счётчики вместо да/нет для точных проверок
  6. Счетчики итераций — предотвращайте бесконечные циклы
  7. Gate подтверждения — для критических действий
  8. Самодокументирование — встраивайте знания в initialData
  9. Graceful уведомления — ошибки Telegram не должны блокировать workflow