Hands‑On Spring AI with Ollama: Local Model Calls and Prompt Templates
This guide walks through integrating Ollama’s local LLM with Spring AI, explaining Prompt and PromptTemplate concepts, configuring application.yml, implementing a PromptService, adding controller endpoints, using external template files, and testing the setup with curl commands.
Scenario
Spring AI can be combined with Ollama to call tools and build prompts; the article provides a complete from‑scratch implementation.
Prompt and PromptTemplate concepts
1.1 Prompt
A Prompt is a collection of one or more Message objects that represent the full text sent to an LLM. In Spring AI a Prompt contains:
messages : a list where each message has a role such as system, user, assistant, or tool.
options : call options like temperature or max tokens that can override global defaults.
1.2 PromptTemplate
PromptTemplate is a template engine that lets you create text with placeholders ({variable}). It supports:
Placeholder substitution, e.g., {name} replaced at runtime.
Conditional expressions via the built‑in StringTemplate engine (depending on Spring AI version).
Resource loading: templates can be loaded from external files (e.g., classpath:/prompts/xxx.st) for centralized management.
1.3 Applying PromptTemplate in tool calls
Previously the .user() method of ChatClient only accepted a raw user message. Using PromptTemplate you can dynamically build system instructions, parameterize user messages, and keep templates external for non‑developers to edit.
Implementation
Update application.yml
server:
port: 886
spring:
ai:
ollama:
base-url: http://localhost:11434
chat:
model: qwen2.5:7b-instruct
options:
temperature: 0.7
num-ctx: 4096 # context window size
logging:
level:
org.springframework.ai.chat.client: DEBUG # view tool call details
com.badao.ai: DEBUG # view generated promptNew service class PromptService.java
package com.badao.ai.service;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Service;
import java.util.Map;
@Service
public class PromptService {
private final ChatClient chatClient;
private final ResourceLoader resourceLoader;
public PromptService(ChatClient chatClient, ResourceLoader resourceLoader) {
this.chatClient = chatClient;
this.resourceLoader = resourceLoader;
}
/**
* Use PromptTemplate to load a system prompt from classpath and parameterize a user message.
* @param city city name to insert into the template
* @param language language to insert into the template
*/
public String chatWithTemplate(String city, String language) {
// 1. Load system message (may have no placeholders)
Resource systemResource = resourceLoader.getResource("classpath:prompts/system-message.st");
PromptTemplate systemPromptTemplate = new PromptTemplate(systemResource);
Message systemMessage = systemPromptTemplate.createMessage();
// 2. Create user message template and replace placeholders
String userTemplate = "请用 {language} 回答:{city} 今天的天气怎么样?如果需要,可以调用工具查询。";
PromptTemplate userPromptTemplate = new PromptTemplate(userTemplate);
Message userMessage = userPromptTemplate.createMessage(Map.of("language", language, "city", city));
// 3. Assemble Prompt
Prompt prompt = new Prompt(java.util.List.of(systemMessage, userMessage));
// 4. Call ChatClient
return chatClient.prompt(prompt).call().content();
}
/** Use ChatClient's fluent API to set system and user messages directly. */
public String chatWithSystemAndUser(String userMessageText) {
return chatClient.prompt()
.system("你是一个乐于助人的助手,如果用户询问实时信息(如天气、时间),请调用工具获取。并且在回答的最后提醒用户注意带伞,小心地滑,注意擦防晒霜")
.user(userMessageText)
.call()
.content();
}
}External template file (optional)
Create src/main/resources/prompts/system-message.st with the following content:
你是一个智能生活助手。你可以查询天气、时间以及进行数学计算。请使用工具获取实时数据,并用友好的语气回答用户。If you prefer not to use an external file, define the template string directly in code.
Controller extensions
package com.badao.ai.controller;
import com.badao.ai.service.PromptService;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api")
public class ToolChatController {
private final ChatClient chatClient;
private final PromptService promptService;
public ToolChatController(ChatClient chatClient, PromptService promptService) {
this.chatClient = chatClient;
this.promptService = promptService;
}
// Existing generic chat endpoint
@PostMapping("/chat")
public ChatResponse chat(@RequestBody ChatRequest request) {
String result = chatClient.prompt()
.user(request.message())
.call()
.content();
return new ChatResponse(200, "success", result);
}
// New endpoint: use template to build prompt
@PostMapping("/chat/template")
public ChatResponse chatTemplate(@RequestBody TemplateRequest request) {
String result = promptService.chatWithTemplate(request.city(), request.language());
return new ChatResponse(200, "success", result);
}
// New endpoint: system message + user message
@PostMapping("/chat/system")
public ChatResponse chatSystem(@RequestBody ChatRequest request) {
String result = promptService.chatWithSystemAndUser(request.message());
return new ChatResponse(200, "success", result);
}
public record ChatRequest(String message) {}
public record TemplateRequest(String city, String language) {}
public record ChatResponse(int code, String msg, String data) {}
}Testing
Invoke the template‑based weather query (English) with curl:
curl -X POST http://localhost:886/api/chat/template \
-H "Content-Type: application/json" \
-d '{"city": "青岛", "language": "英文"}'Response screenshots illustrate the result when using a custom prompt.
Key takeaways
Prompt consists of a message list plus options, representing a complete model call.
PromptTemplate provides a {var} placeholder engine that can dynamically generate Prompts.
SystemMessage defines the assistant’s role, boundaries, and tool‑use strategy.
UserMessage can be generated via PromptTemplate for context‑aware queries.
External template files stored under classpath:/prompts/ simplify maintenance and support internationalization.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
The Dominant Programmer
Resources and tutorials for programmers' advanced learning journey. Advanced tracks in Java, Python, and C#. Blog: https://blog.csdn.net/badao_liumang_qizhi
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
