LLMs, GenAI, RAG, agents, evaluation · insight-1701
Never let user text become the template, and render each prompt exactly once
- Evidence type
- practitioner heuristic
- Difficulty
- easy
- Roles
- ML engineer, Applied AI
- Industries
- cross-industry
Summary: situation, action, reason, boundary
Situation
A prompt is built by pasting a customer message into a template string and then passing the result through a second formatting call.
Action
Treat the template as code and the message as a variable value. Render once, with user text supplied only as a value. Pass only plain strings and numbers in the render context, not whole objects, and add a test whose message contains braces and dotted field names.
Reason
Python's format syntax lets a field name be followed by attribute and index lookups, so an object in the context can be read through text. Jinja's own documentation says its sandbox alone is not perfect security, which is why the context is kept small.
Boundary
Any prompt assembled with str.format, f-strings followed by a template engine, or an editable template store.
Symptom or risk
Braces in the message are read as placeholders: the call fails with a missing-key error, or a field such as a customer record attribute is expanded into the prompt, where the model can repeat it.
Exceptions
Templates written and reviewed only by your team, rendered once, with no user text in the template body.
Trade-offs
Flat string contexts need extra assembly code and a prompt-building layer.
Pitfalls
Appending user text before the final render, or trusting delimiters around the text to stop template syntax from being evaluated.
Example
Hypothetical: the complaint text is concatenated into a template and the result is formatted again with a customer object in the context. A complaint containing the text {customer.email} comes back with the address filled in; a complaint containing {braces} raises a KeyError. Both were reproduced in Python.
Lessons and concepts
Claims and their sources
The Python documentation's format string syntax allows a field name to be followed by attribute lookups (resolved with getattr) and index lookups, as in {0.weight} and {players[0]}; the page carries no warning about untrusted format strings, so the risk statement is the course's inference.
The Jinja documentation says its sandbox intercepts attribute access, method calls and string formatting but is not by itself a solution for perfect security, and advises passing only the data needed.