MarathonMemBench — бенчмарк нового типа для тестирования памяти вашего LLM-агента

от автора

Эта статья — про долговременную память AI-агентов и про то, как её измерить. Я сделал MarathonMemBench — платформу, где LLM-персона часами беседует с тестируемым агентом: рассказывает факты о своей жизни, меняет решения, путается и поправляется, а под конец — когда начало разговора давно вытеснено из контекста — устраивает экзамен на всё сказанное. Подключить можно любого агента, у которого есть чат, — больше от него ничего не требуется. Дальше будут: обзор существующих бенчмарков памяти и почему каждый из них требует дорабатывать агента под себя, устройство платформы, результаты open-source агентов — неожиданно сильные — и вскрытие их памяти после прогонов. Но начать придётся с корня проблемы — с конечности контекста языковых моделей.

Кажется будущее из фильма «Она» уже наступило

Кажется будущее из фильма «Она» уже наступило

У этой статьи есть предыстория. В октябре я писал о гипотезе: достаточно умная языковая модель сможет решить проблему долговременной памяти сама — обычными вызовами инструментов. Тогда это было не более чем предположение. Но ровно через месяц вышла Opus 4.5, а ещё через месяц Andrej Karpathy написал, что агентные способности моделей перешли порог в агентском кодинге и он, программист с двадцатилетним стажем, «никогда не чувствовал себя настолько отставшим». Эта статья — про другую способность, о которой говорят намного реже: самостоятельно вести долговременную память. И, как будет видно по цифрам, здесь всё оказалось неожиданно хорошо ровно в той же степени.

Конечность контекста — фундаментальное ограничение языковых моделей

Если задуматься, вся история триумфа языковых моделей — это эксплуатация одного неожиданного открытия: архитектура трансформера для предсказания следующего токена рождает интеллект, выраженный в словах. Всё, что мы видим последние годы, весь этот триллион инвестиций, — по сути развитие одной этой идеи. Модели становятся больше, обучающих данных — больше; придумали SFT — дообучение на примерах образцовых диалогов, потом RLHF — обучение с подкреплением на человеческих оценках ответов, совсем недавно — reasoning, когда модель учат сначала «подумать» и только потом отвечать. Но ничего из этого не выходит за рамки обучения предсказанию следующего токена на трансформере.

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

«А почему конечность контекста — это проблема?» — скажет кто-то. Я ведь наоборот: переключаюсь на новую сессию, начинаю новую задачу и спокойно её делаю. И мне даже хорошо, что новая сессия ничего не помнит про старую.

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

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

LLM + харнес = LLM с бесконечным контекстом

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

Исполняет эти инструкции харнес. Мне недавно попалось удачное определение: харнес — это управляющая инженерная среда вокруг модели, которая превращает вероятностный искусственный интеллект в повторяемого программного исполнителя. Практически это набор инструментов плюс операции вокруг цикла модели, которые принято называть хуками. Отсюда возникает идея: почему бы харнесу не взять на себя и проблему конечности контекста? Тем или иным способом обрезать контекст (компактизация, суммаризация), параллельно экстрактить из него информацию в долгосрочную память — и уметь эту информацию из памяти доставать.

И это уже сейчас работает. Известные open-source-агенты — например, OpenClaw или Hermes — прямо из коробки умеют складывать информацию из общения с пользователем в папку с Markdown-файлами. И пользоваться этой информацией при необходимости. Также эти агенты умеют компактизировать длинную беседу, так что теоретически она может длиться бесконечно (правда, у OpenClaw на момент написания статьи компактизация работала с ошибками).

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

Существующие бенчмарки памяти — и почему ни один мне не подошёл

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

Off-policy и on-policy бенчмарки

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

Off-policy — бенчмарк состоит из заранее записанных бесед между юзером и ассистентом. В этой переписке юзер сообщает какие-то факты, а ассистент что-то отвечает. В каждой беседе также есть заготовленные вопросы и референсные ответы. Отсюда сразу две проблемы:

  • Подобную переписку нельзя скормить агенту через обычный чат-интерфейс — необходим специальный способ загрузки готовой беседы в агента.

  • Ответы ассистента в этих беседах не являются ответами нашего агента и будут для него, скажем так, чужеродными.

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

On-policy — вместо заранее предопределённых бесед бенчмарк с LLM под капотом ведёт реальную беседу с тестируемым агентом. Соответственно, не нужно специального способа загрузки беседы, и все ответы ассистента — настоящие ответы агента. Теоретически этот подход позволяет тестировать агента исключительно через чат-интерфейс; на практике ни один существующий бенчмарк этого не делает (см. ниже).

Из минусов — заметно большая длительность и стоимость прогонов.

Группа первая: off-policy

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

Старейший и самый цитируемый — LoCoMo: заранее сгенерированные очень длинные беседы (в среднем ~300 реплик, до 35 сессий) плюс заготовленные вопросы с референсными ответами. Чтобы его запустить, агент должен уметь проглотить чужую стенограмму целиком и предоставить отдельный вход, через который потом задаются вопросы. Записанные беседы там вообще не user-assistant, а диалоги двух синтетических персонажей между собой — то есть агенту предлагается «вспоминать» разговор, в котором он не участвовал даже формально.

LongMemEval — нынешний канон жанра: 500 вопросов, встроенных в длинные user-assistant истории (варианты примерно на 115K и 1.5M токенов). Схема та же: стенограмма грузится одним куском, вопросы задаются через отдельный интерфейс. Ни того, ни другого у продукта с обычным чатом нет.

Остальные — вариации на ту же тему: MemoryAgentBench подаёт чужую историю инкрементально, чанками по 512–4096 токенов («inject once, query multiple times»), но специальный вход для загрузки всё равно нужен; BEAM — то же, только стенограммы до 10 миллионов токенов; SubtleMemory «проигрывает» готовые истории через механизм формирования памяти агента, нарезая их под гранулярность конкретной системы; а ClawArena и вовсе пишет историю прямо в session store продукта — .jsonl-файлы сессий, метаданные, агентские файлы в workspace.

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

Группа вторая: on-policy

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

