Une mémoire à long terme pour les agents IA.
WOS est une API de mémoire. Vous stockez une fois les souvenirs d'un utilisateur, puis ne rappelez que ceux qui sont pertinents pour chaque requête et les transmettez au prompt de votre modèle.
La récupération est purement sémantique, sans correspondance par mots-clés ni BM25 : la qualité du rappel est donc identique d'une langue à l'autre. Chaque requête renvoie un contexte réduit et borné, quel que soit le volume stocké, et aucun modèle n'est jamais exécuté sur vos souvenirs stockés.
Opérations de base
store- enregistre un souvenir pour un utilisateur.recall- récupère les souvenirs pertinents pour une requête. C'est l'appel principal.search- recherche sémantique brute sur les souvenirs stockés.supersede- met à jour ou remplace un souvenir devenu obsolète.forget- supprime un souvenir unique ou un utilisateur entier (RGPD).
Trois modèles, une même lignée.
Les modèles WOS portent le nom des supports par lesquels l'humanité a conservé le savoir au fil de l'histoire - Tablet, Scroll, Codex. La pierre, le rouleau, le livre relié : chacun apporte davantage à votre agent que le précédent.
Tablet
DisponibleUn moyen sobre, rapide et économique d'inscrire et de rappeler la mémoire - la fondation sur laquelle chaque modèle s'appuie.
Scroll
DisponibleAjoute un modèle de langage qui lit votre question de plus près et ramène un contexte plus complet, pour que les indices dispersés reviennent ensemble au lieu qu'il en manque un.
Codex
À venirS'ouvre de lui-même à la bonne page - il choisit la mémoire et les outils dont chaque instant a besoin, et s'affine à mesure qu'on l'utilise.
Le rapport de benchmark complet de Tablet 1 se trouve sur la page benchmark.
Payez-nous $2. Économisez plusieurs fois ce montant sur votre LLM.
WOS fournit à votre LLM ~1,200 tokens par requête - une tranche bornée et pertinente - au lieu d'entasser l'historique complet dans chaque prompt. L'écart est énorme, et il grandit avec votre historique.
Chaque $1 dépensé sur WOS fait économiser ~$98 sur le LLM. Un historique plus grand ou un modèle plus cher → un ROI plus grand.
D'où viennent les économies
- Sans WOS, vous entassez l'historique entier dans chaque prompt -
100K tokens × $2.50/1M = $0.25par requête, au tarif d'entrée de GPT-4o (environ 2× plus sur les modèles de classe Opus). - Avec WOS, vous ingérez une fois (
$2/1M), puis chaque requête n'est qu'une petite récupération ($3/1M × 1,200) plus votre LLM sur seulement ~1,200 tokens. - Moins votre LLM lit de tokens, moins vous payez - et WOS garde ce nombre constant à mesure que la mémoire grandit.
Toutes les langues, la même précision.
La récupération est purement sémantique - embeddings uniquement, zéro correspondance par mots-clés ou BM25. La qualité du rappel est donc identique que vos utilisateurs écrivent en 日本語, en 中文, en Español ou en anglais.
La correspondance lexicale comme BM25 est calibrée sur la forme d'une langue donnée - morphologie, espacement, écriture. Dans un store multilingue, la qualité de récupération varie alors selon la langue. WOS n'utilise aucune correspondance lexicale : toutes les langues empruntent le même chemin.
Un seul store, trois langues à la fois
Vous ne choisissez pas une langue par store - mélangez-les librement. Ci-dessous, la mémoire d'un même utilisateur contient à la fois du japonais, de l'anglais et de l'espagnol, et chaque question retrouve le bon souvenir quelle que soit la langue. Il s'agit d'un échange réel avec l'API en production :
# one user, three languages stored together mem.add("彼女はコーヒーより紅茶が好き", user_id="alice") # Japanese mem.add("she works at a design studio in Brooklyn", user_id="alice") # English mem.add("A ella le encanta hacer senderismo los sábados", user_id="alice") # Spanish
"¿Qué bebe ella?" -> 彼女はコーヒーより紅茶が好き "what does she do on weekends?" -> A ella le encanta hacer senderismo los sábados "彼女の仕事は?" -> she works at a design studio in Brooklyn
Aucune étape de traduction, aucune détection de langue, aucune configuration par langue. Souvenirs et questions sont placés selon le sens, pas selon la langue - si le sens correspond, la langue n'a pas d'importance.
Pourquoi nous avons banni les mots-clés à dessein
Un scoring lexical tel que BM25 renforce la récupération pour certaines langues plus que pour d'autres, ce qui pose problème quand un même store contient de nombreuses langues. Nous l'avons donc entièrement retiré du moteur et faisons respecter cette règle en revue de code : avec le moindre scoring lexical dans le chemin, la qualité du rappel varierait selon la langue.
Aucun modèle ne s'exécute sur vos souvenirs.
Le stockage est verbatim et le moteur cherche par embeddings - économique, rapide et déterministe. Un modèle n'est jamais exécuté sur vos souvenirs stockés. Tablet n'utilise aucun modèle ; Scroll et Codex en ajoutent un autour du moteur pour de meilleurs résultats, mais il ne voit jamais que votre requête, jamais ce que vous avez stocké.
- Moteur déterministe. Le moteur renvoie les mêmes souvenirs pour la même requête, à chaque fois - c'est pourquoi la variance de notre benchmark provient uniquement du modèle lecteur.
- Économique à grande échelle. Aucun coût de génération pour stocker ou récupérer : votre facture suit le stockage - pas l'usage de modèle - à mesure que la mémoire grandit.
Vos mots, intacts
Une conception courante exécute un modèle de langage à l'écriture pour extraire et réécrire des « faits » à partir du texte. Cette conception sacrifie trois choses : un coût de génération à chaque écriture, une latence supplémentaire, et le stockage d'une paraphrase du modèle plutôt que des mots d'origine. WOS fait le choix inverse - il stocke ce qui a été dit, sans modification, et laisse votre LLM interpréter à la lecture, le texte original en main.
90.7%, mesuré et reproductible.
90.7% sur LongMemEval-S, en moyenne sur 5 runs indépendants (σ 0.5%, aucun trié sur le volet), noté par le juge canonique GPT-4o du benchmark.
Sur le même benchmark, les scores varient fortement selon le protocole de notation - le juge, le prompt, et ce que la couche de récupération est autorisée à faire. Nous utilisons le juge tiers canonique GPT-4o du benchmark tel que publié, ne modifions rien pour coller au test, et publions le code de notation et le prompt du lecteur pour que chacun puisse reproduire exactement les 90.7%.
Le protocole, en un tableau
| Élément | Ce que nous faisons |
|---|---|
| Jeu de données | LongMemEval-S (nettoyé), ~240K tokens d'historique par question |
| Juge | Le juge canonique GPT-4o du benchmark - un tiers, pas nous |
| Runs | 5 runs indépendants, chaque score publié, moyenne rapportée (σ 0.5%) |
| Lecteur | Modèle lecteur et prompt fixes, publiés verbatim |
Ce qui garantit l'honnêteté : un juge tiers, le prompt du lecteur publié tel quel, une récupération purement sémantique, et chaque run rapporté - pas seulement le meilleur. Le moteur de récupération est déterministe - relancez-le et vous obtenez les mêmes souvenirs.
Nous gravissons des benchmarks plus difficiles
Nous testons sur le benchmark standard le plus difficile que nous n'avons pas encore conquis - et le chiffre affiché est le record parmi tous les modèles WOS, réécrit chaque fois qu'un meilleur modèle sort. Passé 94%, nous passons à un benchmark plus difficile.
Deux tarifs de tokens par modèle,
plus $0.0001 par requête.
Par million de tokens plus $0.0001 fixe par requête, à l'usage. Pas d'abonnement, pas de loyer de stockage, pas de plafond de mémoire. Vous payez quand votre agent écrit ou lit - jamais pour ce qu'il retient.
| Modèle | Entrée / 1M | Sortie / 1M | |
|---|---|---|---|
| Tablet 1 | $2 | $3 | Disponible |
| Scroll 1 | $4 | $8 | Disponible |
| Codex 1 | - | - | À définir |
- $0.0001 par requête. Des frais fixes sur chaque appel API, en plus de l'usage en tokens.
- Le stockage est gratuit. L'ingestion se paie une fois ; la conservation ne coûte rien. Aucune limite de nombre, aucune limite de rétention.
- Nous le stockons. Nous ne nous entraînons jamais dessus, ne l'utilisons jamais et ne le regardons jamais. La mémoire de votre agent vous appartient - nous ne faisons que l'organiser pour que vous puissiez la retrouver.
- Pourquoi Tablet est si peu cher : son moteur n'exécute aucun modèle, notre coût se limite donc aux embeddings et au disque - pas aux GPU. Scroll et Codex ajoutent un modèle, et c'est ce que couvre leur prix plus élevé.
Trois appels : store, recall, answer.
Une seule API. L'appel recall() renvoie le court terme, le long terme et le contexte environnant en un seul aller-retour, prêt à être inséré dans votre prompt.
Store
add() les faits et les tours de conversation de votre utilisateur. Encodé en embeddings à l'entrée - aucun LLM.
Recall
recall() renvoie court terme + long terme + contexte en un seul appel - un contexte borné, de taille fixe.
Answer
Fournissez ce contexte borné à votre LLM - n'importe quel fournisseur, votre clé.
from wontopos import Client mem = Client(api_key="wos-...") mem.add("she prefers tea over coffee", user_id="alice") # one call: short + long + context ctx = mem.recall("what does alice drink?", user_id="alice")
Votre premier recall en 5 minutes.
Une clé, une ligne d'installation, trois appels - votre agent a de la mémoire. Chaque extrait de cette page a réellement été exécuté ; les réponses sont montrées verbatim.
Obtenir une clé API
Créez-en une dans la console. Une clé de 155 caractères commençant par wos-live- est affichée une seule fois. Gardez-la dans une variable d'environnement - jamais dans le code.
Installation
pip install wontopos # Python npm install wontopos # TypeScript / JavaScript cargo add wontopos # Rust # curl - nothing to install, just set WOS_API_KEY
Créez un store, puis store & recall
Un store est le user_id sous lequel vous lisez et écrivez. Les stores sont explicites : créez-en un d'abord (l'appel ci-dessous), puis stockez et rappelez sous celui-ci. Store - encodé en embeddings à l'entrée, aucun appel LLM. Recall - court terme + long terme + contexte en un seul aller-retour.
from wontopos import Client
mem = Client(api_key="wos-live-...", user_id="alice") # set the store once
mem.create_store() # create it (stores are explicit)
mem.add("she prefers tea over coffee") # no user_id needed
# one call → short-term + long-term + context
ctx = mem.recall("what does alice drink?"){"user_id": "alice", "status": "created"}{"id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624", "status": "stored (1 chunks)"}user_id au client et chaque appel l'utilise - inutile de le répéter ; surchargez un appel isolé en lui passant user_id. Les stores sont explicites : stocker dans un store inexistant ou rappeler depuis celui-ci renvoie 404 - créez-le d'abord. Chaque compte démarre avec un store default : sans aucun user_id, le chemin zéro configuration fonctionne donc tel quel. Voir Stores pour les lister et les gérer.recall() renvoie quatre blocs - short_term (tours récents), long_term (souvenirs pertinents), context (ce qui entourait la meilleure correspondance) et une instruction indiquant au LLM comment les utiliser. Insérez l'ensemble tel quel dans votre prompt.
Stores - créer, lister, supprimer.
Un store est le user_id sous lequel vous lisez et écrivez - un espace mémoire isolé par utilisateur final, agent ou sujet. Les stores sont explicites : créez-en un avant d'y stocker ou d'y rappeler, sinon l'appel renvoie 404. Chaque compte démarre avec un store default, vous pouvez donc commencer sans appel de création.
mem.create_store("alice") # create (idempotent)
mem.list_stores() # [{"user_id","created_at"}, ...]
mem.delete_store("alice") # delete the store + all its memories{ "user_id": "alice", "status": "created" } // "exists" if it already did{ "collections": [
{ "user_id": "default", "created_at": "2026-06-26T02:23:14Z" },
{ "user_id": "alice", "created_at": "2026-06-26T02:24:01Z" }
], "count": 2 }{ "error": { "type": "not_found_error",
"message": "Store 'ghost' does not exist. Create it first with
POST /api/v1/memory/collection {\"user_id\":\"ghost\"}, then store or recall." } }"alice", "user_42") pour garder la mémoire de chaque personne séparée, ou un unique store default pour un agent personnel. Vous pouvez aussi créer et parcourir les stores dans la console (Memory ids → Issue) sans écrire de code. La suppression d'un store est définitive - elle efface tous les souvenirs qu'il contient.Python - chaque méthode, en trois groupes.
Écrire, lire, supprimer. Chaque exemple ci-dessous a été exécuté contre l'API en production le 2026-06-10 ; les réponses sont verbatim.
pip install wontopos
from wontopos import Client mem = Client(api_key="wos-live-...") # or read from an env var
Choisir un modèle
La clé API détermine quelle mémoire (votre compte) ; le modèle détermine quel moteur la lit. Tous les modèles partagent une même mémoire : vous pouvez donc stocker avec l'un et rappeler avec un autre. Définissez un défaut sur le client ; surchargez un appel isolé en passant model=.
mem = Client(api_key="wos-live-...", model="tablet-1") # default engine mem.recall("...", user_id="alice") # tablet-1 mem.recall("...", user_id="alice", model="tablet-1") # or pick a model per call
list_models ✓ live-tested
Le catalogue - les ids que vous pouvez passer à model et la disponibilité de chacun. Les modèles memory: "shared" lisent le même store ; "isolated" garde le sien. Aucune clé API requise.
mem.list_models()[{"id": "tablet-1", "name": "Tablet 1", "available": true, "memory": "shared"}]Le catalogue ci-dessus reflète toujours les modèles disponibles à l'instant même - passez tout autre id et vous obtenez une erreur claire. Les nouveaux modèles y apparaissent automatiquement dès leur sortie.
Écrire
add ✓ live-tested
Stocke un souvenir. Encodé en embeddings à l'entrée - aucun appel LLM, vous ne payez que les embeddings.
mem.add("she prefers tea over coffee", user_id="alice")
{"id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624", "status": "stored (1 chunks)"}add_turn ✓ live-tested
Stocke un tour de conversation (utilisateur + assistant) à la fois en mémoire court terme et long terme.
mem.add_turn("alice", "hi", "hello!")
{"status": "ok"}add_bulk ✓ live-tested
Importe un gros bloc de texte. Découpé et encodé côté serveur - idéal pour importer un historique existant.
mem.add_bulk("Alice moved to Brooklyn in March. She works at a design studio downtown.", user_id="alice")
{"elapsed_secs": 0.154154944, "status": "ok", "stored": 1, "total_chunks": 1}update ✓ live-tested
Un fait a changé. L'ancien souvenir est marqué superseded (conservé pour l'historique) ; le nouveau prend sa place dans le recall.
mem.update("alice", old_memory_id="576700aa-...", new_content="she switched to coffee this year")
{"new_memory_id": "07e94433-b7cc-4e49-8d8f-f37fc1a392b7",
"old_memory_id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624", "status": "superseded"}Lire
search ✓ live-tested
Recherche sémantique, du plus pertinent au moins pertinent. Pur embedding - aucune correspondance par mots-clés, n'importe quelle langue trouve donc n'importe quel souvenir. Le SDK renvoie directement le tableau memories ; le corps HTTP brut est montré ci-dessous.
r = mem.search("what does she drink?", user_id="alice", limit=1)
{"memories": [{
"id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624",
"content": "she prefers tea over coffee",
"category": "general",
"time_bucket": "2026-06",
"importance": 0.3,
"similarity": 0.6316057443618774,
"is_superseded": false,
"superseded_by": null,
"created_at": "2026-06-10T04:20:39.688276876Z"
}], "search_ms": 315, "total_found": 1}| Champ | Signification |
|---|---|
| similarity | Similarité d'embedding brute avec votre requête (0–1). |
| is_superseded | Vrai si ce fait a été remplacé par update(). |
| search_ms | Temps de récupération côté serveur. |
recall ✓ live-tested
Un seul aller-retour renvoie tout ce dont votre LLM a besoin - collez le résultat directement dans votre prompt : un contexte borné, de taille fixe, quel que soit le volume stocké.
ctx = mem.recall("what does she drink?", user_id="alice")
{"short_term": {"count": 2, "turns": [{"role": "user", "content": "hi", ...}]},
"long_term": {"count": 4, "memories": [{"content": "she prefers tea over coffee",
"similarity": 0.63, ...}]},
"context": {"count": 4, "around_top_memory": [
"[match] she prefers tea over coffee",
"[after] Alice moved to Brooklyn in March. ..."]},
"instruction": "Use short_term for recent context, long_term for relevant
past memories, context for surrounding conversation of the
most relevant memory."}history ✓ live-tested
Tours de conversation récents (mémoire court terme), du plus ancien au plus récent.
turns = mem.history("alice")
{"count": 2, "turns": [
{"role": "user", "content": "hi", "timestamp": "2026-06-10T04:20:40.989011337Z"},
{"role": "assistant", "content": "hello!", "timestamp": "2026-06-10T04:20:40.989013416Z"}
], "user_id": "alice"}stats ✓ live-tested
Compteurs de souvenirs pour un utilisateur.
mem.stats("alice")
{"short_term_turns": 2, "total_memories": 4, "user_id": "alice"}Supprimer
delete ✓ live-tested
Supprime un souvenir unique par id.
mem.delete("alice", memory_id="576700aa-...")
{"memory_id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624", "status": "deleted"}delete_all ✓ live-tested
Efface tout pour un utilisateur - un seul appel, conforme RGPD.
mem.delete_all("alice")
{"memories_deleted": 4, "status": "deleted", "user_id": "alice"}TypeScript - chaque méthode, en trois groupes.
Écrire, lire, supprimer. Chaque exemple ci-dessous a été exécuté contre l'API en production le 2026-06-10 ; les réponses sont verbatim.
npm install wontopos
import { Client } from "wontopos"; const mem = new Client({ apiKey: "wos-live-..." });
Choisir un modèle
La clé API détermine quelle mémoire (votre compte) ; le modèle détermine quel moteur la lit. Tous les modèles partagent une même mémoire : vous pouvez donc stocker avec l'un et rappeler avec un autre. Définissez un défaut dans le constructeur ; surchargez un appel isolé avec withModel().
const mem = new Client({ apiKey: "wos-live-...", model: "tablet-1" }); // default mem.recall("...", "alice"); // tablet-1 mem.withModel("tablet-1").recall("...", "alice"); // or pick a model per call
listModels ✓ live-tested
Le catalogue - les ids que vous pouvez passer à model et la disponibilité de chacun. Les modèles memory: "shared" lisent le même store ; "isolated" garde le sien. Aucune clé API requise.
await mem.listModels();
[{"id": "tablet-1", "name": "Tablet 1", "available": true, "memory": "shared"}]Le catalogue ci-dessus reflète toujours les modèles disponibles à l'instant même - passez tout autre id et vous obtenez une erreur claire. Les nouveaux modèles y apparaissent automatiquement dès leur sortie.
Écrire
add ✓ live-tested
Stocke un souvenir. Encodé en embeddings à l'entrée - aucun appel LLM, vous ne payez que les embeddings.
await mem.add("she prefers tea over coffee", "alice");
{"id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624", "status": "stored (1 chunks)"}addTurn ✓ live-tested
Stocke un tour de conversation (utilisateur + assistant) à la fois en mémoire court terme et long terme.
await mem.addTurn("alice", "hi", "hello!");
{"status": "ok"}addBulk ✓ live-tested
Importe un gros bloc de texte. Découpé et encodé côté serveur - idéal pour importer un historique existant.
await mem.addBulk("Alice moved to Brooklyn in March. She works at a design studio downtown.", "alice");
{"elapsed_secs": 0.154154944, "status": "ok", "stored": 1, "total_chunks": 1}update ✓ live-tested
Un fait a changé. L'ancien souvenir est marqué superseded (conservé pour l'historique) ; le nouveau prend sa place dans le recall.
await mem.update("alice", "576700aa-...", "she switched to coffee this year");
{"new_memory_id": "07e94433-b7cc-4e49-8d8f-f37fc1a392b7",
"old_memory_id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624", "status": "superseded"}Lire
search ✓ live-tested
Recherche sémantique, du plus pertinent au moins pertinent. Pur embedding - aucune correspondance par mots-clés, n'importe quelle langue trouve donc n'importe quel souvenir. Le SDK renvoie directement le tableau memories ; le corps HTTP brut est montré ci-dessous.
const r = await mem.search("what does she drink?", "alice", 1);
{"memories": [{
"id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624",
"content": "she prefers tea over coffee",
"category": "general",
"time_bucket": "2026-06",
"importance": 0.3,
"similarity": 0.6316057443618774,
"is_superseded": false,
"superseded_by": null,
"created_at": "2026-06-10T04:20:39.688276876Z"
}], "search_ms": 315, "total_found": 1}| Champ | Signification |
|---|---|
| similarity | Similarité d'embedding brute avec votre requête (0–1). |
| is_superseded | Vrai si ce fait a été remplacé par update(). |
| search_ms | Temps de récupération côté serveur. |
recall ✓ live-tested
Un seul aller-retour renvoie tout ce dont votre LLM a besoin - collez le résultat directement dans votre prompt : un contexte borné, de taille fixe, quel que soit le volume stocké.
const ctx = await mem.recall("what does she drink?", "alice");
{"short_term": {"count": 2, "turns": [{"role": "user", "content": "hi", ...}]},
"long_term": {"count": 4, "memories": [{"content": "she prefers tea over coffee",
"similarity": 0.63, ...}]},
"context": {"count": 4, "around_top_memory": [
"[match] she prefers tea over coffee",
"[after] Alice moved to Brooklyn in March. ..."]},
"instruction": "Use short_term for recent context, long_term for relevant
past memories, context for surrounding conversation of the
most relevant memory."}history ✓ live-tested
Tours de conversation récents (mémoire court terme), du plus ancien au plus récent.
const turns = await mem.history("alice");
{"count": 2, "turns": [
{"role": "user", "content": "hi", "timestamp": "2026-06-10T04:20:40.989011337Z"},
{"role": "assistant", "content": "hello!", "timestamp": "2026-06-10T04:20:40.989013416Z"}
], "user_id": "alice"}stats ✓ live-tested
Compteurs de souvenirs pour un utilisateur.
await mem.stats("alice");
{"short_term_turns": 2, "total_memories": 4, "user_id": "alice"}Supprimer
delete ✓ live-tested
Supprime un souvenir unique par id.
await mem.delete("alice", "576700aa-...");
{"memory_id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624", "status": "deleted"}deleteAll ✓ live-tested
Efface tout pour un utilisateur - un seul appel, conforme RGPD.
await mem.deleteAll("alice");
{"memories_deleted": 4, "status": "deleted", "user_id": "alice"}Rust - chaque méthode, en trois groupes.
Écrire, lire, supprimer. Chaque exemple ci-dessous a été exécuté contre l'API en production le 2026-06-10 ; les réponses sont verbatim.
cargo add wontopos
use wontopos::Client; let mem = Client::new("wos-live-...");
Choisir un modèle
La clé API détermine quelle mémoire (votre compte) ; le modèle détermine quel moteur la lit. Tous les modèles partagent une même mémoire : vous pouvez donc stocker avec l'un et rappeler avec un autre. Définissez un défaut avec with_model() ; chaînez-le à nouveau pour surcharger un appel isolé.
let mem = Client::new("wos-live-...").with_model("tablet-1"); // default mem.recall("...", "alice").await?; // tablet-1 mem.with_model("tablet-1").recall("...", "alice").await?; // or pick a model per call
list_models ✓ live-tested
Le catalogue - les ids que vous pouvez passer à with_model et la disponibilité de chacun. Les modèles memory: "shared" lisent le même store ; "isolated" garde le sien. Aucune clé API requise.
mem.list_models().await?;
[{"id": "tablet-1", "name": "Tablet 1", "available": true, "memory": "shared"}]Le catalogue ci-dessus reflète toujours les modèles disponibles à l'instant même - passez tout autre id et vous obtenez une erreur claire. Les nouveaux modèles y apparaissent automatiquement dès leur sortie.
Écrire
add ✓ live-tested
Stocke un souvenir. Encodé en embeddings à l'entrée - aucun appel LLM, vous ne payez que les embeddings.
mem.add("she prefers tea over coffee", "alice", json!({})).await?;
{"id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624", "status": "stored (1 chunks)"}add_turn ✓ live-tested
Stocke un tour de conversation (utilisateur + assistant) à la fois en mémoire court terme et long terme.
mem.add_turn("alice", "hi", "hello!").await?;
{"status": "ok"}add_bulk ✓ live-tested
Importe un gros bloc de texte. Découpé et encodé côté serveur - idéal pour importer un historique existant.
mem.add_bulk("Alice moved to Brooklyn in March...", "alice", "general").await?;
{"elapsed_secs": 0.154154944, "status": "ok", "stored": 1, "total_chunks": 1}update ✓ live-tested
Un fait a changé. L'ancien souvenir est marqué superseded (conservé pour l'historique) ; le nouveau prend sa place dans le recall.
mem.update("alice", "576700aa-...", "she switched to coffee this year").await?;
{"new_memory_id": "07e94433-b7cc-4e49-8d8f-f37fc1a392b7",
"old_memory_id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624", "status": "superseded"}Lire
search ✓ live-tested
Recherche sémantique, du plus pertinent au moins pertinent. Pur embedding - aucune correspondance par mots-clés, n'importe quelle langue trouve donc n'importe quel souvenir. Le SDK renvoie directement le tableau memories ; le corps HTTP brut est montré ci-dessous.
let r = mem.search("what does she drink?", "alice", 1).await?;
{"memories": [{
"id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624",
"content": "she prefers tea over coffee",
"category": "general",
"time_bucket": "2026-06",
"importance": 0.3,
"similarity": 0.6316057443618774,
"is_superseded": false,
"superseded_by": null,
"created_at": "2026-06-10T04:20:39.688276876Z"
}], "search_ms": 315, "total_found": 1}| Champ | Signification |
|---|---|
| similarity | Similarité d'embedding brute avec votre requête (0–1). |
| is_superseded | Vrai si ce fait a été remplacé par update(). |
| search_ms | Temps de récupération côté serveur. |
recall ✓ live-tested
Un seul aller-retour renvoie tout ce dont votre LLM a besoin - collez le résultat directement dans votre prompt : un contexte borné, de taille fixe, quel que soit le volume stocké.
let ctx = mem.recall("what does she drink?", "alice").await?;
{"short_term": {"count": 2, "turns": [{"role": "user", "content": "hi", ...}]},
"long_term": {"count": 4, "memories": [{"content": "she prefers tea over coffee",
"similarity": 0.63, ...}]},
"context": {"count": 4, "around_top_memory": [
"[match] she prefers tea over coffee",
"[after] Alice moved to Brooklyn in March. ..."]},
"instruction": "Use short_term for recent context, long_term for relevant
past memories, context for surrounding conversation of the
most relevant memory."}history ✓ live-tested
Tours de conversation récents (mémoire court terme), du plus ancien au plus récent.
let turns = mem.history("alice").await?;
{"count": 2, "turns": [
{"role": "user", "content": "hi", "timestamp": "2026-06-10T04:20:40.989011337Z"},
{"role": "assistant", "content": "hello!", "timestamp": "2026-06-10T04:20:40.989013416Z"}
], "user_id": "alice"}stats ✓ live-tested
Compteurs de souvenirs pour un utilisateur.
mem.stats("alice").await?;
{"short_term_turns": 2, "total_memories": 4, "user_id": "alice"}Supprimer
delete ✓ live-tested
Supprime un souvenir unique par id.
mem.delete("alice", "576700aa-...").await?;
{"memory_id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624", "status": "deleted"}delete_all ✓ live-tested
Efface tout pour un utilisateur - un seul appel, conforme RGPD.
mem.delete_all("alice").await?;
{"memories_deleted": 4, "status": "deleted", "user_id": "alice"}curl - aucune installation, mêmes méthodes.
Aucun SDK à installer - n'importe quel client HTTP convient. Définissez votre clé une fois et appelez les mêmes endpoints que les SDK encapsulent. URL de base https://api.wontopos.com, authentification via X-API-Key, JSON en entrée comme en sortie.
# set your key once (never hard-code it) export WOS_API_KEY="wos-live-..."
Écrire
store ✓ live-tested
Stocke un souvenir. Encodé en embeddings à l'entrée - aucun appel LLM.
curl -X POST https://api.wontopos.com/api/v1/memory/store \ -H "X-API-Key: $WOS_API_KEY" -H "Content-Type: application/json" \ -d '{"user_id":"alice","content":"she prefers tea over coffee"}'
{"id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624", "status": "stored (1 chunks)"}store-turn ✓ live-tested
Stocke un tour de conversation (utilisateur + assistant) à la fois en mémoire court terme et long terme.
curl -X POST https://api.wontopos.com/api/v1/memory/store-turn \ -H "X-API-Key: $WOS_API_KEY" -H "Content-Type: application/json" \ -d '{"user_id":"alice","user_msg":"hi","assistant_msg":"hello!"}'
{"status": "ok"}supersede ✓ live-tested
Un fait a changé - l'ancien souvenir est marqué superseded, le nouveau prend sa place dans le recall.
curl -X POST https://api.wontopos.com/api/v1/memory/supersede \ -H "X-API-Key: $WOS_API_KEY" -H "Content-Type: application/json" \ -d '{"user_id":"alice","old_memory_id":"576700aa-...","new_content":"she switched to coffee this year"}'
{"new_memory_id": "07e94433-...", "old_memory_id": "576700aa-...", "status": "superseded"}Lire
search ✓ live-tested
Recherche sémantique, du plus pertinent au moins pertinent. Pur embedding - n'importe quelle langue trouve n'importe quel souvenir.
curl -X POST https://api.wontopos.com/api/v1/memory/search \ -H "X-API-Key: $WOS_API_KEY" -H "Content-Type: application/json" \ -d '{"user_id":"alice","query":"what does she drink?","max_results":1}'
{"memories": [{"id": "576700aa-...", "content": "she prefers tea over coffee",
"similarity": 0.63, "is_superseded": false}], "search_ms": 315, "total_found": 1}recall ✓ live-tested
Un seul aller-retour renvoie court terme + long terme + contexte + une instruction. Collez-le directement dans votre prompt.
curl -X POST https://api.wontopos.com/api/v1/memory/recall \ -H "X-API-Key: $WOS_API_KEY" -H "Content-Type: application/json" \ -d '{"user_id":"alice","query":"what does she drink?"}'
{"short_term": {"count": 2, "turns": [...]},
"long_term": {"count": 4, "memories": [{"content": "she prefers tea over coffee", "similarity": 0.63}]},
"context": {"count": 4, "around_top_memory": ["[match] she prefers tea over coffee"]},
"instruction": "Use short_term for recent context, long_term for relevant past memories..."}Supprimer
forget ✓ live-tested
Supprime un souvenir par id, ou omettez l'id pour tout supprimer pour un utilisateur (RGPD).
curl -X POST https://api.wontopos.com/api/v1/memory/forget \ -H "X-API-Key: $WOS_API_KEY" -H "Content-Type: application/json" \ -d '{"user_id":"alice"}' # omit memory_id = delete all
{"memories_deleted": 1, "status": "deleted", "user_id": "alice"}Engrams
Des outils de rappel invocables que votre modèle peut appeler - chacun étant une stratégie de récupération différente sur la même mémoire. Utilisez-en un, ou lancez-en plusieurs à la fois.
De nouveaux engrammes sortent régulièrement - cette liste s'allonge.
Memoir & Archive Bientôt disponible
Celui-ci est un mode de modèle, pas un outil invocable. Choisissez-le au niveau du modèle et chaque rappel, simple recherche comprise, revient avec le temps écrit de cette façon. Bientôt disponible.
Time_awareness Bientôt disponible
Un mode de modèle, pas une option par appel. Choisissez le modèle - Time_awareness-memoir ou Time_awareness-archive - et chaque rappel revient rendu de cette façon : une simple recherche, un recall ou n'importe quel engramme, sans paramètre supplémentaire. Un Memoir se lit comme une personne se souvient ; une Archive tient un registre exact - la différence se voit surtout dans la façon dont chacun écrit le temps. Le préfixe Time_awareness- laisse la place à d'autres capacités plus tard.
Memoir
Time_awareness-memoirRaconte ce qui s'est passé et comment un moment a mené au suivant, avec le sens diffus du temps propre au souvenir humain - à lire comme une expérience, pas comme une liste.
Archive
Time_awareness-archiveRenvoie les correspondances comme des enregistrements exacts - temps écoulé précis et ancres absolues, structurés pour qu'un modèle les lise directement.
store / add sous un user_id (ce user_id est le store de cette personne). Stockez d'abord ; ensuite tout rappel - y compris la simple recherche ci-dessous - revient horodaté. Voir Démarrage rapide pour stocker.# plain search - no engram - on a memoir model
r = mem.search("what does Alice drink?", user_id="alice", model="Time_awareness-memoir", tz=9)
# every hit gets a .time field → "a couple weeks ago" (archive model → "2 weeks ago (Jun 09)")tz est le décalage UTC de l'appelant, en heures - ainsi « ce matin » et la limite de journée à 4 h du matin tombent dans son heure locale. Omettez-le pour UTC ; en HTTP, c'est l'en-tête X-WOS-Timezone. En gros, par région : côte Est des États-Unis -5, Centre -6, côte Ouest -8 · Royaume-Uni / Lisbonne 0 · Europe centrale +1 · Europe de l'Est +2 · Inde +5.5 · Chine / Singapour +8 · Corée / Japon +9 · Sydney +10. (Heure standard - l'heure d'été décale certaines régions de +1 ; passez ce que vos utilisateurs utilisent réellement.)
Même recherche, deux modèles - les souvenirs sont identiques, seul time change :
{ "count": 3, "memories": [
{ "content": "Alice prefers tea over coffee", "time": "a couple weeks ago" },
{ "content": "met Alice at the cafe downtown", "time": "yesterday afternoon" },
{ "content": "Alice moved to Brooklyn", "time": "about half a year ago" }
] }{ "count": 3, "memories": [
{ "content": "Alice prefers tea over coffee", "time": "2 weeks ago (Jun 09)" },
{ "content": "met Alice at the cafe downtown", "time": "yesterday at 14:00" },
{ "content": "Alice moved to Brooklyn", "time": "6 months ago (Dec 2025)" }
] }| Écoulé | Memoir | Archive |
|---|---|---|
| 3 min | a few minutes ago | 3 minutes ago |
| 14 min | about 15 minutes ago | 14 minutes ago |
| 30 min | half an hour ago | 30 minutes ago |
| 50 min | about an hour ago | 50 minutes ago |
| 2 h | a couple hours ago | 2 hours ago, at 13:10 |
| 8 h | this morning | 8 hours ago, at 07:10 |
| hier ap.-midi | yesterday afternoon | yesterday at 14:00 |
| la nuit dernière | last night | 17 hours ago, at 22:00 |
| 2 jours | a couple days ago | 2 days ago (Tue 15:10) |
| 6 jours | several days ago | 6 days ago (Fri 15:10) |
| 9 jours | about a week ago | last week (Jun 16) |
| 16 jours | a couple weeks ago | 2 weeks ago (Jun 09) |
| 35 jours | about a month ago | last month (May 21) |
| 60 jours | a couple months ago | 2 months ago (Apr 2026) |
| 180 jours | about half a year ago | 6 months ago (Dec 2025) |
| 380 jours | about a year ago | last year (Jun 2025) |
| 800 jours | a couple years ago | 2 years ago (Apr 2024) |
| 1500 jours | about 4 years ago | 4 years ago (May 2022) |
Chaque valeur ci-dessus est la sortie réelle du moteur de rendu. Regardez les deux lignes « hier » : un Memoir sépare l'après-midi de la nuit dernière - une journée est un sommeil - tandis qu'une Archive écrit une seule heure d'horloge et ne trace aucune frontière entre jour et nuit.
Comment chaque mode lit le temps
Memoir - comme les gens le disent vraiment. Les moments récents restent assez nets (environ 15 minutes, une demi-heure), puis la formulation s'élargit à mesure qu'on remonte - quelques semaines, environ six mois, quelques années - comme la mémoire elle-même se relâche avec la distance. À l'intérieur d'une journée, l'horloge cède la place à un repère : ce matin, la nuit dernière, hier après-midi. Et une journée est un sommeil, pas un tic de calendrier : la frontière se situe vers 4 h du matin, heure locale, si bien qu'une fin de soirée tardive se lit encore comme le même soir, pas déjà comme le lendemain.
Archive - précis, toujours avec une ancre. Chaque ligne porte le temps écoulé exact plus une référence absolue à partir de laquelle un modèle peut calculer, et l'ancre se resserre à mesure qu'on se rapproche : une heure d'horloge pour aujourd'hui (il y a 8 heures, à 07:10), un jour de semaine et une heure cette semaine (il y a 2 jours (mar. 15:10)), une date ce mois-ci (la semaine dernière (16 juin)), un mois et une année au-delà (il y a 6 mois (déc. 2025)). Jamais vague, jamais faux.
deep_recall
Rappel multi-sauts. Cherche votre requête, puis prend la meilleure correspondance et relance une recherche sur son contenu - ramenant du contexte lié qu'une recherche unique manquerait. Idéal quand les souvenirs se référencent entre eux (une personne → ses projets → les détails). Renvoie jusqu'à ~12 résultats.
out = mem.engram("deep_recall", "what should I know about Alice?", user_id="alice"){ "engram": "deep_recall", "hops": 2, "count": 12,
"memories": [ ... ],
"usage": { "input_tokens": 5, "output_tokens": 61 } }usage (entrée + sortie), compté par le même tokenizer que le reste de l'API ; aucun frais caché par engramme. Besoin de plusieurs à la fois ? Appelez-les en parallèle - chaque engramme est une requête indépendante.timeline
Rappel chronologique. Renvoie les souvenirs triés du plus récent au plus ancien selon la date de l'événement, pas selon la pertinence. Pour les questions « quand X a-t-il eu lieu », l'historique et les séquences. Renvoie jusqu'à 15 résultats.
events = mem.engram("timeline", "project milestones", user_id="alice"){ "engram": "timeline", "hops": 1, "count": 15,
"memories": [ ... ],
"usage": { "input_tokens": 4, "output_tokens": 88 } }usage (entrée + sortie), compté par le même tokenizer que le reste de l'API ; aucun frais caché par engramme. Besoin de plusieurs à la fois ? Appelez-les en parallèle - chaque engramme est une requête indépendante.gather
Collecte large. Cherche, puis s'étend autour des trois meilleures correspondances - un filet plus large que deep_recall. Utilisez-le pour ramener tout ce qui touche à une personne, un projet ou un sujet en un seul appel. Renvoie jusqu'à ~18 résultats.
related = mem.engram("gather", "everything about Project Atlas", user_id="alice"){ "engram": "gather", "hops": 4, "count": 18,
"memories": [ ... ],
"usage": { "input_tokens": 6, "output_tokens": 142 } }usage (entrée + sortie), compté par le même tokenizer que le reste de l'API ; aucun frais caché par engramme. Besoin de plusieurs à la fois ? Appelez-les en parallèle - chaque engramme est une requête indépendante.Chaque endpoint, une seule URL de base.
Aucun SDK requis - n'importe quel client HTTP convient. URL de base https://api.wontopos.com, authentification via l'en-tête X-API-Key, JSON en entrée comme en sortie. Les opérations mémoire sont en POST ; la gestion des stores utilise POST / GET / DELETE sur /collection. Un store doit exister au préalable (voir Stores), sinon les opérations dans le store renvoient 404.
| Endpoint | Rôle | Champs du corps |
|---|---|---|
| POST /api/v1/memory/collection | créer un store | user_id |
| GET /api/v1/memory/collections | lister vos stores | (aucun) |
| DELETE /api/v1/memory/collection | supprimer un store + ses souvenirs | user_id |
| /api/v1/memory/store | stocker un souvenir | user_id · content · metadata? |
| /api/v1/memory/store-turn | stocker un tour de conversation | user_id · user_msg · assistant_msg |
| /api/v1/memory/bulk-store | importer un bloc de texte | user_id · content · category? |
| /api/v1/memory/search | recherche sémantique | user_id · query · max_results? |
| /api/v1/memory/recall | court + long + contexte | user_id · query |
| /api/v1/memory/history | tours récents | user_id |
| /api/v1/memory/stats | compteurs de souvenirs | user_id |
| /api/v1/memory/supersede | remplacer un fait modifié | user_id · old_memory_id · new_content |
| /api/v1/memory/forget | en supprimer un (ou tous) | user_id · memory_id? (omis = tout supprimer) |
# create the store once (stores are explicit) curl -X POST https://api.wontopos.com/api/v1/memory/collection \ -H "X-API-Key: $WOS_API_KEY" -H "Content-Type: application/json" \ -d '{"user_id":"alice"}' # store a memory curl -X POST https://api.wontopos.com/api/v1/memory/store \ -H "X-API-Key: $WOS_API_KEY" -H "Content-Type: application/json" \ -d '{"user_id":"alice","content":"she prefers tea over coffee"}' # recall - one call, ready for your prompt curl -X POST https://api.wontopos.com/api/v1/memory/recall \ -H "X-API-Key: $WOS_API_KEY" -H "Content-Type: application/json" \ -d '{"user_id":"alice","query":"what does alice drink?"}'
{"id": "576700aa-f0e0-4c26-99a0-10e2d5b0d624", "status": "stored (1 chunks)"}Les mêmes fonctionnalités pour tous.
Les paliers ne font que relever vos limites.
Chaque palier exécute le moteur complet - même qualité de rappel, mêmes langues, chaque méthode. Les paliers progressent automatiquement jusqu'au Tier 5 à mesure que vos achats de crédits cumulés augmentent, sans dossier à déposer ni rendez-vous commercial. Enterprise (Tier 6) est la seule exception.
Limites de dépense
Chaque palier plafonne ce que vous pouvez dépenser par mois calendaire. Vous passez immédiatement au palier suivant dès que vos achats de crédits cumulés atteignent le seuil correspondant.
| Palier d'utilisation | Achat de crédits | Limite de dépense mensuelle |
|---|---|---|
| Tier 1 | $5 | $100 |
| Tier 2 | $40 | $500 |
| Tier 3 | $200 | $1,000 |
| Tier 4 | $400 | $5,000 |
| Tier 5 | $1,000 | $25,000 |
| Tier 6 - Enterprise | Contactez-nous | Sans limite |
Limites de débit
Les limites de débit sont par compte - toutes les clés API d'un compte partagent une même limite, qui augmente avec votre palier. La dépasser renvoie un 429 avec un en-tête retry-after ; patientez (1s → 2s → 4s) puis réessayez. Chaque endpoint est compatible avec l'idempotence, les nouvelles tentatives sont donc sans risque.
| Palier | Requêtes par minute |
|---|---|
| Tier 1 | 150 |
| Tier 2 | 300 |
| Tier 3 | 600 |
| Tier 4 | 1,500 |
| Tier 5 | 3,000 |
| Tier 6 - Enterprise | Sur mesure |
Enterprise (Tier 6) bénéficie de limites de débit sur mesure, d'un SLA, d'un support dédié et d'une licence d'auto-hébergement en option - contactez-nous.
Quand quelque chose tourne mal.
Les erreurs reviennent sous forme d'enveloppe JSON avec un type stable, un message lisible et un request_id que vous pouvez nous transmettre pour signaler un problème.
{"type": "error", "error": {
"type": "authentication_error",
"message": "Invalid or revoked API key.",
"request_id": "063f8b83-eee2-4383-a5cf-11e4bcd29d7c"
}}| HTTP | Signification | Que faire |
|---|---|---|
| 401 | Clé API invalide ou révoquée | Vérifiez la clé ; émettez-en une nouvelle dans la console. |
| 422 | Corps malformé (champ manquant ou de mauvais type) | Le message nomme le champ exact - corrigez et réessayez. |
| 429 | Limite de débit atteinte | Patientez de façon exponentielle (1s → 2s → 4s) puis réessayez. Sans risque : tous les endpoints sont compatibles avec l'idempotence. |
| 5xx | Problème côté serveur | Réessayez avec backoff ; joignez le request_id si vous nous contactez. |
# SDK error handling (Python) from wontopos import Client, WosError try: mem.search("...", user_id="alice") except WosError as e: if e.status == 401: ... # bad key elif e.status == 429: ... # back off and retry
Les limites de débit sont par compte, partagées entre toutes vos clés, et augmentent avec votre palier - voir Paliers d'utilisation. L'usage de votre compte est visible dans la console.
Vos serveurs, vos données.
Le moteur peut tourner dans votre propre infrastructure - même API, mêmes SDK. Pointez le client vers votre hôte et rien d'autre ne change.
mem = Client(api_key="...", base_url="https://wos.your-host.com")- Résidence des données. Les souvenirs ne quittent jamais votre réseau.
- Même surface. Les mêmes méthodes et endpoints fonctionnent à l'identique.
- Licence. Les offres d'auto-hébergement sont définies par déploiement - contactez-nous.
Au-delà de la fenêtre de contexte.
WOS rappelle depuis des historiques de 1.4M tokens - bien au-delà de toute fenêtre de contexte de LLM - et rend toujours une tranche serrée d'environ ~1,470 tokens.
La mémoire de votre agent n'est pas plafonnée par ce qui tient dans un prompt. Elle conserve tout et ne récupère que ce qui compte, quelle que soit la taille de l'historique.
Privé, et à vous.
Vos données restent dans votre store. Nous ne nous entraînons jamais dessus, ne les consultons pas et ne les réutilisons pas - nous ne faisons que les organiser pour que vous puissiez les retrouver.
- BYOK. Votre clé LLM est envoyée à chaque requête et jamais stockée.
- Isolé. Les souvenirs sont cloisonnés par compte, puis par
user_id. - Suppression RGPD & auto-hébergement. Un seul appel efface un utilisateur ; exécutez le moteur dans votre propre environnement si vous préférez.