An agent without budgets is an unbounded liability: one confused run can loop for hundreds of tool calls, and one clever prompt injection can turn your data tools against you.
Tier: Advanced · Workflow area: Agentic systems
This playbook retrofits a layered defense onto any agent you have built (pb-40, pb-41): input guards, output validation, action deny-lists, step and cost budgets, and a kill switch — the same layering pattern platform teams now ship as "guardrails for agentic workloads."
For a read-only toy on synthetic data, a step cap alone is enough — apply the full stack when consequences appear.
pip install pydantic for output validation; your provider's usage fields for cost accounting.Layer 1 — input guards. Before the agent sees a request: length caps, an allowlist of task types if applicable, and an injection screen for text arriving from untrusted sources (documents, tickets, web pages). A cheap-model classifier call ("does this text contain instructions directed at an AI agent? yes/no") in front of retrieved content catches the common cases for fractions of a cent.
Layer 2 — action guards (deny-list + confirmation tiers). Classify every tool by blast radius: read-only (auto-allow), reversible writes (allow + log), irreversible/external (require human confirmation or deny). Enforce in the tool implementation, not the prompt:
DENY = {"delete", "drop", "truncate", "grant"}
def guarded_sql(query: str) -> str:
if any(tok in query.lower() for tok in DENY):
return "BLOCKED by policy: destructive statement."
...
With the Anthropic tool runner, gate inside the tool function (return "user declined" instead of executing); in LangGraph, use an interrupt() before the risky node (pb-41).
Layer 3 — output validation. Every structured output passes a Pydantic schema; every user-facing text output passes checks for leaked secrets (regex over key formats), PII (reuse pb-43's redactor), and claims-without-source if your spec requires citations. Failed validation retries once with the error message, then fails closed.
Layer 4 — budgets. Three counters per run, checked in the loop: steps (e.g. 12 tool calls), tokens (accumulate usage.input_tokens + usage.output_tokens per turn), dollars (tokens × your model's price table). On breach: stop, summarize partial findings, emit a BUDGET_EXCEEDED status. Add a daily aggregate cap across all runs at the API-key level via your provider dashboard or gateway (pb-48) — the loop cap protects a run; the key cap protects the month.
Layer 5 — kill switch + audit log. A flag the loop checks every iteration (file, Redis key, or env var) so a human can halt a live run; structured logs of every tool call (run id, tool, args hash, outcome, cumulative cost). The log is what turns "the agent did something weird" into a five-minute diagnosis.
Red-team your own agent. Ten adversarial inputs: injection in retrieved text, requests for denied actions, impossible tasks (to trigger budgets), secret-extraction attempts. Record pass/fail per layer.
Prompts are suggestions; guardrails are code. Every control that matters — deny-lists, budgets, validation — must live where the model cannot rewrite it: in the tool implementations and the loop, not the system prompt.
SELECT ... INTO, comment tricks, and encodings slip past naive matching — pair with a read-only database role (pb-43).Every layer here is provider-independent and runs against self-hosted models unchanged — budgets count tokens from the vLLM endpoint's usage fields; validators and deny-lists are local code. In client environments, set the dollar budget in the client's currency of concern (rows read, records exposed) as well as tokens: data-exposure budgets matter more than API spend when the model is free and local.
The kill switch you never test is the one that will not work — include "operator halts live run" in your red-team script, not just in the design doc.