Repository navigation
feat(orchestrator): repeat each eval K times via a pluggable attacker - #184
Open
saichandrapandraju wants to merge 1 commit into
Open
saichandrapandraju wants to merge 1 commit into
saichandrapandraju wants to merge 1 commit into
Conversation
Introduce the attacker concept into core MiDojo and run each (user_task x injection_task) pair for K attempts, a fresh ephemeral sandbox per attempt. This is the first step of the adaptive attacker-model workstream: this change lands the loop; the model-driven attacker that reads earlier attempts and refines the payload builds on it. - `midojo.attacks.attacker`: the `Attacker` protocol (`async propose(goal, history) -> AttackAttempt`), `AttemptOutcome`, and `StaticAttacker`, which proposes the suite's injection on every attempt. It lives in the attack library beside the techniques and payload sets it draws on. `propose` is async so a model-driven attacker slots in without reshaping the loop; no objective source exists yet, so the loop passes `goal=None`. - `-k/--attempts` (default 1) drives `_run_pair_attempts`. Every attempt runs, and the pair's verdicts come from a representative attempt (the first that broke, else the last), so a break on any attempt counts and the Security percentage stays the attack success rate. At K>1 the Utility and Security cells add a `(count/total)` tally of attempts that completed the task and broke the agent. Stopping at the first break is left to the adaptive attacker that hunts for one. - `results.json` gains `attempts`, per-pair `metrics` (raw counts) and the `attempt_log` only at K>1, so a default run writes the historical shape. The `AttemptOutcome` records are both the metrics source and the history an adaptive attacker will read. - README documents `--attempts`; AGENTS.md lists the Attacker concept. With `StaticAttacker`, K>1 shows how often an injection breaks the agent under the agent's own stochasticity. Validated E2E on the weather suite over the OpenShell gateway. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: saichandrapandraju <saichandrapandraju@gmail.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
First step of the adaptive attacker workstream. This adds the attacker concept to core MiDojo and an attempt loop that runs each (user task × injection task) pair K times, each attempt in a fresh sandbox. A follow-up PR adds a model-driven attacker that reads earlier attempts and refines its payload. It plugs into this loop and doesn't depend on the channel/injection-plan work.
With the static attacker this already works on its own:
-k Kshows how often a fixed injection breaks the agent across K independent runs (the agent's own stochasticity).What changes
-k/--attempts(default1). With the default, the console output andresults.jsonare exactly what they were before.midojo.attacks.attacker: theAttackerprotocol (async propose(goal, history) -> AttackAttempt),AttemptOutcome, andStaticAttacker, which proposes the suite's injection unchanged on every attempt. The orchestrator loop is written against the protocol, so the model-driven attacker slots in without changing the loop.(count/total)tally of attempts that completed the task and broke the agent.results.jsongainsattempts, per-pairmetrics(raw counts:attempts_run,attempts_to_break,delivered,successes) and anattempt_logwith oneAttemptOutcomeper attempt, only at K>1. The attempt log is also the history the adaptive attacker will read.E2E validation
weathersuite, one pairing, 3 attempts. The injection reached the agent every time (injection in get_weather) and the agent resisted every time. Rawmidojo-runoutput:The run also needs the model settings from the weather Quick Start (
LITELLM_API_HOST,LITELLM_MODEL, ...), passed through the environment. The new part ofresults.json:attempt_logholds the threeAttemptOutcomerecords (payload, where it was delivered, whether it broke, the agent's output, the eval ID).Testing
tests/test_attacker.pycovers the static attacker, the metrics, and the result cells, including that a single-attempt run renders exactly as before and that N/A stays plain.uv run pytestpasses (309 tests, withtests/test_langchain_sdk.pyskipped locally for its missinglangchaindependency).🤖 Generated with Claude Code