AgentDojo harness
Benchmarking the engine against prompt injection.
The harness-agentdojo Python package runs OpenAPPA as a tool-call defense inside AgentDojo, the prompt-injection benchmark.
The key property: the policy never reads the injected text. It tracks which sources the conversation's context came from (a per-turn label fold) and blocks tool calls whose contract the folded context cannot satisfy.
Layout#
appa-agent-python— the PyO3 adapter that keeps each dispatch handle in Rust and owns the complete check, execution, and outcome-admission transaction.harness-agentdojo/src/appa_dojo/defense.py— a drop-in replacement for AgentDojo'sToolsExecutor. Allowed calls execute through a capability-scoped loopback bridge; blocked calls return on the normal tool-error channel and never execute.harness-agentdojo/src/appa_dojo/contracts/workspace.toml— the packaged policy data. Every suite tool is labeled by its source type, never by whether a given result actually carries an injection — that is the benchmark's ground truth, and peeking is cheating. Readers of third-party text aresuspicious, pure-state readerstrusted, sinks require atrustedcontext.appa-dojo-sidecar— the retained JSON-lines compatibility path; benchmark pipelines use PyO3 by default.
Running the benchmark#
uv sync
# Compare a defended and an undefended pipeline via OpenRouter:
uv run appa-dojo bench --model openai/gpt-4o-mini-2024-07-18 --defense appa
uv run appa-dojo bench --model openai/gpt-4o-mini-2024-07-18 --defense none
bench prints clean utility, utility under attack, attack success rate, and the number of policy-blocked calls. Per-episode JSON, including full message logs, lands under --logdir.
Narrowing a run#
uv run appa-dojo bench --model openai/gpt-4o-mini-2024-07-18 \
--user-tasks user_task_0 user_task_13 --injection-tasks injection_task_0 \
--attack important_instructions --logdir runs
Sharding#
AgentDojo runs one episode at a time, so a full suite is slow: the workspace suite is 40 clean + 40 × 14 attacked = 600 episodes per defense. Episodes are independent and cached, so you can split user tasks across several bench processes writing to the same --logdir — each fills different cells of the grid, cached cells are skipped, and a killed run resumes where it stopped.