obsidian:~/vault$ search --hybrid obsidian

サンプル保管庫の場所

# AI基盤としてのObsidian:決定版テクニカルリファレンス

words: 2099 read_time: 67m updated: 2026-04-17 02:05
$ retriever search --hybrid obsidian

重要ポイント

ノートテイキングではなく、コンテキストエンジニアリング。 AI活用におけるObsidian vaultの価値は、ノートそのものではなく、それらを検索可能にする検索レイヤーにあります。16,000ファイルのvaultでも検索機能がなければ、書き込み専用のデータベースに過ぎません。一方、200ファイルのvaultでもhybrid searchとMCP統合があれば、AIナレッジベースとして機能します。検索インフラこそがプロダクトであり、ノートは原材料なのです。

ハイブリッド検索は、キーワード単体・セマンティック単体の検索を上回ります。 BM25は正確な識別子や関数名を捕捉し、ベクトル検索は異なる用語間の同義語や概念的な一致を捕捉します。Reciprocal Rank Fusion(RRF)はスコアのキャリブレーションなしに両者を統合できます。どちらか一方だけでは、両方の失敗パターンをカバーできません。MS MARCOパッセージランキングに関する研究でもこのパターンは確認されており、ハイブリッド検索は単独手法を一貫して上回っています。3 ハイブリッドリトリーバーの詳細解説では、RRFの数学的背景、実数値を用いた具体例、失敗モード分析、インタラクティブな融合計算ツールを取り上げています。

MCPがAIツールにvaultへの直接アクセスを提供します。 Model Context Protocol(MCP)サーバーは、リトリーバーをツールとして公開し、Claude Code、Codex CLI、CursorなどのAIツールから直接呼び出せるようにします。エージェントがvaultに問い合わせると、出典情報付きのランク付けされた結果を受け取り、ファイル全体を読み込むことなくコンテキストを活用できます。MCPサーバーは検索エンジンの薄いラッパーです。

ローカルファーストにより、APIコストがゼロでプライバシーも完全に保護されます。 スタック全体が1台のマシン上で動作します。ストレージにSQLite、エンベディングにModel2Vec、キーワード検索にFTS5、ベクトルKNNにsqlite-vecを使用します。クラウドサービスもAPI呼び出しもネットワーク依存もありません。個人的なノートがマシンの外に出ることは一切ありません。49,746チャンクの完全な再エンベディングはOpenAI APIの料金で約$0.30ですが、真のコストはレイテンシ、プライバシーの露出、そしてオフラインで動作すべきシステムのネットワーク依存性にあります。4

インクリメンタルインデックスにより、システムは10秒以内に最新状態を維持します。 ファイルの更新日時を比較して変更を検出し、変更されたファイルのみを再チャンク化・再エンベディングします。フルリインデックスはApple Mシリーズハードウェアで約4分。日常的な編集のインクリメンタル更新は10秒以内で完了します。手動操作なしにシステムが常に最新の状態を保ちます。

アーキテクチャは200から20,000以上のノートまでスケールします。 同じ3層設計(取り込み、検索、統合)があらゆるvaultサイズで機能します。小規模なvaultではBM25のみの検索から始め、キーワードの衝突が問題になったらベクトル検索を追加し、正確な一致とセマンティックな一致の両方が必要になったらRRFフュージョンを追加します。各レイヤーは独立して有用であり、独立して取り外し可能です。


このガイドの使い方

本ガイドはシステム全体をカバーしています。現在の状況に応じて、最適な出発点を選んでください。

現在の状況 ここから始める 次に探索する
Obsidian + AI初心者 なぜObsidianがAIインフラに適しているのかクイックスタート VaultアーキテクチャMCPサーバーアーキテクチャ
既存のvaultにAIアクセスを追加したい MCPサーバーアーキテクチャClaude Code統合 エンベディングモデル全文検索
検索システムを構築したい 完全な検索パイプラインReciprocal Rank Fusion パフォーマンスチューニングトラブルシューティング
チーム・エンタープライズでの活用 意思決定フレームワークナレッジグラフパターン 開発者ワークフローレシピ移行ガイド

Contractと記されたセクションには、実装の詳細、設定ブロック、失敗モードが含まれています。Narrativeと記されたセクションでは、コンセプト、アーキテクチャの決定、設計選択の背景にある考え方に焦点を当てています。Recipeと記されたセクションでは、ステップバイステップのワークフローを提供しています。


なぜObsidianがAIインフラに適しているのか

本ガイドのテーゼ:Obsidian vaultが個人向けAIナレッジベースに最適な基盤である理由は、ローカルファースト、プレーンテキスト、グラフ構造であり、ユーザーがスタックのすべてのレイヤーをコントロールできるからです。

Obsidianが他のツールにないAI向けの利点を提供する理由

プレーンテキストのmarkdownファイル。 すべてのノートはファイルシステム上の.mdファイルです。独自フォーマットもデータベースエクスポートも、コンテンツを読むためのAPIも不要です。ファイルを読めるツールなら何でもvaultを読めます。grepripgrep、Pythonのpathlib、SQLite FTS5——これらはすべてソースファイルに直接動作します。検索システムを構築する際、インデックスの対象はAPIレスポンスではなくファイルそのものです。ソースがファイルシステムであるため、インデックスは常にソースと一致します。

ローカルファーストアーキテクチャ。 vaultは自分のマシン上に存在します。サーバーもクラウド同期への依存もなく、APIのレート制限も、コンテンツの処理方法を規定する利用規約もありません。外部サービスなしに、ノートのエンベディング、インデックス作成、チャンク化、検索が可能です。AIインフラにとってこれが重要な理由は、検索パイプラインがAPIエンドポイントの応答速度ではなく、ディスクの速度で動作するからです。プライバシーの面でも重要です。認証情報、健康データ、財務情報、個人的な記録を含むノートがマシンの外に出ることはありません。

wiki-linkによるグラフ構造。 Obsidianの[[wiki-link]]構文は、ノート間の有向グラフを作成します。OAuth実装に関するノートは、トークンローテーション、セッション管理、APIセキュリティに関するノートとリンクします。グラフ構造は、概念間の人間がキュレーションした関係性をエンコードしています。ベクトルエンベディングはセマンティックな類似性を捕捉しますが、wiki-linkはトピックについて考えながら著者が意図的に作成したつながりを捕捉します。グラフはエンベディングでは再現できないシグナルなのです。

プラグインエコシステム。 Obsidianには2,500以上のコミュニティプラグインがあります(2026年3月時点、2025年半ばの1,800以上から増加)。Dataviewはvaultをデータベースのようにクエリできます。TemplaterはJavaScriptロジックを使ってテンプレートからノートを生成します。Git統合はvaultをリポジトリに同期します。Linterはフォーマットの一貫性を保ちます。Basesコアプラグイン(v1.9.10で導入)は、frontmatterプロパティをフィールドとして使い、テーブル、ギャラリー、カレンダー、カンバンボードなどのデータベースライクなビューをvaultファイル上に追加し、.baseファイルとして保存されます。27 これらのプラグインは、基盤となるプレーンテキスト形式を変更することなく、vaultに構造を追加します。検索システムがインデックスするのは、プラグインそのものではなく、プラグインの出力です。

500万人以上のユーザー。 Obsidianには大規模で活発なコミュニティがあり、テンプレート、ワークフロー、プラグイン、ドキュメントが制作されています。vaultの整理やプラグインの設定で問題に遭遇した場合、すでに誰かがソリューションを文書化している可能性が高いでしょう。コミュニティはObsidian関連ツール(MCPサーバー、インデックススクリプト、パブリッシングパイプライン、APIラッパーなど)も生み出しています。

ファイルシステム単体では得られないもの

markdownファイルのディレクトリにはプレーンテキストの利点がありますが、Obsidianが追加する3つの要素が欠けています。

  1. 双方向リンク。 Obsidianはbacklinkを自動的に追跡します。ノートAからノートBにリンクすると、ノートBにはノートAが参照していることが表示されます。グラフパネルは接続のクラスターを可視化します。この双方向の認識は、生のファイルシステムでは提供されないメタデータです。

  2. プラグインレンダリング付きライブプレビュー。 Dataviewクエリ、Mermaidダイアグラム、コールアウトブロックがリアルタイムでレンダリングされます。テキストエディタよりもリッチな執筆体験でありながら、保存形式はプレーンテキストのままです。リッチな環境で執筆・整理し、検索システムは生のmarkdownをインデックスします。

  3. コミュニティインフラ。 プラグインの発見、テーママーケットプレイス、同期サービス(オプション)、パブリッシュサービス(オプション)、ドキュメントエコシステム。個々の機能はスタンドアロンツールで再現可能ですが、Obsidianはそれらを一貫したワークフローにパッケージングしています。

Obsidianが提供しないもの(自分で構築するもの)

Obsidianには検索インフラが含まれていません。基本的な検索(全文検索、ファイル名、タグ)はありますが、エンベディングパイプライン、ベクトル検索、フュージョンランキング、MCPサーバー、資格情報フィルタリング、チャンキング戦略、外部AIツールとの統合フックはありません。本ガイドは、Obsidianの上に構築するインフラを扱います。 vaultが基盤であり、検索パイプライン、MCPサーバー、統合フックがインフラです。

ここで説明するアーキテクチャは、markdown優先であり、Obsidian専用ではありません。 Logseq、Foam、Dendron、またはmarkdownファイルのプレーンなディレクトリを使っている場合でも、検索パイプラインは同一に動作します。チャンカーは.mdファイルを読み取り、エンベッダーはテキスト文字列を処理し、インデクサーはSQLiteに書き込みます。これらのコンポーネントはいずれもObsidian固有の機能に依存していません。Obsidianの貢献は、リトリーバーがインデックスするmarkdownファイルを生み出す、執筆・整理環境にあります。


