使用 Spring AI 建立可回答問題的 Kotlin 應用程式 — 教學
在本教學中,你將學習如何建立一個 Kotlin 應用程式,該應用程式透過 Spring AI 連接到 LLM,將文件儲存在向量資料庫中,並使用來自這些文件的內容來回答問題。
在本教學中,你將使用以下工具:
- Spring Boot 作為配置與執行 Web 應用程式的基礎。
- Spring AI 用於與 LLM 互動並執行基於內容的檢索。
- IntelliJ IDEA 用於產生專案並實作應用程式邏輯。
- Qdrant 作為執行相似性搜尋的向量資料庫。
- Docker 用於在本機執行 Qdrant。
- OpenAI 作為 LLM 提供者。
在你開始之前
下載並安裝最新版本的 IntelliJ IDEA 並使用旗艦版 (Ultimate) 訂閱。
如果你使用的 IntelliJ IDEA 沒有旗艦版訂閱,或者你使用其他 IDE,可以使用網頁版專案產生器產生 Spring Boot 專案。
在 OpenAI 平台上建立一個 OpenAI API 金鑰以存取 API。
安裝 Docker 以在本機執行 Qdrant 向量資料庫。
安裝 Docker 後,開啟你的終端機並執行以下指令來啟動容器:
bashdocker run -p 6333:6333 -p 6334:6334 qdrant/qdrant
建立專案
你也可以使用 Spring Boot 網頁版專案產生器 作為產生專案的替代方案。
在 IntelliJ IDEA 旗艦版中建立一個新的 Spring Boot 專案:
在 IntelliJ IDEA 中,選取 File | New | Project。
在左側面板中,選取 New Project | Spring Boot。
在 New Project 視窗中指定以下欄位與選項:
Name: springAIDemo
Language: Kotlin
Type: Gradle - Kotlin
此選項指定了建置系統與 DSL。
Package name: com.example.springaidemo
JDK: Java JDK
本教學使用 Oracle OpenJDK version 21.0.1。 如果你尚未安裝 JDK,可以從下拉式清單中下載。
Java: 17
如果你尚未安裝 Java 17,可以從 JDK 下拉式清單中下載。

確保你已指定所有欄位,然後點擊 Next。
在 Spring Boot 欄位中選取最新的穩定版本。
選取本教學所需的以下相依性:
- Web | Spring Web
- AI | OpenAI
- SQL | Qdrant Vector Database

點擊 Create 以產生並設定專案。
IDE 將產生並開啟一個新專案。下載與匯入專案相依性可能需要一些時間。
完成後,你可以在專案檢視中看到以下結構:

