Choosing between one session document and several state records is an early design decision for any stateful agent. Document stores and key-value backends lack relational schema formalism, but the choice still shapes query latency, consistency behavior, and how much of the reasoning trace you can inspect after failure.
With teams in our early access program, we have used four patterns and seen each work well in some contexts and poorly in others. This post covers their access characteristics and when each is or is not appropriate.
Two pressures before the patterns
Agent state schemas face two competing pressures. First is read performance: every reasoning step reads state, so the schema must support fast point lookups and bounded range scans. Requiring multiple fetches and joins to rebuild current state multiplies per-step latency across a multi-step task.
The second is write granularity. Agents append actions, update fields, and record intermediate results incrementally. Rewriting the entire state document for each partial update causes write amplification, raises storage cost, and increases the chance that sibling-agent writes conflict.
Each pattern below balances these pressures differently. None optimizes both at once. Choose by first deciding whether read performance or write granularity matters more for your workload.
Pattern 1: Session document with embedded action log
This pattern keeps the agent's complete current state in one document keyed by session ID, with an embedded array of action records covering the session history.
A session document could look like this:
{
"session_id": "sess_abc123",
"agent_id": "agent_research_v2",
"created_at": "2026-01-15T09:00:00Z",
"status": "active",
"goal": "Summarize the quarterly financials and flag anomalies",
"current_step": 7,
"context": {
"documents_reviewed": 3,
"pending_tools": ["fetch_spreadsheet"],
"scratch_pad": "Q3 revenue down 8% YoY. Need to check Q3 2024 baseline."
},
"actions": [
{ "step": 1, "type": "tool_call", "tool": "search", "ts": "2026-01-15T09:00:12Z" },
{ "step": 2, "type": "tool_result", "result_summary": "5 documents found", "ts": "2026-01-15T09:00:13Z" }
]
}
Read performance: Excellent for context retrieval. One session-ID lookup returns everything needed for the next reasoning step, with no joins or secondary lookups.
Write behavior: Application semantics make the action log append-only, but storage still updates the document on every step. If stored as one blob, each append rewrites the full document. That works at moderate action counts, under a few hundred, but gets costly as the log grows.
Best fit: Predictably short sessions with bounded step counts, or sessions where the log supports current-session reasoning rather than cross-session history.
Watch out for: Long sessions with action arrays reaching thousands of entries. Array growth raises per-write cost, while deserializing the full log raises per-read cost. Move the log to a separate document collection when sessions routinely pass a few hundred steps.
Pattern 2: Normalized context document with event stream
The normalized pattern divides the agent's current state from its history. A compact context document contains fields needed for the next reasoning step, while an event stream stores actions and observations as individual records ordered by session ID and timestamp.
The context document stays small and stable while being updated in place. The event stream is append-only, with no mutation of existing records. Critical-path reads fetch only context. Replay, analysis, and long-context reasoning query the stream by session ID and time range.
Read performance: Hot-path context reads are fast because they use a small document and point lookup. History reads add another round trip, but they are not required on every step, so the amortized cost is acceptable.
Write behavior: High-frequency writes perform better than in Pattern 1. Context changes are small in-place mutations, while event appends insert new records rather than changing old ones. Sibling agents can append to the same session stream without conflict because each writes its own records.
Best fit: Long-running sessions, multi-agent sessions with a shared event stream, and systems where replay or audit trails matter.
Watch out for: N+1 reads. If every step needs both the snapshot and recent history, avoid two sequential requests when one batched read can provide both. LaserData supports batched reads in one network round trip, so use that path.
Pattern 3: Versioned state snapshots
The snapshot pattern writes the agent's full state as an immutable, versioned record after every step. Instead of changing a current-state document, each step creates a snapshot keyed by session ID and step number. The highest-versioned snapshot represents current state.
{
"session_id": "sess_abc123",
"step_version": 7,
"snapshot_ts": "2026-01-15T09:00:58Z",
"full_context": { ... },
"actions_up_to_this_step": [ ... ]
}
Read performance: Fetching current state requires the maximum step version for a session, using a range query with DESC limit-1. A compound index on (session_id, step_version) keeps this fast, though it adds slight overhead versus a point lookup.
Write behavior: Excellent. Every write is a pure insert, and existing records remain unchanged. Unique (session_id, step_version) keys prevent write conflicts. This suits concurrent or parallel execution by multiple agents within one session.
Best fit: Point-in-time recovery, audit-required use cases, and parallel agent execution where write conflicts would otherwise be a concern.
Watch out for: Storage growth. Snapshot writes accumulate instead of replacing prior state. A 100-step session with a 10KB snapshot per step consumes 1MB for that session. With 1,000 concurrent sessions each running 100 steps, one batch consumes 1GB. Add compaction or retention early: retain the last N snapshots, archive older ones, or discard them after session close.
Pattern 4: Typed field partitioning
Typed partitioning divides agent state across documents by field type: a stable, rarely changed goal document, frequently updated working memory, an append-only completed-actions document, and potentially shared knowledge.
This is the most granular pattern. Each document type has its own update rate and access pattern, so each can be optimized separately. Goals can be indexed for lookup by goal type across sessions. Working memory can favor rapid write-read cycles. Shared knowledge can be cached aggressively because it changes less often.
Read performance: It depends on the access pattern. When a step needs only one type, fetching one small document is faster than retrieving and deserializing a large composite document with unused fields.
Write behavior: This pattern reduces write amplification most effectively. Updating working memory leaves the goal and actions documents untouched, and concurrent agents can update separate partitions without conflict.
Best fit: Complex agent systems whose state components have clearly different access patterns. It also suits multi-agent systems that share selected components, such as a knowledge base, without sharing the full session context.
Watch out for: Coordination overhead. Cross-partition reads need sequential or batched requests. If each reasoning step regularly needs several partitions, that overhead can outweigh the write gains. Profile first: when 80% of reads need only working memory and 20% need multiple partitions, this pattern fits. If 80% need multiple partitions, reconsider.
Choosing a pattern
The most useful criteria we have found are step depth and concurrency pattern.
For short sessions, under 50 steps, with one agent per session, use Pattern 1, the embedded-log session document. It is simple, fast to read, and has acceptable write cost.
For long sessions, 50+ steps, with one agent per session, use Pattern 2, normalized context plus event stream. Pattern 1 degrades as its embedded log expands, while separation keeps the hot-path document small.
For parallel execution or audit requirements, use Pattern 3, versioned snapshots. Pure inserts avoid conflicts and provide natural rollback.
For complex multi-agent sessions with distinct state components, use Pattern 4, typed field partitioning. Its granularity helps when state components have genuinely different access frequencies and isolation needs.
Hybrid designs are also useful: Pattern 2 for working context and Pattern 3 for milestone snapshots. The agent writes a snapshot after each major phase rather than each step. This gives up pure Pattern 3 point-in-time recovery for better storage efficiency while preserving auditability at phase granularity.
Schema evolution in production
Agent state schemas change as capabilities expand. Pattern 4 handles that evolution most gracefully because a new capability can add a document type without changing existing documents. Pattern 1 is most brittle: changing embedded context fields requires migrating existing sessions or adding dual-read logic for old and new shapes.
Whichever pattern you choose, add schema version fields from the beginning. When context changes, the version lets read logic interpret older documents without a migration. This is inexpensive during design and costly to retrofit once thousands of active sessions contain mixed schemas.
LaserData does not require a fixed document-field schema, so evolution belongs at the application layer rather than in database migrations. That flexibility suits agent workloads whose schemas change quickly during development. We are not saying schema governance is optional; we are saying the application owns it. Without a registry or version fields, inconsistent shapes eventually cause read errors when agent code handles missing fields poorly.