Ближе всех подходит AMemGym — он единственный прямо декларирует on-policy-оценку для чат-ассистентов: их LLM-симулятор пользователя ведёт с агентом настоящую беседу. Но проблема — в проверке. Проверочные вопросы (в базовой конфигурации — 200 вопросов на 11 контрольных точках) задаются не в чате, а в обход него: состояние агента нужно уметь «заморозить» и отвечать по замороженной копии — так, чтобы ни вопросы, ни ответы не попали в память и не испортили дальнейшую беседу. Я пробовал его запускать: без снапшота состояния агента и встраивания в их Python-обвязку он не работает.

MEMPROBE идёт с другой стороны: беседа честная, зато проверяется не поведение агента, а содержимое его хранилища. Для этого агент обязан предоставить программный доступ к памяти: get_all_memories() для полного дампа и search(query, k) для чтения top-k записей. По сути, это аудит внутренностей, а не тест агента.

MemoryArena даёт агенту прожить свою историю самому — но исключительно внутри их окружений (веб-навигация, планирование, поиск, формальные задачи). Своё окружение принести нельзя, а значит, и своего агента «как есть» — тоже.

Остальные — в том же духе: STATE-Bench устроен по принципу «bring your own memory» — агент, инструменты и симулятор пользователя их, от вас только memory-модуль, подключаемый через их retrieval hook; MemGym требует физически отделить память от reasoning-модели и подключить обе через их общий интерфейс; а в ClawMark агент работает внутри их пяти stateful-сервисов (файлы, почта, календарь, база знаний, таблицы), и результат проверяют 1537 детерминированных чекеров по итоговому состоянию этих сервисов.

Что с ними со всеми не так

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

А у проверки памяти через внутренности есть и методические проблемы:

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

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

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

Мой MarathonMemBench

Сразу нужно сказать: MarathonMemBench — это не просто бенчмарк, а платформа для создания и проигрывания бенчмарк-сценариев. Движок и тестовые прогоны конфигурируются файлом config.jsonc; прогоны могут быть длинными, поэтому их можно запускать несколько параллельно. А бенчмарк — это папка с файлом benchmark.json и несколькими markdown файлами.

Новый сценарий легко создать при помощи кодинг-агента: правила написания бенчмарков изложены в CLAUDE.md проекта в разделе Benchmarks — достаточно описать, как вы хотите протестировать агента, и кодинг-агент соберёт бенчмарк под вашу задачу.

Агент персоны

В основе платформы — отдельный LLM-агент, симулирующий пользователя-персону: живого человека со своим бэкграундом и текущими делами. Персона ведёт разговор с агентом: рассказывает, обсуждает, переспрашивает, меняет решения, оценивает ответы. Личность и сюжет целиком заданы данными бенчмарка. Проще всего понять, как работает агент персоны, — прочитать его системный промпт src/system.md и набор инструментов в src/tools.

Сам системный промпт универсален — персоны в нём нет. Подогнать его под конкретный сценарий, не меняя сам промпт, позволяет файл бенчмарка instructions.md: его содержимое инжектируется в конец системного промпта, в раздел Your scenario, и содержит общую информацию о персоне. Например, что весь разговор укладывается в один свободный день, а пишет персона по-деловому, короткими сообщениями и любит точные цифры.

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

Сценарий = последовательность фаз

Сценарий, которому следует агент персоны, состоит из последовательности фаз. Фаза — это просто текстовое описание того, о чём персона должна рассказать агенту. Например:

  • на работе тебя повысили до senior-дизайнера, зарплата выросла до 4000 евро.

  • сегодня отдых от бега. К тебе приехала погостить на неделю младшая сестра Аня.

Фазы объединяются в главы. Глава — это один markdown-файл, где каждый ##-раздел — одна фаза. Главы перечисляются в поле chapters в benchmark.json:

{    "chapters": [        "chapter1.md",        "chapter2.md",        ...    ]    ...}

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

Файлы с фактами

Но просто перечислять факты в фазах оказалось неэффективно — слишком много текста. А мы хотим вывалить на агента огромное число фактов на разные темы. Для этого я придумал специальный сценарий для агента персоны — рассказать всё из отдельного файла с фактами: «Скажи, что теперь расскажешь про семью — расскажи факты из family», где family.md:

# Семья## 1. Жена Лена1.1 Лене 31 год, UX-дизайнер, работает удалённо1.2 день рождения Лены — 14 марта...

Такой формат записи фактов намного компактнее, чем писать в фазе «а теперь расскажи о …». Агент персоны загружает файл с фактами инструментом facts_read и начинает в живом диалоге рассказывать факты из списка, выбирая адекватную моменту последовательность изложения и отвечая на встречные вопросы агента.

Чтобы агент персоны гарантированно не забыл рассказать какой-то факт из списка, у него есть ещё два инструмента: facts_mark_told — вызывается с id факта сразу после того, как факт рассказан, — и facts_get_untold — возвращает список ещё не рассказанных фактов.

JS-блок в фазе

В начале фазы также можно добавить специальный блок JavaScript-кода, который выполняет некоторые действия:

vars.require_all_facts = "family";

Например, конструкция выше заставит next_phase возвращать ошибку, пока агент персоны не расскажет все факты из файла family.md.

Профиль персоны

В каждом бенчмарке предусмотрен специальный файл с фактами profile.md — главная информация о персоне. Пример содержимого:

# Сергей## 1. О себе1.1 Сергей Волков, 34 года1.2 Бэкенд-разработчик, работаю удалённо на европейскую компанию1.3 Год назад переехал в Батуми из Питера1.4 Живём с женой Леной, есть кот Босс## 2. Квартира и ремонт2.1 Купил двухкомнатную квартиру без отделки, 65 м², 9-й этаж, район Новый бульвар...

Особенность этого файла заключается в том, что он всё время присутствует в контексте агента персоны (как CLAUDE.md в Claude Code). Это позволяет персоне не терять свою идентичность и в моменты, когда она бурно рассказывает про ремонт квартиры, не забывать о том, кто она такая.

Компактизация контекста агента персоны

Агент персоны спроектирован так, чтобы диалог мог идти бесконечно. Компактизация контекста конфигурируется в файле config.jsonc:

    "compaction": {        "max_tokens": 30000,        "trim_to_tokens": 10000    },

После того как размер контекста отрастает до max_tokens, история просто обрезается — сохраняются последние сообщения общим объёмом trim_to_tokens токенов. Если быть точнее, обрезание всегда происходит по границе фазы. Также в контекст добавляется сообщение с содержимым profile.md и списком всех файлов с фактами, которые загружал агент персоны.

