écrits/tutorial/2026/05
Tutorial22 mai 2026·25 min

Créer des agents IA avec mémoire persistante grâce à Mem0 et Vercel AI SDK

Apprenez à ajouter une mémoire persistante et personnalisée à vos agents IA avec Mem0 et Vercel AI SDK v5 dans Next.js. Ce tutoriel couvre la configuration, le stockage, la récupération, les sessions multi-utilisateurs et la construction d'un chatbot prêt pour la production.

Chaque agent IA que vous avez utilisé vous oublie dès que la conversation se termine. Demandez-lui une recommandation de livre demain et il repart de zéro, ignorant tout ce que vous lui aviez confié. Ce fossé entre une session de chat et une vraie continuité, c'est précisément le problème que Mem0 résout.

Dans ce tutoriel, vous allez connecter la couche de mémoire persistante de Mem0 à une application Next.js en utilisant Vercel AI SDK v5. Au terme de ce guide, vous disposerez d'un chatbot qui se souvient d'informations entre les sessions, personnalise ses réponses en fonction des interactions passées, et gère une mémoire isolée pour chaque utilisateur.

Prérequis

Avant de commencer, assurez-vous d'avoir :

  • Node.js 18 ou version ultérieure
  • Un projet Next.js 14 ou 15 (ou créez-en un nouveau comme indiqué ci-dessous)
  • Une clé API Mem0 — inscrivez-vous sur mem0.ai (niveau gratuit disponible)
  • Une clé API OpenAI (ou un autre fournisseur compatible avec Vercel AI SDK)
  • Une familiarité avec React, TypeScript et Next.js App Router

Ce que vous allez construire

Un assistant IA personnel capable de :

  1. Mémoriser les préférences exprimées en conversation — habitudes alimentaires, technologies favorites, style de communication
  2. Rappeler le contexte passé à chaque nouvelle session sans que l'utilisateur ait à se répéter
  3. Isoler la mémoire par utilisateur grâce aux identifiants utilisateur, chaque personne disposant de son propre espace mémoire
  4. Afficher un panneau de mémoires permettant aux utilisateurs de consulter et gérer ce que l'agent sait d'eux

Pourquoi la mémoire persistante est essentielle pour les agents IA

Les grands modèles de langage sont sans état par conception. Chaque appel API est indépendant du précédent. La solution courante — injecter l'intégralité de l'historique de conversation dans la fenêtre de contexte — s'effondre pour les assistants à long terme :

  • Les fenêtres de contexte se remplissent, forçant la troncature
  • Les coûts en tokens augmentent linéairement avec la longueur de la conversation
  • La continuité inter-sessions est impossible sans gestion manuelle de la mémoire

Mem0 résout cela avec une couche de mémoire sémantique qui stocke les faits extraits, ne récupère que les plus pertinents pour chaque requête, et maintient la taille de la mémoire bornée quelle que soit la durée des interactions.

Étape 1 : Configuration du projet

Créez un nouveau projet Next.js ou utilisez un existant :

npx create-next-app@latest mem0-agent --typescript --tailwind --app
cd mem0-agent

Installez les packages nécessaires :

npm install ai @mem0/vercel-ai-provider mem0ai
npm install @ai-sdk/openai

Créez un fichier .env.local à la racine du projet :

MEM0_API_KEY=m0-votre-clé-ici
OPENAI_API_KEY=sk-votre-clé-ici

Étape 2 : Initialiser le fournisseur Mem0

Mem0 fournit un fournisseur officiel pour Vercel AI SDK appelé @mem0/vercel-ai-provider. Il enveloppe n'importe quel modèle avec une couche de mémoire automatique — les souvenirs sont extraits de chaque message et injectés comme contexte au tour suivant.

Créez lib/mem0.ts :

import { createMem0 } from "@mem0/vercel-ai-provider";
 
export const mem0 = createMem0({
  provider: "openai",
  mem0ApiKey: process.env.MEM0_API_KEY!,
  apiKey: process.env.OPENAI_API_KEY!,
});

C'est tout pour la configuration. Le fournisseur gère automatiquement le stockage et la récupération de la mémoire lorsque vous l'utilisez comme modèle dans n'importe quelle fonction Vercel AI SDK.

Étape 3 : Construire la route API de chat

Créez app/api/chat/route.ts :

import { streamText } from "ai";
import { mem0 } from "@/lib/mem0";
import { NextRequest } from "next/server";
 
export async function POST(request: NextRequest) {
  const { messages, userId } = await request.json();
 
  const result = streamText({
    model: mem0("gpt-4o", { user_id: userId }),
    system: `Vous êtes un assistant personnel utile. Soyez concis et personnalisé.
Utilisez ce que vous savez sur l'utilisateur pour adapter vos réponses.`,
    messages,
  });
 
  return result.toDataStreamResponse();
}

La différence clé par rapport à une route Vercel AI SDK standard est l'option user_id passée au modèle. Mem0 l'utilise pour partitionner les mémoires — chaque identifiant utilisateur correspond à un espace mémoire isolé. Vous pouvez utiliser n'importe quelle chaîne stable : un UUID Supabase, un identifiant Clerk, ou un token de session.

Étape 4 : Gérer les mémoires directement

Pour un contrôle plus fin, utilisez le Node SDK de mem0ai en parallèle du fournisseur. Créez lib/memory-client.ts :

import MemoryClient from "mem0ai";
 
export const memoryClient = new MemoryClient({
  apiKey: process.env.MEM0_API_KEY!,
});
 
export async function addMemories(
  messages: Array<{ role: string; content: string }>,
  userId: string
) {
  return memoryClient.add(messages, { userId });
}
 
export async function searchMemories(query: string, userId: string) {
  return memoryClient.search(query, {
    filters: { userId },
    limit: 10,
  });
}
 
export async function getAllMemories(userId: string) {
  return memoryClient.getAll({ filters: { userId } });
}
 
export async function deleteMemory(memoryId: string) {
  return memoryClient.delete(memoryId);
}

Créez ensuite une route API pour la gestion des mémoires dans app/api/memories/route.ts :

import { NextRequest, NextResponse } from "next/server";
import {
  searchMemories,
  getAllMemories,
  deleteMemory,
} from "@/lib/memory-client";
 
export async function GET(request: NextRequest) {
  const userId = request.nextUrl.searchParams.get("userId");
  const query = request.nextUrl.searchParams.get("query");
 
  if (!userId) {
    return NextResponse.json({ error: "userId requis" }, { status: 400 });
  }
 
  const memories = query
    ? await searchMemories(query, userId)
    : await getAllMemories(userId);
 
  return NextResponse.json({ memories });
}
 
export async function DELETE(request: NextRequest) {
  const { memoryId } = await request.json();
  await deleteMemory(memoryId);
  return NextResponse.json({ success: true });
}

Étape 5 : Construire l'interface de chat

Créez components/Chat.tsx :

"use client";
 
import { useChat } from "ai/react";
import { useState } from "react";
 
interface ChatProps {
  userId: string;
}
 
export default function Chat({ userId }: ChatProps) {
  const [input, setInput] = useState("");
 
  const { messages, append, isLoading } = useChat({
    api: "/api/chat",
    body: { userId },
  });
 
  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    if (!input.trim()) return;
    append({ role: "user", content: input });
    setInput("");
  };
 
  return (
    <div className="flex flex-col h-full">
      <div className="flex-1 overflow-y-auto p-4 space-y-4">
        {messages.length === 0 && (
          <p className="text-gray-400 text-center mt-8">
            Commencez une conversation. Je me souviendrai de vous entre les sessions.
          </p>
        )}
        {messages.map((m) => (
          <div
            key={m.id}
            className={`flex ${m.role === "user" ? "justify-end" : "justify-start"}`}
          >
            <div
              className={`max-w-[80%] rounded-2xl px-4 py-2 ${
                m.role === "user"
                  ? "bg-blue-600 text-white"
                  : "bg-gray-800 text-gray-100"
              }`}
            >
              {m.content}
            </div>
          </div>
        ))}
        {isLoading && (
          <div className="flex justify-start">
            <div className="bg-gray-800 rounded-2xl px-4 py-2 text-gray-400">
              En train de réfléchir...
            </div>
          </div>
        )}
      </div>
 
      <form onSubmit={handleSubmit} className="p-4 border-t border-gray-700">
        <div className="flex gap-2">
          <input
            value={input}
            onChange={(e) => setInput(e.target.value)}
            placeholder="Écrivez un message..."
            className="flex-1 bg-gray-800 rounded-xl px-4 py-2 text-white outline-none"
          />
          <button
            type="submit"
            disabled={isLoading || !input.trim()}
            className="bg-blue-600 hover:bg-blue-700 disabled:opacity-50 text-white px-6 py-2 rounded-xl"
          >
            Envoyer
          </button>
        </div>
      </form>
    </div>
  );
}