クイックスタート:初めてのAI連携Vault

このセクションでは、5分でVaultをAIツールに接続します。Obsidianのインストール、Vaultの作成、MCPサーバーのインストール、そして最初のクエリ実行までを行います。クイックスタートでは、すぐに結果を確認できるコミュニティMCPサーバーを使用します。本番環境向けのカスタム検索パイプライン構築については、後続のセクションで解説します。

前提条件

  • macOS、Linux、またはWindows
  • Node.js 18+(MCPサーバー用)
  • Obsidian 1.12+(CLI統合用。MCPのみの構成であれば、それ以前のバージョンでも動作します)
  • Claude Code、Codex CLI、またはCursorがインストール済みであること

ステップ1:Vaultを作成する

obsidian.mdからObsidianをダウンロードし、新しいVaultを作成してください。MCPサーバーには絶対パスが必要なため、覚えやすい場所を選びましょう。

# Example vault location
~/Documents/knowledge-base/

検索対象となるノートをいくつか追加します。10〜20件程度のノートがあれば、結果を確認するには十分です。各ノートは.mdファイルとし、意味のあるタイトルと少なくとも1段落のコンテンツを含めてください。

ステップ2:MCPサーバーをインストールする

いくつかのコミュニティMCPサーバーが、即座にVaultへアクセスする手段を提供しています。2025〜2026年にかけてエコシステムは大きく成長しました。特筆すべき最近のアップデートとして、MCPVault v0.11.0(2026年3月)では、frontmatterやハッシュタグをカウント付きでスキャンするlist_all_tagsの追加、ドットフォルダの処理改善、.baseおよび.canvasファイルのサポートが実装されました。25 パッケージ名もnpmで@bitbonsai/mcpvaultに変更されています。

サーバー 作者 トランスポート プラグイン必須 主な特徴
obsidian-mcp-server StevenStavrakis STDIO いいえ 軽量、ファイルベース
mcp-obsidian MarkusPfundstein STDIO ローカルREST API REST経由のフルVault CRUD
obsidian-mcp-tools jacksteamdev STDIO はい(プラグイン) セマンティック検索 + Templater
obsidian-claude-code-mcp iansinnott WebSocket はい(プラグイン) Claude Code向け自動検出
obsidian-mcp-server cyanheads STDIO ローカル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ファイルを読み取り、基本的な検索を実行し、結果を返します。これが最小限の実用的な構成です。

このクイックスタートに含まれないもの: - Hybrid検索(BM25 + ベクトル検索 + RRF fusion) - Embeddingベースのセマンティック検索 - 資格情報によるフィルタリング - インクリメンタルインデックス - フックによる自動コンテキスト注入

これらの機能の構築方法は、本ガイドの残りのセクションで解説します。クイックスタートはコンセプトの実証であり、フルパイプラインが本番品質の検索を実現します。


AI ワークフローのためのObsidian CLI

Obsidian 1.12(2026年2月)では、AIワークフローに新たな統合面を開く組み込みコマンドラインインターフェースが導入されました。28 CLIはObsidian GUIのリモートコントロールとして機能します — Obsidianが起動している必要があります(最初のコマンド実行時に自動起動されます)。設定 > 一般 > コマンドラインインターフェースから有効にしてください。

CLIがAIインフラにとって重要な理由

CLIは、以前はGUIやプラグインAPIを必要としていたObsidianネイティブの操作へのプログラム的なアクセスを提供します。AIワークフローにおける主要な機能は以下の通りです:

  • スクリプトやフックからの検索。 obsidian search "query"およびobsidian search:context "query"により、シェルスクリプト、フック、または自動化パイプラインからVault検索を実行できます。search:contextは前後のコンテキスト付きでマッチ行を返すため、AIプロンプトへの入力に適しています。
  • デイリーノートの自動化。 obsidian dailyで今日のデイリーノートを開くか作成します。シェルスクリプトと組み合わせることで、デイリーブリーフィングの自動化ワークフローが実現できます — フックでAI生成のサマリーをデイリーノートに追記するといった使い方が可能です。
  • テンプレートベースのノート作成。 obsidian template listおよびobsidian template createで、Templaterまたはコアテンプレートからノートを生成できます。AIエージェントがMarkdownファイルを直接書くことなく、構造化されたVaultエントリを作成できるようになります。
  • プロパティ管理。 obsidian property setおよびobsidian property getでfrontmatterプロパティの読み書きが可能です。YAMLを解析せずにスクリプトからメタデータを更新できます。
  • プラグイン制御。 obsidian plugin enable/disable/listでプラグインをプログラム的に管理できます。バッチ処理中にインデックスプラグインを切り替える場合などに便利です。
  • タスク管理。 obsidian task list/add/completeで構造化されたタスクアクセスが可能です。Vault内の作業項目を管理するAIエージェントに有用です。

CLI vs MCP:AIアクセスにおける使い分け

CLIとMCPサーバーは異なる役割を持ち、競合ではなく補完関係にあります:

観点 Obsidian CLI MCPサーバー
呼び出し元 シェルスクリプト、フック、cronジョブ AIエージェント(Claude Code、Codex、Cursor)
プロトコル POSIXプロセス(stdin/stdout/stderr) MCP(STDIO またはHTTP上のJSON-RPC)
強み Obsidianネイティブ操作(テンプレート、プラグイン、プロパティ) カスタム検索(embeddings、BM25、RRF fusion)
制限事項 ベクトル検索なし、embeddingパイプラインなし Obsidian内部操作へのアクセス不可
最適な用途 自動化スクリプト、取り込みパイプライン、フックアクション セッション中のリアルタイムAIエージェントクエリ

推奨: 取り込みの自動化(ノート作成、プロパティ管理、Obsidianネイティブ検索の実行)にはCLIを、検索(embeddingを使ったhybrid検索)にはMCPを使用しましょう。PreToolUseフックでobsidian search:contextを高速な事前チェックとして呼び出し、ランク付けされた結果が必要な場合にフルMCPリトリーバーにフォールバックする構成も可能です。

例:CLIを活用した取り込みフック

#!/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 エージェントプラグイン

Obsidianプラグインの中で成長著しいカテゴリとして、AIコーディングエージェントをVault UIに直接埋め込むものがあります。外部MCPサーバー設定の代替手段として、外部ツールから接続するのではなく、Obsidianのサイドバー内でAIエージェントを実行します。

Claudian

Claudianは、Claude CodeをAIコラボレーターとしてVaultに組み込みます。VaultディレクトリがClaudeの作業ディレクトリとなり、ファイルの読み書き、検索、bashコマンド、マルチステップワークフローといったフルエージェント機能を利用できます。29

AIインフラにおける主な特徴: - コンテキスト認識プロンプト。 フォーカス中のノートを自動添付し、@notenameによるファイル参照、タグベースの除外、エディタ選択範囲のコンテキスト利用をサポートします。 - ビジョンサポート。 ドラッグ&ドロップ、ペースト、またはファイルパスで画像を分析できます — Vaultにキャプチャしたスクリーンショットや図表の処理に便利です。 - スラッシュコマンド。 /commandでトリガーされる再利用可能なプロンプトテンプレートを作成でき、Vault操作を標準化できます。 - パーミッションモード。 YOLO(自動承認)、Safe(各アクション承認)、Plan(計画のみ)の各モードに加え、安全ブロックリストとVault内限定動作を備えています。

Agent Client

Agent Clientは、Agent Client Protocol(ACP)を介してClaude Code、Codex CLI、Gemini CLIを統合されたObsidianサイドバーに統合します。30

主な特徴: - マルチエージェント切り替え。 同一パネルからClaude Code、Codex、Gemini CLIとチャットでき、必要に応じてエージェントを切り替えられます。 - ノート参照。 @notenameでプロンプトにノート内容を含められます。Claudianと同様の機能ですが、エージェントに依存しません。 - シェル実行。 チャット内でターミナルコマンドをインラインで実行できます — 会話を離れることなく、ビルドスクリプト、gitコマンド、その他のターミナル操作が可能です。 - アクション承認。 ファイルの読み取り、編集、コマンド実行に対するきめ細かな制御が可能です。

エージェントプラグイン vs 外部MCP:使い分けの指針

シナリオ エージェントプラグイン 外部MCP
AIアシスタントによるVaultノートの執筆・編集 適している — エージェントがエディタのコンテキストを認識 動作するがエディタ認識なし
複数リポジトリにまたがるコード開発 制限あり — Vaultスコープに限定 適している — プロジェクトスコープでフルファイルシステムアクセス
大規模インデックスコーパスからの検索 基本的な検索のみ フルhybrid検索パイプライン
ノート作成中のVaultへの簡単なQ&A 最適 — コンテキスト切り替え不要 ターミナルへの切り替えが必要

推奨: Vault中心のワークフロー(ノートの執筆、整理、要約)にはエージェントプラグインを使用しましょう。AIエージェントがフル検索パイプラインやVault外のコードベースへのアクセスを必要とする開発ワークフローには、外部MCPサーバーが適しています。両者は共存可能です — ノート作業にはObsidian内でClaudianを、開発には外部のClaude Code + MCPを使い分けるのが効果的です。


判断フレームワーク:Obsidian vs 代替ツール

すべてのユースケースに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が必要 エクスポートが必要 ファイルに直接アクセス すでにコンテキスト内
プラグインエコシステム 2,500以上のプラグイン インテグレーション なし N/A N/A
オフライン対応 完全対応 読み取り専用キャッシュ 部分的 完全対応 完全対応
10,000以上のノートに対応 はい はい(API使用時) 性能低下 はい いいえ(単一ファイル)
コスト 無料(コア機能) $10/月〜 無料 無料 無料

