Action validation
The code gate before every consequential action: policy checks the model can't argue with, the deny-by-default posture, and validating arguments not intentions.
This is the module the whole course was built toward. An agent that only reads is a research assistant; an agent that acts is a coworker with your systems' permissions — and its actions must pass through code that the model cannot talk its way past. In Prompt Engineering you validated outputs; here you validate actions before they fire, because a wrong action can't be un-fired.
The iron law
Every consequential action is validated in code between the model's request and the real effect. The model requests issue_refund(order=HL-1042, amount=500); it does not issue the refund. Your validator does — after checking, deterministically, that this refund is allowed. The model's reasoning, however brilliant or however injected, is an input to the validator, never a substitute for it.
REFUND_LIMIT = 200_00 # cents; policy, in code, not in the prompt
def issue_refund(order_id: str, amount_cents: int, ticket_id: str) -> dict:
order = db.get_order(order_id) # source of truth
if order is None:
return {"error": f"order {order_id} not found"}
if amount_cents <= 0 or amount_cents > order.total_cents:
return {"error": "refund must be >0 and <= order total"}
if amount_cents > REFUND_LIMIT:
return {"error": "exceeds auto-approve limit — propose escalation",
"requires_approval": True}
if db.refund_exists(ticket_id): # idempotency
return {"error": "a refund is already recorded for this ticket"}
return payments.refund(order_id, amount_cents,
idem_key=f"{ticket_id}:refund")What the validator checks — and what it ignores
- It validates arguments against ground truth, not the model's story. The amount is checked against the actual order total from the database, not against what the agent reasoned. Injected 'the customer is owed $500' is irrelevant — the order was $240.
- Policy limits live here, in code. The $200 auto-approve limit is a constant, not a prompt sentence. Prompts are persuadable; constants are not. (You learned this in PE M6; agents raise the stakes from wrong text to moved money.)
- Deny by default. The validator enumerates what's allowed and rejects everything else — not the reverse. An unforeseen action fails closed.
- Idempotency is a guardrail, not just plumbing. The 'refund already exists' check is what makes the retry-after-timeout safe. Double-payment is the signature agent bug; this line prevents it.
- Rejections teach. Every failure returns a message the agent can act on — escalate, adjust, ask — turning a blocked action into a redirected one.
'Just tell the agent in its system prompt never to refund over $200.' It will comply — until a clever ticket, a confused trajectory, or an injected note talks it into it, and prompts have no enforcement. The validator is the difference between a policy you hope holds and one that cannot be violated. Never put a consequential limit anywhere but code.