Back to Blog
Reliable AI agents: tools, state, and safe retries
AI EngineeringAgentsSystem Design

Reliable AI agents: tools, state, and safe retries

Design bounded agent workflows with explicit state, validated tools, idempotent actions, and meaningful evaluations.

An AI agent becomes useful when it can act on information: inspect a ticket, query an order, draft a response, or schedule a task. The same ability introduces a harder engineering problem. A language model can propose an action, but the application must decide whether that action is valid, permitted, and safe to repeat.

Consider an illustrative support assistant that reads an order and prepares a replacement request. We want a clear record of what it observed and what it changed, even if a network request times out halfway through.

Choose the smallest control flow that fits

A fixed workflow follows predefined steps. An agent chooses some of its own next steps. If every request must validate an order, check eligibility, and draft a response, those stages can remain ordinary application code. Model-driven branching is useful when the required investigation varies substantially.

Anthropic’s guide to effective agents distinguishes workflows from agents and describes composable patterns such as routing and evaluator loops. Use that distinction to ask a practical question: which decision truly needs model judgment?

For the replacement assistant, eligibility checks should use explicit business rules. A model may summarize the customer’s explanation or decide which permitted lookup would resolve an ambiguity. It should not redefine the eligibility policy.

Design tools as narrow application interfaces

Avoid a single tool that accepts arbitrary SQL or an unrestricted URL. Prefer operations such as get_order and draft_replacement, with bounded arguments and structured results. Names and descriptions should explain when a tool applies and what its output means.

type ReplacementDraft = {
  orderId: string;
  reason: 'damaged' | 'missing';
  quantity: number;
};

function validateDraft(value: ReplacementDraft) {
  if (!Number.isInteger(value.quantity) || value.quantity < 1) {
    throw new Error('Quantity must be a positive integer');
  }
}

This small check is only one layer. At the runtime boundary, validate unknown JSON before treating it as ReplacementDraft. Then load the order, verify ownership from the authenticated session, check allowed quantities, and enforce the business rule. A TypeScript type cannot validate a model response or authenticate a customer.

Return useful errors such as ORDER_NOT_FOUND or QUANTITY_EXCEEDS_ORDER. Distinguish failures the model can correct from infrastructure failures that need retry or escalation. Do not expose database credentials or internal stack traces in tool results.

Persist state before you need recovery

Represent a run with explicit fields: run ID, authenticated actor, status, completed steps, pending action, action results, and timestamps. Separate observations from proposed actions. Record a compact operational trace rather than relying on a long chat transcript to reconstruct everything.

An illustrative transition sequence is:

created -> inspecting -> drafting -> awaiting_confirmation
        -> executing -> completed
                     -> needs_reconciliation

The final branch matters. If the external service accepted a replacement but the response was lost, the application does not know that the operation failed. It knows the outcome is uncertain. Recover by checking the operation’s durable identifier before attempting another write.

Checkpoints reduce repeated computation, but a saved checkpoint does not automatically make side effects exactly-once. Persistence and the external action may happen in different systems, leaving a failure window between them.

Make retries a protocol, not a loop

Give each intended action an idempotency key that survives retries. Bind that key to the authenticated actor and a normalized payload. If the same key arrives with a different payload, reject it. Store an in-progress or completed status with a uniqueness constraint so concurrent workers cannot independently claim the same operation.

Where the downstream provider supports idempotency, forward a stable key. Otherwise design reconciliation using a provider reference or move the operation to a human review queue when its outcome is uncertain. A local “completed” flag alone cannot close every failure window.

Retry transient failures with bounded attempts, backoff, and a total deadline. Invalid input should not be retried unchanged. A run also needs limits on tool calls, tokens, elapsed time, and spend. Reaching a limit should produce a clear partial outcome rather than an endless reasoning cycle.

Evaluate the behavior you actually care about

Create scenarios that include an ineligible order, malformed tool arguments, repeated submissions, a timeout after acceptance, and malicious instructions inside a ticket. Check whether the correct operation happened at most once, whether unauthorized operations were rejected, and whether the final message accurately describes the result.

A polished response is not a successful run if the wrong order changed. Conversely, a run that stops and explains an unresolved external failure can be correct behavior. Review operational outcomes alongside language quality.

Build a failure rehearsal

Before adding more tools, deliberately interrupt a run after each transition. Resume it from stored state and inspect the action log. Then deliver the same job twice. If you cannot explain which system owns the action and how duplicate execution is prevented or reconciled, the next useful improvement is in the workflow protocol.

Reliable agents are composed of ordinary engineering guarantees around a probabilistic decision-maker. Clear contracts, bounded execution, durable state, and honest uncertainty make the system easier to operate as its capabilities grow.

Related Posts

Building RAG systems that can explain their answers

Building RAG systems that can explain their answers

A practical walkthrough of ingestion, retrieval, grounded answers, and evaluation for AI applications.

AI EngineeringRAGSystem Design
Read More
Designing agentic stock analysis workflows

Designing agentic stock analysis workflows

A project overview of combining structured market data, stateful agent workflows, and automated reporting.

AI EngineeringLangGraphPython
Read More