Table of Content
Spring AI Alibaba builds a flight ticket assistant (practical version)

Updated on:June-27th-2025
Recommendation
Master the Spring AI Alibaba framework and build an efficient air ticket assistant application.
Core content:
1. Spring AI Alibaba project background and advantages
2. Detailed step-by-step guidance: from pulling the project to backend startup
3. pom.xml file interpretation and key configuration parameter description
Yang Fangxian
Founder of 53A/Most Valuable Expert of Tencent Cloud (TVP)
<properties> <java.version>17</java.version> <vaadin.version>24.4.7</vaadin.version> <maven-deploy-plugin.version>3.1.1</maven-deploy-plugin.version> <spring-ai-alibaba.version>1.0.0-M6.1</spring-ai-alibaba.version></properties>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId></dependency>
public class AssistantConfig {
/**
* Flight Assistant
*
* @param modelBuilder
* @param vectorStore
* @param chatMemory
*/
public ChatClient getChatClient (ChatClient.Builder modelBuilder, VectorStore vectorStore, ChatMemory chatMemory) {
log.info( "Flight Assistant Configuration CustomerSupportAssistant().." );
ChatClient chatClient = modelBuilder
.defaultSystem( """
You are a customer chat support agent for the airline "Flight-Booking". Please respond in a friendly, helpful and pleasant manner.
You are interacting with customers through a live chat system.
You can support the operations of querying the reservation details of existing tickets, changing the ticket date, cancelling the ticket reservation, etc. The remaining functions will be added in subsequent versions. If the questions asked by users are not supported, please inform us of the details.
Before providing operations such as ticket booking details inquiry, ticket date change, ticket booking cancellation, etc., you must always obtain the following information from the user: booking number, customer name.
After querying information for users, the content of each field needs to be displayed in separate lines. When displaying the content in separate lines, pay attention to keeping the style consistent. You need to use - separate lines display. Other formats are not acceptable.
Before asking the user, please check the message history to obtain the booking number, customer name, etc., and try to avoid repeated inquiries that may cause trouble to the user.
Before changing your booking, you must ensure that the terms allow you to do so.
If the change requires a fee, you must obtain the user's consent before proceeding.
Use the provided functions to get booking details, change bookings, and cancel bookings.
If necessary, you can call the corresponding function to assist in completion.
Please speak Chinese.
Today's date is {current_date}.
""")
.defaultAdvisors(
// Session memory
new PromptChatMemoryAdvisor (chatMemory),
// Storage, based on RAG
new QuestionAnswerAdvisor (vectorStore, SearchRequest.builder().topK( 4 ).similarityThresholdAll().build()),
// logger log printing
new SimpleLoggerAdvisor ()
)
.defaultFunctions(
"getBookingDetails" ,
"changeBooking" ,
"cancelBooking"
)
.build();
return chatClient;
}
CommandLineRunner ingestTermOfServiceToVectorStore (EmbeddingModel embeddingModel, VectorStore vectorStore,
Resource termsOfServiceDocs) {
log.info( "Vector data storage.." );
return args -> {
// Ingest the document into the vector store
vectorStore.write( new TokenTextSplitter ().transform( new TextReader (termsOfServiceDocs).read()));
vectorStore.similaritySearch( "Canceling Bookings" ).forEach(doc -> {
log.info( "Similar Document: {}" , doc.getText());
});
};
}
public VectorStore vectorStore (EmbeddingModel embeddingModel) {
log.info( "vectorStore initialized.." );
return SimpleVectorStore.builder(embeddingModel).build();
}
public ChatMemory chatMemory () {
log.info( "chatMemory().." );
return new InMemoryChatMemory ();
}
public RestClient.Builder restClientBuilder () {
log.info( "restClientBuilder().." );
return RestClient.builder();
}
}
@RequestMapping(path = "/chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE)public Flux<String> chat(String chatId, String userMessage) { return assistantService.chat(chatId, userMessage);}
/** * User conversation * * @param chatId * @param userMessageContent * @return */ public Flux<String> chat(String chatId, String userMessageContent) { log.info("User conversation ID: {}", chatId); return this.chatClient.prompt() .system(s -> s.param("current_date", LocalDate.now().toString())) .user(userMessageContent) .advisors(a -> a.param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId).param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 100)) .stream() .content(); }
@Bean@Description("Get flight booking details")public Function<BookingDetailsRequest, BookingDetails> getBookingDetails() { log.info("Get flight booking details 1..");return request -> { log.info("Get flight booking details 2..");try {return flightBookingService.getBookingDetails(request.bookingNumber(), request.name()); } catch (Exception e) { logger.warn("Booking details: {}", NestedExceptionUtils.getMostSpecificCause(e).getMessage());return new BookingDetails(request.bookingNumber(), request.name(), null, null, null, null, null); } };}
public record BookingDetailsRequest(String bookingNumber, String name) {}