產生的 Gradle 專案對應於 Maven 的標準目錄配置:
main/kotlin資料夾下有用於應用程式的套件與類別。- 應用程式的進入點是
SpringAiDemoApplication.kt檔案中的main()方法。
更新專案配置
使用以下內容更新你的
build.gradle.ktsGradle 建置檔案:kotlinplugins { kotlin("jvm") version "2.4.0" kotlin("plugin.spring") version "2.4.0" // 其餘外掛程式 }將
springAiVersion設定為1.0.0:kotlinextra["springAiVersion"] = "1.0.0"點擊 Sync Gradle Changes 按鈕以同步 Gradle 檔案。
使用以下內容更新你的
src/main/resources/application.properties檔案:text# OpenAI spring.ai.openai.api-key=YOUR_OPENAI_API_KEY spring.ai.openai.chat.options.model=gpt-4o-mini spring.ai.openai.embedding.options.model=text-embedding-ada-002 # Qdrant spring.ai.vectorstore.qdrant.host=localhost spring.ai.vectorstore.qdrant.port=6334 spring.ai.vectorstore.qdrant.collection-name=kotlinDocs spring.ai.vectorstore.qdrant.initialize-schema=true將你的 OpenAI API 金鑰設定到
spring.ai.openai.api-key屬性中。執行
SpringAiDemoApplication.kt檔案以啟動 Spring Boot 應用程式。 啟動後,在瀏覽器中開啟 Qdrant 集合 (collections) 頁面以查看結果:
建立一個控制器來載入與搜尋文件
建立一個 Spring @RestController 來搜尋文件並將其儲存在 Qdrant 集合中:
在
src/main/kotlin/org/example/springaidemo目錄中,建立一個名為KotlinSTDController.kt的新檔案,並加入以下程式碼:kotlinpackage org.example.springaidemo // 匯入必要的 Spring 與工具類別 import org.slf4j.LoggerFactory import org.springframework.ai.document.Document import org.springframework.ai.vectorstore.SearchRequest import org.springframework.ai.vectorstore.VectorStore import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController import org.springframework.web.client.RestTemplate import kotlin.uuid.ExperimentalUuidApi import kotlin.uuid.Uuid @RestController @RequestMapping("/kotlin") class KotlinSTDController( private val restTemplate: RestTemplate, private val vectorStore: VectorStore, ) { private val logger = LoggerFactory.getLogger(this::class.java) @OptIn(ExperimentalUuidApi::class) @PostMapping("/load-docs") fun load() { // 從 Kotlin 文件載入文件列表 val kotlinStdTopics = listOf( "collections-overview", "constructing-collections", "iterators", "ranges", "sequences", "collection-operations", "collection-transformations", "collection-filtering", "collection-plus-minus", "collection-grouping", "collection-parts", "collection-elements", "collection-ordering", "collection-aggregate", "collection-write", "list-operations", "set-operations", "map-operations", "read-standard-input", "opt-in-requirements", "scope-functions", "time-measurement", ) // 文件的基礎 URL val url = "https://raw.githubusercontent.com/JetBrains/kotlin-web-site/refs/heads/master/docs/topics/" // 從 URL 檢索每個文件並將其加入向量存儲庫 kotlinStdTopics.forEach { topic -> val data = restTemplate.getForObject("$url$topic.md", String::class.java) data?.let { it -> val doc = Document.builder() // 使用隨機 UUID 建立文件 .id(Uuid.random().toString()) .text(it) .metadata("topic", topic) .build() vectorStore.add(listOf(doc)) logger.info("Document $topic loaded.") } ?: logger.warn("Failed to load document for topic: $topic") } } @GetMapping("docs") fun query( @RequestParam query: String = "operations, filtering, and transformations", @RequestParam topK: Int = 2 ): List<Document>? { val searchRequest = SearchRequest.builder() .query(query) .topK(topK) .build() val results = vectorStore.similaritySearch(searchRequest) logger.info("Found ${results?.size ?: 0} documents for query: '$query'") return results } }更新
SpringAiDemoApplication.kt檔案以宣告一個RestTemplatebean:kotlinpackage org.example.springaidemo import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication import org.springframework.context.annotation.Bean import org.springframework.web.client.RestTemplate @SpringBootApplication class SpringAiDemoApplication { @Bean fun restTemplate(): RestTemplate = RestTemplate() } fun main(args: Array<String>) { runApplication<SpringAiDemoApplication>(*args) }執行應用程式。
在終端機中,向
/kotlin/load-docs端點發送一個 POST 請求以載入文件:bashcurl -X POST http://localhost:8080/kotlin/load-docs文件載入後,你可以透過 GET 請求搜尋它們:
Bashcurl -X GET http://localhost:8080/kotlin/docs
你也可以在 Qdrant 集合 (collections) 頁面查看結果。
實作 AI 聊天端點
文件載入後,最後一步是加入一個端點,透過 Spring AI 的檢索增強生成 (Retrieval-Augmented Generation, RAG) 支援,使用 Qdrant 中的文件來回答問題:
開啟
KotlinSTDController.kt檔案,並匯入以下類別:kotlinimport org.springframework.ai.chat.client.ChatClient import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor import org.springframework.ai.chat.client.advisor.vectorstore.QuestionAnswerAdvisor import org.springframework.ai.chat.prompt.Prompt import org.springframework.ai.chat.prompt.PromptTemplate import org.springframework.web.bind.annotation.RequestBody定義一個
ChatRequest資料類別:kotlin// 代表聊天查詢的請求負載 data class ChatRequest(val query: String, val topK: Int = 3)將
ChatClient.Builder加入控制器的建構函式參數中:kotlinclass KotlinSTDController( private val chatClientBuilder: ChatClient.Builder, private val restTemplate: RestTemplate, private val vectorStore: VectorStore, )在控制器類別內部,建立一個
ChatClient執行個體:kotlin// 使用簡單的記錄建議器 (advisor) 建立聊天用戶端 private val chatClient = chatClientBuilder.defaultAdvisors(SimpleLoggerAdvisor()).build()在
KotlinSTDController.kt檔案底部,加入一個新的chatAsk()端點,邏輯如下:kotlin@PostMapping("/chat/ask") fun chatAsk(@RequestBody request: ChatRequest): String? { // 定義帶有占位符號的提示範本 val promptTemplate = PromptTemplate( """ {query}. Please provide a concise answer based on the "Kotlin standard library" documentation. """.trimIndent() ) // 透過將占位符號替換為實際值來建立提示 (prompt) val prompt: Prompt = promptTemplate.create(mapOf("query" to request.query)) // 配置檢索建議器以使用相關文件增強查詢 val retrievalAdvisor = QuestionAnswerAdvisor.builder(vectorStore) .searchRequest( SearchRequest.builder() .similarityThreshold(0.7) .topK(request.topK) .build() ) .promptTemplate(promptTemplate) .build() // 將提示連同檢索建議器發送到 LLM 並檢索生成的內容 val response = chatClient.prompt(prompt) .advisors(retrievalAdvisor) .call() .content() logger.info("Chat response generated for query: '${request.query}'") return response }執行應用程式。
在終端機中,向新端點發送一個 POST 請求以查看結果:
bashcurl -X POST "http://localhost:8080/kotlin/chat/ask" \ -H "Content-Type: application/json" \ -d '{"query": "What are the performance implications of using lazy sequences in Kotlin for large datasets?", "topK": 3}'
恭喜!你現在擁有一個可以連接到 OpenAI 並使用從儲存在 Qdrant 中的文件中檢索的內容來回答問題的 Kotlin 應用程式。 嘗試實驗不同的查詢或匯入其他文件以探索更多可能性。
你可以在 Spring AI demo GitHub 存儲庫中查看完成的專案。
下一步
- 在 Kotlin AI Examples 中探索其他 Spring AI 範例
- 使用 Spring Boot 與 Claude 建立一個任務管理器應用程式
