Акты Становления

010: Агент/План

Контекстное сообщение, содержащее граф потока данных из Вызовов Инструментов, который представляет стратегию агента. Оно передается между шагами для обеспечения итеративного выполнения и адаптации.

Сообщение План — это основа итеративного выполнения. Когда LLM получает текущий План вместе с активным объектом Состояние, она может определить свою точную позицию в рабочем процессе. Эта ситуационная осведомленность позволяет агенту быть по-настоящему адаптивным. Он может следовать существующему плану, генерировать новые Вызовы Инструментов для его расширения или полностью отбросить его и перепланировать в ответ на неожиданные результаты. Это создает гибкую модель выполнения, которая может варьироваться от жесткой, предопределенной процедуры до гибкой, исследовательской стратегии.

Как формируется План

Связи в графе создаются не с помощью явных указателей, а через простую и мощную конвенцию потока данных с использованием объекта Состояние.

Это устанавливает четкую зависимость: второй Вызов Инструмента не может быть выполнен, пока первый не завершится и не заполнит Состояние.

Например, План для получения профиля пользователя и его последующего суммирования будет состоять из двух Вызовов Инструментов:

записывает в

считывается

state.user.profile

fetchUserProfile

summarizeProfile

[
  {
    "_tool": "fetchUserProfile",
    "userName": "Alice",
    "_outputPath": "†state.userProfileData"
  },
  {
    "_tool": "summarizeProfile",
    "profile": "†state.userProfileData",
    "_outputPath": "†state.profileSummary"
  }
]

Здесь вызов summarizeProfile зависит от результата fetchUserProfile, создавая двухэтапный план. Эту зависимость можно представить в виде простого графа.

Содержимое Плана: Граф потока данных

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

state.sunny

state.notSunny

state.suggestion

state.suggestion

Получить погоду

Солнечно?

Найти парк

Найти фильм

Предложить вариант

Содержимое сообщения План — это граф потока данных. Эта структура используется для представления стратегии агента в виде последовательности взаимосвязанных Вызовов Инструментов. Представляя рабочий процесс в виде графа, система может четко определить зависимости между шагами, где результат одного Вызова Инструмента становится входными данными для другого. Этот формат на основе графа обеспечивает ясную, машиночитаемую структуру, которую Цикл Выполнения агента может интерпретировать и выполнять.

Базовая структура графа не ограничивается только определением исполняемых рабочих процессов. Агент может получить запрос на создание графа из Вызовов Инструментов, который представляет что-то совершенно иное — визуализацию социальной сети, рабочий процесс GitHub Actions или схему базы данных.

Крайне важно отличать эти результаты от Плана. Хотя они используют ту же структуру графа, они не являются «планами» в архитектурном смысле, если только они не передаются в последующий Запрос как контекстное сообщение План с намерением быть выполненными. Это различие предотвращает путаницу между созданием представления существующей системы и созданием исполняемой стратегии.

Хотя содержимое Плана может быть мощным инструментом для мозгового штурма, обсуждения и «размышлений вслух», его основное применение в этой системе — определение исполняемых рабочих процессов. Для этой цели мы используем особый тип графа, называемый направленным ациклическим графом (DAG), где каждый узел — это Вызов Инструмента.

A DAG имеет несколько ключевых свойств, которые делают его идеальным для выполнения:

Для реализации итеративной логики, такой как цикл «for», используется шаблон вложенного, делегированного выполнения. Внешний План управляет состоянием цикла (например, счетчиком итераций), и на каждой итерации он вызывает подзапрос через Делегат. Этот подзапрос содержит свой собственный отдельный, ациклический План, который выполняет логику для одной итерации. Это гарантирует, что циклы создаются явно и безопасно.

  • Граф: «Граф» — это всё содержимое сообщения План — совокупность всех Вызовов Инструментов (узлов) и зависимостей по данным, которые их связывают (ребра).
  • Направленный: Связи односторонние, они определяются потоком данных. Шаг, который создает данные, должен предшествовать шагу, который их использует.
  • Ациклический: Рабочий процесс не может иметь циклических зависимостей, что обеспечивает ему четкое начало и конец. Это критически важная функция безопасности, предотвращающая создание LLM рабочего процесса с бесконечным циклом. Система проверяет План на ацикличность перед выполнением.

Разделение планирования и выполнения

Самая мощная особенность этой архитектуры — полное разделение планирования и выполнения. Поскольку План — это просто декларативная структура данных, агент может сгенерировать весь граф Вызовов Инструментов до запуска какого-либо кода.

LLM выступает в роли планировщика, собирая массив Вызовов, который представляет предполагаемый рабочий процесс. Эту структуру данных затем можно:

  • Проверить: Система может проверить граф на наличие циклических зависимостей или других структурных ошибок.
  • Сымитировать: Можно выполнить «пробный запуск», чтобы предвидеть поведение рабочего процесса.
  • Представить на утверждение: План можно показать человеку для проверки, изменения или утверждения перед выполнением, создавая критически важный уровень безопасности и взаимодействия.

Выполнение обрабатывается Циклом Выполнения, который интерпретирует сообщение План и запускает Вызовы Инструментов в правильном порядке на основе их зависимостей, заполняя объект Состояние по мере выполнения.

План как развивающаяся стратегия

План не статичен; это живая стратегия, которую можно адаптировать на каждом шаге цикла выполнения. Ключевое различие заключается в том, что Plan — это не просто какой-либо вывод от LLM. Когда агент впервые генерирует набор Вызовов Инструментов, это просто предлагаемая последовательность действий. Она становится настоящим Планом только тогда, когда передается как контекстное сообщение в следующий запрос в цикле.

Этот цикл превращает разовый результат в непрерывную стратегию:

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

Пример: Планирование на шаг вперед

Этот пример демонстрирует, как Plan обеспечивает структурный «оптимальный путь», который направляет агента, не позволяя ему отклоняться от стандартной процедуры даже при наличии других вероятных действий.

Сценарий: Агенту поддержки клиентов необходимо обработать возврат средств. Стандартная процедура, инициированная запросом пользователя, — сначала проверить историю платежей для получения контекста, а затем оформить возврат.

1. Исходный запрос

Цикл начинается с запроса клиента. На основе этого input, LLM формирует стандартный двухэтапный Plan для обработки возврата. Это представляет собой идеальный, наиболее распространенный рабочий процесс.

Контекст и схема для запроса

// Agent.Request(config, schema, context)
{
  "schema": {
    "type": "object",
    "properties": {
      "calls": { "type": "array" },
      "output": {
        "type": "object",
        "nullable": true,
        "properties": {
          "confirmationId": { "type": "string" },
          "message": { "type": "string" }
        }
      }
    }
  },
  "context": [
    {
      "type": "input",
      "request": "I'd like a refund for my last order.",
      "customerId": "cust_123",
      "amount": 50.0
    }
  ]
}

`solution` от LLM

{
  "calls": [
    {
      "_tool": "checkBillingHistory",
      "customerId": "†input.customerId"
    },
    {
      "_tool": "issueRefund",
      "customerId": "†input.customerId",
      "amount": "†input.amount"
    }
  ],
  "output": null
}

2. Следующий запрос в цикле

Цикл Выполнения выполняет вызов checkBillingHistory и заполняет Состояние. История выявляет некоторую сложность (например, предыдущий чарджбэк). В этот момент неуправляемый агент мог бы с полным основанием выбрать другой доступный инструмент, escalateToSupervisor.

Однако сообщение Plan в контексте обеспечивает необходимую структуру. Сопоставляя то, что он знает (сложное State), с тем, что он должен делать (Plan), LLM понимает свое точное положение в рабочем процессе и придерживается «оптимального пути».

Контекст

[
  {
    "type": "state",
    "billingHistory": {
      "orders": 5,
      "lastChargeback": "2025-09-10"
    }
  },
  {
    "type": "plan",
    "plan": [
      {
        "_tool": "checkBillingHistory",
        "customerId": "†input.customerId"
      },
      {
        "_tool": "issueRefund",
        "customerId": "†input.customerId",
        "amount": "†input.amount"
      }
    ]
  }
]

`solution` от LLM

{
  "calls": [
    {
      "_tool": "issueRefund",
      "customerId": "†input.customerId",
      "amount": "†input.amount"
    }
  ],
  "output": {
    "confirmationId": "refund_xyz789",
    "message": "The refund has been processed successfully."
  }
}

Plan обеспечивает процедурную последовательность, предотвращая преждевременную эскалацию и удерживая агента на намеченном пути.

Пример: Корректировка плана

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

Контекст

Агенту предоставляется существующий «оптимальный» Plan и новый Input от пользователя, который вводит новое ограничение.

[
  { type: 'tool', tool: Tool.bookFlight },
  { type: 'tool', tool: Tool.bookHotel },
  { type: 'tool', tool: Tool.findPetFriendlyHotel },
  {
    type: 'plan',
    plan: [
      {
        _tool: 'bookFlight',
        destination: '†input.destination',
      },
      {
        _tool: 'bookHotel',
        destination: '†input.destination',
      },
    ],
  },
  {
    type: 'input',
    destination: 'Berlin',
    instruction: "Actually, I'll be traveling with my dog.",
  },
];

`solution` от LLM

LLM распознает, что первоначальный план больше не подходит. Он отбрасывает старый план и генерирует новый, заменяя bookHotel более специализированным инструментом.

{
  "calls": [
    {
      "_tool": "bookFlight",
      "destination": "†input.destination"
    },
    {
      "_tool": "findPetFriendlyHotel",
      "destination": "†input.destination"
    }
  ],
  "output": null
}

Агент не просто меняет параметр; он коренным образом изменяет свою стратегию, выбирая более подходящий инструмент (findPetFriendlyHotel) на основе новых требований. Этот новый набор Tool Calls становится Plan для следующего шага в Цикле Выполнения.

