← Field notes

Tool call taxonomy: every agent action needs a category and a cost

Key takeaways

  • Every tool an agent can call belongs to one of five categories: read-only, idempotent write, reversible write, irreversible write, and external side-effect — and the category determines what guardrails apply.
  • Read-only calls carry no confirmation requirement; irreversible writes (send email, charge card, publish live) sit behind an outbox and require a decision log entry.
  • Cost scores matter as much as reversibility: an agent that calls a $0.12/call API 8,000 times overnight is running a billing incident, not a workflow.
  • Taxonomize before you build. The taxonomy is the safety architecture — adding it after the agent is running means auditing every call path under production conditions.

An agent fires a "send confirmation email" tool call. Then it retries because the first response timed out. The customer gets two emails. Your CRM logs two events. Your suppression list is now dirty. Nobody notices until a compliance audit six months later.

That is what unclassified tool calls cost you — not a crash, not an error, just silent state corruption that compounds over time. The fix is not better error handling. It is a taxonomy applied before the agent runs.

The five categories

Every tool an agent can call belongs to exactly one of these categories. Assign it at registration time, not at runtime.

1. Read-only

Fetches state. Produces no side effects. Safe to call any number of times.

Examples: get_order(order_id), query_inventory(sku), fetch_user_profile(user_id), search_knowledge_base(query)

Read-only calls carry zero reversibility risk. They can be retried freely, parallelized, and cached. The only cost is latency and compute.

2. Idempotent write

Writes state, but calling it twice produces the same result as calling it once. Safe to retry.

Examples: upsert_contact(email, fields), set_feature_flag(user_id, flag, value), put_object(bucket, key, content)

Idempotent writes are the safest write category. Design your internal APIs to be idempotent wherever possible — it makes the entire agent loop more resilient. A retry on failure is always safe.

3. Reversible write

Mutates state in a way that can be undone by a subsequent call.

Examples: update_order_status(order_id, status), move_file(src, dst), assign_ticket(ticket_id, agent_id), patch_record(id, fields)

Reversible writes require a compensating action to undo. The agent must know what the prior state was, which means it needs to read before it writes — and store that prior state somewhere it can retrieve if rollback is needed. Without that, "reversible" is theoretical.

4. Irreversible write

Executes an action that cannot be undone by any subsequent call.

Examples: send_email(to, subject, body), charge_card(customer_id, amount), publish_page(page_id), delete_record(id), submit_tax_filing(payload)

This is the category that breaks things. An agent that retries an irreversible write on timeout does not recover — it doubles the damage. Irreversible writes require a structural guarantee that they execute exactly once.

5. External side-effect

Triggers state changes in systems outside your control.

Examples: fire_webhook(url, payload), sync_to_crm(contact), push_to_analytics(event), notify_third_party_api(data)

External side-effects deserve their own category even when the individual call looks reversible. The downstream system may not be idempotent. You cannot inspect or roll back what happens after the HTTP response returns 200.

Irreversible writes: the outbox pattern

The standard solution for exactly-once execution of irreversible writes is the outbox pattern. It works like this:

  1. The agent writes its intent to an outbox table — a structured record of what it wants to do, not the action itself.
  2. A separate dispatcher process reads the outbox, executes the action, and marks the record as dispatched.
  3. The dispatcher uses a deduplication key (typically a deterministic hash of the intent) to skip records it has already processed.

The agent never calls the irreversible tool directly. It only writes to the outbox.

Why this prevents double-execution: The dispatcher is the only process that calls the tool. If the agent retries, it writes a duplicate outbox record with the same deduplication key. The dispatcher sees the key, skips it, and moves on. The action fires once.

Why this creates an audit trail: Every irreversible action is a row in the outbox table with a timestamp, the agent session that created it, the full payload, and the dispatch status. When something goes wrong — and it will — you have a complete record of what was intended, what was sent, and when.

The outbox pattern adds latency. For most irreversible writes, that is the correct trade-off. A 200ms delay on a card charge is invisible to the user. A duplicate charge is not.

Cost scores and the self-halting orchestrator

Tool calls are not free. Some are cheap (a database read costs microseconds). Some are expensive (a GPT-4 call with a large context costs real money). Some carry business cost beyond compute — a failed card charge has a fee, a mis-sent email has a deliverability cost.

Avakata attaches a cost estimate to every tool at registration time. The estimate is a normalized score that combines compute cost, API cost, and business risk weight. A read-only database call might score 0.05. A card charge scores 0.9. An email send scores 0.8.