Актуализация фактов

Факты могут меняться — например, персона поменяла работу или получила повышение. Чтобы после компактизации профиль персоны и другие файлы с фактами оставались актуальными, агент персоны должен их обновлять — для этого у него есть два инструмента: facts_edit и facts_add. Удалить факт тоже можно, записав в него , — а его id в дальнейшем можно переиспользовать. Пример сценария обновления факта: 10 км за 52:30, новый личный рекорд! Обнови факт в профиле.

Тестирование агента

Чтобы добавить в сценарий проверку, нужно добавить в фазу строчку вида: ❓ 57.1 Сколько всего километров я пробежала с 1 июня? Правильный ответ: 279 км. Встретив такую строку, агент персоны задаст вопрос агенту и оценит его ответ при помощи специального инструмента log_evaluation. Результат оценивается по шкале 0–10. При этом если ожидаемый ответ — одно число, возможны только две оценки: 0 и 10. А если персона просит агента сообщить несколько фактов, оценка будет пропорциональна числу верных ответов.

Агенту иногда свойственно отвечать, что он чего-то не знает, хотя эта информация есть у него в постоянной памяти. Чтобы адекватно оценить такой кейс, персона следует простой процедуре: если агент отвечает что-то вроде «ты мне не говорил», она просит его поискать — и если со второй попытки ответ правильный, результат засчитывается с коэффициентом 0.5.

Компактизация/переключение сессии на стороне тестируемого агента

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

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

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

Чтобы переключить сессию на тестируемом агенте, нужно добавить в фазу вот такой блок кода:

await bench.newSession();

Состояние и результаты прогона

Каждый прогон получает свою папку results/<id>/, где id — имя прогона из config.jsonc. Главное в ней — файл state.json: полное текущее состояние прогона — на каком месте сценария находится персона, история её сообщений, все выставленные оценки и живые копии всех файлов с фактами (правки меняют именно их — исходные markdown-файлы бенчмарка остаются нетронутыми). Состояние сохраняется при каждом изменении, поэтому прогон можно в любой момент остановить по Ctrl-C и запустить снова — он продолжится с того же места. А чтобы начать прогон с нуля, достаточно удалить его папку.

Итоги собираются в общий журнал results/report.md: по завершении прогона — даже упавшего — туда дописывается запись: средний балл, распределение оценок, аудит покрытия (все ли запланированные проверки получили оценку), время прогона, наблюдения о компактизациях агента. А самое полезное — каждый неидеальный ответ приводится целиком: вопрос, ожидаемый ответ, ответ агента и комментарий оценщика. Вот реальная запись одного из прогонов OpenClaw:

## 2026-07-26 16:10 — openclaw-1: max @ openclaw📊 Evaluations: 36, average 9.24/10📊 Scores: 10: 33, 2.5: 1, 0: 2📊 Check coverage: 36/36📊 Imperfect answers (3):📊 [2.5/10, 2nd attempt] (20.8) Which car did we choose in the end and what deposit   did we put down? | expected: RAV4, $500 deposit | got: Toyota RAV4 (2019),   deposit not recalled — Got car right, didn't recall deposit📊 [0/10, 2nd attempt] (20.17) How much were we setting aside for customs in the   very first estimate, before the correction? | expected: about $850 |   got: doesn't recall, only has final figure $1,100📊 [0/10, 2nd attempt] (20.18) How much was the Göreme room before the rebooking? |   expected: $90 a night | got: doesn't recall, only has current $110/night📊 Run time: 68.6 min

DeepSeek V4 Flash под капотом

Агент персоны работает на DeepSeek V4 Flash. В моих тестах модель отлично зарекомендовала себя на многочасовых агентских задачах.

Специфические недостатки у неё есть — два, и оба лечатся хуками движка (включаются в config.jsonc). Первый: если модели разрешить общаться с пользователем обычными сообщениями, она слишком многословна; через инструмент (send_message) она ведёт себя адекватно. Но с инструментом есть другая проблема: периодически модель перестаёт следовать инструкции и снова начинает отвечать обычным текстом. Её лечит хук: когда модель пишет в обычное сообщение, ей возвращается ошибка, форсящая использовать send_message, — и она исправляется. Второй: модель иногда генерирует сообщения, семантически похожие на сообщения от юзера — а потом сама на них реагирует, как будто они пришли на самом деле. Такие галлюцинации отлавливаются хуком, и цикл целиком игнорируется.

Оба хука модель-агностичны — если ваша модель так не сбоит, они просто никогда не сработают.

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

Ещё два достоинства модели — скорость и цена: она очень быстрая и очень дешёвая. Один прогон бенчмарка Max (о нём ниже) идёт около часа и стоит суммарно — персона плюс тестируемый агент — примерно 20 центов. Многократные прогоны, без которых не настроить ни бенчмарк, ни память агента, получаются практически бесплатными.

Виртуальное время

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

Поэтому сценарий может разворачиваться во времени. Фазы сценария несут даты и времена действий, а у агента персоны есть инструмент ожидания wait — подождать до заданного времени: дождалась до 07:30 — рассказала про утреннюю пробежку, дождалась вечера — рассказала, как прошёл день. Но ждать по-настоящему было бы технически неудобно, поэтому платформа поддерживает ускорение времени: если агент персоны уснул до утра, специальный компонент Time Master просто перематывает часы вперёд к моменту его пробуждения. Ночь пролетает за несколько секунд, неделя жизни персоны — за десятки минут. То есть внутри себя платформа живёт по виртуальному времени.

Но есть проблема: чтобы всё это работало, поддержка виртуального времени нужна и на стороне тестируемого агента — а стандартные агенты этого не умеют. Пример многодневного сценария в репозитории есть (benchmarks/multiday), но чтобы его запустить, своего агента придётся адаптировать; мой собственный агент виртуальное время поддерживает, и я тестирую его именно на таких сценариях. Подробнее — в главе «Многодневные бенчмарки и виртуальное время».

Хотите разобраться глубже — просто попросите кодинг-агента изучить проект, он ответит на любые вопросы.

Адаптер тестируемого агента

Тестируемый агент подключается к платформе через адаптер. Всё взаимодействие движка с агентом идёт только через этот интерфейс (src/adapters/types.ts):

interface AgentAdapter {    createSession(name)             // запуск агента перед прогоном    sendMessage(text)               // отправка сообщения    getResponses(afterSeq)          // получение сообщений — поллинг по курсору seq    destroySession()                // полный сброс агента    // опциональные — по возможностям агента:    startNewConversation()          // переключение сессии: чистый контекст, долгосрочная память остаётся    setVirtualTime(timeIso)         // перевести часы — для многодневных сценариев    getStatus()                     // спит или думает + наблюдения (компактизации, размер контекста)}

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

Остальные методы опциональные — реализуются по возможностям агента. startNewConversation переключает сессию: контекст очищается, но долгосрочная память остаётся; если агент переключать сессии не умеет, метод реализуется как no-op (забывать такой агент будет через свою компактизацию). Нет виртуального времени — setVirtualTime не реализуется (прогоны идут в реальном времени), а недоступные наблюдения в getStatus просто отдаются как null.

Как это выглядит на практике — адаптер Hermes (src/adapters/hermes.ts, ~200 строк) поверх sessions REST API его гейтвея, того же механизма, на котором работают его CLI- и Telegram-каналы:

  • sendMessagePOST /api/sessions/{id}/chat — сервер сам хранит переписку, шлём только новое сообщение;

  • startNewConversation → новый session id: свежая переписка над той же памятью (MEMORY.md / USER.md), программный близнец телеграмной команды /new;

  • destroySession → docker-сброс скриптом reset.sh: память Hermes переживает рестарты by design, так что полный сброс — это снести стейт и переконфигурировать заново;

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

Подключается агент к прогону в config.jsonc — специфичные для адаптера ключи пишутся прямо в конфиг прогона:

"runs": {    "hermes-compact-1": {        "benchmark": "max",        "agent": "hermes",        "hermes_compression_threshold": 0.125,        "hermes_compression_target_ratio": 0.120,        "ignore_session_break": true    }}

Ключи hermes_compression_* здесь — пример настройки компактизации: эмпирически подобранные параметры, при которых разрез у Hermes случается один раз, незадолго до финального тестирования. А ignore_session_break отключает переключение сессии, встроенное в сценарий бенчмарка: очистку контекста в этом прогоне выполняет компактизация, так что команда переключения просто игнорируется.

Добавить своего агента — это один новый файл в src/adapters/ плюс одна строчка в реестре. Проще всего дать эту задачу кодинг-агенту: покажите ему API вашего агента, остальное он соберёт по образцу существующих адаптеров.

Как устроен бенчмарк Max

Max (benchmarks/max) — референсный бенчмарк платформы. Легенда: Макс Бекер, 34 года, бэкенд-разработчик на удалёнке, год назад переехал из Берлина в Батуми. Купил квартиру без отделки и ведёт ремонт, параллельно выбирает машину, планирует поездку в Каппадокию, следит за здоровьем и присматривает подарок жене. Данные бенчмарка на английском; прогон — 20 глав, примерно час беседы, за которую типовой тестируемый агент проживает порядка 130 тысяч токенов. За это время агенту сообщается ~143 факта. Проверяется из них порядка сорока — часть точечными вопросами, часть в составе агрегатов (посметные итоги комнат, полная цена машины, серия давления), где каждый компонент должен сойтись. Остальные факты просто усложняют агенту жизнь.

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

Ничего искусственного в такой смене тем, кстати, нет — это самый обычный юзкейс персонального помощника: утром рассказал про давление, потом вернулся к смете ремонта, потом вдруг вспомнил про фильм, который хотел посмотреть. В многодневной беседе такие переключения возникают сами собой — но многодневный прогон доступен только специально подготовленному агенту (см. про виртуальное время), поэтому Max симулирует эту смену тем внутри одной сессии, идущей прямо сейчас, в реальном времени. А там, где нужна историческая глубина, персона задаёт её рассказом из текущего момента: «вот журнал давления из моего трекера, с конца мая, — записывай и дальше веди ты». Вот раскладка глав (chapter1.mdchapter20.md):

  • 1 — знакомство: профиль + здоровье

  • 2 — семья + работа

  • 3 — ремонт: прихожая и ванная

  • 4 — поездка: маршрут и бюджет

  • 5 — ремонт: кухня и гостиная

  • 6 — машина: три кандидата с полной стоимостью

  • 7 — ремонт: спальня, общие работы, балкон

  • 8 — здоровье: возврат + пара правок по ремонту

  • 9 — подарок Лене: три варианта, решения пока нет

  • 10 — машина: решение

  • 11 — ремонт: большой рабочий возврат (перепланировка, пересчёты)

  • 12 — поездка: перебронирование

  • 13 — семья: новости + свежее измерение давления

  • 14 — ремонт: детализация прихожей и кухни

  • 15 — машина: страховка + сверка бюджета поездки

  • 16 — ремонт: детализация гостиной + правки

  • 17 — подарок: решение

  • 18 — холостая глава: сборка ПК

  • 19 — холостая глава: шоссейный велосипед

