Постоянную память агента в 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
Порядок работ выглядит так.
- Создать класс, наследующий MemoryEditor, и реализовать три метода.
- Описать конфигурацию: Pydantic-класс на базе MemoryBaseConfig с бакетом, индексом и регионом.
- В add_items() считать эмбеддинги и записать векторы вместе с метаданными.
- В search() выполнить векторный поиск с фильтрами по метаданным и вернуть объекты MemoryItem.
- В remove_items() удалить векторы по идентификаторам, когда воспоминание устарело.
- Зарегистрировать провайдер и указать его _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, один набор кейсов для оценки. Роли и консолидацию добавляйте дальше, там, где метрики это подтверждают.