Разбор трёх типов памяти: Working (контекстное окно LLM), Short-term (сессия агента), Long-term (векторная БД с ChromaDB). Практическая архитектура.
# Архитектура памяти AI-агента — три уровня # Уровень 1: Working Memory — контекстное окно LLM (то, что модель "видит" прямо сейчас) # Уровень 2: Short-term Memory — сессия агента (история сообщений в Redis/Postgres) # Уровень 3: Long-term Memory — векторная БД (все прошлые взаимодействия, факты, предпочтения) class MemoryArchitecture: """Трёхуровневая архитектура памяти агента.""" def __init__(self): self.working_memory = [] # список сообщений в контекстном окне self.short_term = [] # история текущей сессии self.long_term = None # ChromaDB коллекция
pip install tiktoken import tiktoken encoder = tiktoken.encoding_for_model("gpt-4") # Функция безопасной обрезки контекста def trim_context(messages, max_tokens=8000, reserve=2000): """Обрезает историю сообщений, сохраняя system prompt и последние N сообщений.""" total = 0 trimmed = [] system_msg = None for msg in messages: if msg["role"] == "system": system_msg = msg continue available = max_tokens - reserve if system_msg: available -= len(encoder.encode(system_msg["content"])) trimmed.append(system_msg) for msg in reversed(messages): if msg["role"] == "system": continue tokens = len(encoder.encode(msg["content"])) if total + tokens > available: break total += tokens trimmed.insert(1, msg) return trimmed
pip install redis import redis import json from datetime import datetime r = redis.Redis(host='localhost', port=6379, decode_responses=True) class SessionMemory: """Краткосрочная память: Redis хранит всю историю сессии.""" def __init__(self, session_id, ttl=3600): self.key = f"session:{session_id}" self.ttl = ttl def add_message(self, role, content): msg = {"role": role, "content": content, "ts": datetime.now().isoformat()} r.rpush(self.key, json.dumps(msg)) r.expire(self.key, self.ttl) def get_history(self, limit=50): raw = r.lrange(self.key, -limit, -1) return [json.loads(m) for m in raw]
pip install chromadb openai import chromadb from openai import OpenAI client = OpenAI() chroma = chromadb.PersistentClient(path="./agent_memory") collection = chroma.get_or_create_collection("memories") def embed(text): """Получить эмбеддинг через OpenAI ADA-002.""" resp = client.embeddings.create(model="text-embedding-ada-002", input=text) return resp.data[0].embedding def remember(fact, metadata=None): """Сохранить факт в долгосрочную память.""" collection.add(documents=[fact], embeddings=[embed(fact)], ids=[f"mem_{hash(fact)}"], metadatas=[metadata or {}]) def recall(query, n=5): """Найти релевантные воспоминания.""" results = collection.query(query_embeddings=[embed(query)], n_results=n) return results['documents'][0]
def memory_pipeline(user_input, session_id): """Полный пайплайн: user msg → embed → search → inject → respond.""" # 1. Получаем историю сессии (short-term) session = SessionMemory(session_id) history = session.get_history() # 2. Ищем релевантные воспоминания (long-term) relevant = recall(user_input, n=3) # 3. Инжектим в system prompt memory_context = "\n".join(f"- {m}" for m in relevant) system_prompt = f"""Ты AI-агент с памятью. Релевантные воспоминания: {memory_context} История диалога ниже. Отвечай полезно, используя контекст.""" # 4. Формируем messages для LLM (working memory) messages = [{"role": "system", "content": system_prompt}] + history[-20:] messages.append({"role": "user", "content": user_input}) # 5. LLM ответ и сохранение resp = client.chat.completions.create(model="gpt-4", messages=messages) answer = resp.choices[0].message.content session.add_message("user", user_input) session.add_message("assistant", answer) return answer
pip install chromadb openai redis tiktoken import os, json, hashlib from datetime import datetime import chromadb, redis, tiktoken from openai import OpenAI class MemoryAgent: """Полноценный AI-агент с трёхуровневой памятью.""" def __init__(self, session_id, model="gpt-4"): self.client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) self.redis = redis.Redis(decode_responses=True) self.chroma = chromadb.PersistentClient(path="./agent_db") self.coll = self.chroma.get_or_create_collection("ltm") self.session = session_id self.model = model self.enc = tiktoken.encoding_for_model(model) def chat(self, msg): # Long-term recall emb = self._embed(msg) memories = self.coll.query(query_embeddings=[emb], n_results=3)['documents'][0] ctx = "\n".join(f"• {m}" for m in memories) # Short-term history raw = self.redis.lrange(f"sess:{self.session}", -30, -1) hist = [json.loads(m) for m in raw] # Build messages + trim msgs = [{"role":"system","content":f"Память:\n{ctx}\n\nБудь полезным."}] + hist + [{"role":"user","content":msg}] msgs = self._trim(msgs, 8000) # LLM call resp = self.client.chat.completions.create(model=self.model, messages=msgs) ans = resp.choices[0].message.content # Save to both memories for role, text in [("user",msg),("assistant",ans)]: self.redis.rpush(f"sess:{self.session}", json.dumps({"role":role,"content":text,"ts":datetime.now().isoformat()})) self.coll.add(documents=[text], embeddings=[self._embed(text)], ids=[f"{self.session}_{hashlib.md5(text.encode()).hexdigest()[:8]}"]) return ans def _embed(self, text): return self.client.embeddings.create(model="text-embedding-ada-002", input=text).data[0].embedding def _trim(self, msgs, limit): total = 0; out = [] for m in reversed(msgs): t = len(self.enc.encode(m["content"])) if total + t > limit: break total += t; out.append(m) return list(reversed(out))