obsidian:~/vault$ search --hybrid obsidian

Obsidian MCP+混合檢索:2026參考指南

# 應用程式基礎請使用官方Obsidian文件;MCP、混合搜尋,以及為16,894個檔案的AI vault建立索引,請參考Blake的指南。

words: 3385 read_time: 49m updated: 2026-06-01 14:42
$ retriever search --hybrid obsidian

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。grepripgrep、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 件事:

  1. 雙向連結。 Obsidian 會自動追蹤 backlinks。當您從 Note A 連到 Note B 時,Note B 會顯示 Note A 參照了它。圖譜面板會視覺化連結群集。這種雙向感知是原始檔案系統不會提供的 metadata。

  2. 具備 plugin 渲染的即時預覽。 Dataview 查詢、Mermaid 圖表與 callout 區塊會即時渲染。寫作體驗比文字編輯器更豐富,但儲存格式仍是純文字。您在豐富環境中寫作與整理;檢索系統索引原始 markdown。

  3. 社群基礎架構。 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-obsidian repo 自 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 listobsidian template create 會從 Templater 或核心 templates 產生筆記,讓 AI agents 不必直接寫入 markdown 檔案,也能建立結構化知識庫項目。
  • 屬性管理。 obsidian property setobsidian 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.mdAGENTS.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可在結果中包含來源URL
  • status—可將已封存或草稿筆記排除在有效搜尋之外

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。 使用含有動態欄位的範本建立筆記。透過範本預先填入createdtypedomain欄位,確保每則新筆記一開始就具備正確的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編輯,並支援欄位值自動完成。可減少typedomaintags欄位中的拼寫錯誤。一致的中繼資料能提升檢索篩選準確度。

會傷害索引的外掛

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_fuseembed_batchget_stale_files
  • CLI旗標:--incremental--vault--model
  • 設定鍵:bm25_weightmax_tokensbatch_size
  • 錯誤訊息:SQLITE_LOCKEDConnectionRefusedError
  • 特定專門術語:PostToolUsePreToolUseAGENTS.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擴充功能將向量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分數時(很少見,但若它們在某個清單中排名相同,且都未出現在另一個清單中,仍可能發生),依下列方式打破平手:

  1. 優先選擇同時出現在兩個清單中的chunk,而非只出現在單一清單中的chunk
  2. 在同時出現在兩個清單中的chunk之間,優先選擇合併排名較低者
  3. 在只出現在單一清單中的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 會:

  1. 掃描 vault 中允許資料夾內的所有 .md 檔案
  2. 從檔案系統讀取每個檔案的 mtime_ns
  3. 與資料庫中儲存的 mtime_ns 比較
  4. 識別 3 種類別:
  5. 新檔案: 路徑存在於檔案系統,但不存在於資料庫
  6. 已變更檔案: 路徑兩邊都存在,但 mtime_ns 不同
  7. 已刪除檔案: 路徑存在於資料庫,但不存在於檔案系統
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 執行時:

  1. Model hash 檢查。將儲存的 model hash 與目前 model 比對。若不同,會自動切換到完整重新建立索引模式,並警告使用者。
  2. 檔案掃描。走訪允許的資料夾,收集檔案路徑與 mtimes。
  3. 變更偵測。與已儲存資料比對。
  4. 批次處理。以每批 64 個檔案重新 chunk 並重新 embed 已變更檔案。
  5. 進度回報。印出已處理檔案數與經過時間。
  6. 優雅關閉。處理 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

