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

Создание Workflows

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

Быстрый старт

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

Структура Workflow

Каждый 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" }
}

Разделяйте ответственность: action узлы ВЫПОЛНЯЮТ работу, check узлы ТОЛЬКО проверяют, fix узлы ТОЛЬКО исправляют. Используйте счетчики итераций для предотвращения бесконечных циклов.

Ветвление по типу действия

Используйте когда 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"
}
}

Gate подтверждения

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

{
"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"
}
}

Паттерны Input Schema

Ответ Да/Нет

{
"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
}
]
}
}

Объявление знаний как глобальных переменных

Объявляйте переиспользуемые знания как глобальные переменные в variableRegistry workflow со значением default:

{
"variableRegistry": {
"quality_rules": {
"type": "string",
"description": "Переиспользуемые правила качества, применяемые агентом на шагах",
"default": "Правило 1: ... Правило 2: ..."
},
"validation_checklist": {
"type": "string",
"description": "Чеклист, который агент проверяет перед завершением шага",
"default": "Проверка 1: ... Проверка 2: ..."
}
}
}

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

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

Это делает workflow самодокументируемым. Все знания встроены, внешняя документация не нужна.

Чеклист валидации

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

  1. Структура

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

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

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

    • true и false connections определены
    • Оператор валиден

Сохранение Workflows

Создать новый

mcp__moira__manage({
action: "create",
workflow: {
id: "my-workflow",
metadata: { name: "...", version: "1.0.0", description: "..." },
nodes: [...]
}
})

Редактировать

mcp__moira__manage({
action: "edit",
workflowId: "my-workflow",
changes: {
metadata: { version: "1.1.0" },
updateNodes: [{ nodeId: "step-1", changes: { directive: "Новый текст" } }],
},
});

Загрузить файл

// Для агентов с доступом к файловой системе
const { uploadUrl } = await mcp__moira__token({ action: "upload" });
// Загрузите JSON файл по uploadUrl

Определение возможностей агента

Разные агенты имеют разные возможности. Проектируйте 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"
}
}

Всегда предоставляйте fallback пути для агентов с ограниченными возможностями. MCP tools доступны всегда.

Паттерн планирования

Используйте когда 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. variableRegistry.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, не “в соответствующей папке”
Избыточность там, где применимаПовторяйте то, что пункту действительно нужно«Сделать коммит» — в пункте, который коммитом заканчивается, а не в каждом

Атомарность пункта (S9)

Каждый пункт плана или задачи должен быть самодостаточным. Агент часто получает директиву ОДНОГО пункта изолированно — без остального плана — поэтому пункт должен нести всё необходимое для его выполнения:

  • Повторить нюансы, от которых зависит пункт.
  • Повторить релевантное исходное требование внутри пункта.
  • Повторить сквозное действие (отчёт о прогрессе, запуск тестов, коммит) в тех пунктах, которым оно действительно нужно.

Общего или глобального scope между пунктами нет, поэтому пункт, опирающийся на «как выше», невыполним. Ответ — то, что нужно исполнителю в одиночестве, и решается это по каждому пункту, а не безусловным дублированием: повторённый текст становится вторым источником правды и расходится, а пункт, обязанный нести всё, разрастается, пока не начинает нести сам результат. См. Обязательная избыточность каждого пункта плана.

{
"id": "execute-plan-item",
"type": "agent-directive",
"directive": "Execute one plan item.\n\nThe item text is self-contained: it states the original requirement, the files to read/modify with full paths, and the cross-cutting actions (run tests, make commit) that apply to THIS item.\n\nDo NOT assume any context from other items.",
"completionCondition": "Item executed, tests run, and commit made as stated in the item",
"connections": { "success": "next-item" }
}

Два связанных паттерна развивают это руководство: паттерн Replan пересматривает многошаговый план по ходу выполнения, а паттерн Completeness Self-Review проверяет каждое требование против реального артефакта перед выдачей.

Пример реализации

