Created
March 20, 2026 23:18
-
-
Save hirosichen/b74115e108f5a96db45bee73c243880d to your computer and use it in GitHub Desktop.
Spring AI 課程 Lesson 7:進階 RAG
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| "cells": [ | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "# Lesson 7:Advanced RAG — 提升檢索準確率\n", | |
| "\n", | |
| "## 本節概要\n", | |
| "\n", | |
| "本課程對應教材:\n", | |
| "- **8.1** Advanced RAG 概覽 - 提升檢索準確率的關鍵技術\n", | |
| "- **8.2** Embedding 模型優化 - 從選擇到效能調校\n", | |
| "- **8.3** Re-ranking 實戰 - 精準重排以獲得最佳答案\n", | |
| "\n", | |
| "學完本課你將能夠:\n", | |
| "1. 理解 Naive RAG 的不足與 Advanced RAG 的改進方向\n", | |
| "2. 使用 Metadata 過濾提升搜尋精確度\n", | |
| "3. 實現查詢改寫(Query Rewriting)\n", | |
| "4. 理解 Re-ranking 的原理與效果\n", | |
| "5. 結合 ChatMemory + RAG 建立有記憶的知識庫助手" | |
| ], | |
| "id": "c7ad939d83336f3d" | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "---\n", | |
| "## 7.1 為什麼需要 Advanced RAG?\n", | |
| "\n", | |
| "### Naive RAG 的問題\n", | |
| "\n", | |
| "Lesson 6 學到的基礎 RAG 流程(Naive RAG)在真實場景中會遇到:\n", | |
| "\n", | |
| "| 問題 | 範例 |\n", | |
| "|------|------|\n", | |
| "| **檢索不精確** | 問「年假」卻搜到「病假」的文件 |\n", | |
| "| **上下文不足** | 搜到的段落缺少關鍵前後文 |\n", | |
| "| **雜訊太多** | 搜到的 3 個段落中有 2 個不相關 |\n", | |
| "| **使用者問題模糊** | 「那個制度是什麼?」→ AI 不知道「那個」指什麼 |\n", | |
| "\n", | |
| "### Advanced RAG 的改進\n", | |
| "\n", | |
| "```\n", | |
| "Naive RAG:\n", | |
| " 問題 → 搜尋 → AI 回答\n", | |
| "\n", | |
| "Advanced RAG:\n", | |
| " 問題 → [前處理] → 搜尋 → [後處理] → AI 回答\n", | |
| " ↑ ↑\n", | |
| " 查詢改寫 Re-ranking\n", | |
| " 查詢擴展 Metadata 過濾\n", | |
| " 意圖辨識 上下文壓縮\n", | |
| "```\n", | |
| "\n", | |
| "| 階段 | 技術 | 目的 |\n", | |
| "|------|------|------|\n", | |
| "| **Pre-Retrieval** | 查詢改寫、查詢擴展 | 讓搜尋更精確 |\n", | |
| "| **Retrieval** | Metadata 過濾、混合搜尋 | 縮小搜尋範圍 |\n", | |
| "| **Post-Retrieval** | Re-ranking、上下文壓縮 | 篩選最相關的結果 |" | |
| ], | |
| "id": "ab3dc44170296fe7" | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "---\n", | |
| "## 7.2 環境準備" | |
| ], | |
| "id": "9e380fa0ef9c0611" | |
| }, | |
| { | |
| "cell_type": "code", | |
| "metadata": { | |
| "ExecuteTime": { | |
| "end_time": "2026-03-18T15:18:22.895863Z", | |
| "start_time": "2026-03-18T15:18:22.070427Z" | |
| } | |
| }, | |
| "source": [ | |
| "@file:DependsOn(\"org.springframework.ai:spring-ai-openai:1.0.0\")\n", | |
| "@file:DependsOn(\"org.springframework.ai:spring-ai-client-chat:1.0.0\")\n", | |
| "@file:DependsOn(\"org.springframework.ai:spring-ai-vector-store:1.0.0\")\n", | |
| "@file:DependsOn(\"org.springframework.ai:spring-ai-advisors-vector-store:1.0.0\")\n", | |
| "@file:DependsOn(\"org.slf4j:slf4j-simple:2.0.16\")\n", | |
| "\n", | |
| "import org.springframework.ai.openai.OpenAiChatModel\n", | |
| "import org.springframework.ai.openai.OpenAiEmbeddingModel\n", | |
| "import org.springframework.ai.openai.api.OpenAiApi\n", | |
| "import org.springframework.ai.chat.client.ChatClient\n", | |
| "import org.springframework.ai.openai.OpenAiChatOptions\n", | |
| "import org.springframework.ai.vectorstore.SimpleVectorStore\n", | |
| "import org.springframework.ai.vectorstore.SearchRequest\n", | |
| "import org.springframework.ai.document.Document\n", | |
| "import org.springframework.ai.chat.client.advisor.vectorstore.QuestionAnswerAdvisor\n", | |
| "import org.springframework.ai.chat.memory.MessageWindowChatMemory\n", | |
| "import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor\n", | |
| "import org.springframework.ai.chat.memory.ChatMemory\n", | |
| "import java.io.File\n", | |
| "\n", | |
| "// ===== API Key 設定(二擇一)=====\n", | |
| "val OPENAI_API_KEY = \"\"\n", | |
| "// ===================================\n", | |
| "\n", | |
| "fun loadApiKey(manualKey: String): String {\n", | |
| " if (manualKey.isNotBlank()) return manualKey\n", | |
| " listOf(\".env\", \"../.env\", \"../../.env\").forEach { path ->\n", | |
| " val file = File(path)\n", | |
| " if (file.exists()) {\n", | |
| " file.readLines().forEach { line ->\n", | |
| " if (line.startsWith(\"OPENAI_API_KEY=\")) {\n", | |
| " return line.substringAfter(\"OPENAI_API_KEY=\").trim()\n", | |
| " }\n", | |
| " }\n", | |
| " }\n", | |
| " }\n", | |
| " return System.getenv(\"OPENAI_API_KEY\")\n", | |
| " ?: error(\"請直接填入 API Key,或在專案根目錄建立 .env 檔案\")\n", | |
| "}\n", | |
| "\n", | |
| "val apiKey = loadApiKey(OPENAI_API_KEY)\n", | |
| "val openAiApi = OpenAiApi.builder().apiKey(apiKey).build()\n", | |
| "val chatModel = OpenAiChatModel.builder().openAiApi(openAiApi).build()\n", | |
| "val embeddingModel = OpenAiEmbeddingModel(openAiApi)\n", | |
| "val chatClient = ChatClient.builder(chatModel).build()\n", | |
| "\n", | |
| "println(\"環境準備完成!\")" | |
| ], | |
| "id": "aa287abddf473ffd", | |
| "outputs": [ | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "環境準備完成!\n" | |
| ] | |
| } | |
| ], | |
| "execution_count": 1 | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "---\n", | |
| "## 7.3 建立更豐富的知識庫\n", | |
| "\n", | |
| "這次我們建立一個更大、有更多 Metadata 的知識庫,模擬真實企業場景:" | |
| ], | |
| "id": "925b8bedd0e92216" | |
| }, | |
| { | |
| "cell_type": "code", | |
| "metadata": { | |
| "ExecuteTime": { | |
| "end_time": "2026-03-18T15:18:28.177228Z", | |
| "start_time": "2026-03-18T15:18:24.425599Z" | |
| } | |
| }, | |
| "source": [ | |
| "val vectorStore = SimpleVectorStore.builder(embeddingModel).build()\n", | |
| "\n", | |
| "val docs = listOf(\n", | |
| " // 休假制度\n", | |
| " Document(\"年假規定:全職員工每年享有 14 天年假。到職滿一年後,每增加一年年資增加 1 天,最多 30 天。年假需提前 3 天申請。\",\n", | |
| " mapOf(\"source\" to \"員工手冊\", \"category\" to \"休假\", \"department\" to \"全公司\", \"updated\" to \"2025-01\")),\n", | |
| " Document(\"病假規定:員工每年有 30 天全薪病假。超過 3 天需附醫生證明。住院期間不計入病假天數。\",\n", | |
| " mapOf(\"source\" to \"員工手冊\", \"category\" to \"休假\", \"department\" to \"全公司\", \"updated\" to \"2025-01\")),\n", | |
| " Document(\"特休假:婚假 8 天、喪假 3-8 天(視親等)、產假 56 天、陪產假 7 天。需附相關證明文件。\",\n", | |
| " mapOf(\"source\" to \"員工手冊\", \"category\" to \"休假\", \"department\" to \"全公司\", \"updated\" to \"2025-01\")),\n", | |
| "\n", | |
| " // 財務制度\n", | |
| " Document(\"報帳流程:業務支出需在 7 天內提交報帳單。5000 元以下主管核准,5000-20000 元經理核准,20000 元以上副總核准。\",\n", | |
| " mapOf(\"source\" to \"財務手冊\", \"category\" to \"財務\", \"department\" to \"全公司\", \"updated\" to \"2025-03\")),\n", | |
| " Document(\"出差補助:國內出差每日膳雜費 700 元,住宿費上限 2500 元。國際出差依目的地國家標準另行核定。\",\n", | |
| " mapOf(\"source\" to \"財務手冊\", \"category\" to \"財務\", \"department\" to \"全公司\", \"updated\" to \"2025-03\")),\n", | |
| "\n", | |
| " // 工程部專屬\n", | |
| " Document(\"程式碼審查規範:所有合併請求(PR)需至少一位資深工程師審查通過。審查重點:程式碼品質、測試覆蓋率、安全性。\",\n", | |
| " mapOf(\"source\" to \"工程部規範\", \"category\" to \"技術\", \"department\" to \"工程部\", \"updated\" to \"2025-02\")),\n", | |
| " Document(\"部署流程:所有程式碼需通過 CI/CD 管線。開發環境可自由部署,測試環境需主管核准,正式環境需技術主管和 PM 雙核准。\",\n", | |
| " mapOf(\"source\" to \"工程部規範\", \"category\" to \"技術\", \"department\" to \"工程部\", \"updated\" to \"2025-02\")),\n", | |
| " Document(\"技術選型原則:新專案優先使用 Spring Boot 3 + Java 21。AI 功能使用 Spring AI 框架。前端使用 React 或 Vue。\",\n", | |
| " mapOf(\"source\" to \"工程部規範\", \"category\" to \"技術\", \"department\" to \"工程部\", \"updated\" to \"2026-01\")),\n", | |
| "\n", | |
| " // 資安\n", | |
| " Document(\"密碼政策:密碼每 90 天更換,長度至少 12 位,需包含大小寫字母、數字和特殊符號。禁止使用最近 5 次用過的密碼。\",\n", | |
| " mapOf(\"source\" to \"資安手冊\", \"category\" to \"資安\", \"department\" to \"全公司\", \"updated\" to \"2025-06\")),\n", | |
| " Document(\"遠端工作資安要求:必須使用公司 VPN 連線。禁止在公共 WiFi 下處理公司資料。離開電腦必須鎖定螢幕。\",\n", | |
| " mapOf(\"source\" to \"資安手冊\", \"category\" to \"資安\", \"department\" to \"全公司\", \"updated\" to \"2025-06\")),\n", | |
| "\n", | |
| " // 績效\n", | |
| " Document(\"績效考核:每半年一次。自評 30%、主管評 50%、同儕評 20%。等級 S/A/B/C/D,B 以上可獲年終獎金。\",\n", | |
| " mapOf(\"source\" to \"人事手冊\", \"category\" to \"績效\", \"department\" to \"全公司\", \"updated\" to \"2025-01\")),\n", | |
| " Document(\"升遷標準:連續兩次考核 A 以上可提報升遷。升遷需經部門主管推薦、人事委員會審核、總經理核准。\",\n", | |
| " mapOf(\"source\" to \"人事手冊\", \"category\" to \"績效\", \"department\" to \"全公司\", \"updated\" to \"2025-01\"))\n", | |
| ")\n", | |
| "\n", | |
| "vectorStore.add(docs)\n", | |
| "\n", | |
| "println(\"已載入 ${docs.size} 份文件\")\n", | |
| "println(\"類別:${docs.map { it.metadata[\"category\"] }.distinct().joinToString(\"、\")}\")\n", | |
| "println(\"部門:${docs.map { it.metadata[\"department\"] }.distinct().joinToString(\"、\")}\")" | |
| ], | |
| "id": "7acf8fb375bb4ca6", | |
| "outputs": [ | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "已載入 12 份文件\n", | |
| "類別:休假、財務、技術、資安、績效\n", | |
| "部門:全公司、工程部\n" | |
| ] | |
| } | |
| ], | |
| "execution_count": 2 | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "---\n", | |
| "## 7.4 技術一:Metadata 過濾\n", | |
| "\n", | |
| "### 問題:搜尋範圍太大\n", | |
| "\n", | |
| "```\n", | |
| "問:「工程部的部署流程是什麼?」\n", | |
| "\n", | |
| "Naive RAG:搜尋所有 12 份文件 → 可能混入不相關的結果\n", | |
| "Metadata 過濾:只搜尋 department=工程部 的文件 → 更精確\n", | |
| "```" | |
| ], | |
| "id": "65e14515540a11f5" | |
| }, | |
| { | |
| "cell_type": "code", | |
| "metadata": { | |
| "ExecuteTime": { | |
| "end_time": "2026-03-18T15:18:35.241035Z", | |
| "start_time": "2026-03-18T15:18:34.889600Z" | |
| } | |
| }, | |
| "source": [ | |
| "import org.springframework.ai.vectorstore.filter.FilterExpressionBuilder\n", | |
| "\n", | |
| "val b = FilterExpressionBuilder()\n", | |
| "\n", | |
| "// 只搜尋工程部的文件\n", | |
| "val engineeringFilter = b.eq(\"department\", \"工程部\").build()\n", | |
| "\n", | |
| "val filteredResults = vectorStore.similaritySearch(\n", | |
| " SearchRequest.builder()\n", | |
| " .query(\"部署流程是什麼?\")\n", | |
| " .topK(3)\n", | |
| " .filterExpression(engineeringFilter)\n", | |
| " .build()\n", | |
| ")\n", | |
| "\n", | |
| "println(\"=== 使用 Metadata 過濾(department=工程部)===\")\n", | |
| "for ((i, doc) in filteredResults.withIndex()) {\n", | |
| " println(\"\\n第 ${i + 1} 名 [${doc.metadata[\"source\"]}]:\")\n", | |
| " println(doc.text)\n", | |
| "}" | |
| ], | |
| "id": "77469f9ee87d5386", | |
| "outputs": [ | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "=== 使用 Metadata 過濾(department=工程部)===\n", | |
| "\n", | |
| "第 1 名 [工程部規範]:\n", | |
| "部署流程:所有程式碼需通過 CI/CD 管線。開發環境可自由部署,測試環境需主管核准,正式環境需技術主管和 PM 雙核准。\n", | |
| "\n", | |
| "第 2 名 [工程部規範]:\n", | |
| "技術選型原則:新專案優先使用 Spring Boot 3 + Java 21。AI 功能使用 Spring AI 框架。前端使用 React 或 Vue。\n", | |
| "\n", | |
| "第 3 名 [工程部規範]:\n", | |
| "程式碼審查規範:所有合併請求(PR)需至少一位資深工程師審查通過。審查重點:程式碼品質、測試覆蓋率、安全性。\n" | |
| ] | |
| } | |
| ], | |
| "execution_count": 3 | |
| }, | |
| { | |
| "cell_type": "code", | |
| "metadata": { | |
| "ExecuteTime": { | |
| "end_time": "2026-03-18T15:18:45.772490Z", | |
| "start_time": "2026-03-18T15:18:45.545595Z" | |
| } | |
| }, | |
| "source": [ | |
| "// 對比:不使用過濾\n", | |
| "val unfilteredResults = vectorStore.similaritySearch(\n", | |
| " SearchRequest.builder()\n", | |
| " .query(\"部署流程是什麼?\")\n", | |
| " .topK(3)\n", | |
| " .build()\n", | |
| ")\n", | |
| "\n", | |
| "println(\"=== 不使用過濾 ===\")\n", | |
| "for ((i, doc) in unfilteredResults.withIndex()) {\n", | |
| " println(\"\\n第 ${i + 1} 名 [${doc.metadata[\"source\"]}]:\")\n", | |
| " println(doc.text)\n", | |
| "}\n", | |
| "\n", | |
| "println(\"\\n--- 觀察:沒有過濾時,可能搜到不相關部門的文件 ---\")" | |
| ], | |
| "id": "9f6bec148a58b836", | |
| "outputs": [ | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "=== 不使用過濾 ===\n", | |
| "\n", | |
| "第 1 名 [工程部規範]:\n", | |
| "部署流程:所有程式碼需通過 CI/CD 管線。開發環境可自由部署,測試環境需主管核准,正式環境需技術主管和 PM 雙核准。\n", | |
| "\n", | |
| "第 2 名 [財務手冊]:\n", | |
| "報帳流程:業務支出需在 7 天內提交報帳單。5000 元以下主管核准,5000-20000 元經理核准,20000 元以上副總核准。\n", | |
| "\n", | |
| "第 3 名 [工程部規範]:\n", | |
| "技術選型原則:新專案優先使用 Spring Boot 3 + Java 21。AI 功能使用 Spring AI 框架。前端使用 React 或 Vue。\n", | |
| "\n", | |
| "--- 觀察:沒有過濾時,可能搜到不相關部門的文件 ---\n" | |
| ] | |
| } | |
| ], | |
| "execution_count": 4 | |
| }, | |
| { | |
| "cell_type": "code", | |
| "metadata": { | |
| "ExecuteTime": { | |
| "end_time": "2026-03-18T15:18:51.648928Z", | |
| "start_time": "2026-03-18T15:18:51.398Z" | |
| } | |
| }, | |
| "source": [ | |
| "// 組合多個過濾條件\n", | |
| "val combinedFilter = b.and(\n", | |
| " b.eq(\"category\", \"休假\"),\n", | |
| " b.eq(\"department\", \"全公司\")\n", | |
| ").build()\n", | |
| "\n", | |
| "val leaveResults = vectorStore.similaritySearch(\n", | |
| " SearchRequest.builder()\n", | |
| " .query(\"請假規定\")\n", | |
| " .topK(5)\n", | |
| " .filterExpression(combinedFilter)\n", | |
| " .build()\n", | |
| ")\n", | |
| "\n", | |
| "println(\"=== 組合過濾(category=休假 AND department=全公司)===\")\n", | |
| "for ((i, doc) in leaveResults.withIndex()) {\n", | |
| " println(\"${i + 1}. [${doc.metadata[\"category\"]}] ${doc.text.take(50)}...\")\n", | |
| "}" | |
| ], | |
| "id": "6b81a91f23726c27", | |
| "outputs": [ | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "=== 組合過濾(category=休假 AND department=全公司)===\n", | |
| "1. [休假] 病假規定:員工每年有 30 天全薪病假。超過 3 天需附醫生證明。住院期間不計入病假天數。...\n", | |
| "2. [休假] 年假規定:全職員工每年享有 14 天年假。到職滿一年後,每增加一年年資增加 1 天,最多 30 天。...\n", | |
| "3. [休假] 特休假:婚假 8 天、喪假 3-8 天(視親等)、產假 56 天、陪產假 7 天。需附相關證明文件。...\n" | |
| ] | |
| } | |
| ], | |
| "execution_count": 5 | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "### Filter 語法\n", | |
| "\n", | |
| "```kotlin\n", | |
| "val b = FilterExpressionBuilder()\n", | |
| "\n", | |
| "b.eq(\"key\", \"value\") // key == value\n", | |
| "b.ne(\"key\", \"value\") // key != value\n", | |
| "b.in(\"key\", \"v1\", \"v2\") // key in [v1, v2]\n", | |
| "b.and(expr1, expr2) // expr1 AND expr2\n", | |
| "b.or(expr1, expr2) // expr1 OR expr2\n", | |
| "```" | |
| ], | |
| "id": "ad15baa7f05aae45" | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "---\n", | |
| "## 7.5 技術二:查詢改寫(Query Rewriting)\n", | |
| "\n", | |
| "### 問題:使用者的問題太模糊\n", | |
| "\n", | |
| "```\n", | |
| "使用者:「那個要怎麼請?」 → AI 不知道「那個」是什麼\n", | |
| "使用者:「升遷要什麼條件?」 → 太簡短,可能搜不到最佳段落\n", | |
| "```\n", | |
| "\n", | |
| "### 解決方案:用 AI 改寫查詢\n", | |
| "\n", | |
| "```\n", | |
| "原始問題:「那個要怎麼請?」\n", | |
| " ↓ AI 改寫\n", | |
| "改寫後:「年假的申請流程和規定是什麼?需要提前幾天申請?」\n", | |
| " ↓\n", | |
| "用改寫後的問題去搜尋 → 更精確的結果\n", | |
| "```" | |
| ], | |
| "id": "482426b362fa8a3e" | |
| }, | |
| { | |
| "cell_type": "code", | |
| "metadata": { | |
| "ExecuteTime": { | |
| "end_time": "2026-03-18T15:19:10.349525Z", | |
| "start_time": "2026-03-18T15:19:07.779114Z" | |
| } | |
| }, | |
| "source": [ | |
| "// 查詢改寫函式\n", | |
| "fun rewriteQuery(originalQuery: String, conversationContext: String = \"\"): String {\n", | |
| " val rewritten: String = chatClient.prompt()\n", | |
| " .system(\"\"\"你是一個查詢改寫助手。\n", | |
| "將使用者的問題改寫為更適合搜尋企業知識庫的查詢。\n", | |
| "規則:\n", | |
| "1. 把模糊的詞替換為具體的詞\n", | |
| "2. 展開縮寫和代稱\n", | |
| "3. 只回傳改寫後的查詢,不要加其他文字\n", | |
| "4. 如果有對話上下文,根據上下文理解指代詞(如「那個」「這個」)\n", | |
| "\n", | |
| "對話上下文:$conversationContext\"\"\")\n", | |
| " .user(originalQuery)\n", | |
| " .call()\n", | |
| " .content() ?: originalQuery\n", | |
| " return rewritten\n", | |
| "}\n", | |
| "\n", | |
| "// 測試查詢改寫\n", | |
| "val queries = listOf(\n", | |
| " \"那個要怎麼請?\" to \"之前在聊年假的事\",\n", | |
| " \"升遷要什麼?\" to \"\",\n", | |
| " \"出差錢怎麼報?\" to \"\"\n", | |
| ")\n", | |
| "\n", | |
| "for ((query, context) in queries) {\n", | |
| " val rewritten = rewriteQuery(query, context)\n", | |
| " println(\"原始:$query\")\n", | |
| " println(\"改寫:$rewritten\")\n", | |
| " println()\n", | |
| "}" | |
| ], | |
| "id": "c6b8cccaeb0f0301", | |
| "outputs": [ | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "原始:那個要怎麼請?\n", | |
| "改寫:年假申請流程是什麼?\n", | |
| "\n", | |
| "原始:升遷要什麼?\n", | |
| "改寫:升遷需要哪些條件和流程?\n", | |
| "\n", | |
| "原始:出差錢怎麼報?\n", | |
| "改寫:出差費用報銷流程是什麼?\n", | |
| "\n" | |
| ] | |
| } | |
| ], | |
| "execution_count": 6 | |
| }, | |
| { | |
| "cell_type": "code", | |
| "metadata": { | |
| "ExecuteTime": { | |
| "end_time": "2026-03-18T15:19:36.965604Z", | |
| "start_time": "2026-03-18T15:19:35.864890Z" | |
| } | |
| }, | |
| "source": [ | |
| "// 使用改寫後的查詢搜尋,對比效果\n", | |
| "val originalQuery = \"那個要怎麼請?\"\n", | |
| "val rewrittenQuery = rewriteQuery(originalQuery, \"之前在聊年假的事\")\n", | |
| "\n", | |
| "println(\"=== 原始查詢搜尋結果 ===\")\n", | |
| "val origResults = vectorStore.similaritySearch(\n", | |
| " SearchRequest.builder().query(originalQuery).topK(2).build()\n", | |
| ")\n", | |
| "for (doc in origResults) {\n", | |
| " println(\" [${doc.metadata[\"category\"]}] ${doc.text.take(60)}...\")\n", | |
| "}\n", | |
| "\n", | |
| "println(\"\\n=== 改寫後查詢搜尋結果 ===\")\n", | |
| "val rewrittenResults = vectorStore.similaritySearch(\n", | |
| " SearchRequest.builder().query(rewrittenQuery).topK(2).build()\n", | |
| ")\n", | |
| "for (doc in rewrittenResults) {\n", | |
| " println(\" [${doc.metadata[\"category\"]}] ${doc.text.take(60)}...\")\n", | |
| "}" | |
| ], | |
| "id": "f218f853b480b475", | |
| "outputs": [ | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "=== 原始查詢搜尋結果 ===\n", | |
| " [技術] 程式碼審查規範:所有合併請求(PR)需至少一位資深工程師審查通過。審查重點:程式碼品質、測試覆蓋率、安全性。...\n", | |
| " [休假] 特休假:婚假 8 天、喪假 3-8 天(視親等)、產假 56 天、陪產假 7 天。需附相關證明文件。...\n", | |
| "\n", | |
| "=== 改寫後查詢搜尋結果 ===\n", | |
| " [休假] 年假規定:全職員工每年享有 14 天年假。到職滿一年後,每增加一年年資增加 1 天,最多 30 天。年假需提前 3 天申...\n", | |
| " [休假] 病假規定:員工每年有 30 天全薪病假。超過 3 天需附醫生證明。住院期間不計入病假天數。...\n" | |
| ] | |
| } | |
| ], | |
| "execution_count": 7 | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "---\n", | |
| "## 7.6 技術三:Re-ranking(重新排序)\n", | |
| "\n", | |
| "### 問題:向量搜尋的結果不是最佳排序\n", | |
| "\n", | |
| "```\n", | |
| "向量搜尋 Top 3 結果:\n", | |
| " 1. 密碼政策(相關度 0.85) ← 相關但不是最佳答案\n", | |
| " 2. 遠端工作資安要求(0.82) ← 最佳答案!\n", | |
| " 3. 報帳流程(0.78) ← 不太相關\n", | |
| "\n", | |
| "Re-ranking 後:\n", | |
| " 1. 遠端工作資安要求(重排分 0.95)← 提升到第一名\n", | |
| " 2. 密碼政策(0.70)\n", | |
| " 3. 報帳流程(0.30) ← 降低排名\n", | |
| "```\n", | |
| "\n", | |
| "### 在 Notebook 中模擬 Re-ranking\n", | |
| "\n", | |
| "正式環境可用 Voyage AI、Cohere 等 Re-ranking 服務。\n", | |
| "這裡我們用 AI 來模擬 Re-ranking 的效果:" | |
| ], | |
| "id": "eff7f28d08353847" | |
| }, | |
| { | |
| "cell_type": "code", | |
| "metadata": { | |
| "ExecuteTime": { | |
| "end_time": "2026-03-18T15:19:57.412712Z", | |
| "start_time": "2026-03-18T15:19:57.301327Z" | |
| } | |
| }, | |
| "source": [ | |
| "// 用 AI 模擬 Re-ranking\n", | |
| "fun rerank(question: String, documents: List<Document>, topN: Int = 2): List<Document> {\n", | |
| " val docsText = documents.mapIndexed { i, doc ->\n", | |
| " \"文件 ${i + 1}: ${doc.text}\"\n", | |
| " }.joinToString(\"\\n\\n\")\n", | |
| "\n", | |
| " val ranking: String = chatClient.prompt()\n", | |
| " .system(\"\"\"你是一個文件相關性評估助手。\n", | |
| "根據使用者問題,將以下文件按相關性從高到低排序。\n", | |
| "只回傳文件編號,用逗號分隔。例如:2,1,3\n", | |
| "只回傳數字和逗號,不要加其他文字。\"\"\")\n", | |
| " .user(\"\"\"問題:$question\n", | |
| "\n", | |
| "$docsText\"\"\")\n", | |
| " .call()\n", | |
| " .content() ?: \"\"\n", | |
| "\n", | |
| " // 解析排序結果\n", | |
| " val indices = ranking.trim().split(\",\")\n", | |
| " .mapNotNull { it.trim().toIntOrNull()?.minus(1) }\n", | |
| " .filter { it in documents.indices }\n", | |
| " .take(topN)\n", | |
| "\n", | |
| " return indices.map { documents[it] }\n", | |
| "}\n", | |
| "\n", | |
| "println(\"Re-ranking 函式建立完成!\")" | |
| ], | |
| "id": "2c426477767d7c7c", | |
| "outputs": [ | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "Re-ranking 函式建立完成!\n" | |
| ] | |
| } | |
| ], | |
| "execution_count": 8 | |
| }, | |
| { | |
| "cell_type": "code", | |
| "metadata": { | |
| "ExecuteTime": { | |
| "end_time": "2026-03-18T15:20:08.929337Z", | |
| "start_time": "2026-03-18T15:20:07.128282Z" | |
| } | |
| }, | |
| "source": [ | |
| "// 測試 Re-ranking 效果\n", | |
| "val question = \"遠端工作時要注意什麼安全問題?\"\n", | |
| "\n", | |
| "// Step 1: 向量搜尋(取多一些結果)\n", | |
| "val searchResults = vectorStore.similaritySearch(\n", | |
| " SearchRequest.builder().query(question).topK(5).build()\n", | |
| ")\n", | |
| "\n", | |
| "println(\"=== 向量搜尋 Top 5 ===\")\n", | |
| "for ((i, doc) in searchResults.withIndex()) {\n", | |
| " println(\"${i + 1}. [${doc.metadata[\"category\"]}] ${doc.text.take(50)}...\")\n", | |
| "}\n", | |
| "\n", | |
| "// Step 2: Re-ranking\n", | |
| "val reranked = rerank(question, searchResults, topN = 3)\n", | |
| "\n", | |
| "println(\"\\n=== Re-ranking 後 Top 3 ===\")\n", | |
| "for ((i, doc) in reranked.withIndex()) {\n", | |
| " println(\"${i + 1}. [${doc.metadata[\"category\"]}] ${doc.text.take(50)}...\")\n", | |
| "}" | |
| ], | |
| "id": "5e6748c6e7329fea", | |
| "outputs": [ | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "=== 向量搜尋 Top 5 ===\n", | |
| "1. [資安] 遠端工作資安要求:必須使用公司 VPN 連線。禁止在公共 WiFi 下處理公司資料。離開電腦必須鎖定...\n", | |
| "2. [資安] 密碼政策:密碼每 90 天更換,長度至少 12 位,需包含大小寫字母、數字和特殊符號。禁止使用最近 ...\n", | |
| "3. [技術] 部署流程:所有程式碼需通過 CI/CD 管線。開發環境可自由部署,測試環境需主管核准,正式環境需技術...\n", | |
| "4. [技術] 程式碼審查規範:所有合併請求(PR)需至少一位資深工程師審查通過。審查重點:程式碼品質、測試覆蓋率、...\n", | |
| "5. [技術] 技術選型原則:新專案優先使用 Spring Boot 3 + Java 21。AI 功能使用 Spr...\n", | |
| "\n", | |
| "=== Re-ranking 後 Top 3 ===\n", | |
| "1. [資安] 遠端工作資安要求:必須使用公司 VPN 連線。禁止在公共 WiFi 下處理公司資料。離開電腦必須鎖定...\n", | |
| "2. [資安] 密碼政策:密碼每 90 天更換,長度至少 12 位,需包含大小寫字母、數字和特殊符號。禁止使用最近 ...\n", | |
| "3. [技術] 部署流程:所有程式碼需通過 CI/CD 管線。開發環境可自由部署,測試環境需主管核准,正式環境需技術...\n" | |
| ] | |
| } | |
| ], | |
| "execution_count": 9 | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "---\n", | |
| "## 7.7 組合技:完整的 Advanced RAG Pipeline\n", | |
| "\n", | |
| "把所有技術串在一起:" | |
| ], | |
| "id": "a74d923db332ef38" | |
| }, | |
| { | |
| "cell_type": "code", | |
| "metadata": { | |
| "ExecuteTime": { | |
| "end_time": "2026-03-18T15:20:18.003424Z", | |
| "start_time": "2026-03-18T15:20:17.915704Z" | |
| } | |
| }, | |
| "source": [ | |
| "// Advanced RAG Pipeline\n", | |
| "fun advancedRagQuery(\n", | |
| " question: String,\n", | |
| " conversationContext: String = \"\",\n", | |
| " departmentFilter: String? = null\n", | |
| "): String {\n", | |
| " // Step 1: 查詢改寫\n", | |
| " val rewrittenQuery = rewriteQuery(question, conversationContext)\n", | |
| " println(\" [改寫] $question → $rewrittenQuery\")\n", | |
| "\n", | |
| " // Step 2: 向量搜尋(帶 Metadata 過濾)\n", | |
| " val searchBuilder = SearchRequest.builder()\n", | |
| " .query(rewrittenQuery)\n", | |
| " .topK(5)\n", | |
| "\n", | |
| " if (departmentFilter != null) {\n", | |
| " val b = FilterExpressionBuilder()\n", | |
| " searchBuilder.filterExpression(b.eq(\"department\", departmentFilter).build())\n", | |
| " println(\" [過濾] department = $departmentFilter\")\n", | |
| " }\n", | |
| "\n", | |
| " val searchResults = vectorStore.similaritySearch(searchBuilder.build())\n", | |
| " println(\" [搜尋] 找到 ${searchResults.size} 個結果\")\n", | |
| "\n", | |
| " // Step 3: Re-ranking\n", | |
| " val topDocs = if (searchResults.size > 2) {\n", | |
| " rerank(rewrittenQuery, searchResults, topN = 3)\n", | |
| " } else {\n", | |
| " searchResults\n", | |
| " }\n", | |
| " println(\" [重排] 取前 ${topDocs.size} 個最相關段落\")\n", | |
| "\n", | |
| " // Step 4: 組裝 Prompt 送給 AI\n", | |
| " val context = topDocs.joinToString(\"\\n\\n\") { doc ->\n", | |
| " \"[${doc.metadata[\"source\"]} / ${doc.metadata[\"category\"]}]\\n${doc.text}\"\n", | |
| " }\n", | |
| "\n", | |
| " return chatClient.prompt()\n", | |
| " .system(\"\"\"你是公司的智能助手,根據以下文件回答問題。\n", | |
| "只根據文件內容回答,找不到就說不知道。用繁體中文回答。\n", | |
| "\n", | |
| "文件:\n", | |
| "$context\"\"\")\n", | |
| " .user(question)\n", | |
| " .call()\n", | |
| " .content() ?: \"(無回應)\"\n", | |
| "}\n", | |
| "\n", | |
| "println(\"Advanced RAG Pipeline 建立完成!\")" | |
| ], | |
| "id": "52d16cee42da2279", | |
| "outputs": [ | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "Advanced RAG Pipeline 建立完成!\n" | |
| ] | |
| } | |
| ], | |
| "execution_count": 10 | |
| }, | |
| { | |
| "cell_type": "code", | |
| "metadata": { | |
| "ExecuteTime": { | |
| "end_time": "2026-03-18T15:20:33.501979Z", | |
| "start_time": "2026-03-18T15:20:23.768872Z" | |
| } | |
| }, | |
| "source": [ | |
| "// 測試 Advanced RAG\n", | |
| "println(\"========== 測試 1:模糊問題 ==========\")\n", | |
| "val a1 = advancedRagQuery(\n", | |
| " question = \"那個要怎麼處理?\",\n", | |
| " conversationContext = \"剛才在討論出差的費用\"\n", | |
| ")\n", | |
| "println(\"\\n答:$a1\")\n", | |
| "\n", | |
| "println(\"\\n========== 測試 2:部門過濾 ==========\")\n", | |
| "val a2 = advancedRagQuery(\n", | |
| " question = \"程式碼審查的流程是什麼?\",\n", | |
| " departmentFilter = \"工程部\"\n", | |
| ")\n", | |
| "println(\"\\n答:$a2\")\n", | |
| "\n", | |
| "println(\"\\n========== 測試 3:一般問題 ==========\")\n", | |
| "val a3 = advancedRagQuery(\n", | |
| " question = \"升遷需要什麼條件?考核怎麼算分?\"\n", | |
| ")\n", | |
| "println(\"\\n答:$a3\")" | |
| ], | |
| "id": "f48d39e5c8f17f49", | |
| "outputs": [ | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "========== 測試 1:模糊問題 ==========\n", | |
| " [改寫] 那個要怎麼處理? → 出差費用如何處理?\n", | |
| " [搜尋] 找到 5 個結果\n", | |
| " [重排] 取前 3 個最相關段落\n", | |
| "\n", | |
| "答:抱歉,我不太確定你指的是哪個問題或情況。能否提供更多詳細信息或明確的問題,以便我更好地協助你?\n", | |
| "\n", | |
| "========== 測試 2:部門過濾 ==========\n", | |
| " [改寫] 程式碼審查的流程是什麼? → 程式碼審查的標準操作流程是什麼?\n", | |
| " [過濾] department = 工程部\n", | |
| " [搜尋] 找到 3 個結果\n", | |
| " [重排] 取前 3 個最相關段落\n", | |
| "\n", | |
| "答:程式碼審查的流程包括對所有合併請求(PR)進行審查,並需至少一位資深工程師審查通過。審查的重點包括程式碼品質、測試覆蓋率和安全性。\n", | |
| "\n", | |
| "========== 測試 3:一般問題 ==========\n", | |
| " [改寫] 升遷需要什麼條件?考核怎麼算分? → 升遷需要什麼條件?績效考核如何計算分數?\n", | |
| " [搜尋] 找到 5 個結果\n", | |
| " [重排] 取前 3 個最相關段落\n", | |
| "\n", | |
| "答:升遷需要連續兩次考核獲得 A 以上等級,並經過部門主管推薦、人事委員會審核以及總經理核准。\n", | |
| "\n", | |
| "考核分數是通過自評佔 30%、主管評佔 50%、同儕評佔 20%來計算的。\n" | |
| ] | |
| } | |
| ], | |
| "execution_count": 11 | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "---\n", | |
| "## 7.8 終極實戰:有記憶 + RAG 的智能助手\n", | |
| "\n", | |
| "結合 Lesson 5 的 ChatMemory + Lesson 6-7 的 RAG,打造一個有記憶、能查知識庫的助手:" | |
| ], | |
| "id": "5972c04707223bac" | |
| }, | |
| { | |
| "cell_type": "code", | |
| "metadata": { | |
| "ExecuteTime": { | |
| "end_time": "2026-03-18T15:20:39.304828Z", | |
| "start_time": "2026-03-18T15:20:39.234637Z" | |
| } | |
| }, | |
| "source": [ | |
| "// 建立有記憶 + RAG 的 ChatClient\n", | |
| "val memory = MessageWindowChatMemory.builder().maxMessages(15).build()\n", | |
| "\n", | |
| "val smartAssistant = ChatClient.builder(chatModel)\n", | |
| " .defaultSystem(\"\"\"你是「公司智能助理」,具備以下能力:\n", | |
| "1. 記住對話中使用者提到的資訊(如姓名、部門)\n", | |
| "2. 根據公司知識庫回答制度和流程問題\n", | |
| "3. 用繁體中文回答,語氣親切專業\n", | |
| "4. 找不到答案時誠實告知,並建議聯繫相關部門\"\"\")\n", | |
| " .defaultAdvisors(\n", | |
| " MessageChatMemoryAdvisor.builder(memory).build(),\n", | |
| " QuestionAnswerAdvisor.builder(vectorStore)\n", | |
| " .searchRequest(SearchRequest.builder().topK(3).build())\n", | |
| " .build()\n", | |
| " )\n", | |
| " .build()\n", | |
| "\n", | |
| "fun ask(message: String): String {\n", | |
| " return smartAssistant.prompt()\n", | |
| " .user(message)\n", | |
| " .advisors { it.param(ChatMemory.CONVERSATION_ID, \"smart-session\") }\n", | |
| " .call()\n", | |
| " .content() ?: \"(無回應)\"\n", | |
| "}\n", | |
| "\n", | |
| "println(\"智能助理已就緒!(ChatMemory + RAG)\")" | |
| ], | |
| "id": "159fe716759dfcce", | |
| "outputs": [ | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "智能助理已就緒!(ChatMemory + RAG)\n" | |
| ] | |
| } | |
| ], | |
| "execution_count": 12 | |
| }, | |
| { | |
| "cell_type": "code", | |
| "metadata": { | |
| "ExecuteTime": { | |
| "end_time": "2026-03-18T15:20:55.029914Z", | |
| "start_time": "2026-03-18T15:20:42.715364Z" | |
| } | |
| }, | |
| "source": [ | |
| "// 模擬完整的多輪對話\n", | |
| "val conversation = listOf(\n", | |
| " \"你好!我是小華,工程部的新人,今天剛入職\",\n", | |
| " \"我想了解一下年假有幾天?\",\n", | |
| " \"那病假呢?需要什麼證明嗎?\",\n", | |
| " \"我們部門的程式碼審查規範是什麼?\",\n", | |
| " \"幫我總結一下我剛才問的所有制度重點\"\n", | |
| ")\n", | |
| "\n", | |
| "for (msg in conversation) {\n", | |
| " println(\"\\n\" + \"=\".repeat(50))\n", | |
| " println(\"小華:$msg\")\n", | |
| " println(\"-\".repeat(50))\n", | |
| " println(\"助理:${ask(msg)}\")\n", | |
| "}" | |
| ], | |
| "id": "227f671a7560cef", | |
| "outputs": [ | |
| { | |
| "name": "stdout", | |
| "output_type": "stream", | |
| "text": [ | |
| "\n", | |
| "==================================================\n", | |
| "小華:你好!我是小華,工程部的新人,今天剛入職\n", | |
| "--------------------------------------------------\n", | |
| "助理:你好,小華!歡迎加入工程部!在這裡,我們有一些重要的流程和規範,希望能對你有幫助。\n", | |
| "\n", | |
| "1. **部署流程**:所有程式碼需要通過 CI/CD 管線。在開發環境中,你可以自由部署;但在測試環境部署時,需要主管的核准;而在正式環境中,則需要技術主管和 PM 共同核准。\n", | |
| "\n", | |
| "2. **技術選型原則**:新專案會優先使用 Spring Boot 3 搭配 Java 21。若專案涉及 AI 功能,將使用 Spring AI 框架。前端方面,你可以選擇使用 React 或 Vue。\n", | |
| "\n", | |
| "3. **程式碼審查規範**:所有的合併請求(PR)需至少一位資深工程師審查通過,重點會放在程式碼品質、測試覆蓋率和安全性。\n", | |
| "\n", | |
| "如果有任何其他問題或需要進一步的協助,隨時告訴我!祝你在這裡工作順利!\n", | |
| "\n", | |
| "==================================================\n", | |
| "小華:我想了解一下年假有幾天?\n", | |
| "--------------------------------------------------\n", | |
| "助理:根據公司的年假規定,所有全職員工每年享有 14 天年假。到職滿一年後,每增加一年年資會增加 1 天年假,最多可以累積到 30 天。申請年假時,需提前 3 天提出申請。如果你有更多疑問或需要進一步的協助,建議你可以聯繫人力資源部門了解更多詳情。\n", | |
| "\n", | |
| "==================================================\n", | |
| "小華:那病假呢?需要什麼證明嗎?\n", | |
| "--------------------------------------------------\n", | |
| "助理:根據公司的病假規定,員工每年有 30 天的全薪病假。如果病假超過 3 天,你需要提供醫生的證明。住院期間的時間不會計入病假天數。如果有其他問題或需要進一步的幫助,建議你可以聯繫人力資源部門了解更多詳情。\n", | |
| "\n", | |
| "==================================================\n", | |
| "小華:我們部門的程式碼審查規範是什麼?\n", | |
| "--------------------------------------------------\n", | |
| "助理:在我們工程部,程式碼審查規範為:所有合併請求(PR)需至少一位資深工程師審查通過。審查的重點包括程式碼品質、測試覆蓋率和安全性。如果有其他問題或需要進一步的資訊,隨時告訴我!\n", | |
| "\n", | |
| "==================================================\n", | |
| "小華:幫我總結一下我剛才問的所有制度重點\n", | |
| "--------------------------------------------------\n", | |
| "助理:好的,小華,以下是你剛才問到的制度重點總結:\n", | |
| "\n", | |
| "1. **程式碼審查規範**:所有合併請求(PR)需至少一位資深工程師審查通過,重點在於程式碼品質、測試覆蓋率和安全性。\n", | |
| "\n", | |
| "2. **部署流程**:所有程式碼必須通過 CI/CD 管線。在開發環境中可以自由部署,測試環境需要主管核准,正式環境則需要技術主管和 PM 雙重核准。\n", | |
| "\n", | |
| "3. **績效考核**:每半年進行一次,評估由自評(30%)、主管評(50%)和同儕評(20%)組成,等級分為 S/A/B/C/D,達到 B 級以上可獲得年終獎金。\n", | |
| "\n", | |
| "如果還有其他問題或需要更多資訊,隨時告訴我!\n" | |
| ] | |
| } | |
| ], | |
| "execution_count": 13 | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "### 觀察重點\n", | |
| "\n", | |
| "1. **記憶**:助理記得使用者叫小華、是工程部新人\n", | |
| "2. **RAG**:制度相關的問題都能從知識庫中找到答案\n", | |
| "3. **多輪理解**:「那病假呢?」— 知道前面在聊休假\n", | |
| "4. **總結能力**:能回顧整個對話中提到的所有制度" | |
| ], | |
| "id": "43c1d815d508e7a5" | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "---\n", | |
| "## 7.9 RAG 效能調校建議\n", | |
| "\n", | |
| "| 調校項目 | 建議 |\n", | |
| "|---------|------|\n", | |
| "| **分塊大小** | 200-500 字最佳,太大搜不準、太小缺上下文 |\n", | |
| "| **topK** | 開始用 3-5,根據效果調整 |\n", | |
| "| **Metadata** | 越豐富越好:來源、類別、日期、部門 |\n", | |
| "| **Embedding 模型** | `text-embedding-3-small`(快)vs `text-embedding-3-large`(準) |\n", | |
| "| **查詢改寫** | 對模糊問題效果顯著,但增加一次 AI 呼叫成本 |\n", | |
| "| **Re-ranking** | 正式環境用 Voyage AI / Cohere,效果最好 |\n", | |
| "\n", | |
| "### 選擇策略\n", | |
| "\n", | |
| "```\n", | |
| "資料量小(< 100 份文件):\n", | |
| " → Naive RAG + Metadata 過濾就夠了\n", | |
| "\n", | |
| "資料量中(100 - 10000):\n", | |
| " → + 查詢改寫 + Re-ranking\n", | |
| "\n", | |
| "資料量大(> 10000):\n", | |
| " → + 混合搜尋(向量 + 關鍵字)\n", | |
| " → + 專業 Embedding 模型\n", | |
| " → + 專業 Re-ranking 服務\n", | |
| "```" | |
| ], | |
| "id": "3f091ca8ae6044d3" | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "---\n", | |
| "## 本節重點回顧\n", | |
| "\n", | |
| "| 概念 | 重點 |\n", | |
| "|------|------|\n", | |
| "| **Naive vs Advanced RAG** | Advanced 在搜尋前後加入優化步驟 |\n", | |
| "| **Metadata 過濾** | 用 FilterExpression 縮小搜尋範圍 |\n", | |
| "| **查詢改寫** | AI 改寫模糊問題為精確查詢 |\n", | |
| "| **Re-ranking** | 對搜尋結果重新排序,提升相關性 |\n", | |
| "| **ChatMemory + RAG** | 結合記憶和知識庫,打造智能助手 |\n", | |
| "| **效能調校** | 分塊大小、topK、Metadata 是關鍵 |\n", | |
| "\n", | |
| "### 到目前為止的學習路徑\n", | |
| "\n", | |
| "```\n", | |
| "Lesson 1-2: ChatClient 基礎 + 流式輸出\n", | |
| "Lesson 3: 提示詞工程 + 結構化輸出\n", | |
| "Lesson 4: Function Calling(讓 AI 做事)\n", | |
| "Lesson 5: ChatMemory(讓 AI 記住)\n", | |
| "Lesson 6: RAG 基礎(讓 AI 讀文件)\n", | |
| "Lesson 7: Advanced RAG(讓 AI 讀得更準) ← 你在這裡\n", | |
| "```" | |
| ], | |
| "id": "9a4d113e2b8b1716" | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "---\n", | |
| "## 下一步\n", | |
| "\n", | |
| "**Lesson 8:多模態處理 — 讓 AI 看見與聽見**\n", | |
| "- 對應教材:5.2 多模態處理、5.3 圖像生成\n", | |
| "- 圖片分析與圖像生成\n", | |
| "- 語音轉文字與語音合成" | |
| ], | |
| "id": "42ceda22325b2ff3" | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "---\n", | |
| "## 參考資源\n", | |
| "\n", | |
| "- [Spring AI RAG 文件](https://docs.spring.io/spring-ai/reference/api/retrieval-augmented-generation.html)\n", | |
| "- [Spring AI Filter Expression](https://docs.spring.io/spring-ai/reference/api/vectordbs.html#metadata-filters)\n", | |
| "- [Advanced RAG Techniques](https://docs.spring.io/spring-ai/reference/api/retrieval-augmented-generation.html)\n", | |
| "- 教材 8.1 Advanced RAG 概覽\n", | |
| "- 教材 8.2 Embedding 模型優化\n", | |
| "- 教材 8.3 Re-ranking 實戰" | |
| ], | |
| "id": "b38cc21e4f742eab" | |
| } | |
| ], | |
| "metadata": { | |
| "kernelspec": { | |
| "display_name": "Kotlin", | |
| "language": "kotlin", | |
| "name": "kotlin" | |
| }, | |
| "language_info": { | |
| "name": "kotlin" | |
| } | |
| }, | |
| "nbformat": 4, | |
| "nbformat_minor": 5 | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment