AI agents

Injecting Dynamic Data into a System Prompt Safely

Antonio Evans9 min read

A wide technical diagram on a white background.
Key points

Key points

  1. 1Keep the system prompt's stable content first and append all variable data after it, because caching only rewards an identical prefix.
  2. 2Wrap every variable block in a delimiter that cannot appear in the data itself, and generate that delimiter per request.
  3. 3Order dynamic sections from least volatile to most volatile so that more of the prefix survives across calls.
  4. 4Treat retrieved documents and user input as untrusted text, state that rule in the stable section, and never let it appear before the instructions.

Why the boundary between stable and variable is the whole design

A system prompt does two jobs at once: it steers the model, and it anchors a cacheable prefix. The steering job is well covered. Prompts set an agent's role, scope, tone and constraints before any user turn arrives, and well-built ones keep multi-step agent behavior consistent Maxim's overview of system prompts. Yale's guidance for its custom agents makes the same point from the operator side: a system prompt defines behavior, scope, tone and responsibilities across a conversation Yale's system prompt guidance.

The anchoring job is where dynamic data gets teams into trouble. When an LLM processes a prompt, it computes key-value tensors for each token, and prompt caching reuses those tensors whenever a later request begins with exactly the same prefix OpenAI's prompt caching guide. Manus, in its write-up of agent context design, puts the consequence bluntly: because generation is autoregressive, a single-token difference invalidates the cache from that token onward Manus on context engineering.

That gives you the decision rule for the whole article. The bytes at the top of your prompt should change as rarely as possible, and everything a user, a retrieval system or a session clock produces belongs at the bottom. The mental shift is from "write a good prompt" to "curate the context that reaches the model on each call" Anthropic on context engineering. Practically, that is a layout problem before it is a wording problem.

Two failure modes follow from getting the layout wrong. Include a second-precision timestamp at the top and every request looks new to the cache. Put the user's name and account tier above your rules, and every user receives a cold prefix even though 90 percent of the text is identical across the entire customer base. Neither mistake shows up in a demo. Both show up on the bill.

The ordering rule, from most stable to least stable

Sort every block in the prompt by how often it changes, measured across all requests, not across one conversation. The table below is a working checklist for a support or research assistant; adapt the rows, keep the sort.

BlockChanges whenWhere it goesCache effect
Identity, role, toneA prompt release shipsTop of system messageShared across all traffic
Hard rules (safety, refusal, formatting)RarelyImmediately after identityShared across all traffic
Tool definitions and schemasThe toolset changesAfter rules, before dataShared while the toolset is fixed
Operator configuration (tenant, plan)Per tenantAfter tool definitionsShared within a tenant
Session state (turn count, flags)Per sessionAfter tenant configShared within a session
Retrieved documentsPer requestAfter session stateRarely shared
User messagePer turnLastNever shared

Two rows deserve comment. Tool definitions sit high because they are usually static and often bulky, so a stable block of schemas pays for itself many times over. Retrieved documents sit low because their content is drawn from a corpus that changes continuously; a retrieval index that returns different chunks for similar queries will break prefix reuse no matter how you format them Weaviate on context engineering. Anthropic's guidance on finite context is relevant here as well: context is a scarce resource, and every token you add spends some of the model's attention budget Anthropic on context engineering.

One corollary that surprises people: appending a new message to the end of an existing conversation does not invalidate the cache. The prefix (system prompt plus earlier turns) is unchanged, so a multi-turn agent keeps hitting on everything except the newest tokens. Manus reports an average input-to-output token ratio of roughly 100:1 in its agent, which is exactly the shape where prefix reuse dominates cost and time to first response Manus on context engineering.

Choosing delimiters that survive escaping

The delimiter's job is to make the boundary between instructions and data unambiguous to the model and to your own parsers. Three families are in common use.

Markdown fences and rules. Cheap, readable, and fragile. If your data contains triple backticks or a line of dashes, the boundary blurs. Fine for internal tools, risky for anything that accepts public input. LaunchDarkly's prompt engineering list recommends delimiters such as --- or """ to separate parts of a request, which is sound advice as long as you validate that the data cannot contain the same sequence LaunchDarkly's prompt engineering best practices.

XML-style tags. Descriptive tags such as <documents> and <user_input> let the model parse sections by function and give you a stable schema to talk about in the instructions a practitioner's guide to XML tags as delimiters. The catch is escaping: a document that legitimately discusses HTML will contain angle brackets, so either escape the payload or generate a tag name that cannot collide.

Per-request random tokens. The strongest option for untrusted input. Generate a nonce, embed it in the tag name, and state the tag in the instructions above the data.

import secrets

