Go back or try another path¶
Two tools: snapshot and restore go back to an earlier point. fork keeps the State as it is and tries something
else on a copy. Use them between turns, not while the model is being waited on or tool calls are pending.
Go back¶
state.snapshot() returns a StateSnapshot: the State's value at that moment, frozen, so it never changes. Take one
before something you may want to undo, and state.restore(snapshot) goes back:
from alpineagents import Agent, Message, State
agent = Agent(model="claude-sonnet-5")
state = State(messages=[Message.user("Fix calc.py")])
agent.run(state)
before = state.snapshot() # a frozen value: it never changes
state.add_message(Message.user("Rewrite it as a class"))
agent.run(state)
if "class" not in state.answer:
state.restore(before) # the State is back where the snapshot was taken
print(len(state.history) > len(before.history)) # True: history keeps everything, and says it went back
beforeis the State as it was after the first run.- The second run adds to
state. restore(before)puts the messages back. Ifbeforewas taken from another State, or while calls were pending,restoreraisesValueError.
What goes back and what does not:
| Part | After restore |
|---|---|
messages, turn, extra_data, finished, answer, stopped |
What they were |
usage |
Unchanged: the tokens were spent |
history |
Unchanged, plus one context_change entry with kind == "restore". A store saves the going back like any other step |
A snapshot lives in memory. To keep one, keep what a store keeps, its history: State(history=snapshot.history)
rebuilds the same value, in this process or another.
Try another path¶
state.fork() makes a new State with the same history and a new id, bound to no store and to no running Agent:
from alpineagents import Agent, Message, State
agent = Agent(model="claude-sonnet-5")
state = State(messages=[Message.user("Fix calc.py")])
agent.run(state)
attempt = state.fork() # the same content, a new id, nothing shared
attempt.add_message(Message.user("Rewrite it as a class"))
agent.run(attempt)
print(state.answer) # unchanged by the attempt
print(attempt.answer)
attemptstarts as a copy ofstate. They share nothing.- The new message and the run change only
attempt. -
state.answeris still the first answer. -
With
Agent(store=...), the fork is saved under its own id the first time the Agent runs it. - A fork of a finished State is finished too. To continue from its messages, start
State(messages=state.messages). - To use a snapshot of another State, build one from it with
State(history=snapshot.history). fork().snapshot() == state.snapshot(): equal histories give equal snapshots.fork()raisesValueErrorwhile the model is being waited on. Fork beforethink, or after it returns, for example inReporter.on_think_end.
Related¶
- State and history: what a snapshot is
- Survive crashes and restarts: what a store keeps