English translation is not available yet. Showing Russian content.
Что такое Session Management в Harness и какие стратегии (TTL, LRU, GC)?
Краткий тезис
Session Management в Harness — это шестой слой (из семи) в архитектуре RAG|agentic RAG, отвечающий за поддержание контекста диалога и состояния сессии. Он управляет временем жизни сессий, кэшированием и очисткой. Основные стратегии: TTL (Time To Live) — автоматическое удаление после периода неактивности, LRU (Least Recently Used) — вытеснение старых сессий при переполнении кэша и GC (Garbage Collection) — фоновая сборка мусора для завершённых/истекших сессий. Дополнительно применяется locking для предотвращения race conditions при конкурентном доступе. Понимание этих механизмов критично для построения масштабируемых многопользовательских агентов.
1. Термин: Session Management (управление сессиями)
Session Management — это процесс поддержания контекста разговора с агентом между отдельными запросами пользователя. В отличие от stateless RAG, где каждый запрос обрабатывается изолированно, в RAG|agentic RAG агент должен помнить историю диалога, промежуточные действия и результаты выполнения инструментов. Harness определяет отдельный слой (слой 6 из 7, harness-one/session), который централизованно управляет:
- Созданием и удалением сессий (привязка к user_id или conversation_id)
- Хранением состояния (история сообщений, контекст, переменные)
- Временем жизни сессии (когда и как сессия завершается)
- Параллельным доступом (блокировки записи)
Для этого используются три ключевые стратегии: TTL, LRU и GC, а также механизм блокировок.
2. TTL (Time To Live) — время жизни по таймеру
TTL — самая простая стратегия: каждой сессии назначается максимальный срок хранения. Если сессия неактивна (нет запросов) в течение заданного интервала, она помечается как истекшая и удаляется.
- Как работает: при создании сессии ставится поле expires_at = now + TTL. При каждом запросе к сессии поле обновляется (если применяется скользящее окно) или остаётся фиксированным (абсолютное истечение).
- Типичные значения TTL: 5–30 минут для интерактивных чатов, до 24 часов для долгоживущих задач.
- Преимущества: простота реализации, предсказуемое поведение, низкие накладные расходы.
- Недостатки: даже редко используемая сессия может быть преждевременно удалена, если в последнюю минуту пользователь вернулся; жёсткое ограничение не учитывает загрузку памяти.
Пример конфигурации:
session:
strategy: TTL
ttl_seconds: 900 # 15 минут
sliding_window: true # продлевать при каждом взаимодействии
3. LRU (Least Recently Used) — вытеснение старых сессий
LRU применяется, когда ресурсы ограничены (например, количество одновременно хранимых сессий фиксировано). Сессия, к которой обращались реже всего (или давно), удаляется при попытке создать новую сессию сверх лимита.
- Как работает: поддерживается очередь порядка использования (или хэш-таблица с двусвязным списком). При каждом доступе сессия перемещается в конец (самые свежие). Когда достигается лимит, удаляется элемент из начала.
- Типичный лимит: настраивается под доступную память (например 10 000 сессий на ноду).
- Преимущества: автоматически адаптируется к паттернам пользователей — неиспользуемые сессии вытесняются, активные остаются.
- Недостатки: возможна ситуация, когда сессия с очень старым, но всё ещё нужным контекстом удаляется ради набора новых сессий (т.н. «вытеснение полезной сессии»).
Реализация в Python (упрощённая):
from collections import OrderedDict
class LRUSessionStore:
def __init__(self, capacity: int):
self.capacity = capacity
self.store = OrderedDict()
def get(self, session_id: str):
if session_id in self.store:
self.store.move_to_end(session_id)
return self.store[session_id]
return None
def put(self, session_id: str, session):
if session_id in self.store:
self.store.move_to_end(session_id)
elif len(self.store) >= self.capacity:
# удаляем самую старую (первую)
oldest = next(iter(self.store))
self.store.popitem(last=False)
self.store[session_id] = session
4. GC (Garbage Collection) — фоновая сборка мусора
GC — это фоновая процедура, которая периодически сканирует все сессии и удаляет те, которые уже не нужны. В Harness GC обычно комбинируется с TTL и LRU, но может работать и независимо.
Критерии для удаления:
-
Истёк TTL (сессия завершена по таймеру)
-
Сессия явно завершена пользователем (
EndSessionintent) -
Сессия в состоянии
failedилиaborted(агент не смог завершить работу) -
Сессия превысила максимальное количество шагов (лимит на число итераций)
-
Периодичность: каждые 60–300 секунд в зависимости от нагрузки.
-
Инструменты: фоновый поток или асинхронная задача (в Harness используется asyncio с await asyncio.sleep(interval)).
-
Преимущества: не блокирует основной поток ответов; гарантирует освобождение памяти даже при сбоях TTL/LRU (например, если TTL не продлился из-за ошибки).
-
Недостатки: задержка между фактическим истечением и удалением (окно для потенциальных проблем с памятью).
Псевдокод GC:
async def garbage_collector(store, interval=120):
while True:
await asyncio.sleep(interval)
now = time.time()
for sid in list(store.keys()):
session = store.get(sid, lock=False)
if session.is_expired(now) or session.is_finished():
store.delete(sid)
5. Locking — блокировки для предотвращения race conditions
В многопользовательской распределённой системе возможны ситуации, когда один и тот же запрос обрабатывается дважды (дублирование), или два запроса приходят одновременно к одной сессии. Locking (блокировка) гарантирует, что запись в сессию (обновление истории, состояния) происходит атомарно.
В Harness используется два уровня блокировок:
- Оптимистичная блокировка (optimistic locking): перед записью проверяется версия сессии (например,
updated_atилиversion_id). Если версия изменилась с момента чтения — операция повторяется. - Пессимистичная блокировка (pessimistic locking): на время записи захватывается распределённый lock (через Redis, ZooKeeper или базу данных). Используется для критических операций.
Типичная стратегия: пессимистичные блокировки на уровне процесса (in-process mutex) для одной рабочей ноды, оптимистичные для межнодовой консистентности.
Пример с asyncio:
import asyncio
class SessionStore:
def __init__(self):
self._locks = {} # session_id -> asyncio.Lock
async def get_or_create_lock(self, session_id):
if session_id not in self._locks:
self._locks[session_id] = asyncio.Lock()
return self._locks[session_id]
async def update_session(self, session_id, data):
lock = await self.get_or_create_lock(session_id)
async with lock:
session = await self.load(session_id)
session.update(data)
await self.save(session)
6. Сравнение стратегий
| Характеристика | TTL | LRU | GC |
|---|---|---|---|
| Основание удаления | Время с последнего доступа | Частота/давность использования | Явные признаки завершения |
| Управление памятью | Косвенное (если сессия не удалена, память занята) | Прямое (фиксированный лимит) | Фоновое (дополняет TTL/LRU) |
| Простота реализации | Очень низкая | Средняя | Средняя |
| Гарантия утилизации | Нет (может висеть мёртвая сессия до TTL) | Да (старые вытесняются) | Да (но с задержкой) |
| Применимость | Чат-боты с короткими диалогами | Высоконагруженные системы с ограниченной памятью | Комплексные сценарии с долгими сессиями |
В Harness по умолчанию используется комбинация: TTL (скользящее окно) + LRU (лимит на количество активных сессий) + GC (фоновая очистка каждые 5 минут). Параметры выставляются в конфигурации слоя session.
7. Архитектура Session Management в Harness
Слой session в Harness представлен модулем harness_one.session. Внутри:
- SessionStore — абстракция над хранилищем (in-memory dict, Redis, SQLite). По умолчанию in-memory для низкой задержки.
- SessionWorker — фоновый воркер, запускающий GC и проверяющий TTL.
- SessionMiddleware — ASGI/WSGI middleware, который для каждого входящего запроса извлекает session_id (из cookie, заголовка или тела), загружает сессию, передаёт в обработчик и сохраняет обновлённую.
Конфигурация указывается в YAML-файле агента:
layers:
- name: session
config:
store:
type: redis
url: redis://localhost:6379/0
ttl: 600
capacity: 10000
gc_interval: 120
locking: pessimistic
8. Практические соображения
- Выбор TTL: слишком короткий — пользователь теряет контекст; слишком длинный — растёт память. Рекомендуется ставить TTL = медианная продолжительность диалога * 2.
- LRU и долгие задачи: если агент выполняет задачу 30 минут, а лимит LRU настроен на 5 минут неактивности, сессия может быть вытеснена даже при наличии активного background-воркера. Решение — отмечать сессию как «занятую» (busy) и исключать из LRU.
- GC и консистентность: при распределённых хранилищах (Redis) GC должен быть идемпотентным и обрабатывать удаление с помощью скриптов Lua.
- Мониторинг: метрики — количество активных сессий, процент истекших по TTL, частота вытеснений LRU.
Пет-проект для закрепления
Задача: реализовать упрощённый Session Manager для агента чат-бота с поддержкой TTL+LRU+GC и блокировками.
Инструменты: Python (asyncio), Redis (опционально), тестовый фреймворк pytest.
Шаги:
- Создайте класс
Sessionс полями:id,messages,created_at,last_accessed,status(active/finished). - Реализуйте
SessionManagerс методамиget(sid),create(sid),delete(sid),get_or_create(sid). - В
get_or_createдобавьте LRU-очередь (коллекцияOrderedDict) с capacity. - Добавьте TTL: при каждом
getобновляйтеlast_accessed, и в фоновой задаче (asyncio.create_task) раз в 30 секунд проверяйте все сессии на истечение. - Реализуйте GC: принудительно удаляйте сессии со статусом
finished. - Добавьте
asyncio.Lockдля каждой сессии — оборачивайте запись в блокировку. - Напишите тесты: проверьте, что после TTL сессия удаляется, что LRU вытесняет старые, что одновременные запросы не приводят к race condition.
Ожидаемый результат: консольное приложение, эмулирующее 10 параллельных пользователей; метрики (сколько сессий создано, сколько удалено по TTL/LRU, максимальный размер кэша).
Связь с другими вопросами
| Вопрос | Тема |
|---|---|
| 745 | Что такое agentic RAG и чем отличается от обычного RAG |
| 746 | Компоненты архитектуры Harness |
| 747 | Слой Tool Execution |
| 748 | Слой Reasoning (планирование) |
| 750 | Слой Memory (краткосрочная и долгосрочная память) |
| 751 | Мониторинг и observability в Agentic RAG |
Навигация
- Предыдущий: 748
- Следующий: 750
- Индекс: 00. Индекс разборов