Перейти к содержанию
Публикация AiManual

Постоянная память для AI-агентов: NVIDIA NeMo Agent Toolkit и Amazon S3 Vectors на EKS

Разбираем, как добавить постоянную память мультиагентной системе на NVIDIA NeMo Agent Toolkit: кастомный провайдер на Amazon S3 Vectors, методы add_items(), sea

Коротко

Что будет в материале

  1. 01

    Зачем агентам постоянная память и какие требования к ней

  2. 02

    Подсистема памяти в NVIDIA NeMo Agent Toolkit

  3. 03

    Почему Amazon S3 Vectors подходит как бэкенд памяти

  4. 04

    Сборка кастомного провайдера памяти для S3 Vectors

Постоянную память агента в NVIDIA NeMo Agent Toolkit (NAT) собирают из трёх частей: абстрактного интерфейса MemoryEditor с операциями записи, поиска и удаления воспоминаний, конкретного бэкенда хранения и YAML-конфигурации, по которой NAT находит провайдер. Бэкендом может выступать Amazon S3 Vectors: векторное хранилище внутри Amazon S3 с семантическим поиском, метаданными и строгой согласованностью записей.

Ниже практическая сборка этого стека: какие методы реализовать, как считать эмбеддинги, как фильтровать воспоминания по agent_id, team_id и ticker, как выкатить агентов на Amazon EKS через nat serve с доступом к хранилищу по IAM-ролям для сервисных аккаунтов (IRSA) и как измерить эффект от памяти. Сквозной пример - мультиагентная система инвестиционных исследований с ролями Research, Analysis и Synthesis.

Зачем агентам постоянная память и какие требования к ней

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

Разница видна на простом примере. Research-агент без памяти при каждом запуске заново собирает данные по тикеру AAPL: те же новости, те же отчёты. С памятью он поднимает прошлые находки, проверяет, что изменилось, и тратит токены только на новое. В мультиагентной системе эффект усиливается: Analysis-агент получает структурированную выжимку вместо повторного сбора сырых ссылок.

К хранилищу памяти в продакшене предъявляют четыре требования.

ТребованиеЧто даёт
Семантический поискВоспоминания достаются по смыслу запроса, а не по совпадению ключевых слов
Богатые метаданныеФильтры по агенту, команде, тикеру и дате ограничивают поиск нужным срезом
Строгая согласованностьЗаписанное воспоминание доступно поиску сразу, без окна задержки
Эластичное масштабированиеРост числа агентов и объёма воспоминаний не требует ручного перепланирования

Amazon S3 Vectors закрывает весь список требований. Формально это возможность Amazon Simple Storage Service (Amazon S3), отдельный сервис разворачивать не нужно, а на выходе получаются семантический поиск, богатые метаданные, строгая согласованность и эластичное масштабирование.

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

Подсистема памяти в NVIDIA NeMo Agent Toolkit

NAT - открытый фреймворк для сборки, профилирования и настройки AI-агентов. Привязки к конкретному фреймворку агентов нет: он работает со Strands Agents, LangChain, LlamaIndex, CrewAI и кастомными реализациями. Четыре рабочие возможности NAT выглядят так.

  • Оркестрация агентов. Рабочие процессы собираются из настраиваемых LLM, инструментов и промптов. Запуск локально через nat run или как постоянный сервис через nat serve.
  • Профилирование. Расход токенов, задержка, пропускная способность и время работы по агентам и отдельным инструментам. Узкие места в мультиагентных процессах находятся именно здесь.
  • Оценка. Встроенные оценщики точности ответов, релевантности контекста, обоснованности ответа и траектории агента. Кастомные оценщики поддерживаются.
  • Настройка гиперпараметров. Автоматический подбор temperature, top_p и max_tokens так, чтобы качество оставалось высоким, а стоимость и задержка падали.

Память вынесена в отдельную подсистему: она хранит и достаёт историю разговоров, пользовательские предпочтения и долгосрочные знания между вызовами агента. Модуль расширяемый, кастомный провайдер создаётся через плагинный интерфейс NAT. Из коробки доступны Mem0, MemMachine, Redis и Zep. Разбор связки NAT с S3 Vectors опубликован в блоге AWS: Build agent memory with NVIDIA NeMo Agent Toolkit and Amazon S3 Vectors.

MemoryEditor, MemoryItem и MemoryBaseConfig

MemoryEditor - абстрактный интерфейс, который обязаны реализовать все бэкенды памяти. В нём три метода: add_items(), search() и remove_items(). Этого набора хватает на полный цикл: записать воспоминание, найти его по смыслу, удалить устаревшее.

