Write operations

Learn how to declare state-changing operations with @write in the AgentIdem Nebutex SDK and define logical identities for duplicate detection.

Write operations represent actions that may change external state.

Examples include:

  • charging a customer
  • issuing a refund
  • creating an order
  • sending a message
  • updating a database
  • provisioning infrastructure
  • writing to an external API
  • creating a user
  • modifying a record
  • triggering a workflow

Use @write for operations that can produce these kinds of side effects.

Basic write

Import write from agentidem and decorate the function that performs the state-changing operation.

from agentidem import write

@write
def create_order(order_id: str):
    return external_service.create_order(order_id)

The function can still be called normally from your agent code.

def run_agent(order_id: str):
    return create_order(order_id)

AgentIdem records the decorated call as a WRITE operation in the execution trace.

What AgentIdem records

A traced write can include information such as:

  • operation name
  • operation kind
  • arguments
  • logical identity
  • result
  • error
  • execution status
  • observation state
  • timestamps

The operation kind for a write is:

WRITE

Successful and failed writes

AgentIdem distinguishes between whether an operation completed successfully and whether its acknowledgement was observed.

For example:

SUCCESS + RECEIVED

means the write succeeded and the caller received the acknowledgement.

SUCCESS + LOST

means the side effect succeeded, but the acknowledgement was lost.

FAILED + FAILED

means the write did not complete successfully.

These distinctions matter because a lost acknowledgement can cause retry logic to repeat a side effect that already happened.

Logical identities

A write can define an explicit logical identity.

from agentidem import write

@write(identity=lambda payment_id, amount: payment_id)
def charge(payment_id: str, amount: int):
    return payments.charge(payment_id, amount)

The identity describes what makes two write executions represent the same logical side effect.

In this example, payment_id is used as the logical identity.

That means two successful calls involving the same payment ID can be recognized as executions of the same logical write.

Why identities matter

Calling the same Python function twice does not always mean that the same side effect happened twice.

For example:

charge("payment-100", 2500)
charge("payment-200", 2500)

Both calls use the same function, but they represent different logical operations because they use different payment IDs.

With:

@write(identity=lambda payment_id, amount: payment_id)

AgentIdem can distinguish between them.

By contrast:

charge("payment-100", 2500)
charge("payment-100", 2500)

represents repeated execution of the same logical write when both calls complete successfully.

Duplicate detection

AgentIdem detects repeated successful writes.

A failed write should not be counted as a completed duplicate side effect.

For example, if the first write fails before completing:

WRITE charge
FAILED

and a retry later succeeds:

WRITE charge
SUCCESS

that is not the same situation as two successful writes.

Duplicate detection is concerned with completed side effects.

Lost acknowledgements

A lost acknowledgement models this sequence:

  1. the write executes
  2. the side effect succeeds
  3. AgentIdem records the write as successful
  4. the acknowledgement is treated as lost
  5. the agent may observe an injected failure
  6. retry logic may execute the same logical write again

This is intentionally different from throwing an exception before the write runs.

A write may have changed the real world even though the agent believes the operation failed.

Before-operation failures

AgentIdem can also inject a failure before a selected write executes.

In that case:

  1. the failure occurs before the write runs
  2. the side effect does not happen
  3. the operation is recorded as failed

This lets AgentIdem distinguish between:

  • a write that never happened
  • a write that succeeded but was not acknowledged
  • a write that failed while executing

Example

Consider a refund operation:

from agentidem import write

@write(identity=lambda payment_id, amount: payment_id)
def refund(payment_id: str, amount: int):
    return payments.refund(
        payment_id=payment_id,
        amount=amount,
    )

An agent can call it normally:

def refund_agent(payment_id: str, amount: int):
    return refund(payment_id, amount)

If the refund succeeds but the acknowledgement is lost, retry logic may call refund again.

AgentIdem can use the configured identity to determine whether both successful executions represent the same logical refund.

Choosing an identity

A useful identity should represent the logical side effect rather than incidental runtime data.

For example:

@write(identity=lambda order_id: order_id)
def create_order(order_id: str):
    ...

or:

@write(identity=lambda user_id, message: user_id)
def send_welcome_message(user_id: str, message: str):
    ...

The correct identity depends on what uniquely represents the operation in your application.

Identity resolution errors

If AgentIdem cannot resolve the configured identity for a write, it can raise:

IdentityResolutionError

This keeps identity failures explicit instead of silently falling back to incorrect duplicate detection.

Next steps