Obsidianが過剰な場合

  • 単一プロジェクトのコンテキスト。 AIが現在のコードベースに関するコンテキストだけを必要とする場合は、CLAUDE.mdAGENTS.md、またはプロジェクトレベルのドキュメントに記載しましょう。これらのファイルはリポジトリと一緒に移動し、自動的に読み込まれます。
  • 構造化データ。 コンテンツがテーブル、レコード、スキーマである場合は、データベースを使用してください。Obsidianのノートは散文が主体です。Dataviewでfrontmatterフィールドをクエリすることは可能ですが、構造化クエリには本格的なデータベースの方が適しています。
  • 一時的なリサーチ。 プロジェクト終了後にノートを破棄する予定であれば、markdownファイルを入れたスクラッチディレクトリの方がシンプルです。一時的なコンテンツのために検索インフラを構築する必要はありません。

Obsidianが最適な場合

  • 数ヶ月から数年にわたる知識の蓄積。 コーパスが成長するにつれて価値は複利的に増加します。200件のノートを含むvaultを6ヶ月間毎日クエリする方が、5,000件のノートを含むvaultを一度だけクエリするよりも大きな価値を生み出します。
  • 一つのコーパスに複数のドメイン。 プログラミング、アーキテクチャ、セキュリティ、デザイン、個人プロジェクトに関するノートを含むvaultは、プロジェクト固有のCLAUDE.mdでは実現できないクロスドメイン検索の恩恵を受けられます。
  • プライバシーに敏感なコンテンツ。 ローカルファーストであるため、検索パイプラインがコンテンツを外部サービスに送信することはありません。クラウドサービスにはアップロードしたくないコンテンツも含め、vault には好きなものを保存できます。

メンタルモデル:3つのレイヤー

このシステムは、独立して動作しながらも組み合わせることで相乗効果を発揮する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にはノイズが蓄積していきます。ツイートのスクリーンショット、注釈のないコピペ記事、コンテキストのない書きかけのメモなどです。取り込みレイヤーは、エントリーポイントにおける品質管理を担当します。スコアリングパイプライン、タグ付け規則、手動レビュープロセスなど、vaultに検索する価値のあるコンテンツが確実に格納されるようにする仕組みであれば何でも構いません。

Retrieval(検索) はvaultをクエリ可能にします。これがエンジンの中核です。ノートを検索単位にチャンキングし、チャンクをベクトル空間にembeddingし、キーワード検索とセマンティック検索のためにインデックスを作成し、RRFで結果を融合します。検索レイヤーは、ファイルのディレクトリをクエリ可能なナレッジベースへと変換します。このレイヤーがなければ、vaultは手動ブラウジングと基本的な検索でしかナビゲートできず、AIツールからプログラムでアクセスすることはできません。

Integration(統合) は検索レイヤーをAIツールに接続します。MCPサーバーは検索機能を呼び出し可能なツールとして公開します。フックはコンテキストを自動的に注入します。スキルは新しい知識をvaultに取り込みます。統合レイヤーは、ナレッジベースとそれを利用するAIエージェントとの間のインターフェースです。

各レイヤーは意図的に疎結合に設計されています。取り込みのスコアリングパイプラインはembeddingについて何も知りません。検索エンジンはシグナルのルーティングルールについて何も知りません。MCPサーバーはノートがどのように作成されたかを知りません。この疎結合により、各レイヤーを独立して改善できます。取り込みパイプラインを変更せずにembeddingモデルを差し替えることも、検索エンジンを変更せずに新しいMCP機能を追加することも、インデックスに手を加えずにシグナルのスコアリングヒューリスティクスを変更することも可能です。


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の文章を含むすべてのフォルダ — プロジェクト、エリア、リソース、シグナル、デイリーノートが該当します。

インデックスから除外すべきフォルダ: テンプレート(プレースホルダー変数のみでコンテンツを含まない)、添付ファイル(バイナリファイル)、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/

インデクサーはスキャン前にこのファイルを読み込み、一致するパスを完全にスキップします。除外されたパス内のファイルはチャンキングされず、embeddingsも生成されず、検索結果にも表示されません。

ノートスキーマ

すべてのノートには 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 — FTS5の見出しコンテキストに0.3の重みでインデックスされ、本文で異なる用語が使われている場合でもキーワードマッチを提供します

任意だが有用なフィールド:

  • domain — ドメインスコープクエリを可能にします(「セキュリティノートのみ検索」など)
  • source — キャプチャしたコンテンツの出典情報。リトリーバーは検索結果にソースURLを含めることができます
  • status — アーカイブ済みや下書きのノートをアクティブな検索から除外できます

チャンキングの設計規約

リトリーバーは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...

3つのH2セクションから、3つの独立して検索可能なチャンクが生成されます。各チャンクには、embeddingsがその意味を適切に捉えるのに十分なコンテキストが含まれています。「期限切れトークンの処理」というクエリは、3番目のチャンクに的確にマッチします。

検索に不向きな構造:

# 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見出しのない1つの長いセクションからは、1つの大きなチャンクしか生成されません。embeddingsはセクション内のすべてのトピックを平均化してしまいます。どのサブトピックについてクエリしても、ノート全体が同等にマッチしてしまうのです。

原則: 1つのセクションが複数の概念を扱う場合は、H2サブセクションに分割してください。チャンキングはチャンカーが自動的に処理します。

ノートに含めるべきでないもの

検索品質を低下させるコンテンツ:

  • 注釈なしで記事全文をコピー&ペーストしたもの。 リトリーバーが元の記事のキーワードをインデックスし、自分が書いていないコンテンツでVaultが希釈されてしまいます。代わりに要約を追加するか、重要なポイントを抽出するか、ソースURLへのリンクを貼りましょう。
  • テキスト説明のないスクリーンショット。 リトリーバーはMarkdownテキストをインデックスします。altテキストや周辺の説明がない画像は、BM25とベクトル検索のどちらからも見えません。
  • 認証情報の文字列。 APIキー、トークン、パスワード、接続文字列などです。認証情報フィルタリングがあるとはいえ、最も安全なアプローチはノートにシークレットを一切貼り付けないことです。代わりに名前で参照してください(「~/.envにあるCloudflareのAPIトークン」など)。
  • キュレーションされていない自動生成コンテンツ。 ツールがノートを生成した場合(会議の文字起こし、Readwiseのハイライト、RSSインポートなど)、永続的なVaultに取り込む前にレビューと注釈を行ってください。キュレーションされていない自動インポートは、検索可能な価値を追加せずに量だけを増やします。

AIワークフロー向けプラグインエコシステム

AI検索のためのVault品質を向上させるObsidianプラグインは、構造系(一貫性の強制)、クエリ系(メタデータの公開)、同期系(Vaultの最新状態の維持)の3カテゴリに分類されます。

必須プラグイン

Dataview。 frontmatterフィールドを使ってVaultをデータベースのようにクエリできます。動的なインデックスを作成しましょう:「過去30日以内に更新されたsecurityタグ付きの全ノート」や「ステータスがactiveの全プロジェクトノート」などが可能です。Dataviewは検索そのものを直接支援するわけではありませんが、Vaultのカバレッジにおけるギャップの特定や、更新が必要なノートの発見に役立ちます。

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

## 参考情報

Linter。 Vault全体にフォーマットルールを適用するプラグインです。一貫した見出し階層(H1をタイトル、H2をセクション、H3をサブセクションに使用)により、チャンカーが予測可能な結果を生成できます。検索精度に影響するLinterルールは以下の通りです:

  • 見出しの増分:連続した見出しレベルを強制(H1からH3への飛ばしを防止)
  • YAMLタイトル:ファイル名と一致させる
  • 末尾のスペース:削除(FTS5のトークン化アーティファクトを回避)
  • 連続する空行:1行に制限(よりクリーンなチャンクを生成)

Git連携。 Vaultのバージョン管理を実現します。変更履歴の追跡、マシン間の同期、誤削除からの復旧が可能になります。また、Gitはインデクサーが増分変更検出に使用するmtimeデータも提供します。

インデックスに役立つプラグイン

Smart Connections。 Obsidian内でAIを活用したセマンティック検索を提供するプラグインです。Smart Connections v4はデフォルトでローカルにembeddingsを生成するため、Vaultのインデックスが完了すれば、セマンティックな関連付けと検索はAPI呼び出しなしで完全にオフラインで動作します。23 本ガイドの検索システムはObsidianの外部(Pythonパイプラインとして動作)ですが、Smart Connectionsは執筆中にセマンティックな関連性を探索する用途で活用できます。両システムは同じコンテンツをインデックスしますが、用途が異なります。Smart Connectionsはエディタ内での発見に、外部リトリーバーはMCPを通じたAIツール連携に適しています。

Metadata Menu。 フィールド値のオートコンプリート付きで、構造化されたfrontmatter編集を提供するプラグインです。typedomaintagsフィールドの入力ミスを軽減できます。一貫したメタデータは検索フィルタリングの精度向上につながります。

インデックスに悪影響を与えるプラグイン

Excalidraw。 描画データをJSONとしてmarkdownファイルに埋め込みます。JSONは構文的に有効なmarkdownですが、チャンク分割やembedding生成時にはゴミデータとなります。.indexignoreまたはファイル拡張子によるフィルタリングで、Excalidrawファイルをインデックスから除外してください。

Kanban。 ボードの状態を特殊なフォーマットのmarkdownとして保存します。このフォーマットはKanbanの描画用に設計されており、文章の検索には適していません。チャンカーはカードタイトルやメタデータの断片を生成し、embeddingの品質が低下します。Kanbanボードはインデックスから除外してください。

