Изграждане на дългосрочна памет за ИИ агенти с Elasticsearch
В съвременните системи с изкуствен интелект поддържането на контекст в рамките на един разговор обикновено се решава чрез „напомпване“ на контекстния прозорец с историята на сесията. Този подход обаче бързо се сблъсква с ограничението на разходите, латентността и т. нар. ефект на „изгубване в средата“ (lost in the middle), при който моделите игнорират факти, намиращи се далеч от краищата на промпта. В инженерна публикация на Elasticsearch Labs [1], авторът Ноам Шварц описва как голям контекстен прозорец от 1 милион токена може да служи като работна чернова за единична извеждаща стъпка (short-term memory), но не представлява истинска дългосрочна памет.
Решението е изграждането на слой за дългосрочна памет (long-term memory), базиран на Elasticsearch, който оцелява след края на сесиите, мащабира се с години взаимодействие и позволява извличане на факти по съдържание, време и потребител. Разработената архитектура постига средна пълнота на извличането (Recall@10) от 0.89 при нулева опасност от изтичане на данни между отделните потребители (zero tenant leaks) [1].
Трикомпонентна архитектура на паметта
Дизайнът на паметта следва разделението в когнитивната психология между епизодична, семантична и процедурна памет, формулирано за ИИ агенти чрез теоретичната рамка COALA. Всеки тип памет има различен жизнен цикъл и се записва в отделен индекс в Elasticsearch:
- Епизодична памет (episodic memory): Времево маркиран архив на суровите потребителски реплики. Тя се записва постоянно при всяко съобщение и подлежи на бързо времево затихване.
- Семантична памет (semantic memory): Дестилирани, стабилни твърдения за потребителя (например негови предпочитания или факти за локацията му). Тя се курира, изчиства се от дубликати и се обновява чрез процеси на заместване.
- Процедурна памет (procedural memory): Стъпка по стъпка ръководства (playbooks) за решаване на проблеми. Този индекс съхранява полета за брой успехи (
success_count) и провали (failure_count), които се увеличават при потвърждение от потребителя дали дадено действие е помогнало.
Паралелно с тези три индекса съществува и четвърта повърхност за извличане — споделен продуктов каталог или база данни с познания (world data), която не е памет в когнитивния смисъл, но се чете през същия конвейер за търсене [1].
Изображение: Svetni.me / Авторско изображение
Двустепенно хибридно извличане
За осигуряване на максимална точност се прилага двустепенен конвейер за извличане, комбиниращ лексикални и семантични сигнали:
- Първи етап (Over-fetch): Използва се хибридно търсене над традиционния инвертиран индекс BM25 (за точни съвпадения на идентификатори, версии и кодове като „Lumio Hub v2“) и плътни вектори Jina v5 (за улавяне на семантичния смисъл на заявката). Двата крака на търсенето извличат по 80 кандидати, които се обединяват чрез алгоритъма Reciprocal Rank Fusion (RRF) с константа на ранга
rank_constant=30. Стойността е по-ниска от стандартната за Elasticsearch (60), което дава по-голямо тегло на най-високо оценените елементи. - Втори етап (Rerank): Всички кандидати преминават през крос-енкодер модел Jina v2 за преранжиране. Тъй като крос-енкодерите оценяват заявката и документа съвместно (с пълно внимание върху двойката), те предоставят много по-силен сигнал за релевантност на цената на по-висока изчислителна мощност [1].
За избягване на проблемите с перифразирането от страна на агента, при което се губят важни технически термини, системата извършва автоматично предварително извличане (pre-recall) върху оригиналното потребителско съобщение, преди моделът да е модифицирал текста.
Консолидация и заместване на факти
Тъй като епизодичните логове се трупат бързо, системата изпълнява процес на консолидация, който превръща суровите реплики в дълготрайни семантични факти и процедурни инструкции. В производствена среда това се извършва като фонова задача (например на всеки 24 часа), докато в демонстрационната имплементация работи на всяка стъпка за улеснение на инспекцията.
Едно от най-важните изисквания към паметта е възможността за обработка на противоречия (заместване). Ако потребителят каже „Преместих се да живея от Бристол в Единбург“, системата не трябва просто да запише нов факт, оставяйки стария активен. Агентът автоматично открива противоречието и извиква операция по запис, която маркира стария факт като заместен чрез попълване на полетата superseded_by и superseded_at. Стандартните заявки за извличане прилагат филтър must_not exists: superseded_by, скривайки старата информация от текущия контекст, но запазвайки историческата одитна пътека в индекса за специални исторически справки [1].
За да се избегне забавянето при бързи последващи заявки в рамките на един и същ ход на разговора (propagation gap), всеки запис се извършва с параметър refresh=True. Това принуждава Elasticsearch веднага да опресни индекса и Jina v5 ембедингите да станат налични за търсене под 100 милисекунди.
Времево затихване и честотно стимулиране
Всички извлечени документи се модифицират с формула за затихване на релевантността (decay), изчислена директно в Painless скрипт в Elasticsearch:
- Recency (Времево затихване): Прилага се функция на Гаус. Епизодичният индекс използва оригиналното времево клеймо на събитието, докато семантичният индекс използва полето
last_used_at, което се обновява (bump) при всяко успешно извличане. Това гарантира, че стар, но често използван факт, остава с висок приоритет. - Frequency (Честотен бонус): Резултатите в семантичния индекс се стимулират допълнително по формулата
1 + log10(1 + use_count) * weight, за да се даде предимство на факти, които агентът е ползвал многократно в миналото.
Процедурният индекс е изключен от времевото затихване, за да не се възнаграждават просто наскоро опитвани действия, а да се разчита на реалната статистика за успеваемост [1].
Сигурност на ниво документ (DLS) и MCP интеграция
Защитата на данните в многопотребителски среди се налага директно на ниво клъстер чрез Document-Level Security (DLS). На всеки потребител се издава уникален API ключ, чиято роля съдържа DLS филтър с логика: user_id == потребител OR must_not exists: user_id. По този начин потребителят вижда единствено собствените си записи и споделения каталог, без риск от изтичане на данни към други наематели.
Слоят за памет е достъпен през стандартен Model Context Protocol (MCP) на адрес /api/atlas/mcp/{user_id}, позволявайки лесно интегриране с всякакви MCP клиенти като Claude Desktop или Cursor, без да се налага пренаписване на логиката на агента [1].
Източници:
[1]: How we built a persistent agent memory layer on Elasticsearch with 0.89 recall and zero tenant leaks - Elasticsearch Labs