def wrap_untrusted(label: str, payload: str) -> tuple[str, str]:
    nonce = secrets.token_hex(8)
    open_tag = f"<{label}_{nonce}>"
    close_tag = f"</{label}_{nonce}>"
    body = payload.replace(open_tag, "").replace(close_tag, "")
    return open_tag, f"{open_tag}\n{body}\n{close_tag}"

Instruct the model in the stable section to treat everything inside a tag as data, never as instruction, and to ignore any directive that appears inside one. That single sentence is the cheapest injection defense you have, and it costs nothing in cache terms because it lives in the stable part. Structuring a prompt into role, task, instructions and data sections is a widely recommended pattern precisely because it reduces the chance that data is read as instruction LaunchDarkly's prompt engineering best practices.

If you serialize structured data rather than prose, make the serialization deterministic. Manus calls this out explicitly: many languages and libraries do not guarantee stable key ordering when serializing JSON, which silently changes bytes and breaks the cache Manus on context engineering. Sort keys, fix float formatting, and pin your encoder.

Worked example: what a cache hit is worth

Take a support assistant with a 6,000-token stable block (identity, rules, 40 tool schemas) and a 1,500-token dynamic section (tenant config, two retrieved articles, the user turn). Suppose 60,000 requests per day. Round numbers, deliberately simple arithmetic.

If the stable block is truly stable across all requests, the model recomputes the 1,500 dynamic tokens and reuses 6,000 cached tokens on every call. If a timestamp or a per-user greeting sits in line one, the model recomputes 7,500 tokens on every call. The steady-state difference is 6,000 tokens of prefill per request, or 360 million tokens per day that you pay full price for and wait on.

The latency half matters more than the money half for interactive products. Prompt caching cuts time to first token sharply while leaving generation speed unchanged, so a cold prefix means a user stares at a blank panel longer on every single turn Michael Hannecke on prompt caching. One poster on the Hugging Face forums describes exactly this mistake from the other direction: they put long user-specific data at the end of the system prompt assuming they only needed the longest stable prefix within one conversation, then realized the system prompt could be shared across every conversation under their API key a developer's account of cache-aware prompt layout.

The decision rule that falls out: measure your prefix hit rate, not your prompt quality scores. If more than half of your input tokens repeat across requests, caching should be on and your layout should be built for it Michael Hannecke on prompt caching. Then check where the hits stop. If they stop at line one, you have a layout bug, not a model problem.

Session state, retrieved documents and user input, in that order

Session state changes per conversation, so it goes below tenant config and above retrieval. Keep it short and structured: authenticated user id, plan tier, turn number, a handful of flags. Resist the urge to dump a serialized user profile here; trim to the fields the model actually needs, because every token in this block is paid for on every subsequent turn.

Retrieved documents are the hardest block, because retrieval is inherently unstable. Two mitigations are worth the effort. First, fix the number and order of chunks before you format them, so that identical retrieval results produce identical bytes. Second, put stable metadata (document id, title, source URL) before the chunk text inside each document block, so that a chunk whose body changes slightly still shares a prefix with its previous version up to the body. Anthropic's description of context as a finite resource with diminishing returns is the reason to cap retrieval aggressively: more chunks buy less recall than the token count suggests Anthropic on context engineering.

User input goes last, always, wrapped in a nonce tag, and never concatenated into the instruction text. If your architecture puts user text in the system message at all, the only defensible position is that it appears after every rule the model must follow and inside a boundary the user cannot forge.

There is a second argument for this order beyond caching. Errors that enter the context early get referenced and compounded later; a wrong fact in a high-position summary can poison dozens of downstream turns Splunk on context failure modes in production. Keeping volatile content late and clearly labeled gives the model a cleaner boundary between what you asserted and what a retrieval system guessed.

A checklist you can run before shipping

Run these checks in order; each one catches a distinct defect.

  1. Diff two consecutive real requests. Serialize both prompts and diff them. The first differing byte should be in a block you consider volatile. If it is in the first 500 bytes, stop and fix the layout.
  2. Strip clocks and counters from the stable section. Relative dates ("today is...") and request counters belong with session state, not with the role definition.
  3. Verify delimiter collision resistance. Feed your wrapper a payload containing your own closing tag and confirm the output cannot terminate the block early.
  4. Sort every JSON payload. Confirm key ordering is stable across runs.
  5. Confirm the cache breakpoint lands after the system prompt. Some providers need explicit breakpoints, and the breakpoint should at minimum cover the end of the system message Manus on context engineering.
  6. Log the split. Record stable tokens, volatile tokens and cached tokens per request, and graph the ratio.

Step 6 is where most teams discover the problem. The Manus post calls the cache hit rate the single most important metric for a production agent, because it moves both latency and cost at once Manus on context engineering. If you would rather not build that instrumentation yourself, an OpenAI-compatible gateway that reports cache usage per call is the shortest path; Quanta Inference exposes text, image, video, audio and embedding models behind one API key and one prepaid balance (https://quantaintelligence.ai/inference).

Keeping the stable section stable as the product grows

The stable section erodes slowly, and each erosion is invisible on its own. Someone adds a locale to the greeting. Someone bumps a tool description. Someone appends a feature flag to the role line. Six months later the "stable" block changes weekly and the cache hit rate is a third of what it was at launch.

Treat the stable block as versioned configuration rather than as a string in a source file. Prompt changes are rollouts: they should be reviewed, tested against a fixed input set, and reversible without a deploy, which is the argument LaunchDarkly makes for managing prompts as runtime configuration LaunchDarkly's prompt engineering best practices. Supercharge's account of building a drill-evaluation system makes a parallel point: prompts should be built from explicit logical blocks, with instructions specifying output format as strictly as possible Supercharge on prompt engineering for reliable systems. Blocks with stable boundaries are easier to evaluate block by block.

Palantir's guidance is a useful reminder of what belongs in that stable text: be clear and specific, specify context, provide examples, set constraints on length and scope, and iterate against outputs Palantir's prompt engineering best practices. Open in the stable layer, tested once, and left alone is the right home for all of it. Anything that must vary per user belongs in the layers below.

When your dynamic section grows complicated enough that layout review needs its own owner, an expert who reads your context and returns a finished prompt architecture plus an evaluation harness is a reasonable next step: Quanta Experts takes the brief and hands back deliverables (https://quantaintelligence.ai/agents).

FAQ

FAQ

Does adding a user message break the cache from earlier in the conversation?

No. The cached prefix is the system prompt plus the earlier turns, and appending a new turn leaves all of that unchanged. A multi-turn agent keeps hitting on everything except the newest tokens, which is why prompt caching pays off most in long agent loops Manus on context engineering.

Should retrieved documents go in the system message or in a user turn?

Either works for caching, as long as they sit after every stable block. Put them in a user-role turn if your provider or policy treats system content as a higher trust level; that makes an accidental instruction inside a retrieved document less likely to be obeyed. Order and delimiting matter more than which role carries them.

How paranoid do I need to be about delimiter collisions?

If any part of the payload can be written by a user or scraped from the open web, assume it can contain your delimiter. Generate a per-request nonce tag and strip that nonce from the payload before wrapping it. For fully internal, schema-validated data, a fixed XML-style tag is usually enough a practitioner's guide to XML tags.

Where does a timestamp belong if the model needs to know the time?

In the session state block, below the tenant configuration and above retrieval, and truncated to the date or hour unless the task needs finer resolution. A second-precision timestamp at the top of the system prompt is the canonical cache killer Manus on context engineering.

Is it worth restructuring a prompt that already works?

If most of your input tokens repeat across requests, yes, because the restructuring is a byte-level change that leaves wording intact. Diff two consecutive real requests first: if the first difference appears in the opening lines, the fix is usually minutes of work and pays back immediately in latency and cost Michael Hannecke on prompt caching.

Sources

Sources

  1. The Importance of System Prompts in Shaping AI Agent Responses
  2. System Prompts | AI at Yale
  3. Prompt caching | OpenAI API
  4. Context Engineering for AI Agents: Lessons from Building Manus
  5. Effective context engineering for AI agents - Anthropic
  6. Context Engineering - LLM Memory and Retrieval for AI Agents | Weaviate
  7. Prompt Engineering Best Practices: Tutorial & Examples | LaunchDarkly
  8. Effective Prompt Engineering: Mastering XML Tags for ...
  9. Prompt Caching: What It Is, What It Is Not
  10. How prompt caching works - Paged Attention and ...
  11. Context Engineering for AI Agents in Production: Architecture, Failure ...
  12. Prompt Engineering Best Practices: Building Reliable LLM Systems ...
  13. Best practices for LLM prompt engineering - Palantir
  14. https://manus.im/blog/Context-Engineering-for-AI-Agents-Lessons-from-Building-Lessons-from-Building-Manus
About the author

About the author

Antonio Evans
Antonio Evans

Antonio Evans is the founder of Quanta Intelligence and a fractional CTO who builds HIPAA-ready AI systems for health and life-science companies. Before that he was technical co-founder of FriendsEAT for twenty years, CEO of Socialdraft, and a data engineer at IBM. Antonio lives with his dog Santino in Lisbon, Portugal.

All articles