Calendar。 最小限の内容(多くの場合、日付の見出しのみ)でデイリーノートを作成します。空または内容の少ないノートは低品質なチャンクを生成します。デイリーノートを使用する場合は、実質的な内容を記述するか、デイリーノートフォルダをインデックスから除外してください。

重要なプラグイン設定

ファイルリカバリー → 有効化。 ノートの誤削除に対する保護機能です。検索とは直接関係しませんが、依存するナレッジベースにとって不可欠な設定です。

厳密な改行 → 無効化。 Markdown標準の改行(段落区切りに二重改行)は、Obsidianの厳密モード(<br>として単一改行を処理)と比べて、よりクリーンなチャンクを生成します。

新規ファイルのデフォルト保存場所 → 指定フォルダ。 新規ファイルを00-inbox/にルーティングすることで、未分類のノートがドメインフォルダを汚染するのを防ぎます。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のみ、静的単語Embedding(attentionレイヤーなし)

Model2Vecは、sentence transformerの知識を静的トークンEmbeddingに蒸留します。BERT、MiniLM、その他のtransformerモデルのように入力に対してattentionレイヤーを実行する代わりに、事前計算されたトークンEmbeddingの加重平均によってベクトルを生成します。5 実用上の効果として、逐次計算が不要なため、Embedding速度はtransformerベースのモデルと比較して50〜500倍高速になります。

MTEBベンチマークスイートにおいて、potion-base-8Mはall-MiniLM-L6-v2の性能の89%を達成しています(平均スコア50.03 vs 56.09)。6 この11%の品質差が、速度とシンプルさの利点とのトレードオフです。短い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]

遅延読み込み。 モデルはインポート時ではなく、最初の使用時に読み込まれます。retrieverがBM25のみのフォールバックモードで動作している場合(例:Embedding用venvが未インストールの場合)、embedderモジュールのインポートにコストはかかりません。

分離された仮想環境。 モデルは専用のvenv(例:~/.claude/venvs/memory/)で実行され、ツールチェインの他の部分との依存関係の競合を回避します。_activate_venv()関数は、実行時にvenvのsite-packagessys.pathに追加します。

# Create isolated venv
python3 -m venv ~/.claude/venvs/memory
~/.claude/venvs/memory/bin/pip install model2vec

バッチ処理。 embedderは64件ずつのバッチでテキストを処理し、Model2Vecのオーバーヘッドを分散させます。インデクサーはチャンクを1つずつEmbeddingするのではなく、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(retrieval) 検索に最適化された静的モデル
potion-multilingual-128M 256 約500 MB 300x 多言語Vault(101言語対応)
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を選ぶ場合: 静的Embeddingファミリーを離れずに、potion-base-8Mより高い品質を求めるケースです。2025年1月にリリースされ、baai/bge-base-en-v1.5から蒸留されたより大きな語彙を使用し、MTEB平均52.46を達成しています(potion-base-8Mから5%の向上)。同じ256次元出力とnumpyのみの依存関係を維持しています。20 モデルファイルが4倍大きくなるためメモリ使用量は増加しますが、Embedding速度はtransformerモデルと比較して桁違いに高速なままです。

potion-retrieval-32Mを選ぶ場合: 主な用途が検索(Vault検索がまさにこれに該当)の場合です。このバリアントはpotion-base-32Mから検索タスクに特化してファインチューニングされており、MTEBのretrievalベンチマークで36.35を記録しています(ベースモデルの33.52に対して)。20 ファインチューニングが汎用性能と引き換えに検索特化の向上を実現しているため、全体のMTEB平均は49.73に低下します。

potion-multilingual-128Mを選ぶ場合: Vaultに複数言語のノートが含まれている場合です。2025年5月にリリースされた101言語対応モデルで、多言語タスクにおいて最高性能の静的Embeddingモデルです。他のpotionモデルと同じnumpyのみの依存関係を維持しながら、任意の言語のテキストに対してEmbeddingを生成できます。24 より大きなモデルファイル(約500 MB)が、クロスリンガル能力とのトレードオフとなります。日本語、中国語、ドイツ語など、英語以外の言語のノートが英語コンテンツと混在している場合に使用してください。

all-MiniLM-L6-v2を選ぶ場合: 速度よりも検索品質を重視し、PyTorchがインストール済みの場合です。384次元ベクトルにより、SQLiteデータベースのサイズは256次元と比較して約50%増加します。Embedding速度は、Mシリーズハードウェアで15,000ファイルのフルリインデックスの場合、1分未満から約10分に低下します。

nomic-embed-text-v1.5を選ぶ場合: ローカルで最高の検索品質が必要で、インデックス作成の遅延を許容できる場合です。768次元ベクトルにより、データベースサイズはおよそ3倍になります。PyTorchと最新のCPUまたはGPUが必要です。

text-embedding-3-smallを選ぶ場合: ネットワークレイテンシーとプライバシーを許容できるトレードオフとして受け入れられる場合です。APIは最高品質のEmbeddingを生成しますが、クラウド依存、トークンあたりのコスト(100万トークンあたり$0.02)、そしてコンテンツがOpenAIのサーバーに送信されるという点が伴います。

それ以外の場合はpotion-base-8Mを維持してください。 速度の優位性は反復的なインデックス作成(開発中のリインデックス)において重要であり、numpyのみの依存関係はPyTorchのインストールの複雑さを回避し、256次元ベクトルによりデータベースをコンパクトに保てます。

量子化と次元削減

Model2Vec v0.5.0以降では、精度と次元を削減したモデルの読み込みがサポートされています。20 モデルを切り替えることなく、制約のあるハードウェアへのデプロイやデータベースサイズの削減に役立ちます:

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に加えて)もサポートしており、静的モデルに蒸留可能なsentence transformerの範囲が拡大しています。22

Vault固有のEmbeddingへのファインチューニング

Model2Vec v0.4.0以降では静的Embeddingの上にカスタム分類モデルをトレーニングでき、v0.7.0では蒸留用の語彙量子化と設定可能なプーリングが追加されています。22 これは、デフォルトのpotionモデルではセマンティックなニュアンスを十分に捉えられない可能性のある、専門的な語彙を持つVault(医療ノート、法律文献、ドメイン固有の用語)で特に有用です:

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で十分な検索品質が得られます。ファインチューニングが有効なのは、汎用モデルでは捉えられないドメイン固有の関連性を検索が一貫して見逃す場合に限られます。

モデルハッシュによる追跡

インデクサーは、モデル名と語彙サイズから導出されたハッシュを保存します。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からモデルがダウンロードされます。ダウンロードに失敗した場合(ネットワーク障害、企業ファイアウォールなど)、retrieverはBM25のみのモードにフォールバックします。初回ダウンロード後、モデルはローカルにキャッシュされます。

次元の不一致。 データベースをクリアせずにモデルを切り替えると、保存済みベクトルと新しいEmbeddingの次元が異なります。インデクサーはモデルハッシュでこれを検出し、フルリインデックスをトリガーします。ハッシュチェックが失敗した場合(適切なハッシュのないカスタムモデル)、sqlite-vecは次元の不一致によるKNNクエリでエラーを返します。

大規模Vaultでのメモリ圧迫。 50,000件以上のチャンクを一括でEmbeddingすると、大量のメモリを消費する可能性があります。インデクサーは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パラメータは、テキストの複製を保存するのではなく、chunksテーブルを直接参照するようFTS5に指示します。これによりストレージ要件が半減しますが、チャンクの挿入・更新・削除時にFTS5を手動で同期する必要があります。

カラム構成。 3つのカラムがインデックス化されます: - 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_fuseembed_batchget_stale_files
  • CLIフラグ: --incremental--vault--model
  • 設定キー: bm25_weightmax_tokensbatch_size
  • エラーメッセージ: SQLITE_LOCKEDConnectionRefusedError
  • 専門用語: PostToolUsePreToolUseAGENTS.md

これらのクエリに対して、BM25は完全一致を即座に見つけ出します。ベクトル検索では意味的に関連するコンテンツが返されますが、完全一致よりも概念的な議論の方が上位にランク付けされる可能性があります。

BM25が失敗する場面

BM25は、保存されたコンテンツと異なる用語を使用するクエリでは失敗します:

  • クエリ:「how to handle authentication failures」→ Vault内には「login error recovery」や「session expiration handling」に関するノートが存在。キーワードが異なるため、BM25はマッチしません。
  • クエリ:「what is the best way to manage state」→ Vault内には「Redux store patterns」や「context providers」に関するノートが存在。「state management」が具体的な技術名で表現されているため、BM25は見逃してしまいます。

BM25はまた、大規模なVaultでのキーワード衝突にも弱いという特徴があります。15,000ファイルのVaultでは、「configuration」で検索するとほぼすべてのプロジェクトノートがconfigurationに言及しているため、数百件のノートがマッチします。結果は技術的には正しいものの、実用上は役に立ちません — ランキングでは、どの「configuration」ノートが現在のクエリに関連しているかを判断できないのです。

FTS5トークナイザー

FTS5はデフォルトでunicode61トークナイザーを使用し、ASCIIおよびUnicodeテキストを処理します。CJK(中国語、日本語、韓国語)コンテンツが多いVaultでは、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文字ごとに分割するため、部分文字列マッチングが可能になりますが、インデックスサイズが大きくなります(およそ3倍)。

メンテナンス

FTS5は、基盤となるchunksテーブルが変更された際に明示的な同期が必要です:

# 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)検索をSQLiteに導入します。このセクションでは、sqlite-vecの設定、ノートから検索可能なベクトルへのembeddingパイプライン、具体的なクエリパターンについて解説します。

