從零打造 Entity Embedding Graph RAG:一門實戰迷你課的完整藍圖
從零打造 Entity Embedding Graph RAG:一門實戰迷你課的完整藍圖
這篇文章是我規劃「Entity Embedding Graph RAG」迷你課程的完整思考記錄。核心概念只有一句話:用語意相似度取代關鍵字匹配來建構圖的邊。用 LanceDB + NetworkX 幾十行 Python 就能落地,學完直接變 portfolio。
這門課要解決什麼問題?
在上一篇中,我得出了一個結論:200 nodes 的個人知識圖譜不需要升級到 Memgraph 或 HelixDB,真正值得做的是 Entity Embedding Graph——用 embedding 相似度自動建構 skill 之間的語意邊,取代手動維護的 skill-graph.json。
這門課就是把這個想法從概念變成可落地的教學內容。
目標學員
- 想快速上手 GraphRAG 的工程師
- 已經會 Python,對 embedding 有基本認知
- 想要一個能寫進履歷的 side project
學完之後
學員會擁有一個完整的 Graph-RAG Career Assistant:輸入 career 相關問題,系統透過 semantic skill graph + chunk retrieval 給出有結構的回答。Repo 直接 pin 到 GitHub profile 當 portfolio。
為什麼是 Entity Embedding Graph?
在切入課綱之前,先回顧三種 Semantic Graph 的實作方式,解釋為什麼選 B:
三條路線
| 比較面向 | A: Chunk kNN | B: Entity Embedding | C: HelixDB / LightRAG |
|---|---|---|---|
| 核心思路 | chunk 之間靠向量相似度連邊 | skill entity 靠向量相似度連邊 | vector + graph 融合在同一 DB |
| 新程式碼行數 | ~25 行 | ~40 行 | 大量 |
| 需要新套件 | ❌ | ❌ | ✅ |
| 適合 200 筆 | ✅ | ✅ | ❌ overkill |
為什麼 B 最適合教學?
方式 A(Chunk kNN) 在單一 Q&A 場景表現好,但對「Kubernetes 需要什麼?」這種多 skill 比較問題只能靠 chunk 裡碰巧提到的內容,結果不穩定。
方式 B(Entity Embedding) 直接在 skill 層級建圖,捕捉 skill 之間的結構化語意關係。embed("Kubernetes") · embed("Docker") = 0.87——這種邊不是人工定義的,是語意空間自動發現的。
效果差異一目了然:
| 情境 | 方式 A (chunk) | 方式 B (entity) |
|---|---|---|
| 單 chunk Q&A | 好 | 中等 |
| 多 skill 比較 | 中等 | 優秀 |
| 整體 RAG 準確率提升 | +10–20% | +30–90% |
| Career knowledge graph | 快但有雜訊 | 精準且穩定 |
方式 C 概念最美,但 200 nodes 殺雞用牛刀。課程不應該讓學員花 80% 時間搞環境。
最佳策略:B 為主幹,A 為輔助
- Entity Embedding Graph(方式 B) 作為主幹:skill 之間的語意邊
- Chunk kNN(方式 A) 作為 Hybrid 擴展:用 skill graph 導航後,再到 chunk 裡撈具體內容
這就是 HybridRAG——entity 給結構,chunk 給內容。
課程大綱:6 章 × 2–4 小時
每一章都能對應到一個實際的 .py 或 .ipynb 產出物。目標是「做中學」,不是講空泛的 embedding 理論。
Ch1:為什麼需要 Skill Entity Graph?(15 min)
目標:讓學員 10 分鐘內懂「為什麼用 Entity Embedding Graph,而不是只做 vector search 或手寫 skill-graph.json」。
內容:
-
三種 graph 方式的對比:純 keyword graph → chunk kNN → skill entity graph
-
Demo:同一個 query「Kubernetes 需要什麼?」在
- 只用 vector search
- 加上 skill entity graph
產出的 context 差異
對應檔案:notebooks/01_motivation.ipynb
作業:跑 demo,自己觀察兩種方式的回答品質差異。
Ch2:Embedding 夠用的基礎(20 min)
目標:只講「夠用」的 embedding 知識,用實際會用到的模型當例子。
內容:
- 什麼是 embedding:文字 → 向量,靠 cosine similarity 衡量語意相近
- LLM vs embedding model:LLM 是「生成」,sentence-transformers 是「編碼」
- 為什麼選 BGE / MiniLM 這種 sentence embedding model,而不隨便拿 LLM last hidden state
- 如何評估 embedding 品質:用幾組技能對(很近 / 很遠)看 cosine 分佈
實作小段:
from sentence_transformers import SentenceTransformer
import numpy as np
model = SentenceTransformer("BAAI/bge-m3")
skills = ["Python", "FastAPI", "Kubernetes", "Docker", "Photoshop"]
embeddings = model.encode(skills)
# Similarity matrix — 讓學員「看見」語意距離
for i, s1 in enumerate(skills):
for j, s2 in enumerate(skills):
sim = np.dot(embeddings[i], embeddings[j]) / (
np.linalg.norm(embeddings[i]) * np.linalg.norm(embeddings[j])
)
if i < j:
print(f" {s1} ↔ {s2}: {sim:.3f}")
學員會看到 Python–FastAPI 相似度遠高於 Python–Photoshop,直覺理解 embedding 的意義。
對應檔案:notebooks/02_embedding_basics.ipynb
作業:embed 自己的技能清單,觀察哪些技能 cluster 在一起。
Ch3:Skill List → Entity Embedding Table(25 min)
目標:從「一串技能名稱」變成「可查詢的 LanceDB 向量表」,為建圖打底。
內容:
- Skill schema 設計:
id, name, category, level, description, vector - 為什麼
description有時候比只 embedname還穩定 - 批次 embedding + 存進 LanceDB
- 寫一個可重複執行、增量更新的 CLI 工具
# embed_skills.py — 核心邏輯
import lancedb
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("BAAI/bge-m3")
def embed_and_store(skills_yaml_path, db_path="data/skills.lance"):
skills = load_yaml(skills_yaml_path)
texts = [f"{s['name']}: {s.get('description', s['name'])}" for s in skills]
vectors = model.encode(texts)
db = lancedb.connect(db_path)
table = db.create_table("skills", [
{"id": i, "name": s["name"], "category": s.get("category", ""),
"text": texts[i], "vector": vec.tolist()}
for i, (s, vec) in enumerate(zip(skills, vectors))
], mode="overwrite")
return table
對應檔案:src/embed_skills.py
CLI 用法:python src/embed_skills.py --input data/skills.yaml --out data/skills.lance
作業:加 10 個新 skill 並重新 embed,觀察 table 變化。
Ch4:用相似度自動建 Skill Graph — 核心章(30 min)
目標:完整實作方式 B,從向量表 → NetworkX skill graph。
本章設計為三步:
Step 1:kNN 查詢
# build_graph.py — 核心邏輯
import networkx as nx
def build_knn_graph(table, top_k=5, threshold=0.2):
"""用 embedding 相似度自動建立 skill 之間的語意圖。"""
skills = table.to_pandas()
graph = nx.Graph()
for _, row in skills.iterrows():
graph.add_node(row["name"], category=row.get("category", ""))
for _, row in skills.iterrows():
results = table.search(row["vector"]).limit(top_k + 1).to_list()
for r in results:
if r["name"] != row["name"] and r["_distance"] < threshold:
similarity = 1 - r["_distance"]
graph.add_edge(row["name"], r["name"], weight=similarity)
return graph
Step 2:邊權重與圖類型
nx.Graph()vsnx.DiGraph():skills 通常是無向「相似」關係weight = 1 - distance:方便後續做加權最短路 / ranking
Step 3:Hyperparameter 調整策略
top_k太小 → graph 斷開成孤島,太大 → 變雜訊threshold直接畫 histogram,挑讓「平均 degree 介於 3–8」的區間
# 調參工具
import matplotlib.pyplot as plt
distances = []
for _, row in skills.iterrows():
results = table.search(row["vector"]).limit(20).to_list()
distances.extend([r["_distance"] for r in results if r["name"] != row["name"]])
plt.hist(distances, bins=50)
plt.axvline(x=0.2, color='r', linestyle='--', label='threshold=0.2')
plt.xlabel("Distance")
plt.ylabel("Count")
plt.title("Skill Embedding Distance Distribution")
plt.legend()
plt.show()
對應檔案:src/build_graph.py + notebooks/03_hyperparam_tuning.ipynb
作業:給學員一份 100–200 筆技能清單,調 top_k / threshold,觀察:
- 哪些技能 cluster 在一起(後端 / 前端 / DevOps)
- 哪些「奇怪的邊」提示 threshold 太鬆
Ch5:接回 Hybrid RAG Pipeline(35 min)
目標:把 entity graph 正式接入 retrieval pipeline。這是最核心的章節,需要整合 skill graph + chunk layer 形成完整的 HybridRAG。
為什麼要加 Chunk Layer?
純 Entity Graph 有兩個痛點:
- 結構好但內容少:Entity graph 抓到 "Kubernetes → Docker",但沒有「具體怎麼學 Docker 的 chunk」
- Chunk 單獨用太碎:純 vector search 抓 chunk 會抓到「提到 Kubernetes 但不相關」的雜訊
解法:Chunk metadata 加 skill labels,讓 chunk 變成 graph-aware。
預處理:Chunk Enrichment
# chunk_enrich.py — 讓 chunk 知道自己屬於哪些 skills
def enrich_chunks(chunk_table, skill_table, top_k=3):
"""為每個 chunk 自動標註相關的 skills(用 embedding 相似度)。"""
chunks = chunk_table.to_pandas()
for idx, chunk in chunks.iterrows():
# 找這個 chunk 最接近的 skills
related = skill_table.search(chunk["vector"]).limit(top_k).to_list()
chunk_table.update(
where=f"id = {chunk['id']}",
values={"linked_skills": [r["name"] for r in related]}
)
設計決策:這裡用 embedding 相似度做 chunk→skill 標註。對於縮寫或別名(如 k8s / Kubernetes、Go / Golang),embedding model 本身就能處理大部分情況。如果遇到極端 case,可在建表前做一輪資料清理,或引入極輕量的 LLM 輔助萃取——這是「Context-first」設計哲學,在寫入階段就把語意與結構整理好。
查詢時:Hybrid Retrieval
# rag_pipeline.py — 完整 hybrid retrieval 流程
import networkx as nx
def hybrid_retrieve(query, query_embedding, skill_table, chunk_table, skill_graph,
skill_top_k=5, graph_hops=2, chunk_top_k=10):
"""
Query → Skill Graph Expansion → Graph-guided Chunk Retrieval
1. 在 skill entity table 找 seed skills
2. 沿 skill graph 走 N hop,擴展到相關 skills
3. 用這組 skills 過濾 chunk table,做二次檢索
"""
# Step 1: 找 seed skills(entity-level retrieval)
seed_skills = skill_table.search(query_embedding).limit(skill_top_k).to_list()
seed_names = {r["name"] for r in seed_skills}
# Step 2: Graph expansion — 沿語意圖走 N hop
expanded_skills = set()
for skill_name in seed_names:
if skill_name in skill_graph:
neighbors = nx.single_source_shortest_path_length(
skill_graph, skill_name, cutoff=graph_hops
)
expanded_skills.update(neighbors.keys())
all_skills = seed_names | expanded_skills
# Step 3: Graph-guided chunk retrieval
# 只在帶有相關 skill labels 的 chunks 裡搜
chunks = chunk_table.search(query_embedding).limit(chunk_top_k * 3).to_list()
# 過濾:只保留 linked_skills 有交集的 chunks
filtered = [
c for c in chunks
if set(c.get("linked_skills", [])) & all_skills
][:chunk_top_k]
return {
"seed_skills": list(seed_names),
"expanded_skills": list(expanded_skills - seed_names),
"chunks": filtered
}
Aha Moment 範例設計
這是讓學員一眼看出 Semantic Graph 威力的關鍵對比——展示 「跨越未知(Unknown Unknowns)」 的能力。
情境:知識庫裡有三篇文章:
- Chunk A:"FastAPI 是一個基於 Python 的現代 Web 框架,主打高併發..."
- Chunk B:"Node.js 伺服器效能調校與架構優化指南..."
- Chunk C:"Hono 實戰:為 Edge Runtime 設計的極速微型 Web 框架..." (沒提到 Python 或 FastAPI)
Query:「我平常用 Python 寫 FastAPI,現在想在 JavaScript/Edge 生態找一個類似的輕量、高效能 API 框架,有什麼學習資源?」
| 方法 | 檢索結果 | 解釋 |
|---|---|---|
| ❌ Vector-only | Chunk A + Chunk B | 靠字面相似度,找不到 Hono |
| ✅ Hybrid RAG | Chunk C 🎯 | Skill graph:FastAPI → 輕量 Web 框架 → Hono;Graph 搭了一座語意的橋 |
學員看到這個對比的瞬間就會明白:Graph 不是取代向量搜尋,而是向量搜尋的「導航圖」。
三種方式對決
在 notebook 中用同一個 query 跑三種 retrieval,讓學員自己觀察:
| 方法 | Context 品質 | 缺點 |
|---|---|---|
| Vector-only chunk | 快但雜 | 可能抓到字面相關但不相關的 chunk |
| Skill graph only | 結構好但內容少 | 只有 skill name,沒有學習細節 |
| Hybrid(Skill + Chunk) | 最佳 | 零額外依賴 |
對應檔案:src/chunk_enrich.py + src/rag_pipeline.py + notebooks/05_rag_comparison.ipynb
作業:測試 3 個跨領域 career query,比較 Hybrid 效果。調 cutoff=1 vs cutoff=2 觀察擴展範圍。
Ch6:Bonus — Learning Path 推薦 + 延伸(20 min)
目標:展示 skill graph 的進階應用,讓學員有「原來還能這樣用」的驚喜。
內容(挑 1–2 個):
- Learning Path 推薦:用 graph 最短路、PageRank 做「必修技能」推薦
- GraphRAG / LightRAG 對比:它們如何用 LLM extract entity + embed description,對照我們的簡化版 pipeline
- Semantic Compression 概念:學術背景介紹,讓學員定位自己在做的事情
# recommend_path.py — 簡單的 learning path 推薦
def recommend_next_skills(skill_graph, current_skills, top_n=5):
"""根據你已有的技能,推薦下一步該學什麼。"""
candidates = {}
for skill in current_skills:
if skill not in skill_graph:
continue
for neighbor in skill_graph.neighbors(skill):
if neighbor not in current_skills:
weight = skill_graph[skill][neighbor].get("weight", 0.5)
candidates[neighbor] = candidates.get(neighbor, 0) + weight
# 按權重排序,相似度越高 = 越值得學
ranked = sorted(candidates.items(), key=lambda x: -x[1])
return ranked[:top_n]
# 用法:
# recommend_next_skills(graph, ["Python", "FastAPI", "Docker"])
# → [("Kubernetes", 1.74), ("Flask", 1.2), ("Celery", 0.95), ...]
對應檔案:notebooks 全 + src/recommend_path.py(可選)
作業:fork repo,加一個自己想要的功能。
Repo 結構:課程導向設計
entity-embedding-graph-rag/
├── README.md # 總覽 + 快速啟動
├── requirements.txt # pip install -r
├── Dockerfile # 可選:一鍵 Docker 跑起來
│
├── data/
│ ├── skills.yaml # 課程技能清單(200 筆)
│ ├── skills.lance/ # 預 embed 的 LanceDB(git lfs 或 .gitignore)
│ └── skill_graph.gpickle # 預建的 NetworkX graph
│
├── src/
│ ├── __init__.py
│ ├── embed_skills.py # Ch3: embedding + LanceDB
│ ├── build_graph.py # Ch4: kNN → NetworkX
│ ├── chunk_enrich.py # Ch5: chunk metadata 加 skill labels
│ ├── rag_pipeline.py # Ch5: query → graph → retrieval
│ ├── recommend_path.py # Ch6: learning path 推薦(可選)
│ └── utils.py # cosine, threshold 工具函數
│
├── notebooks/
│ ├── 01_motivation.ipynb # Ch1: demo 差異
│ ├── 02_embedding_basics.ipynb # Ch2: 玩 similarity
│ ├── 03_hyperparam_tuning.ipynb # Ch4: 調 top_k / threshold
│ ├── 05_rag_comparison.ipynb # Ch5: before / after 對比
│ └── 06_hybrid_demo.ipynb # Ch5: Hybrid Retrieval 實驗
│
├── tests/
│ └── test_graph.py # pytest 驗證 graph 品質
│
└── Makefile # make embed, make build, make rag
設計原則:
- 學員可以選「只看 notebook 自學」或「跟影片一步步跑 src script」
data/下有預建資料,clone 後就能跑src/就是 production 版程式碼,notebook 是教學版- Repo 乾淨不臃腫,學完直接當 portfolio
錄播課製作建議
錄製策略
- 總長:2–4 小時,分 6 章,每章 20–30 分鐘
- 風格:「敲 code + 解釋為什麼」,不是 slides 為主
- 工具:OBS Studio(免費)+ VS Code(邊錄邊跑)
- 平台:YouTube playlist 或 Bilibili
- 順序:先全錄 notebook(講解 + demo),再補 src script(production 版)
每章腳本模板
問題(30s) → 為什麼要這樣做?
Code walkthrough(10min) → 邊敲邊解釋
結果展示(5min) → 跑 query 看效果
邊角 case + 調參(5min)
作業 + 下章預告(1min)
章節影片對應
| 章節 | 影片標題 | 對應檔案 | 長度 | 作業 |
|---|---|---|---|---|
| Ch1 | 為什麼需要 Skill Entity Graph? | 01_motivation.ipynb |
15min | 跑 demo 比較 |
| Ch2 | Embedding 夠用的基礎 | 02_embedding_basics.ipynb |
20min | embed 自己的技能清單 |
| Ch3 | 建 Entity Table | embed_skills.py |
25min | 加 10 個新 skill |
| Ch4 | 自動建 Graph(核心) | build_graph.py |
30min | 調 threshold 看 cluster |
| Ch5 | 接回 Hybrid RAG Pipeline | chunk_enrich.py + rag_pipeline.py |
35min | 測 3 個 career query |
| Ch6 | Bonus: Learning Path + 延伸 | notebooks 全 | 20min | fork repo 加功能 |
學術定位:與 2024–2026 趨勢對齊
這門課教的不是冷門 hack,而是學術界和產業界的主流方向:
- 正式名稱:graph-augmented vector retrieval 或 semantic graph
- 定義:在向量空間上建圖 G=(V,E),節點是 embedding 後的實體,邊是語意或結構關係
- 設計哲學:圖不是取代向量搜尋,而是「增強」它——先用向量找 seed,再沿著圖做 multi-hop 擴展
- 產業趨勢:GraphRAG 幾乎變成「高階企業 RAG」的標配
我們的做法 vs LightRAG
| 面向 | LightRAG | 我們的方式(Entity Embedding) |
|---|---|---|
| Graph 邊來源 | LLM 自動提取 entity + relationship | 向量相似度(cosine > threshold) |
| 自動化程度 | 高(全自動,但多 LLM call + 成本) | 中(只 embed,穩定且便宜) |
| 小資料規模 | LLM extract 不穩定 | embedding 更穩定 |
| 需要外部 API | 是(LLM call) | 否(本地 sentence-transformers) |
如果 skill list 已知,純 embedding 比 LLM extract 穩定,尤其是小資料規模。這就是我們的甜蜜點。
推廣與變現策略
開源 Repo
- GitHub:pin 到 profile,README 嵌入影片 playlist + star 計數器
- 標籤:
rag、graphrag、career、embedding、lancedb - 加 GitHub Discussions,讓學員貼自己的 graph 截圖討論
推廣管道
- iT 邦幫忙 / Reddit LocalLLaMA / X(標 #GraphRAG #RAG)
- 強調賣點:「LanceDB + NetworkX 零依賴實作 Entity Embedding Graph RAG」
變現模式
免費 → GitHub repo + 基礎影片 playlist
付費進階 → Cypher / Memgraph 整合、production 部署(Gumroad, NT$300–500)
總結:一張表看完全局
| 元件 | 現有方案 | 課程教的升級 |
|---|---|---|
| 圖結構 | NetworkX + 手動 JSON | NetworkX + 自動 embedding 建邊 |
| Chunk 檢索 | LanceDB vector search | LanceDB + skill graph 導航 |
| 查詢流程 | Vector → top-k chunks | Vector → Skill Graph → Graph-guided Chunks |
| 外部依賴 | 零 | 繼續零 |
核心概念只有一句話:用語意相似度取代關鍵字匹配來建構圖的邊。
你的 LanceDB + NetworkX 已經完全夠用。把 semantic kNN graph 做好,就是跟 2024–2026 學術趨勢對齊的做法——只是你在個人 scale 上用幾十行 Python 落地,而不是用大廠框架包起來。學員學完可以直接把 repo 當 portfolio:「我做了一個帶 semantic skill graph 的 career assistant」。
延伸閱讀
- Memgraph vs HelixDB vs 手刻 Semantic Graph — 為什麼 200 nodes 不需要圖資料庫
- GraphRAG 資料庫新勢力:Lance-graph vs HelixDB vs FalkorDB — 新一代圖向量資料庫完整評測
- Awesome-GraphRAG — 學術界 GraphRAG 資源列表
Career Knowledge Base 是一個本地優先的履歷知識庫系統,使用 Python + LanceDB + NetworkX + LangChain 建構。
- ← Previous
GraphRAG 資料庫新勢力:Lance-graph vs HelixDB vs FalkorDB 完整評測 - Next →
Ch1:為什麼你的 RAG 需要 Skill Entity Graph?