Module 6 of 12 · Open Source Track
Conversation Management

The API forgets everything between calls — so you carry the memory. Five concepts: the stateless reality, history management patterns, token budgets, message pruning, and a production conversation manager.

Track 3: Memory & Context ⏱ ~16 min read 6 / 12
Concept 1 of 5

The model forgets between every call

Each API call to the model (Mistral via Ollama) is stateless: there is no session, no server-side storage, no memory of any kind. What feels like a continuous chat is an illusion your code creates.

To give the model “memory,” you resend the entire message history — system prompt plus every prior user and assistant turn — on every single request. Leave a message out and, to the model, it never happened.

Concept 1 of 5

The expert with perfect amnesia

BEFORE: Imagine hiring a world-class expert for advice, but every time you walk into the room they have zero memory of any previous conversation with you.

PAIN: Say “so what about option B?” and they stare blankly — no idea what option A was, who you are, or what problem you're solving. Every interaction starts from absolute zero.

MAPPING: That's exactly how the API works. Each call is a fresh room with a fresh expert. The only way to give “memory” is to hand over a written transcript of every prior exchange — your code replays the whole conversation from scratch each time.

Concept 1 of 5

What actually goes over the wire

  1. Build the full arraysystem message + all prior user and assistant turns, in order.
  2. Send everythingThe whole array goes in the request — nothing is stored server-side.
  3. Get one replyThe model reads it all fresh and returns the next assistant message.
  4. Append & repeatAdd the reply to your array; next turn resends the whole (bigger) thing.
system (re-sent)
msg 1 (re-sent)
reply 1 (re-sent)
msg 2 (re-sent)
reply 2 (re-sent)
msg 3 (NEW)
Concept 1 of 5

Pseudocode

history = [ system_message ]

FUNCTION send(user_text):
  history.append({ user: user_text })

  # send the WHOLE array — no memory
  # exists on the server side
  reply = api.chat(messages = history)

  history.append({ assistant: reply })
  RETURN reply

# 'history' IS the memory. It lives in
# your code and grows every single turn.
Concept 1 of 5

Misconceptions

"The model remembers our previous conversation."
No. Each call is fully stateless — no session, no server storage. If a message isn't in the array you send, it doesn't exist as far as the model is concerned.
"The context window is memory."
No. The context window is a whiteboard erased after every request. Memory is what your code persists and replays across calls.

The API is stateless — you resend the full conversation on every call. “Memory” is your growing messages array, not the model's.

This is a superpower: you control the whole context, so you can edit, branch, and inject facts at will.

Concept 2 of 5

Three ways to carry the history

Because you resend history every turn, you need a strategy for what to send. Three patterns exist: full history (send everything), sliding window (keep only the last N messages), and summarization (compress old turns into a short summary, then drop the originals).

Each trades off differently. Full history is simplest but grows unbounded. A window is cheap but forgets early context. Summarization preserves meaning at bounded cost. Most production agents use a hybrid: a summary + recent turns + the system message.

Concept 2 of 5

Packing one strict carry-on bag

BEFORE: Imagine packing for a two-week trip, but the airline allows one carry-on with a strict 7 kg limit.

PAIN: Cram everything in and it's rejected at the gate. Pack only what fits right now and you might reach the formal dinner in hiking clothes because you dropped the dress shoes days ago.

MAPPING: The context window is that bag. Full history crams everything in (fine for short trips). Sliding window packs only the last few days (cheap, but you lose early essentials). Summarization writes a packing list of what you left behind plus today's essentials — the best balance for long journeys.

Concept 2 of 5

The three strategies

  1. Full historySend all messages every time. Simplest code; best for short chats under ~20 turns.
  2. Sliding window (N)Keep only the last N messages. Fixed cost, but early context is lost forever.
  3. SummarizationCompress old turns into a summary, prepend it, drop the originals. Keeps meaning at bounded cost.
  4. Hybrid (production)Summary of old turns + full recent turns + always-present system message.
summary of msgs 1–14
+ kept verbatim ↓
msg 15
msg 16
NEW
Concept 2 of 5

Which strategy to use

IF chat is short (< ~20 turns):
  → full history (simplest)

IF only recent context matters
   AND you want fixed cost:
  → sliding window (last N)

IF early facts still matter
   in a long conversation:
  → summarize old + keep recent

# production default: hybrid —
# summary + recent turns + system

On local Mistral, summarization is an extra Ollama call — no external cost, but it adds latency.

Concept 2 of 5

Misconceptions

"A sliding window and summarization are the same thing."
No. A window throws old messages away and loses their information forever. Summarization compresses them — preserving the meaning while discarding the verbatim text.
"Summarization is lossless."
No — it's lossy compression. It keeps the gist but drops specifics: exact names, IDs, dates, amounts. That's why production systems keep “pinned facts” that are never summarized.

Full, sliding window, and summarization trade completeness against token cost. There is no single best — it depends on chat length and how much early context matters.

A 50-turn support chat is ~75K tokens on full history, ~15K on a window, ~8–12K summarized while keeping more context than a window alone.

Concept 3 of 5

Four consumers share one fixed window

The context window is a fixed size (e.g. 32K tokens for Mistral via Ollama). Four things compete for that space: the system prompt, the conversation history, the current user message, and the reserved response.

History grows every turn, so the space left for a response shrinks over time. You must count input tokens before each call and prune when the remaining headroom drops too low — otherwise the model runs out of room to answer.

Concept 3 of 5

The fixed-size suitcase

BEFORE: Imagine a suitcase with exactly 32,000 units of space — not one more — and everything you need must fit inside.

PAIN: You pack a travel guide (system prompt), a photo album of memories (history), today's souvenirs (current message), and must leave room to bring things home (the response). Overstuff the album and there's no space left for souvenirs or the trip home.

MAPPING: The context window is that suitcase. The four consumers — system prompt, history, current message, and reserved response — all compete for the same fixed space, and the album (history) keeps growing.

Concept 3 of 5

Budgeting each request

  1. System promptFixed 200–800 tokens — but paid on EVERY request, not just the first.
  2. HistoryVariable and growing; the piece you actively manage with pruning.
  3. Current messageVariable — this turn's user input.
  4. Response headroomWhat remains after input. Keep enough space for the model to reply.
Sys 15%
History 55%
Msg
Resp
Concept 3 of 5

Pseudocode

# run BEFORE every API call
input = estimate_tokens(all_messages)
# rough rule: ~4 chars = 1 token

available = CONTEXT_LIMIT
             - system_tokens
             - history_tokens
             - current_msg_tokens

IF available < RESPONSE_HEADROOM:
  # not enough room to reply
  history = prune_to_budget(history)

SEND messages
Concept 3 of 5

Misconceptions

"max_tokens limits what the model reads."
No. It only caps how much the model writes (output). It doesn't shrink the input you send — your history still fills the window.
"Local models have unlimited context."
No. Ollama's context is configurable but finite, and larger windows need far more RAM — a 32K window on Mistral 7B costs roughly 8 GB beyond the base weights.

System prompt + history + current message + response all share one fixed window. Count input before each call and prune when headroom runs low.

The system prompt is paid every turn: 500 tokens × 50 turns = 25,000 cumulative input tokens. Keep it tight.

Concept 4 of 5

Cut the filler, keep the facts

Pruning is selectively removing messages to stay within token limits while preserving the information the model actually needs. It's not just “drop the oldest” — you score messages by importance.

Greetings and filler get cut first; key facts (account numbers, decisions, tool results) are preserved no matter how old. The critical rule: after removing any message, the remaining array must still alternate user/assistant, with a user message first after the system prompt.

Concept 4 of 5

The film editor's cut

BEFORE: Imagine you're a film editor with 6 hours of raw footage that must become a 90-minute movie — and key plot points are scattered throughout.

PAIN: Blindly cut the first 4.5 hours and you lose the character introductions. Keep everything and the audience drowns in filler scenes.

MAPPING: Pruning a conversation works the same way. You score each message by importance: greetings and filler are cut first; key facts are preserved no matter how old; and sometimes you replace a whole block of scenes with a narrator's summary.

Concept 4 of 5

