Skip to content

Lesson 4: Module 04: State and Memory

Give your machines the ability to remember, within a session and across sessions.

Learning Objectives

  • Define machine state (session-scoped data that persists across steps) with state block
  • Read and update state from compute steps
  • Use remember steps for persistent knowledge retrieval (RAG, Retrieval-Augmented Generation, which means feeding real documents to the AI so it answers based on facts, not guesses)
  • Know when to use state vs memory vs step outputs

Complexity Ladder: Applies to all levels; state and memory enhance machines at any complexity.

The Concept: Giving Machines Memory

Think of state as a notepad the machine carries during a single conversation — it can write things down and read them back. Memory is like a filing cabinet — information stored there survives after the conversation ends and can be retrieved in future sessions.

So far, our machines have been stateless — data flows from inputs through steps to outputs, then disappears. That works for single-shot tasks, but agents often need to:

  • Track progress within a multi-turn interaction (state)
  • Remember facts across separate sessions (memory)
  • Ground responses in retrieved knowledge (RAG)
Step Outputs State Memory
(between steps) (within a run) (across runs)
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ steps.a.field │ │ state.counter │ │ remember step │
│ │ │ │ │ │
│ Flows A -> B │ │ Persists across │ │ Survives between │
│ One direction │ │ steps in a run │ │ separate runs │
│ Simplest option │ │ Session-scoped │ │ Permanent storage│
│ │ │ │ │ │
│ Use for: passing │ │ Use for: counters│ │ Use for: knowledge│
│ data between │ │ conversation │ │ base, cache, │
│ adjacent steps │ │ history, flags │ │ user preferences │
└──────────────────┘ └──────────────────┘ └──────────────────┘

Mashin provides two mechanisms: state for session-scoped data and memory for persistent knowledge.

Start With Koda

Koda requires a free account. Sign in or create an account to use Koda exercises throughout this course. If you’re not signed in yet, read on; the exercises will be here when you’re ready.

Before diving into the syntax, try building a stateful machine with Koda:

“Build a counter machine that remembers how many times it’s been called. Each time it runs, it should increment its count, remember the current message, and respond with both the count and the previous message.”

Check that Koda uses a state block with fields for the count and last message. Study the structure; the rest of this module explains each piece in detail.

State: Session-Scoped Data

State persists across steps within a machine run and across multiple invocations of the same machine instance. Define it under implements:

implements
state
messages as list, default: [] // A list of message maps, starts empty
iteration as number, default: 0 // A counter, starts at zero
user_preference as string // Optional string

Each field has a name, type, and optional default value. State is available to all steps via state.<field>.

Reading State

In compute steps:

compute check_count
{current_count: state.iteration, total_messages: length(state.messages)}

In ask step prompts:

ask reason_with_history, using: "anthropic:claude-sonnet-4"
with task "Previous messages: ${state.messages}\nCurrent iteration: ${state.iteration}\n\nRespond to: ${input.message}"

Updating State

A compute step can update state by including state fields in its output:

compute increment
{
state: {
iteration: state.iteration + 1,
messages: state.messages ++ [{role: "user", content: input.message}]
}
}

The state key in the return value tells mashin to update those specific state fields. Fields not mentioned keep their current values.

Building It: Conversation Counter

A simple machine that tracks how many times it’s been called and remembers the last message:

machine conversation_tracker "Conversation Tracker"
accepts
message as string, is required
responds with
response as string
call_count as number
implements
state
call_count as number, default: 0
last_message as string
// Pure computation step, updates state
compute update_count
{
state: {
call_count: state.call_count + 1,
last_message: input.message
},
previous_message: state.last_message,
current_count: state.call_count + 1
}
// LLM step, generates a response
ask respond, using: "anthropic:claude-haiku-4"
with task "The user said: ${input.message}\nThis is call number ${steps.update_count.current_count}.\nTheir previous message was: ${steps.update_count.previous_message}.\n\nAcknowledge their message and mention the call count."
returns
response as string, is required

Memory: Persistent Knowledge

While state tracks session data, remember steps store and retrieve knowledge that persists across sessions and machines. There are three memory types:

Memory TypePurposeExample
Semantic (search by meaning, finds similar content even with different wording)Find content by meaning”What do we know about shipping?”
Structured (key-value lookup, exact match by a known key)Key-value lookupCache, user preferences
Episodic (event timeline, a chronological log of what happened)Event timelineInteraction logs, activity history

Semantic Memory (RAG)

The most powerful memory pattern: store documents, retrieve by semantic similarity (matching meaning rather than exact words), and ground LLM responses in real data.

machine knowledge_qa "Knowledge QA"
accepts
question as string, is required
responds with
answer as string
citations as list
grounded as boolean
implements
// Step 1: Retrieve relevant context from the knowledge base
recall retrieve
collection: "knowledge_base"
query: input.question
limit: 5
// Step 2: Generate grounded answer using retrieved context
ask answer, using: "anthropic:claude-sonnet-4"
with role "You answer questions based ONLY on the provided context. If the context doesn't contain the answer, say 'I don't have that information.' Always cite sources. NEVER make up information not in the context."
with task "Context:\n${steps.retrieve.results}\n\nQuestion: ${input.question}"
returns
answer as string, is required
citations as list
grounded as boolean

This is the RAG pattern. It reduces hallucinations by grounding the LLM’s response in retrieved documents rather than relying on training data alone.

Storing to Memory

remember store_fact
collection: "knowledge_base"
content: input.document
metadata: {source: input.source}

Structured Memory (Key-Value)

For caching and preferences, exact lookups by key, not similarity search:

// Write a value to structured memory
remember cache_result
key: "user_prefs:${input.user_id}"
value: {theme: "dark", language: "en"}
// Read a value from structured memory
recall get_prefs
key: "user_prefs:${input.user_id}"

When to Use What

NeedUseWhy
Track iteration count in a loopstateSession-scoped, ephemeral
Store conversation messagesstateScoped to this machine run
Cache an API response:remember (structured)Persist across runs, TTL support
Search knowledge base:remember (semantic)Similarity search, RAG grounding
Log user interactions:remember (episodic)Timeline, audit trail
Pass data between stepsStep outputssteps.name.field, simplest option

Rule of thumb: If data only matters during this run, use state. If data needs to survive between runs, use memory. If data only needs to flow from one step to the next, just use step outputs.

Key Syntax

// State definition (under implements)
state
name as type, default: value
items as list, default: []
// State access in expressions
state.field
// State access in ask prompts
${state.field}
// Memory store
remember name
collection: "name"
content: input.document
// Memory retrieve
recall name
collection: "name"
query: input.question
limit: 5

Common Mistakes

  1. Using state when step outputs suffice. If data just flows from Step A to Step B, use steps.a.field in Step B. Don’t store it in state just to read it back.

  2. Forgetting default values. State fields need defaults. Without them, you’ll get nil on first access, which may crash downstream steps.

  3. Storing everything in semantic memory. Semantic search (search by meaning) is powerful but not free. Use structured memory (key-value lookup) for exact lookups (user preferences, cache) and semantic memory only when you need similarity search.

What’s Next

In Module 05, you’ll learn how to control execution flow: chaining steps, branching conditionally, and building loops.