Lesson 3 · Agentic generation for the Teaching Assistant

When the agent owns memory: harness, session, memory

New assumptions: the agent may write memory, and Postgres keeps only what is needed to run the agents. This lesson shows what still has to stay in Postgres, where a Thread lives now, and where Google's Memory Bank fits. About 15 minutes.

Warm-up: from memory, why did Lesson 1 recommend one agent session per job?

Generators never wrote memory, Postgres was the only source of truth, and no platform could reliably keep a session through an overnight Reaction. The first two premises are now withdrawn, and the third turned out to be wrong for ADK (see below). So the rule is open again.

What changed

Three layers of state

LayerHoldsWho reads and writes itLives in
HarnessAnything your code must know before or without asking a modelYour code, deterministicallyPostgres (thin)
SessionOne Thread's transcript: Lessons, Replies, Reactions sent as events, tool callsThe agent runtimeADK session table in Supabase, a Claude Managed Agents session, or a Gemini interaction chain
Long-term memoryWhat lasts across Threads: what was hard, preferences (Notes), Topic recapsThe agent (and, optionally, your code)Memory Bank, Claude Managed Agents memory stores, or your own memory tool

The harness test. Keep a fact in Postgres if your code needs it deterministically: to route a Telegram update, draw the right buttons, deduplicate, schedule, cap spend, or choose which Thread to start. A model's memory is probabilistic and eventually consistent. Button rows and idempotency can't depend on it.

For this bot the harness is roughly: Telegram message_id ↔ Thread/session and message kind (so a reply or a button tap finds its target), the Topic's latest Lesson (only its Simple/Proceed/Difficult taps take effect; a tap on an older Lesson gets a toast), update_id deduplication, jobs and runRefs, the published Backlog copy, which Topic is in progress (and whether it is finished or paused), and the usage and spend log. Everything else is a candidate to move.

A Thread can now be one session

/next → create session→ Lesson→ tap / Reply = new event→ idle for hours, nothing running→ tap = new event→ /next → newer Thread; this session stays resumable

The Thread is the session. A Reaction or Reply is appended as a new event and runs one turn, and nothing runs in between. The overnight gap is harmless because the session is stored, not held in a running process. Threads are never closed (ticket 12, 2026-09-17): older Threads still accept Replies, so a session must stay resumable indefinitely, and there is no "close" moment for writing memory. Write memories incrementally instead: after each turn from that turn's events (the quickstart recommends add_events_to_memory for incremental processing), or when a newer Thread starts. Two Lessons may be generated at once, but they belong to different sessions, and the Topic level stays a harness rule, so concurrent turns don't conflict.

ADK 2.1 (TS) on Cloud RunClaude Managed AgentsGemini Antigravity
Thread → sessionDatabaseSessionService on Supabase Postgres; each event is one runner callOne session per Thread; it sits idle between events (runtime is billed on active time). How long an idle session stays resumable is unverified[2]A previous_interaction_id chain plus environment_id; environments are kept 7 days after last activity[3], so a Reply to an older Thread may need a fresh interaction seeded from memory
Long-term memoryVertexAiMemoryBankService, or your own toolMemory stores: files the agent reads and writes, versioned and redactable[4]No memory service documented; you could call Memory Bank over REST
Keeps OpenRouterFor generation, yes. Memory Bank extraction runs on GeminiNoNo

One cost to watch with session-per-Thread: the context grows with each turn, and every turn re-sends it. That is fine for a Thread of a few Lessons. Keep Threads short, and let /next start fresh.

Your question: is Memory Bank relevant?

Yes. It is the managed form of the "agent writes memory" layer. What it does, per Google's docs:[5]

How it maps onto your project

Today (ticket 07)With Memory Bank
Append-only raw events kept foreverSession events, plus memory revisions as the audit trail
Topic recap written on finish, pause or retireIncremental add_events_to_memory after each turn, with a custom "difficulties / level" topic
Notes with Topic or all-Topics scopeDirect CreateMemory with a Note topic; scope carried in the memory
Fixed context for each call (last 30 Lesson lines…)Similarity retrieval chosen by the agent
psql readback for Strategy SessionsLost. The toolkit would read memories through the API instead

What it costs you

Practice: which layer?

1. The Telegram message_id of the Topic's latest Lesson, so a Simple/Proceed/Difficult tap is either applied or answered with a toast.

2. The full text of the Thread's three Lessons and the learner's Instruction, needed for the next Follow-up.

3. "Struggles with idempotency; an analogy to insurance claims numbers helped", learned across several Threads.

4. Which Topic is in progress, so the 07:00 run knows what to start.

5. A pending Lesson generation's runRef and attempt count.

Take back to your tickets. (1) This reverses a decision in ticket 07 and is hard to undo once memory lives in a vendor store, so it deserves an ADR: "agents own session and memory; Postgres is the harness". (2) Ticket 10 (data model) shrinks to the harness tables. (3) Ticket 08 needs a new readback path, since Strategy Sessions would read Memory Bank or memory stores, not psql views. (4) The map's OpenRouter-only preference needs an exception for memory extraction if you pick Memory Bank.

Read next (primary source): Agent Platform Memory Bank overview, then the ADK quickstart you found. For the Anthropic equivalent, read the memory-stores section of the Managed Agents docs.

Unsure where some piece of state belongs? Ask me, your teacher, with the concrete item, and we'll run the harness test on it together.