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), andPostgresStateStore(PostgreSQL). - Unified protocol: All implement
save_state,load_state, anddelete_statedefined inaisuite/agents/state_store.py. - Simple activation: Pass
state_storeandthread_idtoRunner.run_sync()orRunner.continue_sync(). - Production ready:
PostgresStateStoreuses tablesagent_thread_heads,agent_messages, andagent_compactionswith ACID guarantees. - Conflict safety: Optimistic locking via revision numbers raises
StateConflictErroron 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →