Example vault location
#
重點摘要
重點在於上下文工程,而非筆記記錄。 Obsidian 知識庫對 AI 的價值不在於筆記本身,而在於讓筆記可被查詢的檢索層。一個擁有 16,000 個檔案但沒有檢索功能的知識庫,只是一個只寫不讀的資料庫。一個僅有 200 個檔案但具備混合搜尋與 MCP 整合的知識庫,才是真正的 AI 知識庫。檢索基礎設施才是產品,筆記只是原料。
混合檢索優於純關鍵字或純語意搜尋。 BM25 能精確捕捉識別符與函式名稱。向量搜尋能捕捉同義詞及跨不同術語的概念匹配。Reciprocal Rank Fusion(RRF)將兩者合併,無需進行分數校準。任何單一方法都無法涵蓋兩種失敗模式。MS MARCO 段落排序的研究證實了這個模式:混合檢索的效能持續優於單獨使用任一方法。1 混合檢索器深度解析涵蓋了 RRF 數學原理、使用真實數據的範例、失敗模式分析,以及互動式融合計算器。
MCP 讓 AI 工具直接存取知識庫。 Model Context Protocol(MCP)伺服器將檢索器公開為工具,使 Claude Code、Codex CLI、Cursor 及其他 AI 工具能直接呼叫。代理查詢知識庫,接收帶有來源歸屬的排序結果,並在不載入完整檔案的情況下使用上下文。MCP 伺服器是檢索引擎之上的薄封裝層。
本機優先意味著零 API 成本與完整隱私。 整個技術堆疊在單一機器上運行:SQLite 負責儲存、Model2Vec 負責嵌入向量、FTS5 負責關鍵字搜尋、sqlite-vec 負責向量 KNN。無需雲端服務、無需 API 呼叫、無網路依賴。個人筆記永遠不會離開您的機器。完整重新嵌入 49,746 個區塊以 OpenAI API 價格計算約需 $0.30,但真正的成本在於延遲、隱私暴露,以及對一個應該能離線運作的系統產生網路依賴。2
增量索引讓系統在 10 秒內保持最新。 透過檔案修改時間比對來偵測變更。只有修改過的檔案才會重新分塊與重新嵌入。在 Apple M 系列硬體上,完整重新索引約需四分鐘。一天中的典型編輯量進行增量更新只需不到十秒。系統無需手動介入即可保持最新。
此架構可從 200 篇筆記擴展至 20,000 篇以上。 相同的三層設計(擷取、檢索、整合)適用於任何知識庫規模。從小型知識庫的 BM25 純關鍵字搜尋開始。當關鍵字碰撞成為問題時,加入向量搜尋。當您同時需要精確匹配與語意匹配時,加入 RRF 融合。每一層都可獨立使用,也可獨立移除。
如何使用本指南
本指南涵蓋完整系統。您的起點取決於您目前的狀態:
| 您是… | 從這裡開始 | 然後探索 |
|---|---|---|
| Obsidian + AI 新手 | 為什麼選擇 Obsidian 作為 AI 基礎設施、快速開始 | 知識庫架構、MCP 伺服器架構 |
| 已有知識庫,想接入 AI | MCP 伺服器架構、Claude Code 整合 | 嵌入模型、全文搜尋 |
| 正在建構檢索系統 | 完整檢索管線、Reciprocal Rank Fusion | 效能調校、疑難排解 |
| 團隊或企業情境 | 決策框架、知識圖譜模式 | 開發者工作流程食譜、遷移指南 |
標記為合約的章節包含實作細節、設定區塊與失敗模式。標記為敘事的章節聚焦於概念、架構決策,以及設計選擇背後的推理。標記為食譜的章節提供逐步工作流程。
為什麼選擇 Obsidian 作為 AI 基礎設施
本指南的核心論點:Obsidian 知識庫是個人 AI 知識庫的最佳基底,因為它是本機優先、純文字、圖結構的,且使用者可控制技術堆疊的每一層。
Obsidian 提供了其他替代方案所沒有的 AI 優勢
純文字 Markdown 檔案。 每篇筆記都是檔案系統上的 .md 檔案。沒有專有格式、不需要資料庫匯出、不需要 API 就能讀取內容。任何能讀取檔案的工具都能讀取您的知識庫。grep、ripgrep、Python 的 pathlib、SQLite FTS5——它們都能直接處理原始檔案。當您建構檢索系統時,您索引的是檔案,而非 API 回應。索引始終與原始檔案保持一致,因為來源就是檔案系統。
本機優先架構。 知識庫存在於您的機器上。沒有伺服器、不依賴雲端同步、沒有 API 速率限制、沒有管控您如何處理自己內容的服務條款。您可以在不使用任何外部服務的情況下對筆記進行嵌入、索引、分塊與搜尋。這對 AI 基礎設施至關重要,因為檢索管線的速度取決於您的磁碟效能,而非 API 端點的回應速度。這對隱私同樣重要:包含憑證、健康資料、財務資訊及個人反思的筆記永遠不會離開您的機器。
透過 wiki-link 建立的圖結構。 Obsidian 的 [[wiki-link]] 語法在筆記之間建立有向圖。一篇關於 OAuth 實作的筆記會連結到關於權杖輪換、工作階段管理與 API 安全性的筆記。圖結構編碼了人為策劃的概念間關係。向量嵌入捕捉語意相似性,但 wiki-link 捕捉的是作者在思考主題時建立的有意識連結。這是嵌入向量無法複製的訊號。
外掛生態系統。 Obsidian 擁有超過 1,800 個社群外掛。Dataview 讓您像查詢資料庫一樣查詢知識庫。Templater 使用 JavaScript 邏輯從範本生成筆記。Git 整合將知識庫同步至儲存庫。Linter 確保格式一致性。這些外掛為知識庫增添結構,而不改變底層的純文字格式。檢索系統索引的是這些外掛的輸出,而非外掛本身。
超過 500 萬使用者。 Obsidian 擁有龐大的活躍社群,產出範本、工作流程、外掛與文件。當您遇到知識庫組織或外掛設定的問題時,很可能已有人記錄了解決方案。社群也產出 Obsidian 周邊工具:MCP 伺服器、索引腳本、發布管線與 API 封裝器。
單純的檔案系統無法提供的功能
一個 Markdown 檔案的目錄具備純文字優勢,但缺少 Obsidian 增添的三項功能:
-
雙向連結。 Obsidian 自動追蹤反向連結。當您從筆記 A 連結到筆記 B 時,筆記 B 會顯示筆記 A 引用了它。圖面板以視覺化方式呈現連結叢集。這種雙向感知是純檔案系統無法提供的後設資料。
-
帶有外掛渲染的即時預覽。 Dataview 查詢、Mermaid 圖表和標註區塊即時渲染。書寫體驗比文字編輯器更豐富,而儲存格式仍然是純文字。您在豐富的環境中書寫與組織;檢索系統索引的是原始 Markdown。
-
社群基礎設施。 外掛探索、主題市集、同步服務(選用)、發布服務(選用),以及文件生態系統。您可以使用獨立工具複製任何單一功能,但 Obsidian 將它們整合為一套連貫的工作流程。
Obsidian 不做的事(以及您需要建構的部分)
Obsidian 不包含檢索基礎設施。它具備基本搜尋功能(全文、檔案名稱、標籤),但沒有嵌入管線、沒有向量搜尋、沒有融合排序、沒有 MCP 伺服器、沒有憑證過濾、沒有分塊策略,也沒有與外部 AI 工具的整合鉤子。本指南涵蓋您在 Obsidian 之上建構的基礎設施。 知識庫是基底。檢索管線、MCP 伺服器與整合鉤子才是基礎設施。
此處描述的架構是以 Markdown 為優先,而非 Obsidian 獨佔。 如果您使用 Logseq、Foam、Dendron 或純 Markdown 檔案目錄,檢索管線的運作方式完全相同。分塊器讀取 .md 檔案。嵌入器處理文字字串。索引器寫入 SQLite。這些元件都不依賴 Obsidian 特定功能。Obsidian 的貢獻在於提供書寫與組織環境,產出檢索器所索引的 Markdown 檔案。
快速開始:第一個 AI 連接的 Vault
本節將在五分鐘內將 vault 連接到 AI 工具。您將安裝 Obsidian、建立 vault、安裝 MCP 伺服器,並執行第一次查詢。快速開始使用社群 MCP 伺服器以獲得即時結果。後續章節會涵蓋如何建構適用於正式環境的自訂檢索管線。
前置需求
- macOS、Linux 或 Windows
- Node.js 18+(用於 MCP 伺服器)
- 已安裝 Claude Code、Codex CLI 或 Cursor
步驟 1:建立 Vault
從 obsidian.md 下載 Obsidian 並建立新的 vault。選擇一個您容易記住的位置——MCP 伺服器需要絕對路徑。
# Example vault location
~/Documents/knowledge-base/
新增一些筆記,讓檢索器有內容可以處理。即使只有 10-20 篇筆記也足以看到結果。每篇筆記應為 .md 檔案,具有有意義的標題和至少一段內容。
步驟 2:安裝 MCP 伺服器
多個社群 MCP 伺服器提供即時的 vault 存取功能。生態系統在 2025-2026 年間有了顯著成長:
| 伺服器 | 作者 | 傳輸方式 | 需要外掛 | 主要功能 |
|---|---|---|---|---|
| obsidian-mcp-server | StevenStavrakis | STDIO | 否 | 輕量級,基於檔案 |
| mcp-obsidian | MarkusPfundstein | STDIO | Local REST API | 透過 REST 進行完整 vault CRUD |
| obsidian-mcp-tools | jacksteamdev | STDIO | 是(外掛) | 語意搜尋 + Templater |
| obsidian-claude-code-mcp | iansinnott | WebSocket | 是(外掛) | Claude Code 自動探索 |
| obsidian-mcp-server | cyanheads | STDIO | Local REST API | 標籤、frontmatter 管理 |
快速開始的最簡單選項是直接讀取 .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 工具,詢問一個您的 vault 筆記可以回答的問題:
Search my Obsidian vault for notes about [topic you wrote about]
AI 工具會呼叫 MCP 伺服器,搜尋您的 vault 並回傳匹配的內容。您應該會看到包含檔案路徑和相關摘錄的結果。
您剛才建構了什麼
您透過標準協定將本機知識庫連接到了 AI 工具。MCP 伺服器讀取您的 vault 檔案、執行基本搜尋並回傳結果。這是最小可行版本。
此快速開始未提供的功能: - 混合檢索(BM25 + 向量搜尋 + RRF 融合) - 基於 embedding 的語意搜尋 - 憑證過濾 - 增量索引 - 基於 hook 的自動上下文注入
本指南的其餘部分涵蓋如何建構這些功能。快速開始驗證了概念。完整管線則提供正式環境品質的檢索。
決策框架: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 |
|---|---|---|---|---|---|
| 本機優先 | 是 | 否(雲端) | 部分(iCloud) | 是 | 是 |
| 純文字 | 是(markdown) | 否(區塊) | 否(專有格式) | 是 | 是 |
| 圖譜結構 | 是(wiki-link) | 部分(提及) | 否 | 否 | 否 |
| AI 可索引 | 直接存取檔案 | 需要 API | 需要匯出 | 直接存取檔案 | 已在上下文中 |
| 外掛生態系統 | 1,800+ 外掛 | 整合服務 | 無 | 不適用 | 不適用 |
| 離線功能 | 完整 | 僅快取唯讀 | 部分 | 完整 | 完整 |
| 支援 10K+ 筆記 | 是 | 是(需要 API) | 效能下降 | 是 | 否(單一檔案) |
| 費用 | 免費(核心) | $10/月以上 | 免費 | 免費 | 免費 |
何時 Obsidian 大材小用
- 單一專案上下文。如果 AI 僅需要當前程式碼庫的上下文,請將其放入
CLAUDE.md、AGENTS.md或專案層級的文件中。這些檔案隨程式碼庫一起移動,並會自動載入。 - 結構化資料。如果內容是表格、記錄或綱要,請使用資料庫。Obsidian 筆記以散文為主。Dataview 可以查詢 frontmatter 欄位,但真正的資料庫更能處理結構化查詢。
- 臨時研究。如果筆記在專案結束後會被丟棄,使用包含 markdown 檔案的臨時資料夾會更簡單。不要為暫時性內容建構檢索基礎設施。
何時 Obsidian 是正確選擇
- 數月或數年持續累積知識。價值隨語料庫成長而複合增長。一個擁有 200 篇筆記、每天查詢使用六個月的 vault,比一個擁有 5,000 篇筆記但只查詢一次的 vault 提供更多價值。
- 單一語料庫包含多個領域。一個包含程式設計、架構、資安、設計和個人專案筆記的 vault,受益於跨領域檢索——這是特定專案的
CLAUDE.md無法提供的。 - 對隱私敏感的內容。本機優先意味著檢索管線永遠不會將內容傳送至外部服務。vault 包含您放入的任何內容,包括您不願上傳到雲端服務的內容。
心智模型:三層架構
此系統由三個獨立運作的層級組成,但組合後會產生複合效果。每一層關注不同的面向,也有不同的失敗模式。
┌─────────────────────────────────────────────────────┐
│ 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)決定哪些內容進入知識庫。若缺乏篩選機制,知識庫會逐漸累積雜訊:推文截圖、未加註解的複製貼上文章、缺乏上下文的半成品想法。攝取層負責在進入點執行品質控管。無論是評分管線、標籤慣例或人工審查流程——任何能確保知識庫內容值得檢索的機制皆可。
檢索層(Retrieval)使知識庫變得可查詢。這是核心引擎:將筆記切分為搜尋單元、將區塊嵌入向量空間、建立關鍵字與語意搜尋索引、透過 RRF 融合結果。檢索層將一個檔案目錄轉化為可查詢的知識庫。若缺少此層,知識庫僅能透過手動瀏覽和基本搜尋導覽,無法被 AI 工具以程式化方式存取。
整合層(Integration)將檢索層連接至 AI 工具。MCP 伺服器將檢索功能公開為可呼叫的工具。Hooks 自動注入上下文。Skills 將新知識擷取回知識庫。整合層是知識庫與使用它的 AI 代理之間的介面。
各層級在設計上彼此解耦。攝取評分管線不需了解 embeddings。檢索器不需了解訊號路由規則。MCP 伺服器不需了解筆記是如何建立的。這種解耦意味著您可以獨立改進任何一層。替換嵌入模型而無需更動攝取管線。新增 MCP 功能而無需修改檢索器。變更訊號評分啟發式方法而無需觸及索引。
為 AI 使用而設計的知識庫架構
針對 AI 檢索最佳化的知識庫,與針對個人瀏覽最佳化的知識庫遵循不同的慣例。本節涵蓋資料夾結構、筆記架構、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 散文的內容——專案、領域、資源、訊號、每日筆記。
應從索引中排除的資料夾:範本(包含佔位變數,非實際內容)、附件(二進位檔案)、Obsidian 設定檔,以及任何包含您不希望出現在檢索索引中的敏感內容的資料夾。
.indexignore 檔案
在知識庫根目錄建立 .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/
索引器在掃描前讀取此檔案,並完全略過匹配的路徑。被排除路徑中的檔案永遠不會被切分、不會被嵌入,也不會出現在搜尋結果中。
筆記架構
每篇筆記都應具備 YAML frontmatter。檢索器使用 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——啟用類型篩選查詢(如「僅顯示 MOC」或「僅顯示訊號」)tags——以 0.3 權重索引於 FTS5 標題上下文中,即使內文使用不同術語也能提供關鍵字匹配
非必填但有價值的欄位:
domain——啟用領域範圍查詢(如「僅搜尋安全相關筆記」)source——擷取內容的來源歸屬;檢索器可在結果中包含來源 URLstatus——允許將已封存或草稿筆記從活躍搜尋中排除
切分慣例
檢索器以 H2(##)標題為邊界進行切分。這意味著您的筆記結構直接影響檢索的精細度:
有利於檢索的寫法:
## 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...
三個 H2 區段產生三個可獨立搜尋的區塊。每個區塊都有足夠的上下文讓嵌入向量捕捉其含義。關於「過期權杖處理」的查詢會精確匹配第三個區塊。
不利於檢索的寫法:
# 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 標題的長區段只會產生一個大型區塊。嵌入向量會在該區段中所有主題間取平均值。任何子主題的查詢都會同等程度地匹配整篇筆記。
經驗法則:若某個區段涵蓋超過一個概念,請將其拆分為 H2 子區段。切分器會處理其餘工作。
筆記中應避免的內容
會降低檢索品質的內容:
- 未加註解的整篇文章原文複製貼上。檢索器會索引原始文章的關鍵字,使您的知識庫被非自己撰寫的內容所稀釋。請改為新增摘要、擷取重點,或連結至來源 URL。
- 沒有文字說明的截圖。檢索器索引的是 Markdown 文字。沒有替代文字或周圍描述的圖片,對 BM25 和向量搜尋而言都是不可見的。
- 憑證字串。API 金鑰、權杖、密碼、連線字串。即使有憑證過濾機制,最安全的做法仍是永遠不要將機密資訊貼入筆記中。請改以名稱引用(如「
~/.env中的 Cloudflare API 權杖」)。 - 未經整理的自動產生內容。若工具產生了一篇筆記(會議逐字稿、Readwise 重點標記、RSS 匯入),請在其進入永久知識庫前先行審閱並加上註解。未經整理的自動匯入只會增加數量,卻不會增加可檢索的價值。
AI 工作流程的外掛生態系
能提升知識庫品質以利 AI 檢索的 Obsidian 外掛可分為三類:結構型(強制一致性)、查詢型(公開中繼資料)以及同步型(保持知識庫即時更新)。
必備外掛
Dataview。 利用 frontmatter 欄位像資料庫一樣查詢您的知識庫。建立動態索引:「所有標記為 security 且在過去 30 天內更新的筆記」或「所有狀態為 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 內部提供 AI 驅動語意搜尋的外掛。Smart Connections v4 預設在本機建立 embeddings——一旦您的知識庫完成索引,語意關聯和查找功能便可完全離線運作,無需任何 API 呼叫。21 雖然本指南中的檢索系統是 Obsidian 外部的(以 Python 管線運行),Smart Connections 在撰寫時探索語意關係方面仍相當實用。這兩個系統索引相同的內容但服務不同的使用情境:Smart Connections 用於編輯器內的探索發現,外部檢索器則透過 MCP 提供 AI 工具整合。
Metadata Menu。 提供結構化的 frontmatter 編輯功能,並支援欄位值的自動完成。減少 type、domain 和 tags 欄位中的拼寫錯誤。一致的中繼資料能提升檢索過濾的準確度。
不利於索引的外掛
Excalidraw。 將繪圖儲存為嵌入在 markdown 檔案中的 JSON。該 JSON 在語法上是有效的 markdown,但在分塊和生成 embeddings 時會產生無意義的內容。請透過 .indexignore 排除 Excalidraw 檔案,或依副檔名進行過濾。
Kanban。 將看板狀態儲存為特殊格式的 markdown。該格式是為看板呈現設計的,並非為文章檢索而設計。分塊器會產生卡片標題和中繼資料的碎片,無法有效生成 embeddings。請將 Kanban 看板從索引中排除。
Calendar。 建立內容極少的每日筆記(通常只有日期標題)。空白或近乎空白的筆記會產生低品質的分塊。如果您使用每日筆記,請在其中撰寫實質內容,或將每日筆記資料夾從索引中排除。
重要的外掛設定
檔案復原 → 啟用。 防止意外刪除筆記。雖與檢索無直接關聯,但對於您所依賴的知識庫而言至關重要。
嚴格換行 → 停用。 標準 Markdown 換行(雙換行代表段落)比 Obsidian 的嚴格模式(單換行代表 <br>)產生更乾淨的分塊。
新檔案預設位置 → 指定資料夾。 將新檔案導向 00-inbox/,使未分類的筆記不會污染領域資料夾。收件匣是暫存區域;檔案在分類後移至對應的領域資料夾。
Wiki-link 格式 → 盡可能使用最短路徑。 較短的連結目標讓檢索器在索引連結結構時更容易解析。
Embedding 模型:選擇與設定
Embedding 模型將文字區塊轉換為數值向量,用於語意搜尋。模型的選擇決定了檢索品質、索引大小、embedding 速度及執行時期的依賴項。本節說明為何 Model2Vec 的 potion-base-8M 是預設選擇,以及何時應考慮其他替代方案。
為何選擇 Model2Vec potion-base-8M
模型: minishlab/potion-base-8M
參數量: 760 萬
維度: 256
大小: ~30 MB
依賴項: model2vec(僅需 numpy,不需要 PyTorch)
推論: 僅 CPU,靜態詞嵌入(無注意力層)
Model2Vec 將句子轉換器的知識蒸餾為靜態詞彙嵌入。與 BERT、MiniLM 及其他 transformer 模型對輸入執行注意力層不同,Model2Vec 透過對預先計算的詞彙嵌入進行加權平均來產生向量。3 實際效果是:embedding 速度比基於 transformer 的模型快 50-500 倍,因為不需要序列計算。
在 MTEB 基準測試套件中,potion-base-8M 達到 all-MiniLM-L6-v2 89% 的效能表現(平均分數 50.03 對比 56.09)。4 這 11% 的品質差距是速度與簡潔性優勢的代價。對於較短的 markdown 區塊(典型知識庫中平均 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]
延遲載入。 模型在首次使用時才載入,而非在匯入時。當檢索器以 BM25 純文字回退模式運作時(例如未安裝 embedding 虛擬環境),匯入 embedder 模組不會產生任何開銷。
隔離虛擬環境。 模型在專用的虛擬環境中執行(例如 ~/.claude/venvs/memory/),以避免與工具鏈其餘部分產生依賴衝突。_activate_venv() 函式在執行時期將虛擬環境的 site-packages 加入 sys.path。
# Create isolated venv
python3 -m venv ~/.claude/venvs/memory
~/.claude/venvs/memory/bin/pip install model2vec
批次處理。 Embedder 以 64 個為一批處理文字,以攤平 Model2Vec 的額外開銷。索引器將區塊傳送給 embed_batch() 而非逐一嵌入單個區塊。
何時選擇替代方案
| 模型 | 維度 | 大小 | 速度 | 品質(MTEB) | 最適用情境 |
|---|---|---|---|---|---|
| potion-base-8M | 256 | 30 MB | 500x | 50.03 | 預設:本機執行、快速、無需 GPU |
| potion-base-32M | 256 | 120 MB | 400x | 52.46 | 更高品質,仍為靜態模型 |
| potion-retrieval-32M | 256 | 120 MB | 400x | 36.35(檢索) | 針對檢索最佳化的靜態模型 |
| all-MiniLM-L6-v2 | 384 | 80 MB | 1x | 56.09 | 更高品質,仍為本機執行 |
| 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-32M 當您想要比 potion-base-8M 更高的品質,但不想離開靜態嵌入模型家族時。此模型於 2025 年 1 月發布,使用從 baai/bge-base-en-v1.5 蒸餾而來的更大詞彙表,MTEB 平均分數達到 52.46(比 potion-base-8M 提升 5%),同時維持相同的 256 維輸出及僅依賴 numpy。18 模型檔案增大 4 倍會提高記憶體使用量,但 embedding 速度仍比 transformer 模型快數個數量級。
選擇 potion-retrieval-32M 當您的主要用途是檢索時(知識庫搜尋正是如此)。此變體從 potion-base-32M 專門針對檢索任務進行微調,在 MTEB 檢索基準測試中得分 36.35,而基礎模型為 33.52。18 整體 MTEB 平均分數下降至 49.73,因為微調犧牲了通用效能以換取檢索專用的提升。
選擇 all-MiniLM-L6-v2 當檢索品質比速度更重要,且您已安裝 PyTorch 時。384 維向量使 SQLite 資料庫大小比 256 維向量增加約 50%。在 M 系列硬體上,完整重新索引 15,000 個檔案的 embedding 速度從不到 1 分鐘降至約 10 分鐘。
選擇 nomic-embed-text-v1.5 當您需要最佳的本機檢索品質,並接受較慢的索引速度時。768 維向量大約使資料庫大小增為三倍。需要 PyTorch 及現代 CPU 或 GPU。
選擇 text-embedding-3-small 當網路延遲和隱私是可接受的取捨時。API 產生最高品質的嵌入,但引入了雲端依賴、按 token 計費(每百萬 token 0.02 美元),且會將您的內容傳送至 OpenAI 的伺服器。
在所有其他情況下,請繼續使用 potion-base-8M。 速度優勢對於迭代式索引(開發期間重新索引)至關重要,僅依賴 numpy 避免了 PyTorch 安裝的複雜性,256 維向量也讓資料庫保持精簡。
量化與降維
Model2Vec v0.5.0+ 支援以降低精度和維度載入模型。18 這適用於在受限硬體上部署或在不更換模型的情況下縮減資料庫大小:
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 風格截斷——前 N 個維度承載最多資訊。將 256 維降至 128 維可將向量儲存減半,對短文字檢索的品質影響極小。
截至 2025 年 5 月,Model2Vec 也支援 BPE 和 Unigram 分詞器(除 WordPiece 外),這擴展了可被蒸餾為靜態模型的句子轉換器範圍。20
針對知識庫的自訂 Embedding 微調
Model2Vec v0.4.0+ 支援在靜態嵌入之上訓練自訂分類模型,v0.7.0 新增了詞彙量化和可設定的蒸餾池化方式。20 這對於包含專業詞彙的知識庫(醫療筆記、法律參考、領域特定術語)特別有用,因為預設的 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")
對大多數知識庫而言,預設的 potion-base-8M 即可產生足夠的檢索品質。只有當檢索持續遺漏通用模型無法捕捉的領域特定關聯時,微調才值得進行。
模型雜湊追蹤
索引器會儲存一個由模型名稱和詞彙表大小衍生的雜湊值。若您更換 embedding 模型,索引器會在下次增量執行時偵測到不匹配,並自動觸發完整重新索引。
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 下載模型。若下載失敗(網路問題、企業防火牆),檢索器會回退至純 BM25 模式。模型在首次下載後會快取於本機。
維度不匹配。 若您在未清除資料庫的情況下更換模型,已儲存的向量與新嵌入的維度不同。索引器會透過模型雜湊偵測此情況並觸發完整重新索引。若雜湊檢查失敗(自訂模型未正確設定雜湊),sqlite-vec 將在維度不匹配的 KNN 查詢時報錯。
大型知識庫的記憶體壓力。 在單一批次中嵌入 50,000 個以上的區塊可能會消耗大量記憶體。索引器以 64 個為一批處理以限制尖峰記憶體使用量。若記憶體仍然不足,請縮減批次大小。
使用 FTS5 的全文搜尋
SQLite 的 FTS5 擴充功能提供了具備 BM25 排名的全文搜尋。FTS5 是混合檢索(hybrid retrieval)管線中的關鍵字搜尋元件。本節涵蓋 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。
欄位。 三個欄位被建立索引:
- chunk_text — 每個區塊的主要內容(BM25 權重:1.0)
- section — H2 標題文字(BM25 權重:0.5)
- heading_context — 筆記標題、標籤和中繼資料(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)有描述性強的標題且能準確預測內容品質,請提高 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 在查詢使用與儲存內容不同術語時會失敗:
- 查詢:「how to handle authentication failures」→ 知識庫中包含關於「login error recovery」和「session expiration handling」的筆記。BM25 無法匹配,因為關鍵字不同。
- 查詢:「what is the best way to manage state」→ 知識庫中包含關於「Redux store patterns」和「context providers」的筆記。BM25 未能匹配,因為「狀態管理」是透過特定技術名稱來表達的。
BM25 在大規模關鍵字碰撞時也會失敗。在擁有 15,000 個檔案的知識庫中,搜尋「configuration」會匹配數百篇筆記,因為幾乎每篇專案筆記都提及 configuration。結果在技術上是正確的,但在實用上毫無意義——排名無法判斷哪篇「configuration」筆記與當前查詢相關。
FTS5 分詞器
FTS5 預設使用 unicode61 分詞器,可處理 ASCII 和 Unicode 文字。對於包含大量 CJK(中文、日文、韓文)內容的知識庫,請考慮使用 trigram 分詞器:
-- For CJK-heavy vaults
CREATE VIRTUAL TABLE chunks_fts USING fts5(
chunk_text, section, heading_context,
content=chunks, content_rowid=id,
tokenize='trigram'
);
預設的 unicode61 分詞器以詞邊界進行分割,這對於詞與詞之間沒有空格的語言效果不佳。trigram 分詞器每三個字元進行分割,能實現子字串匹配,但代價是索引大小增加(約為 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 的向量搜尋
sqlite-vec 擴充功能將向量 KNN(K-Nearest Neighbors,K 近鄰)搜尋帶入 SQLite。本節涵蓋 sqlite-vec 的設定、從筆記到可搜尋向量的嵌入(embedding)管線,以及特定的查詢模式。
sqlite-vec 虛擬資料表
CREATE VIRTUAL TABLE chunk_vecs USING vec0(
id INTEGER PRIMARY KEY,
embedding float[256]
);
vec0 模組以打包的二進位資料儲存 256 維浮點向量。id 欄位與 chunks 資料表一對一對應,使向量結果能與區塊中繼資料進行聯結查詢。
嵌入管線
管線從筆記流向可搜尋的向量:
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 查詢
向量搜尋查詢會先將輸入查詢進行嵌入,然後依餘弦距離找出 K 個最近的區塊:
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 運算子執行近似最近鄰搜尋。k 參數控制回傳的結果數量。distance 欄位包含餘弦距離(0 = 完全相同,2 = 完全相反)。
向量搜尋擅長的情境
向量搜尋在概念比特定詞彙更重要的查詢中表現出色:
- 查詢:「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」的筆記(相同概念透過實作細節來表達)
向量搜尋失敗的情境
向量搜尋在處理精確識別符時表現不佳:
- 查詢:
_rrf_fuse→ 回傳關於「fusion algorithms」和「rank merging」的筆記,但可能將實際的函式定義排名低於概念性討論 - 查詢:
PostToolUse→ 回傳關於「tool lifecycle hooks」和「post-execution handlers」的筆記,而非特定的 hook 名稱
向量搜尋在處理結構化資料時也表現不佳。JSON 設定檔、YAML 區塊和程式碼片段產生的嵌入捕捉的是結構模式而非語義意義。一個包含 "review": true 的 JSON 檔案,其嵌入結果與一篇關於程式碼審查的散文討論截然不同。
優雅降級
如果 sqlite-vec 載入失敗(缺少擴充功能、不相容的平台、損壞的函式庫),檢索器會退回到僅使用 BM25 搜尋:
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
檢索器在嘗試向量查詢前會檢查 vec_available。當停用時,所有搜尋僅使用 BM25,並跳過 RRF 融合步驟。
Reciprocal Rank Fusion(RRF)
RRF合併兩個排名清單,無需進行分數校準。本節涵蓋演算法、一個完整的查詢追蹤範例、k參數的調校,以及為何選擇RRF而非其他替代方案。如需互動式計算器(可編輯排名、情境預設及視覺化架構瀏覽器),請參閱hybrid retriever深入探討。
演算法
RRF僅根據文件在各清單中的排名位置來分配分數:
score(d) = Σ (weight_i / (k + rank_i))
其中:
- k是平滑常數(60,依據Cormack等人的研究1)
- rank_i是文件在結果清單i中以1為起始的排名
- weight_i是可選的各清單權重乘數(預設為1.0)
在多個清單中排名良好的文件會獲得較高的融合分數。僅出現在單一清單中的文件則僅從該來源獲得分數。
為何選擇RRF而非替代方案
加權線性組合需要將BM25分數與cosine similarity距離進行校準。BM25分數無上界且隨語料庫規模而變化。餘弦距離的範圍為[0, 2]。結合兩者需要正規化,而正規化參數取決於資料集。RRF僅使用排名位置,無論評分方法為何,排名始終是從1開始的整數。
學習式融合模型需要標註的訓練資料——查詢與文件的相關性配對。對於個人知識庫而言,這樣的訓練資料並不存在。您需要手動判斷數百個查詢與文件的配對才能訓練出有用的模型。RRF無需任何訓練資料即可運作。
Condorcet投票方法(Borda count、Schulze method)在理論上優雅但實作和調校更為複雜。原始RRF論文證明RRF在TREC評估資料上的表現優於Condorcet方法。1
實際融合範例
查詢:”how does the review aggregator handle disagreements”
BM25將review-aggregator.py排在第3位(精確關鍵字匹配”review”、”aggregator”、”disagreements”),但將兩個設定檔排在更前面(它們更顯著地匹配”review”)。向量搜尋將同一區塊排在第1位(語義匹配衝突解決)。經RRF融合後:
| 區塊 | BM25 | Vec | 融合分數 |
|---|---|---|---|
| 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 |
在兩個清單中排名良好的區塊會浮現至頂部。僅出現在單一清單中的區塊獲得單一來源分數,排在雙重排名結果之後。實際的分歧解決邏輯勝出,因為兩種方法都找到了它——BM25透過關鍵字,向量搜尋透過語義。
如需完整的逐步追蹤及每個排名的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倍)。適用於您信任個別排序器能正確找到最佳結果的情況。
- 預設k(60):平衡。排名1的分數為1/61 = 0.0164,排名10的分數為1/70 = 0.0143(相差1.15倍)。排名差異被壓縮,賦予同時出現在多個清單中更大的權重。
- 較高的k(例如200):同時出現在兩個清單中的重要性遠大於排名位置。排名1的分數為1/201,排名10的分數為1/210——幾乎相同。適用於個別排序器產生雜訊排名,但跨清單一致性可靠的情況。
建議從k=60開始。原始RRF論文發現此值在不同TREC資料集上具有穩健性。僅在針對您自己的查詢分佈測量失敗案例後才進行調校。
平手處理
當兩個區塊具有相同的RRF分數時(罕見但在同一清單中排名相同且未出現在另一清單中時可能發生),按以下規則決定順序:
- 優先選擇同時出現在兩個清單中的區塊,而非僅出現在單一清單中的區塊
- 在同時出現於兩個清單的區塊中,優先選擇合併排名較低的
- 在僅出現於單一清單的區塊中,優先選擇在該清單中排名較低的
完整的檢索管線
本節追蹤一個查詢從輸入到輸出經過整個管線的過程:BM25 搜尋、向量搜尋、RRF 融合、token 預算截斷,以及上下文組裝。
端對端流程
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
總延遲:約23毫秒,在 Apple M3 Pro 硬體上對 49,746 個區塊的資料庫進行查詢。
搜尋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 參數防止檢索器回傳超過 AI 工具可使用量的上下文。此估算使用每個 token 4 個字元的比例(對英文文章而言是合理的近似值)。結果以貪婪方式截斷:按排名順序加入結果,直到預算用盡為止。
這是一種保守策略。更精密的做法會考慮每個結果的品質分數,並優先選擇較短但品質較高的結果,而非較長但品質較低的結果。貪婪方法較為簡單,且在實務中運作良好,因為 RRF 排名已經按相關性排序結果。
資料庫結構(完整版)
-- 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
檢索器在初始化時檢查可用功能並調整查詢策略。缺少某個元件會降低品質,但不會導致錯誤。唯一的硬性失敗情況是資料庫檔案遺失。
正式環境統計數據
在包含 16,894 個檔案、49,746 個區塊、83 MB SQLite 資料庫的知識庫上測量,硬體為 Apple M3 Pro:
| 指標 | 數值 |
|---|---|
| 總檔案數 | 16,894 |
| 總區塊數 | 49,746 |
| 資料庫大小 | 83 MB |
| BM25 查詢延遲(p50) | 12ms |
| 向量查詢延遲(p50) | 8ms |
| RRF 融合延遲 | 3ms |
| 端對端搜尋延遲(p50) | 23ms |
| 完整重新索引時間 | 約4分鐘 |
| 增量重新索引時間 | <10秒 |
| Embedding 模型 | potion-base-8M(256維) |
| BM25 候選池 | 30 |
| 向量候選池 | 30 |
| 預設結果上限 | 10 |
| 預設 token 預算 | 4,000 tokens |
內容雜湊與變更偵測
索引器需要知道自上次索引執行以來哪些檔案已變更。本節涵蓋變更偵測機制與雜湊策略。
檔案修改時間比對
索引器為 chunks 表中的每個區塊儲存 mtime_ns(以奈秒為單位的檔案修改時間)。在增量執行時,索引器會:
- 掃描知識庫中允許資料夾內的所有
.md檔案 - 從檔案系統讀取每個檔案的
mtime_ns - 與資料庫中儲存的
mtime_ns進行比對 - 識別三種類別:
- 新檔案: 路徑存在於檔案系統但不在資料庫中
- 已變更檔案: 路徑同時存在於兩處但
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 而非內容雜湊
內容雜湊(檔案內容的 SHA-256)比 mtime 比對更可靠——它能偵測到檔案被觸及但未實際變更的情況(例如 git checkout 恢復了原始 mtime)。然而,雜湊需要在每次增量執行時讀取每個檔案。對於 16,894 個檔案,讀取檔案內容需要 2-3 秒。從檔案系統讀取 mtime 則不到 100 毫秒。
權衡取捨:mtime 比對偶爾會觸發對未變更檔案的不必要重新索引(誤報),但絕不會遺漏實際的變更。誤報僅會在每次執行時多花費幾次額外的 embedding 呼叫。速度差異(100 毫秒對比 3 秒)使得 mtime 成為在每次 AI 互動時執行的系統中務實的選擇。
處理刪除
當檔案從知識庫中刪除時,索引器會移除資料庫中該檔案的所有區塊:
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],
)
FTS5 內容同步表需要透過 INSERT INTO chunks_fts(chunks_fts, rowid, ...) VALUES('delete', ?, ...) 明確刪除每個移除的列。索引器在檔案移除流程中處理此操作。
增量索引與完整重新索引
索引器支援兩種模式:增量(快速,日常使用)和完整(較慢,偶爾使用)。本節涵蓋各模式的使用時機、冪等性保證以及損壞復原。
增量重新索引
使用時機: 編輯筆記後的日常索引。這是預設模式。
運作方式: 1. 掃描 vault 中的檔案變更(mtime 比較) 2. 刪除已刪除檔案的區塊 3. 重新分塊並重新產生已變更檔案的 embeddings 4. 為新檔案插入新區塊 5. 同步 FTS5 索引
典型耗時: 在 16,000 個檔案的 vault 上,一天的編輯量不到 10 秒。
python index_vault.py --incremental
完整重新索引
使用時機: - 更換 embedding 模型後(偵測到模型雜湊不符) - 結構描述遷移後(新欄位、變更的索引) - 資料庫損壞後(完整性檢查失敗) - 增量索引產生非預期結果時
運作方式: 1. 刪除所有現有資料(區塊、向量、FTS5 條目) 2. 掃描整個 vault 3. 對所有檔案進行分塊 4. 對所有區塊產生 embeddings 5. 從頭建立 FTS5 索引
典型耗時: 在 Apple M3 Pro 上處理 16,894 個檔案約需 4 分鐘。
python index_vault.py --full
冪等性
兩種模式都具有冪等性:執行同一指令兩次會產生相同結果。索引器會在插入新區塊之前刪除該檔案的現有區塊,因此對已是最新狀態的資料庫重新執行增量索引不會產生任何變更。重新執行完整索引則會產生完全相同的資料庫。
損壞復原
如果 SQLite 資料庫損壞(寫入期間斷電、磁碟錯誤、交易進行中程序被終止):
# Check integrity
sqlite3 vectors.db "PRAGMA integrity_check;"
# If corruption detected, full reindex rebuilds from source files
python index_vault.py --full
真正的資料來源永遠是 vault 檔案,而非資料庫。資料庫是衍生產物,可以隨時重建。這是一個關鍵的設計特性:您永遠不需要備份資料庫。
--incremental 旗標
當索引器以 --incremental 執行時:
- 模型雜湊檢查。 比較已儲存的模型雜湊與目前模型。若不同,則自動切換為完整重新索引模式並警告使用者。
- 檔案掃描。 遍歷允許的資料夾,收集檔案路徑和 mtime。
- 變更偵測。 與已儲存的資料進行比較。
- 批次處理。 以每批 64 個的方式重新分塊並重新產生已變更檔案的 embeddings。
- 進度回報。 輸出已處理的檔案數量與經過時間。
- 優雅關閉。 處理 SIGINT 訊號時會完成目前檔案後再停止。
憑證過濾與資料邊界
個人筆記中包含機密資訊:API 金鑰、Bearer 權杖、資料庫連線字串、除錯過程中貼上的私鑰。憑證過濾器可防止這些內容進入檢索索引。
問題所在
一則關於除錯 OAuth 整合的筆記可能包含:
The token was: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
I used this curl command:
curl -H "Authorization: Bearer sk-ant-api03-abc123..."
如果沒有過濾,JWT 和 API 金鑰都會被分塊、產生 embeddings 並儲存在資料庫中。搜尋「authentication」會傳回包含真實機密資訊的區塊。更糟的是,如果檢索器透過 MCP 將結果提供給 AI 工具,這些機密資訊會出現在 AI 的上下文視窗中,甚至可能出現在工具的日誌中。
基於模式的過濾
憑證過濾器在每個區塊儲存之前執行,匹配 25 種供應商特定模式加上通用模式:
供應商特定模式:
| 模式 | 範例 | 正規表達式 |
|---|---|---|
| OpenAI API 金鑰 | sk-... |
sk-[a-zA-Z0-9_-]{20,} |
| Anthropic API 金鑰 | 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 金鑰 | sk_live_... |
[sr]k_(live\|test)_[a-zA-Z0-9]{24,} |
| Cloudflare 權杖 | ... |
多種模式 |
通用模式:
| 模式 | 偵測方式 |
|---|---|
| JWT 權杖 | eyJ[a-zA-Z0-9_-]+\.eyJ[a-zA-Z0-9_-]+ |
| Bearer 權杖 | Bearer\s+[a-zA-Z0-9_\-\.]+ |
| 私鑰 | -----BEGIN (RSA\|EC\|OPENSSH) PRIVATE KEY----- |
| 高熵值 base64 | 每字元熵值 >4.5 位元、40 個以上字元的字串 |
| 密碼指派 | 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 之前過濾。 被清理後的文字才是產生 embedding 的對象。向量表示永遠不會編碼憑證模式。查詢「API 金鑰」會傳回討論 API 金鑰管理的筆記,而非包含實際金鑰的筆記。
-
替換而非移除。
[REDACTED:pattern-name]標記保留了周圍文字的語意上下文。embedding 會捕捉到「此處原本有類似憑證的內容」,但不會編碼憑證本身。 -
記錄模式而非值。 過濾器會記錄匹配到的模式(例如「從 oauth-debug.md 中清除了 2 個憑證 [jwt, bearer-token]」),但絕不記錄憑證的值。
基於路徑的排除
.indexignore 檔案提供基於路徑的粗粒度排除。憑證過濾器則提供已索引檔案內的細粒度清除。兩者都是必要的:
.indexignore用於排除您確知包含敏感內容的整個資料夾(健康筆記、財務記錄、職涯文件)- 憑證過濾器用於處理意外嵌入在可索引內容中的機密資訊
資料分類
對於包含多元內容的 vault,請考慮依敏感度對筆記進行分類:
| 層級 | 範例 | 是否索引? | 是否過濾? |
|---|---|---|---|
| 公開 | 部落格草稿、技術筆記 | 是 | 是 |
| 內部 | 專案計畫、架構決策 | 是 | 是 |
| 敏感 | 薪資資料、健康記錄 | 否(.indexignore) | 不適用 |
| 限制 | 憑證、私鑰 | 否(.indexignore) | 不適用 |
MCP Server 架構
Model Context Protocol(MCP)伺服器將檢索器以工具形式公開,供 AI 代理呼叫。本節涵蓋伺服器設計、功能範圍及權限邊界。
協定選擇:STDIO 與 HTTP
MCP 支援兩種傳輸模式:
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 授權、結構化工具輸出(型別化回傳結構描述)及引導功能(伺服器主動發起的使用者提示)。2025年11月的版本正式推出 Streamable HTTP 作為一級傳輸模式、
.well-knownURL 探索機制以自動瀏覽伺服器功能、結構化工具註解用於宣告工具為唯讀或具變動性,以及 SDK 層級標準化系統。1619 下一版規格(暫定2026年中)提議新增長時間執行任務的非同步操作、針對醫療和金融等產業的領域特定協定擴充,以及多代理工作流程的代理間通訊標準。19 對於個人知識庫伺服器,STDIO 仍是最簡單的途徑。Streamable HTTP 傳輸和.well-known探索主要惠及具有多租戶路由和負載平衡的企業 HTTP 部署。請關注 MCP 發展藍圖,以掌握影響您傳輸選擇的更新。
功能設計
MCP 伺服器應公開最精簡的工具集:
search — 主要工具。執行混合檢索並回傳排序結果。
{
"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 伺服器應執行嚴格的邊界控制:
-
唯讀。伺服器讀取知識庫和索引資料庫,不建立、修改或刪除筆記。寫入操作(擷取新筆記)由獨立的 hooks 或 skills 處理,而非 MCP 伺服器。
-
知識庫範圍限定。伺服器僅讀取已設定知識庫路徑內的檔案。路徑遍歷嘗試(
../../etc/passwd)必須被拒絕。 -
憑證過濾輸出。即使資料庫包含預先過濾的內容,仍應在輸出時套用憑證過濾作為縱深防禦措施。
-
權杖限制回應。對所有工具回應執行
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 整合
Hooks 在定義的生命週期節點擴展 Claude Code 的行為。兩個 hooks 與 Obsidian 整合相關:
PreToolUse hook — 在代理處理工具呼叫前查詢知識庫,自動注入相關上下文。
#!/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 — 將重要的工具輸出擷取回知識庫,供未來檢索使用。
#!/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 模式
橋接模組提供 Python API,供 hooks 和 skills 呼叫:
# 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
用於將洞察擷取回知識庫的 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 的上下文視窗:
- 將每次查詢注入的上下文限制在1,500至2,000個權杖。超過此量會與代理的工作記憶競爭。
- 包含來源標註。始終包含檔案路徑和章節標題,以便代理可以引用來源。
- 截斷區塊文字。過長的區塊應以
...截斷而非完全省略。前300至500個字元通常包含關鍵資訊。 - 不要在每次工具呼叫時都注入。PreToolUse hook 應根據呼叫的工具選擇性地注入上下文。讀取操作不需要知識庫上下文,寫入和編輯操作則能從中受益。
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 以取得專案層級的指令。請加入知識庫搜尋的相關指引:
## 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 不支援 hooks。自動注入上下文的模式(PreToolUse hook)無法使用。取而代之的是,您需要在 AGENTS.md 中明確加入指令,告知代理在開始工作前先搜尋知識庫。
Cursor 及其他工具
Cursor 以及其他支援 MCP 的 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 檔案可以加入使用知識庫的指示:
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 | 設定介面 |
非 MCP 工具的替代方案
對於不支援 MCP 的工具,可以將檢索器封裝為 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 快取
知識庫中的結構化筆記可作為可重複使用的上下文區塊,減少 AI 互動中的 token 消耗。本節涵蓋快取鍵設計與 token 預算管理。
模式說明
與其在每次互動時搜尋上下文,不如預先從結構良好的知識庫筆記建構上下文區塊並加以快取:
# 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
},
}
快取失效
快取失效依據兩個訊號:
- TTL 到期。 每個上下文區塊都有存活時間。當 TTL 到期時,系統會重新查詢知識庫來重建該區塊。
- 知識庫變更偵測。 當索引器偵測到構成已快取上下文區塊的檔案有所變更時,該區塊會立即失效。
Token 預算管理
一個工作階段從總上下文預算開始。已快取的區塊會消耗部分預算:
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)
已快取的區塊在工作階段開始時載入。動態搜尋結果則依每次查詢填入剩餘預算。這種混合方式讓代理擁有常用上下文的基線,同時保留預算以應對特定查詢。
使用前後的 Token 消耗比較
未使用快取: 每次相關查詢都會觸發知識庫搜尋,回傳 1,500-2,000 個 token 的上下文。在一個工作階段中進行 10 次查詢,代理會消耗 15,000-20,000 個 token 的知識庫上下文。
使用快取: 三個預建的上下文區塊共消耗 4,500 個 token。額外搜尋每次獨特查詢增加 1,500-2,000 個 token。在 10 次查詢中有 6 次被快取區塊涵蓋的情況下,代理消耗 4,500 +(4 × 1,500)= 10,500 個 token——大約是未快取用量的一半。
用於上下文壓縮的 PostToolUse Hooks
工具輸出可能非常冗長:堆疊追蹤、檔案列表、測試結果。PostToolUse hook 可以在這些輸出佔用上下文視窗空間之前進行壓縮。
問題所在
一個執行測試的 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 個通過、1 個失敗。
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 關鍵字 |
計算通過/失敗數量,僅顯示失敗項目 |
| 檔案列表 | 指令中含 ls 或 find |
截取前 20 項並附上總數 |
| 堆疊追蹤 | Traceback 關鍵字 |
保留第一個和最後一個框架及錯誤訊息 |
| Git 狀態 | modified: / new file: |
依狀態彙總數量 |
| 建置輸出 | warning: / error: |
移除資訊行,保留警告及錯誤 |
信號攝取與分流管線
攝取層決定哪些內容進入知識庫。若缺乏篩選機制,知識庫會累積大量雜訊。本節涵蓋將信號路由至領域資料夾的評分管線。
來源
信號來自多種管道:
- RSS 訂閱:技術部落格、資安公告、版本發布說明
- 書籤:透過 Obsidian Web Clipper 或書籤小工具儲存的瀏覽器書籤
- 電子報:電子郵件電子報中的重點摘錄
- 手動擷取:閱讀、對話或研究過程中撰寫的筆記
- 工具輸出:透過 hooks 擷取的重要 AI 工具輸出
評分維度
每個信號依四個維度評分(各為 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
生產環境統計數據
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 同時追蹤正向連結和 backlink:
- 正向連結:筆記 A 包含
[[Note B]]→ A 連結至 B - Backlink:筆記 B 顯示筆記 A 引用了它
圖譜根據上下文編碼不同類型的關聯:
| 連結模式 | 語義 | 範例 |
|---|---|---|
| 行內連結 | 「與…相關」 | “See [[OAuth Token Rotation]] for details” |
| 標題連結 | 「具有子主題」 | ”## Related\n- [[Token Rotation]]\n- [[Session Management]]” |
| 標籤式連結 | 「被歸類為」 | ”[[type/reference]]” |
| MOC 連結 | 「屬於…的一部分」 | Maps of Content 筆記列出相關筆記 |
Maps of Content(MOC)
MOC 是索引筆記,將相關筆記組織成可導覽的結構:
---
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]]
MOC 以兩種方式提升檢索效果:
- 直接匹配。搜尋「authentication overview」時會匹配到 MOC 本身,為代理提供一份精選的相關筆記清單。
- 上下文擴展。找到特定筆記後,檢索器可以檢查該筆記是否出現在任何 MOC 中,並將 MOC 的結構納入結果,為代理提供更廣泛主題的全貌。
用於上下文擴展的圖譜遍歷
檢索器的未來增強方向:在找到最佳結果後,透過追蹤連結來擴展上下文:
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)
這尚未在目前的檢索器中實作,但代表圖譜結構的自然延伸。
反模式
孤立叢集。一組筆記彼此互相連結,但與知識庫其餘部分沒有任何連接。Obsidian 的圖譜面板會將這些顯示為斷開的孤島。孤立叢集表示缺少 MOC 或缺少跨領域連結。
標籤蔓生。不一致地使用標籤,或建立過多細粒度的標籤。一個在 5,000 篇筆記中有 500 個獨特標籤的知識庫,平均每 10 個標籤僅有 1 篇筆記——這些標籤對過濾毫無用處。整合為 20-50 個對應領域資料夾的高階標籤。
連結密集、內容稀薄的筆記。完全由 wiki-link 組成而沒有任何文字說明的筆記。這類筆記的索引效果很差,因為分塊器沒有可供 embedding 的文字。至少添加一段說明為何這些被連結的筆記彼此相關的上下文。
對所有引用都使用雙向連結。並非每個引用都需要成為 wiki-link。順帶提及「OAuth」不需要使用 [[OAuth 2.0 Overview]]。將 wiki-link 保留給有意圖的、可導覽的關聯——即點擊連結能提供有用上下文的情境。
開發者工作流程實戰方案
結合知識庫檢索與日常開發任務的實用工作流程。
早晨上下文載入
透過載入相關上下文開始新的一天:
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
擷取的洞見會立即被索引,可供未來檢索使用。經過數月累積,這些微型擷取會建構出一套針對實作細節的知識語料庫。
專案啟動
開始新專案或新功能時:
- 搜尋知識庫:「我對[技術/模式]了解多少?」
- 審閱前 5 筆結果,回顧先前的決策和注意事項
- 檢查該領域是否已有 MOC;若無,則建立一個
- 搜尋失敗模式:「[技術]的問題」
使用知識庫搜尋進行除錯
遇到錯誤或非預期行為時:
Search my vault for [error message or symptom]
先前的除錯筆記通常包含根本原因和修復方法。這對跨專案的重複問題特別有價值——知識庫記住了您所遺忘的事物。
程式碼審查準備
在審查 PR 之前:
Search my vault for patterns and conventions about [module being changed]
知識庫會回傳與正在審查的程式碼相關的先前決策、架構約束和編碼標準。審查是基於組織知識進行的,而不僅僅是差異比較。
效能調校
本節涵蓋針對不同知識庫規模與使用模式的最佳化策略。
索引大小管理
| 知識庫規模 | 區塊數 | 資料庫大小 | 完整重建索引 | 增量更新 |
|---|---|---|---|---|
| 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 篇時,請考慮: - 將批次大小從 64 增加到 128 以加速 embedding 處理 - 使用 WAL 模式(預設)以支援並行存取 - 在離峰時段執行完整重建索引
查詢最佳化
WAL 模式。 SQLite 的 Write-Ahead Logging 模式可在索引器寫入時允許並行讀取:
db.execute("PRAGMA journal_mode=WAL")
當 MCP 伺服器在索引器執行增量更新的同時處理查詢時,這一點至關重要。
連線池。 MCP 伺服器應重複使用資料庫連線,而非每次查詢都開啟新連線。搭配 WAL 模式的單一長期連線即可支援並行讀取。
# 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 區段,降低後續搜尋的查詢延遲。
擴展性基準測試
測試環境:Apple M3 Pro、36 GB RAM、NVMe SSD:
| 操作 | 500 篇筆記 | 5K 篇筆記 | 15K 篇筆記 | 50K 篇筆記 |
|---|---|---|---|---|
| BM25 查詢 | 2ms | 5ms | 12ms | 25ms |
| 向量查詢 | 1ms | 3ms | 8ms | 20ms |
| RRF 融合 | <1ms | <1ms | 3ms | 5ms |
| 完整搜尋 | 3ms | 8ms | 23ms | 50ms |
所有基準測試包含資料庫存取、查詢執行與結果格式化。MCP STDIO 通訊的網路延遲會額外增加 1-2ms。
疑難排解
索引偏移
症狀: 搜尋傳回過時的結果,或遺漏最近新增的筆記。
原因: 新增筆記後增量索引器未執行,或檔案的 mtime 未更新(例如從其他機器同步時保留了原始時間戳記)。
修復方式: 執行完整重建索引:python index_vault.py --full
Embedding 模型更換
症狀: 更換 embedding 模型後,向量搜尋傳回無意義的結果。
原因: 舊的向量(來自先前的模型)正與新的查詢向量進行比較。兩者的維度或向量空間語義不相容。
修復方式: 索引器應偵測到模型雜湊值不符並自動觸發完整重建索引。若未自動執行,請手動清除資料庫並重建索引:
rm vectors.db
python index_vault.py --full
FTS5 維護
症狀: 經過多次增量更新後,FTS5 查詢傳回不正確或不完整的結果。
原因: FTS5 內部區段可能在多次小規模更新後變得碎片化。
修復方式: 重建並最佳化:
INSERT INTO chunks_fts(chunks_fts) VALUES('rebuild');
INSERT INTO chunks_fts(chunks_fts) VALUES('optimize');
MCP 逾時
症狀: AI 工具回報 MCP 伺服器逾時。
原因: 首次查詢會觸發模型載入(延遲初始化),需要 2-5 秒。AI 工具的預設 MCP 逾時時間可能更短。
修復方式: 在伺服器啟動時預先暖機模型:
# In MCP server initialization
retriever = HybridRetriever(db_path, vault_path)
retriever.search("warmup", limit=1) # Trigger model load
SQLite 檔案鎖定
症狀: 出現 SQLITE_BUSY 或 SQLITE_LOCKED 錯誤。
原因: 多個行程同時寫入資料庫。WAL 模式允許並行讀取,但同一時間只允許一個寫入者。
修復方式: 確保只有一個行程(索引器)寫入資料庫。MCP 伺服器和 hooks 應僅進行讀取。若您需要並行寫入,請使用 WAL 模式並設定忙碌逾時:
db.execute("PRAGMA busy_timeout=5000") # Wait up to 5 seconds
sqlite-vec 無法載入
症狀: 向量搜尋已停用;檢索器以純 BM25 模式運作。
原因: sqlite-vec 擴充功能未安裝、在程式庫路徑中找不到,或與 SQLite 版本不相容。
修復方式:
# Install via pip
pip install sqlite-vec
# Or compile from source
git clone https://github.com/asg017/sqlite-vec
cd sqlite-vec && make
驗證擴充功能是否正確載入:
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 = 32 # Reduce from 64
同時確保索引器逐一處理檔案(讀取、分塊、embedding 每個檔案後再處理下一個),而非將所有檔案一次載入記憶體。
遷移指南
從 Apple Notes 遷移
- 透過「全部匯出」選項(macOS)匯出 Apple Notes,或使用
apple-notes-liberator等遷移工具 - 使用
markdownify或pandoc將 HTML 匯出檔轉換為 markdown - 將轉換後的檔案移至知識庫的
00-inbox/資料夾 - 逐一檢查並為每篇筆記新增 frontmatter
- 將筆記移至適當的領域資料夾
從 Notion 遷移
- 從 Notion 匯出:Settings → Export → Markdown & CSV
- 將匯出的壓縮檔解壓至知識庫的
00-inbox/資料夾 - 修正 Notion 特有的 markdown 問題:
- Notion 使用
- [ ]表示核取方塊——這是標準 markdown 語法 - Notion 以 HTML 形式包含屬性表格——請轉換為 YAML frontmatter
- Notion 以相對路徑嵌入圖片——請將圖片複製到您的附件資料夾
- 新增標準 frontmatter(
type、domain、tags) - 將 Notion 頁面連結替換為 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 知識庫開啟該目錄(Obsidian → Open Vault → Open folder)
- 若該目錄已納入版本控制,請將
.obsidian/加入.gitignore - 建立 frontmatter 範本並套用至現有檔案
- 在閱讀和整理的過程中,開始使用
[[wiki-links]]連結筆記 - 立即執行索引器——檢索系統從第一天就能運作
從其他檢索系統遷移
若您正從不同的 embedding/搜尋系統遷移:
- 不要嘗試遷移向量。 不同模型產生的向量空間不相容。請使用新模型執行完整重建索引。
- 遷移內容,而非索引。 知識庫檔案才是唯一真實來源。索引只是衍生產物。
- 遷移後進行驗證。 執行 10-20 個您已知答案的查詢,驗證結果是否符合預期。
更新日誌
| 日期 | 變更 |
|---|---|
| 2026-03-03 | 更新 MCP 規格演進(2025年11月正式發布:Streamable HTTP、.well-known、tool annotations)。新增 Model2Vec 微調及 BPE/Unigram 分詞器支援。新增社群 MCP 伺服器比較表。更新 Smart Connections 至 v4。 |
| 2026-03-02 | 新增 potion-base-32M 與 potion-retrieval-32M 至模型比較。新增量化/降維章節。新增 MCP 規格演進說明。 |
| 2026-03-01 | 初始發布 |
參考文獻
-
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:每百萬 token 0.02 美元。估計整個知識庫完整重新索引的成本:約 0.30 美元。 ↩
-
van Dongen, T. et al. Model2Vec: Turn any Sentence Transformer into a Small Fast Model。arXiv,2025。描述從句子轉換器產生靜態 embeddings 的蒸餾方法。 ↩
-
MTEB: Massive Text Embedding Benchmark。potion-base-8M 平均得分 50.03,相比 all-MiniLM-L6-v2 的 56.09(保留率 89%)。 ↩
-
SQLite FTS5 Extension。FTS5 提供具備 BM25 排序及可設定欄位權重的全文搜尋功能。 ↩
-
sqlite-vec: A vector search SQLite extension。在 SQLite 中提供用於 KNN 向量搜尋的
vec0虛擬表。 ↩ -
Robertson, S. and Zaragoza, H. The Probabilistic Relevance Framework: BM25 and Beyond。Foundations and Trends in Information Retrieval,2009。 ↩
-
Karpukhin, V. et al. Dense Passage Retrieval for Open-Domain Question Answering。EMNLP,2020。密集表示在開放領域問答上的表現超越 BM25 達 9-19%。 ↩
-
Reimers, N. and Gurevych, I. Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks。EMNLP,2019。密集語義相似度的基礎性研究。 ↩
-
Luan, Y. et al. Sparse, Dense, and Attentional Representations for Text Retrieval。TACL,2021。Hybrid 混合檢索在 MS MARCO 上持續優於單一模態方法。 ↩
-
SQLite Write-Ahead Logging。WAL 模式支援並行讀取搭配單一寫入器。 ↩
-
Gao, Y. et al. Retrieval-Augmented Generation for Large Language Models: A Survey。arXiv,2024。RAG架構與分塊策略的綜述。 ↩
-
Thakur, N. et al. BEIR: A Heterogeneous Benchmark for Zero-shot Evaluation of Information Retrieval Models。NeurIPS,2021。 ↩
-
Model2Vec: Distill a Small Fast Model from any Sentence Transformer。Minish Lab,2024。 ↩
-
Obsidian Documentation。Obsidian 官方文件。 ↩
-
Model Context Protocol Specification。連接 AI 工具與資料來源的MCP標準。 ↩
-
作者的生產環境資料。16,894 個檔案、49,746 個區塊、83.56 MB SQLite 資料庫,14 個月內處理了 7,771 個訊號。查詢延遲透過
time.perf_counter()測量。 ↩ -
Model2Vec Potion Models。Minish Lab,2025。Potion-base-32M(MTEB 52.46)、potion-retrieval-32M(MTEB 檢索 36.35),以及 v0.5.0+ 的量化/維度縮減功能。 ↩↩↩
-
Update on the Next MCP Protocol Release。2025 年 11 月版本推出了 Streamable HTTP 傳輸、.well-known URL 探索、結構化工具註解及SDK層級標準化。下一版本暫定於 2026 年中推出,將包含非同步操作、領域特定擴充及代理間通訊。 ↩↩
-
Model2Vec Releases。v0.4.0(2025 年 2 月):訓練/微調支援。v0.5.0(2025 年 4 月):後端重寫、量化、維度縮減。v0.7.0(2025 年 10 月):詞彙量化、BPE/Unigram 分詞器支援。 ↩↩
-
Smart Connections for Obsidian。Smart Connections v4:本地優先的 AI embeddings,語義搜尋在初次索引後可離線運作。 ↩