The orchestrator aggregates cost in real time across the session. Every tool call increments a running total. When the total crosses a configurable threshold, the orchestrator self-halts and surfaces a decision point to a human operator or a supervisor agent.

This does three things:

  • Prevents runaway sessions. An agent stuck in a retry loop on an expensive tool does not run indefinitely.
  • Makes cost visible. You can see, per session, exactly which tools drove cost and by how much.
  • Creates a natural checkpoint for irreversible actions. A high-cost irreversible write approaching the threshold triggers a halt before execution, not after.

The threshold is not a hard limit on capability — it is a circuit breaker. Set it conservatively at first. Raise it as you build confidence in the agent's behavior.

The external side-effect problem at volume

A single webhook call to your CRM is harmless. Ten thousand webhook calls from an agent processing a bulk job are not.

External side-effects have a property that makes them uniquely dangerous at scale: they are individually reversible in theory but collectively irreversible in practice.

Consider a contact sync agent that calls sync_to_crm(contact) for every record in a 50,000-row import. If the agent retries on any failure without deduplication:

  • Contacts get created twice with slightly different field values.
  • Your CRM's merge logic may or may not catch duplicates, depending on which fields it keys on.
  • Downstream automations in the CRM fire on both records — emails, sequences, lead scores.
  • You now have a data integrity problem that requires manual remediation across three systems.

The fix is the same as for irreversible writes: write intent to an outbox, dispatch with a deduplication key, and treat the external system as if it is not idempotent — because you cannot verify that it is.

Webhook storms are a related failure mode. An agent that fires webhooks in a tight loop under error conditions can overwhelm a receiving endpoint, trigger rate limiting, and cause the very failures it is trying to recover from. Rate limiting and backoff must be enforced at the dispatcher level, not left to the agent.

Where to start

Before you deploy your next agent, do this:

  1. List every tool it can call. Not the categories of tools — the specific functions, with their signatures.
  2. Assign a category to each. Read-only, idempotent write, reversible write, irreversible write, or external side-effect.
  3. Attach a reversibility flag. Boolean. true means a compensating action exists and is implemented. false means it does not.
  4. Attach a cost score. Even a rough estimate is better than nothing. Normalize to a 0–1 scale or use raw dollar estimates — pick one and be consistent.
  5. Implement the outbox pattern for every irreversible write and external side-effect. No exceptions.

This is not a checklist you run once. It is the schema for your tool registry. Every new tool gets these fields before it is available to any agent.

The taxonomy is the safety architecture. An agent that cannot distinguish between reading a record and charging a card is not a production system — it is a liability waiting for the right retry condition.

Frequently asked questions

What is a tool call taxonomy for AI agents?

A tool call taxonomy is a classification system that assigns every action an agent can take to a named category: read-only, idempotent write, reversible write, irreversible write, or external side-effect. Each category carries a reversibility flag (can this be undone?) and a cost estimate (compute, money, or downstream impact). The taxonomy matters because agents operate autonomously at speed — without a shared classification layer, there is no principled basis for deciding which actions need a confirmation step, which need a decision log, and which can run silently. It also makes audits tractable: instead of reviewing raw tool calls, reviewers work from a structured record of what category of action was taken, why, and at what cost.

How do you prevent AI agents from making expensive or irreversible mistakes?

Three mechanisms work together. First, category-based guardrails: any tool call classified as an irreversible write or external side-effect must produce a decision log entry before execution, and the action is routed through an outbox rather than fired directly. Second, real-time cost aggregation: the agent tracks cumulative cost (API spend, tokens, external calls) against a pre-set threshold and self-halts if it would breach the limit on the next action. Third, taxonomize before you build: guardrails are far cheaper to add at design time than to retrofit. If every tool is assigned a category when it is first defined, the enforcement logic is baked in from the start rather than bolted on after an incident.

What is the outbox pattern for agent side effects?

In the outbox pattern, an agent does not execute a side-effecting action directly. Instead, it writes its intent — for example, 'send this email with subject X to address Y' — as a record to an outbox store (a database table, queue, or log). A separate dispatcher process reads the outbox and executes each pending action exactly once, using idempotency keys to prevent double-execution on retry. This separation provides three guarantees: the agent's reasoning step and the real-world action are decoupled, so a crash between the two does not cause a lost or duplicate action; every intended action is logged before it happens, creating a complete audit trail; and high-risk actions can be held in the outbox for human review before the dispatcher releases them.

Book a 30-min discovery →