Saltar al contenido
astorlm
Idioma: Español
← Mapa

Nivel 12

Sesiones

El historial de un agente vive en memoria, y la memoria termina con el programa. Una sesión lo va escribiendo, así la conversación sobrevive al proceso: puedes retomarla mañana, o bifurcarla.
1/34 Pliegues del bandoneón:
  • user
  • assistant
  • tool_result
El héroe necesita una forma de entrar a la Cueva de los Ecos. Astor trabaja con un registro de aventura: un archivo de sesión en disco donde cada mensaje se anota apenas existe.

EventBus

El problema

El historial del nivel 2 es una lista en memoria. Cuando el programa se detiene (un deploy, una caída, alguien que cierra la pestaña y vuelve mañana), la lista desaparece y el agente empieza de cero, preguntando otra vez todo lo que ya sabía.

Guardarla solo al final tampoco alcanza: una ejecución que falla a mitad de camino es justo la que querrías mirar, o retomar desde donde se cortó.

La solución

Dale a cada conversación un id y un lugar en disco, y trata el historial como ese archivo, no como una variable. Tres movimientos lo cubren:

  • Guardar sobre la marcha

    Escribe cada mensaje en el archivo de la sesión apenas existe, una línea por mensaje. Si algo se cae a mitad de camino, no se pierde nada.

  • Retomar

    Un agente nuevo con el mismo id de sesión vuelve a leer el archivo en su historial antes del próximo pedido.

  • Bifurcar

    Copia el historial en una sesión nueva para probar otra cosa. La original queda como estaba.

JSONL, un mensaje JSON por línea, es el formato habitual: agregar un mensaje es agregar una línea, y una última línea escrita a medias es fácil de detectar. Claude Code y Codex guardan sus propias sesiones de la misma forma, y así es como las retoman.

El elenco

El mismo elenco de siempre, en una aventura.

El registro de aventura el archivo de sesión
Una ranura por id de sesión, una marca por mensaje, escrita apenas existe.
El bandoneón el historial
Lo que el agente tiene en memoria. Se vacía cuando se apaga el juego; el registro no.
CONTINUE retomar
Un agente nuevo con el mismo id, con el registro vuelto a leer en su historial.
COPY A QUEST bifurcar
Una segunda ranura que empieza como copia de la primera. Después, cada una crece por su cuenta.
Astor el bucle
Corre el bucle como siempre, y cada mensaje va al registro.
El pueblo las herramientas
El anciano, el guardia y la tienda: talk_to y buy.

El código

Con astorlm: pasa un sessionId y un FileSessionManager. El agente carga esa sesión cuando se crea y la guarda después de cada mensaje; un id nuevo empieza una vacía. fork() devuelve un agente nuevo sobre una copia de la sesión.

Desde cero: el bucle del nivel 2, con su historial leído primero de un archivo JSONL y cada mensaje nuevo agregado al final. Bifurcar es copiar el archivo.

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?')

Qué vigilar

  • Una sesión retomada vuelve a mandar todo. El día 30 de una conversación larga lleva encima los 29 anteriores. Las sesiones conservan el historial; compactarlo (nivel 9) es lo que evita que supere la ventana de contexto.
  • Bifurcar copia el historial, no el mundo. La antorcha se compró una vez, y está en las dos ranuras. Los emails enviados, los pedidos hechos y los archivos escritos por las herramientas pasaron de verdad, en todas las ramas.
  • Revisa la última línea al retomar. Si el programa murió entre una llamada a una herramienta y su resultado, el registro termina con una llamada que nadie respondió, y la mayoría de los proveedores la rechaza. Descártala, o agrégale un resultado de error.
  • Un solo escritor por sesión. Dos procesos agregando líneas al mismo archivo a la vez las mezclan. Dale a cada ejecución su propia sesión, o un candado.
  • Los archivos de sesión son datos personales. Guardan todo lo que dijo el usuario y cada resultado de herramienta. Decide dónde viven, quién puede leerlos y cuándo se borran.