sqlite-vec仮想テーブル

CREATE VIRTUAL TABLE chunk_vecs USING vec0(
    id INTEGER PRIMARY KEY,
    embedding float[256]
);

vec0モジュールは256次元のfloatベクトルをパックされたバイナリデータとして格納します。idカラムはchunksテーブルと1対1で対応しており、ベクトル検索結果とチャンクメタデータの結合(JOIN)が可能です。

Embeddingパイプライン

ノートから検索可能なベクトルへの処理フローは以下の通りです:

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モジュールを使用して、floatベクトルを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クエリ

ベクトル検索クエリでは、入力クエリをembeddingに変換し、cosine距離で最も近い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カラムにはcosine距離(0 = 同一、2 = 正反対)が格納されます。

距離制約によるKNNページネーション

sqlite-vec v0.1.7以降、KNNクエリでWHERE distance < ?制約がサポートされるようになりました。これにより、大規模な結果セットに対してカーソルベースのページネーションが可能となり、前のページを再スキャンする必要がなくなりました。26

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仮想テーブルにネイティブDELETEサポートが追加されました。26 以前はベクトルの削除にテーブルの再作成が必要でしたが、インデクサーのファイル削除パスで直接ベクトルを削除できるようになっています:

# 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])

ノートの削除や移動時のインクリメンタルな再インデックスが簡素化されました。インデクサーは「アクティブID」のシャドウテーブルを管理したり、バッチ再構築を行ったりする必要がなくなっています。

ベクトル検索が有効なケース

ベクトル検索は、特定の単語よりも概念の一致が重要なクエリで威力を発揮します:

  • クエリ:「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」に関するノートが返される

また、構造化データもベクトル検索の苦手分野です。JSONの設定ファイル、YAMLブロック、コードスニペットは、意味的な内容ではなく構造的なパターンを捉えたembeddingを生成します。"review": trueを含むJSONファイルは、コードレビューについての散文的な議論とは異なるembeddingになります。

グレースフルデグラデーション

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は、スコアの較正を必要とせずに2つのランク付きリストを統合します。このセクションでは、アルゴリズム、クエリトレースの実例、kパラメータのチューニング、そして他の手法ではなくRRFを選択する理由を解説します。編集可能なランク、シナリオプリセット、ビジュアルアーキテクチャエクスプローラーを備えたインタラクティブな計算ツールについては、hybrid retrieverの詳細解説をご覧ください。

アルゴリズム

RRFは、各リストにおけるランク位置のみに基づいてドキュメントにスコアを割り当てます。

score(d) = Σ (weight_i / (k + rank_i))

各変数の意味は以下の通りです。 - k — 平滑化定数(Cormack et al.3に従い60を使用) - rank_i — 結果リストiにおけるドキュメントの1始まりのランク - weight_i — リストごとのオプション重み係数(デフォルト1.0)

複数のリストで上位にランクされたドキュメントほど、高い統合スコアを受け取ります。片方のリストにのみ出現するドキュメントは、その単一ソースからのスコアのみとなります。

代替手法ではなくRRFを選ぶ理由

重み付き線形結合では、BM25スコアとcosine distanceの較正が必要です。BM25スコアは上限がなくコーパスサイズに応じてスケールしますが、cosine distanceは[0, 2]の範囲に制限されています。両者を組み合わせるには正規化が必要であり、その正規化パラメータはデータセットに依存します。一方、RRFはランク位置のみを使用するため、スコアリング手法に関係なく常に1から始まる整数で処理できます。

学習型融合モデルには、クエリとドキュメントの関連性ペアというラベル付き学習データが必要です。個人のナレッジベースには、そのような学習データは存在しません。有用なモデルを学習するには、数百のクエリ・ドキュメントペアを手動で評価する必要があります。RRFは学習データなしで機能します。

Condorcet投票法(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”がより顕著に一致する2つの設定ファイルがそれより上位に配置されます。ベクトル検索では、同じチャンクをポジション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

両方のリストで上位にランクされたチャンクが最上位に浮上します。片方のリストにのみ出現するチャンクは単一ソースのスコアとなり、両方にランクインした結果の下に位置づけられます。実際のdisagreement resolution(意見不一致の解決)ロジックが勝利するのは、BM25がキーワードで、ベクトル検索がセマンティクスで、両手法ともにそれを見つけたためです。

ランクごとのRRF計算を含む完全なステップバイステップのトレースや、異なるk値の試行については、インタラクティブRRF計算ツールをお試しください。

実装

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データセットにおいてロバストであることが確認されています。自身のクエリ分布で失敗ケースを測定してから、初めてチューニングを行ってください。

タイブレーキング

2つのチャンクが同一のRRFスコアを持つ場合(片方のリストで同じランクかつもう一方に未出現という場合にまれに発生)、以下の順序でタイブレークを行います。

  1. 両方のリストに出現するチャンクを、片方のみに出現するチャンクより優先する
  2. 両方のリストに出現するチャンク同士では、合計ランクがより低いものを優先する
  3. 片方のリストのみに出現するチャンク同士では、そのリストでのランクがより低いものを優先する

完全な検索パイプライン

このセクションでは、クエリが入力から出力まで、パイプライン全体を通過する流れを追跡します。BM25検索、ベクトル検索、RRF融合、トークンバジェットによる切り詰め、そしてコンテキスト組み立てまでの一連の処理を解説します。

エンドツーエンドのフロー

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チャンクデータベースでの計測結果です。

検索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

トークンバジェットによる切り詰め

max_tokensパラメータは、AIツールが処理できる量を超えるコンテキストをリトリーバーが返すのを防ぎます。推定には1トークンあたり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
デフォルトトークンバジェット 4,000トークン

コンテンツハッシュと変更検出

インデクサーは、前回のインデックス実行以降にどのファイルが変更されたかを把握する必要があります。このセクションでは、変更検出メカニズムとハッシュ戦略について解説します。

ファイル更新時刻の比較

インデクサーはchunksテーブル内の各チャンクに対してmtime_ns(ナノ秒単位のファイル更新時刻)を保存しています。増分実行時、インデクサーは以下の処理を行います:

  1. 許可されたフォルダ内のすべての.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であってコンテンツハッシュではないのか

コンテンツハッシュ(ファイル内容のSHA-256)はmtime比較よりも信頼性が高くなります。たとえば、git checkoutで元のmtimeが復元された場合のように、内容が変更されていないのにファイルが「触れられた」ケースを検出できるからです。しかし、ハッシュを使うには増分実行のたびにすべてのファイルを読み込む必要があります。16,894ファイルの場合、ファイル内容の読み込みに2〜3秒かかりますが、ファイルシステムからのmtime読み取りは100ms未満で完了します。

このトレードオフをまとめると、mtime比較は変更されていないファイルを不要に再インデックスすることがあります(偽陽性)が、実際の変更を見逃すことはありません。偽陽性のコストは、実行ごとに数回の余分なembedding呼び出し程度です。速度差(100ms対3秒)を考慮すると、AIインタラクションのたびに実行されるシステムにはmtimeが実用的な選択となります。

削除の処理

Vault からファイルが削除された場合、インデクサーはデータベースからそのファイルのすべてのチャンクを削除します:

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文はsqlite-vec v0.1.7以降でネイティブに動作します。26 それ以前のバージョンでは回避策(仮想テーブルの削除と再作成、または外部の「アクティブID」セットの管理)が必要でした。直接削除に依存する場合は、0.1.7より前のバージョンからアップグレードしてください。

FTS5のcontent-syncテーブルでは、削除された行ごとにINSERT INTO chunks_fts(chunks_fts, rowid, ...) VALUES('delete', ?, ...)による明示的な削除が必要です。インデクサーはファイル削除処理の一環としてこれを処理しています。


インクリメンタルリインデックスとフルリインデックス

インデクサーには2つのモードがあります。インクリメンタル(高速、日常使用向け)とフル(低速、必要時のみ)です。このセクションでは、それぞれの使い分け、冪等性の保証、および破損からの復旧について解説します。

インクリメンタルリインデックス

使用するタイミング: ノートを編集した後の日常的なインデックス作成。デフォルトモードです。

処理内容: 1. Vault内のファイル変更をスキャン(mtime比較) 2. 削除されたファイルのチャンクを削除 3. 変更されたファイルを再チャンク・再エンベディング 4. 新規ファイルのチャンクを挿入 5. FTS5インデックスを同期

所要時間の目安: 16,000ファイルのVaultで、1日分の編集に対して10秒未満。

python index_vault.py --incremental

フルリインデックス

使用するタイミング: - エンベディングモデルを変更した後(モデルハッシュの不一致を検出) - スキーマ移行後(新規カラム、インデックス変更) - データベース破損後(整合性チェック失敗時) - インクリメンタルインデックスで予期しない結果が出た場合

処理内容: 1. 既存データをすべて削除(チャンク、ベクトル、FTS5エントリ) 2. Vault全体をスキャン 3. 全ファイルをチャンク化 4. 全チャンクをエンベディング 5. FTS5インデックスをゼロから構築

所要時間の目安: Apple M3 Proで16,894ファイルに対して約4分。

python index_vault.py --full

冪等性

どちらのモードも冪等です。同じコマンドを2回実行しても、同じ結果が得られます。インデクサーはファイルの既存チャンクを削除してから新しいチャンクを挿入するため、すでに最新のデータベースに対してインクリメンタルインデックスを再実行しても変更はゼロになります。フルインデックスを再実行した場合も、同一のデータベースが生成されます。

破損からの復旧

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 で実行される場合の処理フロー:

  1. モデルハッシュチェック。 保存済みのモデルハッシュと現在のモデルを比較します。異なる場合は自動的にフルリインデックスモードに切り替わり、ユーザーに警告を表示します。
  2. ファイルスキャン。 許可されたフォルダを巡回し、ファイルパスとmtimeを収集します。
  3. 変更検出。 保存済みデータと比較します。
  4. バッチ処理。 変更されたファイルを64件ずつのバッチで再チャンク・再エンベディングします。
  5. 進捗レポート。 処理済みファイル数と経過時間を出力します。
  6. グレースフルシャットダウン。 SIGINTを受信した場合、現在のファイル処理を完了してから停止します。

クレデンシャルフィルタリングとデータ境界

個人ノートには機密情報が含まれることがあります。APIキー、Bearerトークン、データベース接続文字列、デバッグ中に貼り付けた秘密鍵などです。クレデンシャルフィルターは、これらが検索インデックスに取り込まれるのを防ぎます。

問題の背景

OAuth連携のデバッグに関するノートには、以下のような内容が含まれている可能性があります:

The token was: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
I used this curl command:
  curl -H "Authorization: Bearer sk-ant-api03-abc123..."

フィルタリングなしでは、JWTとAPIキーの両方がチャンク化、エンベディング、データベースへの保存対象となります。「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アクセスキー 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

主要な設計上の判断:

  1. エンベディング前にフィルタリング。 クリーニング済みテキストがエンベディングの対象となります。ベクトル表現にクレデンシャルのパターンがエンコードされることはありません。「APIキー」で検索すると、APIキーの管理について議論しているノートが返され、実際のキーを含むノートは返されません。

  2. 削除ではなく置換。 [REDACTED:pattern-name] トークンにより、周囲のテキストの意味的コンテキストが保持されます。エンベディングは「クレデンシャルに類似する何かがここにあった」というセマンティクスを捉えつつ、クレデンシャル自体はエンコードしません。

  3. 値ではなくパターンをログに記録。 フィルターはどのパターンがマッチしたか(例:「oauth-debug.mdから2件のクレデンシャルをスクラブ [jwt, bearer-token]」)をログに記録しますが、クレデンシャルの値そのものは決してログに残しません。

パスベースの除外

.indexignore ファイルはパスによる粗粒度の除外を提供します。クレデンシャルフィルターはインデックス対象ファイル内での細粒度のスクラビングを提供します。両方が必要です:

  • .indexignore:機密コンテンツが含まれているとわかっているフォルダ全体の除外に使用(健康記録、財務記録、キャリア関連文書など)
  • クレデンシャルフィルター:インデックス対象のコンテンツに誤って埋め込まれた機密情報の検出に使用

データ分類

多様なコンテンツを含むVaultでは、ノートを機密度別に分類することを検討してください:

レベル インデックス対象? フィルター適用?
公開 ブログ下書き、技術ノート はい はい
内部 プロジェクト計画、アーキテクチャ決定 はい はい
機密 給与データ、健康記録 いいえ(.indexignore) 該当なし
制限付き クレデンシャル、秘密鍵 いいえ(.indexignore) 該当なし

MCP サーバーアーキテクチャ

Model Context Protocol(MCP)サーバーは、AIエージェントが呼び出せるツールとしてリトリーバーを公開します。このセクションでは、サーバー設計、機能の範囲、権限の境界について解説します。

プロトコルの選択:STDIO vs 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サービスとして実行するモードです。リモートアクセス、マルチクライアント構成、またはVaultを共有サーバーに置くチーム構成に適しています。

{
  "mcpServers": {
    "obsidian": {
      "url": "http://localhost:3333/mcp"
    }
  }
}

推奨事項: 個人用Vaultの場合はSTDIOを使用してください。構成がシンプルで、ネットワークへの露出がないためセキュリティ面でも優れており、サーバーのライフサイクルもAIツールが管理してくれます。複数のツールや複数のマシンから同一Vaultへ同時にアクセスする必要がある場合にのみHTTPを選択しましょう。

MCP仕様の進化。 2025年6月のMCP仕様では、OAuth 2.1認可、構造化ツール出力(型付きリターンスキーマ)、およびエリシテーション(サーバー主導のユーザープロンプト)が追加されました。2025年11月のリリースでは、ファーストクラスのトランスポートモードとしてStreamable HTTP、自動サーバー機能検出のための.well-known URLディスカバリー、ツールが読み取り専用か変更操作かを宣言する構造化ツールアノテーション、そしてSDKティア標準化システムが導入されています。1821 次回の仕様リリース(暫定的に2026年半ば)では、長時間タスク向けの非同期操作、ヘルスケアや金融といった業界向けのドメイン固有プロトコル拡張、マルチエージェントワークフローにおけるエージェント間通信標準が提案されています。21 個人用Vaultサーバーにとっては、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. 読み取り専用。 サーバーはVaultとインデックスデータベースの読み取りのみを行います。ノートの作成、変更、削除は一切行いません。書き込み操作(新規ノートのキャプチャなど)は、MCPサーバーではなく、別のフックやスキルが担当します。

  2. Vaultスコープ。 サーバーは設定されたVaultパス内のファイルのみを読み取ります。パストラバーサル攻撃(../../etc/passwd)は必ず拒否しなければなりません。

  3. クレデンシャルフィルタリング済み出力。 データベースに事前フィルタリング済みのコンテンツが含まれている場合でも、多層防御の一環として出力時にクレデンシャルフィルタリングを適用してください。

  4. トークン制限付きレスポンス。 すべてのツールレスポンスに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の設定、フックの統合、そして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など)を一覧表示するはずです。

