Production agent code spends more effort handling things going wrong than handling the happy path - tools fail, APIs time out, and models occasionally misuse a tool. Reliability here is what separates a demo from something you can actually ship.

Tools fail - plan for it explicitly

def call_tool_safely(tool_function, **kwargs):
    try:
        return {"success": True, "result": tool_function(**kwargs)}
    except Exception as e:
        return {"success": False, "error": str(e)}

Feed the failure back to the model as an observation, the same way you would feed back a success - a well-prompted agent can often recover by trying a different approach, but only if it actually knows the call failed.

Validate model-generated tool arguments before executing

Never trust that the model filled in a tool's arguments correctly - validate them the same way you would validate any external input, before running anything with real consequences:

def book_flight(flight_id):
    if not flight_id or not flight_id.isdigit():
        return {"error": "Invalid flight_id"}
    # proceed only after validation

Set limits, always

Cap the number of steps an agent can take, and for any action with real cost or consequence (spending money, sending a message, deleting data), require explicit confirmation rather than letting the agent act fully autonomously - the same "never trust input blindly" instinct behind this site's own form validation and prepared SQL statements applies directly here.

Detecting infinite loops

An agent can occasionally get stuck retrying the same failing action - tracking recent actions and detecting repetition is a simple, effective safeguard against this failure mode.