MemoryItem описывает одну единицу памяти и содержит поля для истории разговоров, тегов, метаданных, user_id и опциональной текстовой строки. Метаданные несут основную нагрузку: именно в них уезжают agent_id, team_id и ticker, по которым потом фильтрует поиск.

MemoryBaseConfig - базовый класс Pydantic, от которого наследуются кастомные конфигурации памяти. NAT находит провайдера по полю _type в YAML-конфиге.

class S3VectorsMemory(MemoryEditor):
    def add_items(self, items):            # запись воспоминаний
        ...
    def search(self, query, **filters):    # семантический поиск
        ...
    def remove_items(self, ids):           # удаление устаревшего
        ...

Скелет намеренно упрощён: имена модулей и точные сигнатуры сверяйте с документацией NAT, интерфейс развивается.

Автоматическая обёртка auto_memory_agent

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

Почему Amazon S3 Vectors подходит как бэкенд памяти

Amazon S3 Vectors - возможность Amazon S3, то есть векторы живут в том же сервисе, где уже лежат остальные данные, с привычной моделью доступа через IAM. Отдельный кластер под векторный поиск в контуре не появляется, как это описано в разборе AWS.

Что это даёт памяти агента по пунктам:

  • Строгая согласованность: после add_items() воспоминание сразу находится через search(), без окна задержки и ретраев.
  • Метаданные: фильтры по agent_id, team_id, ticker и timestamp ограничивают выдачу нужным срезом ещё до сравнения векторов.
  • Семантический поиск: воспоминания достаются по смыслу запроса, а не по совпадению слов.
  • Эластичное масштабирование: рост числа агентов и объёма воспоминаний не требует ручного шардирования индекса.

Выбор между встроенными провайдерами NAT и S3 Vectors стоит вести по контуру эксплуатации. Mem0, MemMachine, Redis и Zep закрывают распространённые сценарии, но добавляют в стек отдельный сервис хранения со своими обновлениями, бэкапами и политиками доступа. Если данные уже в S3, а команда хочет один контур и один IAM-слой, S3 Vectors убирает лишний компонент. Ограничения встроенных провайдеров в разборе AWS не раскрываются, так что перед выбором проверьте их документацию и то, как они ведут себя на вашем профиле нагрузки.

Сборка кастомного провайдера памяти для S3 Vectors

Порядок работ выглядит так.

  1. Создать класс, наследующий MemoryEditor, и реализовать три метода.
  2. Описать конфигурацию: Pydantic-класс на базе MemoryBaseConfig с бакетом, индексом и регионом.
  3. В add_items() считать эмбеддинги и записать векторы вместе с метаданными.
  4. В search() выполнить векторный поиск с фильтрами по метаданным и вернуть объекты MemoryItem.
  5. В remove_items() удалить векторы по идентификаторам, когда воспоминание устарело.
  6. Зарегистрировать провайдер и указать его _type в YAML-конфиге агента.
memory:
  _type: s3_vectors
  bucket: agent-memory-prod
  index: investment-research
  region: eu-central-1

Генерация эмбеддингов с Amazon Titan Text Embeddings V2

Вектор для текста воспоминания считает модель эмбеддингов. Amazon Titan Text Embeddings V2 доступна через Amazon Bedrock и подходит как базовый вариант для S3 Vectors:

import json, boto3

bedrock = boto3.client("bedrock-runtime", region_name="eu-central-1")

response = bedrock.invoke_model(
    modelId="amazon.titan-embed-text-v2:0",
    body=json.dumps({"inputText": memory_text}),
)
vector = json.loads(response["body"].read())["embedding"]

Два момента ломают поиск чаще всего. Размерность вектора должна совпадать с размерностью индекса S3 Vectors, иначе запись отклоняется или выдача получается бессмысленной. Текст нужно считать одной и той же моделью при записи и при поиске: после смены модели старые векторы становятся несовместимыми. Точные идентификаторы моделей и поддерживаемые размерности сверяйте в документации Amazon Bedrock, пример выше показывает форму вызова, а не фиксированный набор параметров.

Запись и поиск векторов с фильтрацией по метаданным

Метаданные превращают векторное хранилище в рабочую память. Раскладка, которая окупается на практике: agent_id, team_id, ticker, timestamp и type со значениями вроде episode или semantic.

Поиск тогда выглядит как обычный вызов с фильтрами: Analysis-агент запрашивает подтверждённые факты по AAPL и не подмешивает в контекст выводы других команд.

mem.search(
    "что изменилось в выручке за квартал",
    agent_id="research",
    team_id="equities",
    ticker="AAPL",
    top_k=5,
)

