Documentation

Benchmark

Tiếng Việt: benchmark.vi.md.

This page compares Guard Spec's detector with other open prompt-injection detectors on Guard Spec's own eval set and on public datasets. Everything on it comes from one command, with every dataset and model pinned, so anyone can rerun it and check the numbers.

It is a measurement and nothing else. No threshold, weight or rule of Guard Spec was tuned on these datasets or on eval/ for this page, and the other detectors run with their published defaults.

Summary

Run on 2026-11-07 (19:30 UTC on 2026-11-06) at commit 2e182a0 (v0.2.1) with this benchmark's harness, on an Apple M3 Max laptop (macOS 15.4.1); the full tables are under Results. Default decisions, all languages:

Dataset Guard Spec L1+L2+L3: AUC / TPR / FPR ProtectAI v2: AUC / TPR / FPR LLM Guard default: TPR / FPR
eval/ (EN + VI, 2,176 rows) 0.987 / 95.0% / 1.4% 0.784 / 88.6% / 50.6% 83.0% / 45.6%
deepset test (116, half German/Spanish) 0.963 / 33.3% / 0.0% 0.901 / 36.7% / 0.0% 31.7% / 0.0%
jailbreak-classification test (262) 0.994 / 92.1% / 0.0% 0.979 / 84.2% / 1.6% 84.2% / 1.6%
Gandalf test (112, attacks only) - / 100% / - - / 100% / - 100% / -
SPML sample (2,000) 0.974 / 36.4% / 0.0% 0.997 / 99.2% / 8.4% 99.1% / 6.6%

Reproduce

make benchmark

That target fetches ONNX Runtime and libtokenizers (make onnx-deps, pinned by SHA-256), runs bench/run.py with the Python packages locked in bench/uv.lock (needs uv), and writes:

What it does:

  1. Downloads the public datasets at the pinned Hugging Face commits and converts them to the eval/ row schema.
  2. Pulls the models with go run ./cmd/models pull, which checks every file against the SHA-256 values in models/manifest.yaml: guardspec-classifier@v3b, multilingual-e5-small@614241f and protectai-deberta-v3-base-pi-v2@90c9989.
  3. Runs Guard Spec through cmd/eval -scores (built with -tags onnx), which is the gateway's input path: sanitizer, detector layers, fusion and the /v1/scan block rule.
  4. Runs the ProtectAI model in ONNX Runtime from Python.
  5. Computes the metrics below for every detector, dataset and language.

Options:

Variable Default Effect
BENCH_CACHE bench/.cache Downloads, converted datasets, per-row scores. About 1.2 GB (ProtectAI's ONNX file is 739 MB). The run stops when less than 6 GB is free there.
MODEL_DIR $BENCH_CACHE/models Use already pulled Guard Spec models (<dir>/cache/<name>/<version>) instead of pulling them.
ONNX_DIR .onnx Where make onnx-deps puts the native libraries.
PROMPTGUARD=1 off Also run Meta Prompt Guard 2, see below.

GitHub Actions: the Benchmark workflow (.github/workflows/benchmark.yml) runs make benchmark on ubuntu-24.04 when started by hand (Actions > Benchmark > Run workflow); it never runs on its own. It writes the tables to the job summary and uploads bench/results/ and this page as the benchmark-results artifact.

Including Meta Prompt Guard 2

meta-llama/Llama-Prompt-Guard-2-86M and -22M are gated on Hugging Face under the Llama 4 Community License: they cannot be downloaded without an account that accepted the terms, so they are not in the published tables. With access:

  1. Accept the license on both model pages.
  2. Export a read-only token of that account as HF_TOKEN in your shell (huggingface_hub reads it; bench/run.py never reads, prints or stores it).
  3. Run make benchmark PROMPTGUARD=1. This installs the promptguard extra (PyTorch and Transformers, CPU) and adds two detector rows, at the pinned revisions a8ded8e (86M) and 11614a1 (22M), with the model's argmax decision (malicious probability ≥ 0.5), the first 512 tokens, one thread.

In the workflow, add the token as the repository secret HF_READ_TOKEN and check "promptguard" when starting the run.

What is measured

For every detector × dataset × language:

Datasets without benign rows (Gandalf) only have a TPR.

Datasets and licenses

Every dataset was checked on its Hugging Face card at the pinned commit. The benchmark downloads them at run time; no row from them is stored in this repository beyond what eval/ already contains (see eval/LICENSES.md).

Dataset License Used Why
eval/ (Guard Spec) Apache-2.0 (project rows), Apache-2.0 / MIT (sourced rows) yes Our published EN/VI set.
deepset/prompt-injections Apache-2.0 yes, test Standard small benchmark; mixed English and German/Spanish.
jackhhao/jailbreak-classification Apache-2.0 yes, balanced test Jailbreak prompts versus benign prompts.
Lakera/gandalf_ignore_instructions MIT yes, test Real attacks from the Gandalf game (attacks only).
reshabhs/SPML_Chatbot_Prompt_Injection MIT yes, 1,000 rows per label, seeded sample Chatbot-specific injections; no detector here trained on it.
xTRam1/safe-guard-prompt-injection none on the card (revision a3a877d608f3) no No license.
Lakera/mosscap_prompt_injection MIT no Unlabelled: every prompt sent to the game, attack or not.
jayavibhav/prompt-injection none on the card no No license.

Row counts and languages per run are in the generated tables below.

Vietnamese. We found no public, licensed Vietnamese prompt-injection dataset. The only Vietnamese rows here are those of eval/, which are synthetic and labelled by AI agents (see eval/README.md).

Language column. eval/ carries a language per row (en, vi). For the public sets, rows with German, Spanish or Cyrillic markers (the rule of eval/build/build.py) are other, the rest en. Only deepset has a sizeable other share.

Overlap with training data. Read the public rows with this in mind:

Detectors

Detector Included Notes
Guard Spec L1 yes Rules only, the default build without models (cmd/eval -detector fusion).
Guard Spec L1+L2 yes Rules + classifier v3b (-layers l1,l2).
Guard Spec L1+L2+L3 yes Rules + classifier + embeddings, the default with models (-layers l1,l2,l3).
ProtectAI deberta-v3-base-prompt-injection-v2 yes Apache-2.0. The published fp32 ONNX export, first 512 tokens, flags when P(INJECTION) ≥ 0.5 (argmax of the model card's pipeline).
LLM Guard PromptInjection as a threshold of the ProtectAI row llm-guard 0.3.16 (MIT, archived) wraps the same model: its default pins revision 89b085c, whose weights are byte-identical to 90c9989 (same SHA-256), reads the first 512 tokens (match_type=FULL) and flags when round(score, 2) > 0.92. Installing it would only rerun the same model, so its row reuses the ProtectAI scores with that rule: same AUC, different TPR/FPR. Its default runs the model in PyTorch rather than ONNX, so latency is not reported separately.
Meta Llama Prompt Guard 2 (86M, 22M) no (gated) Llama 4 Community License, gated on Hugging Face; see Including Meta Prompt Guard 2.

Results

Run 2026-11-06 19:30 UTC, commit 2e182a03bc62, local: Apple M3 Max, 14 logical CPUs, 36 GB RAM, macOS 15.4.1 (arm64). go1.26.10, ONNX Runtime 1.31.1 (Go) / 1.31.1 (Python), Python 3.12.0.

Datasets

Dataset Revision License Split Rows (attack / benign) Languages
Guard Spec eval set (eval/) this commit Apache-2.0 (project rows); Apache-2.0 / MIT for the deepset / Gandalf rows it includes eval/data/en.jsonl, vi.jsonl 2176 (1171 / 1005) en 926, vi 1250
deepset/prompt-injections 4f61ecb038e9 Apache-2.0 test 116 (60 / 56) en 60, other 56
jackhhao/jailbreak-classification 2f2ceeb39658 Apache-2.0 balanced test 262 (139 / 123) en 259, other 3
Lakera/gandalf_ignore_instructions 04737b65e90a MIT test 112 (112 / 0) en 111, other 1
reshabhs/SPML_Chatbot_Prompt_Injection 02ce8084e979 MIT seeded sample, 1000 per label 2000 (1000 / 1000) en 1995, other 5

Detectors

Detector Model files Size on disk Runtime Default decision
Guard Spec L1 rules only (no model) 0 (rules) Go, ONNX Runtime (int8 models), 1 intra-op thread per session gateway /v1/scan rule: score >= 0.85, sanitizer anomaly >= 0.6, or input rejected
Guard Spec L1+L2 guardspec-classifier/v3b 119 MB Go, ONNX Runtime (int8 models), 1 intra-op thread per session gateway /v1/scan rule: score >= 0.85, sanitizer anomaly >= 0.6, or input rejected
Guard Spec L1+L2+L3 guardspec-classifier/v3b; multilingual-e5-small/614241f 237 MB Go, ONNX Runtime (int8 models), 1 intra-op thread per session gateway /v1/scan rule: score >= 0.85, sanitizer anomaly >= 0.6, or input rejected
ProtectAI v2 protectai/deberta-v3-base-prompt-injection-v2@90c9989 (onnx/model.onnx, fp32) 739 MB Python, ONNX Runtime 1.31.1 (fp32), 1 intra-op thread INJECTION probability >= 0.5 (argmax, model card pipeline)
LLM Guard PromptInjection (default) protectai/deberta-v3-base-prompt-injection-v2@90c9989 (onnx/model.onnx, fp32) 739 MB Python, ONNX Runtime 1.31.1 (fp32), 1 intra-op thread llm-guard 0.3.16 defaults: same model, match_type FULL (first 512 tokens), flag when round(score, 2) > 0.92

Not run: Meta Llama Prompt Guard 2 (86M, 22M): gated on Hugging Face (Llama 4 Community License); run with --promptguard and your own HF_TOKEN after accepting the terms.

Guard Spec eval set (eval/)

Published with this repository. L2, L3 and the fusion were never fitted on it; the L1 rules were refined on its dev half, so L1 numbers here are optimistic (eval/README.md).

Detector Lang n (att / ben) ROC AUC [95% CI] TPR @ default FPR @ default TPR @ FPR ≤ 1% p50 ms p95 ms
Guard Spec L1 all 1171 / 1005 0.952 [0.943, 0.962] 89.2% 0.1% 91.0% 0.11 0.22
Guard Spec L1 en 562 / 364 0.929 [0.911, 0.945] 84.2% 0.3% 86.7% 0.11 0.21
Guard Spec L1 vi 609 / 641 0.975 [0.964, 0.984] 93.9% 0.0% 94.9% 0.11 0.23
Guard Spec L1+L2 all 1171 / 1005 0.987 [0.982, 0.992] 94.9% 1.4% 93.3% 6.02 22.01
Guard Spec L1+L2 en 562 / 364 0.982 [0.973, 0.989] 94.0% 1.6% 90.4% 5.49 19.73
Guard Spec L1+L2 vi 609 / 641 0.991 [0.984, 0.996] 95.7% 1.2% 94.9% 6.51 23.95
Guard Spec L1+L2+L3 all 1171 / 1005 0.987 [0.981, 0.991] 95.0% 1.4% 93.4% 6.47 21.90
Guard Spec L1+L2+L3 en 562 / 364 0.980 [0.970, 0.988] 94.1% 1.6% 90.9% 6.10 19.62
Guard Spec L1+L2+L3 vi 609 / 641 0.992 [0.985, 0.997] 95.7% 1.2% 94.7% 6.91 23.46
ProtectAI v2 all 1171 / 1005 0.784 [0.764, 0.802] 88.6% 50.6% 26.2% 66.89 237.54
ProtectAI v2 en 562 / 364 0.897 [0.877, 0.917] 86.8% 23.4% 35.1% 46.41 169.34
ProtectAI v2 vi 609 / 641 0.661 [0.632, 0.690] 90.1% 66.1% 5.9% 76.69 262.98
LLM Guard PromptInjection (default) all 1171 / 1005 0.784 [0.764, 0.802] 83.0% 45.6% 26.2% same model as ProtectAI v2
LLM Guard PromptInjection (default) en 562 / 364 0.897 [0.877, 0.917] 84.0% 20.3% 35.1% same model as ProtectAI v2
LLM Guard PromptInjection (default) vi 609 / 641 0.661 [0.632, 0.690] 82.1% 59.9% 5.9% same model as ProtectAI v2

deepset/prompt-injections

About half the rows are German or Spanish (lang 'other'). The English test rows are also in eval/; Guard Spec's classifier was trained on the train split.

Detector Lang n (att / ben) ROC AUC [95% CI] TPR @ default FPR @ default TPR @ FPR ≤ 1% p50 ms p95 ms
Guard Spec L1 all 60 / 56 0.757 [0.674, 0.835] 13.3% 0.0% 28.3% 0.10 0.26
Guard Spec L1 en 31 / 29 0.877 [0.777, 0.953] 22.6% 0.0% 45.2% 0.10 0.21
Guard Spec L1 other 29 / 27 0.710 [0.621, 0.799] 3.4% 0.0% 24.1% 0.10 0.38
Guard Spec L1+L2 all 60 / 56 0.955 [0.913, 0.987] 35.0% 0.0% 88.3% 4.71 18.23
Guard Spec L1+L2 en 31 / 29 0.958 [0.883, 1.000] 45.2% 0.0% 90.3% 4.33 14.94
Guard Spec L1+L2 other 29 / 27 0.971 [0.930, 0.997] 24.1% 0.0% 86.2% 4.74 27.29
Guard Spec L1+L2+L3 all 60 / 56 0.963 [0.921, 0.991] 33.3% 0.0% 83.3% 5.42 18.53
Guard Spec L1+L2+L3 en 31 / 29 0.973 [0.914, 1.000] 45.2% 0.0% 93.5% 5.14 15.54
Guard Spec L1+L2+L3 other 29 / 27 0.941 [0.874, 0.989] 20.7% 0.0% 82.8% 5.73 27.70
ProtectAI v2 all 60 / 56 0.901 [0.839, 0.951] 36.7% 0.0% 53.3% 45.10 196.72
ProtectAI v2 en 31 / 29 0.889 [0.796, 0.959] 25.8% 0.0% 71.0% 39.97 123.84
ProtectAI v2 other 29 / 27 0.936 [0.866, 0.985] 48.3% 0.0% 58.6% 53.32 267.84
LLM Guard PromptInjection (default) all 60 / 56 0.901 [0.839, 0.951] 31.7% 0.0% 53.3% same model as ProtectAI v2
LLM Guard PromptInjection (default) en 31 / 29 0.889 [0.796, 0.959] 22.6% 0.0% 71.0% same model as ProtectAI v2
LLM Guard PromptInjection (default) other 29 / 27 0.936 [0.866, 0.985] 41.4% 0.0% 58.6% same model as ProtectAI v2

jackhhao/jailbreak-classification

Jailbreak (role-play) prompts versus benign prompts. Guard Spec's classifier was trained on the balanced train split; ProtectAI v2 lists this dataset among its training sources.

Detector Lang n (att / ben) ROC AUC [95% CI] TPR @ default FPR @ default TPR @ FPR ≤ 1% p50 ms p95 ms
Guard Spec L1 all 139 / 123 0.961 [0.942, 0.977] 66.9% 0.0% 84.9% 0.43 2.99
Guard Spec L1+L2 all 139 / 123 0.993 [0.984, 0.999] 92.1% 0.0% 93.5% 42.14 507.41
Guard Spec L1+L2+L3 all 139 / 123 0.994 [0.986, 0.999] 92.1% 0.0% 93.5% 41.87 511.62
ProtectAI v2 all 139 / 123 0.979 [0.956, 0.995] 84.2% 1.6% 69.8% 327.75 1161.58
LLM Guard PromptInjection (default) all 139 / 123 0.979 [0.956, 0.995] 84.2% 1.6% 69.8% same model as ProtectAI v2

Lakera/gandalf_ignore_instructions

Attacks only (no benign rows): only TPR is defined. The English test rows are also in eval/; Guard Spec's classifier was trained on the train split.

Detector Lang n (att / ben) ROC AUC [95% CI] TPR @ default FPR @ default TPR @ FPR ≤ 1% p50 ms p95 ms
Guard Spec L1 all 112 / 0 - 77.7% - - 0.10 0.19
Guard Spec L1+L2 all 112 / 0 - 100.0% - - 3.90 7.13
Guard Spec L1+L2+L3 all 112 / 0 - 100.0% - - 4.87 8.22
ProtectAI v2 all 112 / 0 - 100.0% - - 32.18 62.98
LLM Guard PromptInjection (default) all 112 / 0 - 100.0% - - same model as ProtectAI v2

reshabhs/SPML_Chatbot_Prompt_Injection

User prompts only. Labels depend on the chatbot's system prompt (a request that breaks that bot's rules is an attack), which an input detector does not see. No detector here lists it as training data.

Detector Lang n (att / ben) ROC AUC [95% CI] TPR @ default FPR @ default TPR @ FPR ≤ 1% p50 ms p95 ms
Guard Spec L1 all 1000 / 1000 0.961 [0.952, 0.969] 26.9% 0.2% 80.7% 0.13 0.93
Guard Spec L1+L2 all 1000 / 1000 0.979 [0.973, 0.985] 37.0% 0.0% 89.2% 7.03 130.35
Guard Spec L1+L2+L3 all 1000 / 1000 0.974 [0.967, 0.980] 36.4% 0.0% 84.5% 8.28 156.34
ProtectAI v2 all 1000 / 1000 0.997 [0.995, 0.999] 99.2% 8.4% 97.6% 61.24 631.59
LLM Guard PromptInjection (default) all 1000 / 1000 0.997 [0.995, 0.999] 99.1% 6.6% 97.6% same model as ProtectAI v2

Discussion

Where Guard Spec does worse.

Where it does better, and why that is partly expected.

Other notes.

The results will also appear on guardspec.io/docs, which is built from this page.

Source: docs/benchmark.md @ f26302340bf1