Главная / Статьи

Объяснение памяти агента: окно контекста, RAG и постоянное состояние

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

Издатель: REACTAPP.TOP10 мин чтения2195 слов

Источники: Agent Memory Explained

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

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

Модель не имеет состояния. Каждый вызов API начинается с нуля, без воспоминаний о вашем последнем сообщении, предыдущей сессии или имени, которое вы ей дали. Все, что она знает во время вызова, — это то, что содержится в запросе, и ничего больше.

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

Модель без состояния, приложение с состоянием

Новички предполагают, что модель ведёт диалог. Это не так. Диалог ведёт приложение, которое его воспроизводит. Когда вы отправляете четвёртое сообщение в чат, ваш код каждый раз передаёт все сообщения 1, 2, 3 и 4. Ощущение преемственности возникает благодаря повторной отправке истории, а модель просто получает более длинный входной данные, чем раньше.

Учитывая это, три уровня перестают быть абстрактными:

  • Окно контекста — это то, что находится в запросе прямо сейчас.
  • Поиск материалов (RAG) — это способ нахождения информации для включения в запрос.
  • Постоянное состояние — это способ, с помощью которого данные сохраняются после завершения запроса, чтобы их можно было добавить обратно позже.
  • Контейнер, механизм поиска и хранилище — три разных элемента. Если смешивать их, получится использование векторной базы данных для решения проблемы, которую можно было бы решить с помощью файла preferences.json.

    Уровень 1: окно, или рабочий стол модели

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

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

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

    Затраты:

    1. Деньги и задержки зависят от того, что вы пересылаете. Пересылка 51 сообщения из длинного чата означает пересылку 50 сообщений из истории. Длинный контекст не является бесплатным только потому, что он помещается в окно.
    2. Внимание распределяется неравномерно. Модели обычно наиболее надежно используют начало и конец длинного входного текста, а среднюю часть — в меньшей степени, что является хорошо известным эффектом «потери информации в середине». Факт, находящийся на 60% глубины очень длинного запроса, присутствует, но может так и не быть эффективно использован. То, что что-то помещается в окно, не означает, что оно будет прочитано.
  • Всё заканчивается. Как только сессия завершена, окно исчезает. Окно контекста никогда не сохраняет ничего. Это самая распространённая ошибка новичков: длительный разговор кажется частью памяти, но на самом деле это буфер, а не хранилище.
  • Вся суть этого механизма можно описать в нескольких строках:

    messages = []
    
    def turn(user_text):
        messages.append(
            {"role": "user",
             "content": user_text}
        )
        # every call resends
        # the ENTIRE history
        reply = client.messages.create(
            model="claude-opus-5",
            max_tokens=1024,
            messages=messages,
        )
        messages.append(
            {"role": "assistant",
             "content": reply.content[0].text}
        )
        return reply
    
    # messages grows forever.
    # so does your bill.
    

    Этот список и является здесь «памятью». Он существует только внутри вашего процесса, поэтому как только процесс завершается, исчезает и список.

    Окно само по себе достаточно, когда задача короткая, необходимый материал небольшой и известен заранее, и ничто не должно сохраняться после окончания сессии. Для подведения итогов, проверки одного файла или однократной обработки не требуется ничего большего. Не стоит добавлять туда лишние механизмы.

    Уровень 2: поиск, референтная библиотека

    Рано или поздно материал становится слишком обширным или перестаёт оправдывать затраты. Возможно, речь идёт о 10 000 заявок на поддержку, внутреннем вики или кодовой базе. Нельзя включать всё это в каждый запрос, и не стоит этого делать.

    Генерация с использованием поиска деляет работу: заранее готовится индекс, а затем осуществляется поиск информации при поступлении вопроса.

    Сначала вы разрезаете документы на фрагменты, преобразуете каждый фрагмент в эмбеддинг (числовой вектор, отражающий смысл) и сохраняете его. Когда поступает вопрос, вы преобразуете его в эмбеддинг, находите фрагменты, векторы которых наиболее близки к этому эмбеддингу, и вставляете их в окно контекста рядом с вопросом.

    # 1. AHEAD OF TIME (once, offline)
    for chunk in split(documents):
        store.add(
            text=chunk,
            vector=embed(chunk),
        )
    
    # 2. AT QUESTION TIME (every request)
    def answer(question):
        hits = store.search(
            embed(question), top_k=5
        )
        context = "\n\n".join(h.text for h in hits)
        return client.messages.create(
            model="claude-opus-5",
            max_tokens=1024,
            # retrieved chunks land in the same request
            messages=[{
                "role": "user",
                "content": f"{context}\n\nQ: {question}",
            }],
        )
    

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

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

    Затраты:

    1. Результат определяется заранее. Размер фрагментов, модель вжимания данных и структура индекса фиксируются при создании индекса. Факт, распределенный между двумя фрагментами, или сформулированный не так, как спрашивают пользователи, просто не находится, и модель отвечает с уверенностью на основе того, что получила. Для наблюдателя плохая работа по поиску информации и галлюцинации выглядят одинаково.
  • В основном это режим только для чтения. Классический RAG подобен библиотечному пропуску, а не тетради. Агент ищет информацию, но ничего не записывает, и всё, что он узнает в ходе сессии, не попадает в индекс, если только вы сами не создадите соответствующий путь.
  • Необходим дополнительный шаг. Встраивание запроса и поиск в индексе добавляют задержку к каждому запросу.
  • Метод извлечения информации подходит тогда, когда база знаний велика, общая для всех пользователей и редко меняется — например, документация, правила, каталоги продуктов и код. Основной признак заключается в том, что один корпус данных обслуживает всех пользователей. Если информация, которую вы хотите запомнить, принадлежит конкретному пользователю и меняется по мере разговора, метод извлечения информации неэффективен, и вам понадобится уровень 3.

    Уровень 3: постоянное состояние, тетрадь

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

    Шаблон состоит из цикла чтения, изменения и записи, действующего в течение всей сессии:

    # session start: load
    with open("state.json") as f:
        state = json.load(f)
    
    system = (
        "Known about this user:\n"
        f"{json.dumps(state)}"
    )
    
    messages = []
    
    def turn(user_text):
        messages.append(
            {"role": "user",
             "content": user_text}
        )
        reply = client.messages.create(
            model="claude-opus-5",
            max_tokens=1024,
            system=system,   # state in
            messages=messages,
        )
        return reply
    
    # ... along the way ...
    state["prefers"] = "metric units"
    state["last_checkpoint"] = "step_4_done"
    
    # session end: save
    with open("state.json", "w") as f:
        json.dump(state, f)
    

    Концептуально больше ничего не требуется. Файл JSON считается реальной реализацией; так же как и строка в базе данных, хранилище типа key-value или папка с записями в формате Markdown. Средство хранения — это самая простая часть, а интересно то, кто решает, что сохранять и что загружать обратно.

    Правильный выбор в этом вопросе — это сложная часть, и именно здесь различаются фреймворки агентов. Самый прямой современный подход заключается в передаче этой задачи модели: с помощью инструмента памяти от Anthropic Claude получает небольшой набор команд для файлов (просмотр, создание, замена, вставка, удаление и переименование), которые работают в изолированной папке /memories; таким образом агент сам выбирает, что стоит сохранить, и читает эти записи в начале следующей сессии. Ваше собственное приложение выполняет операции в любом выбранном вами хранилище. Инструмент работает на стороне клиента, что позволяет сохранять файлы под вашим контролем.

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

    Затраты:

    1. Кто-то должен заниматься упорядочиванием информации. Если записать всё, получится непоисковый текст; если написать слишком мало, нарушится непрерывность. Создание качественного постоянного состояния — сложная задача.
    2. Устаревшие данные хуже, чем их отсутствие. Предпочтения, сохранённые восемь месяцев назад и так и не пересмотренные, применяются с полной уверенностью. Ошибки при получении данных приводят к неопределённости, тогда как устаревшее состояние вызывает ошибочное поведение.
    3. Загрузка — это не бесплатно. Что бы вы ни загружали, это всё равно занимает место в окне, поэтому уровень 3 также влияет на уровень 1.

    Используйте постоянное состояние, если один и тот же пользователь или задача многократно запускают агента, и то, что узнает запуск n, должно повлиять на запуск n+1.

    Три подхода рядом

    Окно контекста Поиск информации Постоянное состояние
    Роль Рабочий стол Библиотека Тетрадь
    Хранит Всё, что указано в запросе Большой общий корпус данных Небольшие факты, записанные агентом
    Создано Вашим приложением при каждом вызове Задачей индексации Агентом или вашим кодом
    Сохраняется после перезагрузки Нет Да Да
    Основной риск Затраты, потеря данных посредине процесса Пропущенные фрагменты данных Устаревшие факты

    Самая важная строка — это та, что касается перезапусков. Она служит разделительной линией между длинным чатом и агентом с памятью.

    Они формируют ответ

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

    1. Сначала загружается персистентное состояние: у этого клиента есть бизнес-план, и он предпочитает краткие ответы. Два факта, примерно сорок токенов.
    2. Затем выполняется операция поиск информации на основе вопроса: извлекается раздел политики возврата для бизнес-планов вместе с двумя последними исключениями. Три блока информации, примерно тысяча двести токенов.
    3. Окно контекста включает в себя всё вышесказанное, а также информацию из живого чата и определения инструментов; модель анализирует всю эту информацию вместе.
  • После ответа агент записывает одну строку в перманентное состояние: был задан вопрос о политике возврата в середине августа, поэтому в следующей сессии его не рассматривают как нового клиента.
  • Одна заявка, три уровня. Окно — это место, куда всё попадает, в то время как получение информации и использование перманентного состояния — это два отдельных способа доступа к ней.

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

    Какой вариант вам действительно нужен?

    Новички почти всегда сначала берут векторную базу данных. Это самая заметная часть стека, но редко бывает и правильным решением. Лучше подниматься по лестнице развития.

    Начните с окна. Все необходимое помещается в один запрос по желаемой цене? Тогда останавливайтесь. Никакого индекса, никакого хранилища.

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

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

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

    Kраткое изложение

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

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

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