<НА ГЛАВНУЮ

Создание персистентного персонализированного агентного ИИ с распадом памяти и самооценкой

'Пошаговый гайд о том, как персистентная память, распад и простое извлечение превращают чат-бота в персонализированного агента; включает полный 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

Switch to English