フックの統合

フックはClaude Codeのライフサイクルの特定のポイントで動作を拡張します。Obsidian統合に関連するフックは2つあります:

PreToolUseフック — エージェントがツールコールを処理する前にボールトを検索し、関連するコンテキストを自動的に注入します。

#!/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フック — 重要なツール出力をキャプチャし、将来の検索に備えてボールトに書き戻します。

#!/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を提供します:

# 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スキル

インサイトをボールトにキャプチャするためのClaude Codeスキルです:

/capture "OAuth token rotation requires both access and refresh token invalidation"
  --domain security
  --tags oauth,tokens

このスキルは00-inbox/に適切なfrontmatter付きの新しいノートを作成し、インクリメンタルな再インデックスをトリガーすることで、新しいノートが即座に検索可能になります。

カスタムコマンドパターン

Claude Codeのスキルはボールト操作を名前付きコマンドとしてラップできます。実践者たちはボールトを読み取り元と書き込み先の両方として扱う、Obsidian専用のコマンドライブラリを構築してきました。

シグナルスキャニング。 /scan-intelコマンドは外部ソースを検索し、個人の研究関心に対して発見事項をスコアリングし、条件を満たすシグナルをfrontmatter付きのボールトノートとして書き出します:

/scan-intel --topics "agent infrastructure, security" --lookback 7d

このコマンドは設定済みのソース(arXiv、HN、RSS)からフェッチし、スコアリングモデル(関連性、実行可能性、深度、権威性)を適用し、基準を通過したシグナルをトピック別のボールトフォルダに書き込みます。ボールトが自動化されたインテリジェンスパイプラインの下流コンシューマーとなるわけです。

キャプテンズログ。 /captains-logコマンドは全リポジトリの日次git活動を集約し、構造化されたジャーナルエントリをボールトに書き込みます。決定事項、気づき、未解決のスレッドも含まれます:

/captains-log

このコマンドはGitHubからコミット履歴を取得し、リポジトリごとにグループ化して、ナラティブなジャーナルエントリとしてフォーマットします。日々のログが蓄積されることで、何がリリースされ、なぜそうしたのかが検索可能な記録として残ります。

Obsidianキャプチャ。 /obsidian-captureコマンドは現在のClaude Codeセッションからインサイトを取得し、適切なメタデータとともにボールトに直接書き込みます:

/obsidian-capture "SAST gates in agent loops increase security degradation"
  --folder AI-Tools --tags security,agents

このパターンはあらゆるボールト操作に拡張できます:MOCの作成、プロジェクトステータスノートの更新、関連シグナルのリンク、蓄積された日次ログからの週次ダイジェスト生成などです。

コミュニティの事例。 実践者たちが自分のコマンドライブラリを公開しています。あるデベロッパーはデイリーレビュー、プロジェクト計画、リサーチキャプチャ、コンテンツワークフローをカバーする22個のカスタムObsidian + Claude Codeコマンドを共有しました。1 別のデベロッパーはコード分析からダイアグラムノートをボールトに生成する「Visual Explainer」スキルを構築しました。2 コマンドは様々ですが、アーキテクチャは一貫しています:Claude Codeスキルがインターフェース、ボールトノートがストレージレイヤー、そして検索インフラがクエリエンジンです。

コンテキストウィンドウの管理

統合にあたっては、Claude Codeのコンテキストウィンドウに配慮する必要があります:

  • 注入するコンテキストはクエリあたり1,500〜2,000トークンに制限してください。 これ以上になると、エージェントのワーキングメモリを圧迫します。
  • ソースの帰属情報を含めてください。 エージェントが出典を参照できるよう、ファイルパスとセクション見出しを必ず含めましょう。
  • チャンクテキストは切り詰めてください。 長いチャンクは完全に省略するのではなく、...で切り詰めるべきです。通常、先頭の300〜500文字に重要な情報が含まれています。
  • すべてのツールコールで注入しないでください。 PreToolUseフックは呼び出されるツールに応じてコンテキストを選択的に注入すべきです。読み取り操作にはボールトコンテキストは不要です。書き込みや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から読み取ります。ボールト検索のガイダンスを含めましょう:

## 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
フック ~/.claude/hooks/ 非対応
スキル ~/.claude/skills/ 非対応
指示ファイル CLAUDE.md AGENTS.md
承認モード --dangerously-skip-permissions suggest / auto-edit / full-auto

重要な違い: Codex CLIはフックをサポートしていません。自動コンテキスト注入パターン(PreToolUseフック)は利用できません。代わりに、作業開始前にボールトを検索するよう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ファイルには、Vaultを活用するための指示を記述できます:

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 設定UI
Claudian(Obsidianプラグイン) N/A(組み込み) Claude Code CLI Obsidianプラグイン設定
Agent Client(Obsidianプラグイン) N/A(組み込み) ACP Obsidianプラグイン設定

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統合ほどスマートではありませんが、どのツールでも使える汎用的な方法です。


構造化ノートによるプロンプトキャッシング

Vault内の構造化されたノートは、AI操作全体でトークン使用量を削減する再利用可能なコンテキストブロックとして活用できます。ここでは、キャッシュキーの設計とトークンバジェット管理について解説します。

基本パターン

操作のたびにコンテキストを検索するのではなく、整理されたVaultノートからコンテキストブロックを事前構築してキャッシュしておきます:

# 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
    },
}

キャッシュの無効化

キャッシュの無効化は、2つのシグナルに基づいて行われます:

  1. TTLの期限切れ。 各コンテキストブロックにはTTL(有効期限)が設定されています。TTLが切れると、Vaultへの再クエリによってブロックが再構築されます。
  2. Vaultの変更検知。 キャッシュ済みコンテキストブロックの構成元となったファイルの変更をインデクサーが検知すると、そのブロックは即座に無効化されます。

トークンバジェット管理

セッション開始時にコンテキストの総バジェットが設定されます。キャッシュブロックはそのバジェットの一部を消費します:

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)

キャッシュブロックはセッション開始時にロードされます。動的検索結果は、クエリごとに残りのバジェットを使って取得されます。このハイブリッドアプローチにより、エージェントは頻繁に必要なコンテキストのベースラインを確保しつつ、特定のクエリ用のバジェットも温存できます。

キャッシュ有無によるトークン使用量の比較

キャッシュなしの場合: 関連するクエリのたびにVault検索が実行され、1,500〜2,000トークンのコンテキストが返されます。1セッションで10回クエリを実行すると、Vaultコンテキストだけで15,000〜20,000トークンを消費することになります。

キャッシュありの場合: 3つの事前構築コンテキストブロックで合計4,500トークンを消費します。追加の検索はユニークなクエリごとに1,500〜2,000トークンが加算されます。10回のクエリのうち6回がキャッシュでカバーされる場合、消費量は4,500 +(4 × 1,500)= 10,500トークン——キャッシュなしの場合のおよそ半分です。


PostToolUseフックによるコンテキスト圧縮

ツールの出力は冗長になりがちです。スタックトレース、ファイル一覧、テスト結果など、そのままではコンテキストウィンドウを圧迫します。PostToolUseフックを使えば、これらの出力をコンテキストに取り込む前に圧縮できます。

問題点

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トークンの出力ですが、本質的な情報はわずか2行——200件のパスと1件の失敗——だけです。

フックの実装

#!/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

再帰トリガーの防止

圧縮フック自体が出力を生成する場合、ガードがなければ自分自身を再帰的にトリガーしてしまう可能性があります:

# 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 status modified: / new file: ステータス別に件数を集計
ビルド出力 warning: / error: 情報行を除去し、警告/エラーのみ保持

シグナル取り込みとトリアージパイプライン

取り込みレイヤーは、何をVaultに入れるかを決定します。キュレーションなしでは、Vaultにノイズが蓄積する一方です。ここでは、シグナルをドメインフォルダに振り分けるスコアリングパイプラインを解説します。

ソース

シグナルは複数のチャネルから入ってきます:

  • RSSフィード: 技術ブログ、セキュリティアドバイザリ、リリースノート
  • ブックマーク: Obsidian Web Clipperやブックマークレット経由で保存されたブラウザのブックマーク
  • ニュースレター: メールニュースレターからの重要な抜粋
  • 手動キャプチャ: 読書中、会話中、リサーチ中に書いたメモ
  • ツール出力: フック経由でキャプチャされた重要なAIツールの出力
  • iOS共有エクステンション: ObsidianのiOSアプリ(2026年初頭にアップデート)には共有エクステンションが搭載されており、Safari、SNS、その他のアプリからObsidianを開くことなく直接Vaultにコンテンツを保存できます。31 これにより、モバイルからの低摩擦な取り込みパスが実現します——Safariから記事を共有するだけで、スコアリング待ちのVaultノートとして到着します。
  • Obsidian CLI: シェルスクリプトやフックからobsidian file createでノートを作成したり、obsidian file appendで既存ノートに追記したりできるため、デスクトップ上で自動化された取り込みパイプラインを構築できます。

スコアリングの4軸

各シグナルは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

運用実績

14ヶ月間の運用データ:

指標
処理済みシグナル総数 7,771
自動振り分け(0.55以上) 4,832(62%)
レビュー待ち(0.40〜0.55) 1,543(20%)
破棄(0.40未満) 1,396(18%)
アクティブなドメインフォルダ数 12
1日あたりの平均シグナル数 約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は検索において2つの利点をもたらします:

  1. 直接マッチ。 「authentication overview」の検索はMOC自体にマッチし、関連ノートのキュレーションされたリストをエージェントに提供します。
  2. コンテキスト拡張。 特定のノートを見つけた後、リトリーバーはそのノートがいずれかの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)

この機能は現在のリトリーバーには実装されていませんが、グラフ構造の自然な拡張として位置づけられます。

アンチパターン

孤立クラスター。 互いにリンクし合っているものの、Vault全体との接続を持たないノート群です。Obsidianのグラフパネルでは、これらが切り離された島として可視化されます。孤立クラスターは、MOCの不足やドメイン間リンクの欠如を示しています。

タグの肥大化。 タグを一貫性なく使用したり、過度に細分化されたタグを作成するパターンです。5,000ノートに500のユニークタグがあるVaultでは、10タグあたり平均1ノートとなり、フィルタリングに役立ちません。ドメインフォルダに対応する20〜50の高レベルタグに統合しましょう。

リンク過多・コンテンツ過少のノート。 wiki-linkのみで構成され、散文テキストがないノートです。チャンカーがembeddingsに変換するテキストがないため、インデックス化が困難になります。リンク先のノートがなぜ関連しているのかを説明する段落を少なくとも1つ追加してください。

あらゆる場所への双方向リンク。 すべての言及にwiki-linkが必要なわけではありません。「OAuth」について軽く触れるだけなら[[OAuth 2.0 Overview]]は不要です。wiki-linkは、クリックして有用なコンテキストが得られる意図的でナビゲート可能な関係性にのみ使用しましょう。


開発者ワークフローレシピ

Vault検索と日常の開発タスクを組み合わせた実践的なワークフローを紹介します。

朝のコンテキストロード

関連するコンテキストを読み込んで1日を始めましょう:

Search my vault for notes about [current project] updated in the last week

リトリーバーがアクティブなプロジェクトに関する最近のノートを返し、中断したところを素早く思い出せます。昨日のコミットメッセージを読み返すよりも効果的です。

コーディング中のリサーチキャプチャ

機能を実装中にエディタを離れることなくインサイトを記録できます:

/capture "FastAPI dependency injection with async generators requires yield,
not return. The generator is the dependency lifecycle."
  --domain programming
  --tags fastapi,dependency-injection

キャプチャされたインサイトは即座にインデックス化され、将来の検索で利用可能になります。数ヶ月の蓄積により、実装固有の知識のコーパスが構築されていきます。

プロジェクトキックオフ

新しいプロジェクトや機能を開始する際のワークフロー:

  1. Vaultを検索:「[技術/パターン]について何を知っているか?」
  2. 上位5件の結果から、過去の決定事項や注意点を確認
  3. そのドメインのMOCが存在するか確認し、なければ作成
  4. 障害モードを検索:「[技術]の問題点」

Vault検索によるデバッグ

エラーや予期しない動作に遭遇した場合:

Search my vault for [error message or symptom]

過去のデバッグノートには、根本原因と修正方法が記録されていることが多いです。プロジェクト横断的に繰り返し発生する問題に対して特に有効で、忘れてしまったことをVaultが覚えていてくれます。

コードレビューの準備

PRをレビューする前に:

Search my vault for patterns and conventions about [module being changed]

Vaultは、レビュー対象のコードに関連する過去の意思決定、アーキテクチャ上の制約、コーディング規約を返します。差分だけでなく、組織の知識に基づいた情報に裏打ちされたレビューが可能になります。


パフォーマンスチューニング

このセクションでは、さまざまなVaultサイズと使用パターンに応じた最適化戦略を解説します。

インデックスサイズの管理

Vaultサイズ チャンク数 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ノート以上の場合、以下を検討してください: - バッチサイズを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プラグマは、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モードでは同時読み取りは可能ですが、書き込みは1プロセスのみに限られます。

対処法: データベースへの書き込みは1つのプロセス(インデクサー)のみに限定してください。MCPサーバーとフックは読み取り専用とします。並行書き込みが必要な場合は、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")

大規模Vaultのメモリ問題

症状: 大規模なVault(50,000ノート以上)のフルリインデックス時にメモリ不足エラーが発生する。

原因: embeddingのバッチサイズが大きすぎる、またはすべてのファイル内容が同時にメモリに読み込まれています。

対処法: バッチサイズを縮小し、ファイルをインクリメンタルに処理します:

BATCH_SIZE = 32  # Reduce from 64

また、すべてのファイルをメモリに読み込むのではなく、インデクサーがファイルを1つずつ処理する(各ファイルの読み込み、チャンキング、embedding生成を順に行う)ようにしてください。


移行ガイド

Apple Notesからの移行

  1. macOSの「すべてをエクスポート」オプション、またはapple-notes-liberatorなどの移行ツールを使用してApple Notesをエクスポートします
  2. HTMLエクスポートをmarkdownifypandocでMarkdownに変換します
  3. 変換したファイルをVaultの00-inbox/フォルダに移動します
  4. 各ノートを確認し、frontmatterを追加します
  5. ノートを適切なドメインフォルダに移動します

Notionからの移行

  1. Notionからエクスポート:Settings → Export → Markdown & CSV
  2. エクスポートしたZIPファイルをVaultの00-inbox/フォルダに展開します
  3. Notion固有のMarkdownアーティファクトを修正します:
  4. Notionの- [ ]チェックリストは標準的なMarkdown形式なのでそのまま使えます
  5. Notionのプロパティテーブル(HTML形式)をYAML frontmatterに変換します
  6. Notionが相対パスで埋め込んだ画像を添付ファイルフォルダにコピーします
  7. 標準的なfrontmatter(typedomaintags)を追加します
  8. Notionのページリンクを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. Vaultに移動し、frontmatterを追加してフォルダに整理します

プレーンMarkdown(Obsidianなし)からの移行

すでにMarkdownファイルのディレクトリがある場合:

  1. そのディレクトリをObsidian Vaultとして開きます(Obsidian → Open Vault → Open folder)
  2. ディレクトリがバージョン管理されている場合は.obsidian/.gitignoreに追加します
  3. frontmatterテンプレートを作成し、既存のファイルに適用します
  4. ノートを読みながら整理し、[[wiki-links]]でリンクを作成していきます
  5. インデクサーをすぐに実行しましょう——検索システムは初日から機能します

別の検索システムからの移行

異なるembedding/検索システムから移行する場合:

  1. ベクトルの移行は不要です。 異なるモデルは互換性のないベクトル空間を生成します。新しいモデルでフルリインデックスを実行してください。
  2. 移行すべきはコンテンツであり、インデックスではありません。 Vaultのファイルが唯一の正(source of truth)です。インデックスは派生物にすぎません。
  3. 移行後に検証しましょう。 答えがわかっている10〜20のクエリを実行し、結果が期待どおりか確認してください。

変更履歴

日付 変更内容
2026-04-01 Obsidian CLIセクションを追加(AIワークフロー向けv1.12コマンド)。エージェントプラグインセクションを追加(Claudian、Agent Client)。Vault整理用のBasesコアプラグインを記載。プラグイン数を2,500+に更新。取り込みソースにiOS Share Extensionを追加。互換性マトリクスを組み込みエージェントプラグインで更新。
2026-03-30 MCPVault v0.11.0:list_all_tagsツール、.base/.canvasサポート、@bitbonsai/mcpvaultにリネーム。Obsidian Desktop v1.12.7がCLIバイナリを同梱し、ターミナル操作を高速化。
2026-03-23 sqlite-vec v0.1.7安定版を記載:vec0テーブルのDELETEサポート、ページネーション用KNN距離制約。DiskANN近似最近傍インデックスが今後のリリースで予告。
2026-03-07 embeddingモデル比較にpotion-multilingual-128M(101言語、2025年5月)を追加。sqlite-vecはv0.1.7-alpha.10(CI/CD修正のみ、機能変更なし)。MCP仕様と検索手法が最新であることを確認。
2026-03-03 MCP仕様の進化を更新(2025年11月リリース:Streamable HTTP、.well-known、ツールアノテーション)。Model2VecのファインチューニングとBPE/Unigramトークナイザーサポートを追加。コミュニティMCPサーバー比較表を追加。Smart Connectionsをv4に更新。
2026-03-02 モデル比較にpotion-base-32Mとpotion-retrieval-32Mを追加。量子化/次元削減セクションを追加。MCP仕様の進化に関する注記を追加。
2026-03-01 初版リリース

参考文献


  1. Internet Vin, “22 commands I use with Obsidian and Claude Code,” March 2026, 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: 100万トークンあたり$0.02。Vault全体の再インデックスにかかる推定コスト: 約$0.30。 

  5. van Dongen, T. et al. Model2Vec: Turn any Sentence Transformer into a Small Fast Model. arXiv, 2025. sentence transformerから静的embeddingsを生成する蒸留手法について記述。 

  6. MTEB: Massive Text Embedding Benchmark. potion-base-8Mの平均スコアは50.03で、all-MiniLM-L6-v2の56.09に対して89%の性能を維持。 

  7. SQLite FTS5 Extension. FTS5はBM25ランキングと設定可能なカラム重み付けによる全文検索を提供。 

  8. sqlite-vec: A vector search SQLite extension. SQLite内でKNNベクトル検索を実行するvec0仮想テーブルを提供。 

  9. Robertson, S. and Zaragoza, H. The Probabilistic Relevance Framework: BM25 and Beyond. Foundations and Trends in Information Retrieval, 2009. 

  10. Karpukhin, V. et al. Dense Passage Retrieval for Open-Domain Question Answering. EMNLP, 2020. 密ベクトル表現がオープンドメイン質問応答においてBM25を9〜19%上回ることを実証。 

  11. Reimers, N. and Gurevych, I. Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks. EMNLP, 2019. 密な意味的類似度に関する基礎的研究。 

  12. Luan, Y. et al. Sparse, Dense, and Attentional Representations for Text Retrieval. TACL, 2021. MS MARCOにおいて、hybrid検索が単一モダリティのアプローチを一貫して上回ることを実証。 

  13. SQLite Write-Ahead Logging. 単一ライターによる並行読み取りを実現するWALモード。 

  14. Gao, Y. et al. Retrieval-Augmented Generation for Large Language Models: A Survey. arXiv, 2024. RAGアーキテクチャとchunking戦略に関するサーベイ。 

  15. Thakur, N. et al. BEIR: A Heterogeneous Benchmark for Zero-shot Evaluation of Information Retrieval Models. NeurIPS, 2021. 

  16. Model2Vec: Distill a Small Fast Model from any Sentence Transformer. Minish Lab, 2024. 

  17. Obsidian Documentation. Obsidianの公式ドキュメント。 

  18. Model Context Protocol Specification. AIツールとデータソースを接続するためのMCP標準仕様。 

  19. 著者の本番データ。16,894ファイル、49,746チャンク、83.56 MBのSQLiteデータベース、14ヶ月間で7,771シグナルを処理。クエリレイテンシはtime.perf_counter()で計測。 

  20. Model2Vec Potion Models. Minish Lab, 2025. Potion-base-32M(MTEB 52.46)、potion-retrieval-32M(MTEB retrieval 36.35)、およびv0.5.0以降の量子化・次元削減機能。 

  21. Update on the Next MCP Protocol Release. 2025年11月リリースでStreamable HTTPトランスポート、.well-known URLディスカバリ、構造化ツールアノテーション、SDKティアの標準化を実装。次回リリースは2026年半ばを予定しており、非同期操作、ドメイン固有拡張、エージェント間通信を含む。 

  22. Model2Vec Releases. v0.4.0(2025年2月): トレーニング・ファインチューニングサポート。v0.5.0(2025年4月): バックエンド書き直し、量子化、次元削減。v0.7.0(2025年10月): 語彙量子化、BPE/Unigramトークナイザーサポート。 

  23. Smart Connections for Obsidian. Smart Connections v4: ローカルファーストのAI embeddings、初回インデックス作成後はオフラインでセマンティック検索が動作。 

  24. potion-multilingual-128M. Minish Lab, 2025年5月. 101言語対応の静的embeddingモデル、最高性能の多言語静的embeddings。他のpotionモデルと同じnumpyのみの依存関係。 

  25. MCPVault v0.11.0. 2026年3月. frontmatterとハッシュタグをカウント付きでスキャンする新しいlist_all_tagsツール。ドット付きフォルダの処理改善、.baseおよび.canvasファイルのサポート。パッケージ名がnpmで@bitbonsai/mcpvaultに変更。 

  26. sqlite-vec v0.1.7 Release. 2026年3月17日. 安定版リリース: vec0仮想テーブルのDELETEサポート、ページネーション用のKNN距離制約、ファズテストの改善。DiskANN近似最近傍インデックスは将来のリリースで提供予定。 

  27. Introduction to Bases. v1.9.10で導入されたObsidianコアプラグイン。frontmatterプロパティをフィールドとして使用し、Vaultファイルに対するデータベースライクなビュー(テーブル、ギャラリー、カレンダー、カンバンボード)を提供。ファイルは.base形式で保存。 

  28. Obsidian 1.12 Desktop Changelog. 2026年2月27日. ターミナルベースのVault自動化のためのObsidian CLIを導入。コマンドには検索、デイリーノート、テンプレート、プロパティ、プラグイン、タスク、開発者ツールを含む。CLIドキュメント。 

  29. Claudian. Vault内にAIコラボレーターとしてClaude Codeを組み込むObsidianプラグイン。サイドバーチャット、コンテキスト対応プロンプト、ビジョンサポート、スラッシュコマンド、権限モードを提供。 

  30. Agent Client. Agent Client Protocol(ACP)を通じてClaude Code、Codex CLI、Gemini CLIの統合インターフェースを提供するObsidianプラグイン。ノートのメンション、シェル実行、アクション承認をサポート。 

  31. Obsidian iOS Changelog. 2026年初頭のアップデートには、他のアプリからVaultへ直接コンテンツを保存するShare Extension、デイリーノートとブックマークウィジェットの修正、View Noteウィジェットの更新改善を含む。 

VAULT obsidian.md INDEXED