Пример: Обработка сбоев

Этот пример демонстрирует, как агент может отклониться от «оптимального» Плана при столкновении с неожиданным сбоем. Процесс показан в два этапа: первоначальный «оптимальный» план и перепланирование, которое происходит после сбоя инструмента.

1. Первоначальный план

Агенту предоставляется набор инструментов и вводные данные от пользователя. Он генерирует оптимистичный, двухэтапный «оптимальный» план, который не учитывает возможность сбоя.

Исходный контекст

Agent.Request(config, {
  schema: {
    type: 'object',
    properties: {
      calls: { type: 'array' },
      output: {
        type: 'object',
        nullable: true,
        properties: {
          status: {
            type: 'string',
            enum: ['Success', 'Failed'],
          },
        },
      },
    },
  },
  context: [
    { type: 'tool', tool: 'Tool.processPayment' },
    { type: 'tool', tool: 'Tool.confirmOrder' },
    { type: 'tool', tool: 'Tool.reportFailure' },
    { type: 'input', amount: 50.0 },
  ],
});

Исходное решение

{
  "calls": [
    {
      "_tool": "processPayment",
      "amount": "†input.amount",
      "_outputPath": "†state.receipt || †state.error"
    },
    {
      "_tool": "confirmOrder",
      "receipt": "†state.receipt"
    }
  ],
  "output": null
}

2. Сбой и перепланирование

Цикл Выполнения пытается выполнить processPayment, но инструмент дает сбой. Движок заполняет †state.error. На следующей итерации LLM видит это новое состояние ошибки вместе с исходным (теперь устаревшим) планом и генерирует новое решение для обработки сбоя.

Контекст для следующего запроса

[
  {
    "type": "state",
    "error": { "code": "card_declined", "message": "Your card was declined." }
  },
  // Исходный, теперь уже устаревший, план все еще находится в контексте
  {
    "type": "plan",
    "plan": [
      { "_tool": "processPayment", "_outputPath": "†state.receipt || †state.error" },
      { "_tool": "confirmOrder", "receipt": "†state.receipt" }
    ]
  }
]

Новое решение (перепланированное)

{
  "calls": [
    {
      "_tool": "reportFailure",
      "error": "†state.error"
    }
  ],
  "output": { "status": "Failed" }
}

Агент распознал error в State, проигнорировал устаревший «оптимальный» Plan и сгенерировал новый, одношаговый план для reportFailure. Это демонстрирует способность агента реактивно обрабатывать неожиданные результаты.

Пример: Планирование на основе схемы

Этот пример демонстрирует, как предоставление schema для объекта Состояние действует как чертеж, направляя LLM к созданию структурно корректного Плана.

Контекст со схемой State

Вызывающая сторона предоставляет Input и сообщение State, которое содержит только schema. Эта схема определяет предполагаемый поток данных, указывая «переменные», которые должен использовать план.

[
  { "type": "tool", "tool": "Tool.detectLanguage" },
  { "type": "tool", "tool": "Tool.isEnglish" },
  { "type": "tool", "tool": "Tool.translateText" },
  {
    "type": "input",
    "text": "Bonjour le monde"
  },
  {
    "type": "state",
    "schema": {
      "type": "object",
      "properties": {
        "language": { "type": "string" },
        "isEnglish": { "type": "boolean" },
        "translatedText": { "type": "string" }
      }
    }
  }
]

`solution` от LLM

LLM использует схему State в качестве руководства для построения действительного плана, правильно связывая _outputPath одного инструмента с входом следующего.

{
  "calls": [
    {
      "_tool": "detectLanguage",
      "text": "†input.text",
      // LLM знает, что нужно использовать этот путь из схемы.
      "_outputPath": "†state.language"
    },
    {
      "_tool": "isEnglish",
      // Он правильно ссылается на вывод предыдущего шага.
      "language": "†state.language",
      "_outputPath": "†state.isEnglish"
    },
    {
      "_tool": "translateText",
      "text": "†input.text",
      "isEnglish": "†state.isEnglish",
      "_outputPath": "†state.translatedText"
    }
  ],
  "output": null
}

Предоставляя schema, вызывающая сторона дает LLM четкий чертеж потока данных. LLM не нужно угадывать имена переменных или последовательность; он просто заполняет предопределенные слоты, что приводит к более надежному и предсказуемому Plan.

Этот итеративный цикл планирования и выполнения является ядром Процесса. Это самодостаточный снимок рабочего процесса, фиксирующий доступные Инструменты, текущее Состояние и сам План.

От одного плана к многоразовым рабочим процессам

Сообщение План определяет последовательность действий для конкретной задачи. Чтобы сделать эти рабочие процессы по-настоящему мощными, нам нужен способ инкапсулировать их в многоразовые компоненты, которые можно вызывать из других Планов.

Протокол для этого параллельного выполнения описан в 011: Агент/Экземплирование.