Главная / Статьи / Понимание искусственных интеллектуальных агентов с помощью аналогии мозг-палец

Понимание искусственных интеллектуальных агентов с помощью аналогии мозг-палец

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

1513 слов

Обзор

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

Аналогия

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

Всё начинается с того, что мозг приказывает пальцу открыть приложение для заказа еды чтобы вы могли просмотреть рестораны поблизости. Палец нажимает на иконку, и на экране появляется список ресторанов.

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

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

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

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

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

Когда вы создаёте агента для конкретной цели, вы определяете набор инструментов и передаёте этот список ЯЗЫКУ БОЛЬШИХ МОДЕЛЕЙ, который выступает в роли мозга для принятия решений. (Далее символ -> будет использоваться для обозначения перехода управления к следующему шагу.) Когда пользователь задаёт агенту задачу, процесс выглядит следующим образом: ЯЗЫК БОЛЬШИХ МОДЕЛЕЙ анализирует текущий шаг или оставшуюся работу и выбирает наиболее подходящий инструмент —> затем передаёт этот инструмент исполнителю, прося его запустить его и доложить о результатах —> ЯЗЫК БОЛЬШИХ МОДЕЛЕЙ читает эти результаты и проверяет, удовлетворяют ли они запросу пользователя. Если да, процесс прекращается на этом этапе; если нет, ЯЗЫК БОЛЬШИХ МОДЕЛЕЙ выбирает следующий подходящий инструмент на основе последних результатов, и цикл повторяется до тех пор, пока ЯЗЫК БОЛЬШИХ МОДЕЛЕЙ не определит, что задача полностью выполнена и дальнейшие вызовы инструментов не требуются.

Реализация

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

Инструменты

get_restaurants() {
    // In production, replace with an API call such as GET /api/v1/restaurants
  return list of restaurants;
}

get_menu(restaurantName) {
    // In production, replace with an API call such as GET /api/v1/restaurants/${restaurantName or restaurantId}/menu
  return menuItems;
}

add_to_cart(sessionId, menuItemId) {
  // In production, replace with an API call such as POST /api/v1/cart
  return updatedCart;
}

place_order(sessionId) {
  // In production, replace with an API call such as POST /api/v1/order
  return orderDetails;
}

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

Выполняющий инструменты модуль

toolExecutor(toolCall) {
  const tool = toolNameMap[toolCall.name];

  return tool.execute(toolCall.arguments);
}

Сам исполнитель инструмента — это просто ещё одна обычная функция. Его аргумент toolCall содержит информацию о вызываемом инструменте — его название и аргументы, — причём эта информация варьируется в зависимости от того, какой именно инструмент вызывается: название ресторана, ID позиции меню или что-то совершенно другое. Ключевой момент заключается в том, что когда большая языковая модель указывает исполнителю, какой инструмент нужно запустить, она передаёт точные, структурированные данные, такие как название инструмента get_menu с аргументом {restaurantName: "Spicy Pizza"}. Нет необходимости вручную извлекать или анализировать эту информацию из необработанного текстового вывода большой языковой модели.

Агент

// Give all the tools to the LLM
llm = OpenAILLM.bindTools([get_restaurants, get_menu, add_to_cart, place_order])

// Take the user query to run the agent loop
reactAgent(userInput) {
  while (true) {
    response = llm(userInput);

    if (response.isFinalAnswer) {
      return response.answer;
    }

    result = toolExecutor(response.toolCall);

    userInput = response + result;
  }
}

Что стоит обратить внимание в псевдокоде

  1. Название reactAgent относится к шаблону подсказок ReAct (Reason and Act), при котором большая языковая модель определяет инструмент, который необходимо вызвать, наблюдает за результатом этого вызова, а затем решает, требуется ли запустить ещё один инструмент или задача завершена и цикл может прекратиться.
  2. Обратите внимание на цикл while(true). Именно здесь агенты отличаются от условной логики, которую обычно используют в языках вроде Java или Python. Вместо того чтобы жёстко задавать вызовы функций в ветках if-else или циклах for, вы предоставляете большой языковой модели набор инструментов — каждый из которых имеет имя и описание — и позволяете самой модели во время выполнения решать, какой инструмент вызвать дальше, какие аргументы передать и когда работа завершена и цикл должен прекратиться.
  • Тем не менее, производственные системы на самом деле не опираются на примитивный цикл while(true); он используется здесь исключительно для иллюстрации основного агентского поведения в простом коде. На практике следует использовать фреймворки оркестрации, такие как LangGraph. Даже с подобным фреймворком стандартной практикой является ограничение глубины рекурсии, чтобы LLM нельзя было вызывать бесконечно — неограниченный цикл может привести к ошибкам, расточительству токенов и ненужным затратам.
  • Проверка response.isFinalAnswer в псевдокоде указывает на то, что агент пришел к полному ответу и дальнейшие вызовы инструментов не требуются. После этого агент возвращает пользователю краткий ответ вместо того, чтобы запускать еще один инструмент.
  • Возможно, вы также заметили строку userInput = response + result, при которой объединённый результат возвращается в LLM через response = llm(userInput) в следующей итерации. Это происходит потому, что каждый вызов LLM является безсостоянийным — он не сохраняет информацию о предыдущих шагах, даже в рамках одной сессии или взаимодействия с пользователем. Следовательно, каждый раз после завершения выполнения инструмента необходимо пересылать полную историю разговора: системное указание, описывающее поведение агента, первоначальный запрос пользователя, предыдущий ответ ИИ с рекомендацией использования инструмента и результат, полученный этим инструментом. Затем LLM обрабатывает всю эту последовательность, чтобы определить, достигнута ли цель или требуются дополнительные вызовы инструментов.
  • Диаграмма последовательности выполнения LLM и инструментов

    На приведённой ниже диаграмме показано, как LLM выбирает следующий инструмент, как его исполнитель запускает этот инструмент, и как этот цикл повторяется до тех пор, пока LLM не придёт к выводу, что задача завершена и дальнейшее выполнение инструментов не требуется.

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

    Код на GitHub

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

    Связанная литература

  • Постепенное открытие инструментов для ИИ-агентов в крупном масштабе — Объясняется, почему большие каталоги инструментов снижают производительность ИИ-агентов, и как постепенное открытие инструментов с использованием манифестов и схем типа «just-in-time» решает эту проблему.
  • Вызов Claude, GPT и Gemini через совместимые с OpenAI конечные точки — Рассматривается, какие функции поддерживают совместимые с OpenAI слои Anthropic и Gemini, где они незаметно отказывают в определенных возможностях, и когда имеет смысл направлять запросы ко всем трем сервисам через один шлюз.