How to Persist Agent State with Different State Stores in Aisuite

Aisuite provides three built-in state store implementations—InMemoryStateStore, FileStateStore, and PostgresStateStore—that enable you to persist agent conversations to memory, local JSON files, or PostgreSQL using a unified protocol.

The aisuite library (by Andrew Ng) includes a pluggable persistence layer that serializes an agent’s RunState so conversations can survive process restarts. By implementing the StateStore protocol defined in aisuite/agents/state_store.py, you can swap between ephemeral, file-based, and database-backed storage without modifying agent logic.

The State Store Protocol and Available Implementations

All state stores implement the StateStore protocol from aisuite/agents/state_store.py, which requires three methods: save_state, load_state, and delete_state. The library ships with three concrete implementations:

Store Module Persistence Use Case
InMemoryStateStore aisuite/agents/state_store.py Ephemeral Python dict Unit tests, single-process prototyping
FileStateStore aisuite/agents/state_store.py JSON files on disk Local debugging, simple scripts
PostgresStateStore aisuite/agents/postgres_state_store.py PostgreSQL tables (agent_thread_heads, agent_messages, agent_compactions) Production services, multi-worker deployments

When initializing a store, you pass it to any Runner.run* or Runner.continue_* method along with a unique thread_id to activate persistence.

Persisting Agent State with InMemoryStateStore

The InMemoryStateStore keeps state in a process-local dictionary. Data is lost when the process exits, making this ideal for unit tests or short-lived interactions.

import aisuite as ai

agent = ai.Agent(name="assistant", model="gpt-4")
store = ai.InMemoryStateStore()

# First run creates thread "demo/1"

run = ai.Runner.run_sync(
    agent, 
    "Tell me a joke", 
    state_store=store, 
    thread_id="demo/1"
)
print(run.final_output)

# Continue within the same process

run2 = ai.Runner.continue_sync(
    agent, 
    "Another one?", 
    state_store=store, 
    thread_id="demo/1"
)

Persisting Agent State with FileStateStore

The FileStateStore writes each thread to a JSON file under a configurable root directory. This requires no external dependencies and persists across process restarts.

from pathlib import Path
import aisuite as ai

store = ai.FileStateStore(root=Path("./state_files"))

# Writes to ./state_files/demo%2F1.json

result = ai.Runner.run_sync(
    agent, 
    "Explain recursion", 
    state_store=store, 
    thread_id="demo/1"
)

# Later, in a new process, resume from the same file

new_store = ai.FileStateStore(root=Path("./state_files"))
result2 = ai.Runner.continue_sync(
    agent, 
    "Give an example", 
    state_store=new_store, 
    thread_id="demo/1"
)

Persisting Agent State with PostgresStateStore

The PostgresStateStore provides ACID guarantees and multi-process access via PostgreSQL. It stores data in three tables—agent_thread_heads, agent_messages, and agent_compactions—managed in aisuite/agents/postgres_state_store.py.

import aisuite as ai

pg_store = ai.PostgresStateStore.from_dsn(
    dsn="postgresql://user:pass@localhost:5432/aisuite",
    create_schema=True,  # Creates tables on first run

)

run = ai.Runner.run_sync(
    agent, 
    "Summarize this text", 
    state_store=pg_store, 
    thread_id="sql_demo/42"
)

# Resumed from another machine using the same DSN

run2 = ai.Runner.continue_sync(
    agent, 
    "Add a title", 
    state_store=pg_store, 
    thread_id="sql_demo/42"
)

Optimistic Concurrency and StateConflictError

All stores implement optimistic concurrency control. Each save_state call must include the expected revision number (handled automatically by the Runner). If two processes write the same thread_id concurrently, the second write raises StateConflictError.

store = ai.InMemoryStateStore()

# Simulate first write

first_state = store.load_state("thread/1")  # revision 1

store.save_state("thread/1", updated_state, revision=first_state.revision)

# Attempt stale write

try:
    store.save_state("thread/1", newer_state, revision=first_state.revision)
except ai.StateConflictError as exc:
    print("Write rejected:", exc)  # Revision mismatch

How the Runner Handles State Hydration

When persisting, the Runner (in aisuite/agents/runner.py) dehydrates message artifacts to JSON-compatible structures before calling save_state. On load, it rehydrates them back to objects. This ensures FileStateStore JSON and PostgresStateStore rows contain serializable data while full objects are restored to the agent context.

Summary

  • Three built-in stores: InMemoryStateStore (ephemeral), FileStateStore (JSON files), and PostgresStateStore (PostgreSQL).
  • Unified protocol: All implement save_state, load_state, and delete_state defined in aisuite/agents/state_store.py.
  • Simple activation: Pass state_store and thread_id to Runner.run_sync() or Runner.continue_sync().
  • Production ready: PostgresStateStore uses tables agent_thread_heads, agent_messages, and agent_compactions with ACID guarantees.
  • Conflict safety: Optimistic locking via revision numbers raises StateConflictError on concurrent writes.

Frequently Asked Questions

What is the StateStore protocol in aisuite?

The StateStore protocol defines the interface for persisting RunState objects, requiring save_state, load_state, and delete_state methods. It is implemented by InMemoryStateStore, FileStateStore, and PostgresStateStore in aisuite/agents/state_store.py and aisuite/agents/postgres_state_store.py.

How do I resume a conversation after restarting my application?

Use Runner.continue_sync() with the same thread_id and a state store instance containing the saved state. For FileStateStore, ensure the same root directory path. For PostgresStateStore, use the same database DSN and thread identifier.

Can multiple processes share the same agent state?

Yes, when using PostgresStateStore, multiple workers can share state via PostgreSQL. The store implements optimistic concurrency control using atomic revision checks to prevent write conflicts between processes.

What happens if two processes try to update the same thread simultaneously?

The second write will fail with StateConflictError. Each save_state call must include the expected revision number, and if the stored revision has changed since loading, the update is rejected to prevent data loss.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →