Let tools use the State¶
A tool can read or change the run it is part of.
from alpineagents import Agent, State, tool
@tool
def remember(key: str, value: str, state: State) -> str:
"""Save a note for later in this run"""
with state.edit_extra_data() as data:
data.setdefault("notes", {})[key] = value
return f"Saved {key}"
@tool
def recall(key: str, state: State) -> str:
"""Read a note saved with remember"""
return state.extra_data.get("notes", {}).get(key, f"No note named {key}")
agent = Agent(model="claude-sonnet-5", tools=[remember, recall])
print(agent.run("Remember that the deploy key is in vault, then tell me where it is"))
- A parameter typed
Stateis hidden from the model. The model seesremember(key, value)andrecall(key). - When the tool runs, that parameter receives the current State.
state.extra_datakeeps values across turns. The model never sees it.edit_extra_data()changes it.
What a tool can do with the State¶
| Call | Effect |
|---|---|
state.extra_data[...] |
Read your own values. It is read-only: change it with with state.edit_extra_data() as data: |
state.finish(answer) |
End the run after this turn. See Stop a run |
state.add_message(Message.notice(text)) |
Tell the model something. It goes into the context right after this turn's results |
state.answer, state.turn, state.usage, ... |
Read the run |
Thread safety¶
Tools in one turn can run at the same time on worker threads. Every State method is thread-safe. A
with state.edit_extra_data() as data: block is atomic: edits from several threads take turns, so
data["n"] = data.get("n", 0) + 1 never loses an update. If the block raises, nothing changes. Values must be JSON
(a set raises TypeError naming the key: use a list).