К курсу

Цикл агента

Один шаг цикла агентаПрактика: один шаг циклаЦикл в кодеПрактика: цикл в кодеОстановка и повторыПрактика: остановка и повторыЧто решает только кодПрактика: что решает код

Надёжность в проде

Таймаут и ключ идемпотентностиПрактика: таймаут и ключ идемпотентности

Устройство агентов

Workflow или агентПрактика: workflow или агентReAct и plan-and-executeПрактика: ReAct и plan-and-executeReflexionОдин агент или ройПрактика: один агент или рой

Контекст и стоимость

Почему растёт счёт за агентаПрактика: токены и кэшКонтекст-инженерияПрактика: управление окномПамять агентаПрактика: что агент хранит

Инструменты и протоколы

MCPПрактика: MCP
←→ENВойти
Агенты › Устройство агентовDeep dive

Reflexion

Опирается наPlan-and-Solve

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

Агенту поддержки Northwind нужно, чтобы track_order работал с новым перевозчиком, поэтому фоновый агент пишет парсер, который переводит события перевозчика в статусы Northwind. У Northwind есть 40 настоящих событий, размеченных вручную. Первая попытка верно разбирает 37. Второй прогон с тем же промптом ошибается на тех же 3.

Записка в кармане

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

Reflexion, метод из статьи 2023 года, проделывает это с агентом. Ему нужны площадка, которую можно очистить, экзаменатор и карман для записок.

Почему тот же промпт снова проваливается

В статье базовые агенты на HotPotQA (вопросы в несколько шагов) при повторах с температурой 0.7 не решили ни одной задачи, проваленной с первого раза. На AlfWorld (бытовые задачи в текстовом мире) обычные повторы какое-то время давали прирост, но между 6-й и 7-й попытками он прекратился.

Retry подходит для сбоев, которые проходят сами, как в блоке об остановке. Неверный подход сам не пройдёт.

Один круг по шагам

Неудачная попытка превращается в записку, а следующая начинается в чистой среде с этой запиской.
Неудачная попытка превращается в записку, а следующая начинается в чистой среде с этой запиской.
  1. Исполнитель, ваш обычный цикл агента, проходит всю задачу в свежей среде. Цепочка его шагов и наблюдений называется траекторией.
  2. Оценщик оценивает траекторию. В статье это была точная сверка на HotPotQA, написанные самим агентом юнит-тесты для кода, а на AlfWorld либо языковая модель, либо написанное вручную правило (одно и то же действие и один и тот же ответ больше 3 циклов или больше 30 действий). В некоторых из этих вариантов модель вообще не участвует.
  3. Если проверка пройдена, ваш код останавливается.
  4. Если нет, модель рефлексии получает траекторию, сигнал оценщика и прежние записки. Она пишет, что пошло не так и что сделать иначе.
  5. Ваш код сохраняет записку и оставляет только несколько последних. В статье предел обычно составлял от 1 до 3, потому что записки должны помещаться в контекст.
  6. Ваш код сбрасывает среду, и исполнитель начинает заново с записками в промпте.

После первой неудачи запись о парсере у Northwind выглядит так:

{
  "trial": 1,
  "evaluator": {
    "passed": false,
    "failed_fixtures": [
      "evt_12",
      "evt_29",
      "evt_33"
    ],
    "example": {
      "event": "DELIVERY_ATTEMPT_FAILED",
      "expected": "in_transit",
      "got": "delivered"
    }
  },
  "note": "I treated any status containing DELIVER as delivered. Match full status codes. DELIVERY_ATTEMPT_FAILED means in_transit."
}

Весь цикл короткий:

def reflexion(task, max_trials=4, keep_notes=3):
    notes = []
    for trial in range(1, max_trials + 1):
        env = reset_environment(task)
        trajectory = run_actor(task, notes, env)      # полный прогон агента
        verdict = run_fixtures(env)                   # обычный код, без модели
        if verdict["passed"]:
            return {"status": "passed", "trials": trial, "result": trajectory}
        note = reflect(task, trajectory, verdict, notes)  # один вызов модели
        notes = (notes + [note])[-keep_notes:]
    return {"status": "gave_up", "trials": max_trials}

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

Три похожих цикла

Различает их то, что остаётся от одной попытки к другой; строка Reflexion показывает прогоны статьи на AlfWorld.
Различает их то, что остаётся от одной попытки к другой; строка Reflexion показывает прогоны статьи на AlfWorld.

Evaluator-optimizer из блока о workflow критикует один черновик и правит его. В прогонах статьи на AlfWorld Reflexion выбрасывает попытку, сбрасывает среду и переносит только текст. Агент для кода из той же статьи — исключение: следующая попытка видит ещё и прежний код с результатами тестов, так что для кода граница между двумя циклами тонкая. Цикл Northwind выше переносит только записки.

В посте LangChain от февраля 2024 года есть учебный пример под названием Reflexion: он пишет черновик ответа, ищет подтверждения и правит тот же ответ. В самом посте сказано, что он идёт по одной фиксированной траектории. Это может быть полезно, но сброса между попытками там нет.

Сколько это стоит

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

Потолок задаёт экзаменатор

Один GPT-4 набрал 80,1% на обоих бенчмарках. На MBPP 16,3% решений, прошедших собственные тесты агента, оказались неверными, и Reflexion оказался ниже обычного GPT-4.
Один GPT-4 набрал 80,1% на обоих бенчмарках. На MBPP 16,3% решений, прошедших собственные тесты агента, оказались неверными, и Reflexion оказался ниже обычного GPT-4.

HumanEval и MBPP — это наборы коротких задач на программирование. Авторы статьи объясняют проигрыш на MBPP ложными срабатываниями тестов. Когда тесты проходят на неверном коде, цикл останавливается и сообщает неверный ответ. Та же беда у любого оценщика, который может одобрить плохую работу, в том числе у второй модели, оценивающей что-то столь расплывчатое, как тон.

Чтобы понять, что даёт каждая часть метода, её отключают. Такой эксперимент на 50 самых сложных задачах HumanEval на Rust с GPT-4 говорит о том, что важны обе половины. Pass@1 — это доля задач, где решение, которое система отправила, проходит тесты самого бенчмарка:

Настройка

Pass@1

Базовая модель

60%

Записки без тестов

52%

Тесты без записок

60%

Тесты и записки

68%

Разница между 60% и 68% — это 4 задачи из 50. Строку с 52% авторы объясняют так: без тестов агент не мог понять, верен ли его код, поэтому продолжал работу и вносил вредные правки.

Reflexion не сработал ещё в двух местах. На WebShop, бенчмарке с заданиями в интернет-магазине, где нужен разнообразный поиск, авторы остановились после четырёх попыток: агент не показывал улучшения. На HumanEval для Python с небольшой моделью starchat-beta он набрал 26%, столько же, сколько базовый вариант. Это результаты на моделях и бенчмарках 2023 года, так что измеряйте на своих задачах.

Нет сброса — нет Reflexion

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

Вывод

Глубже

Reflexion: Language Agents with Verbal Reinforcement LearningРаздел 3 про сам цикл, приложение B.1 про WebShop, где метод застрял.Reflection Agents (LangChain)Показывает рядом простую рефлексию, пример Reflexion с одной траекторией и поиск по дереву.Reflexion (Prompt Engineering Guide)Короткий обзор трёх компонентов и того, где метод применим.
‹ Назад
Предложить улучшениеНашёл ошибку