2026Author
agentbounds
Ship a prompt or swap a model and you have no honest way to know whether the agent just got less safe, because LLMs fail differently every run and a single test pass tells you almost nothing.
The result
Point agentbounds at any agent and it reports per-category hold rates with the exact inputs that broke it, then fails your CI build when a change makes the agent measurably less safe.
agentbounds is an adversarial stress-tester for LLM agents, built from scratch in Python with zero runtime dependencies and 170 passing unit tests. The name is the idea: an agent is only as safe as the bounds it actually respects. agentbounds measures how often it respects them. Its suite is organized around the OWASP LLM Top 10: prompt injection, jailbreaks, secret-leakage, hallucination, robustness, and over-refusal, and it fires each attack many times rather than once.
- Framing
- OWASP LLM Top 10
- Unit tests
- 170 passing
- Scoring
- Severity-weighted
- CI
- Regression gate
Architecture
Target agent
any LLM agent under test
Adversarial suite
OWASP LLM Top 10 categories
N-trial runner
repeat each attack
Text + LLM-judge detectors
inconclusive calls excluded
Scoring
per-category hold rates, severity-weighted
Local dashboard
hold rates + breaking inputs
You aim agentbounds at an agent and it runs the OWASP-aligned suite against it. Each test is fired repeatedly rather than once, so a rare failure does not hide behind a lucky pass. A mix of LLM judges and clearly-labeled text heuristics classifies every response as held, broken, or inconclusive. Those results roll up into per-category hold rates and a single severity-weighted risk score, browsable in a local dashboard alongside the precise inputs that caused each break. The same numbers feed a CI gate, so a regression trips the build instead of reaching production quietly.
Key decisions and trade-offs
Hold rates over N trials, not a single pass or fail
LLM output is nondeterministic, so one verdict is noise. Running each attack many times and reporting the fraction the agent holds turns a coin flip into a measurement you can compare across prompt and model changes, and it exposes intermittent failures that a single pass would paper over.
Inconclusive judge calls leave the denominator
When the judge itself is unsure, counting that trial as either a pass or a failure would fabricate confidence the tool does not have. agentbounds excludes inconclusive calls from the rate entirely and labels heuristic detectors as heuristic, so the reported number means exactly what it says: the share of the trials that were actually decided, with the undecided ones left out of the arithmetic instead of rounded in either direction.
A severity-weighted score, not a raw count
A leaked secret and a mild over-refusal are not the same failure, and averaging them flat would let low-stakes wins mask a dangerous one. Weighting by severity keeps the headline number aligned with real risk rather than test volume.
A CI regression gate mapped to the OWASP LLM Top 10
Safety that is only ever checked by hand drifts. Gating the build on the same category hold rates (injection, jailbreaks, secret-leakage, hallucination, robustness, over-refusal) makes an agent getting less safe a failing check a reviewer sees on the PR, not a surprise found in production.
What other tools do better
promptfoo has far broader attack coverage than this suite. overstep does authorization testing properly, against the server's own enforcement. egress-guard stops secrets leaving on the network path. Those three are named in the README rather than worked around, because the useful question for anyone choosing a tool is which one covers their risk, and a comparison that lists only strengths is not a comparison. What is left to agentbounds is the measurement discipline: rates instead of verdicts, an explicit undecided bucket, and a gate that fails on a drop.
Saying what is not built yet
The shipped surface is adversarial prompts, text and LLM-judge detectors, per-category hold rates, a severity-weighted risk score, and a CI regression gate. The effect-assertion layer, which would check what an agent actually did rather than what it said back, is still being built. Neither the README nor this page counts it as done.
Results
- 170 passing unit tests.
- An adversarial suite spanning the six OWASP LLM Top 10 categories: prompt injection, jailbreaks, secret-leakage, hallucination, robustness, and over-refusal.
- Per-category hold rates reported over repeated trials, with the exact breaking inputs.
- Severity-weighted risk score.
- CI regression gate that fails a build when an agent gets measurably less safe.
- A local dashboard for browsing a run.
- A gap rather than a result: the effect-assertion layer is still being built.
Inside the repo
The repo is private for now, so here is the real layout. Every module and count below is taken straight from the source.
agentbounds/
- attacks.pyThe adversarial inputs and the built-in library of them: each attack bundles a prompt to send with how a failure is recognized.
- targets.pyThe works-on-any-agent seam: the thin interface that lets agentbounds point at a local function, an HTTP endpoint, or a hosted agent.
- detectors.pyThe honest heart: how it decides whether an agent actually failed an attack, rather than assuming it did.
- runner.pyRuns the suite against a target over N trials, which is how it handles the nondeterminism problem instead of pretending one sample is a verdict.
- report.pyTurns raw results into the real deliverable: a readable reliability report with the exact inputs that broke the agent.
- compare.pyDiffs two runs to catch reliability regressions, which is the feature that makes this a CI gate and not a one-off audit.
- history.pyThe longitudinal view: reliability tracked across many runs over time.
- scorecard.pyRenders a run as a self-contained SVG scorecard, so someone browsing the repo sees the result without running anything.
- badge.pyA shields-style SVG badge generated from a run, for the README.
- target_config.pyBuilds an HTTP target from a declarative JSON config, so testing a deployed agent needs no code.
- io_formats.pyLoad attacks from a file and export results as JSON, the two extensibility seams.
- cli.pyThe command-line entry point; compare_cli.py is its counterpart for diffing two saved runs.
- web.pyA local dashboard for browsing a run.
How it fits together
Read attacks.py and detectors.py together: an attack is only meaningful next to the rule that decides whether it landed, and detectors.py is where the honesty of the whole tool lives. targets.py is the adapter that lets any agent be tested, runner.py executes the suite over repeated trials, and report.py turns that into something a human reads. compare.py plus history.py are what turn a single audit into a regression gate you can fail a build on.
- Tests
- 170 passing unit tests across six test modules in tests/
- Stack
- Python, zero runtime dependencies