Read operations represent actions that observe state without changing it.
Use @read for operations that retrieve or inspect information.
Examples include:
- fetching an order
- reading a database record
- checking whether a user exists
- retrieving account state
- loading configuration
- reading external API data
- checking the status of a resource
Basic read
Import read from agentidem and decorate the function that performs the state-observing operation.
from agentidem import read
@read
def get_order(order_id: str):
return database.get(order_id)
The function can still be called normally from your agent code.
def run_agent(order_id: str):
order = get_order(order_id)
return order
AgentIdem records the decorated call as a READ operation in the execution trace.
What AgentIdem records
A traced read can include information such as:
- operation name
- operation kind
- arguments
- result
- error
- execution status
- observation state
- timestamps
The operation kind for a read is:
READ
Reads are not side effects
A read observes state.
It does not represent a state-changing side effect.
For example:
@read
def get_payment(payment_id: str):
return database.get_payment(payment_id)
Calling get_payment multiple times may produce multiple trace entries, but those calls are not treated as duplicate side effects.
AgentIdem's duplicate side effect detection is focused on successful write operations.
Read and write together
A typical agent may perform reads before deciding whether a write is necessary.
from agentidem import read, write
@read
def get_order(order_id: str):
return database.get(order_id)
@write(identity=lambda order_id: order_id)
def create_order(order_id: str):
return external_service.create_order(order_id)
def run_agent(order_id: str):
order = get_order(order_id)
if order is None:
return create_order(order_id)
return order
In the trace, AgentIdem can distinguish:
READ get_order
WRITE create_order
This distinction makes it easier to understand which operations only observed state and which operations could have changed the outside world.
Read failures
A read can fail during execution.
For example:
@read
def get_order(order_id: str):
raise RuntimeError("Database unavailable")
A failed read is recorded as a failed operation.
The failure may affect the rest of the agent execution, but it is still not treated as a completed side effect.
Reads in traces
Consider an agent that checks a payment before issuing a refund.
from agentidem import read, write
@read
def get_payment(payment_id: str):
return payments.get(payment_id)
@write(identity=lambda payment_id: payment_id)
def refund(payment_id: str):
return payments.refund(payment_id)
def refund_agent(payment_id: str):
payment = get_payment(payment_id)
if payment["status"] == "paid":
return refund(payment_id)
return "no_refund"
A trace might contain:
READ get_payment
WRITE refund
The read provides context for the execution, while the write represents the operation that may change external state.
Reads and fault scenarios
AgentIdem can record reads while running controlled fault scenarios against an agent.
The important distinction remains:
READ
observes state.
WRITE
may change state.
Fault scenarios that test side effect safety are primarily concerned with what happens around state-changing operations.
Reads still appear in traces so you can understand the full sequence of execution.
When to use @read
Use @read when a function:
- retrieves information
- inspects current state
- checks whether something exists
- fetches data used for a later decision
- does not intentionally change external state
Do not use @read for operations that create, update, delete, send, publish, charge, refund, provision, or otherwise modify external state.
Those operations should use @write.