{
"variableRegistry": {
"plan_writing_requirements": {
"type": "string",
"description": "Правила, которым агент следует при написании плана",
"default": "ТРЕБОВАНИЯ К НАПИСАНИЮ ПЛАНА:\n\n- Плоский линейный список — без иерархии, без вложенных подпунктов, просто 1, 2, 3...\n- Один пункт = одна задача — не микрошаг ('скачать файл'), а законченная задача ('обновить workflow до v2.1.0')\n- Полная самодостаточность — содержит ВСЁ для выполнения: зачем делать, что делать, какие файлы читать/менять, куда сохранять, что коммитить\n- НЕТ отдельных 'глобальных правил' — всё нужное для пункта должно быть В пункте\n- Явные действия — не 'как обычно', а конкретно: 'загрузить на moira-local через token', 'сделать коммит'\n- Избыточность разрешена — лучше повторить 'сделать коммит' в каждом пункте, чем забыть\n- Полные пути к файлам — не 'в соответствующей папке', а /full/path/to/file.json\n- Измеримый результат — легко проверить выполнение пункта"
}
},
"nodes": [
{
"type": "start",
"id": "start",
"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": "Представь план и спроси, утверждён ли он. `approved` записывает собственный ответ пользователя; без ответа записывать нечего.",
"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" }
}

Храните план в файле (например, ./plan.md) вместо контекста для больших планов. Это предотвращает переполнение контекста и позволяет восстановиться после архивации сессии.

Паттерн эскалации

Используйте когда 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 с планированием (robust-task, 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": "user-notification",
"message": "⚠️ *Требуется эскалация*\n\nШаг не удался после {{step_retry}} попыток.\n\nВарианты:\n- revise_plan\n- ask_user\n- skip",
"format": "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`decision` записывает собственный выбор пользователя: каждый из трёх вариантов ведёт в своё место, поэтому предположенный ответ выбирает маршрут за него.",
"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, позволяя агенту пересмотреть подход на основе того, что он узнал из неудач.

Устанавливайте max_retries в зависимости от сложности задачи. Простые задачи: 2-3 повтора. Сложные задачи: 3-5 повторов. Всегда предоставляйте вариант skip для некритичных шагов.

Production паттерны

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

Express/Full Mode ветвление

Маршрутизация в упрощённый или полный 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 валидации: Агенты склонны к оптимизму. На вопрос “Результат валиден? да/нет” они могут ответить “да” даже когда нашли проблемы. Это обесценивает validation loops.

Решение: Используйте числовой счётчик проблем вместо 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": "user-notification",
"message": "🚀 *Шаг {{current_step}}/{{total_steps}}*\n\n{{current_step_description}}",
"format": "markdown",
"connections": {
"default": "execute-step",
"error": "execute-step"
}
}

Паттерн: Требуется ввод пользователя

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

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

Паттерн: Оповещение об эскалации

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

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

Паттерн: Итоговое уведомление

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

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

Укажите для connections.error ту же цель, что и для default, если сбой уведомления не должен блокировать workflow. Без error connection полный сбой уже продолжает выполнение через default с явным результатом all_failed.

Для очень долгих задач (часы) рассмотрите периодические heartbeat уведомления “всё ещё работаю”, чтобы пользователь знал что процесс жив.

Паттерны файловой персистенции

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

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

Эти паттерны работают только для агентов с файловым доступом. Предусмотрите fallback для MCP-only агентов.

Структура директорий

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

./{{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": "Представь план и спроси, утверждён ли он. `approved` записывает собственный ответ пользователя: без ответа записывать нечего, а предположенный ответ уводит прогон в ветку, которую пользователь не выбирал. `user_response_text` — этот ответ дословно.",
"completionCondition": "Пользователь явно ответил да или нет (не предполагаемый ответ)",
"inputSchema": {
"properties": {
"approved": { "type": "string", "enum": ["да", "нет"] },
"user_response_text": {
"type": "string",
"description": "Точный текст ответа пользователя"
}
},
"required": ["approved", "user_response_text"]
}
}

Ключевые техники

  1. Скажите, чей ответ записывает полеapproved хранит ответ пользователя, поэтому без ответа записывать туда нечего
  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": "План показан выше. Спроси, утверждён ли он. `approved` записывает собственный ответ пользователя; предположенный ответ уводит прогон в ветку, которую он не выбирал.",
"inputSchema": {
"properties": {
"approved": { "type": "string", "enum": ["да", "нет"] }
}
},
"connections": { "success": "route-approval" }
}

Это известное ограничение workflows выполняемых агентами. Всегда тестируйте ноды с пользовательским вводом вручную перед деплоем.

Лучшие практики

  1. Один узел = одна ответственность — не смешивайте проверку и исправление
  2. Ясные директивы — начинайте с глагола: Создай, Проверь, Исправь
  3. Явные отрицания — “НЕ исправляй, ТОЛЬКО проверь”
  4. Используйте inputSchema — всегда определяйте ожидаемую структуру ответа
  5. Числовая валидация — используйте счётчики вместо да/нет для точных проверок
  6. Счетчики итераций — предотвращайте бесконечные циклы
  7. Gate подтверждения — для критических действий
  8. Самодокументирование — объявляйте знания как значения по умолчанию в variableRegistry
  9. Graceful уведомления — ошибки каналов связи не должны блокировать workflow

Связанное