Obsidian MCP+混合檢索:2026參考指南
# 應用程式基礎請使用官方Obsidian文件;MCP、混合搜尋,以及為16,894個檔案的AI vault建立索引,請參考Blake的指南。
Obsidian不是筆記應用程式。它是本機優先、純文字、圖狀結構的markdown語料庫;加入檢索基礎架構後,就會成為AI脈絡資源池。16,894個檔案。49,746個片段。23ms查詢。0次API呼叫。1個83 MB SQLite檔案。本指南涵蓋完整系統:從知識庫架構,到hybrid檢索,再到MCP整合與營運工作流程。
關鍵重點
重點是情境工程,不是做筆記。 Obsidian vault 對 AI 的價值不在筆記本身,而在讓筆記可被查詢的檢索層。沒有檢索能力的 16,000 個檔案 vault,只是只能寫入的資料庫。具備 hybrid 檢索與 MCP 整合的 200 個檔案 vault,才是 AI 知識庫。檢索基礎架構才是產品;筆記只是原料。
Hybrid 檢索勝過純關鍵字或純語意搜尋。 BM25 能抓到精確識別碼與函式名稱。向量搜尋能跨越不同術語,抓到同義詞與概念相符的結果。Reciprocal Rank Fusion (RRF) 能合併兩者,且不需要校準分數。單靠任一方法,都無法同時覆蓋這兩種失敗模式。MS MARCO 段落排序研究也確認了這個模式:hybrid 檢索一貫優於單獨使用任一方法。3 hybrid retriever 深度解析涵蓋 RRF 數學、真實數字範例、失敗模式分析,以及互動式融合計算器。
MCP 讓 AI 工具直接存取 vault。 Model Context Protocol(MCP)伺服器會把 retriever 暴露成工具,讓 Claude Code、Codex CLI、Cursor,以及其他 AI 工具可以直接呼叫。代理會查詢 vault、收到附有來源歸因的排序結果,並在不載入完整檔案的情況下使用脈絡。MCP 伺服器只是包在檢索引擎外的一層薄封裝。
Local-first 代表零 API 成本與完整隱私。 整套堆疊都在單一機器上執行:SQLite 負責儲存,Model2Vec 產生 embeddings,FTS5 處理關鍵字搜尋,sqlite-vec 處理向量 KNN。不需要雲端服務、不呼叫 API、不依賴網路。個人筆記永遠不會離開本機。以 OpenAI API 價格來看,完整重新嵌入 49,746 個 chunks 大約只需 $0.30;但真正的成本是延遲、隱私暴露,以及讓一套本應離線運作的系統產生網路依賴。4
增量索引可在 10 秒內讓系統保持最新。 系統會比較檔案修改時間來偵測變更。只有被修改的檔案會重新 chunking 與重新嵌入。在 Apple M 系列硬體上,完整重新索引大約需要 4 分鐘。一般單日編輯量的增量更新會在 10 秒內完成。系統無須手動介入,也能與時俱進。
此架構可從 200 則筆記擴展到 20,000+ 則筆記。 相同的三層設計(匯入、檢索、整合)適用於任何 vault 規模。小型 vault 可先從僅使用 BM25 的搜尋開始。當關鍵字碰撞成為問題,再加入向量搜尋。需要同時取得精確比對與語意比對時,再加入 RRF 融合。每一層都能獨立發揮作用,也能獨立移除。
如何使用本指南
本指南涵蓋完整系統。起點取決於您目前的位置:
| 您是… | 從這裡開始 | 接著探索 |
|---|---|---|
| Obsidian + AI 新手 | 為什麼選擇 Obsidian 作為 AI 基礎架構、快速開始 | Vault 架構、MCP 伺服器架構 |
| 已有 vault,想加入 AI 存取 | MCP 伺服器架構、Claude Code 整合 | Embedding 模型、全文搜尋 |
| 正在建置檢索系統 | 完整檢索管線、Reciprocal Rank Fusion | 效能調校、疑難排解 |
| 團隊或企業情境 | 決策框架、知識圖譜模式 | 開發者工作流程配方、遷移指南 |
標示為 Contract 的章節包含實作細節、設定區塊與失敗模式。標示為 Narrative 的章節著重於概念、架構決策,以及設計選擇背後的推理。標示為 Recipe 的章節提供逐步工作流程。
為什麼選擇 Obsidian 作為 AI 基礎架構
本指南的核心論點是:Obsidian vault 是個人 AI 知識庫的最佳基底,因為它 local-first、使用純文字、具備圖譜結構,而且使用者掌控整個堆疊的每一層。
Obsidian 提供了其他替代方案無法給 AI 的能力
純文字 markdown 檔案。 每則筆記都是您檔案系統上的 .md 檔案。沒有專有格式、不需要資料庫匯出,也不需要 API 才能讀取內容。任何能讀取檔案的工具都能讀取您的 vault。grep、ripgrep、Python 的 pathlib、SQLite FTS5,全都可以直接處理來源檔案。當您建置檢索系統時,索引的是檔案,而不是 API 回應。索引永遠與來源一致,因為來源就是檔案系統。
Local-first 架構。 Vault 存在您的機器上。沒有伺服器、沒有雲端同步依賴、沒有 API 速率限制,也沒有服務條款規範您如何處理自己的內容。您可以在不依賴任何外部服務的情況下,對筆記進行嵌入、索引、chunking 與搜尋。這對 AI 基礎架構很重要,因為檢索管線的速度取決於磁碟速度,而不是 API 端點回應速度。隱私同樣重要:包含憑證、健康資料、財務資訊與私人思考的個人筆記,永遠不會離開您的機器。
透過 wiki-links 建立圖譜結構。 Obsidian 的 [[wiki-link]] 語法會在筆記之間建立有向圖。關於 OAuth 實作的筆記,會連到 token 輪替、session 管理與 API 安全性的筆記。圖譜結構編碼了人為整理過的概念關係。向量 embeddings 捕捉語意相似度,但 wiki-links 捕捉的是作者思考該主題時刻意建立的連結。圖譜是一種信號,embeddings 無法複製。
Plugin 生態系。 截至 2026 年 3 月,Obsidian 有 2,500+ 個社群 plugins(高於 2025 年中期的 1,800+)。Dataview 讓您像查詢資料庫一樣查詢 vault。Templater 透過 JavaScript 邏輯,從模板產生筆記。Git 整合會將 vault 同步到儲存庫。Linter 強制維持格式一致性。Bases 核心 plugin(於 v1.9.10 引入)則在 vault 檔案之上,使用 frontmatter 屬性作為欄位,新增類似資料庫的檢視:表格、圖庫、行事曆與 kanban 看板,並儲存為 .base 檔案。15 這些 plugins 會在不改變底層純文字格式的前提下,為 vault 加入結構。檢索系統索引的是這些 plugins 的輸出,而不是 plugins 本身。
5 百萬+ 使用者。 Obsidian 擁有龐大且活躍的社群,持續產出模板、工作流程、plugins 與文件。當您遇到 vault 組織或 plugin 設定問題時,很可能已有人記錄了解法。社群也產出 Obsidian 周邊工具:MCP 伺服器、索引腳本、發布管線,以及 API wrappers。
單靠檔案系統缺少什麼
一個 markdown 檔案目錄具備純文字優勢,但缺少 Obsidian 補上的 3 件事:
-
雙向連結。 Obsidian 會自動追蹤 backlinks。當您從 Note A 連到 Note B 時,Note B 會顯示 Note A 參照了它。圖譜面板會視覺化連結群集。這種雙向感知是原始檔案系統不會提供的 metadata。
-
具備 plugin 渲染的即時預覽。 Dataview 查詢、Mermaid 圖表與 callout 區塊會即時渲染。寫作體驗比文字編輯器更豐富,但儲存格式仍是純文字。您在豐富環境中寫作與整理;檢索系統索引原始 markdown。
-
社群基礎架構。 Plugin 探索、主題市集、同步服務(選用)、發布服務(選用),以及文件生態系。您可以用獨立工具複製任一個別功能,但 Obsidian 會把它們整合成連貫的工作流程。
Obsidian 不會做什麼(以及您要建置什麼)
Obsidian 不包含檢索基礎架構。它有基本搜尋(全文、檔名、標籤),但沒有 embedding 管線、沒有向量搜尋、沒有融合排序、沒有 MCP 伺服器、沒有憑證過濾、沒有 chunking 策略,也沒有可供外部 AI 工具使用的整合 hooks。本指南涵蓋的是您在 Obsidian 之上建置的基礎架構。 Vault 是基底。檢索管線、MCP 伺服器與整合 hooks 才是基礎架構。
這裡描述的架構是 markdown-first,而不是 Obsidian-exclusive。 如果您使用 Logseq、Foam、Dendron,或只是一個普通 markdown 檔案目錄,檢索管線也能以相同方式運作。Chunker 讀取 .md 檔案。Embedder 處理文字字串。Indexer 寫入 SQLite。這些元件都不依賴 Obsidian 專屬功能。Obsidian 的貢獻在於提供寫作與組織環境,產出 retriever 要索引的 markdown 檔案。
快速開始:第一個連接 AI 的知識庫
本節會在5分鐘內,把一個 Obsidian 知識庫(vault)連接到 AI 工具。您將安裝 Obsidian、建立知識庫、安裝 MCP 伺服器,並執行第一次查詢。快速開始會使用社群 MCP 伺服器,以便立即看到結果。後續章節會說明如何建置供正式環境使用的自訂檢索管線。
先決條件
- macOS、Linux 或 Windows
- Node.js 18+(用於 MCP 伺服器)
- Obsidian 1.12+(用於 CLI 整合;較早版本仍可用於僅使用 MCP 的設定)
- 已安裝 Claude Code、Codex CLI 或 Cursor
步驟 1:建立知識庫
從 obsidian.md 下載 Obsidian,並建立新的知識庫。選擇一個您記得的位置,因為 MCP 伺服器需要絕對路徑。
# Example vault location
~/Documents/knowledge-base/
加入幾則筆記,讓檢索器有內容可搜尋。即使只有 10-20 則筆記,也足以看到結果。每則筆記都應是 .md 檔案,具備有意義的標題,並至少包含一段內容。
步驟 2:安裝 MCP 伺服器
有幾個社群 MCP 伺服器可以立即提供知識庫存取能力。這個生態系在 2025-2026 年間大幅成長。近期值得注意的更新包括 MCPVault v0.11.0(2026年3月),它新增了 list_all_tags,可掃描 frontmatter 與 hashtags 並統計數量,也改善了含句點資料夾的處理方式,並支援 .base 與 .canvas 檔案。13 此套件在 npm 上也已重新命名為 @bitbonsai/mcpvault。
2026年4月轉向——以 Obsidian CLI 作為首選橋接方式: Obsidian 1.12.0 引入第一級支援的 CLI,而公開的 1.12.7 安裝程式(2026年3月23日)內建了獨立二進位檔、TUI 與 socket-file 改善,讓終端機工作流程更容易安裝與執行。16 社群工具正積極從 Local REST API 外掛(過去支援
mcp-obsidian)遷移到以 CLI 為基礎的整合,因為它更快也更穩定。MarkusPfundstein/mcp-obsidianrepo 自 2025年6月以來沒有任何 commit,也從未有 tagged release——請將其視為維護模式,並優先採用以 CLI 為基礎的伺服器,或下方列出的較新社群替代方案。20 建議設定請參閱本指南後面的「AI 工作流程中的 Obsidian CLI」章節。
| 伺服器 | 作者 | 傳輸 | 需要外掛 | 主要功能 |
|---|---|---|---|---|
| obsidian-mcp-server | StevenStavrakis | STDIO | 否 | 輕量、以檔案為基礎 |
| mcp-obsidian | MarkusPfundstein | STDIO | Local REST API | 透過 REST 提供完整知識庫 CRUD——維護模式,自 2025年6月以來無 commit20 |
| obsidian-mcp-tools | jacksteamdev | STDIO | 是(外掛) | 語意搜尋 + Templater |
| obsidian-claude-code-mcp | iansinnott | WebSocket | 是(外掛) | Claude Code 自動探索 |
| obsidian-mcp-server | cyanheads | STDIO | Local REST API | Tags、frontmatter 管理 |
| Hybrid Search MCP | 社群 | STDIO | 否 | BM25 + 語意搜尋 MCP 伺服器 + CLI。截至 2026年4月仍屬新穎且積極維護。 |
快速開始最簡單的選項,是使用直接讀取 .md 檔案的檔案型伺服器:
npm install -g obsidian-mcp-server
步驟 3:設定您的 AI 工具
Claude Code——加入 ~/.claude/settings.json:
{
"mcpServers": {
"obsidian": {
"command": "obsidian-mcp-server",
"args": ["--vault", "/absolute/path/to/your/vault"]
}
}
}
Codex CLI——加入 .codex/config.toml:
[mcp_servers.obsidian]
command = "obsidian-mcp-server"
args = ["--vault", "/absolute/path/to/your/vault"]
Cursor——加入 .cursor/mcp.json:
{
"mcpServers": {
"obsidian": {
"command": "obsidian-mcp-server",
"args": ["--vault", "/absolute/path/to/your/vault"]
}
}
}
步驟 4:執行第一次查詢
開啟您的 AI 工具,詢問一個可由知識庫筆記回答的問題:
Search my Obsidian vault for notes about [topic you wrote about]
AI 工具會呼叫 MCP 伺服器,由它搜尋您的知識庫並傳回相符內容。您應該會看到包含檔案路徑與相關摘錄的結果。
您剛完成的內容
您已透過標準協定,將本機知識庫連接到 AI 工具。MCP 伺服器會讀取知識庫檔案、執行基本搜尋,並傳回結果。這是最小可行版本。
這個快速開始不會提供下列能力: - Hybrid retrieval(BM25 + 向量搜尋 + RRF fusion) - 以 embeddings 為基礎的語意搜尋 - 憑證篩選 - 增量索引 - 以 hook 為基礎的自動脈絡注入
本指南其餘內容會說明如何逐一建置這些能力。快速開始用於驗證概念;完整管線則提供正式環境等級的檢索品質。
AI 工作流程中的 Obsidian CLI
Obsidian 1.12(2026年2月)引入了內建命令列介面,為 AI 工作流程開啟新的整合介面。16 CLI 可作為 Obsidian GUI 的遠端控制器——Obsidian 必須正在執行(或會在第一個命令時自動啟動)。請在 Settings > General > Command line interface 中啟用。
為什麼 CLI 對 AI 基礎設施很重要
CLI 提供程式化方式,存取過去需要 GUI 或外掛 API 才能使用的 Obsidian 原生操作。對 AI 工作流程而言,關鍵能力包括:
- 從 scripts 與 hooks 搜尋。
obsidian search "query"和obsidian search:context "query"可從任何 shell script、hook 或自動化管線執行知識庫搜尋。search:context變體會傳回相符行及其周邊脈絡,適合將結果餵入 AI prompts。 - Daily notes 自動化。
obsidian daily會開啟或建立今天的 daily note。搭配 shell scripting,這能支援自動化每日簡報工作流程——hook 可以把 AI 產生的摘要附加到 daily note。 - 以 template 為基礎建立筆記。
obsidian template list和obsidian template create會從 Templater 或核心 templates 產生筆記,讓 AI agents 不必直接寫入 markdown 檔案,也能建立結構化知識庫項目。 - 屬性管理。
obsidian property set和obsidian property get會讀寫 frontmatter 屬性,讓 scripts 不必解析 YAML,也能更新 metadata。 - 外掛控制。
obsidian plugin enable/disable/list可用程式化方式管理外掛,對批次作業期間切換索引外掛很有幫助。 - 任務管理。
obsidian task list/add/complete提供結構化任務存取,適合需要管理知識庫中工作項目的 AI agents。
CLI 與 MCP 的 AI 存取比較
CLI 與 MCP 伺服器扮演不同角色,兩者互補而非競爭:
| 面向 | Obsidian CLI | MCP 伺服器 |
|---|---|---|
| 呼叫者 | Shell scripts、hooks、cron jobs | AI agents(Claude Code、Codex、Cursor) |
| 協定 | POSIX process(stdin/stdout/stderr) | MCP(透過 STDIO 或 HTTP 的 JSON-RPC) |
| 強項 | Obsidian 原生操作(templates、plugins、properties) | 自訂檢索(embeddings、BM25、RRF fusion) |
| 限制 | 沒有向量搜尋,沒有 embedding 管線 | 無法存取 Obsidian 內部操作 |
| 最適合 | 自動化 scripts、匯入管線、hook 動作 | Session 期間的即時 AI agent 查詢 |
建議: 使用 CLI 處理匯入自動化(建立筆記、管理屬性、執行 Obsidian 原生搜尋),並使用 MCP 進行檢索(結合 embeddings 的 hybrid search)。PreToolUse hook 可以先呼叫 obsidian search:context 做快速預檢,再視需要回退到完整 MCP 檢索器取得排名結果。
範例:由 CLI 驅動的匯入 hook
#!/bin/bash
# Hook: append today's signals to daily note via CLI
DATE=$(date +%Y-%m-%d)
SUMMARY="$1"
obsidian daily # ensure daily note exists
obsidian file append "Daily Notes/${DATE}.md" "## AI Summary\n${SUMMARY}"
Obsidian Agent外掛
越來越多Obsidian外掛把AI coding agents直接嵌入vault UI,作為外部MCP伺服器設定的替代方案。這些外掛是在Obsidian側邊欄內執行AI agent,而不是從外部工具連線。
Claudian
Claudian將Claude Code作為AI協作者嵌入vault。vault目錄會成為Claude的工作目錄,讓它具備完整的agentic能力:檔案讀寫、搜尋、bash指令,以及多步驟工作流程。17
AI基礎設施的關鍵功能:
- 具備情境感知的提示。自動附加目前聚焦的筆記,支援@notename檔案提及、以標籤排除內容,以及將編輯器選取範圍作為情境。
- Vision支援。可透過拖放、貼上或檔案路徑分析圖片,適合處理vault中擷取的螢幕截圖與圖表。
- Slash commands。建立可重複使用的提示範本,並由/command觸發,讓vault操作更標準化。
- 權限模式。YOLO(自動核准)、Safe(每個動作都需核准)與Plan(僅規劃)模式,並提供安全封鎖清單與vault範圍限制。
Agent Client
Agent Client透過Agent Client Protocol(ACP),將Claude Code、Codex CLI與Gemini CLI整合進統一的Obsidian側邊欄。18
關鍵功能:
- 多agent切換。可在同一面板中與Claude Code、Codex或Gemini CLI對話,並依需求在agent之間切換。
- 筆記提及。使用@notename將筆記內容納入提示,類似Claudian,但不綁定特定agent。
- Shell執行。直接在聊天中執行終端機指令,包括建置腳本、git指令,或任何終端機操作,不必離開對話。
- 動作核准。對檔案讀取、編輯與指令執行提供細緻控制。
何時使用agent外掛,何時使用外部MCP
| 情境 | Agent外掛 | 外部MCP |
|---|---|---|
| 透過AI協助撰寫與編輯vault筆記 | 較佳——agent看得到編輯器情境 | 可用,但沒有編輯器感知能力 |
| 跨多個repo進行程式碼開發 | 有限——範圍限於vault | 較佳——以專案為範圍,並可存取完整檔案系統 |
| 從大型已索引語料庫擷取內容 | 僅基本搜尋 | 完整hybrid retrieval流程 |
| 記筆記期間快速進行vault問答 | 理想——不需切換情境 | 需要切換到終端機 |
建議:將agent外掛用於以vault為中心的工作流程(撰寫、整理、摘要筆記)。若AI agent需要完整retrieval流程,並且要存取vault外部的程式碼庫,則使用外部MCP伺服器。這兩種做法可以並存——在Obsidian內執行Claudian處理筆記工作,並在外部搭配Claude Code與MCP進行開發。
決策框架:Obsidian與替代方案
不是每個使用情境都需要Obsidian。本節說明何時Obsidian是合適的基礎,何時過度複雜,以及何時其他工具更適合。
決策樹
START: What is your primary content type?
│
├─ Structured data (tables, records, schemas)
│ → Use a database. SQLite, PostgreSQL, or a spreadsheet.
│ → Obsidian is for prose, not tabular data.
│
├─ Ephemeral context (current project, temporary notes)
│ → Use CLAUDE.md / AGENTS.md in the project repo.
│ → These travel with the code and reset per project.
│
├─ Team wiki (shared documentation, onboarding)
│ → Evaluate Notion, Confluence, or a shared git repo.
│ → Obsidian vaults are personal-first. Team sync is possible
│ but not native.
│
└─ Growing personal knowledge corpus
│
├─ < 50 notes
│ → A folder of markdown files + grep is sufficient.
│ → Obsidian adds value mainly through the link graph,
│ which needs density to be useful.
│
├─ 50 - 500 notes
│ → Obsidian adds value. Wiki-links create a navigable graph.
│ → BM25-only search (FTS5) is sufficient at this scale.
│ → Skip vector search and RRF until keyword collisions appear.
│
├─ 500 - 5,000 notes
│ → Full hybrid retrieval becomes valuable. Keyword collisions
│ increase. Semantic search catches queries that BM25 misses.
│ → Add vector search + RRF fusion at this scale.
│
└─ 5,000+ notes
→ Full pipeline is essential. BM25-only returns too much noise.
→ Credential filtering becomes critical (more notes = more
accidentally pasted secrets).
→ Incremental indexing matters (full reindex takes minutes).
→ MCP integration pays dividends on every AI interaction.
比較矩陣
| 評估標準 | Obsidian | Notion | Apple Notes | 純檔案系統 | CLAUDE.md |
|---|---|---|---|---|---|
| Local-first | 是 | 否(雲端) | 部分(iCloud) | 是 | 是 |
| 純文字 | 是(markdown) | 否(blocks) | 否(專有格式) | 是 | 是 |
| 圖譜結構 | 是(wiki-links) | 部分(mentions) | 否 | 否 | 否 |
| 可供AI索引 | 直接檔案存取 | 需要API | 需要匯出 | 直接檔案存取 | 已在情境中 |
| 外掛生態系 | 2,500+個外掛 | 整合功能 | 無 | N/A | N/A |
| 離線能力 | 完整 | 唯讀快取 | 部分 | 完整 | 完整 |
| 可擴展至10K+筆記 | 是 | 是(搭配API) | 會劣化 | 是 | 否(單一檔案) |
| 成本 | 免費(核心功能) | $10/月以上 | 免費 | 免費 | 免費 |
何時Obsidian過度複雜
- 單一專案情境。如果AI只需要目前程式碼庫的情境,請放在
CLAUDE.md、AGENTS.md或專案層級文件中。這些檔案會跟著repo移動,並自動載入。 - 結構化資料。如果內容是表格、記錄或schema,請使用資料庫。Obsidian筆記以散文為優先。Dataview可以查詢frontmatter欄位,但真正的資料庫更擅長處理結構化查詢。
- 暫時性研究。如果筆記會在專案結束後丟棄,使用包含markdown檔案的scratch目錄會更簡單。不要為曇花一現的內容建立retrieval基礎設施。
何時Obsidian是正確選擇
- 持續數月或數年累積知識。隨著語料庫成長,價值會不斷複利累積。每天查詢、持續六個月的200則筆記vault,比只查詢一次的5,000則筆記vault更有價值。
- 單一語料庫涵蓋多個領域。如果vault包含程式設計、架構、安全、設計與個人專案等筆記,跨領域retrieval會帶來專案專屬
CLAUDE.md無法提供的效益。 - 隱私敏感內容。Local-first代表retrieval流程不會把內容傳送到外部服務。vault會包含您放入的任何內容,包括您不會上傳到雲端服務的資料。
心智模型:三層架構
此系統有3個彼此獨立運作、結合後又能相互加乘的層。每一層關注不同問題,也有不同的失敗模式。
┌─────────────────────────────────────────────────────┐
│ INTEGRATION LAYER │
│ MCP servers, hooks, skills, context injection │
│ Concern: delivering context to AI tools │
│ Failure: wrong context, too much context, stale │
└──────────────────────┬──────────────────────────────┘
│ query + ranked results
┌──────────────────────┴──────────────────────────────┐
│ RETRIEVAL LAYER │
│ BM25, vector KNN, RRF fusion, token budget │
│ Concern: finding the right content for any query │
│ Failure: wrong ranking, missed results, slow queries │
└──────────────────────┬──────────────────────────────┘
│ chunked, embedded, indexed
┌──────────────────────┴──────────────────────────────┐
│ INTAKE LAYER │
│ Note creation, signal triage, vault organization │
│ Concern: what enters the vault and how it's stored │
│ Failure: noise, duplicates, missing structure │
└─────────────────────────────────────────────────────┘
Intake決定哪些內容能進入vault。若缺乏篩選,vault會累積雜訊:tweets螢幕截圖、沒有註解的複製貼上文章、缺乏情境的半成品想法。intake層負責在入口處進行品質控管。無論是評分流程、標籤慣例或人工審查流程,重點都是要有某種機制,確保vault中存放的是值得retrieval的內容。
Retrieval讓vault可以被查詢。這是整個引擎:將筆記chunking成搜尋單位,把chunks做成embeddings放入向量空間,為關鍵字與語意搜尋建立索引,再用RRF融合結果。retrieval層會把一個檔案目錄轉換成可查詢的知識庫。沒有這一層,vault仍可透過人工瀏覽與基本搜尋導覽,但AI工具無法以程式化方式存取。
Integration將retrieval層連接到AI工具。MCP伺服器會將retrieval公開成可呼叫工具。Hooks會自動注入情境。Skills會把新知識回寫到vault。integration層是知識庫與使用它的AI agents之間的介面。
這些層在設計上彼此解耦。intake評分流程不需要知道embeddings。retriever不需要知道signal routing規則。MCP伺服器不需要知道筆記如何建立。這種解耦表示您可以獨立改善任一層。替換embedding model而不改動intake流程。新增MCP能力而不修改retriever。調整signal scoring啟發式規則而不碰索引。
供AI使用的vault架構
針對AI檢索最佳化的vault,遵循的慣例會不同於針對個人瀏覽最佳化的vault。本節說明資料夾結構、筆記結構、frontmatter慣例,以及能提升檢索品質的具體模式。
資料夾結構
頂層資料夾使用數字前綴,建立可預期的組織層級。數字不代表優先順序,而是用來分組相關領域,讓結構一目了然。
vault/
├── 00-inbox/ # Unsorted captures, pending triage
├── 01-projects/ # Active project notes
├── 02-areas/ # Ongoing areas of responsibility
├── 03-resources/ # Reference material by topic
│ ├── programming/
│ ├── security/
│ ├── ai-engineering/
│ ├── design/
│ └── devops/
├── 04-archive/ # Completed projects, old references
├── 05-signals/ # Scored signal intake
│ ├── ai-tooling/
│ ├── security/
│ ├── systems/
│ └── ...12 domain folders
├── 06-daily/ # Daily notes (if used)
├── 07-templates/ # Note templates (excluded from index)
├── 08-attachments/ # Images, PDFs (excluded from index)
├── .obsidian/ # Obsidian config (excluded from index)
└── .indexignore # Paths to exclude from retrieval index
應該建立索引的資料夾:所有包含markdown散文內容的資料夾,包括專案、領域、資源、signals、daily notes。
應該排除索引的資料夾:Templates(其中包含佔位變數,而不是內容)、attachments(二進位檔案)、Obsidian設定,以及任何包含敏感內容且不希望進入檢索索引的資料夾。
.indexignore檔案
在vault根目錄建立.indexignore檔案,明確從檢索索引排除路徑。語法與.gitignore相同:
# Obsidian internal
.obsidian/
# Templates contain placeholders, not content
07-templates/
# Binary attachments
08-attachments/
# Personal health/medical notes
02-areas/health/
# Financial records
02-areas/finance/personal/
# Career documents (resumes, salary data)
02-areas/career/private/
indexer會在掃描前讀取此檔案,並完全跳過符合規則的路徑。排除路徑中的檔案永遠不會被切成chunk、永遠不會產生embeddings,也永遠不會出現在搜尋結果中。
筆記結構
每則筆記都應該有YAMLfrontmatter。retriever會使用frontmatter欄位進行篩選與情境補強:
---
title: "OAuth Token Rotation Patterns"
type: note # note | signal | project | moc | daily
domain: security # primary domain for routing
tags:
- authentication
- oauth
- token-management
created: 2026-01-15
updated: 2026-02-28
source: "" # URL if captured from external source
status: active # active | archived | draft
---
檢索所需欄位:
title—用於搜尋結果顯示,以及BM25的標題情境type—支援依類型篩選的查詢(「只顯示MOCs」或「只顯示signals」)tags—以0.3權重編入FTS5標題情境,即使本文使用不同術語,也能提供關鍵字匹配
可選但有價值的欄位:
domain—支援依領域限定範圍的查詢(「只搜尋安全性筆記」)source—記錄擷取內容的來源;retriever可在結果中包含來源URLstatus—可將已封存或草稿筆記排除在有效搜尋之外
Chunking慣例
retriever會在H2(##)標題邊界進行chunking。這表示您的筆記結構會直接影響檢索粒度:
有利於檢索:
## Token Rotation Strategy
The rotation interval depends on the threat model...
## Implementation with refresh_token
The OAuth 2.0 refresh token flow requires...
## Error Handling: Expired Tokens
When a token expires mid-request...
3個H2區段會產生3個可獨立搜尋的chunk。每個chunk都有足夠情境,讓embedding捕捉其意義。關於「expired token handling」的查詢,會具體匹配第3個chunk。
不利於檢索:
# OAuth Notes
Token rotation depends on threat model. The OAuth 2.0 refresh
token flow requires storing the refresh token securely. When a
token expires mid-request, the client should retry after refresh.
The rotation interval is typically 15-30 minutes for access tokens
and 7-30 days for refresh tokens...
一個沒有H2標題的長區段只會產生一個大型chunk。embedding會在區段中的所有主題之間取平均。對任何子主題的查詢,都會同等匹配整則筆記。
經驗法則:如果一個區段涵蓋超過一個概念,請拆分成H2小節。chunker會處理其餘部分。
不應放入筆記的內容
會降低檢索品質的內容:
- 未加註解就原樣複製整篇文章。retriever會索引原文文章的關鍵字,讓vault被您沒有撰寫的內容稀釋。請改為新增摘要、摘錄重點,或連結到來源URL。
- 沒有文字說明的截圖。retriever索引的是markdown文字。沒有替代文字或周邊描述的圖片,對BM25與向量搜尋而言都是不可見的。
- 憑證字串。API金鑰、token、密碼、連線字串。即使有憑證篩選,最安全的做法仍是絕不把secret貼進筆記。請改以名稱參照(「
~/.env中的CloudflareAPItoken」)。 - 未經整理的自動產生內容。如果工具產生了一則筆記(會議逐字稿、Readwise highlights、RSS匯入),請先審閱並加上註解,再放入永久vault。未整理的自動匯入只會增加內容量,卻不會增加可檢索價值。
AI工作流程的外掛生態系
能改善AI檢索保存庫品質的Obsidian外掛分為3類:結構型(強制一致性)、查詢型(揭露中繼資料),以及同步型(讓保存庫保持最新)。
必備外掛
Dataview。 使用frontmatter欄位,像查詢資料庫一樣查詢保存庫。建立動態索引:「過去30天內更新、標記為security的所有筆記」,或「狀態為active的所有專案筆記」。Dataview不會直接幫助檢索,但能協助您找出保存庫涵蓋範圍的缺口,並找到需要更新的筆記。
TABLE type, domain, updated
FROM "03-resources"
WHERE status = "active"
SORT updated DESC
LIMIT 20
Templater。 使用含有動態欄位的範本建立筆記。透過範本預先填入created、type和domain欄位,確保每則新筆記一開始就具備正確的frontmatter。一致的frontmatter可改善檢索篩選。
<%* /* New Resource Note Template */ %>
---
title: "<% tp.file.cursor() %>"
type: note
domain: <% tp.system.suggester(["programming", "security", "ai-engineering", "design", "devops"], ["programming", "security", "ai-engineering", "design", "devops"]) %>
tags: []
created: <% tp.date.now("YYYY-MM-DD") %>
updated: <% tp.date.now("YYYY-MM-DD") %>
source: ""
status: active
---
## Key Points
## Details
## References
Linter。 在整個保存庫中強制套用格式規則。一致的標題階層(H1作為標題、H2作為章節、H3作為小節)可確保分塊器產生可預期的結果。對檢索有影響的Linter規則包括:
- 標題遞增:強制標題層級依序排列(不可從H1跳到H3)
- YAML標題:符合檔案名稱
- 行尾空白:移除(避免FTS5斷詞產生雜訊)
- 連續空白行:限制為1行(產生更乾淨的區塊)
Git整合。 為保存庫提供版本控制。追蹤一段時間內的變更、在多台機器之間同步,並從意外刪除中復原。Git也提供索引器用於增量變更偵測的mtime資料。
有助於索引的外掛
Smart Connections。 這是一個Obsidian外掛,可在Obsidian內提供AI驅動的語意搜尋。Smart Connections v4預設會建立本機embeddings(嵌入向量),保存庫完成索引後,語意連結與查找即可完全離線運作,不需要任何API呼叫。11 v4.5.0(2026年5月5日)將頁尾連結納入Smart Connections Core,因此每個安裝都能在頁尾顯示相關筆記連結,而不必開啟側邊面板。近期的v4版本也為連結清單加入圖形檢視、可設定的停駐位置、索引作業中斷後改進的block-embedding復原,以及「Substrate」:一個跨外掛環境,讓Smart Connections、Smart Chat和Smart Composer共享狀態。21 雖然本指南中的檢索系統位於Obsidian外部(以Python管線執行),Smart Connections仍有助於在寫作時探索語意關係。兩個系統索引相同內容,但服務不同使用情境:Smart Connections用於編輯器內探索;外部檢索器則透過MCP整合AI工具。
2026年4月推出的AI原生外掛。 一波新的社群外掛直接瞄準Claude Code / Codex / Gemini-CLI工作流程:
| 外掛 | 發布時間 | 功能 |
|---|---|---|
| Cortex | 4月4日 | 由Claude Code驅動的保存庫agent:將保存庫視為agent工作區,而不只是筆記儲存區 |
| VaultSearch | 4月7日 | 本機優先的hybrid混合搜尋:BM25 + 語意 + 模糊搜尋(與本指南的檢索堆疊高度重疊) |
| LLM Wiki | 4月9日 | 將保存庫轉為可私密查詢的知識庫 |
| Drift | 4月11日 | 類似VS Code的diff檢視器,用於AI驅動的Obsidian編輯;定位於Claude Code工作流程 |
| EngramQuest | 4月11日 | 從筆記產生記憶挑戰;提供適用於Claude Code / Gemini CLI / Cursor的「AI Skills」 |
| Hybrid Search MCP | 3月(仍屬新品) | MCP伺服器 + CLI,搭配BM25 + 語意搜尋:專為AI助理打造 |
請將這視為正在浮現的應用表面。未來幾季,其中幾個外掛很可能會整合,或被Smart Connections / Obsidian核心吸收。若今天要選一個,VaultSearch和Hybrid Search MCP在理念上最接近本指南的外部檢索器。
Dataview註記: Dataview(長期存在的Obsidian查詢外掛)上次發布0.5.70是在2025年4月,此後實質上已停滯。對於新的工作,Obsidian內建的Bases功能(1.9+)是隱含的後繼方案,也是建議採用的路徑。
Metadata Menu。 提供結構化frontmatter編輯,並支援欄位值自動完成。可減少type、domain和tags欄位中的拼寫錯誤。一致的中繼資料能提升檢索篩選準確度。
會傷害索引的外掛
Excalidraw。 將繪圖儲存為嵌入在markdown檔案中的JSON。該JSON在語法上是有效的markdown,但在分塊與嵌入後會產生無用內容。請透過.indexignore將Excalidraw檔案排除在索引之外,或依檔案副檔名篩選。
Kanban。 將看板狀態儲存為特殊格式的markdown。此格式是為Kanban呈現而設計,不適合散文式內容檢索。分塊器會產生卡片標題與中繼資料片段,而這些內容的嵌入效果不佳。請將Kanban看板排除在索引之外。
Calendar。 建立內容極少的每日筆記(通常只有日期標題)。空白或近乎空白的筆記會產生低品質區塊。若使用每日筆記,請在其中撰寫實質內容,或將每日筆記資料夾排除在索引之外。
重要的外掛設定
檔案復原 → 已啟用。 防止意外刪除筆記。雖然與檢索沒有直接關係,但對您仰賴的知識庫至關重要。
嚴格換行 → 已停用。 相較於Obsidian嚴格模式(單一換行作為<br>),Markdown標準換行(段落使用雙換行)會產生更乾淨的區塊。
新檔案預設位置 → 指定資料夾。 將新檔案導向00-inbox/,避免未分類筆記污染領域資料夾。收件匣是暫存區;檔案經分流整理後再移至領域資料夾。
Wiki-link格式 → 可行時使用最短路徑。 較短的連結目標,能讓檢索器在索引連結結構時更容易解析。
嵌入模型:選擇與設定
嵌入模型會將文字區塊轉換為數值向量,用於語意搜尋。模型選擇會決定檢索品質、索引大小、嵌入速度與執行階段依賴。本節說明為何預設選擇Model2Vec的potion-base-8M,以及何時該改用其他替代方案。
為何選擇Model2Vec potion-base-8M
模型: minishlab/potion-base-8M
參數: 760萬
維度: 256
大小: 約30 MB
依賴: model2vec(僅numpy,無PyTorch)
推論: 僅CPU,靜態詞嵌入(無attention layers)
Model2Vec會將sentence transformer的知識蒸餾為靜態token嵌入。Model2Vec不會像BERT、MiniLM與其他transformer模型那樣在輸入上執行attention layers,而是透過預先計算的token嵌入加權平均來產生向量。5實務上的結果是:因為沒有序列式運算,嵌入速度比transformer-based模型快50到500倍。
在目前的Model2Vec結果頁面中,potion-base-8M達到all-MiniLM-L6-v2全任務分數約92%(51.32對55.80),同時速度快上數個數量級。6剩下的品質差距,就是速度與簡化優勢所需承擔的取捨。對短markdown區塊而言(典型vault平均200到400字),品質差異沒有長文件那麼明顯,因為兩種模型對短而聚焦的文字會收斂到相近的表示法。
設定
# embedder.py
DEFAULT_MODEL = "minishlab/potion-base-8M"
EMBEDDING_DIM = 256
class Model2VecEmbedder:
def __init__(self, model_name=DEFAULT_MODEL):
self._model_name = model_name
self._model = None
def _ensure_model(self):
if self._model is not None:
return
_activate_venv() # Add isolated venv to sys.path
from model2vec import StaticModel
self._model = StaticModel.from_pretrained(self._model_name)
def embed_batch(self, texts):
self._ensure_model()
vecs = self._model.encode(texts)
return [v.tolist() for v in vecs]
延遲載入。模型會在首次使用時載入,而不是在import時載入。當retriever以BM25-only fallback模式運作時(例如未安裝嵌入venv),匯入embedder模組不會產生任何成本。
隔離的虛擬環境。模型會在專用venv中執行(例如~/.claude/venvs/memory/),以避免與其餘toolchain發生依賴衝突。_activate_venv()函式會在執行階段將venv的site-packages加入sys.path。
# Create isolated venv
python3 -m venv ~/.claude/venvs/memory
~/.claude/venvs/memory/bin/pip install model2vec
批次處理。embedder會以64筆為一批處理文字,以分攤Model2Vec的額外成本。indexer會將區塊送入embed_batch(),而不是一次嵌入一個區塊。
何時選擇替代方案
| 模型 | 維度 | 大小 | 速度 | 品質(MTEB) | 最適合 |
|---|---|---|---|---|---|
| potion-base-8M | 256 | 30 MB | 500x | 51.32 | 預設:本機、快速、無GPU |
| potion-base-32M | 256 | 120 MB | 400x | 52.83 | 更高品質,仍是靜態 |
| potion-retrieval-32M | 256 | 120 MB | 400x | 35.06(retrieval) | 針對檢索最佳化的靜態模型 |
| potion-multilingual-128M | 256 | 約500 MB | 300x | — | 多語vault(101種語言) |
| all-MiniLM-L6-v2 | 384 | 80 MB | 1x | 55.80 | 更高品質,仍可本機執行 |
| nomic-embed-text-v1.5 | 768 | 270 MB | 0.5x | 62.28 | 最佳本機品質 |
| text-embedding-3-small | 1536 | API | N/A | 62.30 | 以API為基礎,最高品質 |
若您想要比potion-base-8M更好的品質,但仍留在靜態嵌入家族,請選擇potion-base-32M。它使用從baai/bge-base-en-v1.5蒸餾而來的較大詞彙表,達到52.83的全任務分數(約比potion-base-8M高3%),同時維持相同的256維輸出與僅需numpy的依賴。8模型檔案大4倍會增加記憶體用量,但嵌入速度仍比transformer模型快上數個數量級。
若主要使用情境是檢索(vault搜尋正是如此),請選擇potion-retrieval-32M。此變體從potion-base-32M微調而來,專門用於檢索任務;在Model2Vec的檢索基準表中得分35.06,高於potion-base-32M的32.67。8取捨在於,它針對檢索最佳化,而不是追求通用嵌入品質。
若vault包含多種語言的筆記,請選擇potion-multilingual-128M。這個101語言模型於2025年5月發布,是多語任務中表現最佳的靜態嵌入模型,可為任何語言的任何文字產生嵌入,同時維持與其他potion模型相同的僅numpy依賴。12較大的模型檔案(約500 MB)是換取跨語言能力的代價。若您同時有英文內容,以及日文、中文、德文或其他非英文語言筆記,請使用此模型。
若檢索品質比速度更重要,且已安裝PyTorch,請選擇all-MiniLM-L6-v2。384維向量會讓SQLite資料庫大小比256維向量增加約50%。在M-series硬體上,對15,000個檔案完整重建索引時,嵌入速度會從不到1分鐘降到約10分鐘。
若需要最佳的本機檢索品質,且能接受較慢的索引速度,請選擇nomic-embed-text-v1.5。768維向量大約會讓資料庫大小變成3倍。需要PyTorch與現代CPU或GPU。
若網路延遲與隱私是可接受的取捨,請選擇text-embedding-3-small。API會產生最高品質的嵌入,但會引入雲端依賴、按token計費(每百萬token 0.02美元),並將您的內容傳送到OpenAI伺服器。
其他所有情況都維持使用potion-base-8M。速度優勢對反覆索引(開發期間重新索引)至關重要;僅需numpy的依賴可避開PyTorch安裝複雜度;256維向量也能讓資料庫保持精簡。
量化與降維
Model2Vec v0.5.0+支援以較低精度與較少維度載入模型。8這對部署到受限硬體,或在不更換模型的情況下降低資料庫大小很有幫助:
from model2vec import StaticModel
# Load with int8 quantization (25% of original size)
model = StaticModel.from_pretrained("minishlab/potion-base-8M", quantize=True)
# Load with reduced dimensions (e.g., 128 instead of 256)
model = StaticModel.from_pretrained("minishlab/potion-base-8M", dimensionality=128)
量化模型能以一小部分記憶體占用,保留幾乎相同的檢索品質。降維採用Matryoshka-style截斷:前N個維度承載最多資訊。從256維降到128維可將向量儲存空間減半,對短文字檢索的品質損失極小。
Model2Vec v0.8.x更新tokenizer與持久化內部機制,停止支援Python 3.9,並將發布結果更新到較新的MTEB表格。升級production indexer之前,請先釘選或測試model2vec,因為即使嵌入模型名稱維持不變,library升級仍可能改變模型載入路徑。10
為Vault專用嵌入進行微調
Model2Vec v0.4.0+支援在靜態嵌入之上訓練自訂分類模型;v0.7.0加入詞彙量化與可設定的pooling供蒸餾使用;v0.8.x則重構tokenizer與持久化行為。10這與含有專門詞彙的vault相關(醫療筆記、法律參照、特定領域術語),因為預設potion模型可能無法捕捉語意細微差異:
from model2vec import StaticModel
from model2vec.train import train_model
# Fine-tune on vault-specific data
model = StaticModel.from_pretrained("minishlab/potion-base-8M")
trained_model = train_model(model, train_texts, train_labels)
trained_model.save_pretrained("./vault-embeddings")
對大多數vault而言,預設potion-base-8M已能產生足夠的檢索品質。只有當檢索持續漏掉通用模型無法捕捉的領域特定關聯時,微調才值得投入。
模型Hash追蹤
indexer會儲存由模型名稱與詞彙表大小推導出的hash。如果更換嵌入模型,indexer會在下一次增量執行時偵測到不相符,並自動觸發完整重建索引。
def _compute_model_hash(self):
"""Hash model name + vocab size for compatibility tracking."""
key = f"{self._model_name}:{self._model.vocab_size}"
return hashlib.sha256(key.encode()).hexdigest()[:16]
這可避免在同一個資料庫中混用不同模型產生的向量,否則會得到毫無意義的cosine similarity分數。
失敗模式
模型下載失敗。第一次執行會從Hugging Face下載模型。若下載失敗(網路問題、企業防火牆),retriever會fallback到BM25-only模式。模型在首次下載後會快取到本機。
維度不相符。若未清除資料庫就切換模型,已儲存的向量維度會與新的嵌入不同。indexer會透過模型hash偵測此情況,並觸發完整重建索引。若hash檢查失敗(自訂模型沒有適當hash),sqlite-vec會在維度不符的KNN查詢上報錯。
大型vault的記憶體壓力。一次批次嵌入50,000個以上區塊可能消耗大量記憶體。indexer會以64筆一批處理,以限制尖峰記憶體用量。若記憶體仍然不足,請降低批次大小。
使用FTS5進行全文搜尋
SQLite的FTS5擴充功能提供具備BM25排名的全文搜尋。FTS5是hybrid檢索管線中的關鍵字搜尋元件。本節說明FTS5設定、BM25擅長的情境,以及其特定失敗模式。
FTS5虛擬表格
CREATE VIRTUAL TABLE chunks_fts USING fts5(
chunk_text,
section,
heading_context,
content=chunks,
content_rowid=id
);
內容同步模式。content=chunks參數會指示FTS5直接參照chunks表格,而不是儲存另一份重複文字。這可將儲存需求減半,但也表示在插入、更新或刪除chunks時,必須手動同步FTS5。
欄位。索引包含3個欄位:
- chunk_text — 每個chunk的主要內容(BM25權重:1.0)
- section — H2標題文字(BM25權重:0.5)
- heading_context — 筆記標題、標籤與metadata(BM25權重:0.3)
BM25排名
BM25會依據詞頻、逆文件頻率,以及文件長度正規化來排名文件。FTS5中的bm25()輔助函式接受每個欄位的權重:
SELECT
c.id, c.file_path, c.section, c.chunk_text,
bm25(chunks_fts, 1.0, 0.5, 0.3) AS score
FROM chunks_fts
JOIN chunks c ON chunks_fts.rowid = c.id
WHERE chunks_fts MATCH ?
ORDER BY score
LIMIT 30;
欄位權重(1.0、0.5、0.3)代表:
- chunk_text中的關鍵字相符對分數貢獻最大
- section(標題)中的相符貢獻為一半
- heading_context(標題、標籤)中的相符貢獻為30%
這些權重可以調整。若您的vault(Obsidian中的知識庫)有描述清楚、能高度預測內容品質的標題,請提高section權重。若標籤完整且準確,請提高heading_context權重。
BM25勝出的情境
BM25擅長處理包含精確識別碼的查詢:
- 函式名稱:
_rrf_fuse、embed_batch、get_stale_files - CLI旗標:
--incremental、--vault、--model - 設定鍵:
bm25_weight、max_tokens、batch_size - 錯誤訊息:
SQLITE_LOCKED、ConnectionRefusedError - 特定專門術語:
PostToolUse、PreToolUse、AGENTS.md
對於這類查詢,BM25會立即找到精確相符項目。向量搜尋會傳回語意相關的內容,但可能把精確相符項目排在概念性討論之後。
BM25失效的情境
當查詢使用的術語與已儲存內容不同時,BM25就會失效:
- 查詢:「如何處理驗證失敗」→ Vault內含關於「登入錯誤復原」與「session過期處理」的筆記。由於關鍵字不同,BM25不會相符。
- 查詢:「管理state的最佳方式是什麼」→ Vault內含關於「Redux store patterns」與「context providers」的筆記。因為「state management」是透過特定技術名稱表達,BM25會漏掉。
BM25在大規模資料中也會因關鍵字碰撞而失效。在15,000個檔案的vault中,搜尋「configuration」會相符數百則筆記,因為幾乎每個專案筆記都提到configuration。結果在技術上正確,但實務上毫無用處;排名無法判斷哪一則「configuration」筆記與目前查詢相關。
FTS5Tokenizer
FTS5預設使用unicode61tokenizer,可處理ASCII與Unicode文字。若vault中有大量CJK(中文、日文、韓文)內容,可以考慮使用trigramtokenizer:
-- For CJK-heavy vaults
CREATE VIRTUAL TABLE chunks_fts USING fts5(
chunk_text, section, heading_context,
content=chunks, content_rowid=id,
tokenize='trigram'
);
預設的unicode61tokenizer會依字詞邊界切分。對於字詞之間沒有空格的語言,效果不佳。trigramtokenizer會每3個字元切分一次,能支援子字串相符,但代價是索引大小會增加(約為3倍)。
維護
當底層chunks表格變更時,FTS5需要明確同步:
# After inserting chunks
cursor.execute("""
INSERT INTO chunks_fts(chunks_fts)
VALUES('rebuild')
""")
rebuild指令會從內容表格重建FTS5索引。請在大量插入(完整重新索引)後執行,但不要在個別增量更新後執行;對於後者,請使用INSERT INTO chunks_fts(rowid, chunk_text, section, heading_context)同步個別資料列。
使用sqlite-vec進行Vector Search
sqlite-vec擴充功能將向量KNN(K-Nearest Neighbors)搜尋帶入SQLite。本節涵蓋sqlite-vec設定、從筆記到可搜尋向量的embedding pipeline,以及特定查詢模式。
sqlite-vec Virtual Table
CREATE VIRTUAL TABLE chunk_vecs USING vec0(
id INTEGER PRIMARY KEY,
embedding float[256]
);
vec0模組會將256維浮點向量儲存為封裝的二進位資料。id欄位與chunks表格形成1:1對應,因此可以在向量結果與chunk中繼資料之間進行join。
Embedding Pipeline
pipeline會從筆記流向可搜尋向量:
Note (.md file)
→ Chunker: split at H2 boundaries
→ Chunks (30-2000 chars each)
→ Credential filter: scrub secrets
→ Embedder: Model2Vec encode
→ Vectors (256-dim float arrays)
→ sqlite-vec: store as packed binary
→ Ready for KNN queries
向量序列化
Python的struct模組會將浮點向量序列化,以便儲存於sqlite-vec:
import struct
def _serialize_vector(vec):
"""Pack float list into binary for sqlite-vec."""
return struct.pack(f"{len(vec)}f", *vec)
def _deserialize_vector(blob, dim=256):
"""Unpack binary blob to float list."""
return list(struct.unpack(f"{dim}f", blob))
KNN查詢
vector search查詢會先將輸入查詢轉為embedding,接著依cosine distance找出K個最接近的chunks:
def _vector_search(self, query_text, limit=30):
query_vec = self.embedder.embed_batch([query_text])[0]
packed = _serialize_vector(query_vec)
results = self.db.execute("""
SELECT
cv.id,
cv.distance,
c.file_path,
c.section,
c.chunk_text
FROM chunk_vecs cv
JOIN chunks c ON cv.id = c.id
WHERE embedding MATCH ?
AND k = ?
ORDER BY distance
""", [packed, limit]).fetchall()
return results
sqlite-vec中的MATCH運算子會執行approximate nearest neighbor search。k參數控制要傳回多少結果。distance欄位包含cosine distance(0=相同,2=相反)。
使用Distance Constraints進行KNN分頁
自sqlite-vec v0.1.7起,KNN查詢支援WHERE distance < ?限制,可在大型結果集上使用cursor-based pagination,而不必重新掃描先前頁面。14後續v0.1.8與v0.1.9穩定版本屬於封裝與DELETE錯誤修正版本,而非新的查詢模型版本,因此v0.1.7仍是此分頁模式的功能分界。23
def _paginated_vector_search(self, query_vec, page_size=20, max_distance=None):
"""Paginate through KNN results using distance constraints."""
packed = _serialize_vector(query_vec)
constraint = f"AND distance < {max_distance}" if max_distance else ""
results = self.db.execute(f"""
SELECT cv.id, cv.distance, c.file_path, c.chunk_text
FROM chunk_vecs cv
JOIN chunks c ON cv.id = c.id
WHERE embedding MATCH ?
AND k = ?
{constraint}
ORDER BY distance
""", [packed, page_size]).fetchall()
# Use last result's distance as cursor for next page
next_cursor = results[-1][1] if results else None
return results, next_cursor
這取代了先前先擷取大型k再於Python中切片的模式,可降低大型vault探索式查詢的記憶體使用量。
vec0表格中的DELETE支援
sqlite-vec v0.1.7為vec0 virtual tables新增原生DELETE支援,而v0.1.9修正了涉及長度超過12個字元的中繼資料文字欄位時的DELETE錯誤路徑。1423過去若要移除向量,必須刪除並重建表格。現在indexer的檔案移除路徑可以直接刪除向量:
# Before v0.1.7: required workaround (drop + recreate, or mark as inactive)
# After v0.1.7: direct DELETE works
db.execute("DELETE FROM chunk_vecs WHERE id = ?", [chunk_id])
當筆記遭刪除或移動時,這能簡化增量重新索引。indexer不再需要維護影子「active IDs」表格,也不需要批次重建。
Vector Search何時勝出
當查詢更重視概念而非特定字詞時,vector search特別出色:
- 查詢:”how to handle authentication failures” → 找到關於「login error recovery」的筆記(相同語意空間,不同關鍵字)
- 查詢:”what patterns exist for caching” → 找到關於「memoization」、「Redis TTL strategies」與「HTTP cache headers」的筆記(相關概念,多元術語)
- 查詢:”approaches to testing asynchronous code” → 找到關於「pytest-asyncio fixtures」、「mock event loops」與「async test patterns」的筆記(透過實作細節表達的相同概念)
Vector Search何時失效
vector search不擅長處理精確識別碼:
- 查詢:
_rrf_fuse→ 傳回關於「fusion algorithms」與「rank merging」的筆記,但實際函式定義的排名可能低於概念討論 - 查詢:
PostToolUse→ 傳回關於「tool lifecycle hooks」與「post-execution handlers」的筆記,而不是特定hook名稱
vector search也不擅長處理結構化資料。JSON設定檔、YAML區塊與程式碼片段產生的embeddings,捕捉的是結構模式,而非語意。包含"review": true的JSON檔案,其embedding會不同於一段討論code review的散文內容。
優雅降級
如果sqlite-vec載入失敗(缺少擴充功能、平台不相容、函式庫損毀),retriever會退回BM25-only search:
class VectorIndex:
def __init__(self, db_path):
self.db = sqlite3.connect(db_path)
self._vec_available = False
try:
self.db.enable_load_extension(True)
self.db.load_extension("vec0")
self._vec_available = True
except Exception:
pass # BM25-only mode
@property
def vec_available(self):
return self._vec_available
retriever會在嘗試vector queries之前檢查vec_available。停用時,所有搜尋只會使用BM25,並略過RRF融合步驟。
Reciprocal Rank Fusion(RRF)
RRF會合併兩個已排序清單,而不需要分數校準。本節說明演算法、完整的查詢追蹤範例、如何調整k參數,以及為何選擇RRF而非其他替代方案。若想使用可編輯排名、情境預設與視覺化架構探索器的互動式計算器,請參閱hybrid retriever深度解析。
演算法
RRF只根據每份文件在各清單中的排名位置來指派分數:
score(d) = Σ (weight_i / (k + rank_i))
其中:
- k是平滑常數(60,依循Cormack等人3)
- rank_i是文件在結果清單i中的1起始排名
- weight_i是選用的各清單乘數(預設為1.0)
在多個清單中排名良好的文件會取得較高的融合分數。只出現在單一清單中的文件,則會從該單一來源取得分數。
為何使用RRF而非替代方案
加權線性組合需要校準BM25分數與cosine distances。BM25分數沒有上限,且會隨語料庫大小縮放。Cosine distances的範圍則限定在[0, 2]。若要合併兩者,就必須正規化,而正規化參數取決於資料集。RRF只使用排名位置;無論評分方法為何,排名永遠都是從1開始的整數。
學習式融合模型需要標註訓練資料,也就是查詢與文件的相關性配對。對個人知識庫而言,這種訓練資料並不存在。您必須手動判斷數百組查詢與文件配對,才足以訓練出有用的模型。RRF不需要任何訓練資料即可運作。
Condorcet voting方法(Borda count、Schulze method)在理論上很優雅,但實作與調校更複雜。原始RRF論文證明,在TREC評估資料上,RRF優於Condorcet方法。3
實務中的融合
查詢:「how does the review aggregator handle disagreements」
BM25將review-aggregator.py排在第3位(精準命中「review」、「aggregator」、「disagreements」等關鍵字),但把兩個設定檔排得更高(它們更明顯地匹配「review」)。Vector search則將同一個chunk排在第1位(語意上匹配衝突解決)。經過RRF融合後:
| Chunk | BM25 | Vec | Fused Score |
|---|---|---|---|
| review-aggregator.py “Disagreement Resolution” | #3 | #1 | 0.0323 |
| code-review-patterns.md “Multi-Reviewer” | #4 | #2 | 0.0317 |
| deliberation-config.json “Review Weights” | #1 | — | 0.0164 |
在兩個清單中都排名良好的chunk會浮到最上方。只出現在單一清單中的chunk只會得到單一來源分數,因此會落到雙重排名結果之下。實際的分歧解決邏輯勝出,是因為兩種方法都找到了它:BM25透過關鍵字,vector search透過語意。
如需逐步追蹤每個排名的RRF數學計算,可在互動式RRF計算器中嘗試不同的k值。
實作
RRF_K = 60
def _rrf_fuse(self, bm25_results, vec_results,
bm25_weight=1.0, vec_weight=1.0):
"""Fuse BM25 and vector results using Reciprocal Rank Fusion."""
scores = {}
for rank, r in enumerate(bm25_results, start=1):
cid = r["id"]
if cid not in scores:
scores[cid] = {
"rrf_score": 0.0,
"file_path": r["file_path"],
"section": r["section"],
"chunk_text": r["chunk_text"],
"bm25_rank": None,
"vec_rank": None,
}
scores[cid]["rrf_score"] += bm25_weight / (self._rrf_k + rank)
scores[cid]["bm25_rank"] = rank
for rank, r in enumerate(vec_results, start=1):
cid = r["id"]
if cid not in scores:
scores[cid] = {
"rrf_score": 0.0,
"file_path": r["file_path"],
"section": r["section"],
"chunk_text": r["chunk_text"],
"bm25_rank": None,
"vec_rank": None,
}
scores[cid]["rrf_score"] += vec_weight / (self._rrf_k + rank)
scores[cid]["vec_rank"] = rank
fused = sorted(
scores.values(),
key=lambda x: x["rrf_score"],
reverse=True,
)
return fused
調整k
k常數控制頂端排名結果相較於較低排名結果可取得多少權重:
- 較低的k(例如10):頂端排名結果主導。排名1的分數為1/11 = 0.091,排名10的分數為1/20 = 0.050(相差1.8倍)。適合在您信任個別ranker能正確抓出頂端結果時使用。
- 預設k(60):平衡。排名1的分數為1/61 = 0.0164,排名10的分數為1/70 = 0.0143(相差1.15倍)。排名差異會被壓縮,讓「出現在多個清單中」取得更高權重。
- 較高的k(例如200):同時出現在兩個清單中,會比排名位置重要得多。排名1的分數為1/201,排名10的分數為1/210,幾乎相同。適合在個別ranker產生的排名雜訊較多,但跨清單一致性可靠時使用。
先從k=60開始。原始RRF論文發現,這個值在多樣化的TREC資料集上相當穩健。只有在衡量您自己的查詢分布中的失敗案例後,才建議進一步調整。
平手處理
當兩個chunk具有相同RRF分數時(很少見,但若它們在某個清單中排名相同,且都未出現在另一個清單中,仍可能發生),依下列方式打破平手:
- 優先選擇同時出現在兩個清單中的chunk,而非只出現在單一清單中的chunk
- 在同時出現在兩個清單中的chunk之間,優先選擇合併排名較低者
- 在只出現在單一清單中的chunk之間,優先選擇在該清單中排名較低者
完整的檢索 Pipeline
本節追蹤一筆查詢如何從輸入到輸出,完整通過整個 pipeline:BM25 搜尋、向量搜尋、RRF fusion、token 預算截斷,以及 context 組裝。
端到端流程
User query: "PostToolUse hook for context compression"
│
├─ BM25 Search (FTS5)
│ → MATCH "PostToolUse hook context compression"
│ → Top 30 results ranked by BM25 score
│ → 12ms
│
├─ Vector Search (sqlite-vec)
│ → Embed query with Model2Vec
│ → KNN k=30 on chunk_vecs
│ → Top 30 results ranked by cosine distance
│ → 8ms
│
└─ RRF Fusion
→ Merge 60 candidates (may overlap)
→ Score by rank position
→ Top 10 results
→ 3ms
│
└─ Token Budget
→ Truncate to max_tokens (default 4000)
→ Estimate at 4 chars per token
→ Return results with metadata
→ <1ms
總延遲:約 23ms,測試環境為 Apple M3 Pro 硬體上的 49,746 個 chunk 資料庫。
Search API
class HybridRetriever:
def search(self, query, limit=10, max_tokens=4000,
bm25_weight=1.0, vec_weight=1.0):
"""
Search the vault using hybrid BM25 + vector retrieval.
Args:
query: Search query text
limit: Maximum results to return
max_tokens: Token budget for total result text
bm25_weight: Weight for BM25 results in RRF
vec_weight: Weight for vector results in RRF
Returns:
List of SearchResult with file_path, section,
chunk_text, rrf_score, bm25_rank, vec_rank
"""
# BM25 search
bm25_results = self._bm25_search(query, limit=30)
# Vector search (if available)
if self.index.vec_available:
vec_results = self._vector_search(query, limit=30)
fused = self._rrf_fuse(
bm25_results, vec_results,
bm25_weight, vec_weight,
)
else:
fused = bm25_results # BM25-only fallback
# Token budget truncation
results = []
token_count = 0
for r in fused[:limit]:
chunk_tokens = len(r["chunk_text"]) // 4
if token_count + chunk_tokens > max_tokens:
break
results.append(r)
token_count += chunk_tokens
return results
Token 預算截斷
max_tokens 參數會防止 retriever 回傳超過 AI 工具可使用範圍的 context。估算方式採用每個 token 4 個字元(對英文散文而言是合理近似值)。結果會以貪婪方式截斷:依照排名順序加入結果,直到預算耗盡為止。
這是相對保守的策略。更精細的做法會考量每個結果的品質分數,並偏好較短、品質較高的結果,而不是較長、品質較低的結果。貪婪做法更簡單,實務上也運作良好,因為 RRF ranking 已經依相關性排序結果。
Database Schema(完整)
-- Chunk content and metadata
CREATE TABLE chunks (
id INTEGER PRIMARY KEY,
file_path TEXT NOT NULL,
section TEXT NOT NULL,
chunk_text TEXT NOT NULL,
heading_context TEXT DEFAULT '',
mtime_ns INTEGER NOT NULL,
embedded_at REAL NOT NULL
);
CREATE INDEX idx_chunks_file ON chunks(file_path);
CREATE INDEX idx_chunks_mtime ON chunks(mtime_ns);
-- FTS5 for BM25 search (content-synced to chunks table)
CREATE VIRTUAL TABLE chunks_fts USING fts5(
chunk_text, section, heading_context,
content=chunks, content_rowid=id
);
-- sqlite-vec for vector KNN search
CREATE VIRTUAL TABLE chunk_vecs USING vec0(
id INTEGER PRIMARY KEY,
embedding float[256]
);
-- Model metadata for compatibility tracking
CREATE TABLE model_meta (
key TEXT PRIMARY KEY,
value TEXT
);
優雅降級路徑
Full pipeline: BM25 + Vector + RRF → Best results
No sqlite-vec: BM25 only → Good results (no semantic)
No model download: BM25 only → Good results (no semantic)
No FTS5: Vector only → Decent results (no keyword)
No database: Error → Prompt user to run indexer
retriever 會在初始化時檢查能力,並調整其查詢策略。缺少某個元件會降低品質,但不會造成錯誤。唯一的硬性失敗是找不到資料庫檔案。
Production 統計
以下數據量測於包含 16,894 個檔案、49,746 個 chunks、83 MB SQLite 資料庫的 vault,硬體為 Apple M3 Pro:
| 指標 | 值 |
|---|---|
| 檔案總數 | 16,894 |
| chunk 總數 | 49,746 |
| 資料庫大小 | 83 MB |
| BM25 查詢延遲(p50) | 12ms |
| 向量查詢延遲(p50) | 8ms |
| RRF fusion 延遲 | 3ms |
| 端到端搜尋延遲(p50) | 23ms |
| 完整重新索引時間 | 約 4 分鐘 |
| 增量重新索引時間 | <10 秒 |
| Embedding 模型 | potion-base-8M (256-dim) |
| BM25 候選池 | 30 |
| 向量候選池 | 30 |
| 預設結果限制 | 10 |
| 預設 token 預算 | 4,000 tokens |
內容 Hashing 與變更偵測
indexer 需要知道哪些檔案自上次索引執行後已經變更。本節說明變更偵測機制與 hashing 策略。
檔案修改時間比較
indexer 會針對 chunks table 中的每個 chunk 儲存 mtime_ns(檔案修改時間,單位為奈秒)。在增量執行時,indexer 會:
- 掃描 vault 中允許資料夾內的所有
.md檔案 - 從檔案系統讀取每個檔案的
mtime_ns - 與資料庫中儲存的
mtime_ns比較 - 識別 3 種類別:
- 新檔案: 路徑存在於檔案系統,但不存在於資料庫
- 已變更檔案: 路徑兩邊都存在,但
mtime_ns不同 - 已刪除檔案: 路徑存在於資料庫,但不存在於檔案系統
def get_stale_files(self, vault_mtimes):
"""Find files whose mtime changed or are new."""
stored = dict(self.db.execute(
"SELECT DISTINCT file_path, mtime_ns FROM chunks"
).fetchall())
stale = []
for path, mtime in vault_mtimes.items():
if path not in stored or stored[path] != mtime:
stale.append(path)
return stale
def get_deleted_files(self, vault_paths):
"""Find files in database that no longer exist in vault."""
stored_paths = set(r[0] for r in self.db.execute(
"SELECT DISTINCT file_path FROM chunks"
).fetchall())
return stored_paths - set(vault_paths)
為什麼使用 mtime,而不是內容 Hash
內容 hashing(檔案內容的 SHA-256)會比 mtime 比較更可靠,因為它能偵測檔案被碰觸但內容未變的情況(例如 git checkout 還原原始 mtime)。然而,hashing 需要在每次增量執行時讀取每個檔案。對 16,894 個檔案而言,讀取檔案內容需要 2-3 秒;從檔案系統讀取 mtimes 則少於 100ms。
取捨在於:mtime 比較偶爾會對未變更檔案觸發不必要的重新索引(誤判為變更),但不會漏掉實際變更。誤判的成本只是每次執行多幾次 embedding 呼叫。速度差異(100ms 對 3 秒)讓 mtime 成為務實選擇,尤其是這個系統會在每次 AI 互動時執行。
處理刪除
當檔案從 vault 中刪除時,indexer 會從資料庫移除該檔案的所有 chunks:
def remove_file(self, file_path):
"""Remove all chunks and vectors for a file."""
chunk_ids = [r[0] for r in self.db.execute(
"SELECT id FROM chunks WHERE file_path = ?",
[file_path],
).fetchall()]
for cid in chunk_ids:
self.db.execute(
"DELETE FROM chunk_vecs WHERE id = ?", [cid]
)
self.db.execute(
"DELETE FROM chunks WHERE file_path = ?",
[file_path],
)
DELETE FROM chunk_vecs statement 自 sqlite-vec v0.1.7 起可原生運作,並在 v0.1.9 修正了針對具有較長 metadata 文字欄位的 vec0 tables 執行 DELETE operations 的 bug。1423較早版本需要 workaround(刪除並重新建立 virtual table,或維護外部的「active IDs」集合)。如果執行的是 0.1.9 以前的版本,請先升級,再依賴 metadata-heavy schemas 中的直接刪除。
FTS5 content-sync tables 需要針對每個被移除的 row,透過 INSERT INTO chunks_fts(chunks_fts, rowid, ...) VALUES('delete', ?, ...) 明確刪除。indexer 會在檔案移除流程中一併處理。
增量與完整重新建立索引
索引器支援兩種模式:增量(快速,日常使用)與完整(較慢,偶爾使用)。本節說明何時使用各模式、冪等性保證,以及損毀復原。
增量重新建立索引
使用時機:編輯筆記後的日常索引。這是預設模式。
執行內容: 1. 掃描 Obsidian 儲存庫中的檔案變更(mtime 比對) 2. 刪除已刪除檔案的 chunks 3. 對已變更檔案重新 chunk 並重新 embed 4. 為新檔案插入新的 chunks 5. 同步 FTS5 索引
典型耗時:在含 16,000 個檔案的 Obsidian 儲存庫中,處理一天的編輯通常少於 10 秒。
python index_vault.py --incremental
完整重新建立索引
使用時機: - 變更 embedding model 後(偵測到 model hash 不相符) - schema migration 後(新增欄位、變更索引) - database corruption 後(integrity check 失敗) - 增量索引產生非預期結果時
執行內容: 1. 刪除所有既有資料(chunks、vectors、FTS5 entries) 2. 掃描整個 Obsidian 儲存庫 3. 對所有檔案進行 chunk 4. 對所有 chunks 進行 embed 5. 從零開始建立 FTS5 索引
典型耗時:在 Apple M3 Pro 上處理 16,894 個檔案約需 4 分鐘。
python index_vault.py --full
冪等性
兩種模式都具備冪等性:同一個指令執行兩次會產生相同結果。索引器會先刪除某個檔案的既有 chunks,再插入新的 chunks。因此,在已是最新狀態的資料庫上重新執行增量索引,會產生 0 項變更。重新執行完整索引則會產生相同的資料庫。
損毀復原
如果 SQLite 資料庫損毀(寫入期間斷電、磁碟錯誤、交易中途行程被終止):
# Check integrity
sqlite3 vectors.db "PRAGMA integrity_check;"
# If corruption detected, full reindex rebuilds from source files
python index_vault.py --full
真實來源永遠是 Obsidian 儲存庫中的檔案,而不是資料庫。資料庫是衍生產物,可隨時重建。這是一項關鍵設計特性:您永遠不需要備份資料庫。
--incremental 旗標
索引器以 --incremental 執行時:
- Model hash 檢查。將儲存的 model hash 與目前 model 比對。若不同,會自動切換到完整重新建立索引模式,並警告使用者。
- 檔案掃描。走訪允許的資料夾,收集檔案路徑與 mtimes。
- 變更偵測。與已儲存資料比對。
- 批次處理。以每批 64 個檔案重新 chunk 並重新 embed 已變更檔案。
- 進度回報。印出已處理檔案數與經過時間。
- 優雅關閉。處理 SIGINT 時,會先完成目前檔案再停止。
憑證過濾與資料邊界
個人筆記會包含祕密資訊:API keys、bearer tokens、資料庫連線字串、除錯期間貼上的私密金鑰。憑證過濾器會防止這些內容進入 retrieval 索引。
問題
一則關於除錯 OAuth 整合的筆記可能包含:
The token was: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
I used this curl command:
curl -H "Authorization: Bearer sk-ant-api03-abc123..."
若未過濾,JWT 和 API key 都會被 chunk、embed,並儲存在資料庫中。搜尋「authentication」會傳回含有真實祕密資訊的 chunk。更糟的是,如果 retriever 透過 MCP 將結果送入 AI 工具,這些祕密資訊會出現在 AI 的 context window 中,甚至可能進入工具記錄。
以模式為基礎的過濾
憑證過濾器會在每個 chunk 儲存前執行,比對 25 種供應商特定模式加上通用模式:
供應商特定模式:
| Pattern | Example | Regex |
|---|---|---|
| OpenAI API key | sk-... |
sk-[a-zA-Z0-9_-]{20,} |
| Anthropic API key | sk-ant-api03-... |
sk-ant-api\d{2}-[a-zA-Z0-9_-]{20,} |
| GitHub PAT | ghp_... |
gh[ps]_[a-zA-Z0-9]{36,} |
| AWS Access Key | AKIA... |
AKIA[0-9A-Z]{16} |
| Stripe key | sk_live_... |
[sr]k_(live\|test)_[a-zA-Z0-9]{24,} |
| Cloudflare token | ... |
各種模式 |
通用模式:
| Pattern | Detection |
|---|---|
| JWT tokens | eyJ[a-zA-Z0-9_-]+\.eyJ[a-zA-Z0-9_-]+ |
| Bearer tokens | Bearer\s+[a-zA-Z0-9_\-\.]+ |
| Private keys | -----BEGIN (RSA\|EC\|OPENSSH) PRIVATE KEY----- |
| High-entropy base64 | 熵值 >4.5 bits/char、40+ 字元的字串 |
| Password assignments | password\s*[:=]\s*["'][^"']+["'] |
過濾器實作
def clean_content(text):
"""Scrub credentials from text before indexing."""
result = ScanResult(is_clean=True, match_count=0, patterns=[])
for pattern in CREDENTIAL_PATTERNS:
matches = pattern.regex.findall(text)
if matches:
text = pattern.regex.sub(
f"[REDACTED:{pattern.name}]", text
)
result.is_clean = False
result.match_count += len(matches)
result.patterns.append(pattern.name)
return text, result
關鍵設計選擇:
-
先過濾,再 embedding。被 embed 的是清理後文字。vector representation 永遠不會編碼憑證模式。查詢「API key」會傳回討論 API key 管理的筆記,而不是包含實際金鑰的筆記。
-
取代,而非移除。
[REDACTED:pattern-name]token 保留周圍文字的語意脈絡。embedding 會捕捉「這裡曾有類似憑證的內容」,但不會編碼憑證本身。 -
記錄模式,不記錄值。過濾器只記錄命中的模式(例如「Scrubbed 2 credential(s) from oauth-debug.md [jwt, bearer-token]」),絕不記錄憑證值。
以路徑為基礎的排除
.indexignore 檔案提供以路徑為單位的粗粒度排除。憑證過濾器則在已編入索引的檔案內進行細粒度清理。兩者缺一不可:
.indexignore用於您已知含有敏感內容的整個資料夾(健康筆記、財務紀錄、職涯文件)- 憑證過濾器用於意外嵌入在原本可索引內容中的祕密資訊
資料分類
對於內容多元的 Obsidian 儲存庫,建議依敏感程度分類筆記:
| Level | Examples | Index? | Filter? |
|---|---|---|---|
| Public | 部落格草稿、技術筆記 | 是 | 是 |
| Internal | 專案計畫、架構決策 | 是 | 是 |
| Sensitive | 薪資資料、健康紀錄 | 否(.indexignore) | N/A |
| Restricted | 憑證、私密金鑰 | 否(.indexignore) | N/A |
MCP伺服器架構
Model Context Protocol(MCP)伺服器會將檢索器公開為AI代理程式可呼叫的工具。本節說明伺服器設計、功能範圍與權限邊界。
協定選擇:STDIO與HTTP
MCP支援2種傳輸模式:
STDIO——AI工具會將MCP伺服器作為子行程啟動,並透過stdin/stdout通訊。這是本機工具的標準模式。Claude Code、Codex CLI與Cursor都支援STDIO MCP伺服器。
{
"mcpServers": {
"obsidian": {
"command": "python",
"args": ["/path/to/obsidian_mcp.py"],
"env": {
"VAULT_PATH": "/path/to/vault",
"DB_PATH": "/path/to/vectors.db"
}
}
}
}
HTTP——MCP伺服器會以獨立HTTP服務執行。適合遠端存取、多用戶端設定,或知識庫位於共用伺服器上的團隊組態。
{
"mcpServers": {
"obsidian": {
"url": "http://localhost:3333/mcp"
}
}
}
建議:個人知識庫請使用STDIO。它更簡單、更安全(不暴露於網路),而且伺服器生命週期由AI工具管理。只有在多個工具或多台機器需要同時存取同一個知識庫時,才使用HTTP。
MCP規格演進。2025年6月的MCP規格新增了OAuth 2.1授權、結構化工具輸出(型別化回傳schema),以及elicitation(由伺服器主動發起的使用者提示)。2025年11月版本將Streamable HTTP列為第一級傳輸模式,加入用於自動瀏覽伺服器功能的
.well-knownURL探索、可宣告工具為唯讀或會變更狀態的結構化工具註解,以及SDK分級標準化系統。79下一個規格版本(暫定2026年中)提議加入適用於長時間執行工作的非同步作業、醫療保健與金融等產業的領域專用協定擴充,以及多代理程式工作流程所需的代理程式對代理程式通訊標準。9對個人知識庫伺服器而言,STDIO仍是最簡單的路徑。Streamable HTTP傳輸與.well-known探索主要有利於具備多租戶路由與負載平衡的企業HTTP部署。請留意MCP路線圖,掌握會影響傳輸選擇的更新。
功能設計
MCP伺服器應公開最小化的工具集:
search——主要工具。執行hybrid檢索並回傳排序後的結果。
{
"name": "obsidian_search",
"description": "Search the Obsidian vault using hybrid BM25 + vector retrieval",
"parameters": {
"query": { "type": "string", "description": "Search query" },
"limit": { "type": "integer", "default": 5 },
"max_tokens": { "type": "integer", "default": 2000 }
}
}
read_note——依路徑讀取特定筆記的完整內容。當代理程式想查看搜尋結果的完整脈絡時很有用。
{
"name": "obsidian_read_note",
"description": "Read the full content of a note by file path",
"parameters": {
"file_path": { "type": "string", "description": "Relative path within vault" }
}
}
list_notes——列出符合篩選條件的筆記(依資料夾、標籤、類型或日期範圍)。當代理程式沒有特定查詢、需要探索時很有用。
{
"name": "obsidian_list_notes",
"description": "List notes matching filters",
"parameters": {
"folder": { "type": "string", "description": "Folder path within vault" },
"tag": { "type": "string", "description": "Tag to filter by" },
"limit": { "type": "integer", "default": 20 }
}
}
get_context——便利工具,會執行搜尋,並將結果格式化為適合注入對話的脈絡區塊。
{
"name": "obsidian_get_context",
"description": "Get formatted context from vault for a topic",
"parameters": {
"topic": { "type": "string", "description": "Topic to get context for" },
"max_tokens": { "type": "integer", "default": 2000 }
}
}
權限邊界
MCP伺服器應強制執行嚴格邊界:
-
唯讀。伺服器會讀取知識庫與索引資料庫。它不會建立、修改或刪除筆記。寫入操作(擷取新筆記)由獨立的hook或skill處理,而不是由MCP伺服器處理。
-
限定知識庫範圍。伺服器只會讀取設定知識庫路徑內的檔案。必須拒絕路徑遍歷嘗試(
../../etc/passwd)。 -
輸出進行憑證過濾。即使資料庫包含已預先過濾的內容,也要在輸出時套用憑證過濾,作為縱深防禦措施。
-
回應限制token數。對所有工具回應強制執行
max_tokens,避免AI工具收到過大的脈絡區塊。
錯誤處理
MCP工具應回傳結構化錯誤訊息,協助AI工具復原:
def search(self, query, limit=5, max_tokens=2000):
if not self.db_path.exists():
return {
"error": "Index database not found. Run the indexer first.",
"suggestion": "python index_vault.py --full"
}
results = self.retriever.search(query, limit, max_tokens)
if not results:
return {
"results": [],
"message": f"No results found for '{query}'. Try broader terms."
}
return {
"results": [
{
"file_path": r["file_path"],
"section": r["section"],
"text": r["chunk_text"],
"score": round(r["rrf_score"], 4),
}
for r in results
],
"count": len(results),
"query": query,
}
Claude Code整合
Claude Code是 Obsidian 檢索系統的主要使用者。本節涵蓋MCP設定、Hook整合,以及obsidian_bridge.py模式。
MCP設定
將 Obsidian MCP伺服器加入~/.claude/settings.json:
{
"mcpServers": {
"obsidian": {
"command": "python",
"args": ["/path/to/obsidian_mcp.py"],
"env": {
"VAULT_PATH": "/absolute/path/to/vault",
"DB_PATH": "/absolute/path/to/vectors.db"
}
}
}
}
加入設定後,重新啟動Claude Code。MCP伺服器會以子程序啟動。請確認它正在執行:
> What tools do you have from the obsidian MCP server?
Claude Code應列出可用工具(obsidian_search、obsidian_read_note等)。
Hook整合
Hook會在定義好的生命週期節點擴充Claude Code的行為。與 Obsidian 整合相關的 Hook 有2個:
PreToolUse hook——在 agent 處理工具呼叫前查詢 vault。自動注入相關上下文。
#!/bin/bash
# ~/.claude/hooks/pre-tool-use/obsidian-context.sh
# Automatically inject vault context before tool execution
TOOL_NAME="$1"
PROMPT="$2"
# Only inject context for code-related tools
case "$TOOL_NAME" in
Edit|Write|Bash)
# Query the vault
CONTEXT=$(python /path/to/retriever.py search "$PROMPT" --limit 3 --max-tokens 1500)
if [ -n "$CONTEXT" ]; then
echo "---"
echo "Relevant vault context:"
echo "$CONTEXT"
echo "---"
fi
;;
esac
PostToolUse hook——將重要的工具輸出擷取回 vault,供未來檢索使用。
#!/bin/bash
# ~/.claude/hooks/post-tool-use/capture-insight.sh
# Capture significant outputs to vault (selective)
TOOL_NAME="$1"
OUTPUT="$2"
# Only capture substantial outputs
if [ ${#OUTPUT} -gt 500 ]; then
python /path/to/capture.py --text "$OUTPUT" --source "claude-code-$TOOL_NAME"
fi
obsidian_bridge.py模式
橋接模組會提供 Hook 與 skill 可呼叫的PythonAPI:
# obsidian_bridge.py
from retriever import HybridRetriever
_retriever = None
def get_retriever():
global _retriever
if _retriever is None:
_retriever = HybridRetriever(
db_path="/path/to/vectors.db",
vault_path="/path/to/vault",
)
return _retriever
def search_vault(query, limit=5, max_tokens=2000):
"""Search vault and return formatted context."""
retriever = get_retriever()
results = retriever.search(query, limit, max_tokens)
if not results:
return ""
lines = ["## Vault Context\n"]
for r in results:
lines.append(f"**{r['file_path']}** — {r['section']}")
lines.append(f"> {r['chunk_text'][:500]}")
lines.append("")
return "\n".join(lines)
/capture Skill
用於將洞察擷取回 vault 的Claude Code skill:
/capture "OAuth token rotation requires both access and refresh token invalidation"
--domain security
--tags oauth,tokens
此 skill 會在00-inbox/建立新筆記,包含正確的 frontmatter,並觸發增量重新索引,讓新筆記立即可被搜尋。
自訂指令模式
Claude Code skill 可將 vault 操作包裝成具名指令。實務工作者已建立多組 Obsidian 專用指令庫,將 vault 同時視為讀取來源與寫入目標。
訊號掃描。/scan-intel指令會查詢外部來源,根據個人研究興趣為發現項目評分,並將符合條件的訊號寫成含有 frontmatter 的 vault 筆記:
/scan-intel --topics "agent infrastructure, security" --lookback 7d
此指令會從設定好的來源(arXiv、HN、RSS)擷取內容,套用評分模型(相關性、可行性、深度、權威性),並將通過門檻的訊號寫入特定主題的 vault 資料夾。vault 因而成為自動化情報流程的下游消費者。
船長日誌。/captains-log指令會彙整所有儲存庫的每日 git 活動,將結構化日誌項目寫入 vault,並包含已做出的決策、領悟,以及尚未收束的線索:
/captains-log
此指令會從GitHub拉取 commit 歷史,依儲存庫分組,並格式化為敘事型日誌。日積月累後,每日日誌會形成一份可搜尋的紀錄,說明交付了什麼以及為什麼。
Obsidian 擷取。/obsidian-capture指令會從目前的Claude Code session 取得一項洞察,並以正確 metadata 直接寫入 vault:
/obsidian-capture "SAST gates in agent loops increase security degradation"
--folder AI-Tools --tags security,agents
此模式可延伸到任何 vault 操作:建立 MOC、更新專案狀態筆記、連結相關訊號,或從累積的每日日誌產生每週摘要。
社群範例。實務工作者正在發布自己的指令庫。一位開發者分享了22個搭配 Obsidian 與Claude Code使用的自訂指令,涵蓋每日回顧、專案規劃、研究擷取與內容工作流程。1另一位開發者則建立了「Visual Explainer」skill,可根據程式碼分析在 vault 中產生圖解筆記。2各指令形式不盡相同,但架構一致:以Claude Code skill 作為介面,以 vault 筆記作為儲存層,並以檢索基礎設施作為查詢引擎。
上下文視窗管理
整合時應留意Claude Code的上下文視窗:
- 將每次查詢注入的上下文限制在1,500-2,000 tokens。超過此範圍會與 agent 的工作記憶競爭。
- 包含來源歸屬。務必包含檔案路徑與章節標題,讓 agent 能引用來源。
- 截斷 chunk 文字。較長的 chunk 應以
...截斷,而不是整段省略。前300-500個字元通常包含關鍵資訊。 - 不要在每次工具呼叫都注入。PreToolUse hook 應根據被呼叫的工具選擇性注入上下文。讀取操作不需要 vault 上下文;Write 與 Edit 操作則會受益於此。
Codex CLI整合
Codex CLI會透過config.toml連接到MCP伺服器。此整合模式在設定語法與指令傳遞上,與Claude Code不同。
MCP設定
加入.codex/config.toml或~/.codex/config.toml:
[mcp_servers.obsidian]
command = "python"
args = ["/path/to/obsidian_mcp.py"]
[mcp_servers.obsidian.env]
VAULT_PATH = "/absolute/path/to/vault"
DB_PATH = "/absolute/path/to/vectors.db"
AGENTS.md模式
Codex CLI會讀取AGENTS.md作為專案層級指令。請加入 vault 搜尋指引:
## Available Tools
### Obsidian Vault (MCP: obsidian)
Use the `obsidian_search` tool to find relevant context from the knowledge base.
Search the vault when you need:
- Background on a concept or pattern
- Prior decisions or rationale
- Reference material for implementation
Example queries:
- "authentication patterns in FastAPI"
- "how does the review aggregator work"
- "sqlite-vec configuration"
與Claude Code的差異
| 功能 | Claude Code | Codex CLI |
|---|---|---|
| MCP設定 | settings.json |
config.toml |
| Hooks | ~/.claude/hooks/ |
不支援 |
| Skills | ~/.claude/skills/ |
不支援 |
| 指令檔案 | CLAUDE.md |
AGENTS.md |
| 核准模式 | --dangerously-skip-permissions |
suggest / auto-edit / full-auto |
關鍵差異:Codex CLI不支援 Hook。因此無法使用自動上下文注入模式(PreToolUse hook)。請改為在AGENTS.md中加入明確指令,要求 agent 在開始工作前先搜尋 vault。
Cursor與其他工具
支援MCP的Cursor與其他AI工具,都可以連接到同一個Obsidian MCP伺服器。本節說明常見工具的設定方式。
Cursor
將下列內容新增至專案根目錄中的.cursor/mcp.json:
{
"mcpServers": {
"obsidian": {
"command": "python",
"args": ["/path/to/obsidian_mcp.py"],
"env": {
"VAULT_PATH": "/absolute/path/to/vault",
"DB_PATH": "/absolute/path/to/vectors.db"
}
}
}
}
Cursor的.cursorrules檔案可以包含使用Obsidian保存庫的指示:
When working on implementation tasks, search the Obsidian vault
for relevant context before writing code. Use the obsidian_search
tool with descriptive queries about the concept you're implementing.
相容性矩陣
| 工具 | MCP支援 | 傳輸 | 設定位置 |
|---|---|---|---|
| Claude Code | 完整 | STDIO | ~/.claude/settings.json |
| Codex CLI | 完整 | STDIO | .codex/config.toml |
| Cursor | 完整 | STDIO | .cursor/mcp.json |
| Windsurf | 完整 | STDIO | .windsurf/mcp.json |
| Continue.dev | 部分 | HTTP | ~/.continue/config.json |
| Zed | 開發中 | STDIO | Settings UI |
| Claudian(Obsidian plugin) | 不適用(內嵌) | Claude Code CLI | Obsidian plugin設定 |
| Agent Client(Obsidian plugin) | 不適用(內嵌) | ACP | Obsidian plugin設定 |
非MCP工具的備援方案
對於不支援MCP的工具,可以將retriever包裝成CLI:
# Search from command line
python retriever_cli.py search "query text" --limit 5
# Output formatted for copy-paste into any tool
python retriever_cli.py context "query text" --format markdown
CLI會輸出結構化文字,可手動貼到任何AI工具的輸入中。這不如MCP整合優雅,但通用性最高。
從結構化筆記進行Prompt Caching
保存庫中的結構化筆記可以作為可重複使用的脈絡區塊,降低AI互動中的token用量。本節說明cache key設計與token budget管理。
模式
與其在每次互動時都搜尋脈絡,不如先從結構良好的保存庫筆記預先建立脈絡區塊,並加以快取:
# cache_keys.py
CONTEXT_BLOCKS = {
"auth-patterns": {
"vault_query": "authentication patterns implementation",
"max_tokens": 1500,
"ttl_hours": 24, # Rebuild daily
},
"api-conventions": {
"vault_query": "API design conventions REST patterns",
"max_tokens": 1000,
"ttl_hours": 168, # Rebuild weekly
},
"project-architecture": {
"vault_query": "current project architecture decisions",
"max_tokens": 2000,
"ttl_hours": 12, # Rebuild twice daily
},
}
Cache Invalidation
Cache invalidation基於兩項訊號:
- TTL到期。 每個脈絡區塊都有存活時間。TTL到期時,系統會重新查詢保存庫並重建該區塊。
- 保存庫變更偵測。 當indexer偵測到曾貢獻於某個快取脈絡區塊的檔案發生變更時,該區塊會立即失效。
Token Budget管理
每個session一開始都有總context budget。快取區塊會消耗其中一部分:
Total context budget: 8,000 tokens
├─ System prompt: 1,500 tokens
├─ Cached blocks: 3,000 tokens (pre-loaded)
├─ Dynamic search: 2,000 tokens (on-demand)
└─ Conversation: 1,500 tokens (remaining)
快取區塊會在session開始時載入。動態搜尋結果則依每次查詢填補剩餘budget。這種hybrid方法讓agent擁有常用脈絡的基準,同時保留budget給特定查詢使用。
Token用量前後比較
未使用快取: 每個相關查詢都會觸發一次保存庫搜尋,回傳1,500-2,000個token的脈絡。在一個session的10次查詢中,agent會消耗15,000-20,000個token的保存庫脈絡。
使用快取: 3個預先建立的脈絡區塊共消耗4,500個token。其他搜尋則會為每個唯一查詢新增1,500-2,000個token。若10次查詢中有6次可由快取區塊涵蓋,agent會消耗4,500 + (4 * 1,500) = 10,500個token,約為未快取用量的一半。
用於Context Compression的PostToolUse Hooks
工具輸出可能很冗長:stack traces、檔案清單、測試結果。PostToolUse hook可以在這些輸出佔用context window空間前先行壓縮。
問題
執行測試的Bash工具呼叫可能會回傳:
PASSED tests/test_auth.py::test_login_success
PASSED tests/test_auth.py::test_login_failure
PASSED tests/test_auth.py::test_token_refresh
PASSED tests/test_auth.py::test_session_expiry
... (200 more lines)
FAILED tests/test_api.py::test_rate_limit_exceeded
完整輸出有5,000個token,但真正的訊號只有2行:200 passed、1 failed。
Hook實作
#!/bin/bash
# ~/.claude/hooks/post-tool-use/compress-output.sh
# Compress verbose tool outputs to preserve context window
TOOL_NAME="$1"
OUTPUT="$2"
OUTPUT_LEN=${#OUTPUT}
# Only compress large outputs
if [ "$OUTPUT_LEN" -lt 2000 ]; then
exit 0 # Pass through unchanged
fi
case "$TOOL_NAME" in
Bash)
# Compress test output
if echo "$OUTPUT" | grep -q "PASSED\|FAILED"; then
PASSED=$(echo "$OUTPUT" | grep -c "PASSED")
FAILED=$(echo "$OUTPUT" | grep -c "FAILED")
FAILURES=$(echo "$OUTPUT" | grep "FAILED")
echo "Tests: $PASSED passed, $FAILED failed"
if [ "$FAILED" -gt 0 ]; then
echo "Failures:"
echo "$FAILURES"
fi
fi
;;
esac
防止遞迴觸發
若未加防護,會輸出內容的壓縮hook可能觸發自身:
# Guard against recursive invocation
if [ -n "$COMPRESS_HOOK_ACTIVE" ]; then
exit 0
fi
export COMPRESS_HOOK_ACTIVE=1
壓縮啟發式規則
| 輸出類型 | 偵測 | 壓縮策略 |
|---|---|---|
| 測試結果 | PASSED / FAILED關鍵字 |
計算通過/失敗,只顯示失敗項目 |
| 檔案清單 | command中有ls或find |
截斷為前20個項目+總數 |
| Stack traces | Traceback關鍵字 |
保留第一個與最後一個frame+錯誤訊息 |
| Git status | modified: / new file: |
依狀態彙總數量 |
| Build output | warning: / error: |
移除info行,保留warnings/errors |
訊號匯入與分流管線
匯入層決定哪些內容能進入知識庫(Obsidian的vault)。若缺乏篩選,知識庫會逐漸堆積雜訊。本節說明將訊號分流到領域資料夾的評分管線。
來源
訊號來自多個管道:
- RSS feeds:技術部落格、安全公告、發行說明
- 透過Web Clipper加入書籤:官方Obsidian Web Clipper擴充功能(Chrome、Firefox、Safari)是瀏覽器端擷取中保真度最高的匯入路徑。2026年4月的發行週期,讓它對AI工作流程的實用性大幅提升:22
- 1.4.0(4月9日):互動式YouTube逐字稿UI——固定影片、拖曳瀏覽逐字稿、自動捲動,並醒目顯示目前位置。另外也提供「Open in Reader」預設選項,可一鍵擷取並直接送入Reader模式。
- 1.5.0–1.5.1(4月15日):重點檢視器——可在整個知識庫中瀏覽並搜尋已擷取的重點。進入Reader時加入淡入轉場。YouTube播放/暫停更順暢。1.5.1修復了webpack編譯退化問題。
- 1.6.0–1.6.2(4月21–23日):Highlighter UX全面翻新並支援行動裝置。Defuddle 0.18為LinkedIn、Threads、Bluesky、Discourse與Medium加入特定來源的擷取器。1.6.2修復了Safari嵌入模式的剪貼簿退化問題。 請依來源網域設定範本,讓YouTube逐字稿、GitHub README與長文文章都能落在命名合理的筆記中,並帶有下方評分管線需要的正確frontmatter。
- 電子報:來自電子郵件電子報的關鍵摘錄
- 手動擷取:閱讀、對話或研究期間寫下的筆記
- 工具輸出:透過hooks擷取的重要AI工具輸出
- iOS Share Extension:Obsidian的iOS app(2026年初更新)包含Share Extension,可直接從Safari、社群網路與其他app將內容儲存到知識庫,不必開啟Obsidian。19這建立了一條低摩擦的行動匯入路徑——從Safari分享一篇文章後,它會以知識庫筆記的形式出現,準備進入評分流程。
- Obsidian CLI:Shell scripts與hooks可透過
obsidian file create建立筆記,或透過obsidian file append附加到既有筆記,進而在桌面端啟用自動化匯入管線。
評分維度
每個訊號會依4個維度評分(各自為0.0到1.0):
| 維度 | 問題 | 低分(0.0-0.3) | 高分(0.7-1.0) |
|---|---|---|---|
| 相關性 | 這是否與我的活躍領域相關? | 關係薄弱、超出範圍 | 與目前工作直接相關 |
| 可行動性 | 我能否使用這項資訊? | 純理論、無應用方式 | 可套用的具體技巧或模式 |
| 深度 | 內容是否扎實? | 標題式內容、淺層摘要 | 含範例的詳細分析 |
| 權威性 | 來源可信度如何? | 匿名部落格、未驗證 | 主要來源、同儕審查、受認可的專家 |
綜合分數與分流
composite = (relevance * 0.35) + (actionability * 0.25) +
(depth * 0.25) + (authority * 0.15)
| 分數範圍 | 動作 |
|---|---|
| 0.55+ | 自動分流到領域資料夾 |
| 0.40 - 0.55 | 排入手動審查佇列 |
| < 0.40 | 捨棄(不儲存) |
領域分流
分數高於0.55的訊號,會根據關鍵字比對與主題分類,分流到12個領域資料夾之一:
05-signals/
├── ai-tooling/ # Claude, LLMs, AI development tools
├── security/ # Vulnerabilities, auth, cryptography
├── systems/ # Architecture, distributed systems
├── programming/ # Languages, patterns, algorithms
├── web/ # Frontend, backends, APIs
├── data/ # Databases, data engineering
├── devops/ # CI/CD, containers, infrastructure
├── design/ # UI/UX, product design
├── mobile/ # iOS, Android, cross-platform
├── career/ # Industry trends, hiring, growth
├── research/ # Academic papers, whitepapers
└── other/ # Signals that don't fit a domain
Production統計
經過14個月運作:
| 指標 | 數值 |
|---|---|
| 已處理訊號總數 | 7,771 |
| 自動分流(>0.55) | 4,832(62%) |
| 排入審查(0.40-0.55) | 1,543(20%) |
| 捨棄(<0.40) | 1,396(18%) |
| 活躍領域資料夾 | 12 |
| 每日平均訊號數 | ~18 |
知識圖譜模式
Obsidian的wiki-link圖譜會編碼筆記之間的關係。本節說明連結語意、用於脈絡擴展的圖譜遍歷,以及會降低圖譜品質的反模式。
Backlink語意
每個wiki-link都會在圖譜中建立一條有向邊。Obsidian會追蹤正向連結與backlinks:
- 正向連結:筆記A包含
[[Note B]]→ A連結到B - Backlink:筆記B顯示筆記A引用了它
圖譜會依脈絡編碼不同類型的關係:
| 連結模式 | 語意 | 範例 |
|---|---|---|
| 行內連結 | 「與其相關」 | 「詳情請見[[OAuth Token Rotation]]」 |
| 標題連結 | 「具有子主題」 | ”## Related\n- [[Token Rotation]]\n- [[Session Management]]” |
| 類標籤連結 | 「被分類為」 | ”[[type/reference]]” |
| MOC連結 | 「屬於」 | 列出相關筆記的Map of Content筆記 |
Maps of Content(MOCs)
MOCs是索引筆記,會將相關筆記整理成可導覽的結構:
---
title: "Authentication & Security MOC"
type: moc
domain: security
---
## Core Concepts
- [[OAuth 2.0 Overview]]
- [[JWT Token Anatomy]]
- [[Session Management Patterns]]
## Implementation Patterns
- [[OAuth Token Rotation]]
- [[Refresh Token Security]]
- [[PKCE Flow Implementation]]
## Failure Modes
- [[Token Expiry Handling]]
- [[Session Fixation Prevention]]
- [[CSRF Defense Strategies]]
MOCs會以兩種方式提升檢索效果:
- 直接匹配。搜尋「authentication overview」會匹配到MOC本身,讓agent取得一份經過整理的相關筆記清單。
- 脈絡擴展。找到特定筆記後,retriever可以檢查該筆記是否出現在任何MOCs中,並將MOC的結構納入結果,為agent提供更大主題的地圖。
用於脈絡擴展的圖譜遍歷
retriever未來可強化的一項功能:找到排名最高的結果後,沿著連結擴展脈絡:
def expand_context(results, depth=1):
"""Follow wiki-links from top results to find related context."""
expanded = set()
for result in results:
# Parse wiki-links from chunk text
links = extract_wiki_links(result["chunk_text"])
for link_target in links:
# Resolve link to file path
target_path = resolve_wiki_link(link_target)
if target_path and target_path not in expanded:
expanded.add(target_path)
# Include target's most relevant chunk
target_chunks = get_chunks_for_file(target_path)
# ... rank and include best chunk
return results + list(expanded_results)
這尚未在目前的retriever中實作,但它是圖譜結構的自然延伸。
反模式
孤立叢集。一組筆記彼此互連,卻與知識庫其他部分沒有連結。Obsidian的圖譜面板會把這些顯示為彼此斷開的孤島。孤立叢集表示缺少MOCs,或缺少跨領域連結。
標籤蔓生。標籤使用不一致,或建立過多細粒度標籤。若一個知識庫在5,000則筆記中有500個不重複標籤,平均每10個標籤只對應1則筆記——這些標籤對篩選毫無助益。請整併為20-50個高層級標籤,並對應到您的領域資料夾。
連結很多、內容很少的筆記。筆記完全由wiki-links組成,沒有任何散文說明。這類筆記索引效果不佳,因為chunker沒有可嵌入的文字。至少加入一段脈絡,說明這些被連結的筆記為何相關。
凡事都做雙向連結。並非每一次引用都需要做成wiki-link。順帶提到「OAuth」不代表需要加入[[OAuth 2.0 Overview]]。請將wiki-links保留給刻意建立、可導覽的關係,也就是點擊連結能提供有用脈絡的情境。
開發者工作流程範例
結合筆記庫檢索與日常開發工作的實用流程。
早晨載入脈絡
一天開始時,先載入相關脈絡:
Search my vault for notes about [current project] updated in the last week
檢索器會回傳與目前專案相關的近期筆記,讓您快速回想上次進度。這比重新閱讀昨天的 commit 訊息更有效。
寫程式時捕捉研究內容
實作功能時,不離開編輯器也能記錄洞察:
/capture "FastAPI dependency injection with async generators requires yield,
not return. The generator is the dependency lifecycle."
--domain programming
--tags fastapi,dependency-injection
捕捉到的洞察會立即建立索引,日後可供檢索。數月下來,這些微型記錄會累積成一套與實作高度相關的知識語料庫。
專案啟動
開始新專案或新功能時:
- 搜尋筆記庫:「我對 [technology/pattern] 已經知道什麼?」
- 檢視前 5 筆結果,了解過去的決策與注意事項
- 確認該領域是否已有 MOC;如果沒有,就建立一個
- 搜尋失敗模式:「[technology] 的問題」
使用筆記庫搜尋進行除錯
遇到錯誤或非預期行為時:
Search my vault for [error message or symptom]
過去的除錯筆記通常包含根本原因與修正方式。這對跨專案反覆出現的問題特別有價值——筆記庫會記得您忘記的事。
Code Review 準備
Review PR 之前:
Search my vault for patterns and conventions about [module being changed]
筆記庫會回傳與待 review 程式碼相關的過去決策、架構限制與程式碼標準。這樣的 review 依據的是組織知識,而不只是 diff。
效能調校
本節涵蓋不同筆記庫規模與使用模式下的最佳化策略。
索引大小管理
| 筆記庫大小 | 分塊 | DB 大小 | 完整重新索引 | 增量索引 |
|---|---|---|---|---|
| 500 篇筆記 | ~1,500 | 3 MB | 15 秒 | <1 秒 |
| 2,000 篇筆記 | ~6,000 | 12 MB | 45 秒 | 2 秒 |
| 5,000 篇筆記 | ~15,000 | 30 MB | 2 分鐘 | 4 秒 |
| 15,000 篇筆記 | ~50,000 | 83 MB | 4 分鐘 | <10 秒 |
| 50,000 篇筆記 | ~150,000 | 250 MB | 15 分鐘 | 30 秒 |
達到 50,000 篇以上筆記時,建議考慮: - 將 batch size 從 64 提高到 128,以加快 embedding - 使用 WAL mode(預設)支援並行存取 - 在離峰時段執行完整重新索引
查詢最佳化
WAL mode。 SQLite 的 Write-Ahead Logging mode 可讓 indexer 寫入時仍能並行讀取:
db.execute("PRAGMA journal_mode=WAL")
當 MCP server 在 indexer 執行增量更新時同時處理查詢,這點至關重要。
連線池。 MCP server 應重複使用資料庫連線,而不是每次查詢都開啟新連線。搭配 WAL mode 的單一長期連線即可支援並行讀取。
# MCP server initialization
db = sqlite3.connect(DB_PATH, check_same_thread=False)
db.execute("PRAGMA journal_mode=WAL")
db.execute("PRAGMA mmap_size=268435456") # 256 MB mmap
記憶體映射 I/O。 mmap_size pragma 會指示 SQLite 對資料庫檔案使用記憶體映射 I/O。對 83 MB 的資料庫而言,將整個檔案映射進記憶體可消除大多數磁碟讀取。
FTS5 最佳化。 完整重新索引後,執行:
INSERT INTO chunks_fts(chunks_fts) VALUES('optimize');
這會合併 FTS5 內部的 b-tree segments,降低後續搜尋的查詢延遲。
擴展基準測試
測試環境為 Apple M3 Pro、36 GB RAM、NVMe SSD:
| 操作 | 500 篇筆記 | 5K 篇筆記 | 15K 篇筆記 | 50K 篇筆記 |
|---|---|---|---|---|
| BM25 query | 2ms | 5ms | 12ms | 25ms |
| Vector query | 1ms | 3ms | 8ms | 20ms |
| RRF fusion | <1ms | <1ms | 3ms | 5ms |
| Full search | 3ms | 8ms | 23ms | 50ms |
所有基準測試都包含資料庫存取、查詢執行與結果格式化。MCP STDIO 通訊的網路延遲會額外增加 1-2ms。
疑難排解
索引漂移
症狀: 搜尋回傳過期結果,或漏掉最近新增的筆記。
原因: 新增筆記後沒有執行增量 indexer,或檔案的 mtime 未更新(例如從另一台機器同步時保留了時間戳記)。
修正: 執行完整重新索引:python index_vault.py --full
更換 Embedding 模型
症狀: 更換 embedding 模型後,向量搜尋回傳不合理的結果。
原因: 舊向量(來自先前模型)正在與新的查詢向量比較。維度或向量空間語意彼此不相容。
修正: indexer 應偵測模型 hash 不一致,並自動觸發完整重新索引。如果沒有,請手動清除資料庫並重新索引:
rm vectors.db
python index_vault.py --full
FTS5 維護
症狀: 多次增量更新後,FTS5 查詢回傳錯誤或不完整的結果。
原因: FTS5 內部 segments 可能在大量小型更新後變得碎片化。
修正: 重建並最佳化:
INSERT INTO chunks_fts(chunks_fts) VALUES('rebuild');
INSERT INTO chunks_fts(chunks_fts) VALUES('optimize');
MCP 逾時
症狀: AI tool 回報 MCP server 逾時。
原因: 第一次查詢會觸發模型載入(lazy initialization),需要 2-5 秒。AI tool 預設的 MCP 逾時時間可能更短。
修正: 在 server 啟動時預先暖機模型:
# In MCP server initialization
retriever = HybridRetriever(db_path, vault_path)
retriever.search("warmup", limit=1) # Trigger model load
SQLite 檔案鎖定
症狀: 出現 SQLITE_BUSY 或 SQLITE_LOCKED 錯誤。
原因: 多個程序同時寫入資料庫。WAL mode 允許並行讀取,但同一時間只能有一個 writer。
修正: 確保只有一個程序(indexer)寫入資料庫。MCP server 與 hooks 應只讀取。如果需要並行寫入,請使用 WAL mode 並設定 busy timeout:
db.execute("PRAGMA busy_timeout=5000") # Wait up to 5 seconds
sqlite-vec 無法載入
症狀: 向量搜尋停用;檢索器以 BM25-only 模式執行。
原因: sqlite-vec extension 未安裝、在 library path 中找不到,或與 SQLite 版本不相容。
修正:
# Install via pip
pip install sqlite-vec
# Or compile from source
git clone https://github.com/asg017/sqlite-vec
cd sqlite-vec && make
確認 extension 可載入:
import sqlite3
db = sqlite3.connect(":memory:")
db.enable_load_extension(True)
db.load_extension("vec0")
print("sqlite-vec loaded successfully")
大型筆記庫的記憶體問題
症狀: 對大型筆記庫(50,000 篇以上筆記)執行完整重新索引時出現記憶體不足錯誤。
原因: embedding batch size 太大,或所有檔案內容同時載入記憶體。
修正: 降低 batch size,並以增量方式處理檔案:
BATCH_SIZE = 32 # Reduce from 64
同時確認 indexer 是逐一處理檔案(讀取、分塊,並對每個檔案進行 embedding 後才移至下一個),而不是將所有檔案一次載入記憶體。
遷移指南
從 Apple Notes 遷移
- 透過「Export All」選項(macOS)匯出 Apple Notes,或使用
apple-notes-liberator等遷移工具 - 使用
markdownify或pandoc將 HTML 匯出內容轉換為 markdown - 將轉換後的檔案移至筆記庫的
00-inbox/資料夾 - 檢查並為每篇筆記新增 frontmatter
- 將筆記移至適當的領域資料夾
從 Notion 遷移
- 從 Notion 匯出:Settings → Export → Markdown & CSV
- 將匯出檔解壓縮到筆記庫的
00-inbox/資料夾 - 修正 Notion 特有的 markdown 產物:
- Notion 使用
- [ ]表示 checklist——這些是標準 markdown - Notion 會將 properties tables 納入 HTML——請轉換為 YAML frontmatter
- Notion 以相對路徑嵌入圖片——請將圖片複製到 attachments 資料夾
- 新增標準 frontmatter(
type、domain、tags) - 將 Notion page links 替換為 Obsidian wiki-links
從 Google Docs 遷移
- 使用 Google Takeout 匯出所有文件
- 將
.docx檔案轉換為 markdown:pandoc -f docx -t markdown input.docx -o output.md - 批次轉換:
for f in *.docx; do pandoc -f docx -t markdown "$f" -o "${f%.docx}.md"; done - 移至筆記庫、新增 frontmatter,並整理到資料夾中
從純 Markdown 遷移(未使用 Obsidian)
如果您已經有一個 markdown 檔案目錄:
- 將該目錄作為 Obsidian vault 開啟(Obsidian → Open Vault → Open folder)
- 如果該目錄有版本控制,請將
.obsidian/加入.gitignore - 建立 frontmatter templates,並套用到既有檔案
- 閱讀與整理時,開始用
[[wiki-links]]連結筆記 - 立即執行 indexer——檢索系統從第 1 天就能運作
從其他檢索系統遷移
如果您是從不同的 embedding/search 系統遷移:
- 不要嘗試遷移向量。 不同模型會產生不相容的向量空間。請用新模型執行完整重新索引。
- 遷移內容,而不是索引。 筆記庫檔案才是唯一真實來源。索引只是衍生產物。
- 遷移後進行驗證。 執行 10-20 個您已知答案的查詢,並確認結果符合預期。
變更記錄
| 日期 | 變更 |
|---|---|
| 2026-05-28 | Obsidian 1.13.0 桌面版 + 1.13.0 行動版(Catalyst early-access)發布。桌面版:重新設計的 Settings 面板,會在獨立視窗中開啟,並內建搜尋與鍵盤導覽;Obsidian URIs 現在會在觸發動作前顯示確認對話框;從網路磁碟載入 HTML 資源前新增警告;Bookmarks 檢視新增 Search;強化編輯器圖片處理;File Explorer / Properties / Sync 改進;大量開發者 API 與錯誤修正。行動版:新的 iOS Share Sheet,可設定目標位置;可從分頁切換器重新排序分頁;平板上的長按手勢可調整分割區與釘選側邊欄大小;Bases 新增選單項目,可調整表格檢視中的欄寬;iOS 與搜尋錯誤修正。對 AI 工作流程的影響:Obsidian URIs 的確認對話框為 URI 驅動的 MCP/agent 整合加入一道刻意的關卡;Bases 欄寬調整選單讓 Bases 更適合作為 agent 查詢的 vault 前端索引;iOS Share Sheet 可設定目標,讓 iPhone 擷取路徑(已記錄為主要匯入入口)更快接上 Claude/Codex pipelines。 |
| 2026-05-06 | 重新整理已由來源驗證的時效性:Smart Connections v4.5.0 將 footer connections 移入 Core;sqlite-vec v0.1.8/v0.1.9 穩定版更新封裝與 DELETE 行為;Model2Vec v0.8.x 更新 tokenizer/persistence 內部機制與 benchmark 表格;修正 Obsidian CLI 時序,從「1.12.7 introduced CLI」改為「1.12.0 introduced CLI,1.12.7 improved installation/runtime packaging」。 |
| 2026-04-27 | Web Clipper 4 月週期:1.4.0(互動式 YouTube transcript UI + Open in Reader 預設值)、1.5.0(Highlights viewer)、1.6.0(Highlighter UX 全面翻新 + Defuddle 0.18 source extractors,支援 LinkedIn/Threads/Bluesky/Discourse/Medium)、1.6.1 + 1.6.2(Reader 與 Safari 修正)。將 Web Clipper 重新定位為 AI 工作流程主要的瀏覽器端匯入路徑,而非只是順帶提及的 bookmark。此期間沒有 Obsidian 桌面版、Sync 或 Bases 發布。 |
| 2026-04-16 | Smart Connections v4.3.0(graph view、可設定 dock、block-embedding recovery、Substrate cross-plugin env)。記錄 2026 年 4 月 AI-native plugin 浪潮(Cortex、VaultSearch、LLM Wiki、Drift、EngramQuest、Hybrid Search MCP)。將 MarkusPfundstein/mcp-obsidian 標記為 maintenance-mode(最後 commit 為 2025 年 6 月)。Dataview 已停滯;Bases 是新工作的後繼選項。Obsidian CLI 1.12.7 仍是 AI assistants 的首選橋接方案。 |
| 2026-04-01 | 新增 Obsidian CLI 章節(v1.12 commands for AI workflows)。新增 agent plugin 章節(Claudian、Agent Client)。記錄用於 vault 組織的 Bases core plugin。將 plugin 數量更新為 2,500+。新增 iOS Share Extension 作為匯入來源。以 embedded agent plugins 更新相容性矩陣。 |
| 2026-03-30 | MCPVault v0.11.0:list_all_tags tool、.base/.canvas 支援,重新命名為 @bitbonsai/mcpvault。Obsidian Desktop v1.12.7 內建 CLI binary,以加速 terminal interactions。 |
| 2026-03-23 | 記錄 sqlite-vec v0.1.7 穩定版:支援 vec0 tables 的 DELETE、用於分頁的 KNN distance constraints。DiskANN approximate nearest neighbor index 已宣布將在後續版本推出。 |
| 2026-03-07 | 將 potion-multilingual-128M(101 種語言,2025 年 5 月)加入 embedding model 比較。sqlite-vec 為 v0.1.7-alpha.10(CI/CD 修正,無功能變更)。MCP spec 與 retrieval techniques 已確認仍為最新。 |
| 2026-03-03 | 更新 MCP spec 演進(2025 年 11 月已推出:Streamable HTTP、.well-known、tool annotations)。新增 Model2Vec fine-tuning 與 BPE/Unigram tokenizer 支援。新增 community MCP server 比較表。將 Smart Connections 更新至 v4。 |
| 2026-03-02 | 將 potion-base-32M 與 potion-retrieval-32M 加入 model 比較。新增 quantization/dimensionality reduction 章節。新增 MCP spec 演進註記。 |
| 2026-03-01 | 初始發布 |
參考資料
-
Internet Vin, “我搭配 Obsidian 與Claude Code使用的22個指令,” 2026年3月, x.com/internetvin/status/2026461256677245131. ↩
-
Nicopreme, “Visual Explainer” agent skill with slash commands, x.com/nicopreme/status/2023495040258261460. ↩
-
Cormack, G.V., Clarke, C.L.A., and Buettcher, S. Reciprocal Rank Fusion outperforms Condorcet and individual Rank Learning Methods. SIGIR, 2009。介紹以k=60作為無參數方法,用於合併排序清單的RRF。 ↩↩↩
-
OpenAI Embeddings Pricing。text-embedding-3-small:每百萬tokens為$0.02。估計每次完整重新索引知識庫的成本:約$0.30。 ↩
-
van Dongen, T. et al. Model2Vec: Turn any Sentence Transformer into a Small Fast Model. arXiv, 2025。說明從sentence transformers產生靜態embeddings的蒸餾方法。 ↩
-
potion-base-8M Model Card和Model2Vec results。目前已發布的表格顯示,potion-base-8M為51.32 Avg (All) / 51.08 Avg (MTEB),相較之下all-MiniLM-L6-v2為55.80 Avg (All) / 55.93 Avg (MTEB),約保留全任務分數的92%。 ↩
-
Model Context Protocol Specification。MCP是連接AI工具與資料來源的標準。 ↩
-
Model2Vec Potion Models、potion-base-32M和potion-retrieval-32M。目前model cards顯示,potion-base-32M為52.83 Avg (All),potion-retrieval-32M在檢索表格中為35.06。 ↩↩↩
-
Update on the Next MCP Protocol Release。2025年11月發布版推出Streamable HTTP傳輸、.well-known URL探索、結構化工具註解,以及SDK層級標準化。下一版暫定於2026年中發布,包含非同步作業、特定領域擴充功能,以及agent-to-agent通訊。 ↩↩
-
Model2Vec Releases。v0.4.0(2025年2月):支援訓練/微調。v0.5.0(2025年4月):後端重寫、量化、降維。v0.7.0(2025年10月):詞彙量化、BPE/Unigram tokenizer支援。v0.8.0/v0.8.1(2026年3月):tokenizer與持久化重構、Python 3.9棄用、MTEB V2結果更新,以及Windows路徑相容性。 ↩↩
-
Smart Connections for Obsidian。Smart Connections v4:local-first AI embeddings,語意搜尋在初始索引後可離線運作。 ↩
-
potion-multilingual-128M。Minish Lab,2025年5月。101語言靜態embedding模型,為表現最佳的多語靜態embeddings。與其他potion模型一樣,僅依賴numpy。 ↩
-
MCPVault v0.11.0。2026年3月。新增
list_all_tags工具,可掃描frontmatter與hashtags並統計數量。改善帶點號資料夾處理,支援.base與.canvas檔案。套件在npm上重新命名為@bitbonsai/mcpvault。 ↩ -
sqlite-vec v0.1.7 Release。2026年3月17日。穩定版本:支援vec0虛擬資料表的DELETE、用於分頁的KNN距離限制、fuzz testing改進。DiskANN近似最近鄰索引已宣布將於未來版本推出。 ↩↩↩
-
Introduction to Bases。Obsidian核心plugin於v1.9.10導入。可在知識庫檔案上建立類資料庫視圖(表格、圖庫、行事曆、kanban boards),並使用frontmatter屬性作為欄位。檔案儲存為
.base格式。 ↩ -
Obsidian Desktop v1.12.0 Changelog和Obsidian Desktop v1.12.7 Changelog。v1.12.0導入用於終端機知識庫自動化的CLI;v1.12.7透過獨立binary、TUI和socket-file行為,改善安裝/執行階段封裝。另請參閱CLI documentation。 ↩↩
-
Claudian。Obsidian plugin,可將Claude Code嵌入知識庫作為AI協作者。提供側邊欄聊天、情境感知提示、視覺支援、slash commands和權限模式。 ↩
-
Agent Client。Obsidian plugin,透過Agent Client Protocol (ACP)為Claude Code、Codex CLI和Gemini CLI提供統一介面。支援筆記提及、shell執行與動作核准。 ↩
-
Obsidian iOS Changelog。2026年初更新包含Share Extension,可從其他app直接將內容儲存至知識庫;也包含Daily Note與Bookmark widget修正,以及View Note widget重新整理改善。 ↩
-
MarkusPfundstein/mcp-obsidian。最後commit為2025年6月28日。沒有標記版本。論壇討論(2026年4月)指出,自Obsidian 1.12.x推出第一方CLI後,社群已遷移至以CLI為基礎的整合。本指南保留此項目作為歷史脈絡,並供現有設定的使用者參考,但不建議用於新部署。 ↩↩
-
Smart Connections v4.5.0 Release。2026年5月5日。Footer connections成為Core功能;近期v4版本也包含連結清單的graph views、可設定的connection-panel位置、改進的block-embedding復原、Substrate跨plugin狀態、transformer fallback修正,以及減少重複connection計算。 ↩
-
obsidianmd/obsidian-clipper releases — Web Clipper版本功能對照的主要來源。2026年4月週期:1.4.0(4月9日,YouTube transcript UI + Open in Reader預設)、1.5.0(4月15日,Highlights viewer + Reader fade-in)、1.5.1(4月15日,webpack編譯修正)、1.6.0(4月21日,Highlighter UX + Defuddle 0.18,含LinkedIn/Threads/Bluesky/Discourse/Medium extractors)、1.6.1(4月22日,Reader outline修正 + highlights搜尋)、1.6.2(4月23日,Safari embedded-mode clipboard修正)。另列於Mozilla Add-ons store和Chrome Web Store。 ↩
-
sqlite-vec v0.1.8、sqlite-vec v0.1.9、sqlite-vec v0.1.10-alpha.3,以及sqlite-vec v0.1.10-alpha.4。v0.1.8修正npm封裝;v0.1.9修正metadata文字欄位長度超過12個字元時的DELETE bug;v0.1.10-alpha.3新增正確的
INSERT OR REPLACE INTO支援;v0.1.10-alpha.4(2026年5月18日)修正使用新ivf/diskann功能的vec0資料表在ALTER TABLE RENAME時失敗的問題,以及DiskANN中的cached-statement清理bug。0.1.10系列仍為預發布版本。 ↩↩↩