前情提要:昨天我們成功串接了輕量級的 API 來抓取股價。
本日目標:今天我們要為系統準備最後一個指令/future-self的核心靈魂。我們將介紹當紅的 RAG 技術,並示範如何捨棄龐大的向量資料庫,用純 Python 手工刻出一個「極簡版 RAG (Minimal RAG)」,把部落格的心法精準餵給 AI。
RAG 的全名是 Retrieval-Augmented Generation (檢索增強生成)。
簡單來說,AI 就像一個非常聰明但沒讀過你私人筆記的大學生。當你想讓 AI 根據你的部落格文章來回答問題時,你需要先透過「檢索 (Retrieval)」把相關的文章段落找出來,然後把它跟使用者的問題綁在一起,送給 AI 進行「生成 (Generation)」。
通常的工業級做法是:
很多教學遇到 RAG,第一個直覺就是教你裝一套重量級的向量資料庫。但在我們的專案中,我們要傳達的「部落格心法」其實只有最重要的幾個觀念(人力資本、槓桿清算、安心防線)。
為了這幾句話去建立並維護整個第三方資料庫連線,不只拖慢了系統效能,更大幅增加了我們程式碼的脆弱度。
最佳解法:純手工極簡 RAG (Minimal RAG)。
既然觀念不多,我們何不自己定義向量?我們用最純粹的 Python,自己寫出一個輕量級的檢索引擎。0 依賴、0 延遲,卻能達到 100% 精準的「知識檢索」。
我們在專案中實作了 blog_rag_engine.py 與對應的 blog_chunks.json。我們手動定義了四個維度的特徵(例如:[破產槓桿, 醫療危機, 職涯成長, 財務自由])。
當玩家的模擬結果出爐時,我們將玩家的狀態轉化為一個 Query 向量,並透過我們手寫的餘弦相似度函式,找出最適合的部落格名言:
# blog_rag_engine.py 的核心片段
def cosine_similarity(self, vec1, vec2):
dot_product = sum(a * b for a, b in zip(vec1, vec2))
norm_a = math.sqrt(sum(a * a for a in vec1))
norm_b = math.sqrt(sum(b * b for b in vec2))
if norm_a == 0 or norm_b == 0:
return 0.0
return dot_product / (norm_a * norm_b)
def retrieve_wisdom(self, query_vector, top_k=1):
results = []
for chunk in self.knowledge_base:
# 計算玩家狀態向量與文章向量的相似度
score = self.cosine_similarity(query_vector, chunk["embedding"])
results.append((score, chunk))
# 回傳最相關的部落格金句
results.sort(key=lambda x: x[0], reverse=True)
return [chunk for score, chunk in results[:top_k]]
你看,我們用不到 50 行的純 Python 程式碼,就完美實現了 RAG 的核心靈魂。這不僅維護成本極低,更能確保 AI 能根據玩家「破產」或「提早退休」的精準狀態,給出最適合的雞湯。
今天我們實作了RAG,只要了解 RAG 的底層原理(計算向量相似度),有時候一段純 Python 的陣列運算,就可以高很效。這就是極簡設計的魅力。
思考題:如果是你,你會想把哪一句你最喜歡的名言或心法,寫進 blog_chunks.json 裡面?
明日預告:萬事俱備!明天(Day 29),我們將迎接情感的大結局。我們將親自觸發 /future-self 指令,利用今天寫好的 Minimal RAG 引擎,看看 60 歲的自己,會跨越時空對我們說些什麼。準備好衛生紙吧!