# LLM Zoomcamp 第一堂課：LLM & RAG 介紹

**URL:** https://vip.studycamp.tw/t/llm-zoomcamp-%E7%AC%AC%E4%B8%80%E5%A0%82%E8%AA%B2%EF%BC%9Allm-rag-%E4%BB%8B%E7%B4%B9/8113
**Category:** 自然語言處理
**Tags:** llmzoomcamp
**Created:** [2025年六月2日 04:19 UTC](https://vip.studycamp.tw/t/llm-zoomcamp-%E7%AC%AC%E4%B8%80%E5%A0%82%E8%AA%B2%EF%BC%9Allm-rag-%E4%BB%8B%E7%B4%B9/8113 "2025-06-02T04:19:12Z")
**Posts on this page:** 1
**Page:** 1

<div class="post-metadata">

### Author: ![sky](https://vip.studycamp.tw/user_avatar/vip.studycamp.tw/sky/32/8098_2.png) [@sky](https://vip.studycamp.tw/u/sky)
#### Post date: [2025年六月2日 04:19 UTC](https://vip.studycamp.tw/t/llm-zoomcamp-%E7%AC%AC%E4%B8%80%E5%A0%82%E8%AA%B2%EF%BC%9Allm-rag-%E4%BB%8B%E7%B4%B9/8113/1 "2025-06-02T04:19:12Z")

</div>

> 2025年5月27日的錄影 只是課程介紹，所以本筆記還是依照 2024年的 01-intro 撰寫。
> 
> 本筆記由 Claude 協助（草稿和內容理解討論），有可能不完全正確。

## ▌關於本課程

> 指 LLM Zoomcamp 2025

### 》課程概述

- **課程名稱：** LLM Zoomcamp 2025 - DataTalks.Club
- **課程目標：** 10 週學會建立一個能回答自有知識庫問題的 AI 系統

### 》學習目標

1. 簡述什麼是LLM（大型語言模型）和RAG（檢索增強生成）
2. 實作一個簡單的 RAG 流程
3. 建立問答系統來回答 Zoomcamp 幾個課程中的 FAQ 文件
4. 學習使用不同的搜尋引擎（MinSearch 和 ElasticSearch）

> 講師不會深入說明 LLM 和 RAG，直接視 LLM 為一個黑盒子，RAG 也多著墨在實作。
> 
> 本課程純粹就是直接實作，透過實作一個簡單專案，來示範 RAG，並要求學生在期末，繳交一個 RAG 成品（題目自訂。不知道做什麼的話，就用講師的範例改進）。

### 》作業

作業的部分，比較不直觀，不容易找到。我把我建議看的資料，統整在這裡：

- [**教學影片**](https://www.youtube.com/watch?v=Q75JgLEXMsM&list=PL3MmuxUbc_hIB4fSqLy_0AfTjVLpgjV3R&index=2)（1.1~1.6）
- [**程式：Jupyter Notebook**](https://github.com/DataTalksClub/llm-zoomcamp/blob/main/01-intro/rag-intro.ipynb)（GitHub - rag-intro.ipynb）
- [**作業題目**](https://github.com/DataTalksClub/llm-zoomcamp/blob/main/cohorts/2025/01-intro/homework.md)
- [**作業繳交**](https://courses.datatalks.club/llm-zoomcamp-2025/homework/hw1)（6月17日前繳交，時差因素建議提早）

## ▌RAG 系統架構

### 》什麼是 RAG

RAG（Retrieval Augmented Generation）= Retrieval（檢索）+ Augmented（增強）+ Generation（生成）

- 傳統LLM的限制：用戶問題 → LLM → 基於訓練資料的回答

- RAG：用戶問題 → 檢索相關文檔 → LLM + 檢索內容 → 增強後的回答

RAG 包含三個核心組件：

### 1. 搜尋引擎模組（Search Engine）

- **用途：** 預處理來源文件
- **目的：** 限制提示的大小以應對上下文視窗限制
- **實作選項：**
  - MinSearch（輕量級，記憶體內運行）
  - ElasticSearch（生產環境推薦）

### 2. 提示建構模組（Prompt Builder）

- **功能：** 組合問題、用戶提示和來源文件
- **輸出：** 傳遞給LLM的完整字串

### 3. LLM 生成模組（大型語言模型）

- **功能：** 接收前一步的提示作為輸入
- **實作：** API呼叫到選擇的LLM服務

將講師的架構圖，理解如下。

其中以 […] 包起來的模組，就是程式實作的部分，可以被替換或優化（例如由 MinSearch 改為 ElasticSearch）。

```txt
用戶提問（資料） 
    ↓
[搜尋引擎模組] ←→ 知識庫（資料）
    ↓
相關文檔（資料）
    ↓  
[提示建構模組]
    ↓
完整提示（資料）
    ↓
[LLM生成模組] ←→ API/模型（外部服務）
    ↓
最終答案（資料）

```

我用 [**Mermaid 流程圖**](https://vip.studycamp.tw/t/6588/1) 再畫一次如下。

藍色方框 - 資料、橙色菱形 - 處理模組、紫色方框 - 外部服務。

```mermaid
flowchart TD
    A[用戶提問<br/>資料] --> B{搜尋引擎模組<br/>處理組件}
    C[(知識庫<br/>資料)] <--> B
    B --> D[相關文檔<br/>資料]
    D --> E{提示建構模組<br/>處理組件}
    E --> F[完整提示<br/>資料]
    F --> G{LLM生成模組<br/>處理組件}
    H[API/模型<br/>外部服務] <--> G
    G --> I[最終答案<br/>資料]

    %% 樣式設定
    classDef dataNode fill:#e1f5fe,stroke:#01579b,stroke-width:2px,color:#000
    classDef processNode fill:#fff3e0,stroke:#e65100,stroke-width:2px,color:#000
    classDef externalNode fill:#f3e5f5,stroke:#4a148c,stroke-width:2px,color:#000

    %% 套用樣式
    class A,C,D,F,I dataNode
    class B,E,G processNode
    class H externalNode

```

接著我請 Claude 提出是否有可以改進的地方，以下是回覆。這部分可以作為未來作業的改進方法選項，但與目前進度無關，我先隱藏起來，感興趣的朋友，可以點擊展開觀看。

> **Claude 建議的改進方法，點擊展開觀看**
>
> ```mermaid
> flowchart TD
> A[用戶提問] --> B{查詢分析器}
> B --> C{混合檢索器}
>     
> D[(向量資料庫)] --> C
> E[(關鍵字索引)] --> C
> F[(知識圖譜)] --> C
>     
> C --> G{重排序器}
> G --> H[相關文檔]
>     
> H --> I{智能提示建構器}
> B --> I
> J[歷史對話] --> I
>     
> I --> K[完整提示]
> K --> L{LLM生成器}
>     
> L --> M[初步回答]
> M --> N{品質檢查器}
>     
> N --> O{回答是否合格?}
> O -->|是| P[最終答案]
> O -->|否| Q{回饋優化}
> Q --> I
>     
> N --> R[(監控資料庫)]
> P --> S[用戶回饋]
> S --> R
> 
> %% 樣式設定
> classDef input fill:#e8f5e8,stroke:#2e7d32,stroke-width:2px
> classDef process fill:#fff3e0,stroke:#e65100,stroke-width:2px
> classDef storage fill:#e3f2fd,stroke:#1565c0,stroke-width:2px
> classDef output fill:#fce4ec,stroke:#c2185b,stroke-width:2px
> classDef decision fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
> 
> class A,J,S input
> class B,C,G,I,L,N,Q process
> class D,E,F,R storage
> class H,K,M,P output
> class O decision
> 
> ```

## ▌各Module對RAG組件的具體影響

下表是前述三個模組（搜尋引擎模組 | 提示建構模組 | LLM模組）在本課程各章節演進示意（正確性待驗證）。

| Module | 搜尋引擎模組 | 提示建構模組 | LLM模組 | 主要改進重點 |
| --- | --- | --- | --- | --- |
| 01-intro | MinSearch／ElasticSearch | 基本提示模板 | OpenAI API | 建立基礎架構 |
| 02-open-source | 沿用Module 1 | 沿用Module 1 | 開源LLM (HuggingFace, Ollama) | LLM模組替換 |
| 03-vector | 向量搜尋 (Qdrant, 語意檢索) | 沿用或微調 | 沿用Module 2 | 搜尋引擎模組升級 |
| 04-evaluation | 沿用Module 3 | 評估導向 提示 | 沿用Module 2/3 | 整個流程監控改進 |
| 05-orchestration | 混合搜尋 + 自動化 | 動態提示 | 沿用 | 生產環境整合 |
| 06-project | 自選最佳組合 | 自選最佳組合 | 自選最佳組合 | 端到端實作 |

### 》Module 01-intro (本章內容)

```python
# 建立三個基礎模組
def search_engine(): # MinSearch／ElasticSearch
def prompt_builder(): # 基本模板  
def llm_generator(): # OpenAI API

```

### 》Module 02-open-source

```python
# 主要升級：LLM模組
def search_engine(): # 沿用 Module 1
def prompt_builder(): # 沿用 Module 1
def llm_generator(): # 改用 HuggingFace／Ollama

```

### 》Module 03-vector

```python
# 主要升級：搜尋引擎模組
def search_engine(): # 改用向量搜尋/語意檢索
def prompt_builder(): # 可能微調以配合向量搜尋
def llm_generator(): # 沿用 Module 2

```

### 》Module 04-evaluation

```python
# 主要升級：加入評估和監控機制
def search_engine(): # 沿用 Module 3 + 評估指標
def prompt_builder(): # 加入評估導向的提示優化
def llm_generator(): # 沿用 + 回答品質評估
def evaluation_system(): # 新增：整個流程的監控

```

## ▌資料來源與結構

### 》文件來源

課程使用 DataTalks.Club 各個 Zoomcamp 課程的 FAQ 文件：

- Data Engineering Zoomcamp
- MLOps Zoomcamp
- Machine Learning Zoomcamp

### 》文件結構

每個文件包含以下欄位（講師已整理為 JSON 格式，並附程式）：

```json
{
  "text": "問題的詳細回答內容",
  "section": "所屬章節",
  "question": "問題標題", 
  "course": "課程名稱"
}

```

### 》資料載入程式碼

```python
import requests
import json

# 下載FAQ文件
docs_url = 'https://github.com/DataTalksClub/llm-zoomcamp/blob/main/01-intro/documents.json?raw=1'
docs_response = requests.get(docs_url)
documents_raw = docs_response.json()

# 處理文件結構
documents = []
for course in documents_raw:
    course_name = course['course']
    for doc in course['documents']:
        doc['course'] = course_name
        documents.append(doc)

```

* * *

**MinSearch vs. ElasticSearch 技術實現**

| 特徵 | MinSearch | ElasticSearch |
| --- | --- | --- |
| **實現方式** | Python 記憶體內 | 分散式搜尋引擎 |
| **演算法** | TF-IDF + 餘弦相似度 | BM25 + 更多進階演算法 |
| **資料儲存** | 記憶體中 | 磁碟持久化 |
| **部署方式** | 嵌入式（Python 程式中的一個模組） | 獨立服務（獨立存在於伺服器） |

**功能目標相同**

兩者都是為了在 RAG 系統中擔任搜尋引擎的角色：

- 建立文檔索引
- 接受查詢請求
- 返回相關文檔

**工作流程相同**

```python
# 兩者都遵循相同的使用模式
# 1. 建立索引
index = create_index()

# 2. 索引文檔
index.fit(documents) # MinSearch
# 或
index_documents(es_client, documents) # ElasticSearch

# 3. 搜尋
results = index.search(query) # MinSearch
# 或  
results = es_client.search(query) # ElasticSearch

```

**都支援字段權重**

> ElasticSearch 的權重，也可以不是整數。

```python
python# MinSearch
boost = {"question": 3, "text": 1, "section": 0.5}

# ElasticSearch  
"fields": ["question^3", "text", "section"]

```

**都支援過濾**

```python
# MinSearch
filter_dict = {"course": "data-engineering-zoomcamp"}

# ElasticSearch
"filter": {"term": {"course": "data-engineering-zoomcamp"}}

```

## ▌MinSearch 實作

### 》[MinSearch 簡介](https://github.com/alexeygrigorev/minsearch/blob/main/minsearch.py)

> 連結為講師改寫的 MinSearch source code，一個 class，裡面就兩個 method: fit & search。
> 
> 如果你想看 **[原始的 MinSearch](https://raw.githubusercontent.com/alexeygrigorev/minsearch/main/minsearch.py)**。

MinSearch 是一個極簡的記憶體文字搜尋引擎（Python object），主要用於教學目的。  
fit method：對提供的文件建立索引。  
search method：核心搜尋功能，支援查詢、過濾和權重調整。

- 使用 TF-IDF 和餘弦相似度進行搜尋
- 輕量級實作，適合無法託管 ElasticSearch 的環境
- 在記憶體中運行

### 》建立索引

```python
import minsearch

# 建立索引
index = minsearch.Index(
    text_fields=["question", "text", "section"],
    keyword_fields=["course"]
)

# 建立索引
index.fit(documents)

```

### 》搜尋函數

```python
def search(query, num_results=5):
    boost = {
        "question": 3, # 問題欄位權重最高
        "text": 1, # 內容欄位標準權重
        "section": 0.5 # 章節欄位權重較低
    }
    
    results = index.search(
        query=query,
        filter_dict={"course": "data-engineering-zoomcamp"},
        boost_dict=boost,
        num_results=num_results
    )
    return results

```

## ▌ElasticSearch 實作

### 》Docker 啟動指令

```bash
docker run -it \
  --rm \
  --name elasticsearch \
  -m 4GB \
  -p 9200:9200 \
  -p 9300:9300 \
  -e "discovery.type=single-node" \
  -e "xpack.security.enabled=false" \
  docker.elastic.co/elasticsearch/elasticsearch:8.4.3

```

### 》索引設定

```python
from elasticsearch import Elasticsearch

# 建立連接
es_client = Elasticsearch('http://localhost:9200')

# 索引設定
index_settings = {
    "settings": {
        "number_of_shards": 1,
        "number_of_replicas": 0
    },
    "mappings": {
        "properties": {
            "text": {"type": "text"},
            "section": {"type": "text"},
            "question": {"type": "text"},
            "course": {"type": "keyword"}
        }
    }
}

```

### 》ElasticSearch 查詢

```python
def elastic_search(query):
    search_query = {
        "size": 5,
        "query": {
            "bool": {
                "must": {
                    "multi_match": {
                        "query": query,
                        "fields": ["question^3", "text", "section"],
                        "type": "best_fields"
                    }
                },
                "filter": {
                    "term": {
                        "course": "data-engineering-zoomcamp"
                    }
                }
            }
        }
    }
    
    response = es_client.search(index=index_name, body=search_query)
    result_docs = []
    for hit in response["hits"]["hits"]:
        result_docs.append(hit["_source"])
    return result_docs

```

## ▌LLM整合

### 》提示模板

```python
def build_prompt(query, search_results):
    context = ""
    for doc in search_results:
        context += f"section: {doc['section']}\n"
        context += f"question: {doc['question']}\n"  
        context += f"answer: {doc['text']}\n\n"
    
    prompt_template = """
    You're a course teaching assistant. Answer the QUESTION based on the CONTEXT from the FAQ database. 
    Use only the facts from the CONTEXT when answering the QUESTION.

    QUESTION: {question}

    CONTEXT: {context}
    """
    
    prompt = prompt_template.format(question=query, context=context).strip()
    return prompt

```

### 》LLM呼叫

> 以 OpenAI 為例

```python
from openai import OpenAI

client = OpenAI()

def llm(prompt):
    response = client.chat.completions.create(
        model='gpt-3.5-turbo',
        messages=[{"role": "user", "content": prompt}]
    )
    return response.choices[0].message.content

```

## ▌完整RAG流程

### 》整合函數

```python
def rag(query):
    # 1. 搜尋相關文件
    search_results = search(query) # 或 elastic_search(query)
    
    # 2. 建構提示
    prompt = build_prompt(query, search_results)
    
    # 3. 獲取LLM回應
    answer = llm(prompt)
    
    return answer

```

### 》使用範例

```python
query = "I just discovered the course. Can I still join it?"
answer = rag(query)
print(answer)

```

## ▌技術要求與環境設定

### 》Python套件

```bash
pip install tqdm notebook openai elasticsearch pandas scikit-learn python-dotenv

```

### 》文件結構

```auto
01-intro/
├── rag-intro.ipynb
├── documents.json
├── minsearch.py
├── .env
└── requirements.txt

```

## ▌設計原則與最佳實踐

### 》模組化設計

- 每個組件可以獨立替換
- 例如：可以輕鬆將MinSearch替換為ElasticSearch
- 保持整個流程不變的情況下更換個別元件

### 》權重調整策略

- **question欄位** ：權重3（最重要）
- **text欄位** ：權重1（標準）
- **section欄位** ：權重0.5（輔助）

### 》性能考量

- MinSearch：適合開發和小規模部署
- ElasticSearch：適合生產環境和大規模部署
- 可根據部署環境選擇合適的搜尋引擎

## ▌後續方向

1. **第二章預告** ：探索開源LLM替代方案
2. **本地運行** ：使用Ollama在CPU上運行LLM
3. **向量搜尋** ：第三章將介紹向量資料庫
4. **評估與監控** ：第四章將涵蓋系統評估
5. **進階技術** ：混合搜尋、文件重排序等

### 》提示

- 課程提供完整的Jupyter Notebook實作
- 支援多種LLM提供商（OpenAI、Groq、本地模型等）
- 強調實用性和生產環境適用性
- 包含豐富的社群支援和討論

* * *

## ▌參考資料

> **[llm-zoomcamp/01-intro at main · DataTalksClub/llm-zoomcamp](https://github.com/DataTalksClub/llm-zoomcamp/tree/main/01-intro)**
>
> LLM Zoomcamp - a free online course about real-life applications of LLMs. In 10 weeks you will learn how to build an AI system that answers questions about your knowledge base. - DataTalksClub/llm-...

> <https://github.com/DataTalksClub/llm-zoomcamp/blob/main/01-intro/rag-intro.ipynb>
