為什麼做這個專案
公司裡最常聽到的問題之一是:「那份規定寫在哪裡?」員工手冊、產品說明、內部 FAQ,資料都在,只是找不到。
一般的聊天助理不知道你公司的文件內容。RAG(檢索增強生成)的做法是:先從你的文件裡找出相關段落,再交給模型根據這些段落回答。這是目前企業導入 AI 最常見的架構之一,親手做一次,你會真正理解它的強項和弱點。
大部分答錯的問題,原因都在「搜尋」這一步,而不是模型本身。
開始前的準備
- 完成「LLM API」與「Agent 原理」,能用程式呼叫模型並處理回傳
- 讀過「RAG 知識庫」單元,了解 Embedding、切塊和向量搜尋的概念
- 熟悉「工程基本功」的虛擬環境、模組與 pytest
- 準備一組文件。建議用公開資料練習,例如政府機關的公開問答、開源專案的說明文件,或你自己寫的筆記
實作指南
里程碑 1:準備文件與評估題
先寫考題,再寫程式。這是這個專案最重要、也最常被跳過的一步。
準備一個 eval.jsonl,每一行是一題:
{"question": "新進員工第一年有幾天特休?", "answer": "依到職滿半年後給 3 天", "source": "員工手冊.md"}
題目要涵蓋不同類型:直接查得到的、需要合併兩段的、文件裡根本沒有答案的。最後一種很重要,用來測試它會不會亂掰。
以下是一份文件的內容。請幫我出 10 題問答評估題,包含 6 題能直接在文中找到答案、2 題需要合併兩個段落、2 題文中沒有答案。每題附上標準答案和出處段落。
AI 出的題目你要逐題檢查,標準答案錯了,後面的分數就都沒有意義。
里程碑 2:切塊與建立索引
最簡單的切法是依標題或段落切,再限制每塊的長度:
def chunk_by_heading(text, source, max_chars=800):
chunks, current = [], ""
for line in text.splitlines():
if line.startswith("#") and current:
chunks.append(current.strip())
current = ""
current += line + "\n"
if len(current) > max_chars:
chunks.append(current.strip())
current = ""
if current.strip():
chunks.append(current.strip())
return [{"text": c, "source": source} for c in chunks]
接著選一個 Embedding 模型把每塊轉成向量。可以用雲端服務,也可以用在本機執行的開源模型。向量和原文一起存起來,小專案用一個檔案或 SQLite 就夠了。
里程碑 3:搜尋相關段落
把問題也轉成向量,計算它和每一塊的相似度,取最高的幾塊:
import numpy as np
def top_k(query_vec, chunk_vecs, k=4):
sims = chunk_vecs @ query_vec / (
np.linalg.norm(chunk_vecs, axis=1) * np.linalg.norm(query_vec)
)
return np.argsort(sims)[::-1][:k]
先別急著接模型。拿評估題來測,人工看找到的段落裡有沒有答案。這一步的準確度,決定了整個系統的上限。
里程碑 4:組合提示並附來源回答
系統提示裡把規則講清楚:
你是公司內部文件的問答助理。
只能根據下方 <documents> 裡的內容回答。
每個重點後面標示來源,例如 [員工手冊.md]。
如果文件中沒有答案,請回答「文件中找不到這個問題的答案」,不要推測。
把找到的段落用標籤包起來放進使用者訊息,模型比較容易分清楚哪些是資料、哪些是問題。
里程碑 5:評估與調整
寫一支腳本跑完所有評估題,記錄三件事:搜尋有沒有找到正確出處、答案是否正確、沒答案的題目有沒有誠實說不知道。
答案正不正確,可以先自己判斷,題目多了再請另一個模型幫忙評分,但要抽查它評得對不對。
一次只改一個變數,例如切塊長度從 800 改成 400,再跑一次,把結果記在表格裡:
| 版本 | 切塊長度 | 找到正確出處 | 答案正確 | 誠實說不知道 |
|---|---|---|---|---|
| v1 | 800 | 待填 | 待填 | 待填 |
| v2 | 400 | 待填 | 待填 | 待填 |
里程碑 6:整理成可維護的專案
把程式拆成 chunking.py、index.py、retrieve.py、answer.py,為切塊和搜尋寫幾個 pytest 測試,再寫一份 README 說明怎麼安裝、建立索引和提問。
請讀這個專案的所有檔案,提出一個模組拆分方案,說明每個檔案的職責。先給我方案,我同意後再動手。拆完後評估腳本的分數應該和拆之前一樣。
延伸挑戰
- 混合搜尋:結合關鍵字搜尋和向量搜尋,比較評估分數
- 文件更新時,只重建有變動的部分
- 加一個簡單的網頁介面
- 把搜尋包成一個工具,讓 Agent 自己決定什麼時候查文件
- 有至少 20 題評估題,其中包含文件中沒有答案的題目
- 每個答案都標示出處,點回原文可以找到依據
- 沒有答案的題目,機器人會說找不到,而不是亂掰
- 有一張表格記錄至少兩個版本的評估結果
- 專案有模組、測試和 README,換一台電腦照著做也能執行
做完了?分享你的作品!
到交流區貼上作品連結和心得,你的經驗會幫到下一個人。
專案討論
卡關了、有心得,或發現內容需要更新?都歡迎留言。留言使用 GitHub 帳號登入。