Entity Memory : Retenir les entites mentionnees
Guide complet pour implementer la memoire d'entites dans un systeme RAG : tracker les personnes, produits et concepts mentionnes dans la conversation.
Entity Memory : Retenir les entites mentionnees
L'Entity Memory extrait et stocke les entites cles mentionnees dans la conversation : personnes, produits, entreprises, lieux, dates, etc. Contrairement au Buffer ou Summary Memory qui gardent le texte brut, l'Entity Memory construit un "graphe de connaissances" dynamique enrichi au fil des echanges. Cela permet des references naturelles comme "il", "ce produit", "cette option".
Pourquoi l'Entity Memory ?
Le probleme des references aux entites
Dans une conversation naturelle, les utilisateurs font constamment reference a des entites mentionnees precedemment :
User: "Je cherche le Dell XPS 15"
AI: "Le Dell XPS 15 est un excellent laptop..."
User: "Quelle est sa garantie ?" <- "sa" = Dell XPS 15
AI: "La garantie du Dell XPS 15..."
User: "Et compare a celui de Lenovo ?" <- "celui" = quel produit Lenovo ?
AI: ???
Sans Entity Memory, le LLM doit deviner a quelle entite fait reference l'utilisateur.
L'architecture Entity Memory
┌─────────────────────────────────────────────────────────────┐
│ ENTITY MEMORY │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ ENTITY STORE │ │
│ ├────────────────────────────────────────────────────┤ │
│ │ │ │
│ │ "Dell XPS 15" (PRODUCT) │ │
│ │ - Type: Laptop │ │
│ │ - Prix: 1599 euros │ │
│ │ - Garantie: 2 ans │ │
│ │ - Mentions: 3 │ │
│ │ │ │
│ │ "Lenovo ThinkPad X1" (PRODUCT) │ │
│ │ - Type: Laptop │ │
│ │ - Prix: 1799 euros │ │
│ │ - Mentions: 1 │ │
│ │ │ │
│ └────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
Statistique cle : L'Entity Memory ameliore la pertinence des reponses de 40-60% pour les conversations avec des references frequentes aux memes objets.
Cas d'usage
| Domaine | Entites trackees | Exemple de reference |
|---|---|---|
| E-commerce | Produits, marques, categories | "Ce produit", "le meme en noir" |
| Support | Tickets, produits, problemes | "Mon compte", "cette erreur" |
| RH | Candidats, postes, entreprises | "Cette politique", "le poste" |
| Immobilier | Biens, quartiers, acheteurs | "Cet appartement", "le vendeur" |
Implementation de base
Structure des entites
DEVELOPERpythonfrom typing import Dict, List, Optional from dataclasses import dataclass, field from datetime import datetime import json @dataclass class Entity: """Represente une entite extraite""" name: str type: str # PERSON, PRODUCT, ORGANIZATION, etc. attributes: Dict = field(default_factory=dict) mentions: int = 1 first_seen: datetime = field(default_factory=datetime.now) last_seen: datetime = field(default_factory=datetime.now) def update(self, new_attributes: Dict) -> None: """Met a jour les attributs de l'entite""" self.attributes.update(new_attributes) self.mentions += 1 self.last_seen = datetime.now() def to_dict(self) -> Dict: """Convertit en dictionnaire""" return { "name": self.name, "type": self.type, "attributes": self.attributes, "mentions": self.mentions }
Entity Memory complete
DEVELOPERpythonclass EntityMemory: """ Memoire conversationnelle basee sur les entites """ def __init__(self, llm, entity_types: List[str] = None): self.llm = llm self.entity_types = entity_types or [ "PERSON", "PRODUCT", "ORGANIZATION", "LOCATION", "DATE", "MONEY", "CONCEPT" ] self.entities: Dict[str, Entity] = {} self.recent_messages: List[Dict] = [] def add_message(self, role: str, content: str) -> None: """Ajoute un message et extrait les entites""" self.recent_messages.append({ "role": role, "content": content, "timestamp": datetime.now().isoformat() }) # Extraire les entites du message entities = self._extract_entities(content) self._update_entity_store(entities) def _extract_entities(self, text: str) -> List[Dict]: """Extrait les entites d'un texte avec le LLM""" prompt = f"""Extrais les entites nommees de ce texte. Types d'entites a detecter: {', '.join(self.entity_types)} Texte: "{text}" Retourne un JSON array: [{{"name": "nom", "type": "TYPE", "attributes": {{}}}}, ...] Si aucune entite, retourne: [] JSON:""" response = self.llm.invoke(prompt) try: return json.loads(response) except json.JSONDecodeError: return [] def _update_entity_store(self, entities: List[Dict]) -> None: """Met a jour le store d'entites""" for entity_data in entities: name = entity_data.get("name", "").lower() if not name: continue if name in self.entities: self.entities[name].update(entity_data.get("attributes", {})) else: self.entities[name] = Entity( name=entity_data.get("name"), type=entity_data.get("type", "UNKNOWN"), attributes=entity_data.get("attributes", {}) ) def get_recent_entities(self, limit: int = 5) -> List[Entity]: """Recupere les entites les plus recemment mentionnees""" sorted_entities = sorted( self.entities.values(), key=lambda e: e.last_seen, reverse=True ) return sorted_entities[:limit] def resolve_reference(self, reference: str) -> Optional[str]: """Resout une reference comme 'il', 'ce produit'""" if not self.entities: return None entities_json = json.dumps([e.to_dict() for e in self.get_recent_entities(5)]) prompt = f"""Reference a resoudre: "{reference}" Entites connues: {entities_json} Quelle entite correspond a "{reference}"? Reponds uniquement avec le nom, ou "NONE" si aucune correspondance.""" result = self.llm.invoke(prompt).strip() return result if result != "NONE" else None def get_context(self) -> str: """Genere le contexte a injecter dans le prompt""" if not self.entities: return "" recent = self.get_recent_entities(5) entity_lines = [] for e in recent: attrs = ", ".join([f"{k}: {v}" for k, v in e.attributes.items()]) line = f"- {e.name} ({e.type})" if attrs: line += f": {attrs}" entity_lines.append(line) return "Entites en contexte:\n" + "\n".join(entity_lines) def clear(self) -> None: """Reinitialise la memoire""" self.entities = {} self.recent_messages = []
Integration avec RAG
DEVELOPERpythonclass RAGWithEntityMemory: """Pipeline RAG avec Entity Memory""" def __init__(self, vector_store, llm): self.vector_store = vector_store self.llm = llm self.memory = EntityMemory(llm) def query(self, user_message: str) -> str: """Execute une requete RAG avec contexte d'entites""" # 1. Resoudre les references resolved_message = self._resolve_references(user_message) # 2. Extraire les entites self.memory.add_message("user", resolved_message) # 3. Recuperer les documents docs = self.vector_store.similarity_search(resolved_message, k=3) doc_context = "\n\n".join([d.page_content for d in docs]) # 4. Construire le prompt entity_context = self.memory.get_context() prompt = f"""{entity_context} Documents pertinents: {doc_context} Question: {user_message} Reponds en tenant compte des entites mentionnees.""" # 5. Generer la reponse response = self.llm.invoke(prompt) self.memory.add_message("assistant", response) return response def _resolve_references(self, text: str) -> str: """Resout les references pronominales""" references = ["il", "elle", "ce produit", "cela", "celui-ci"] for ref in references: if ref in text.lower(): resolved = self.memory.resolve_reference(ref) if resolved: text = text.replace(ref, resolved, 1) return text
Techniques avancees
Extraction avec relations
DEVELOPERpythonclass RelationalEntityMemory(EntityMemory): """Entity Memory avec relations entre entites""" def __init__(self, llm, **kwargs): super().__init__(llm, **kwargs) self.relations: List[Dict] = [] def _extract_entities(self, text: str) -> List[Dict]: """Extrait entites ET relations""" prompt = f"""Analyse ce texte et extrait: 1. Les entites nommees 2. Les relations entre entites Types de relations: COMPARES_TO, PREFERS, OWNS, INTERESTED_IN Texte: "{text}" JSON: {{ "entities": [{{"name": "...", "type": "...", "attributes": {{}}}}], "relations": [{{"source": "e1", "relation": "TYPE", "target": "e2"}}] }}""" response = self.llm.invoke(prompt) try: data = json.loads(response) for rel in data.get("relations", []): self.relations.append(rel) return data.get("entities", []) except json.JSONDecodeError: return [] def get_related_entities(self, entity_name: str) -> List[Dict]: """Trouve les entites liees""" related = [] name_lower = entity_name.lower() for rel in self.relations: if rel["source"].lower() == name_lower: related.append({"entity": rel["target"], "relation": rel["relation"]}) elif rel["target"].lower() == name_lower: related.append({"entity": rel["source"], "relation": rel["relation"]}) return related
Persistence multi-session
DEVELOPERpythonfrom pathlib import Path class PersistentEntityMemory(EntityMemory): """Entity Memory avec sauvegarde persistante""" def __init__(self, llm, user_id: str, storage_dir: str = "./entity_store"): super().__init__(llm) self.user_id = user_id self.storage_path = Path(storage_dir) / f"{user_id}.json" self._load() def _load(self) -> None: """Charge les entites depuis le fichier""" if self.storage_path.exists(): with open(self.storage_path, "r") as f: data = json.load(f) for name, entity_data in data.get("entities", {}).items(): self.entities[name] = Entity( name=entity_data["name"], type=entity_data["type"], attributes=entity_data.get("attributes", {}), mentions=entity_data.get("mentions", 1) ) def save(self) -> None: """Sauvegarde les entites""" self.storage_path.parent.mkdir(exist_ok=True) data = { "user_id": self.user_id, "entities": {name: e.to_dict() for name, e in self.entities.items()} } with open(self.storage_path, "w") as f: json.dump(data, f, indent=2) def add_message(self, role: str, content: str) -> None: super().add_message(role, content) self.save()
Implementation avec LangChain
DEVELOPERpythonfrom langchain.memory import ConversationEntityMemory from langchain.chains import ConversationChain from langchain_openai import ChatOpenAI llm = ChatOpenAI(model="gpt-4", temperature=0.7) # Entity Memory native LangChain memory = ConversationEntityMemory(llm=llm, return_messages=True) conversation = ConversationChain(llm=llm, memory=memory, verbose=True) # Utilisation response = conversation.predict(input="Je cherche un Dell XPS 15") response = conversation.predict(input="Quel est son prix ?") # Inspecter les entites print(memory.entity_store)
Bonnes pratiques
1. Types d'entites par domaine
DEVELOPERpython# E-commerce ECOMMERCE_TYPES = ["PRODUCT", "BRAND", "CATEGORY", "PRICE", "FEATURE"] # Support SUPPORT_TYPES = ["PRODUCT", "ISSUE", "SOLUTION", "TICKET_ID", "PERSON"] # RH HR_TYPES = ["PERSON", "POSITION", "COMPANY", "SKILL", "EDUCATION"]
2. Expiration des entites
DEVELOPERpythonfrom datetime import timedelta class ExpiringEntityMemory(EntityMemory): def __init__(self, llm, ttl_minutes: int = 30, **kwargs): super().__init__(llm, **kwargs) self.ttl = timedelta(minutes=ttl_minutes) def _cleanup_expired(self) -> None: now = datetime.now() expired = [name for name, e in self.entities.items() if now - e.last_seen > self.ttl] for name in expired: del self.entities[name] def get_context(self) -> str: self._cleanup_expired() return super().get_context()
3. Priorisation intelligente
DEVELOPERpythondef get_prioritized_entities(self, query: str, limit: int = 5) -> List[Entity]: """Retourne les entites les plus pertinentes pour une requete""" scored = [] for entity in self.entities.values(): score = 0 recency = (datetime.now() - entity.last_seen).total_seconds() score += max(0, 100 - recency / 60) score += entity.mentions * 10 if entity.name.lower() in query.lower(): score += 50 scored.append((entity, score)) scored.sort(key=lambda x: x[1], reverse=True) return [e for e, _ in scored[:limit]]
Quand utiliser Entity Memory ?
Cas ideaux
- E-commerce : Suivi des produits compares
- Support technique : Suivi du probleme et solutions
- CRM/Ventes : Suivi des contacts et opportunites
- Conseil : Retenir preferences et contraintes
Quand eviter
| Situation | Alternative |
|---|---|
| Conversations generiques | Buffer Memory |
| Latence critique | Buffer simple |
| Conversations tres courtes | Buffer Memory |
Guides connexes
- Buffer Memory - Pour les conversations simples
- Summary Memory - Pour les conversations longues
- RAG Conversationnel - Vue d'ensemble
FAQ
Entity Memory avec Ailog
Avec Ailog, beneficiez d'une gestion d'entites native :
- Extraction automatique des entites avec types configurables
- Resolution de coreferences ("il", "celui-ci", etc.)
- Persistence multi-session des entites utilisateur
- Graphe de relations entre entites
- Analytics sur les entites les plus mentionnees
Testez Ailog gratuitement et deployez un chatbot qui retient les entites importantes.
Tags
Articles connexes
RAG Conversationnel : Memoire et contexte multi-sessions
Implementez un RAG avec memoire conversationnelle : gestion du contexte, historique multi-sessions et personnalisation des reponses.
Buffer Memory : Historique de conversation simple
Guide complet pour implementer la memoire tampon dans un systeme RAG conversationnel : garder le contexte des derniers echanges pour des reponses coherentes.
Agents RAG : Orchestrer des systemes multi-agents
Architecturez des systemes RAG multi-agents : orchestration, specialisation, collaboration et gestion des echecs pour des assistants complexes.