This is a submission for the Hermes Agent Challenge.
My Hermes research agent was tracking state across 20+ variables. Turn counter. Running cost. Message history. Sub-tasks done. Sub-tasks pending. Errors per tool. Each was a standalone variable at the top of the loop, updated individually, saved separately, and restored manually after a crash.
By turn 47, the state management was 200 lines of ad-hoc code spread across the loop. I replaced it with one object.
from agent_state_bag import StateBag
state = StateBag({
"turn": 0,
"cost_usd": 0.0,
"messages": [],
"sub_tasks_done": [],
"errors": 0,
})
for turn in range(1, 50):
state["turn"] = turn
state.increment("cost_usd", 0.05)
state.increment("errors") if tool_failed else None
response = call_llm(state["messages"])
state["messages"].append({"role": "assistant", "content": response.text})
StateBag
is a dict wrapper with extra features. It passes through all the dict methods you expect, plus the things a long-running agent actually needs.
state.push_turn() # saves current state to history
state.reset_to(state.last_snapshot())
At the start of each turn, push a snapshot. If something goes catastrophically wrong mid-turn, you can roll back to the clean state before that turn started.
snap = state.snapshot()
changes = state.diff(snap)
I log the diff to my trace file at the end of each turn. When reviewing a run, I can see exactly what each turn changed — without comparing full state snapshots manually.
state.increment("turn") # += 1
state.increment("cost_usd", 0.05) # += 0.05
state.decrement("retries_left") # -= 1
Returns the new value. Initializes to 0 if the key is missing.
state.save("state.json")
state = StateBag.load("state.json")
Plain JSON. Grep-able, inspectable, resumable. I save state after every successful turn. On crash, StateBag.load
restores exactly where I was.
state.push_turn() # checkpoint this turn
state.history # list of all saved snapshots
state.turn_count # 11
state.last_snapshot() # most recent snapshot
The history grows across the run. After the run, I can replay it to see how state evolved turn by turn.
state.merge(worker_output) # other wins on conflict; deep-copied
In my multi-agent setup, workers return their results as dicts. The supervisor merges them into its own state.
Standard library only: json
, copy
, dataclasses
, typing
. No third-party packages.
pip install agent-state-bag