Aller au contenu

Mémoire et connaissances

Sans mémoire, chaque conversation avec un agent repart de zéro : il faut réexpliquer la même convention, redébattre la même décision, réexplorer la même impasse. La mémoire est le magasin à portée de l’espace de travail qui survit aux exécutions, et elle existe pour que les connaissances produites par un agent puissent être trouvées plus tard par un autre.

Elle comporte trois couches, qui répondent à des questions différentes :

  1. Faits — ce qui est vrai (une convention, un résultat, une décision).
  2. Politiques — ce que les agents doivent en faire (une règle normative).
  3. Mémoire de travail — ce qu’un agent était en train de faire.

La distinction importe parce que ces couches atteignent l’agent par des chemins différents. Les faits sont recherchés. Les politiques sont poussées. La mémoire de travail est privée.

Un MemoryFact est une unité de connaissance à portée d’un espace de travail et d’un domaine.

Attribut Ce dont il s’agit
domain Le domaine de connaissances (« conventions », « decisions », « codebase »)
topic Une courte étiquette de sujet
content Le texte de la connaissance
confidence 0,0–1,0, initialisée à partir de la provenance et augmentée à chaque nouvelle mention
memoryType Le genre du fait — détermine sa courbe de décroissance temporelle
veracity Comment il a été connu — initialise la confiance et dimensionne chaque gain de nouvelle mention
sourceObservationIds Les observations qui y ont mené
supersededBy Le fait plus récent qui l’a remplacé, s’il y en a un
validUntil, recallCount, mentionCount Signaux d’expiration et d’usage utilisés au rappel

Les deux dernières lignes sont celles qu’on oublie, et ce sont elles qui décident quel fait remonte à la surface. memoryType porte des paramètres de décroissance de Weibull par type : un engagement à durée déterminée s’estompe rapidement, tandis qu’un fait de profil stable ne le fait pas. veracity consigne la provenance — énoncé par un humain (poids 1,0), déduit par un agent (0,7), produit par un outil (0,5), importé (0,6) — et ce poids initialise la confiance de départ du fait.

Un fait n’est jamais modifié sur place lorsqu’il est contredit. L’écriture d’un fait déclenche une détection de conflits contre les faits du même domaine et du même sujet, puis supplante le perdant et consigne une ligne de conflit. La piste de vérification est le but : vous pouvez toujours voir ce qui était tenu pour vrai avant et ce qui l’a remplacé.

Une MemoryPolicy est une règle normative dérivée de faits, à portée d’un espace de travail et d’un domaine, avec un indicateur d’activation et un requiredRole facultatif.

Chaque politique active de l’espace de travail est injectée dans chaque lancement, sans filtrage — aucun filtrage par domaine, rôle, tâche ou espace. Cela vaut la peine de le savoir avant d’écrire votre vingtième politique, car les politiques se disputent la même fenêtre de contexte que la tâche. C’est aussi pourquoi le requiredRole d’une politique est indicatif : il décrit à qui la règle s’adresse, mais il n’empêche pas la règle d’atteindre tous les autres.

Un MemoryDomain regroupe les faits et les politiques d’un espace de travail. Les domaines donnent aux connaissances une structure organisationnelle et constituent l’unité sur laquelle les autorisations d’accès sont écrites — mais voyez la mise en garde ci-dessous avant de traiter un domaine comme une frontière de confidentialité.

Un domaine est soit à l’échelle de l’espace de travail (architecture), soit à portée d’un dépôt (repo:<slug>/architecture). Un même nom dans les deux portées désigne deux magasins indépendants : un fait qui est vrai pour une base de code n’écrase pas celui à l’échelle de l’espace de travail. search_memory et le préambule de lancement classent en premier la mémoire à portée de dépôt lorsque l’agent travaille dans ce dépôt ; ils n’écartent pas le reste pour autant. Passez repo à propose_fact / propose_policy / search_memory pour écrire ou renforcer cette portée.

Le contrôle d’accès est fail-open pour les lectures

Section intitulée “Le contrôle d’accès est fail-open pour les lectures”

MemoryAccessGrant fait correspondre un rôle d’agent et un domaine à l’un de trois niveaux de permission :

Permission Signification
none Aucun accès
read Peut rechercher et récupérer des faits
write Requis par propose_policy. Les écritures de faits (propose_fact, supersede_fact) ne sont pas soumises à autorisation

L’asymétrie — souple sur les lectures, stricte sur les écritures de politiques — est un compromis délibéré. La mémoire est plus utile lorsqu’elle circule, et un refus de lecture produit un agent qui en sait silencieusement moins plutôt qu’un agent qui signale un problème. Un refus d’écriture de politique, en revanche, est bruyant et corrigeable ; c’est donc là que se trouve l’application des règles. Un domaine nouvellement créé initialise write pour chaque rôle ; le resserrer par la suite est une modification réservée aux RPC, puisqu’il n’existe aucune interface pour les autorisations.