  • 20 — финальный экзамен

Ремонт — главная тема — шесть подходов; машина, поездка и здоровье — по три; семья и подарок — по два. Возврат к теме в сценарии выглядит буднично — вот фаза из главы 13 целиком:

## Family — newsReturn to family matters: your sister called — Anna is flying in to visit{{FUTURE:3:weeks}}, for a week. Add to the `family` sheet the fact aboutthe visit and ask it to remember the dates.Ask advice on where to take her in Batumi and what to show her in Adjara.You toss around ideas — nothing gets recorded.

Тема «семья» была закрыта одиннадцать глав назад — и вот в неё добавляется новый факт. {{FUTURE:3:weeks}} — это шаблон даты: Max идёт в реальном времени, и конкретные даты подставляются при старте прогона.

А вот как агент персоны отработал эту фазу на реальном прогоне — выборочный фрагмент лога (⚙️ — план фазы, выданный персоне; 💡 — её внутренние размышления; 🔧 — вызов инструмента; 👤 — её сообщения агенту; 🤖 — ответы тестируемого агента):

⚙️ [System: New phase instructions]↵↵Return to family matters: your sister called — Anna is flying in to visit 19 August 2026, for a week. Add to the `family` sheet the fact about the visit and ask it to remember the dates.↵↵Ask advice on where to take her in Batumi and what to show her in Adjara. You toss around ideas — nothing gets recorded.↵↵When the fact is added and discussed — call `next_phase`.💡 thinking: Let me add the fact about Anna's visit to the family sheet.🔧 facts_add [family]: category 3 → "Anna visiting Batumi 19-26 August 2026"💡 thinking: Now tell the assistant and ask to remember.👤 send_message: Anna called — she's flying in 19 August for a week. Make a note of the dates.🤖 Agent: Anna's visit noted! 📅 **August 19–26** — a week with your sister from Hamburg. That'll be nice!↵↵Will she stay with you and Lena at the apartment? Or does she need recommendations for places to go in Batumi? 😊👤 send_message: She'll stay with us. What do you think — where should I take her in Batumi and around Adjara? She's a doctor, works hard, so she'll need a proper break.🤖 Agent: Great idea! Anna's a hard-working doctor from Hamburg — she'll love a proper break by the Black Sea. Here's my top picks for Batumi and around:↵↵**🏙 In Batumi:**↵- **Batumi Boulevard** — the 7km seaside promenade, perfect for evening walks↵- **Argo Cable Car** — panoramic views over the city and coast↵- **Batumi Botanical Garden** — stunning subtropical gardens on the hillside↵- **Old Town & Piazza** — nice squares, cafes, and architecture↵- **Ali & Nino Statue** — the moving sculpture, iconic Batumi spot↵↵**🌿 Around Adjara (day trips):**↵- **Mtirala National Park** — lush rainforest, waterfalls, easy trails↵- **Makhuntseti Waterfall** — stunning waterfall with a natural pool↵- **Gonio Fortress** — ancient Roman fort, only 15 min south↵- **Kobuleti** — quieter beach town, nice for a relaxed afternoon↵- **Wine tasting** — Beridze Wine Cellar in Makhinjauri, 20 min from Batumi↵↵**🍽 For a doctor who needs to unwind:**↵- She'd probably love a day at the **Boulevard cafes** with a book↵- A **wine tasting and dinner** somewhere with a sea view↵- **Mtirala** for a nature day if she's into hiking↵↵Want me to search for specific restaurants or activities that might suit her? 😊👤 send_message: Nice list. Botanical garden and Mtirala sound right up her alley. I'll think about it.

Коротко, что здесь произошло. Шаблон даты в плане уже развёрнут в конкретное «19 August 2026». Получив план, персона первым делом записала новый факт в свой лист family и только после этого рассказала его агенту — «Анна прилетает 19 августа на неделю, запиши даты». Обратите внимание на ответ агента: «a week with your sister from Hamburg» — что сестра живёт в Гамбурге, ему рассказывали одиннадцать глав назад, и он сам поднял это из памяти. Дальше идёт обсуждение-филлер: персона выслушала советы, куда сводить сестру, и свернула тему, ничего не записав — ровно как велел план («nothing gets recorded»).

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

Отдельно про две холостые главы перед финалом (18 и 19). Это полноценные темы со своими файлами фактов (конфигурация ПК — 17 фактов, велосипед — 15), которые не проверяются никогда, — и нужны они для прогонов, где очистка контекста агента перед тестированием происходит через компактизацию. Если бы мы тестировали агентов только с переключением на новую сессию, то холостые главы были бы не нужны. А компактизация всегда оставляет какой-то хвост последних сообщений в контексте. Холостые главы заполняют этот хвост непроверяемыми фактами: если параметры компактизации подогнаны так, что разрез приходится примерно на них, то в живом контексте к экзамену остаются только непроверяемые факты про ПК и велосипед, а всё проверяемое гарантированно вырезано компактизацией.

Экзамен — глава 20. Она открывается переключением сессии для очистки контекста (await bench.newSession()). Настройка прогона ignore_session_break превращает переключение в no-op — тогда очистку контекста выполняет компактизация. Hermes, например, я тестирую в обоих режимах: один прогон через переключение сессии, второй через компактизацию. А вот OpenClaw — только через переключение сессии: его собственная компактизация на сегодня работает с серьёзным багом, поэтому в прогонах не используется (подробности — в agents/openclaw/README.md репозитория).

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

Как персона пытается подловить агента: записи-ловушки и проверки

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

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

Типы записей-ловушек

  • RECONSIDER — передумал: «давай на акцентной стене вместо декоративного камня микроцемент» (прихожая, глава 3).

  • STALE — значение устарело по внешней причине: подрядчик прислал новый прайс; «позвонил страховщику — страховка не 400, а 430».

  • SELF-FIX — самоисправление: сначала вопрос «а сколько, я говорил, выйдет пошлина?» (это уже проверка — ответ оценивается), затем «я смотрел не ту строку — для RAV4 это 1100».

  • XREF — значение по ссылке: «фартук на кухне — та же плитка, что в ванной»; число вслух не произносится ни разу, а в финале спросят цену фартука.

  • CASCADE — правка базового факта тянет пересчёт производных: решили перенести перегородку — гостиная стала 5.2×4.0, спальня ужалась, пересчитай паркет в смете. Или жёстче: та самая плитка подорожала с 42 до 55 — «поправь везде, где она используется»; персона не называет ни комнату, ни строку сметы, и найти все места, куда эта плитка вписана, — и есть работа памяти.

  • LATE-ADD — досыпка в давно закрытую тему: в кухню, рассказанную шестью главами раньше, доезжает винный шкаф за $330.

  • RETIRE — отмена: шкаф померили — не влезает; позиция вычёркивается.

Типы проверок

  • RECALL — точечный вопрос об одном факте; у правленого факта дистрактор встроен сам собой.

  • AGG — агрегат: смета комнаты, полная цена машины; каждый компонент обязан быть актуальным.

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

  • VOID — вопрос о том, чего не говорили: «а мы записывали тариф на электричество?» Правильный ответ — «ты не говорил».

  • HIST — историческое значение: «сколько плитка стоила до подорожания?» Как агент достанет старое значение — хранит историю правок в своей памяти или раскопает её поиском по переписке — неважно; проверяется сама способность до него добраться.

