04 · Memory
A custom ContextProvider gives the agent memory: it reads stored facts before a run and learns new ones after — all through the Session.
What this lesson demonstrates
Sessions carry the transcript; memory carries facts. A ContextProvider adds two hooks around every run so the agent can carry structured memory forward. Provide runs before the model call and injects what it knows as instructions (“The user’s name is Ruaidhrí.”). Store runs after a successful run, scans the user’s message, parses out a name and age, and writes them back into the Session. Because the state lives in the agent.Session, it serializes to JSON and can be copied into a brand-new session — so the agent “remembers” a user it never spoke to in that session.
The code
A provider is just two functions registered under one source ID:
func newUserMemoryProvider() agent.ContextProvider {
return agent.NewContextProvider(agent.ContextProviderConfig{
SourceID: userMemorySourceID,
Provide: provideUserMemory,
Store: storeUserMemory,
})
}
Provide returns no messages — only an instructions option, the additive contract of a provider:
return nil, []agent.Option{agent.WithInstructions(instructions.String())}, nil
What to notice
- Memory is additive, not magic.
Providereturnsnilmessages plus oneagent.WithInstructions(...). The SDK appends it to the run — it never rewrites the message you sent.Storeonly fills gaps; it won’t overwrite a name or age it already knows. - The Session is the store.
session.Set(sourceID, state)andsession.Get(sourceID, &state)are the entire persistence layer. Because the session marshals to JSON, memory survives a save/restore round-trip, andnewSession.Set(...)seeds a fresh session with saved facts. - The parsing is pure.
extractNameandextractAgetake strings and return values — no model, no session. That purity is exactly what makes memory testable offline, and the gotcha they guard against: “I am 20 minutes late” must not register an age (only “20 years” does).
How it maps to Azure AI Foundry
The provider runs entirely client-side around the Foundry call. Before the request, Provide folds remembered facts into the instructions Foundry receives; after the response, Store mines the turn for new facts. Foundry itself stays stateless — the framework’s ContextProvider lifecycle is what layers durable, structured memory on top of the model.
Run it
go run ./tutorial/01-get-started/04_memory
Expected: the agent refuses and asks for your name/age, then — once told — recalls both, including across a deserialized session and a fresh session seeded with the saved memory. Offline tests cover wiring, the pure parsers, and a real Store→Provide round-trip through an *agent.Session — no network. TestMemory_Live is gated behind AF_LIVE=1.
Key takeaways
- Sessions carry the transcript; a ContextProvider carries facts.
Provideruns before each model call and injects known facts as instructions;Storeruns after a successful run and mines the turn for new ones. - Memory is additive, not magic.
Providereturnsnilmessages plus oneagent.WithInstructions(...)— the SDK appends it, never rewriting your message — andStoreonly fills gaps, never overwriting a name or age it already has. - The Session is the store.
session.Set/Getunder aSourceIDis the whole persistence layer; because a session marshals to JSON, memory survives a save/restore round-trip and can seed a brand-new session. - Keep the parsers pure.
extractName/extractAgetake strings and return values with no model or session, which is what makes memory testable offline — and lets them guard gotchas like “20 minutes late” not registering an age.
Further reading
- step22 · Foundry Memory — the productionized version, backed by an Azure AI Foundry memory store that persists across sessions.
- step07 · Third-Party Session Storage — a history provider that owns message bodies in your own store.
- step18 · Compaction Pipeline — a context provider that rewrites history rather than appending to it.
Next: 05 · First Workflow