A Prompt Is a Document With Regions of Unequal Authority
By the time a model sees a prompt, it is a single string. Everything that went into it, the directives you wrote months ago, the records your retrieval layer pulled a few milliseconds ago, the text a user typed, the demonstrations you curated, the specification of the output you expect back, has been flattened into one undifferentiated run of characters. But those parts are not equal. Some are instructions the model must follow. Some are material it must analyze and must never obey. Some are patterns to imitate rather than execute. Some you wrote and trust completely; some arrived from outside your system and you should trust not at all.
The model has to work out which is which. If your prompt does not tell it, it will infer, and inference from prose is exactly where the boundary fails: where a directive buried in a retrieved document gets followed, where a claim from one source gets credited to another, where a demonstration gets treated as a task. Explicit structure is how you stop asking. Named, delimited regions convert a judgment the model was forced to make into a fact it is handed, and every judgment you take off its plate is one fewer place a failure can originate.
Concatenation destroys what your system already knew
In a system of any size, nobody writes a prompt. A prompt is composed at request time by code, from sources that differ in almost every property that matters. The standing directives are authored by your team, versioned, and reviewed. The context is fetched: rows from a datastore, chunks from an index, a payload from an upstream service, the raw text of a document a customer uploaded. The task parameters come from the caller. The demonstrations are curated artifacts with their own lifecycle. Somewhere in the middle of this, a tool returns a result and that result becomes context too, arriving after the prompt was assembled and passing through none of the checks the assembler applied.
Each of those parts has a different provenance, a different trust level, and a different intended use. Then a template concatenates them and the distinctions evaporate. What reaches the model is a wall of text in which your carefully reviewed policy and the body of an inbound support ticket sit side by side with nothing between them but a newline and the model’s willingness to guess.
Structure is the act of preserving, in the artifact the model actually reads, the distinctions your system already knows. The regions were always there. Concatenation is what erased them, and marking them is what puts them back.
A delimiter is only a boundary if the content cannot forge it
The first question about any separator is whether the material it separates could contain it. This is not a stylistic concern. It is the whole of the matter, and it is the reason plain-text separators fail in production while looking fine in a prototype.
Separators drawn from ordinary text share an alphabet with the material they are meant to bound. Tables have rules across them. Retrieved prose has headings. A pasted message has gaps in it. When the separator and the payload are made of the same stuff, telling one from the other becomes an act of interpretation, and interpretation fails where the surrounding evidence is thinnest, which is precisely where the input runs long and the sections run many. The separator that looked perfectly obvious on a page of your own writing has to survive a page of someone else’s.
Paired, named markers, tagged regions with an explicit open and an explicit close, fix this by carrying three properties that a prose separator cannot. The region is bounded, so the end is stated rather than deduced from where the next thing seems to begin. The region is named, so it announces what it is rather than merely that it exists. And the region is checkable, because well-formedness is something your assembler can verify before the call rather than something you find out about afterward. Models also read this kind of markup reliably, having been exposed to enormous quantities of it, so the structural intent is reinforced by familiarity rather than fighting it. That helps. It is the secondary argument. The primary one is that an unambiguous boundary is unambiguous whether or not the reader is cooperative.
Which leads directly to the discipline most teams skip. A boundary that the content can imitate is not a boundary. If a retrieved document happens to contain the closing marker for the region you wrapped it in, the region ends where the attacker, or simple bad luck, decided it ends, and everything after that lands in whatever context follows. This is the quoting problem, and it is as old as text processing. It has exactly the same answers here that it has everywhere else: pick delimiters your content will not plausibly contain, and escape, strip, or reject the ones it does, at assembly time, in code. That handling belongs in the layer that builds the prompt, not in an instruction asking the model to be careful. An instruction to disregard stray markers is a request. Sanitizing the payload before it is interpolated is a guarantee.
A region’s name declares what the model may do with it
A boundary tells the model where a section stops. The name tells it what the section is, and that is the more valuable half.
There is a small, natural vocabulary here, and it is not arbitrary convention. It falls out of the handful of things a model can do with a span of text. It can follow it, which is what a directive is for. It can consult it, which is what background material is for: information the task depends on but which is not itself a command and not itself the answer. It can imitate it, which is what a demonstration is for, a pattern to be matched rather than a task to be performed. And it can satisfy it, which is what a specification of the output is for. Follow, consult, imitate, satisfy. Those are the roles, and a named region is how you assign one, unambiguously, to every part of what you send.
The failure a name prevents is subtle and common. A demonstration of the desired output, dropped into a prompt without being marked as a demonstration, is a piece of text that looks exactly like a completed task. A model that treats it as context rather than as a pattern will reason about its contents. A model that treats a specification as a directive will produce the specification instead of conforming to it. These are not exotic failures. They are what happens when the only thing distinguishing a pattern from a payload is the reader’s good judgment.
A name earns its keep by saying what it holds. Label a region for its generic type and you have restated what its position already showed. Label it for the customer’s message, the retrieved policy, the prior step’s output, and you have handed the model semantics it can act on, and handed them to the next engineer who reads the template as well. The naming is cheap and it is doing real work.
Shape follows the same logic, and the shape that matters here is the one that keeps a piece of content attached to whatever identifies it. A source has an origin and a body. Left as two siblings in a flat sequence, they are two spans that happen to sit near each other, and proximity is not a relationship. Wrapped together inside one parent, they are a unit, and the model reads the body as belonging to that origin because the structure says so rather than because the layout implies it. That single pairing is what nesting is for in a prompt, and depth past what the material actually has costs tokens, buries the boundaries that mattered inside boundaries that did not, and multiplies the well-formedness you have to get right.
A shared vocabulary is what makes the prompt layer testable
Consistency in tag names is usually presented as a matter of tidiness. It is worth more than that, and the reason has little to do with the model.
The model-facing argument is real but modest: a name used the same way every time builds a stronger association with the role it plays, and a name that drifts, one label here and an abbreviation of it there, weakens the signal it was meant to carry. Fine. The system-facing argument is the one that should decide the matter. A vocabulary shared across every prompt in the fleet is what turns prompt assembly from a scattering of string templates into a layer you can reason about as a unit.
When untrusted material always lands in a region with the same name, no matter which prompt is being built, you can write one assembler that wraps it, one sanitizer that escapes its delimiters, one audit that scans the codebase for interpolation into any other region and fails the build. When every prompt invents its own words for the same roles, each of those controls has to be reimplemented per template, which means it will be implemented in some templates and not others, and the ones it was skipped in are the ones you will read about later. A convention that exists only in the model’s reading of a single prompt is a style preference. A convention enforced by the code that builds every prompt is an invariant, and invariants are the only kind of safety that survives a team growing.
This is why the vocabulary should be defined once, centrally, and why prompts should be composed through something that knows it rather than assembled by hand in whatever file happens to need a model call.
Structure moves authority from content to position
Now the reason all of this stops being hygiene and starts being security.
Put a directive and a piece of untrusted text in one flat region and you have asked the model to determine authority from content. That is the whole problem in one sentence, because content is precisely what an attacker controls. Text that says it is an instruction, text that claims the previous instructions were a test, text that impersonates the framing of your own system prompt: all of it is just characters in the same flat block as your real directives, competing on nothing but persuasiveness. And the model’s job, absent any other signal, is to read the document and work out what it is being asked to do.
Structure changes the basis of the decision. When directives live in a region designated for directives and fetched material lives in a region designated for material, authority attaches to position rather than to wording. The question is no longer whether a span sounds like an instruction. It is whether the span is in the place where instructions live. Text inside a data region can claim any authority it likes and the claim is simply not addressed to a channel that grants it.
The move is the same one that resolved injection everywhere else it has appeared. Concatenating a value into a query string means the value can become part of the query. Passing it as a bound parameter means it is data and cannot be promoted to code, no matter what it contains. Structure in a prompt is the same separation of code from data, drawn in the same place and for the same reason, and it is just as foundational.
It is also, in this setting, weaker, and being precise about how much weaker is the difference between a system that is defended and a system that believes it is.
Structure has no grammar behind it
A database driver does not weigh whether a parameter is persuasive. It has a grammar, and the parameter is not in the part of the grammar where commands can appear, so the question of what the parameter says never arises.
A model has no such grammar. It is a reader, and a very good one, and everything in its input, tags included, is text it interprets. When you mark a region as untrusted material, you are not building a wall. You are giving the model a strong, consistent, heavily reinforced signal about how to treat what is inside, and it will follow that signal with high reliability and no guarantee whatsoever. There is no theorem here. There is only a very good prior, and priors can be overcome by sufficiently well-crafted content.
Which places structure precisely: necessary, load-bearing, and insufficient on its own. It belongs inside the ordinary stack of controls that governs anything a model is allowed to touch, where sanitizing at assembly keeps untrusted content from forging the markers in the first place, a small grant of authority keeps a directive the model should not have followed from reaching anything that matters, and consequential actions are authorized in code that never sees how convincing the reasoning was. Structure earns its place in that stack. It cannot be the whole of it.
The extension that most often gets missed is temporal. Untrusted content does not only arrive at assembly time. A tool result, a fetched page, a document read mid-run, a message from another agent: all of it enters the context after the prompt was built, and all of it is exactly as untrusted as the input that arrived at the front door. Whatever discipline governs your assembler has to govern everything that enters context later, or the boundary you drew so carefully at the start of the run has a hole in it the moment the first tool returns.
Attribution is a property of the structure, not of the model
The failure that appears the instant a prompt carries more than one source is not fabrication. The model is not inventing claims. It is losing the binding between a claim and where the claim came from, and reporting a real fact against the wrong origin. An invented fact carries its own warning; it tends to read as slightly off, and a reader who knows the domain catches it. A true fact filed under the wrong source reads exactly as it should, and there is nothing in it to catch. It passes every check a human review applies, right up to the point where someone acts on it and discovers the policy they were quoting belonged to a different customer.
That binding is not something the model maintains through diligence. It exists to the extent that the structure creates it, and it decays exactly as the structure blurs. Which means attribution is an input-design problem, and it has three parts.
Every source has to arrive as a closed unit that carries its own identity next to its own content, so the label and the text it labels cannot come apart. Every source has to arrive the same way as every other, because an identified block followed by a bare one poses a question the model can only answer by inference, and the inference it usually reaches is that the bare text is a continuation of the block above. And the identity has to be stable and referenceable, a position or a name that an instruction can point at directly.
Then the instructions have to actually point at it. An instruction that gestures at the material collectively, rather than at a particular source, gives back all the precision the structure just bought. Where one step of the task draws on one source, the instruction has to say which one. Anything less leaves the model to decide, and deciding is the thing the structure was built to take away from it.
The loop closes when the identity comes back in the output, alongside each claim it belongs to. Attribution then stops being something a reviewer assesses by reading and becomes something a test asserts, which is where it belongs, because it is a property of the structure and the structure changes whenever anyone touches the assembler.
Some boundaries belong in the control flow, not in the document
Named regions and unforgeable boundaries govern the material inside a single call. There is a prior question that no amount of structure within the document can answer: whether the material belongs in a single call at all.
There is no default to fall back on here, because the shape of the reasoning picks the shape of the call. If the answer is defined over the whole set, comparison, synthesis, finding where two sources disagree, then the set has to be present, and splitting it destroys the task rather than decomposing it. If the answer is built in stages, each one consuming what the last produced, then the stages want boundaries between them.
What is worth noticing is that this is the same boundary question, asked one level up, and that the answer at this level is categorically stronger. Structure inside the document draws boundaries the model can see. Structure in the control flow draws boundaries the model cannot cross. A region marked as untrusted material is a signal the model is very likely to honor. Material that was never placed in front of the model at all is not a signal, it is a fact about the input, and no amount of adversarial text inside a retrieved document can talk its way into a call it was not included in. So the separation that must hold absolutely does not belong in the document at any level of markup discipline. It belongs in the code that decides what goes into which call, and the strongest boundary available to you is still the oldest one: the two things you cannot afford to have confused are never in the same room.
A structure you have not tested is a hypothesis about the model
The author of a prompt is the worst possible judge of whether its structure is clear, and for an unfixable reason: the author already knows what every region is for, and the model has to derive it. A careful read confirms the intent. It cannot confirm that the intent survived contact with a payload the author never imagined.
The tests are cheap and specific. Ask the model to attribute every claim to its source and check the bindings against the truth, which turns attribution into a pass or fail rather than an impression. Feed sources that deliberately contradict each other, which is the sharpest probe of whether your separation holds, because a structure that has blurred two sources will quietly merge them or silently pick a winner, and a structure that holds will tell you which said what. Place an imperative inside a data region, the kind an attacker would place there, and confirm the directive region still governs. And run real payloads through the assembler and check well-formedness, because the document that contains your own closing marker exists in every corpus large enough to matter.
All of this can be automated, and the reason to automate it is that prompt structure is not a thing you get right once. It erodes. A template gets a new region, a retrieval source starts returning a different shape, a vocabulary drifts because someone in a hurry named a region something almost right. These are ordinary changes, none of them look dangerous in review, and each of them can quietly break a boundary that was holding. Prompt assembly is code that produces the most important input in your system, and it deserves the treatment code gets: fixtures, adversarial cases, assertions that fail the build. A structure verified by one person reading one output on one input is a structure verified for that input.
The prompt is the last interface in the system without a schema
Everywhere else in a system, handing a consumer one long unlabeled string and asking it to sort out which part is which would be indefensible. Your APIs have typed fields. Your queues have message schemas. Your database has columns. Nobody argues for a single text blob and a downstream parser doing its best, because the industry learned decades ago what that costs. Then the same system reaches its model call and does exactly that, and calls it a prompt.
Explicit structure is what closes the gap. Regions with names that say what they are, boundaries the content cannot forge, a vocabulary the assembler enforces across every prompt in the fleet, and a hard separation between the text you authored and the text you fetched. Where the separation must be absolute, it moves out of the document and into the control flow, because a boundary the model merely respects is not one to bet the system on. And all of it is checked by tests that run on real inputs, because the boundary matters most under exactly the conditions a manual read never covers.
None of this makes the model into a parser, and none of it makes the prompt into a contract that the model is obliged to honor. What it does is narrow, and it is worth a great deal. Every distinction your system already knows, and would otherwise throw away at the moment of concatenation, arrives intact. The model stops inferring what it could have been told. The failures that lived in that inference stop being possible, and the ones that remain are yours to defend against with the ordinary tools of least privilege, validation, and testing, rather than with the hope that a very good reader will keep guessing correctly.