Chaque agent dispose d’un AgentWorkingMemory : un bloc-notes en texte libre indexé par espace de travail et par agent. Il persiste d’une exécution à l’autre, est privé à cet agent et est injecté dans son prompt à chaque lancement. C’est là que vit « j’étais à mi-chemin du refactoring de l’authentification » — l’état qu’il vaut la peine de transporter vers l’exécution suivante, mais qui ne mérite pas d’être promu en fait durable.

Ce qui aboutit réellement dans le prompt d’un agent

Section intitulée “Ce qui aboutit réellement dans le prompt d’un agent”

Au lancement, le préambule de mémoire est assemblé à partir de exactement trois éléments :

  • Chaque politique active de l’espace de travail, sous forme de lignes - [domain] rule.
  • La mémoire de travail de l’agent lui-même, mot pour mot.
  • Une liste restreinte de faits correspondant aux mots-clés pour cette tâche — au plus cinq, dans un budget d’environ 1 200 caractères, et uniquement lorsque la description de la tâche compte au moins 20 caractères. Les tâches plus courtes n’obtiennent aucun fait.

La liste restreinte repose uniquement sur FTS et les mots-clés, et elle est plafonnée à dessein. Calculer l’embedding d’une requête est lent, et le chemin de lancement est le seul endroit où la latence est immédiatement visible ; le chemin sémantique lent en est donc délibérément tenu à l’écart. Le préambule se termine en demandant à l’agent de vérifier les éléments porteurs avec search_memory, là où réside le modèle de rappel complet.

Aucune autorisation d’accès n’est consultée sur ce chemin. Ce que la liste restreinte trouve est ce que l’agent voit.

L’outil search_memory destiné aux agents comporte trois modes, qui sont de véritables recherches différentes :

  • keyword — FTS5 uniquement.
  • semantic — résultats BM25 et vectoriels fusionnés par fusion de rangs réciproques.
  • hybrid (le mode par défaut) — rappel polyphonique : quatre voix classées fusionnées, puis diversifiées.

Les quatre voix sont lexicale (FTS), sémantique (KNN vectoriel), temporelle (faits actifs récents) et de graphe — une marche de deux sauts sur les arêtes épisodiques à partir des graines lexicales et sémantiques les plus fortes. La voix de graphe est la raison d’être des arêtes épisodiques : elle fait remonter le fait qui est relié à votre réponse sans correspondre à aucun de vos mots. Les voix sont pondérées par une intention de requête classifiée (la sémantique pèse le plus lourd par défaut, puis le graphe et le lexical, puis la récence), le score de chaque candidat est mis à l’échelle par sa décroissance de Weibull, et l’ensemble de résultats est diversifié par MMR pour que cinq reformulations d’un même fait n’évincent pas quatre faits différents.

La recherche de mémoire sur le bureau et dans le Web est un chemin distinct et plus simple : l’opération RPC memory_fact.search repose uniquement sur FTS5, parce qu’un client léger ne peut pas livrer d’embedding de requête et que le chemin hybride reste interne à l’hôte. Le classement que vous voyez dans l’explorateur de mémoire n’est donc pas celui qu’un agent obtient de search_memory. C’est normal, pas un bogue, mais cela signifie tout de même que « j’ai cherché et je ne l’ai pas trouvé » dans l’interface n’est pas une preuve que l’agent ne le trouvera pas.

L’explorateur de mémoire se trouve à Paramètres → Espace de travail → Mémoire (/workspaces/<id>/settings/memory), avec des onglets Faits, Politiques et Graphe de connaissances.

Il y a là une asymétrie qui surprend les gens : vous pouvez modifier et supprimer la mémoire, mais vous ne pouvez pas en créer. Ni l’onglet Faits ni l’onglet Politiques n’offre de commande d’ajout. La rédaction se fait côté agent, au moyen de propose_fact et propose_policy (supersede_fact / supersede_policy pour les remplacer). remember écrit un élément de palier chaud limité à la session, que consolidate_memory pourra plus tard promouvoir en fait ; record_observation et update_my_notes écrivent le bloc-notes privé injecté au lancement. L’explorateur est une surface de révision et de correction, pas de rédaction — donc si vous voulez un fait en mémoire, le moyen de l’y mettre est de le dire à un agent.

Deux opérations de maintenance sont elles aussi invoquées par un agent plutôt que planifiées : consolidate_memory fusionne et élague les faits accumulés, et harmonize_memory réconcilie les croyances qui ont divergé entre les agents. list_memory_conflicts montre ce que la détection de conflits a déjà consigné. Rien ne les exécute pour vous selon un minuteur. Les décisions de réunion sont recueillies dans le domaine decisions lorsqu’une étape de persistance de réunion les consigne ; il ne s’agit pas d’un minuteur sur les exécutions d’agents terminées.