Five pruning strategies

  1. FIFODrop the oldest messages first. Simple, but may lose critical early context.
  2. Importance scoringTag messages with metadata; drop low-importance ones first.
  3. Semantic dedupRemove redundant exchanges that repeat the same meaning.
  4. Role-based retentionAlways keep certain types: tool results, preferences, key decisions.
  5. Summarize-and-replaceCondense a block into one summary before dropping the originals.
"hi there" (cut)
"thanks!" (cut)
↓ keep ↓
acct #4821 (kept)
decision (kept)
Concept 4 of 5

What to cut, what to keep

FOR each message, oldest first:

  IF it's greeting / filler:
    → cut it first

  IF it holds a key fact
     (id, decision, tool result):
    → keep, no matter how old

  IF a big old block remains:
    → summarize-and-replace it

# AFTER cutting: verify user/assistant
# still alternate, user-first. Broken
# alternation = API error.
Concept 4 of 5

Misconceptions

"Just delete any message to save tokens."
Risky. Removing a message can break user/assistant alternation — the API requires turns to alternate and a user message first after the system prompt, or the call errors out.
"Dropping the oldest is always safe."
Not if the oldest turn holds a key fact — an account number, a decision, a tool result. Score by importance, not just age.

Prune by importance, not just age, and preserve valid alternation. Cut filler; pin the facts.

Smart pruning can shrink a 3,000-token, 20-message chat to 1,200 tokens (7 messages) while keeping ~91% of the actionable information.

Concept 5 of 5

One class that owns the whole loop

A conversation manager is a class that sits between your app and the Ollama API. It bundles everything from the first four concepts into one reusable component so your app code just calls send().

It handles six responsibilities: message storage, token counting, automatic pruning, summarization, metadata tracking, and serialization to JSON so conversations survive restarts. Build it in layers — basic, then sliding window, then auto-summarizing and persistent.

Concept 5 of 5

The CEO's personal assistant

BEFORE: Imagine a CEO who takes every meeting with no assistant — carrying a growing stack of every note from every past meeting, verbatim and unfiltered.

PAIN: By month three the stack is so tall they spend more time flipping through old notes than engaging. Week-one decisions are buried under pages of “sounds good” and “let's circle back.”

MAPPING: The conversation manager is that assistant. It records every exchange, scores messages by importance, compresses old meetings into executive summaries, and ensures the model walks into each meeting with exactly the right briefing.

Concept 5 of 5

Build it in three layers

  1. Basic managerStore messages, prepend the system prompt, call Ollama, track token usage.
  2. Sliding windowOverride “get messages” to send only the last N — and fix alternation.
  3. Smart managerAuto-summarize once tokens cross a threshold; keep recent turns verbatim.
  4. Persistsave()/load() serialize full state to JSON so chats survive restarts.
your app
→
manager
→
Ollama
Concept 5 of 5

Pseudocode

CLASS Manager:
  messages = []
  threshold = 8000

  FUNCTION send(user_text):
    messages.append({ user: user_text })
    reply = api.chat([system] + messages)
    messages.append({ assistant: reply })

    IF last_input_tokens > threshold:
      summarize_old()   # keep recent
    RETURN reply

  FUNCTION save(path):
    write_json(path, all_state)
Concept 5 of 5

Misconceptions

"The manager stores everything in the model."
No — all state lives in your class (and its JSON file). The model is stateless; the manager is the thing that remembers and replays.
"Summarization can replace pinned facts."
No. Summaries lose names, IDs, amounts, dates. Keep immutable “case facts” at the START of context (a high-recall position) — never summarized.

One manager class bundles storage, token counting, pruning, summarization, and JSON persistence. Your app just calls send().

If summarization fails, fall back to a sliding window — and always keep pinned facts out of the summarizer.

Test the core insight

One question per concept. Tap to reveal the answer.

Open the full module on desktop

The desktop version has you build a real ConversationManager against local Mistral — starting basic, adding a sliding window, then auto-summarization and JSON persistence — with animated token-budget and pruning visualizers, in Python and Node.

Open M08 on Desktop → Stateless · history patterns · token budget · pruning · manager · ~55 min

Module 6 of 12 · Open Source Track · Memory & Context