Designing a human review queue for AI output
How to make AI review useful: show the source beside the proposal, route specific exceptions, plan reviewer capacity and measure errors after approval.
An agent submits a purchase order. The supplier’s system creates it, but the connection drops before the reply arrives. The agent sees a timeout and tries again. Now the kitchen has ordered the same delivery twice.
The timeout tells us that the caller stopped waiting. It says nothing about whether the supplier accepted the order. Once an agent can write to another system, its tool needs a way to recognise an earlier attempt and recover the result.
Create an operation ID when the application accepts a proposed action. Store it with the exact arguments, the account it belongs to and any approval. Every attempt to execute that action uses the same ID. A new model response or a restarted worker must be able to find the existing operation.
This is the purpose of an idempotency key: repeated submissions of one intended action can return an equivalent result without repeating the side effect. The server must also reject a reused key with different arguments. Amazon’s explanation of idempotent APIs covers both the request identity and the distinction between a retry and a new request.
For an agent, keep that identity in application code. Asking the model to invent a fresh key on each tool call defeats the protection. Deriving it only from the arguments also causes trouble: two deliberately separate orders may contain exactly the same items.
For a tool that writes to your own database, put the operation record, the business change and the saved result in one transaction. Enforce a unique constraint on the account and operation ID. Two workers can then race for the same operation without both creating a purchase order. A repeated call checks the stored arguments and returns the saved receipt.
An external API needs its own protection. If the supplier accepts idempotency keys, pass the stored operation ID through and follow its documented retention period. Keep your operation record for at least as long as your application allows retries, including manual recovery. An old job replayed after the supplier has forgotten the key may become a new order.
If the supplier supports lookup by a unique client reference, save that reference before sending. After a timeout, query for it. A missing search result is only enough to justify another write if the API guarantees that the lookup is complete and current, or enforces uniqueness on that reference. Otherwise, a delayed first request can still arrive after the lookup.
A tool response needs to distinguish a rejected request from one whose outcome is unknown. Use explicit states in the operation record, and let the execution code decide which transitions are allowed.
| State | What the application knows | Next step |
|---|---|---|
| Prepared | The action and arguments are stored; no attempt has started | Execute with the stored operation ID |
| In progress | A worker has claimed the action | Wait or inspect the existing attempt |
| Succeeded | A receipt identifies the created record | Return the saved result |
| Rejected | The destination confirms it did not accept the action | Correct the input or report the rejection |
| Unknown | An attempt began, but its outcome cannot be confirmed | Reconcile with the destination before another write |
A worker crash can leave an operation marked in progress. Expiring the worker’s claim lets another worker investigate, but does not prove that the external write failed. Recovery still needs the supplier’s idempotency support or a reliable status lookup.
If neither exists, hold the action for a person to check. Show the account, arguments, time of the attempt and any external reference. A generic “try again” button would hide the very information that makes the decision possible.
Retry temporary failures only when repeating the operation is safe. Use increasing delays, add jitter so workers do not all retry together, and cap attempts and total elapsed time. Check what the SDK already retries before adding another retry loop around it. AWS’s guidance on limiting retries describes these controls and why retries at several layers can multiply the load.
An agent also needs a task-level limit. If a tool exhausts its budget, another model turn must not quietly create a new operation for the same action. Return the existing operation ID and state, then stop or route it to the review queue.
Changing the supplier, quantity or delivery address creates a different proposal. Require a new approval where the workflow calls for one, and resolve any uncertain earlier attempt before sending a replacement. Otherwise both versions can arrive.
The useful integration test accepts the order and then drops the response. Run the tool again with the original operation ID. Check that there is one order at the destination and that both attempts resolve to the same receipt. Then send two attempts concurrently and restart a worker between the external write and the local receipt save.
These tests complement the model’s evaluation set. The model may select the correct tool and supply every argument correctly while the integration still creates duplicate records.
Before granting an agent write access, choose one action and trace its recovery after a lost reply. Write down who can establish whether it happened, which identifier they use and when the system stops trying. That recovery path belongs in the first version of the tool.
AI agents that read documents, answer customers and assemble reports inside your existing tools, with an evaluation set, an action log and a person in the loop.
Web and mobile products with AI inside, from a working first version within a week to a launched app on a stack your team can run, at a fixed price.
How to make AI review useful: show the source beside the proposal, route specific exceptions, plan reviewer capacity and measure errors after approval.
Changed documents, deleted files and revoked access need different handling. How to keep a RAG index current without serving fragments of old revisions.
Run an agent against disposable records and simulated tools. Check the resulting state, inject failures and keep production credentials out of the test.