Mémoire Sémantique "Human-in-the-loop"
La Mémoire Sémantique de Synapse Core permet à l'IA de retenir des informations importantes sur l'utilisateur au fil des conversations, avec son consentement explicite.
[!IMPORTANT] Contrairement à un RAG classique (indexation de documents), la mémoire sémantique est active : c'est le LLM qui propose de mémoriser un fait, et l'utilisateur valide.
Prérequis
- Un embedding provider configuré (voir l’interface d’administration, section Synapse Admin)
- Un Vector Store actif (voir RAG & Mémoire Vectorielle)
- L'utilisateur doit implémenter
ConversationOwnerInterface(pour l'isolation des données)
Flux de fonctionnement
1. Utilisateur dit "Je suis allergique aux arachides"
2. LLM détecte l'information importante
3. LLM appelle l'outil propose_to_remember
4. 🧠 Toast discret dans le chat : "Retenir : X [✓] [✕]"
5. Utilisateur clique ✓
6. POST /synapse/api/memory/confirm → MemoryManager::remember()
7. Prochaine conversation → souvenir injecté automatiquement dans le contexte
Activation
L'outil propose_to_remember est automatiquement disponible dans le ToolRegistry dès l'installation du bundle, sans configuration supplémentaire.
Si vous souhaitez le désactiver (opt-out), vous pouvez le faire en overridant les tools_override dans vos options de chat :
$chatService->ask($message, [
'tools_override' => [], // Aucun outil
]);
Le Service MemoryManager
Utilisez le MemoryManager pour manipuler la mémoire programmatiquement :
use ArnaudMoncondhuy\SynapseCore\Core\Memory\MemoryManager;
use ArnaudMoncondhuy\SynapseCore\Shared\Enum\MemoryScope;
class MyService
{
public function __construct(private MemoryManager $memoryManager) {}
public function storeManually(string $userId): void
{
// Mémoriser un fait manuellement (portée utilisateur)
$this->memoryManager->remember(
text: "L'utilisateur parle couramment le japonais.",
scope: MemoryScope::USER,
userId: $userId,
sourceType: 'manual'
);
}
public function searchMemory(string $userId): void
{
// Recherche sémantique dans la mémoire de l'utilisateur
$memories = $this->memoryManager->recall(
query: "langues parlées",
userId: $userId,
limit: 3
);
foreach ($memories as $m) {
echo $m['content'] . ' (score: ' . $m['score'] . ')';
}
}
public function listAndDelete(string $userId): void
{
// Lister tous les souvenirs
$memories = $this->memoryManager->listForUser($userId);
// Supprimer un souvenir par ID
$this->memoryManager->forget($memories[0]->getId(), $userId);
}
}
Portées (MemoryScope)
| Valeur | Description |
|---|---|
MemoryScope::USER |
Souvenir permanent, disponible dans toutes les conversations de l'utilisateur |
MemoryScope::CONVERSATION |
Souvenir éphémère, lié à une conversation spécifique |
API REST pour le Frontend
Le bundle expose des endpoints REST pour gérer les souvenirs depuis votre interface :
| Route | Méthode | Description |
|---|---|---|
/synapse/api/memory/confirm |
POST |
Valider une proposition de mémorisation |
/synapse/api/memory/reject |
POST |
Refuser une proposition |
/synapse/api/memory |
GET |
Lister les souvenirs de l'utilisateur connecté |
/synapse/api/memory/{id} |
DELETE |
Supprimer un souvenir (RGPD) |
Exemple : confirmer un souvenir
await fetch('/synapse/api/memory/confirm', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
fact: "L'utilisateur est allergique aux arachides",
category: 'constraint',
scope: 'user',
conversation_id: '...' // optionnel
})
});
Types de sources (source_type)
| Valeur | Description |
|---|---|
fact |
Extrait d'une conversation (via ProposeMemoryTool) |
manual |
Saisi programmatiquement par l'application |
document |
(Prévu) Issu d'un fichier uploadé (PDF, etc.) |
Sécurité & Isolation des données
[!WARNING] Le filtrage par
user_idest imposé au niveau SQL dans leDoctrineVectorStore, pas en post-traitement PHP. Cela garantit qu'une faille applicative ne peut pas exposer les souvenirs d'un utilisateur à un autre.
L'injection des souvenirs dans le contexte du LLM est gérée par le MemoryContextSubscriber (priorité 50), qui s'exécute après la construction du prompt principal. Seuls les souvenirs avec un score de similarité ≥ 0.7 sont injectés.
Injection automatique dans le contexte
Le MemoryContextSubscriber s'active automatiquement si :
1. Un utilisateur est connecté et implémente ConversationOwnerInterface.
2. Un embedding provider est configuré.
Les souvenirs pertinents sont injectés sous forme de bloc system discret, invisible dans l'historique de la conversation mais présent dans le prompt envoyé au LLM :
Informations connues sur l'utilisateur (mémorisées lors des conversations précédentes) :
- L'utilisateur est allergique aux arachides
- L'utilisateur préfère les réponses concises
Limitations actuelles
- Pas de gestion des contradictions : si l'utilisateur contredit un ancien souvenir, les deux coexistent. Une future version ajoutera
propose_to_updateetpropose_to_forget. - L'upload de documents (PDF, etc.) n'est pas encore disponible via l'UI.