前言
我的 Outlook 信箱累積了將近三萬封郵件,橫跨 15 年的 BBS 文章轉寄、工作往來、以及各種生活記錄。每次想找某封信,要嘛記不得關鍵字,要嘛 Outlook 內建搜尋太慢。某天突發奇想:既然 AI 這麼厲害,能不能讓它幫我管郵件?
於是花了一個週末,從零開始建了一套本地郵件語意搜尋系統,這篇文章記錄整個過程,包括走了哪些彎路。 主要透過與 Claude 的對話(prompt-driven development)逐步完成, 包含程式生成與 debug。
系統架構
整個系統分成三個步驟,一次建置,長期使用:
Step 1:解析 PST
Outlook 的本機備份格式是 .pst,用 libpff-python 套件解析,把每封郵件的主旨、寄件者、日期、本文全部匯出成 emails.json。三萬封郵件大概跑了十幾分鐘。
Step 2:建立向量索引
用 sentence-transformers 的多語言模型(paraphrase-multilingual-MiniLM-L12-v2)把每封郵件轉成向量,存進 ChromaDB 本地資料庫。這個步驟只需要執行一次,之後新增郵件才需要重跑。
Step 3:查詢介面 寫了一個終端機互動介面,支援兩種搜尋模式:
- 語意搜尋:直接輸入問題,找語意相近的郵件
- 關鍵字搜尋(
k:前綴):精確比對人名、地名等關鍵字
查詢結果支援分頁顯示,也可以加上 s: 前綴自動存成 txt 檔案並開啟。
過程中遇到的坑
老實說,整個建置過程遇到不少問題,花了不少時間 debug:
1. libpff 的 API 跟想像不一樣
一開始照著網路上的範例寫 msg.sender_email_address,直接報錯。原來 libpff-python 的屬性都是方法,要用 msg.get_sender_name() 這種形式呼叫,而且本文回傳的是 bytes,還需要手動 decode。花了一段時間用診斷腳本一個個確認正確的屬性名稱。
2. ChromaDB 的重複 ID 問題
由於部分郵件內容高度相似(例如系統通知信或轉寄內容),
使用內容 hash(MD5)作為 ID 時產生重複,導致 ChromaDB 寫入衝突。
這裡的問題本質不是 hash collision,而是「輸入資料本身不具唯一性」。
- 加入 timestamp / message-id 作為 ID 組成
- 或在寫入前進行去重處理
3. Visual C++ Build Tools 的安裝
Windows 上安裝 libpff-python 需要先裝 Visual C++ Build Tools,這個坑很多人踩過。安裝包大概 2-3GB,記得在安裝介面勾選「Desktop development with C++」。
4. API Key 的換行問題
Anthropic Console 顯示 API Key 時會自動斷行,直接複製貼到 .env 就變成多行,導致 401 錯誤。要手動把三段文字接成一行才行。
5. VS Code 的環境變數讀取
.env 檔案在 PowerShell 可以正常讀取,在 VS Code 內建終端機卻讀不到。最後用永久環境變數解決,在 Windows 設定裡新增系統變數,重開 VS Code 後就正常了。
6. 先送 Claude API 再說——結果浪費了不少 token
原本的設計是:搜尋完直接把結果丟給 Claude API 分析。實際用了幾次之後發現,向量搜尋回傳的 8 封郵件不一定全部相關,有時候只有 2-3 封是我真正要的,其他的都是在幫 Claude 燒 token 做白工。
後來的做法是先跑本地查詢,用分頁顯示確認哪幾封是目標,確定有用再決定要不要送 API 分析。這樣不只省錢,整個流程的掌控感也更好——畢竟 Claude API 是用來幫你整理思路的,不是拿來過濾搜尋結果的。目前幾封郵件的內容量其實不多,本地查詢加上人工閱讀完全夠用。
補充:
向量搜尋回傳的 Top-K 並不等於最相關結果,
它本質是「語意相似的候選集合」。
若直接送入 LLM,會造成 token 浪費。
更理想的做法是:
1. 向量搜尋(召回)
2. rerank(例如 cross-encoder 或簡單規則)
3. 再送入 LLM
目前我採用人工篩選作為簡化版 reranking,
在成本與效果之間取得平衡。
目前的限制
封存郵件的完整性問題
目前解析時每封郵件只保留前 5000 字元,這讓 2GB 的 PST 壓縮成約 500MB 的 JSON,空間效率不錯。但我有不少郵件其實是 BBS 文章的長篇封存,動輒上百則推文,5000 字元根本裝不下。這類郵件在資料庫裡只有片段,搜尋到了也不完整。
未來打算把字數限制完全拿掉,接受 JSON 變大的代價,確保每封郵件都能完整保存。畢竟建這套系統的目標之一,就是讓這 15 年、三萬多封累積的 BBS 文章重見天日——如果連完整內容都看不到,那就失去意義了。
意外的收穫
搜尋速度比 Outlook 內建快很多,關鍵字搜尋幾乎即時,而且找到的結果更準確。
更讓我驚喜的是:BBS 文章裡的色碼(ANSI 色彩標記)在終端機裡顯示得非常還原。幾年前的 PTT 文章,outlook看不到色碼,但用目前系統連當年的排版顏色都跑出來了,有種挖到時光膠囊的感覺。 (如下:)
費用
- 建置成本:幾乎是零,全部用開源套件
- Claude API:儲值了 $5 美元(約台幣 160 元),用來測試 API 整合,日常查詢目前不需要
- 時間成本:大概一個下午,主要都花在 debug 上
未來改進方向
1. 完整保存郵件內容 拿掉 5000 字元的截斷限制,確保長篇 BBS 封存郵件也能完整收錄。
2. 建立查詢模板 BBS 文章的搜尋需求有固定的模式,例如找特定 ID 的文章、找特定時期的版聚討論、找某個技術話題的串文。預先建立幾個模板,直接套用比每次重新想關鍵字更有效率。大概像這樣:
# 常用查詢模板
k:板聚 {地點} # 找版聚
k:{ID} {年份} # 找特定 ID 某年的文章
k:{關鍵字} 好文 # 找被推為好文的文章
k:Re: [{板名}] # 找特定版的討論串3. 增量更新 現在新增 PST 要整個重跑建索引,未來可以改成累加模式,只對新郵件建立索引。
4. 換用更好的中文模型 目前用的多語言模型對繁體中文語意理解普通,換用專門針對中文優化的 Embedding 模型應該能改善語意搜尋品質。
目前使用 paraphrase-multilingual-MiniLM-L12-v2,
在跨語言場景表現穩定,但對於繁體中文長文本語意理解仍有限。
未來可考慮:
- BGE-m3
- text-embedding-3-large(若接受雲端)
- 或中文優化模型(如 GTE / E5 系列)
5. 確認 Claude API 的定位 等系統穩定、查詢習慣養成之後,再來評估 Claude API 分析是否真的有加值,還是本地查詢加人工閱讀就足夠了。
結語
這套系統最大的意義,不只是「搜尋更快」——而是讓 15 年來存在硬碟深處的記憶變得可以被找到。三萬多封郵件裡,有早年 BBS 認識的網友、有當年追的動漫討論串、有各種已經記不得細節的生活片段。
現在只要下一個關鍵字,它們就能重新浮現。
未來目標是完全用這套系統取代 Outlook 做 BBS 文章查閱,讓 Outlook 只剩下工作郵件的功能。















.jpg)
