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.

The Dominant Programmer
The Dominant Programmer
The Dominant Programmer
Hands‑On Spring AI with Ollama: Local Model Calls and Prompt Templates

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 prompt

New 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.

Response screenshot
Response screenshot
Custom prompt screenshot
Custom prompt screenshot

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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaLLMTool IntegrationSpring AIPromptTemplateChatClient
The Dominant Programmer
Written by

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

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.