How to Persist and Resume Agent State with State Stores in aisuite
To persist and resume agent state in aisuite, pass a StateStore implementation (such as FileStateStore or PostgresStateStore) along with a unique thread_id to Runner.run_sync(), then use Runner.continue_sync() with the same identifiers to reload the conversation history and continue execution.
The aisuite library decouples agent execution from state persistence through a clean abstraction layer. By leveraging the StateStore protocol defined in aisuite/agents/state_store.py, developers can save conversation history, intermediate steps, and metadata to memory, disk, or PostgreSQL, enabling seamless resumption of long-running agent workflows across process restarts.
Understanding the StateStore Protocol
The foundation of state persistence is the StateStore protocol located in aisuite/agents/state_store.py. This interface defines three core operations: save_state(), load_state(), and delete(). When you invoke Runner.run_sync() with a state_store and thread_id, the runner serializes the complete RunState—including message history, execution steps, and metadata—and delegates storage to the provided implementation.
Each save operation returns a StoredRunState object containing a monotonically increasing revision number, timestamps, and optional metadata. The protocol enforces optimistic concurrency control: the save_state() method accepts an optional revision parameter, and implementations raise a StateConflictError if the stored revision does not match, preventing lost updates in concurrent environments.
Built-in State Store Implementations
aisuite ships with three concrete implementations that share the same API but target different persistence requirements.
InMemoryStateStore: Stores state in a Python dictionary. Ideal for unit tests and short-lived scripts where durability is not required.FileStateStore: Persists JSON files to a local directory (default.aisuite/state). Suitable for simple local development and debugging.PostgresStateStore: Stores data in PostgreSQL tables (agent_thread_heads,agent_messages,agent_compactions). Designed for production deployments requiring durability, multi-process access, and horizontal scalability.
Persisting Agent State
To enable persistence, instantiate a store and pass it to the runner alongside a unique thread identifier. The runner validates that both state_store and thread_id are provided together, raising ValueError if only one is supplied.
from aisuite.agents.runner import Runner
from aisuite.agents.state_store import FileStateStore
# Initialize store (defaults to ./.aisuite/state)
store = FileStateStore()
result = Runner.run_sync(
agent=my_agent,
input="Explain the difference between TCP and UDP.",
thread_id="demo-thread-1",
state_store=store,
max_turns=3,
)
After execution, a JSON file named demo-thread-1.json appears in the state directory, containing the serialized StoredRunState with revision 1.
Resuming a Conversation
To continue a persisted conversation, use Runner.continue_sync() (or the async continue_run). This method loads the existing state via state_store.load_state(), appends the new user message to the conversation history, executes the agent, and writes the updated state back with an incremented revision.
continued = Runner.continue_sync(
target=my_agent,
input="What about HTTP/2?",
thread_id="demo-thread-1",
state_store=store,
)
The runner handles hydration automatically, preserving context from previous turns without manual state management.
Handling Optimistic Concurrency
For high-concurrency scenarios, pass the revision returned by the previous load_state() call when saving. If another process has modified the state in the interim, the store raises StateConflictError via the internal _assert_revision helper, allowing your application to retry or merge changes.
Production-Grade Persistence with PostgreSQL
For distributed systems, PostgresStateStore in aisuite/agents/postgres_state_store.py provides ACID guarantees and schema management. It stores message history in agent_messages and maintains a lightweight head row in agent_thread_heads.
from aisuite.agents.postgres_state_store import PostgresStateStore
pg_store = PostgresStateStore.from_dsn(
"postgresql://user:pass@localhost/aisuite",
create_schema=True
)
run1 = Runner.run_sync(
agent=my_agent,
input="Summarize the last 10 trades.",
thread_id="pg-thread-42",
state_store=pg_store,
)
The implementation supports state compaction via compact_state() to trim ancient messages and control storage growth.
Attaching Custom Metadata
You can attach arbitrary dictionaries to persisted states for auditing or indexing. Pass a metadata dictionary to run_sync(), and retrieve it later via load_state().
result = Runner.run_sync(
agent=my_agent,
input="Explain quantum entanglement.",
thread_id="meta-demo",
state_store=store,
metadata={"user_id": "alice", "session": "quiz-1"},
)
stored = store.load_state("meta-demo")
print(stored.metadata) # {'user_id': 'alice', 'session': 'quiz-1'}
Summary
- The
StateStoreprotocol inaisuite/agents/state_store.pyabstracts persistence behindsave_state()andload_state()methods. - Choose
InMemoryStateStorefor tests,FileStateStorefor local development, orPostgresStateStorefor production workloads. - Always supply both
thread_idandstate_storetoRunner.run_sync()orRunner.continue_sync()to enable persistence. - The runner automatically manages revision numbers and optimistic concurrency control to prevent state corruption.
- Custom metadata and PostgreSQL compaction support advanced lifecycle management for long-running agents.
Frequently Asked Questions
What happens if I provide only a thread_id without a state_store?
The runner raises a ValueError because aisuite requires both parameters to ensure deterministic persistence behavior. You must supply both or neither.
Can I resume a conversation from a different machine or process?
Yes, if you use PostgresStateStore or a shared FileStateStore directory (e.g., on a network filesystem). The state is serialized to JSON or database rows, making it portable across processes.
How does aisuite handle concurrent writes to the same thread?
The StateStore protocol implements optimistic concurrency. Each save returns a revision number; subsequent saves must include this revision. If the revision mismatches, a StateConflictError is raised, forcing the caller to reload and retry.
Is there a limit to how much conversation history is stored?
By default, no. However, PostgresStateStore provides a compact_state() method to truncate old messages, and you can implement similar logic for custom stores by manipulating the RunState object before saving.
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 →