你有沒有試過問 ChatGPT 一個關於自己公司的問題,結果它一本正經地胡說八道?或者你辛苦建了一個內部知識庫,員工問「請假流程係點」,AI 卻答了一個三年前的舊版本?問題很多時候不在大語言模型本身,而在「檢索」這一步——它根本沒找到對的文件,卻照樣自信地回答你。

這篇文章要教你一件事:用 Google 上星期才在 HuggingFace 爆紅的開源嵌入模型 EmbeddingGemma 2,自己動手搭建一套檢索增強生成(RAG)系統。這個模型一週內累積 2,659 個讚、16 萬次下載,是目前開源嵌入模型中最受注目的新星之一。學會之後,你可以把公司的手冊、合約、FAQ 全部餵進去,讓 AI 答得準、答得對,而且完全免費、資料不出門。

為什麼你的 AI 老是答錯?先搞懂「嵌入」是什麼

很多人以為 AI 答錯是因為模型不夠聰明,其實十次有八次是「找不到資料」。大語言模型的記憶停留在訓練那一刻,它不知道你公司上個月改了報銷政策。RAG 的做法很簡單:先從你的文件庫裡找出最相關的幾段文字,再連同問題一起交給 AI,讓它「看著資料回答」。

而「找出最相關」這件事,靠的就是嵌入模型。它會把每一段文字轉換成一組數字(向量),語意相近的句子,數字也會相近。例如「如何申請年假」和「請假程序」用字完全不同,但嵌入模型知道它們講的是同一件事。EmbeddingGemma 2 的強項正是在這裡——它支援多語言,中文、英文混雜的香港文件也能處理,而且模型體積細,普通筆記本電腦都跑得動。

打個比方:以前的做法是叫實習生用關鍵字搜尋,你打「年假」它只找到含「年假」二字的文件;用了嵌入模型,等於請了一個讀過所有文件的老員工,你說「我想放幾日假」,他馬上知道要拿哪份表格給你。

第一步:安裝環境與載入模型

先確認你有 Python 3.10 以上,然後安裝三個套件。這裡用 sentence-transformers,它對初學者最友善:

pip install sentence-transformers torch numpy

載入模型只需要三行:

from sentence_transformers import SentenceTransformer

model = SentenceTransformer("google/embeddinggemma-2")
print(model.get_sentence_embedding_dimension())

第一次執行會自動從 HuggingFace 下載權重,大約 1 至 2 GB,視乎你的網絡。下載完之後就可以離線使用,之後每次啟動都是幾秒鐘的事。如果你用的是 Apple Silicon 的 MacBook,記得安裝 torch 的 arm64 版本,速度會快上好幾倍。

第二步:把文件切塊,這步最影響準確度

很多新手以爲把整份 PDF 丟進去就行,結果效果奇差。原因很簡單:一份 50 頁的手冊變成一條向量,語意被平均掉了,什麼都像一點、什麼都不準。正確做法是切塊(chunking),每塊大約 300 至 500 字,並保留一點重疊。

def split_text(text, chunk_size=400, overlap=50):
    chunks = []
    start = 0
    while start < len(text):
        chunks.append(text[start:start + chunk_size])
        start += chunk_size - overlap
    return chunks

實戰貼士:切塊時盡量在段落或標題邊界切,不要切斷一句話。如果你的文件有結構(例如每條政策一個小節),最好按小節切,再在每塊前面加上標題,例如「【請假政策】……」,這樣檢索時連標題的語意都會被考慮進去,準確度會明顯提升。

第三步:建立向量索引並實際檢索

有了切塊,就可以一次過把它們轉成向量,存起來備用:

chunks = split_text(open("handbook.txt", encoding="utf-8").read())
embeddings = model.encode(chunks, normalize_embeddings=True)

normalize_embeddings=True 這一句千萬不要漏,它會把向量標準化,之後用內積計算相似度就等於餘弦相似度,又快又準。檢索時,把用戶問題也轉成向量,再找出最相似的前三段:

import numpy as np

def search(query, top_k=3):
    q = model.encode([query], normalize_embeddings=True)[0]
    scores = embeddings @ q
    idx = np.argsort(scores)[-top_k:][::-1]
    return [(chunks[i], float(scores[i])) for i in idx]

你可以馬上試一句:「我入職未夠一年,可以放幾多日大假?」看看它找回來的段落對不對。如果分數低於 0.5,通常代表你的文件庫根本沒有相關內容——這時候與其讓 AI 亂答,不如老實跟用戶說「找不到相關資料」,這就是負責任的 RAG 設計。

第四步:接上大語言模型,組成完整 RAG

最後一步,把檢索到的段落塞進提示詞,交給任何一個 LLM 生成答案:

context = "\n\n".join([c for c, s in search(question)])
prompt = f"""只根據以下資料回答,找不到就說不知道。
資料:{context}
問題:{question}"""

這句「找不到就說不知道」是整段提示詞的靈魂。筆者實測過,加與不加,胡說八道的比率相差可以達到一半以上。想再穩陣一點,可以在提示詞要求它標明引用了哪一段,方便用戶核對。

香港中小企的實際場景

上個月有一位做貿易的朋友找我,他們公司有 200 多份供應商合約,業務員每次報價都要人手翻查條款,平均花 20 分鐘。我們用上面這套流程,配合 EmbeddingGemma 2 建立索引,現在業務員打一句「A 供應商的付款期係幾多天」,三秒內就有答案,還附上合約原文出處。整套系統跑在一部普通桌上電腦,零 API 費用,合約數據完全沒有離開公司網絡——對於重視資料私隱的香港企業來說,這一點往往比準確度更重要。

另一個常見場景是客服。台灣有不少電商把退換貨政策、物流時效餵進 RAG,客服機械人的首次解決率明顯提升。關鍵同樣不在模型多大,而在文件切得好不好、檢索找得準不準。

三個令準確度再上一層的貼士

第一,問題改寫。用戶問「幾時可以攞貨」,你可以先叫一個小模型把它改寫成「訂單的出貨時間是什麼」,再拿去檢索,命中率會高很多。第二,混合檢索。把向量搜尋和傳統關鍵字搜尋(BM25)的結果合併,對於產品型號、人名這類專有名詞特別有效。第三,定期更新索引。文件改了就要重新編碼,否則你只會得到一個答得很流暢、但講緊舊世界的 AI。

總結一下:AI 答得唔準,九成係檢索出問題。EmbeddingGemma 2 這類開源嵌入模型,把以往要付費 API 才做到的事,變成一部普通電腦就搞得掂。你今日就可以用一份自己的文件試一試,由 20 條問答開始,逐步擴大。記住三個關鍵:切塊要靚、向量要 normalize、提示詞要講明「唔知就話唔知」。

延伸閱讀

常見問題

Q: EmbeddingGemma 2 同 OpenAI 的 embedding API 比,邊個好?

A: 視乎需求。OpenAI 的 API 免安裝、免維護,適合快速原型;EmbeddingGemma 2 勝在免費、可離線、資料不出門,對處理合約、病歷、客戶資料的香港企業更合適。準確度方面,兩者在中文檢索任務上差距不大,但自建方案長遠成本近乎零。

Q: 我部電腦冇獨立顯卡,跑得動嗎?

A: 跑得動。EmbeddingGemma 2 屬於輕量級嵌入模型,用 CPU 處理幾百段文件只是幾十秒的事。如果文件量大到幾萬段,建議分批編碼並儲存結果,避免一次過佔用記憶體。

Q: 切塊大小應該點揀?

A: 300 至 500 字是安全起點,重疊 50 字左右。條文密集的文件(如合約)可以細一點,敘述性文件可以粗一點。最好的方法是準備 20 條真實問題做測試,試幾個不同大小,看哪個命中率最高。

Q: 檢索到的內容同問題唔相關,可以點補救?

A: 先檢查文件庫是否真的包含答案;其次檢查切塊有沒有切斷關鍵句;第三可以加入關鍵字混合檢索。若相似度分數普遍偏低,代表你的問題用語與文件用語差異太大,考慮加入問題改寫步驟。

Q: 呢套系統可以處理幾多文件?

A: 用 NumPy 做暴力比對,幾萬段以內都應付得來。再大就要用 FAISS 或 Qdrant 這類向量資料庫,幾百萬段都無問題。對大部分中小企的內部知識庫來說,前者已經非常足夠。