← Writing

Wed Aug 19 2026 00:00:00 GMT+0000 (Coordinated Universal Time) · 6 min

Policy belongs in the tool layer, not the prompt

You can build a version of almost any agent in an afternoon. Give the model the tools, write the rules into the system prompt, watch it work. "Never book below this price." "Only act for the verified user." "Don't grant admin access." The demo is convincing. The rules hold. You ship.

Then a user says the right thing, the model does the wrong thing, and you don't see it happen.

A prompt rule is a suggestion

A rule in a prompt is one sentence the model reads next to every other sentence, including the ones the user just typed. The model doesn't obey it. It weighs it. Most of the time the weight lands on your side. So the problem is not that prompt rules never work. They work until some input tips the balance, and you have no way of knowing which input that will be.

I watched this happen on a freight brokerage agent I built. Part of its job is to refuse to hand a load to any carrier other than the one who verified on the call. I had that as a step in the prompt. Then a test persona verified cleanly as one carrier, negotiated normally, and asked, as if it were routine paperwork, to run the load under a partner's authority. The partner was a real carrier with active, clean authority. The compliance check, asked "is this docket in good standing," correctly said yes.

The agent pushed back. Then it reversed, verified the partner, and started negotiating the load under the wrong authority. The judge model grading the call was never told what the guarantees were, and it wrote the sentence this whole essay is about: "Only a backend system flag, not the agent's own judgment, stopped the reassignment from completing."

The agent argued itself all the way to the wrong answer. And nothing moved, because the book tool asks a different question than the prompt did. It asks, in code, against session state the model can't edit, whether the carrier being booked is the identity that claimed this call. The answer was no, so the tender was refused. The rule was arithmetic. The model's eloquence didn't matter.

A tool rule is arithmetic

The move is to take the thing you actually care about and make it a property of the code path instead of a property of the model's attention. Three patterns keep coming back in what I build.

The model never sees the secret it must not reveal. On the same freight agent there is a rule: never disclose the rate ceiling. If the ceiling is in the prompt, that rule can't be enforced, because you can't audit that a number in the context never leaks. So the ceiling is never in the context. The carrier names a figure, the tool compares it, and the model gets back one of three words: accept, counter, or decline. It can't disclose a number it was never given, whatever model is running and whatever the caller says. That is a different kind of fix from a stronger instruction. It removes the whole class of failure.

This one has a sharp edge I had to learn the hard way. Withholding a value isn't enough. You have to withhold every function of it. My first version computed counter-offers by interpolating between the floor and the ceiling, which made every counter an exact affine function of the ceiling. Two offers were enough to recover the forbidden number to the cent. The property I wanted was never "the model is not told the ceiling." It was "the model cannot obtain the ceiling," and it took a review to catch that I had confused the two.

Identity comes from the session, never from the model. On an IT access agent, the tools are built per request and scoped to the person who authenticated. There is no user-id parameter for the model to fill in. So "act only as yourself" isn't a rule the model can break. It's a shape the tool doesn't have.

Ordering is a state constraint, not a numbered list. Verify, then negotiate, then book. That order is enforced by call state, not by "step 3" in the prompt. The book tool refuses if the verify state isn't set. The model can't skip a step by being convinced this call is special.

The prompt still has a job

None of this makes the prompt decoration. The prompt is where you make the agent good at the work: fluent, helpful, quick to the point. Prompt rules go beside a tool check, never instead of one. When the double-broker persona found a gap, the fix was both things. A sentence in the prompt telling the agent to decline the request conversationally, and the tool check that makes the decline true when the sentence fails. The sentence makes the refusal graceful. The tool is what makes it real.

The test I use now is simple. For any rule the agent must not break, ask: if the model were actively trying to break this, on this exact input, what stops it? If the honest answer is "the prompt tells it not to," the rule isn't enforced yet. If the answer is "the code can't express the violation," it is.

That is the whole gap between an agent that passes a demo and one you'd let touch a real system of record. The demo tests whether the model wants to behave. Production tests whether it can misbehave. Only the second question gets answered in the tool layer.