關鍵設計選擇:

  1. 先過濾,再 embedding。被 embed 的是清理後文字。vector representation 永遠不會編碼憑證模式。查詢「API key」會傳回討論 API key 管理的筆記,而不是包含實際金鑰的筆記。

  2. 取代,而非移除。[REDACTED:pattern-name] token 保留周圍文字的語意脈絡。embedding 會捕捉「這裡曾有類似憑證的內容」,但不會編碼憑證本身。

  3. 記錄模式,不記錄值。過濾器只記錄命中的模式(例如「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-known URL探索、可宣告工具為唯讀或會變更狀態的結構化工具註解,以及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伺服器應強制執行嚴格邊界:

  1. 唯讀。伺服器會讀取知識庫與索引資料庫。它不會建立、修改或刪除筆記。寫入操作(擷取新筆記)由獨立的hook或skill處理,而不是由MCP伺服器處理。

  2. 限定知識庫範圍。伺服器只會讀取設定知識庫路徑內的檔案。必須拒絕路徑遍歷嘗試(../../etc/passwd)。

  3. 輸出進行憑證過濾。即使資料庫包含已預先過濾的內容,也要在輸出時套用憑證過濾,作為縱深防禦措施。

  4. 回應限制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_searchobsidian_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基於兩項訊號:

  1. TTL到期。 每個脈絡區塊都有存活時間。TTL到期時,系統會重新查詢保存庫並重建該區塊。
  2. 保存庫變更偵測。 當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中有lsfind 截斷為前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會以兩種方式提升檢索效果:

  1. 直接匹配。搜尋「authentication overview」會匹配到MOC本身,讓agent取得一份經過整理的相關筆記清單。
  2. 脈絡擴展。找到特定筆記後,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

捕捉到的洞察會立即建立索引,日後可供檢索。數月下來,這些微型記錄會累積成一套與實作高度相關的知識語料庫。

專案啟動

開始新專案或新功能時:

  1. 搜尋筆記庫:「我對 [technology/pattern] 已經知道什麼?」
  2. 檢視前 5 筆結果,了解過去的決策與注意事項
  3. 確認該領域是否已有 MOC;如果沒有,就建立一個
  4. 搜尋失敗模式:「[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_BUSYSQLITE_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 遷移

  1. 透過「Export All」選項(macOS)匯出 Apple Notes,或使用 apple-notes-liberator 等遷移工具
  2. 使用 markdownifypandoc 將 HTML 匯出內容轉換為 markdown
  3. 將轉換後的檔案移至筆記庫的 00-inbox/ 資料夾
  4. 檢查並為每篇筆記新增 frontmatter
  5. 將筆記移至適當的領域資料夾

從 Notion 遷移

  1. 從 Notion 匯出:Settings → Export → Markdown & CSV
  2. 將匯出檔解壓縮到筆記庫的 00-inbox/ 資料夾
  3. 修正 Notion 特有的 markdown 產物:
  4. Notion 使用 - [ ] 表示 checklist——這些是標準 markdown
  5. Notion 會將 properties tables 納入 HTML——請轉換為 YAML frontmatter
  6. Notion 以相對路徑嵌入圖片——請將圖片複製到 attachments 資料夾
  7. 新增標準 frontmatter(typedomaintags
  8. 將 Notion page links 替換為 Obsidian wiki-links

從 Google Docs 遷移

  1. 使用 Google Takeout 匯出所有文件
  2. .docx 檔案轉換為 markdown:pandoc -f docx -t markdown input.docx -o output.md
  3. 批次轉換:for f in *.docx; do pandoc -f docx -t markdown "$f" -o "${f%.docx}.md"; done
  4. 移至筆記庫、新增 frontmatter,並整理到資料夾中

從純 Markdown 遷移(未使用 Obsidian)

如果您已經有一個 markdown 檔案目錄:

  1. 將該目錄作為 Obsidian vault 開啟(Obsidian → Open Vault → Open folder)
  2. 如果該目錄有版本控制,請將 .obsidian/ 加入 .gitignore
  3. 建立 frontmatter templates,並套用到既有檔案
  4. 閱讀與整理時,開始用 [[wiki-links]] 連結筆記
  5. 立即執行 indexer——檢索系統從第 1 天就能運作

從其他檢索系統遷移

如果您是從不同的 embedding/search 系統遷移:

  1. 不要嘗試遷移向量。 不同模型會產生不相容的向量空間。請用新模型執行完整重新索引。
  2. 遷移內容,而不是索引。 筆記庫檔案才是唯一真實來源。索引只是衍生產物。
  3. 遷移後進行驗證。 執行 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 初始發布

參考資料


  1. Internet Vin, “我搭配 Obsidian 與Claude Code使用的22個指令,” 2026年3月, x.com/internetvin/status/2026461256677245131

  2. Nicopreme, “Visual Explainer” agent skill with slash commands, x.com/nicopreme/status/2023495040258261460

  3. 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。 

  4. OpenAI Embeddings Pricing。text-embedding-3-small:每百萬tokens為$0.02。估計每次完整重新索引知識庫的成本:約$0.30。 

  5. van Dongen, T. et al. Model2Vec: Turn any Sentence Transformer into a Small Fast Model. arXiv, 2025。說明從sentence transformers產生靜態embeddings的蒸餾方法。 

  6. potion-base-8M Model CardModel2Vec 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%。 

  7. Model Context Protocol Specification。MCP是連接AI工具與資料來源的標準。 

  8. Model2Vec Potion Modelspotion-base-32Mpotion-retrieval-32M。目前model cards顯示,potion-base-32M為52.83 Avg (All),potion-retrieval-32M在檢索表格中為35.06。 

  9. Update on the Next MCP Protocol Release。2025年11月發布版推出Streamable HTTP傳輸、.well-known URL探索、結構化工具註解,以及SDK層級標準化。下一版暫定於2026年中發布,包含非同步作業、特定領域擴充功能,以及agent-to-agent通訊。 

  10. 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路徑相容性。 

  11. Smart Connections for Obsidian。Smart Connections v4:local-first AI embeddings,語意搜尋在初始索引後可離線運作。 

  12. potion-multilingual-128M。Minish Lab,2025年5月。101語言靜態embedding模型,為表現最佳的多語靜態embeddings。與其他potion模型一樣,僅依賴numpy。 

  13. MCPVault v0.11.0。2026年3月。新增list_all_tags工具,可掃描frontmatter與hashtags並統計數量。改善帶點號資料夾處理,支援.base.canvas檔案。套件在npm上重新命名為@bitbonsai/mcpvault。 

  14. sqlite-vec v0.1.7 Release。2026年3月17日。穩定版本:支援vec0虛擬資料表的DELETE、用於分頁的KNN距離限制、fuzz testing改進。DiskANN近似最近鄰索引已宣布將於未來版本推出。 

  15. Introduction to Bases。Obsidian核心plugin於v1.9.10導入。可在知識庫檔案上建立類資料庫視圖(表格、圖庫、行事曆、kanban boards),並使用frontmatter屬性作為欄位。檔案儲存為.base格式。 

  16. Obsidian Desktop v1.12.0 ChangelogObsidian Desktop v1.12.7 Changelog。v1.12.0導入用於終端機知識庫自動化的CLI;v1.12.7透過獨立binary、TUI和socket-file行為,改善安裝/執行階段封裝。另請參閱CLI documentation。 

  17. Claudian。Obsidian plugin,可將Claude Code嵌入知識庫作為AI協作者。提供側邊欄聊天、情境感知提示、視覺支援、slash commands和權限模式。 

  18. Agent Client。Obsidian plugin,透過Agent Client Protocol (ACP)為Claude Code、Codex CLI和Gemini CLI提供統一介面。支援筆記提及、shell執行與動作核准。 

  19. Obsidian iOS Changelog。2026年初更新包含Share Extension,可從其他app直接將內容儲存至知識庫;也包含Daily Note與Bookmark widget修正,以及View Note widget重新整理改善。 

  20. MarkusPfundstein/mcp-obsidian。最後commit為2025年6月28日。沒有標記版本。論壇討論(2026年4月)指出,自Obsidian 1.12.x推出第一方CLI後,社群已遷移至以CLI為基礎的整合。本指南保留此項目作為歷史脈絡,並供現有設定的使用者參考,但不建議用於新部署。 

  21. Smart Connections v4.5.0 Release。2026年5月5日。Footer connections成為Core功能;近期v4版本也包含連結清單的graph views、可設定的connection-panel位置、改進的block-embedding復原、Substrate跨plugin狀態、transformer fallback修正,以及減少重複connection計算。 

  22. 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 storeChrome Web Store。 

  23. sqlite-vec v0.1.8sqlite-vec v0.1.9sqlite-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系列仍為預發布版本。 

VAULT obsidian.md INDEXED