Defenses, Part 1: Filtering and Structured Prompting
Back to Aria, the Acme support bot, and the one attack from lesson 2 that actually worked: the fake "SYSTEM OVERRIDE: debug mode" request that got the model to print its full system prompt, secret discount code included. Two text-level defenses, tested against that exact payload:
- Sandwiching — repeat the critical instruction again after the untrusted text, so it's the last thing the model reads before responding.
- Delimiting — wrap untrusted input in explicit tags (
<untrusted>...</untrusted>) and tell the model never to treat tagged content as instructions.
Predict before you look
Same attack, same model, two different defenses. Before you look: do you expect both defenses to stop the leak, or do you think one might fail?
This is the honest result, not the flattering one: sandwiching didn't work — the model still printed the full system prompt, secret code and all, even with a reminder tacked on the end. Delimiting did work — wrapping the same attack text in <untrusted> tags and explicitly telling the model never to treat tagged content as instructions was enough to get a refusal.
Why the difference? Sandwiching adds more text for the model to weigh against the attack, but it's still just more tokens in the same undifferentiated stream from lesson 1 — the model has to out-reason the attack using judgment alone. Delimiting adds structure: a signal the model can key on regardless of what's inside the tags, closer to a real boundary than a persuasive argument. Neither is bulletproof against every attack, and results like these vary by model and by exact phrasing — which is precisely why defense-in-depth (lesson 6) matters more than any single trick.
Both defenses, plus a naive keyword filter, as tested against the underlying prompt-building functions:
In the widget above, why did sandwiching fail while delimiting succeeded against the same attack?