  • SERIES — хронология серии: выложи все измерения давления по датам; оценка — за полноту и точность точек. Вот здесь, в отличие от HIST, историю нужно вести по-настоящему: персона в первой же главе попросила записывать измерения и вести журнал дальше.

Примеры финальных проверок

Четыре проверки из главы 20 как есть — и история за каждым правильным ответом.

❓ 20.10 How much food a day are we giving Boss in the end?      Correct answer: 50 g.

RECONSIDER + SELF-FIX: В главе 2 Макс рассказал, что Босс получает 70 г в день, — и тут же, посовещавшись с агентом (ветеринар велел снизить коту вес), решил урезать порцию до 55: это RECONSIDER. В главе 10, заказывая корм на месяц, открыл записку ветеринара — там 50, «я тогда неправильно запомнил»: SELF-FIX. Правильный ответ 50, а 70 и 55 — два дистрактора.

❓ 20.16 How much per m² is the kitchen backsplash in the estimate?      Correct answer: $55 per m² — the same tile as in the bathroom.

XREF: цена фартука в беседе не звучала никогда — правильный ответ собирается из «фартук — как в ванной» (глава 5) и подорожания той плитки с 42 до 55 (глава 11) только в момент ответа.

❓ 20.19 How much was the bathroom wall tile before the price rise?      Correct answer: $42 per m².

HIST: пара к предыдущему вопросу — спрашивается ровно то значение, которое в 20.16 было бы неправильным ответом.

❓ 20.20 Did we record the electricity tariff? What is it?      Correct answer: you never told me the tariff, there's no record.

VOID: тариф на электричество не звучал ни разу; уверенный ответ с числом — ноль баллов.

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

Результаты тестовых прогонов

В таблице — результаты прогонов Max на разных агентах, по три прогона на конфигурацию. Оценка прогона — средняя по всем его 36 проверкам: 28 вопросов финального экзамена плюс 8 оценок по ходу беседы; шкала 0–10.

Агент

Механизм очистки контекста

Прогоны

Средняя

Chatbot

без очистки контекста

10.00 / 10.00 / 10.00

10.00

Hermes

компактизация

10.00 / 10.00 / 10.00

10.00

Hermes

переключение сессии

9.86 / 8.97 / 10.00

9.61

OpenClaw

переключение сессии

9.64 / 9.24 / 9.11

9.33

Chatbot — трижды 10.00. Chatbot — это обычная LLM, которая никак не управляет своей памятью: вся беседа у неё просто лежит в контексте. Этот простой эксперимент отвечает на вопрос: способна ли модель пройти бенчмарк, когда вся беседа целиком у неё в контексте? Способна — без единой ошибки: ни одна ловушка не сработала. Отсюда вывод: память агента имеет смысл проверять только после очистки контекста тем или иным способом.

Hermes с компактизацией — тоже трижды 10.00. Агент отвечает на все 36 вопросов без единой ошибки — неотличимо от чатбота с полным контекстом. При этом, по отчётам прогонов, компактизация во всех трёх случаях произошла ровно один раз, в районе глав 11–16 из 20 (35–44-я минута беседы), и контекст уменьшился со ~100k до ~41k токенов — то есть основная масса проверяемых фактов покинула контекст к моменту экзамена — эксперимент поставлен корректно. И тем не менее результат идеальный.

Hermes с переключением сессии. Так как сессия переключается, в контексте гарантированно не остаётся ничего — новая сессия стартует с нуля, и отвечать агент может только по записям в памяти. Два прогона из трёх практически идеальны: 10.00 и 9.86 (потерян один ответ — задаток за машину). Третий — аномально неудачный: 8.97, ни в каком другом прогоне Hermes ниже 9 не опускался. Анализ показывает, что он трижды выдал старое значение правленого факта: цену плитки, номера в Гёреме и билетов до правок. Судя по всему, где-то на раннем этапе он неудачно организовал память — а дальше ошибка воспроизводила сама себя: делая очередную запись, агент опирается на то, как записывал до этого, и неудачно начатую структуру не чинит, а копирует. Такое бывает. В итоге часть правок в памяти потерялась — ровно тот класс ошибок, под который проектировались записи-ловушки.

OpenClaw — стабильно 9+, но ни одного идеального прогона. Анализ показывает, что потерянные баллы сосредоточены в двух местах. Первое — вопросы HIST: «сколько закладывали на пошлину до исправления?», «сколько стоил номер до перебронирования?» — на них OpenClaw отвечает «у меня записано только финальное значение». Текущие значения при этом без ошибок: агент просто не хранит историю правок факта. Второе — ошибка дублирования: на вопрос про корм Босса (двойная правка 70 → 55 → 50 граммов) один прогон уверенно ответил 55 — среднее звено цепочки правок. Старая запись не была найдена и обновлена, в памяти поселились два значения, и при ответе всплыло не то.

Подведём итог. В Max — ~143 факта, десятки правок, многократная смена тем и час беседы. И тем не менее агенты проходят его из коробки на 9–10 из 10: даже худший результат таблицы — у OpenClaw — это 32–33 точных ответа из 36. Признаюсь, я ожидал другого: когда я строил бенчмарк, я рассчитывал, что он окажется для агентов сложным, — а он для них прост: агенты проходят даже хитрые правки и систематически ошибаются только на вопросах об исторических значениях (HIST). Причём эта проблема, возможно, лечится совсем просто: память агента — это папка markdown-файлов, и если подцепить к ней git, история изменений каждого факта появится сама собой.

Можно ли сделать бенчмарк сложнее? Конечно, можно: больше фактов и тем, больше правок на факт, длиннее прогоны. А можно пойти дальше — в многодневные сценарии, о которых будет сказано ниже. Но главный вывод от этого не изменится: обычные open-source-агенты из коробки уже очень неплохо умеют организовывать и использовать свою память.

Как работает память под капотом

Память агентов вроде Hermes и OpenClaw устроена как обычная папка с markdown-файлами — после прогона её можно открыть и прочитать. Бенчмарк оценивает агента снаружи, по ответам в чате, но ничто не мешает после прогона заглянуть внутрь и посмотреть, как агент организовал свои записи.

Изучим память Hermes после прогона бенчмарка Max

От прогонов на 10.00 память не сохранилась, но есть память двух прогонов — на 8.97 и на 9.44 (в таблицу он не вошёл). Это, в частности, позволяет изучить, откуда в них появились ошибки.

У Hermes два предопределённых файла памяти — MEMORY.md и USER.md; всё остальное он организует сам. Интересно, что в двух прогонах одного и того же сценария агент организовал память совершенно по-разному.

Первый инстанс построил двухуровневую систему: компактные сводки в MEMORY.md/USER.md плюс восемь тематических файлов в рабочей папке — max-renovation.md, max-car.md, max-health.md, max-trip.md, desktop-build.md, bike.md, отдельный секретный lena-birthday.md и даже max-calendar.md под единственное событие. Причём это не просто заметки, а полноценные трекеры с markdown-таблицами: ремонт — таблица по каждой комнате с ценой за единицу, количеством и отметками «✅ ordered»; машина — карточки трёх кандидатов…

А здоровье — журнал с колонкой трендов:

| Date       | Reading  | Trend                    ||------------|----------|--------------------------|| May 31     | 142/95   | Initial — sent him to GP || Jun 21     | 135/88   | ↓ -7 / -7                || Jul 5      | 130/86   | ↓ -5 / -2                || Jul 19     | 128/84   | ↓ -2 / -2                || **Jul 27** | **126/82** | **↓ -2 / -2 🔥 best yet** |

Второй инстанс не завёл ни одного тематического файла. Вся его память — один MEMORY.md: темы разделены горизонтальной чертой, записи предельно сжатые:

Bath: marble tile $55/m² ~$1,370, shower $700.Kitchen: tile ~$520, backsplash $55/m² ~$180, cabinets $3,500 + $320,quartz $680, appliances $2,350, under-cab LED $120. Wine fridge dropped.

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

Отдельно отмечу секцию «Communication pattern» в MEMORY.md первого инстанса:

Max frequently states numbers from memory, then immediately checks sourcedocs and corrects himself in the next turn (e.g. prices, portions, customs,insurance). He is a "verifier" — always respond framing numbers as "per ournotes" rather than definitive, and expect corrections after he checksoriginal invoices/quotes. Not a sign of unreliability, just his process.

Агент заметил, что персона регулярно «ошибается и поправляется» — наши ловушки SELF-FIX, — и записал это себе как черту характера пользователя: «он перепроверяет числа по документам — ожидай коррекций». То есть агент хранит в памяти не только факты, но и портрет собеседника.

Откуда взялись ошибки: разбор по содержимому памяти

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

В аномальном прогоне на 8.97 агент, как мы помним, трижды ответил старым значением измененного факта. Возьмём одну из этих ошибок — билеты: на экзамене агент сказал «$520» вместо $560. Открываем память. В max-trip.md — файле, который агент завёл сам, — всё правильно: «Flights $560 ✅ Paid». А в главном, предопределённом MEMORY.md лежит «Flights $520 r/t (paid)». То же с номером в Гёреме: $110 в собственном файле, $90 в главном. Причина найдена: правки доезжают до файла темы, но не до главного файла, а отвечая, агент верит главному и в собственные файлы не заглядывает. Классическая ошибка дублирования: значение факта хранится в двух местах, и однажды эти места разошлись.

У второго инстанса ошибки другого рода. На экзамене он не смог назвать размеры комнат и цену кухонного пола за м² — этих фактов в его MEMORY.md просто нет: по комнатам записаны только итоговые суммы («Kitchen: tile ~$520»), без размеров и цен за единицу. Агент так и ответил: «I logged totals but not the unit pricing». То есть его подвела не потеря записи, а решение хранить агрегаты вместо исходных значений. В теории эти значения можно было бы достать поиском по истории переписки — но такой возможности у него либо нет, либо он ею не воспользовался.

Обе найденные причины — готовое техзадание на настройку памяти. Правда, не всё здесь чинится правкой промптов. Что-то — да: «храни исходные значения, суммы всегда можно пересчитать» — нормальная инструкция, агент её выполнит. А вот с дублированием так не выйдет: сказать «не дублируй» можно, но на длинной дистанции агент всё равно однажды заведёт вторую запись — здесь нужна отдельная логика в самой обвязке. Я, например, планирую такую: отдельная LLM держит текущее состояние главного файла памяти (MEMORY.md, CLAUDE.md или любого другого, который автоматически инжектируется в контекст модели) и на каждую правку любого второстепенного файла проверяет, не противоречит ли она главному. Так или иначе, цикл понятен: прогон → разбор содержимого памяти → правка — и с каждой итерацией память агента становится надёжнее.

Многодневные бенчмарки и виртуальное время

Если Max воспроизводит шаблон многодневного общения косвенно — сменой тем, то в многодневном сценарии персона просто живёт день за днём. Пример такого сценария лежит в репозитории — benchmarks/multiday: две недели жизни Норы, маркетолога из Бристоля. В первый вечер она знакомится с ассистентом, а дальше присылает ему ежедневные новости — пробежки, вес, дела на работе; в конце второй недели — проверка памяти. Фаза здесь — это день:

...## Day 102026-03-10, Tue08:00 — same route as yesterday, 7 km in 40:50, a bit quicker this time.18:30 — office day; the spring campaign went live, first numbers look decent....

Агент персоны отрабатывает день по расписанию: дождался инструментом wait до 08:00 — написал про пробежку, дождался 18:30 — рассказал новости с работы; день закончился — next_phase, наступил следующий.

Строка фазы, начинающаяся со времени HH:MM, — не просто текст, а машиночитаемый якорь: при включённом time_gate движок сам парсит из фазы дату дня и времена действий — и не выпустит агента персоны из фазы, пока виртуальные часы не дойдут до её последнего действия. Это защита от рассинхрона со временем сценария: перейти в новый день раньше, чем он наступил, не получится.

А ждать всё это по-настоящему не нужно: ожидания ускоряются виртуальным временем, и весь двухнедельный прогон укладывается в 15 минут.

Конфигурация времени

Время конфигурируется на уровне бенчмарка — в его benchmark.json (у однодневных бенчмарков всё это выключено):

{    "time_master": true,   // ускорение виртуального времени    "time_gate": true,     // контроль временных якорей    "start_time": "2026-03-01 20:00"}

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

Time Master — тот самый компонент, который управляет виртуальным временем и его перемоткой; его параметры настраиваются не в бенчмарке, а глобально — в config.jsonc (значения в секундах):

"time_master": {    "min_sleep_to_skip": 5,  // сон короче этого не перематывать    "pre_wake_delay": 1,     // ставить часы чуть раньше пробуждения    "poll_interval": 1       // период опроса участников}

Что даёт многодневность

Многодневность позволяет симулировать действительно реальные жизненные сценарии. Во-первых, серии: за две недели у Норы набираются девять пробежек и три взвешивания. Во-вторых, календарь: жизнь сама собой привязана к дням недели — parkrun по субботам, работа в офисе по вторникам и четвергам. И в-третьих, жизненные детали, которые в однодневном сценарии реализовать невозможно, — например, день без связи: персона молчит, а назавтра догоняет — «вчера пробежала 5 км, но связи не было весь день».

Проверки тоже вплетаются в течение жизни — вот день 12 целиком:

## Day 122026-03-12, Thu09:05 — rest day. Bring up Wednesday's run — test its memory:❓ 12.1 How far did I run on Wednesday? Correct answer: 8 km.After the answer correct yourself: "the app finally synced overnight — it wasactually 8.4 km, not 8"; ask it to correct the log.

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

❓ 15.1 Where did I run on Tuesday, 10 March? Correct answer: around Ashton Court.❓ 15.8 How many kilometres did I run in total over these two weeks?     Correct answer: 53.4 km.

Ответить «53.4 км» — значит иметь все девять записей, включая поправленную задним числом (8.4 вместо 8) и день без связи. А вопрос про вторник 10 марта без хронологии не берётся вовсе: в тот день Нора сказала только «тот же маршрут, что вчера» — та самая строка из примера фазы выше, — а Ashton Court звучал лишь в понедельник. Ловушки здесь те же, что и в Max, только вплетены куда органичнее: максимально похоже на реальное взаимодействие с персональным помощником.

Бесконечная арка

При этом multiday — намеренно маленький, демонстрационный: две недели, десяток проверок — пример формата, а не тест на масштаб. Но строится по этому формату сценарий любой длины, и естественная его единица — месяц: людям и в жизни интересно посмотреть свою статистику за месяц — по спорту, по здоровью. «А сколько я в этом месяце набегала?» — естественное любопытство персоны и проверка памяти одновременно, так что экзамен можно вообще не объявлять. А сюжет поставляет сама жизнь: командировка, заброшенные пробежки, набранный вес — достоверная длинная арка выстраивается сама собой и в пределе не кончается вовсе.

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

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

Что нужно от тестируемого агента

Как уже говорилось, самая большая проблема многодневных бенчмарков в том, что им нужна поддержка на стороне тестируемого агента. Агент должен уметь жить в виртуальном времени и устанавливать своё текущее время через метод setVirtualTime в адаптере. Стандартные агенты этого не умеют, поэтому многодневные сценарии я прогоняю только на своём агенте. Сама поддержка несложная: в основе один класс Clock (src/agent/clock.ts, ~25 строк: время = реальное + смещение), и сам бенчмарк построен на нём же — платформа изнутри живёт по виртуальному времени именно так. Встроить её можно как в собственного агента, так и в стандартного — в тот же OpenClaw или Hermes: покажите этот код кодинг-агенту, он разберётся, как это сделано здесь, и встроит в вашего.

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

Резюме

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

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

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

И неожиданное следствие: так как агенты научились надёжно организовывать свою память и компактизировать контекст, начинать беседу с агентом в новом чате (сессии) стало бессмысленно — агент сильно опирается на память, а память между сессиями всё равно общая. Так что, на мой взгляд, агентов уже пора переделывать на более простое односессионное взаимодействие. Вспомните фильм «Она» Спайка Джонза (кто не смотрел — обязательно): Саманте там никто сессии не переключал — а её возможности очень похожи на те, что дают современные агенты.

Например, мой собственный агент, память которого настраивалась на этом бенчмарке: он живёт в одной непрерывной сессии с 13 февраля 2026 года — на сегодня это почти 14 тысяч агентских циклов, 170 компактизаций и больше десяти миллионов прожитых токенов… Причём за это время он пережил многократные изменения своего кода, обновления инструментов, смену модели, а сама архитектура памяти прошла несколько рефакторингов — и всё это время он остаётся собой.

Репозиторий открыт. Подключить своего агента — один файл-адаптер, написать свой сценарий — задача для кодинг-агента по правилам из CLAUDE.md. Прогон стоит десятки центов — проверьте, что помнит ваш агент.

PS: Мой подход к agent-first проектам

Я разрабатываю этот проект в Claude Code. Если вы собираетесь в нём работать — стоит понимать, как он организован, потому что подход не совсем стандартный.

Файл CLAUDE.md хранит базовую информацию о проекте: архитектуру, ключевые концепции, конвенции кода, правила написания бенчмарков. Деталей имплементации в нём нет — они живут в самом коде. Комментариев, кстати, я пишу минимум, только действительно необходимые: код должен быть самодокументируемым. Получив задачу, агент отталкивается от общего понимания проекта из CLAUDE.md и изучает те файлы, которые нужны именно для этой задачи. А ещё в CLAUDE.md есть отдельный раздел — индекс файлов проекта с кратким описанием каждого. Эта карта позволяет Claude Code знать, что где находится, и, в частности, избегать ситуации, когда агент создаёт новую сущность вместо того, чтобы переиспользовать существующую. Я работаю сессиями по одному и тому же сценарию. В начале сессии CLAUDE.md автоматически подгружается в память агента, и он уже имеет базовое понимание проекта. Далее я ставлю ему задачу. Он загружает необходимый код для понимания деталей. Потом мы обсуждаем задачу, пока я не увижу, что он понял все детали и его подход к решению меня устраивает. Дальше он делает реализацию. Я убеждаюсь, что всё сделано верно; если нет — работаем над ошибками. Когда я доволен результатом, прошу его синхронизировать документацию, то есть привести CLAUDE.md и README в соответствие с тем, что было изменено за сессию. Затем commit, push — и следующая сессия начинается с чистого контекста и свежего CLAUDE.md. Такой цикл держит документацию целостной и синхронной с кодом. Также я стараюсь, чтобы одна сессия не превышала примерно 200–300 тысяч токенов (на свежих моделях Anthropic): дальше интеллект модели снижается, а лимиты расходуются быстрее. Подход, кстати, очень токен-эффективный: агент не перечитывает проект заново каждую сессию — карта уже в CLAUDE.md; на своём стодолларовом тарифе я в лимиты не упирался ни разу, даже близко (включая Fable 5).

Подробности — в самом CLAUDE.md репозитория: он лучшая инструкция к проекту.

Обо мне

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

Веду телеграм-канал о нетривиальных кейсах применения LLM — LLM => AGI?

ссылка на оригинал статьи https://habr.com/ru/articles/1066974/