Создание персистентного персонализированного агентного ИИ с распадом памяти и самооценкой
'Пошаговый гайд о том, как персистентная память, распад и простое извлечение превращают чат-бота в персонализированного агента; включает полный Python-демо и оценку эффекта.'
Зачем нужна персистентная память
Обычный чат-бот отвечает на запросы, но помощник, который запоминает предпочтения, темы и проекты, становится значительно полезнее. Персистентная память позволяет агенту вспоминать контекст, подстраивать тон и рекомендации и учиться со временем. В этом руководстве показана компактная, основанная на простых правилах реализация, демонстрирующая ключевые идеи: хранение воспоминаний, распад для забывания устаревших элементов, извлечение релевантного контекста и измерение эффекта персонализации.
Модель памяти и распад
Основные структуры данных — MemoryItem и MemoryStore. MemoryItem хранит тип, содержимое, базовый скор и метку времени. MemoryStore держит элементы и применяет экспоненциальную функцию распада, чтобы воспоминания со временем ослабевали.
import math, time, random
from typing import List
class MemoryItem:
def __init__(self, kind:str, content:str, score:float=1.0):
self.kind = kind
self.content = content
self.score = score
self.t = time.time()
class MemoryStore:
def __init__(self, decay_half_life=1800):
self.items: List[MemoryItem] = []
self.decay_half_life = decay_half_life
def _decay_factor(self, item:MemoryItem):
dt = time.time() - item.t
return 0.5 ** (dt / self.decay_half_life)Это закладывает основу для долгосрочной памяти: элементы с метками времени и экспоненциальной функцией распада, уменьшающей силу воспоминания со временем.
Добавление, поиск и очистка воспоминаний
MemoryStore нуждается в методах для добавления записей, поиска наиболее релевантных элементов по запросу и удаления слабых воспоминаний. В демо используется простая функция схожести (число общих токенов) в сочетании со скором, модифицированным распадом.
def add(self, kind:str, content:str, score:float=1.0):
self.items.append(MemoryItem(kind, content, score))
def search(self, query:str, topk=3):
scored = []
for it in self.items:
decay = self._decay_factor(it)
sim = len(set(query.lower().split()) & set(it.content.lower().split()))
final = (it.score * decay) + sim
scored.append((final, it))
scored.sort(key=lambda x: x[0], reverse=True)
return [it for _, it in scored[:topk] if _ > 0]
def cleanup(self, min_score=0.1):
new = []
for it in self.items:
if it.score * self._decay_factor(it) > min_score:
new.append(it)
self.items = newТакая схема поддерживает дешёвый поиск и автоматически удаляет слабые или старые воспоминания, предотвращая перегрузку контекста.
Дизайн агента и имитация LLM
Класс Agent оборачивает MemoryStore и предоставляет методы perceive и act. Perceive извлекает предпочтения, темы и проекты из ввода пользователя и сохраняет их. Act извлекает контекст для запроса, передаёт его в простую имитацию LLM, затем логгирует диалог и запускает очистку.
class Agent:
def __init__(self, memory:MemoryStore, name="PersonalAgent"):
self.memory = memory
self.name = name
def _llm_sim(self, prompt:str, context:List[str]):
base = "OK. "
if any("prefers short" in c for c in context):
base = ""
reply = base + f"I considered {len(context)} past notes. "
if "summarize" in prompt.lower():
return reply + "Summary: " + " | ".join(context[:2])
if "recommend" in prompt.lower():
if any("cybersecurity" in c for c in context):
return reply + "Recommended: write more cybersecurity articles."
if any("rag" in c for c in context):
return reply + "Recommended: build an agentic RAG demo next."
return reply + "Recommended: continue with your last topic."
return reply + "Here's my response to: " + prompt
def perceive(self, user_input:str):
ui = user_input.lower()
if "i like" in ui or "i prefer" in ui:
self.memory.add("preference", user_input, 1.5)
if "topic:" in ui:
self.memory.add("topic", user_input, 1.2)
if "project" in ui:
self.memory.add("project", user_input, 1.0)
def act(self, user_input:str):
mems = self.memory.search(user_input, topk=4)
ctx = [m.content for m in mems]
answer = self._llm_sim(user_input, ctx)
self.memory.add("dialog", f"user said: {user_input}", 0.6)
self.memory.cleanup()
return answer, ctxИмитация LLM адаптирует ответы исходя из обнаруженных предпочтений (например, убирает ведущую фразу 'OK.' при желании коротких ответов) и даёт рекомендации на основе извлечённой памяти.
Измерение эффекта персонализации
Для количественной оценки памяти демон сравнивает агента с заполненной памятью и агента с чистым состоянием (cold start). Разница в длине ответа используется как простая метрика персонализации.
def evaluate_personalisation(agent:Agent):
agent.memory.add("preference", "User likes cybersecurity articles", 1.6)
q = "Recommend what to write next"
ans_personal, _ = agent.act(q)
empty_mem = MemoryStore()
cold_agent = Agent(empty_mem)
ans_cold, _ = cold_agent.act(q)
gain = len(ans_personal) - len(ans_cold)
return ans_personal, ans_cold, gainЗапуск демо и наблюдения
Финальный скрипт инициализирует MemoryStore с коротким временем полураспада для демонстрации, обучает агента нескольким предпочтениям и темам, затем задаёт вопрос и выводит снимок памяти. Вы увидите, как агент даёт разные рекомендации с памятью и без неё.
mem = MemoryStore(decay_half_life=60)
agent = Agent(mem)
print("=== Demo: teaching the agent about yourself ===")
inputs = [
"I prefer short answers.",
"I like writing about RAG and agentic AI.",
"Topic: cybersecurity, phishing, APTs.",
"My current project is to build an agentic RAG Q&A system."
]
for inp in inputs:
agent.perceive(inp)
print("\n=== Now ask the agent something ===")
user_q = "Recommend what to write next in my blog"
ans, ctx = agent.act(user_q)
print("USER:", user_q)
print("AGENT:", ans)
print("USED MEMORY:", ctx)
print("\n=== Evaluate personalisation benefit ===")
p, c, g = evaluate_personalisation(agent)
print("With memory :", p)
print("Cold start :", c)
print("Personalisation gain (chars):", g)
print("\n=== Current memory snapshot ===")
for it in agent.memory.items:
print(f"- {it.kind} | {it.content[:60]}... | score~{round(it.score,2)}")Практические выводы
- Даже простая память с распадом даёт ощутимую персонализацию: хранение предпочтений и тем помогает выдавать более релевантные рекомендации.
- Механизм распада предотвращает бесконтрольный рост памяти и требует повторного усиления важных записей.
- Небольшая петля оценки (с персонализацией против холодного старта) позволяет количественно оценивать выгоду и настраивать, какие типы воспоминаний важны.
Эту систему легко расширить: заменить token-based схожесть на поиск по эмбеддингам, сохранять MemoryStore на диск или усиливать память при принятии рекомендации. Полный демо-скрипт показывает, как персистентная память превращает статический скрипт в обучающегося, контекстно-осведомлённого компаньона.
Switch Language
Read this article in English