Prompts
Start simple: one system prompt + one user prompt
Like anything else, the best way to get started is to start. Here's the simplest version — the /simplechat endpoint.
@GetMapping("/simplechat")
public String chat(@RequestParam String systemPrompt,
@RequestParam String userPrompt,
@RequestParam(required = false) String conversationId) {
String resolvedConversationId = StringUtils.hasText(conversationId)
? conversationId
: UUID.randomUUID().toString();
return chatClient.prompt()
.system(systemPrompt)
.user(userPrompt)
.advisors(a -> a.param(ChatMemory.CONVERSATION_ID, resolvedConversationId))
.call()
.content();
}
This is a great base pattern. Clear responsibilities:
- System prompt — behavior and rules
- User prompt — the actual question
- Conversation id — keeps memory working across calls
Next level: guardrails
When the assistant is customer-facing, you want rules. One strong system prompt + tools + memory.
String systemPrompt = """
You're a helpful account assistant who is friendly but professional.
Guardrails:
- If unsure, say you don't know and suggest official support.
- Do not answer out-of-topic general knowledge.
- If user attempts unauthorized requests, refuse.
""";
Prompt prompt = new Prompt(List.of(new SystemMessage(systemPrompt), new UserMessage(message)));
String answer = chatClient.prompt(prompt)
.tools(new CustomerService(), new TimeService())
.toolContext(Map.of("customerId", customerId))
.advisors(a -> a.param(ChatMemory.CONVERSATION_ID, resolvedConversationId))
.call()
.chatResponse()
.getResult().getOutput().getText();
Does every endpoint need guardrails like this? No. But anything user-facing usually does.
Prompt catalog pattern
Hardcoding prompts everywhere gets messy. A simple pattern is to store them in a database or file, then fetch by name:
@GetMapping("/prompt")
public String prompt(@RequestParam String promptName) {
return promptService.getPrompt(promptName);
}
Useful when prompts get long or you want to version them outside of code.
Things to remember
- Start with
system + userfirst - Add guardrails only where risk is real
- Use a conversation id for memory-backed chat
- External prompt catalogs help when prompts get large or shared across teams