Aller au contenu
astorlm
Langue: Français
← Carte

Niveau 12

Sessions

L'historique d'un agent vit en mémoire, et la mémoire s'arrête avec le programme. Une session le note au fur et à mesure, si bien que la conversation survit au processus : vous pouvez la reprendre demain, ou la faire bifurquer.
1/34 Plis du bandonéon :
  • user
  • assistant
  • tool_result
Le héros cherche un moyen d'entrer dans la Grotte des Échos. Astor travaille avec un journal d'aventure : un fichier de session sur disque où chaque message est noté dès qu'il existe.

EventBus

Le problème

L'historique du niveau 2 est une liste en mémoire. Quand le programme s'arrête (un déploiement, un plantage, quelqu'un qui ferme l'onglet et revient demain), la liste disparaît et l'agent repart de zéro, en redemandant tout ce qu'il savait déjà.

L'enregistrer seulement à la fin ne suffit pas non plus : une exécution qui échoue en route est justement celle qu'on voudrait examiner, ou reprendre là où elle s'est arrêtée.

La solution

Donnez à chaque conversation un id et une place sur le disque, et traitez l'historique comme ce fichier, pas comme une variable. Trois gestes suffisent :

  • Enregistrer au fil de l'eau

    Écrivez chaque message dans le fichier de session dès qu'il existe, une ligne par message. Si tout s'arrête en route, rien n'est perdu.

  • Reprendre

    Un nouvel agent sur le même id de session relit le fichier dans son historique avant la requête suivante.

  • Bifurquer

    Copiez l'historique dans une nouvelle session pour tenter autre chose. L'originale reste telle quelle.

Le JSONL, un message JSON par ligne, est le format habituel : ajouter un message, c'est ajouter une ligne, et une dernière ligne écrite à moitié se repère facilement. Claude Code et Codex gardent leurs propres sessions de la même façon, et c'est ainsi qu'ils les reprennent.

Les personnages

Les mêmes personnages que d'habitude, en pleine quête.

Le journal d'aventure le fichier de session
Un emplacement par id de session, une marque par message, écrite dès qu'il existe.
Le bandonéon l'historique
Ce que l'agent a en mémoire. Il se vide quand on éteint le jeu ; le journal, non.
CONTINUE reprendre
Un nouvel agent sur le même id, avec le journal relu dans son historique.
COPY A QUEST bifurquer
Un deuxième emplacement qui démarre comme une copie du premier. Ensuite, chacun grandit de son côté.
Astor la boucle
Fait tourner la boucle comme d'habitude, et chaque message part dans le journal.
Le village les outils
L'ancien, le garde et la boutique : talk_to et buy.

Le code

Avec astorlm : passez un sessionId et un FileSessionManager. L'agent charge cette session à sa création et l'enregistre après chaque message ; un nouvel id en démarre une vide. fork() renvoie un nouvel agent sur une copie de la session.

À partir de zéro : la boucle du niveau 2, avec son historique lu d'abord dans un fichier JSONL et chaque nouveau message ajouté à la fin. Bifurquer, c'est copier le fichier.

import { FileSessionManager, OpenAIProvider, createLocalAgent, tool } from 'astorlm'
import { z } from 'zod'

const talkTo = tool({
  name: 'talk_to',
  description: 'Talk to someone in town. Returns what they say.',
  schema: z.object({ npc: z.string() }),
  execute: async ({ npc }) => town.talk(npc), // your code
})

const buy = tool({
  name: 'buy',
  description: 'Buy an item at the shop. Returns the price and the gold left.',
  schema: z.object({ item: z.string() }),
  execute: async ({ item }) => shop.buy(item), // your code
})

// The adventure log: quest-1.jsonl (one message per line) and quest-1.meta.json, in this folder.
const sessionManager = new FileSessionManager({ dir: './sessions' })

const quest = (sessionId: string) =>
  createLocalAgent({
    // Any OpenAI-compatible endpoint: OpenAI, Ollama, LM Studio, vLLM, a proxy…
    provider: new OpenAIProvider({
      baseURL: 'http://localhost:11434/v1', // e.g. Ollama's default address
      model: 'your-model', // e.g. 'llama3.1', 'gpt-4o-mini'
      apiKey: 'YOUR_API_KEY', // local servers usually ignore it
    }),
    tools: [talkTo, buy],
    sessionId, // a new id starts an empty log; a known one loads it
    sessionManager,
  })

// Day 1. Every message is saved as soon as it's added, so a crash loses nothing.
const day1 = await quest('quest-1')
await day1.run('I need to get into the Cave of Echoes. Find out what it takes, and get what you can.')

// …the process ends. Day 2: a new agent on the same id reads the log back before answering.
const day2 = await quest('quest-1')
console.log(day2.getMessages().length) // 6: yesterday is in the history
await day2.run('I got the Silver Key from the mayor’s daughter. What now?')

// A fork: a new session with a copy of every message. quest-1 stays as it was.
const west = await day2.fork({ newSessionId: 'quest-2' })
await west.run('Suppose the key doesn’t fit. Is there another way in?')

Points de vigilance

  • Une session reprise renvoie tout. Le jour 30 d'une longue conversation porte les 29 précédents. Les sessions gardent l'historique ; c'est la compaction (niveau 9) qui l'empêche de dépasser la fenêtre de contexte.
  • Bifurquer copie l'historique, pas le monde. La torche a été achetée une fois, et elle est dans les deux emplacements. Les emails envoyés, les commandes passées et les fichiers écrits par les outils ont eu lieu pour de vrai, dans toutes les branches.
  • Vérifiez la dernière ligne à la reprise. Si le programme est mort entre un appel d'outil et son résultat, le journal se termine par un appel resté sans réponse, et la plupart des fournisseurs le refusent. Supprimez-le, ou ajoutez-lui un résultat d'erreur.
  • Un seul écrivain par session. Deux processus qui ajoutent des lignes au même fichier en même temps les mélangent. Donnez à chaque exécution sa propre session, ou un verrou.
  • Les fichiers de session sont des données personnelles. Ils contiennent tout ce que l'utilisateur a dit et chaque résultat d'outil. Décidez où ils vivent, qui peut les lire et quand ils sont supprimés.