Chapter 6 ended on an uncomfortable result. Once a model reads tainted content, everything it emits for the rest of the session is tainted, because its context now contains the attacker's text and every subsequent token is conditioned on it.
Tracking that spread is honest and not much help. The structural answer is to stop the spread instead: keep the tainted text out of the model that decides things.
Split the work between a planner and a quarantine.
The planner holds the conversation, decides what to do, and calls tools. It sees your system prompt, the authenticated user's turn, and typed results. It never sees a retrieved document, a fetched page, or an uploaded file.
The quarantine reads untrusted content. It has no tools. It cannot call anything, reach anything, or take any action. Its only output is a value conforming to a schema the planner asked for.
The technique comes from Google DeepMind's CaMeL, published as Defeating Prompt Injections by Design. Their terms are the privileged LLM and the quarantined LLM, and their central property is worth quoting because it is stricter than what most implementations attempt: at no point is content handled by the quarantined model exposed to the privileged one. The quarantined model populates references, and the planner passes those references around without ever seeing what they contain.
Willison's earlier dual-LLM pattern is the ancestor. His own assessment of CaMeL, when it appeared, was that it offered a promising direction. Promising is the right word and it is not the same as solved.
The boundary between the two is a schema, and the schema is the whole security mechanism.
public interface IQuarantinedReader
{
Task<T> ExtractAsync<T>(Tagged<string> untrusted, CancellationToken ct)
where T : notnull;
}
public sealed record RefundRequest(decimal Amount, string Currency, string OrderId);The planner asks for a RefundRequest. What comes back is three fields with three types. The document that produced them is never in the planner's context.
Consider what an attacker can do through that interface. They control a PDF. The quarantine reads it. The most they can influence is the value of a decimal, a three-letter currency code, and an order identifier. There is no field in RefundRequest capable of carrying "ignore previous instructions", because a decimal does not have a field for prose.
This is the property to internalise. Quarantine does not make the content safe, it removes the content's ability to be instruction-shaped by the time it reaches the component that acts. The attacker's text cannot become a directive because there is no longer any text.
A dangerous misreading is available here and it is worth closing off directly.
The attacker still controls the values. They wrote the PDF, so they chose the amount. The quarantine faithfully extracted 2,400 because 2,400 is what the document said. Nothing was prevented.
What changed is the class of the attack. Before quarantine, an attacker could make the planner do anything the planner was capable of, including calling tools the task never needed. After quarantine, an attacker can only supply wrong values to the specific operation the planner had already decided to perform.
That is a large reduction and it is not elimination, which is why the extracted values stay tainted and why chapter 9's gate still refuses tainted arguments on irreversible actions. The two primitives are doing different jobs. Quarantine constrains the shape of attacker influence. Provenance and the gate constrain what may be done with it.
Often the planner does not need the value at all, only the ability to move it around.
public sealed record Ref(string Id);
var summary = await _quarantine.SummariseToRefAsync(document, ct); // Ref("doc-1")
await _mail.SendAsync(to: recipient, bodyRef: summary, ct);The planner decides that the summary should be sent to the recipient. It never learns what the summary says. The mail tool resolves the reference at the point of sending, outside the planner's context.
This is CaMeL's mechanism and it generalises well. Any time the planner is routing rather than reasoning about content, pass a reference. The context window stays clean and the attacker's text never enters the component holding the tools.
Three ways this fails in practice, all of them worth designing against explicitly.
Free-text fields in the schema. A RefundRequest with a string Reason re-opens everything. The attacker writes their instruction into Reason, the planner reads the field while composing its next step, and the quarantine has been defeated by a single convenient field. Where a schema genuinely needs prose, that field is a reference, never a value.
Error messages. The quarantine fails to parse, and the exception helpfully includes the offending input. That exception reaches the planner's context. An attacker who can force a parse failure has a channel straight through the boundary, and this one is easy to ship by accident.
Enum abuse. A constrained field still carries a few bits. An attacker choosing between five valid categories is signalling, and across enough calls that is a covert channel. Low bandwidth, real, and mostly acceptable. Know it exists before someone finds it for you.
Chapter 2 left an attack running. A recruitment assistant reads applications; an applicant hides one sentence in white four-point text instructing it to mail the shortlist and internal scoring notes to an address in the contact block.
Without quarantine. The screening model reads the whole PDF, because reading the whole PDF is the task. The hidden sentence enters its context alongside the system prompt. It holds a mail tool, because filing and forwarding notes is what it was built for. It composes a message and sends it. Every component behaved as designed.
With quarantine. The planner asks for a CandidateSummary, declared as a name, a years-of-experience integer, a list of skills drawn from a fixed vocabulary, and a reference to a prose summary it will never read. The quarantine reads the PDF, including the hidden sentence, and returns those four things. There is no field in which the instruction can survive. The planner files the note and the attack is simply absent from the system, not detected and not blocked.
The attacker still influenced the outcome. They chose the skills list, and could have inflated the experience figure. That is the residual risk and it is a recruitment problem rather than a security incident.
Download the full PDF for free?
Free download — no account required