How does PromptTemplate (and SystemPromptTemplate) render variables into messages in Spring AI?
answer
- {placeholder} + Map of values
- StringTemplate ST4 engine, curly-brace delimiters
- create()->Prompt, createMessage()->Message, render()->String
- SystemPromptTemplate -> SystemMessage
- load from classpath .st Resource
basics
~10 sPromptTemplate holds a template string with {placeholder} slots. You call create(Map) with variable values; it substitutes them and returns a Prompt (or a Message). SystemPromptTemplate does the same but produces a SystemMessage.
solid answer
~40 s`PromptTemplate` externalizes prompt text with `{placeholder}` variables. You construct it from a string or a Spring `Resource` (a classpath `.st` file), then render by supplying a `Map<String,Object>` of values: `template.create(Map.of("topic", "beans"))` returns a fully-substituted `Prompt`; `createMessage(map)` returns a single `Message`; `render(map)` returns the raw String. Under the hood it uses the StringTemplate (ST4) engine, so delimiters are curly braces by default. `SystemPromptTemplate` is a specialization whose `createMessage` yields a `SystemMessage`, keeping persona/instruction text templated and separate from user input. This lets you keep prompts in resource files, version them, and inject runtime data cleanly instead of doing ad-hoc string concatenation — which also reduces prompt-injection surface when combined with role separation.
code
java · 21 linesimport org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.ai.chat.prompt.SystemPromptTemplate;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.messages.Message;
import java.util.List;
import java.util.Map;
public class RecipePrompt {
public Prompt build(String cuisine, String ingredient) {
SystemPromptTemplate system = new SystemPromptTemplate(
"You are a {cuisine} chef. Reply with one recipe idea only.");
Message systemMsg = system.createMessage(Map.of("cuisine", cuisine));
PromptTemplate user = new PromptTemplate(
"Suggest a dish that uses {ingredient}.");
Message userMsg = user.createMessage(Map.of("ingredient", ingredient));
return new Prompt(List.of(systemMsg, userMsg));
}
}go deeper
Knows a template has {placeholders} filled from a Map.
Distinguishes create/createMessage/render outputs and uses SystemPromptTemplate for the system role.
Handles brace-collision gotchas, custom renderers/delimiters, and resource-file externalization.
Frames templating within injection-defense strategy and prompt versioning/governance, not just string substitution.
**Purpose.** `org.springframework.ai.chat.prompt.PromptTemplate` lets you define prompt text once, with named placeholders, and fill them at runtime. It replaces error-prone `String.format`/concatenation and lets prompts live in files. **Placeholder syntax.** By default Spring AI uses the StringTemplate 4 (ST) engine, so variables are written with single curly braces: `Tell me about {topic} in {style} style.`. (Because `{}` is the delimiter, literal braces in your text can need care — a known gotcha when your prompt contains JSON examples with braces.) **Construction.** - From a string: `new PromptTemplate("Hello {name}")`. - From a resource: inject a `@Value("classpath:/prompts/system.st") Resource file` and `new PromptTemplate(file)` — keeps long prompts out of code and under version control. **Rendering / output methods.** - `render()` / `render(Map<String,Object>)` → returns the substituted **String**. - `createMessage(Map)` → returns a single **Message** (a `UserMessage` for plain `PromptTemplate`). - `create(Map)` → returns a **Prompt** ready for `chatModel.call(...)`. You pass variable values as a `Map`, e.g. `Map.of("topic", "Spring beans", "style", "concise")`. Missing variables that the template references cause a render failure, so all referenced names must be supplied. **SystemPromptTemplate.** `org.springframework.ai.chat.prompt.SystemPromptTemplate` extends the idea for the SYSTEM role: `createMessage(map)` returns a `SystemMessage`. Typical pattern: one `SystemPromptTemplate` for templated instructions/persona, one `PromptTemplate` for the user turn, then assemble both into a `Prompt`. **Builder / newer API.** Recent Spring AI versions expose `PromptTemplate.builder()` and allow customizing the `TemplateRenderer` (e.g. `StTemplateRenderer` with custom start/end delimiters) — useful when your content legitimately contains `{`/`}` and you want to switch delimiters to avoid clashes. **With ChatClient.** The fluent client accepts templates inline: `chatClient.prompt().user(u -> u.text("Tell me about {topic}").param("topic", "beans")).call()`. This renders through the same mechanism. **Gotchas.** - Curly-brace collisions: prompts containing JSON/code samples with `{}` can confuse the ST parser; escape or change delimiters. - Every referenced placeholder must have a value in the map, or rendering throws. - Templates are text substitution, **not** sanitization — interpolating untrusted user text into a template does not prevent prompt injection; keep untrusted data in a UserMessage and instructions in the system role. - `render(...)` gives a String only; use `create(...)` when you need a Prompt. **When to use.** Use PromptTemplate whenever prompt text has runtime variables, must be reused, or should live in a versioned resource file rather than inline strings.
- Your prompt contains a JSON example with curly braces and rendering breaks. Why, and what do you do?The ST4 engine treats {} as its variable delimiters, so literal braces in JSON collide with it. Fixes: escape them, or configure a custom TemplateRenderer/delimiters via PromptTemplate.builder() (e.g. StTemplateRenderer with different start/end characters).
- What happens if the map is missing a variable the template references?Rendering fails — every placeholder referenced in the template text must have a corresponding value in the map. There's no silent blank substitution for a referenced-but-missing name.
saying these in an interview costs you the question
- Saying PromptTemplate uses ${...} Spring property syntax (it uses ST {..})
- Claiming templating sanitizes user input against prompt injection
- Thinking render() returns a Prompt object
- Not knowing templates can be loaded from a classpath Resource file