Memory & Persistence

When you use LLMs directly they all have a different approach to memory and chat persistence.

Here we standardize on a simple approach

  • By default a session always retains its history/context
  • To disable that behaviour, set a new session to incognito mode
  • Memory is stored at an app + user id level

Let's go over a few examples

Authenticated User

Open a session with a user_id and tell the agent your shoe size. A week later, in a new session, it still knows.

agent, err := agents.New(agents.Options{
	Name:         "support",
	MemoryFilter: map[string]string{"user_id": "user-123"},
})
if err != nil {
	return err
}

monday, err := agent.Chat(ctx)
if err != nil {
	return err
}
if _, err := ask(ctx, monday, "I wear a size 10 shoe."); err != nil {
	return err
}
if err := monday.Close(ctx); err != nil {
	return err
}

friday, err := agent.Chat(ctx)
if err != nil {
	return err
}
answer, err := ask(ctx, friday, "What size should I order in these running shoes?")
if err != nil {
	return err
}
fmt.Println(answer) // Size 10, going by what you told me before.

func ask(ctx context.Context, session *agents.Session, text string) (string, error) {
	if _, err := session.Responses.Create(ctx, text); err != nil {
		return "", err
	}
	var answer strings.Builder
	for event := range session.Events() {
		switch event.Kind {
		case "response_delta":
			answer.WriteString(event.Text)
		case "responded":
			return answer.String(), nil
		}
	}
	return answer.String(), nil
}

What the agent learns is saved as the conversation goes, and recalled when a session opens. Every memory is kept under your app and the user_id, so one person's memories never reach another. In a browser, as on a phone, pass memory: {}: it remembers for the user setUser named, since a user's device can't name anyone else. A backend names whichever of its users it means, or the one it acts for when it names none.

Incognito

An incognito session saves nothing. Tell it your shoe size and the next session has no idea, and the conversation itself is not stored anywhere.

memory := map[string]string{"user_id": "user-456"}

secret, err := agent.Chat(ctx, agents.SessionOptions{MemoryFilter: memory, Incognito: true})
if err != nil {
	return err
}
if _, err := ask(ctx, secret, "I wear a size 10 shoe."); err != nil {
	return err
}
if err := secret.Close(ctx); err != nil {
	return err
}

later, err := agent.Chat(ctx, agents.SessionOptions{MemoryFilter: memory})
if err != nil {
	return err
}
answer, err := ask(ctx, later, "What size should I order in these running shoes?")
if err != nil {
	return err
}
fmt.Println(answer) // I don't know your shoe size yet. What size do you wear?

api, err := client.New(stream.Backend{})
if err != nil {
	return err
}
_, err = api.Agent("support").Sessions.Get(ctx, secret.ID()) // not found

An incognito session has no transcript, so it can't be listed, searched, forked or rewound. It still recalls what earlier sessions learned about the person; it just adds nothing.

Forgetting

Ending a session keeps what it learned. To forget one session, delete its memories. To forget a person, truncate their memories: everything learned about them, in every session and by every agent, is deleted.

api, err := client.New(stream.Backend{})
if err != nil {
	return err
}
if err := api.Agent("support").Sessions.DeleteMemories(ctx, sessionID); err != nil {
	return err
}
if err := api.Memories().Truncate(ctx, "user-123"); err != nil {
	return err
}