Mémoire d'agent IA avec Convex : Architecture complète
Mémoire d'agent IA avec Convex est l'une des innovations les plus significatives dans le paysage de l'intelligence artificielle. Mémoire d'agent IA avec Convex se distingue par sa capacité à transformer les workflows de développement. Dans ce guide complet, nous explorons comment Mémoire d'agent IA avec Convex fonctionne en pratique et comment l'implémenter efficacement.
Mais quand Timmy a pris plus de responsabilités — écrire des articles de blog, gérer les réseaux sociaux, automatiser des tâches navigateur, exécuter des cron jobs — nous avons atteint les limites de la mémoire par fichiers. La recherche était linéaire, sans classement par pertinence. Les anciens souvenirs ne s'estompaient jamais, les fichiers devenaient gonflés de décisions obsolètes. Il n'y avait aucun moyen de faire remonter automatiquement du contexte connexe ou de distinguer une décision architecturale critique d'une observation anodine.
Nous voulions quelque chose de plus cognitif. Pas juste du stockage — un système qui modélise le fonctionnement réel de la mémoire.
Voici l'histoire de la construction de Cortex, un système de mémoire cognitive pour agents IA, propulsé par Convex.
Cours accéléré de 60 secondes sur la mémoire cognitive
Avant de plonger dans l'architecture, parlons du fonctionnement réel de la mémoire humaine. Les sciences cognitives ont identifié plusieurs systèmes mnésiques distincts :
🧠 Mémoire sensorielle — Votre tampon à court terme. Comme se souvenir de ce que quelqu'un a dit il y a 30 secondes. Volume élevé, très temporaire. La plupart s'évapore en minutes.
📔 Mémoire épisodique — Votre journal. Des événements spécifiques liés à des moments et des lieux : « Mardi, nous avons décidé d'utiliser Typefully pour les réseaux sociaux. » Autobiographique, contextuelle, horodatée.
📚 Mémoire sémantique — Votre base de connaissances. Des faits que vous savez simplement : « Convex supporte la recherche vectorielle » ou « la commande de déploiement est npx convex deploy. » Distillée de l'expérience au fil du temps.
⚙️ Mémoire procédurale — La mémoire musculaire. Comment faire les choses : « Pour publier un article : créer un brouillon → ajouter une image → publier → vérifier l'URL → réseaux sociaux. »
📋 Mémoire prospective — Votre liste de tâches. Ce que vous devez faire à l'avenir : « Vérifier les performances SEO lundi prochain » ou « Relancer ce prospect après la démo. »
L'insight : ce ne sont pas que des catégories. Chaque type a une persistance, des taux de déclin et des schémas de récupération différents. Les souvenirs sensoriels doivent s'estomper en heures. Le savoir sémantique doit persister indéfiniment. Les souvenirs émotionnels doivent résister à l'oubli. Et les souvenirs connexes doivent s'activer mutuellement.
C'est ce que fait Cortex. Construisons-le.
Pourquoi Convex ? (Ce n'était pas notre premier choix)
Nous utilisions déjà Convex pour notre CMS blog sur contextstudios.ai. Le choix pour la mémoire cognitive est venu de trois constats :
1. Le modèle document s'adapte naturellement aux objets mémoriels
Un souvenir n'est pas une ligne dans une table. C'est un objet riche avec titre, contenu, valence émotionnelle, force, tags, provenance et relations. Le modèle document de Convex s'y adapte parfaitement :
// convex/schema.ts — Le schéma mémoire Cortex
cortexMemories: defineTable({
store: v.union(
v.literal("sensory"), // tampon 24h
v.literal("episodic"), // événements spécifiques
v.literal("semantic"), // connaissances factuelles
v.literal("procedural"), // workflows pratiques
v.literal("prospective") // intentions futures
),
category: v.union(
v.literal("decision"), v.literal("lesson"),
v.literal("person"), v.literal("rule"),
v.literal("event"), v.literal("fact"),
v.literal("goal"), v.literal("workflow")
),
title: v.string(),
content: v.string(),
embedding: v.array(v.float64()), // vecteur 1536-dim
strength: v.float64(), // 0–1, décline avec le temps
confidence: v.float64(), // 0–1, degré de certitude
valence: v.float64(), // -1 à 1 (charge émotionnelle)
arousal: v.float64(), // 0–1 (intensité émotionnelle)
accessCount: v.number(), // fréquence de récupération
lastAccessedAt: v.number(), // pour le score de récence
source: v.union(
v.literal("conversation"),
v.literal("cron"),
v.literal("observation"),
v.literal("inference"),
v.literal("external")
),
tags: v.array(v.string()),
})
2. Recherche vectorielle intégrée (pas d'infrastructure supplémentaire)
C'était la fonctionnalité décisive. L'indexation vectorielle native signifie que nous stockons les embeddings OpenAI aux côtés des documents mémoriels et les recherchons sans déployer Pinecone ou Qdrant :
.vectorIndex("by_embedding", {
vectorField: "embedding",
dimensions: 1536,
filterFields: ["store", "category", "tags"],
})
3. Fonctions serverless pour les processus cognitifs
La mémoire n'est pas que du stockage — c'est un processus. Convex nous donne mutations, requêtes, actions et cron jobs — exactement les bonnes primitives pour le déclin, la consolidation et l'association :
// convex/crons.ts — Processus cognitifs en arrière-plan
crons.interval("cortex consolidation", { hours: 12 }, internal.cortex.consolidate);
crons.interval("cortex decay", { hours: 24 }, internal.cortex.decay);
crons.interval("cortex cleanup", { hours: 24 }, internal.cortex.cleanupExpired);
Pas de fonctions Lambda. Pas de files de workers. Juste des fonctions TypeScript qui s'exécutent selon un planning.
L'architecture Cortex
Cinq magasins de mémoire
┌───────────────────────────────────────────────────────┐
│ MÉMOIRE CORTEX │
├────────────┬──────────────┬──────────────┬────────────┤
│ SENSORIEL │ ÉPISODIQUE │ SÉMANTIQUE │ PROCÉDURAL │
│ (tampon │ (événem.) │ (savoir) │ (pratique) │
│ 24h) │ │ │ │
├────────────┴──────────────┴──────────────┴────────────┤
│ PROSPECTIF │
│ Objectifs, plans, intentions futures │
└───────────────────────────────────────────────────────┘
Auto-promotion : Sensoriel → Épisodique → Sémantique
Toutes les 12 heures, un cron de consolidation regroupe les souvenirs sensoriels connexes (similarité vectorielle > 0,75), les synthétise en souvenirs épisodiques et marque les originaux comme consolidés. Au fil du temps, les souvenirs épisodiques fréquemment consultés avec une haute confiance sont promus en mémoire sémantique. Le système apprend littéralement ce qui est important.
Déclin mémoriel : l'oubli est une fonctionnalité
Chaque souvenir a un champ strength commençant à 1.0 qui décline quotidiennement :
export const decay = internalMutation({
handler: async (ctx) => {
const now = Date.now();
const memories = await ctx.db
.query("cortexMemories")
.filter(q => q.eq(q.field("archivedAt"), undefined))
.collect();
for (const mem of memories) {
if (mem.store === "prospective") continue;
const daysSinceAccess = (now - mem.lastAccessedAt) / (1000 * 60 * 60 * 24);
const isHighEmotion = Math.abs(mem.valence) > 0.7 && mem.arousal > 0.7;
const decayRate = isHighEmotion ? 0.01 : 0.02;
const newStrength = Math.max(0, mem.strength - decayRate * daysSinceAccess);
if (newStrength < 0.1) {
await ctx.db.patch(mem._id, { strength: newStrength, archivedAt: now });
} else if (newStrength !== mem.strength) {
await ctx.db.patch(mem._id, { strength: newStrength });
}
}
},
});
Deux principes : les souvenirs émotionnels persistent plus longtemps, et la récupération renforce la mémoire — répétition espacée pour agents IA.
Activation par propagation : des souvenirs qui se connectent
Les nouveaux souvenirs trouvent automatiquement des souvenirs connexes via la recherche vectorielle et créent des liens d'association :
export const createAutoAssociations = internalAction({
handler: async (ctx, args) => {
const similar = await ctx.vectorSearch("cortexMemories", "by_embedding", {
vector: args.embedding,
limit: 6,
});
for (const result of similar) {
if (result._id === args.memoryId) continue;
await ctx.runMutation(internal.cortex.insertAssociations, {
associations: [{
from: args.memoryId,
to: result._id,
type: "related",
weight: result._score,
createdAt: Date.now(),
}],
});
}
},
});
La récupération utilise une fonction de score composite combinant quatre signaux :
const compositeScore =
mem.strength * 0.3 +
recencyScore * 0.2 +
accessScore * 0.1 +
mem.vectorScore * 0.4;
Intégration avec OpenClaw
Cortex est exposé via 8 outils MCP (Model Context Protocol) :
| Outil | Objectif |
|---|---|
cortex_remember | Stocker un nouveau souvenir |
cortex_recall | Chercher et récupérer des souvenirs |
cortex_what_do_i_know | Vérification de connaissance thématique |
cortex_why_did_we | Archéologie des décisions |
cortex_forget | Suppression explicite de souvenir |
cortex_stats | Statistiques du système mémoriel |
cortex_checkpoint | Sauvegarder le contexte de travail |
cortex_wake | Briefing matinal |
Le pattern double-écriture
Chaque souvenir va dans Cortex (Convex) et dans des fichiers Markdown locaux. Cortex offre la récupération structurée, la recherche vectorielle et le déclin. Les fichiers Markdown fournissent une piste d'audit lisible.
Construisez le vôtre : Cortex minimum viable
Vous voulez livrer quelque chose de similaire en un week-end ? Voici la version minimale — mémoire sensorielle + sémantique avec recherche vectorielle.
Étape 1 : Configurer le schéma Convex
import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";
export default defineSchema({
memories: defineTable({
store: v.union(v.literal("sensory"), v.literal("semantic")),
title: v.string(),
content: v.string(),
embedding: v.array(v.float64()),
strength: v.float64(),
createdAt: v.number(),
lastAccessedAt: v.number(),
tags: v.array(v.string()),
})
.vectorIndex("by_embedding", {
vectorField: "embedding",
dimensions: 1536,
filterFields: ["store", "tags"],
})
.index("by_store", ["store"]),
});
Étape 2 : Écrire la fonction Remember
export const remember = action({
args: {
store: v.union(v.literal("sensory"), v.literal("semantic")),
title: v.string(),
content: v.string(),
tags: v.array(v.string()),
},
handler: async (ctx, args) => {
const response = await fetch("https://api.openai.com/v1/embeddings", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.OPENAI_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "text-embedding-3-small",
input: `${args.title}: ${args.content}`,
}),
});
const data = await response.json();
const now = Date.now();
await ctx.runMutation(internal.memory.insert, {
...args, embedding: data.data[0].embedding, strength: 1.0, createdAt: now, lastAccessedAt: now,
});
},
});
Étape 3 : Écrire la fonction Recall
export const recall = action({
args: {
query: v.string(),
store: v.optional(v.union(v.literal("sensory"), v.literal("semantic"))),
limit: v.optional(v.number()),
},
handler: async (ctx, args) => {
const response = await fetch("https://api.openai.com/v1/embeddings", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.OPENAI_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ model: "text-embedding-3-small", input: args.query }),
});
const data = await response.json();
const results = await ctx.vectorSearch("memories", "by_embedding", {
vector: data.data[0].embedding,
limit: args.limit ?? 5,
filter: args.store ? { store: args.store } : undefined,
});
for (const r of results) {
await ctx.runMutation(internal.memory.touch, { id: r._id });
}
return results;
},
});
Étape 4 : Ajouter le déclin quotidien
import { cronJobs } from "convex/server";
const crons = cronJobs();
crons.daily("memory decay", { hourUTC: 4, minuteUTC: 0 }, internal.memory.decay);
export default crons;
Étape 5 : Connecter à votre agent
Si vous utilisez MCP, exposez remember et recall comme outils. C'est tout — vous avez un système mémoriel fonctionnel avec recherche sémantique, déclin automatique et deux niveaux de mémoire.
Leçons pratiques
- Commencez par le sensoriel, pas le sémantique. Laissez le pipeline de consolidation décider ce qui est important.
- Le déclin est essentiel. Sans lui, votre stockage mémoriel devient un bric-à-brac de décisions obsolètes.
- Les métadonnées émotionnelles comptent. Taguer les souvenirs avec valence et excitation améliore réellement la qualité de récupération.
- Recherche vectorielle + filtrage métadonnées > recherche vectorielle pure. Convex rend cela trivial.
- Les associations créent la sérendipité. La récupération la plus précieuse est parfois un souvenir connexe.
Les résultats
Après le lancement de Cortex, les sessions commencent avec l'agent qui connaît déjà le contexte. Les décisions sont instantanément récupérables. Les leçons persistent vraiment. Sur le tier gratuit de Convex, nous stockons des centaines de souvenirs sans coût d'infrastructure. La recherche vectorielle retourne des résultats en moins de 100ms.
L'impact réel est qualitatif : travailler avec une IA qui se souvient ressemble à collaborer avec un collègue dans l'équipe depuis des mois, versus tout expliquer à un nouveau prestataire chaque matin.
Et ensuite
Cortex est en production. Si vous construisez des agents IA et luttez avec la persistance du contexte, considérez cette approche. Consultez la documentation agents IA de Convex et le protocole MCP. Votre IA n'a pas à repartir de zéro à chaque session.
FAQ
Quelle est la différence entre Cortex et une base vectorielle comme Pinecone ? Cortex ajoute le déclin mémoriel, la consolidation automatique, les métadonnées émotionnelles et l'activation par propagation en plus de la recherche vectorielle. Sur Convex, tout est sur une seule plateforme.
Combien ça coûte ? Actuellement 0 € sur le tier gratuit de Convex. Les seuls coûts externes sont les embeddings OpenAI (fractions de centime par souvenir).
Ça fonctionne avec d'autres modèles que Claude ?
Oui. Cortex est agnostique au modèle, exposé via des outils MCP. Les embeddings utilisent text-embedding-3-small d'OpenAI, mais vous pourriez utiliser n'importe quel modèle 1536 dimensions.
Comment empêchez-vous le stockage d'informations incorrectes ?
Chaque souvenir a un score de confidence, cortex_forget permet la suppression explicite, et le déclin naturel fait que même les souvenirs incorrects s'estompent.
Et si Convex tombe en panne ? Le pattern double-écriture signifie que chaque souvenir existe dans Convex et dans des fichiers Markdown locaux. L'agent bascule sur la recherche par fichiers.