Master Spring AI Prompt Templates: Dynamic Travel Queries with DeepSeek & QWEN
Learn how to leverage Spring AI's prompt template feature to create flexible, variable-driven queries, and implement backend services using DeepSeek and QWEN models for dynamic travel recommendations, complete with code examples for interfaces, service implementations, and controller routing.
Spring AI supports prompt templates, which allow you to embed variables in a text and replace them at runtime, enabling more flexible and dynamic application scenarios.
For example, if you want to ask a large model for the best places to visit in a city, you would otherwise repeat the same query with only the city name changed. Prompt templates solve this redundancy.
Define the service interface
package com.myai.demo.service;
/**
* AI chat service
*/
public interface ChatService {
/**
* Call the large model with user input and return the result
* @param message User's chat content
* @return Text result from the model
*/
String getChatResult(String message);
/**
* Get the top travel spots for a given city
* @param city City name
* @return Best travel attractions
*/
String getTopTravel(String city);
}Next, implement the service for the DeepSeek model.
DeepSeek implementation
package com.myai.demo.service.impl;
import com.myai.demo.service.ChatService;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
/**
* Chat with DeepSeek model
*/
@Service
@Qualifier("deepseek")
public class DeepSeekChatService implements ChatService {
private final ChatClient chatClient;
public DeepSeekChatService(ChatClient.Builder chatClient) {
this.chatClient = chatClient.build();
}
/**
* Call DeepSeek with user input
*/
@Override
public String getChatResult(String message) {
String result;
try {
result = "DeepSeek returned: " + chatClient.prompt().user(message).call().content();
} catch (Exception e) {
return "Exception";
}
return result;
}
/**
* Get top travel spots for a city using DeepSeek
*/
@Override
public String getTopTravel(String city) {
String answer = chatClient.prompt()
.user(u -> u.text("Tell me the three best places to visit in {city}")
.param("city", city))
.call()
.content();
return "DeepSeek returned: " + answer;
}
}Then, implement the service for the QWEN model.
QWEN implementation
package com.myai.demo.service.impl;
import com.myai.demo.service.ChatService;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.ollama.OllamaChatModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service
@Qualifier("qwen")
public class QwenChatService implements ChatService {
@Autowired
private OllamaChatModel chatModel;
/**
* Call QWEN with user input
*/
@Override
public String getChatResult(String message) {
ChatResponse response = chatModel.call(new Prompt(message));
String result = response.getResult().getOutput().getText();
return "QWEN returned: " + result;
}
/**
* Get top travel spots for a city using QWEN
*/
@Override
public String getTopTravel(String city) {
String answer = ChatClient.create(chatModel).prompt()
.user(u -> u.text("Tell me the three best places to visit in {city}")
.param("city", city))
.call()
.content();
return "QWEN returned: " + answer;
}
}Finally, the controller decides which model to use based on the length of the city name (or message). If the length exceeds a threshold, it calls QWEN; otherwise, it falls back to DeepSeek.
Controller
package com.myai.demo.controller;
import com.myai.demo.service.ChatService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/ai")
public class ChatController {
@Autowired
@Qualifier("deepseek")
private ChatService deepSeekService;
@Autowired
@Qualifier("qwen")
private ChatService qwenService;
@GetMapping("/chat")
public String chat(@RequestParam(value = "message") String message) {
if (message.length() > 5) {
return qwenService.getChatResult(message);
}
return deepSeekService.getChatResult(message);
}
@GetMapping("/getTopTravel")
public String getTopTravel(@RequestParam(value = "city") String city) {
if (city.length() > 2) {
return qwenService.getTopTravel(city);
}
return deepSeekService.getTopTravel(city);
}
}Testing with the city "Beijing" returns the expected travel suggestions; changing the parameter to another city such as "Shanghai" yields results for that location.
Full-Stack Internet Architecture
Introducing full-stack Internet architecture technologies centered on Java
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.