Строгая согласованность S3 Vectors закрывает классическую гонку в мультиагентных процессах: Research-агент записал находку в конце шага, а Analysis-агент в том же запуске уже видит её в поиске. Паузы и повторные попытки для этого не нужны.

Развёртывание агентов на Amazon EKS с доступом к S3 Vectors

NAT запускает агентов двумя способами: локально через nat run и как постоянный сервис через nat serve. Для продакшена нужен второй. Amazon EKS даёт операционный контроль: собственные политики обновлений, сетевые ограничения, единый мониторинг. Такой вариант разобран в публикации AWS, где постоянный слой памяти на S3 Vectors работает внутри NAT на Amazon EKS (описание стека).

Ключи доступа внутри контейнера хранить не стоит. На EKS доступ к S3 Vectors выдают через IRSA (IAM Roles for Service Accounts): создаёте IAM-роль с политикой на нужный бакет и индекс, связываете её с сервисным аккаунтом через OIDC-провайдер кластера, а под получает временные креденшелы автоматически.

apiVersion: v1
kind: ServiceAccount
metadata:
  name: nat-agent
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/nat-s3vectors-memory
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: research-agent
spec:
  replicas: 2
  selector:
    matchLabels:
      app: research-agent
  template:
    metadata:
      labels:
        app: research-agent
    spec:
      serviceAccountName: nat-agent
      containers:
      - name: agent
        image: registry.example.com/nat-research:latest
        command: ["nat", "serve", "--host", "0.0.0.0", "--port", "8000"]
        resources:
          requests:
            cpu: "500m"
            memory: "1Gi"

Автомасштабирование настраивается обычным Horizontal Pod Autoscaler по CPU или по кастомной метрике. Воспоминания лежат вне процесса, в S3 Vectors, поэтому реплики не нужно синхронизировать между собой: поды можно добавлять и убирать без потери памяти. Похожий подход к продакшен-агентам на EKS применяет monday.com, их архитектура с SNS/SQS, ElastiCache и EFS разобрана в отдельной статье.

Сквозной пример: мультиагентная система инвестиционных исследований

Три роли делят работу. Research собирает факты по тикерам и пишет их в память с метаданными agent_id=research, ticker и timestamp. Analysis читает чужие факты по нужному тикеру, делает выводы и сохраняет их отдельным типом записи. Synthesis агрегирует всё по команде team_id=equities и формирует отчёт.

Как это выглядит в динамике. Research нашёл новость про выручку AAPL и вызвал add_items(). Через час Analysis запускается по тому же тикеру, делает search(ticker="AAPL", type="episode") и видит находку в контексте, не тратя токены на повторный сбор. Synthesis запрашивает уже обобщённые выводы и ссылается на них в отчёте.

Разделение по agent_id и team_id даёт ещё одно преимущество: память одного агента можно чистить через remove_items(), не задевая остальных, и разграничивать доступ между командами. Повторный сбор одних и тех же данных уходит из воркфлоу вместе со сдвоенным расходом токенов на одинаковые вызовы.

Консолидация эпизодической памяти в семантическую

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

Паттерн консолидации простой. Отдельная задача раз в сутки выбирает эпизоды за окно, суммирует их моделью в короткие утверждения и записывает как новый тип с метаданными type=semantic и ссылкой на исходные записи. Сырые эпизоды после этого удаляются через remove_items() или уезжают в архив. Результат: после нескольких разборов по AAPL в памяти появляется одна запись «выросла волатильность после квартального отчёта», а не двадцать почти одинаковых заметок.

В опубликованном разборе AWS механика консолидации не описана: это инженерный паттерн поверх интерфейса MemoryEditor, собирать его придётся самостоятельно. Родственные подходы к долгой памяти, включая графовое хранилище и MCP-серверы, разбирались в материале про локального ассистента с постоянной памятью.

Оценка влияния памяти через nat eval

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

Схема сравнения простая: один и тот же набор кейсов прогоняется дважды, с включённой и выключенной памятью, оценки сравниваются. Так вклад памяти виден отдельно от качества модели и промпта. Запускается оценка поверх тех же рабочих процессов, которые вы гоняете через nat run и nat serve; точное имя команды и формат набора кейсов сверяйте с документацией NAT. Как считать метрики собственного агента вне NAT, разобрано в статье про сборку AI-агента с нуля.

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

Начинать стоит с малого: один агент, один индекс S3 Vectors, один набор кейсов для оценки. Роли и консолидацию добавляйте дальше, там, где метрики это подтверждают.

Подписаться на канал