2025年5月27日的錄影 只是課程介紹,所以本筆記還是依照 2024年的 01-intro 撰寫。
本筆記由 Claude 協助(草稿和內容理解討論),有可能不完全正確。
▌關於本課程
指 LLM Zoomcamp 2025
》課程概述
- 課程名稱: LLM Zoomcamp 2025 - DataTalks.Club
- 課程目標: 10 週學會建立一個能回答自有知識庫問題的 AI 系統
》學習目標
- 簡述什麼是LLM(大型語言模型)和RAG(檢索增強生成)
- 實作一個簡單的 RAG 流程
- 建立問答系統來回答 Zoomcamp 幾個課程中的 FAQ 文件
- 學習使用不同的搜尋引擎(MinSearch 和 ElasticSearch)
講師不會深入說明 LLM 和 RAG,直接視 LLM 為一個黑盒子,RAG 也多著墨在實作。
本課程純粹就是直接實作,透過實作一個簡單專案,來示範 RAG,並要求學生在期末,繳交一個 RAG 成品(題目自訂。不知道做什麼的話,就用講師的範例改進)。
》作業
作業的部分,比較不直觀,不容易找到。我把我建議看的資料,統整在這裡:
- 教學影片(1.1~1.6)
- 程式:Jupyter Notebook(GitHub - rag-intro.ipynb)
- 作業題目
- 作業繳交(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)。
用戶提問(資料)
↓
[搜尋引擎模組] ←→ 知識庫(資料)
↓
相關文檔(資料)
↓
[提示建構模組]
↓
完整提示(資料)
↓
[LLM生成模組] ←→ API/模型(外部服務)
↓
最終答案(資料)
我用 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 建議的改進方法,點擊展開觀看
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 (本章內容)
# 建立三個基礎模組
def search_engine(): # MinSearch/ElasticSearch
def prompt_builder(): # 基本模板
def llm_generator(): # OpenAI API
》Module 02-open-source
# 主要升級:LLM模組
def search_engine(): # 沿用 Module 1
def prompt_builder(): # 沿用 Module 1
def llm_generator(): # 改用 HuggingFace/Ollama
》Module 03-vector
# 主要升級:搜尋引擎模組
def search_engine(): # 改用向量搜尋/語意檢索
def prompt_builder(): # 可能微調以配合向量搜尋
def llm_generator(): # 沿用 Module 2
》Module 04-evaluation
# 主要升級:加入評估和監控機制
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 格式,並附程式):
{
"text": "問題的詳細回答內容",
"section": "所屬章節",
"question": "問題標題",
"course": "課程名稱"
}
》資料載入程式碼
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 系統中擔任搜尋引擎的角色:
- 建立文檔索引
- 接受查詢請求
- 返回相關文檔
工作流程相同
# 兩者都遵循相同的使用模式
# 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# MinSearch
boost = {"question": 3, "text": 1, "section": 0.5}
# ElasticSearch
"fields": ["question^3", "text", "section"]
都支援過濾
# MinSearch
filter_dict = {"course": "data-engineering-zoomcamp"}
# ElasticSearch
"filter": {"term": {"course": "data-engineering-zoomcamp"}}
▌MinSearch 實作
》MinSearch 簡介
連結為講師改寫的 MinSearch source code,一個 class,裡面就兩個 method: fit & search。
如果你想看 原始的 MinSearch。
MinSearch 是一個極簡的記憶體文字搜尋引擎(Python object),主要用於教學目的。
fit method:對提供的文件建立索引。
search method:核心搜尋功能,支援查詢、過濾和權重調整。
- 使用 TF-IDF 和餘弦相似度進行搜尋
- 輕量級實作,適合無法託管 ElasticSearch 的環境
- 在記憶體中運行
》建立索引
import minsearch
# 建立索引
index = minsearch.Index(
text_fields=["question", "text", "section"],
keyword_fields=["course"]
)
# 建立索引
index.fit(documents)
》搜尋函數
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 啟動指令
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
》索引設定
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 查詢
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整合
》提示模板
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 為例
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流程
》整合函數
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
》使用範例
query = "I just discovered the course. Can I still join it?"
answer = rag(query)
print(answer)
▌技術要求與環境設定
》Python套件
pip install tqdm notebook openai elasticsearch pandas scikit-learn python-dotenv
》文件結構
01-intro/
├── rag-intro.ipynb
├── documents.json
├── minsearch.py
├── .env
└── requirements.txt
▌設計原則與最佳實踐
》模組化設計
- 每個組件可以獨立替換
- 例如:可以輕鬆將MinSearch替換為ElasticSearch
- 保持整個流程不變的情況下更換個別元件
》權重調整策略
- question欄位:權重3(最重要)
- text欄位:權重1(標準)
- section欄位:權重0.5(輔助)
》性能考量
- MinSearch:適合開發和小規模部署
- ElasticSearch:適合生產環境和大規模部署
- 可根據部署環境選擇合適的搜尋引擎
▌後續方向
- 第二章預告:探索開源LLM替代方案
- 本地運行:使用Ollama在CPU上運行LLM
- 向量搜尋:第三章將介紹向量資料庫
- 評估與監控:第四章將涵蓋系統評估
- 進階技術:混合搜尋、文件重排序等
》提示
- 課程提供完整的Jupyter Notebook實作
- 支援多種LLM提供商(OpenAI、Groq、本地模型等)
- 強調實用性和生產環境適用性
- 包含豐富的社群支援和討論