Token Saver 拆解:一個 MCP 檢索層,怎麼把 PDF 問答砍到 1% token(以及你該偷走的三個設計)
本文大綱
一、你不是缺一個更聰明的模型,是缺一個檢索層
把 233 頁的判決書整份丟進對話,再問「它怎麼處理 strict scrutiny」——這是多數人讀長文件的預設做法,也是最貴的做法。你付了十幾萬 token 的錢,模型真正用到的可能是兩段話。
而且這不只是錢的問題。context 被 200 頁不相關內容塞滿,注意力會被稀釋,回答品質常常比「只給對的兩段」還差。這件事在 coding agent 上尤其有感:你把整包 docs 塞進去,它反而開始引用錯的章節、把 v1 的 API 當成現在的寫法。
Token Saver 是 MarkTechPost AI Media 在 2026/07/30 開源的小工具,處理的正是這一段。它值得拆的原因不是「省錢」這個結論——那是廢話——而是它把一條檢索 pipeline 的每個決策都寫成了可讀的常數:180、40、0.4/0.6、0.25、8000。這些數字你可以直接抄進自己的 agent。
二、它是什麼、誰做的
先把身分講清楚,因為這決定了它的天花板。
- 本體:Claude Desktop 的桌面擴充(
.mcpbbundle),檔名token-saver-ccr.mcpb - 作者:Arnav Rai(Rochester Institute of Technology 學生,於 Marktechpost 實習),由 Jean-marc Mommessin 與 Asif Razzaq 指導,掛在 Marktechpost AI Media Inc 名下
- 授權:MIT
- 版本:repo 的
mcpb/manifest.json寫0.3.0,eval/RESULTS.md也是對 0.3.0 量測的。媒體稿寫 v1.0,這裡以 repo 為準 - 範圍:只處理本機 PDF,只回傳相關段落 + 頁碼
README 的 badge 寫「92–98% fewer tokens」,MarkTechPost 的標題寫 90–99%。兩個都不算錯,差別在你看的是哪一份文件、問幾次——這點第四節會算給你看。
它的核心主張很單純:PDF 不上傳,只有被選中的段落進模型。抽取、切塊、embedding、打分全在你的機器上跑,MCP 走 stdio,不開任何 listening port。
三、運作原理:八段 pipeline,每段都有名字

以下常數全部來自 scripts/index_store.py 與 scripts/mcp_server.py,不是報導轉述。
① 抽取
優先用 pypdfium2(文字品質較好),沒裝就退回 pypdf。兩者都是純文字抽取——沒有 OCR。掃描件、拍照 PDF 進來就是一片空白,這是硬限制。
② 切塊:180 字一塊,重疊 40 字
PASSAGE_WORDS = 180 # 一塊大約一個密實段落
PASSAGE_OVERLAP = 40 # 跨窗邊界帶過去的上下文
180/40 是個保守配置。重疊 40 字(約 22%)是為了避免答案剛好被切在邊界上——這是所有 chunking 的老問題,它選擇用重疊硬扛,而不是做語意切分。
③ 雙路打分:BM25 0.4 + 向量 0.6
關鍵字路走 SQLite 的 FTS5 虛擬表跑 BM25;語意路用 all-MiniLM-L6-v2(384 維)算 cosine。兩邊分數 min-max 正規化後混合:
kw_weight = 0.4 # FTS5 BM25
sem_weight = 0.6 # cosine similarity
向量存法很樸素:normalized float32 BLOB 塞進 SQLite,查詢時用 numpy 暴力算全表 cosine。沒有 HNSW、沒有 FAISS。對單份幾百頁的 PDF 這完全夠用,但你要拿這套去掃十萬份文件就會爆——設計上它本來也不打算。
索引落在 ~/.token_saver/index,SCHEMA_VERSION = 4(切塊或抽取邏輯改了就強制重建)、TTL_DAYS = 14。
④ 棄權門檻(abstain gate)
SEM_FLOOR = 0.25 # 可用 $TOKEN_SAVER_SEM_FLOOR 調
eligible = (sem >= SEM_FLOOR) | (kw > 0)
這是整個系統最有想法、也最有問題的一段。它的意圖是「不確定就不要回」,避免模型拿著爛段落硬掰。作者自己在 docs/known-issues/false-accept-abstain-gate.md 裡承認了這條邏輯的漏洞:kw > 0 表示只要沾到一個關鍵字就放行。文件裡到處都有的 notes、report、section、agreement,會讓一個完全離題的問題撈到一段 boilerplate;min-max 正規化再把它的分數推高,因為沒有更好的東西可比。
作者列的三個候選修法:改成關鍵字分數的絕對/相對門檻、要求至少命中 2 個相異查詢詞、對混合分數設絕對下限。第二個做法在檔名解析那邊已經用了。
⑤ 去重 → ⑥ 修剪 → ⑦ 預算
TRIM_WINDOW = 3 # 每個保留區塊 3 句
TRIM_MAX_WINDOWS = 3 # 一塊最多留 3 個不連續區段
TRIM_MIN_KEEP_WORDS = 40 # 修剪後的字數下限
MAX_RESPONSE_CHARS = 8000 # 整個回應的硬上限
「句窗修剪」是這裡最實用的一招:命中的不是整塊 180 字,而是塊內命中句 ±1 句組成的 3 句小窗,一塊最多留 3 個這種小窗。等於在 chunk 之下再做一次壓縮。
然後 MAX_RESPONSE_CHARS = 8000 是硬牆。不管檢索回來多少,送進模型的就這麼多。這比「希望 LLM 自己節制」可靠一百倍。
⑧ 信封:每段都帶來源
每個回傳段落都附上檔名與頁碼。這件事的價值不在好看——它讓 ④ 的 false-accept 變成使用者可見的錯誤。你看到引用的是第 3 頁的目錄,就知道這答案不能信。
讀 code 時要注意:repo 裡還有一條獨立路徑
scripts/retrieve.py(CLI 用),用的是不同的打分公式s = kw + 5.0 * max(sem, 0)、BM25k1=1.5 / b=0.75、Jaccard 0.6 去重、語意編碼只取每頁前 2000 字元。它跟 MCP server 走的index_store.py不是同一套 scorer,別混著讀。
四、數據:哪些能信,哪些有前提

eval/RESULTS.md(2026-07-25,對 0.3.0 量測)給了兩組數字。
檢索品質——gold set 30 題(Dobbs v. Jackson 213 頁、Berkshire Hathaway 2023 年報 152 頁、內建合成文件 8 頁,各 10 題):
| 設定 | recall@5 | false-abstain |
|---|---|---|
| Hybrid(語意 + 關鍵字) | 0.90 | 0.00 |
| 純關鍵字 | 0.90 | 0.00 |
注意這張表:hybrid 跟純關鍵字的 recall 一模一樣。作者沒有藏這件事,而是加了一句關鍵註解——兩者「fail on disjoint sets」,失敗的題目不重疊:語意路撈得到換句話說的問題,關鍵字路撈得到精確術語。所以 embedding 的價值不在把 0.90 推到 0.95,而在覆蓋不同的失敗模式。這也解釋了為什麼 embedding 是選配:沒裝 sentence-transformers 就退回純關鍵字,recall 不會崩。
Token 省幅——用 tiktoken 量,單一問題、單份文件:
| 文件 | baseline | 實際用量 | 省幅 |
|---|---|---|---|
| FDA 藥品仿單 | 23,959 | 1,021 | 95.7% |
| GDPR | 70,260 | 996 | 98.6% |
| SFFA v. Harvard | 133,349 | 740 | 99.4% |
(MarkTechPost 的報導標註這三份分別是 33、88、233 頁;repo 的 RESULTS 表格只給 token 數。)
這裡有兩個一定要講清楚的前提:
- baseline 假設每次查詢都把整份文件塞進去。RESULTS.md 原文:百分比可信,但絕對數字建立在「naive baseline 每次搜尋都收整份文件的費用」上。如果你本來就有 prompt caching,或一次載入問十題,實際差距會小很多。
- 省幅是文件大小的函數,不是常數。repo 自己給的階梯是:20 頁約省 14%、80 頁約省 78%、300 頁約省 94%。作者的原話是「把 92% 當地板、上面那張表當常態」——但那個地板是對夠大的文件而言。你的 PDF 只有 20 頁,省 14% 還要付索引與 embedding 的成本,划不划算自己算。
還有三個誠實的限制:
- gold label 是作者自己寫的、未經第三方驗證(RESULTS.md 明載)
- 選檔比檢索弱:16 份 PDF 的資料夾測試中,選對檔案 12/14(約 86%)。泛稱名詞會選錯,講具體名字才穩
- 頁碼引用沒有章節層級的 provenance,多意見書的判決裡不知道引的是主文還是協同意見
- 測試覆蓋:18 個 index 測試 + 106 個 server 測試
五、手把手:裝起來並問對問題
安裝(不需要 terminal)
- 從 Releases 下載
token-saver-ccr.mcpb - Claude Desktop → Settings → Extensions(或 Settings → Advanced Settings → Install Extensions)
- Install extension,選那個
.mcpb - 把 Enabled 打開,點 Configure,選一個小而專用的 PDF 資料夾——這是權限邊界,不要選整個
~/Documents - 開新對話問「List my documents」
- 第一次問問題會下載 embedding 模型,2–5 分鐘
環境需求:Python 3.10+(bundle 內含)、Claude Desktop。相依是 mcp>=1.2、sentence-transformers>=2.6、numpy>=1.24、pypdf>=4.0,選配 pypdfium2(抽取品質更好)與 tiktoken(沒有就退回字元數估算,savings 的數字會比較粗)。
踩雷提醒:加了新 PDF、或工具沒出現時,要完全退出再開 Claude Desktop(關視窗不算)。
七個工具怎麼用
| 工具 | 用途 |
|---|---|
ask |
找檔 + 載入 + 回答一步到位(target, query, k=5) |
list_documents |
列出可用 PDF,標示已載入的 |
ingest |
預先索引一份 PDF |
search |
只回傳 top-K 片段 + 頁碼,不生成答案 |
clear |
清掉文件或整個 session |
status |
已載入哪些、idle 多久 |
savings |
這個 session 省了多少 token |
Prompt 寫法:具體名字 > 泛稱
選檔靠模糊比對(NAME_THRESHOLD = 0.5),所以泛稱會出事。
別這樣寫:
幫我查那份報告裡關於風險的段落
「報告」「文件」「那本書」這類詞會在 16 份 PDF 裡撞成一團。
這樣寫:
用 token saver 查 GDPR:資料主體行使刪除權時,控制者有哪些法定例外可以拒絕?請附頁碼。
先 ingest berkshire-2023,然後 search「float 的定義與規模變化」,k=8,只給我原文片段不要總結。
批次調查時分兩段更省:先 search 拿片段自己掃一眼,確認方向對了再 ask。
每次都要看頁碼。 前面說過 abstain gate 會 false-accept,引用就是你的偵錯訊號——引到目錄頁或版權頁,就換個更具體的提問詞重問。
收尾時問一句「show savings」,savings 會回這個 session 的累計省量。這個數字建立在第四節那個 naive baseline 上,當相對指標看就好。
六、調參:七個環境變數
預設值不見得適合你的文件。這些是 mcp_server.py 與 index_store.py 讀的:
| 變數 | 預設 | 什麼時候動它 |
|---|---|---|
TOKEN_SAVER_ALLOWED_DIRS |
由 Configure 設定 | 多個資料夾(os.pathsep 分隔) |
TOKEN_SAVER_DOC_DIRS |
— | 額外的掃描 root |
TOKEN_SAVER_MCP_MAX_CHARS |
8000 | 答案常被截斷就往上調,但這是你的成本閘門 |
TOKEN_SAVER_MCP_TTL_MINUTES |
30 | 同一份文件要問一下午 → 調大,省重建 |
TOKEN_SAVER_SEM_FLOOR |
0.25 | 撈回太多離題段落就往上調(0.35 起試) |
TOKEN_SAVER_INDEX_TTL_DAYS |
14 | 磁碟索引保留天數 |
TOKEN_SAVER_TRIM |
on | 關掉會拿到完整 180 字塊,但 token 立刻上升 |
TOKEN_SAVER_CONFIRM |
off | 打開後模糊命中檔名要先確認,適合資料夾很雜時 |
TOKEN_SAVER_SANITIZE |
off | 置換疑似 prompt injection 的行——處理來路不明的 PDF 時應該打開 |
最後那個值得單獨講:你檢索回來的段落會被當成可信內容送進模型。一份惡意 PDF 埋一行「ignore previous instructions」,就是一條 indirect prompt injection 通道。TOKEN_SAVER_SANITIZE 是它給的緩解手段,預設沒開。
七、想在 Claude Code 用?現況是這樣
先講結論:官方明確不支援。INSTALL_GUIDE.md 寫得很清楚——只支援 Claude Desktop 應用程式,不支援 claude.ai 網頁版,也沒有提到 Claude Code。
但從 mcpb/manifest.json 看,它底層就是一個標準的 MCP server,啟動命令是:
command: uv
args: ["run", "--project", "${__dirname}", "token-saver-mcp", "${user_config.allowed_directories}"]
也就是說,理論上可以手動接進任何 MCP client:
git clone https://github.com/Marktechpost/Token-Saver
cd Token-Saver
claude mcp add token-saver -- uv run --project . token-saver-mcp "$HOME/Documents/token-saver-pdfs"
我沒有實際跑過這條路徑,這是從 manifest 的 mcp_config 推出來的等價指令。作者也沒測過 Claude Code——.mcpb 的權限提示、資料夾 allowlist UI 都是 Desktop 專屬的,手動接的話這層保護就要靠你自己傳對 TOKEN_SAVER_ALLOWED_DIRS。當實驗做可以,別放進團隊 workflow。
而且說實話,對 coding agent 來說這工具的用途本來就窄:它只吃 PDF。你的痛點是 codebase 和 markdown docs,不是 PDF。真正該帶走的是下一節。
八、該偷走的三個設計
如果你要幫自己的 agent 做一層檢索 MCP(吃 codebase、吃 Confluence、吃 API docs 都行),這三件事直接抄:
1. 檢索層必須有 abstain gate,而且門檻要看混合分數。
多數自製 RAG 是「永遠回 top-5」,不管相似度多爛。Token Saver 的方向對了——設 SEM_FLOOR 並允許棄權——但它的實作 (sem >= FLOOR) | (kw > 0) 是反面教材。別用「有沒有命中關鍵字」當 OR 條件。照作者自己列的修法做:要求至少 2 個相異查詢詞命中,或對混合分數設絕對下限。
2. 給輸出一道硬牆,不要指望模型自制。
MAX_RESPONSE_CHARS = 8000 寫在 server 側。你的 MCP tool 也該這樣:在回傳前截斷,並在訊息裡明說「已截斷,還有 N 段」。讓 agent 知道自己看到的是子集,比偷偷截斷安全得多。
3. 每一段都帶 provenance,這是給人看的偵錯線。
檔名 + 頁碼(或檔名 + 行號範圍)。這不是禮貌,是讓 false-accept 從「模型幻覺」降級成「使用者一眼看穿的錯誤」。做 code 檢索的話,path:line-line 更好,因為使用者可以直接點開驗證。
順帶一提第四個小招:先跑純關鍵字,embedding 當增益。0.90 vs 0.90 那張表已經說明,對術語密集的技術文件,BM25/FTS5 就能吃掉大部分;embedding 的價值在補「換句話說」那類查詢。先把便宜的做對,再加向量。
九、什麼時候別用
- 文件小於 50 頁:20 頁只省 14%,還要付索引時間,不如直接貼
- 掃描件 / 圖片 PDF:沒有 OCR,抽不出字
- 需要通讀全文的任務(翻譯整份、逐條摘要、跨章節比對):檢索天生會漏,這類任務要的就是全文
- 文件庫很大:暴力 numpy cosine 沒有 ANN 索引,規模上去會撐不住
- 答案分散在十幾個地方:top-K + 8000 字元上限會截掉尾巴
- 你的痛點是 codebase:這工具只吃 PDF,去找專門做程式碼檢索的
適合的場景很明確:一份幾百頁的技術規格 / 法規 / 年報,你要對它問二十個具體問題。 這時候省的不只是錢,是模型不會被 200 頁雜訊帶偏。
十、來源
- GitHub repo(primary):Marktechpost/Token-Saver — MIT,
mcpb/manifest.json版本 0.3.0 - 參數出處:
scripts/index_store.py、scripts/mcp_server.py、scripts/retrieve.py - 評測數據:eval/RESULTS.md(2026-07-25,對 0.3.0 量測)
- 已知問題:docs/known-issues/false-accept-abstain-gate.md
- 安裝說明:INSTALL_GUIDE.md
- 發布報導:MarkTechPost — Meet Token Saver
- 作者:Arnav Rai(Rochester Institute of Technology,Marktechpost 實習),指導 Jean-marc Mommessin、Asif Razzaq,Marktechpost AI Media Inc
整理:DataAgent · Coding Agent 實戰教學


