The first question anyone sensible asks about an AI agent is what stops it doing something stupid. It is the right question, and "we wrote a careful prompt" is not an answer.
Instructions are a request, not a constraint. If the only thing standing between a model and a bad outcome is text asking it to behave, you have a suggestion where you need a wall. Here is what we actually build instead.
Give it one job
Every agent we deploy under Apex does one thing. Match invoices to purchase orders. Check compliance dates. Prepare a forecast. Not a general assistant with access to the business.
This is the most effective control available and it is barely technical. An agent with one job needs one set of data, one set of permissions and one definition of correct. You can describe what it is allowed to do in a sentence, which means you can enforce it. A general-purpose agent with broad access cannot be reasoned about at all, because the set of things it might do is not enumerable.
Put the rules outside the model
Your business rules do not belong in a prompt. They belong in code the model cannot rewrite.
If approvals over $50,000 need two signatures, that check runs as a condition, before anything is committed, and it fails closed. The agent's job is to prepare the case. The rule's job is to decide whether it passes. The model never gets a vote on the rule, so it cannot be persuaded, confused or drifted out of it.
This is the difference between an agent that follows your rules and an agent that has been asked to. Only one of them is auditable.
Make it cite its source
An agent that produces a number without saying where the number came from cannot be checked, which means it cannot be trusted at scale.
So we build them to answer with the reference attached. This total came from this document, this field, this record. That does two things: a person can verify a decision in seconds instead of reconstructing it, and a lot of errors become self-evident, because a citation that does not support the claim is visible immediately.
It also changes the failure mode. Instead of a confident wrong answer, you get an answer you can disprove.
Let it refuse
Most bad output comes from an agent being forced to produce something when the honest answer is that it does not know.
If a document is unreadable, if two records conflict, if the confidence is low, we want the agent to stop and hand it to a person. That means designing a real path for the exception, and accepting that some percentage of cases will always route to a human. Chasing full automation on the last five per cent is where accuracy goes to die, and those cases are usually the ones worth a person's attention anyway.
A queue of flagged exceptions is a feature. An empty queue on messy inputs means the agent is guessing.
Draw the line at committing
We separate preparing work from committing it. Agents prepare. For anything that pays money, contacts a customer, deletes a record or files something with a regulator, a person commits.
Almost all of the time saving lives in the preparation, so this costs you very little and it changes what a bad day looks like. A wrong draft is noticed and corrected. A wrong payment is a phone call from your bank.
Watch it after launch
Guardrails are not a launch checklist. Models change, your data changes, and the distribution of what arrives changes with your business.
So we log every decision with its inputs and its reference, track how often it refuses, and watch the exception rate. A rising refusal rate usually means the incoming data has changed. A falling one, with no other explanation, is the warning sign — it often means the agent has started guessing on cases it used to hand over.
That monitoring is not overhead on top of the build. It is the part that lets it still be running, correctly, a year later.




