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.

Full-Stack Internet Architecture
Full-Stack Internet Architecture
Full-Stack Internet Architecture
Master Spring AI Prompt Templates: Dynamic Travel Queries with DeepSeek & QWEN

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.

JavaDeepSeekQwenSpring AIprompt templates
Full-Stack Internet Architecture
Written by

Full-Stack Internet Architecture

Introducing full-stack Internet architecture technologies centered on Java

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.