Étape 6 : Ajouter un panneau de mémoires

Donner aux utilisateurs une visibilité sur ce que l'agent sait d'eux renforce la confiance. Créez components/MemoriesPanel.tsx :

"use client";
 
import { useEffect, useState } from "react";
 
interface Memory {
  id: string;
  memory: string;
  created_at: string;
}
 
export default function MemoriesPanel({ userId }: { userId: string }) {
  const [memories, setMemories] = useState<Memory[]>([]);
  const [loading, setLoading] = useState(true);
 
  const fetchMemories = async () => {
    setLoading(true);
    const res = await fetch(`/api/memories?userId=${userId}`);
    const data = await res.json();
    setMemories(data.memories || []);
    setLoading(false);
  };
 
  useEffect(() => {
    fetchMemories();
  }, [userId]);
 
  const handleDelete = async (memoryId: string) => {
    await fetch("/api/memories", {
      method: "DELETE",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ memoryId }),
    });
    setMemories((prev) => prev.filter((m) => m.id !== memoryId));
  };
 
  if (loading) return <p className="text-gray-400 p-4">Chargement des souvenirs...</p>;
 
  return (
    <div className="p-4">
      <div className="flex items-center justify-between mb-4">
        <h2 className="text-lg font-semibold">Ce que je sais de vous</h2>
        <button onClick={fetchMemories} className="text-sm text-blue-400 hover:underline">
          Actualiser
        </button>
      </div>
 
      {memories.length === 0 ? (
        <p className="text-gray-400 text-sm">
          Aucun souvenir pour l'instant. Commencez à discuter pour construire votre profil.
        </p>
      ) : (
        <ul className="space-y-2">
          {memories.map((m) => (
            <li
              key={m.id}
              className="bg-gray-800 rounded-lg p-3 flex items-start justify-between gap-2"
            >
              <span className="text-sm text-gray-200">{m.memory}</span>
              <button
                onClick={() => handleDelete(m.id)}
                className="text-red-400 hover:text-red-300 text-xs shrink-0"
              >
                Supprimer
              </button>
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}

Étape 7 : Assembler le tout

Mettez à jour app/page.tsx :

import Chat from "@/components/Chat";
import MemoriesPanel from "@/components/MemoriesPanel";
 
const USER_ID = "demo-user-001";
 
export default function Home() {
  return (
    <div className="flex h-screen bg-gray-900 text-white">
      <div className="flex-1 flex flex-col max-w-2xl mx-auto border-x border-gray-700">
        <header className="p-4 border-b border-gray-700">
          <h1 className="text-xl font-bold">Assistant IA Personnel</h1>
          <p className="text-sm text-gray-400">Propulsé par Mem0 + Vercel AI SDK</p>
        </header>
        <Chat userId={USER_ID} />
      </div>
 
      <aside className="w-72 border-l border-gray-700 overflow-y-auto">
        <MemoriesPanel userId={USER_ID} />
      </aside>
    </div>
  );
}

Étape 8 : Mémoire multi-utilisateurs en production

Dans une application de production, remplacez le USER_ID codé en dur par une identité d'authentification réelle. Exemple avec NextAuth :

// app/api/chat/route.ts
import { auth } from "@/auth";
import { streamText } from "ai";
import { mem0 } from "@/lib/mem0";
 
export async function POST(request: Request) {
  const session = await auth();
  const userId = session?.user?.id;
 
  if (!userId) {
    return new Response("Non autorisé", { status: 401 });
  }
 
  const { messages } = await request.json();
 
  const result = streamText({
    model: mem0("gpt-4o", { user_id: userId }),
    system: "Vous êtes un assistant personnel utile avec de la mémoire.",
    messages,
  });
 
  return result.toDataStreamResponse();
}

Mem0 garantit que les souvenirs de userId: "alice" ne fuiteront jamais vers userId: "bob". L'espace mémoire de chaque utilisateur est totalement isolé.

Étape 9 : Ajouter des mémoires manuellement

Vous pouvez ajouter des souvenirs manuellement à tout moment — par exemple lorsqu'un utilisateur remplit un formulaire de profil :

await memoryClient.add(
  [
    {
      role: "user",
      content: "Je m'appelle Sara. Je suis data scientist à Tunis.",
    },
  ],
  { userId: "sara-123" }
);

Ces faits sont disponibles pour l'agent dès la prochaine requête, avant que Sara n'ait prononcé un seul mot en chat.

Tester votre implémentation

Lancez le serveur de développement :

npm run dev

Ouvrez http://localhost:3000. Dites à l'assistant quelques faits vous concernant :

  1. "Je suis végétarien et je préfère Python pour le travail en data science."
  2. "Mon fuseau horaire est UTC+1 et je travaille généralement tard le soir."
  3. "Je n'aime pas l'écriture trop formelle."

Rechargez la page pour démarrer une nouvelle session. Demandez une suggestion de repas ou un conseil de programmation — l'assistant devrait utiliser les faits que vous avez partagés lors de la session précédente sans que vous ayez à vous répéter.

Vérifiez le panneau de mémoires sur le côté droit. Vous devriez voir les faits extraits listés, chacun pouvant être supprimé.

Résolution des problèmes courants

Les souvenirs n'apparaissent pas après la conversation

  • Vérifiez que votre MEM0_API_KEY est correctement défini dans .env.local
  • Consultez le tableau de bord Mem0 sur app.mem0.ai pour vérifier que les écritures arrivent bien
  • Assurez-vous de passer le même userId de manière cohérente

Le contexte n'est pas injecté dans les réponses

  • Le fournisseur @mem0/vercel-ai-provider injecte automatiquement les souvenirs. Si les réponses semblent génériques, essayez de poser une question qui fait directement référence à des faits passés.
  • La récupération est sémantique, pas une correspondance exacte. Reformulez votre question si nécessaire.

Dépassement des limites du niveau gratuit

  • Le niveau gratuit de Mem0 permet jusqu'à 500 opérations mémoire par mois. Pour une application en production, passez à un plan payant ou hébergez vous-même la version open source depuis le dépôt GitHub de Mem0.

Prochaines étapes

Maintenant que vous avez un agent IA avec état fonctionnel, voici des extensions naturelles :

  • Recherche sémantique dans les mémoires — utilisez memoryClient.search() avec une requête pour ne récupérer que les souvenirs les plus pertinents
  • Combinaison avec RAG — associez la mémoire Mem0 à un système de récupération de documents pour des agents qui connaissent à la fois les préférences utilisateurs et le contenu spécialisé
  • Catégorisation des mémoires — étiquetez les souvenirs avec des métadonnées comme category: "travail" ou category: "personnel" et filtrez selon le contexte
  • Résumés de session — en fin de session, demandez au modèle de produire un résumé et stockez-le comme souvenir pour la continuité long terme

Conclusion

Les agents sans état sont utiles ; les agents avec état sont indispensables. Avec Mem0 et Vercel AI SDK v5, ajouter une couche de mémoire persistante à votre application Next.js prend moins de 30 minutes de configuration et quelques fichiers TypeScript. Le résultat est un assistant qui devient plus utile à chaque conversation — mémorisant les préférences, le contexte et l'historique de l'utilisateur sans que vous ayez à gérer manuellement une seule table de base de données.

Commencez simplement : déployez la route de chat de base, vérifiez que les souvenirs sont bien stockés, puis développez avec le panneau de mémoires et l'isolation multi-utilisateurs. Chaque étape se cumule pour offrir une expérience utilisateur significativement meilleure.