obsidian:~/vault$ search --hybrid obsidian

Przykładowa lokalizacja sejfu

# Obsidian jako infrastruktura AI: kompletna dokumentacja techniczna

words: 12239 read_time: 47m updated: 2026-04-16 11:23
$ retriever search --hybrid obsidian

Kluczowe wnioski

Inżynieria kontekstu, nie robienie notatek. Wartość skarbca Obsidian dla AI nie tkwi w samych notatkach, lecz w warstwie wyszukiwania, która czyni je dostępnymi do odpytywania. Skarbiec z 16 000 plików bez systemu wyszukiwania to baza danych tylko do zapisu. Skarbiec z 200 plików z wyszukiwaniem hybrydowym i integracją MCP to baza wiedzy AI. Infrastruktura wyszukiwania jest produktem. Notatki to surowiec.

Wyszukiwanie hybrydowe przewyższa czysto słownikowe lub czysto semantyczne. BM25 wychwytuje dokładne identyfikatory i nazwy funkcji. Wyszukiwanie wektorowe wychwytuje synonimy i dopasowania koncepcyjne w różnej terminologii. Reciprocal Rank Fusion (RRF) łączy oba podejścia bez konieczności kalibracji wyników. Żadna z metod osobno nie pokrywa obu trybów awarii. Badania na MS MARCO passage ranking potwierdzają ten wzorzec: wyszukiwanie hybrydowe konsekwentnie przewyższa każdą z metod stosowaną w izolacji.3 Szczegółowa analiza hybrydowego retrievera omawia matematykę RRF, przykłady obliczeniowe z rzeczywistymi liczbami, analizę trybów awarii oraz interaktywny kalkulator fuzji.

MCP zapewnia narzędziom AI bezpośredni dostęp do skarbca. Serwery Model Context Protocol (MCP) udostępniają retriever jako narzędzie, które Claude Code, Codex CLI, Cursor i inne narzędzia AI mogą wywoływać bezpośrednio. Agent odpytuje skarbiec, otrzymuje wyniki rankingowe z atrybucją źródeł i wykorzystuje kontekst bez ładowania całych plików. Serwer MCP to cienka warstwa opakowująca silnik wyszukiwania.

Podejście local-first oznacza zerowe koszty API i pełną prywatność. Cały stos działa na jednej maszynie: SQLite do przechowywania danych, Model2Vec do embeddingów, FTS5 do wyszukiwania słownikowego, sqlite-vec do wektorowego KNN. Żadnych usług chmurowych, żadnych wywołań API, żadnej zależności od sieci. Prywatne notatki nigdy nie opuszczają maszyny. Pełne ponowne generowanie embeddingów dla 49 746 fragmentów kosztowałoby około 0,30 USD w cenach API OpenAI, ale rzeczywistymi kosztami są opóźnienia, narażenie prywatności i zależność od sieci w systemie, który powinien działać offline.4

Indeksowanie przyrostowe utrzymuje aktualność systemu w czasie poniżej 10 sekund. Porównanie czasu modyfikacji plików wykrywa zmiany. Tylko zmodyfikowane pliki są ponownie dzielone na fragmenty i ponownie osadzane wektorowo. Pełna reindeksacja na sprzęcie Apple z serii M zajmuje około czterech minut. Przyrostowe aktualizacje przy typowych edycjach dnia trwają poniżej dziesięciu sekund. System pozostaje aktualny bez ręcznej interwencji.

Architektura skaluje się od 200 do ponad 20 000 notatek. Ten sam trójwarstwowy projekt (przyjmowanie, wyszukiwanie, integracja) działa przy dowolnym rozmiarze skarbca. Można zacząć od wyszukiwania wyłącznie BM25 w małym skarbcu. Wyszukiwanie wektorowe warto dodać, gdy kolizje słów kluczowych stają się problemem. Fuzję RRF dodaje się, gdy potrzebne są zarówno dokładne, jak i semantyczne dopasowania. Każda warstwa jest niezależnie użyteczna i niezależnie usuwalna.


Jak korzystać z tego przewodnika

Niniejszy przewodnik obejmuje kompletny system. Punkt wejścia zależy od aktualnej sytuacji:

Opis sytuacji Zacznij tutaj Następnie przejdź do
Początkujący w Obsidian + AI Dlaczego Obsidian jako infrastruktura AI, Szybki start Architektura skarbca, Architektura serwera MCP
Istniejący skarbiec, potrzebny dostęp AI Architektura serwera MCP, Integracja z Claude Code Modele embeddingów, Wyszukiwanie pełnotekstowe z FTS5
Budowa systemu wyszukiwania Kompletny pipeline wyszukiwania, Reciprocal Rank Fusion Optymalizacja wydajności, Rozwiązywanie problemów
Kontekst zespołowy lub korporacyjny Ramowy model decyzji, Wzorce grafów wiedzy Przepisy dla programistów, Przewodnik migracji

Sekcje oznaczone Kontrakt zawierają szczegóły implementacyjne, bloki konfiguracji i tryby awarii. Sekcje oznaczone Narracja skupiają się na koncepcjach, decyzjach architektonicznych i uzasadnieniu wyborów projektowych. Sekcje oznaczone Przepis zawierają instrukcje krok po kroku.


Dlaczego Obsidian jako infrastruktura AI

Teza tego przewodnika: Skarbce Obsidian stanowią najlepszy substrat dla osobistych baz wiedzy AI, ponieważ działają lokalnie, opierają się na czystym tekście, mają strukturę grafową, a użytkownik kontroluje każdą warstwę stosu.

Co Obsidian daje AI, a alternatywy nie

Pliki markdown w czystym tekście. Każda notatka to plik .md w systemie plików. Żadnego zastrzeżonego formatu, żadnego eksportu bazy danych, żadnego API wymaganego do odczytania treści. Każde narzędzie potrafiące czytać pliki może odczytać skarbiec. grep, ripgrep, Python’s pathlib, SQLite FTS5 — wszystkie działają bezpośrednio na plikach źródłowych. Budując system wyszukiwania, indeksuje się pliki, a nie odpowiedzi API. Indeks jest zawsze spójny ze źródłem, ponieważ źródłem jest system plików.

Architektura local-first. Skarbiec znajduje się na maszynie użytkownika. Żadnego serwera, żadnej zależności od synchronizacji chmurowej, żadnych limitów szybkości API, żadnych warunków użytkowania regulujących sposób przetwarzania własnych treści. Można osadzać wektorowo, indeksować, dzielić na fragmenty i przeszukiwać notatki bez jakiejkolwiek usługi zewnętrznej. Ma to znaczenie dla infrastruktury AI, ponieważ pipeline wyszukiwania działa tak szybko, jak pozwala dysk, a nie tak szybko, jak odpowiada endpoint API. Ma to również znaczenie dla prywatności: notatki osobiste zawierające dane uwierzytelniające, dane zdrowotne, informacje finansowe i prywatne refleksje nigdy nie opuszczają maszyny.

Struktura grafowa dzięki wiki-linkom. Składnia [[wiki-link]] Obsidian tworzy graf skierowany pomiędzy notatkami. Notatka o implementacji OAuth odsyła do notatek o rotacji tokenów, zarządzaniu sesjami i bezpieczeństwie API. Struktura grafowa koduje relacje między koncepcjami wyselekcjonowane przez człowieka. Embeddingi wektorowe wychwytują podobieństwo semantyczne, ale wiki-linki wychwytują celowe powiązania, które autor utworzył w trakcie myślenia o danym temacie. Graf to sygnał, którego embeddingi nie są w stanie odtworzyć.

Ekosystem wtyczek. Obsidian posiada ponad 2500 wtyczek społecznościowych (stan na marzec 2026, wzrost z ponad 1800 w połowie 2025 roku). Dataview odpytuje skarbiec jak bazę danych. Templater generuje notatki z szablonów z logiką JavaScript. Integracja z Git synchronizuje skarbiec z repozytorium. Linter wymusza spójność formatowania. Wbudowana wtyczka Bases (wprowadzona w wersji 1.9.10) dodaje widoki podobne do baz danych — tabele, galerie, kalendarze i tablice kanban — ponad plikami skarbca, wykorzystując właściwości frontmatter jako pola, zapisywane jako pliki .base.27 Wtyczki te dodają strukturę do skarbca bez zmiany bazowego formatu czystego tekstu. System wyszukiwania indeksuje wynik działania tych wtyczek, nie same wtyczki.

Ponad 5 milionów użytkowników. Obsidian ma dużą aktywną społeczność tworzącą szablony, przepływy pracy, wtyczki i dokumentację. W przypadku napotkania problemu z organizacją skarbca lub konfiguracją wtyczki, z dużym prawdopodobieństwem ktoś już udokumentował rozwiązanie. Społeczność tworzy również narzędzia powiązane z Obsidian: serwery MCP, skrypty indeksujące, pipeline’y publikacji i wrappery API.

Czego sam system plików nie zapewnia

Katalog z plikami markdown ma przewagę czystego tekstu, ale brakuje mu trzech rzeczy, które dodaje Obsidian:

  1. Linki dwukierunkowe. Obsidian automatycznie śledzi backlinki. Gdy notatka A odsyła do notatki B, notatka B pokazuje, że notatka A się do niej odwołuje. Panel grafowy wizualizuje klastry powiązań. Ta dwukierunkowa świadomość to metadane, których surowy system plików nie zapewnia.

  2. Podgląd na żywo z renderowaniem wtyczek. Zapytania Dataview, diagramy Mermaid i bloki objaśnień renderują się w czasie rzeczywistym. Doświadczenie pisania jest bogatsze niż w edytorze tekstu, podczas gdy format przechowywania pozostaje czystym tekstem. Pisanie i organizacja odbywają się w bogatym środowisku; system wyszukiwania indeksuje surowy markdown.

  3. Infrastruktura społecznościowa. Odkrywanie wtyczek, marketplace motywów, usługa synchronizacji (opcjonalna), usługa publikacji (opcjonalna) i ekosystem dokumentacji. Każdą pojedynczą funkcję można odtworzyć za pomocą samodzielnych narzędzi, ale Obsidian łączy je w spójny przepływ pracy.

Czego Obsidian NIE robi (i co należy zbudować samodzielnie)

Obsidian nie zawiera infrastruktury wyszukiwania. Posiada podstawowe wyszukiwanie (pełnotekstowe, po nazwie pliku, po tagu), ale nie oferuje pipeline’u embeddingów, wyszukiwania wektorowego, rankingu fuzyjnego, serwera MCP, filtrowania danych uwierzytelniających, strategii podziału na fragmenty ani hooków integracyjnych dla zewnętrznych narzędzi AI. Niniejszy przewodnik obejmuje infrastrukturę budowaną na bazie Obsidian. Skarbiec jest substratem. Pipeline wyszukiwania, serwer MCP i hooki integracyjne stanowią infrastrukturę.

Opisana tu architektura jest zorientowana na markdown, nie wyłączna dla Obsidian. W przypadku korzystania z Logseq, Foam, Dendron lub zwykłego katalogu z plikami markdown, pipeline wyszukiwania działa identycznie. Chunker odczytuje pliki .md. Embedder przetwarza ciągi tekstowe. Indekser zapisuje do SQLite. Żaden z tych komponentów nie zależy od funkcji specyficznych dla Obsidian. Wkładem Obsidian jest środowisko pisania i organizacji, które produkuje pliki markdown indeksowane przez retriever.


Szybki start: pierwszy sejf połączony z AI

Ta sekcja pozwala połączyć sejf z narzędziem AI w pięć minut. Obejmuje instalację Obsidian, utworzenie sejfu, instalację serwera MCP oraz uruchomienie pierwszego zapytania. Szybki start wykorzystuje społecznościowy serwer MCP dla natychmiastowych rezultatów. Kolejne sekcje opisują budowę własnego pipeline’u wyszukiwania do zastosowań produkcyjnych.

Wymagania wstępne

  • macOS, Linux lub Windows
  • Node.js 18+ (do serwera MCP)
  • Obsidian 1.12+ (do integracji z CLI; wcześniejsze wersje działają w konfiguracjach opartych wyłącznie na MCP)
  • Zainstalowany Claude Code, Codex CLI lub Cursor

Krok 1: Utworzenie sejfu

Należy pobrać Obsidian ze strony obsidian.md i utworzyć nowy sejf. Warto wybrać łatwą do zapamiętania lokalizację — serwer MCP wymaga ścieżki bezwzględnej.

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

Warto dodać kilka notatek, aby wyszukiwarka miała z czym pracować. Nawet 10–20 notatek wystarczy, by zobaczyć rezultaty. Każda notatka powinna być plikiem .md z sensownym tytułem i co najmniej jednym akapitem treści.

Krok 2: Instalacja serwera MCP

Kilka społecznościowych serwerów MCP zapewnia natychmiastowy dostęp do sejfu. Ekosystem znacząco się rozwinął w latach 2025–2026. Wśród istotnych aktualizacji warto wyróżnić MCPVault v0.11.0 (marzec 2026), który dodał list_all_tags do skanowania frontmatter i hashtagów z licznikami, ulepszył obsługę folderów z kropką oraz wsparcie dla plików .base i .canvas.25 Pakiet został również przemianowany na @bitbonsai/mcpvault w npm.

Serwer Autor Transport Wymaga wtyczki Kluczowa funkcja
obsidian-mcp-server StevenStavrakis STDIO Nie Lekki, oparty na plikach
mcp-obsidian MarkusPfundstein STDIO Lokalny REST API Pełne CRUD sejfu przez REST
obsidian-mcp-tools jacksteamdev STDIO Tak (wtyczka) Wyszukiwanie semantyczne + Templater
obsidian-claude-code-mcp iansinnott WebSocket Tak (wtyczka) Automatyczne wykrywanie dla Claude Code
obsidian-mcp-server cyanheads STDIO Lokalny REST API Zarządzanie tagami i frontmatter

Do szybkiego startu najprostszą opcją jest serwer oparty na plikach, który bezpośrednio odczytuje pliki .md:

npm install -g obsidian-mcp-server

Krok 3: Konfiguracja narzędzia AI

Claude Code — należy dodać do ~/.claude/settings.json:

{
  "mcpServers": {
    "obsidian": {
      "command": "obsidian-mcp-server",
      "args": ["--vault", "/absolute/path/to/your/vault"]
    }
  }
}

Codex CLI — należy dodać do .codex/config.toml:

[mcp_servers.obsidian]
command = "obsidian-mcp-server"
args = ["--vault", "/absolute/path/to/your/vault"]

Cursor — należy dodać do .cursor/mcp.json:

{
  "mcpServers": {
    "obsidian": {
      "command": "obsidian-mcp-server",
      "args": ["--vault", "/absolute/path/to/your/vault"]
    }
  }
}

Krok 4: Uruchomienie pierwszego zapytania

Należy otworzyć narzędzie AI i zadać pytanie, na które notatki w sejfie mogą odpowiedzieć:

Search my Obsidian vault for notes about [topic you wrote about]

Narzędzie AI wywołuje serwer MCP, który przeszukuje sejf i zwraca pasujące treści. W wynikach powinny pojawić się ścieżki plików oraz odpowiednie fragmenty.

Co właśnie powstało

Lokalna baza wiedzy została połączona z narzędziem AI za pośrednictwem standardowego protokołu. Serwer MCP odczytuje pliki sejfu, wykonuje podstawowe wyszukiwanie i zwraca wyniki. To minimalna działająca wersja.

Czego ten szybki start NIE zapewnia: - Wyszukiwanie hybrydowe (BM25 + wyszukiwanie wektorowe + fuzja RRF) - Wyszukiwanie semantyczne oparte na embeddingach - Filtrowanie poświadczeń - Indeksowanie przyrostowe - Automatyczne wstrzykiwanie kontekstu za pomocą hooków

Dalsza część przewodnika opisuje budowę każdej z tych funkcji. Szybki start potwierdza koncepcję. Pełny pipeline zapewnia wyszukiwanie o jakości produkcyjnej.


Obsidian CLI dla przepływów pracy z AI

Obsidian 1.12 (luty 2026) wprowadził wbudowany interfejs wiersza poleceń, otwierając nową płaszczyznę integracji dla przepływów pracy z AI.28 CLI działa jako zdalny kontroler GUI Obsidian — aplikacja musi być uruchomiona (lub uruchomi się automatycznie przy pierwszym poleceniu). Aktywacja: Ustawienia > Ogólne > Interfejs wiersza poleceń.

Dlaczego CLI ma znaczenie dla infrastruktury AI

CLI zapewnia programistyczny dostęp do natywnych operacji Obsidian, które wcześniej wymagały GUI lub APIów wtyczek. Kluczowe możliwości dla przepływów pracy z AI:

  • Wyszukiwanie ze skryptów i hooków. obsidian search "query" oraz obsidian search:context "query" uruchamiają przeszukiwanie sejfu z dowolnego skryptu powłoki, hooka lub pipeline’u automatyzacji. Wariant search:context zwraca pasujące wiersze z otaczającym kontekstem, co jest przydatne do zasilania wyników w promptach AI.
  • Automatyzacja notatek dziennych. obsidian daily otwiera lub tworzy dzisiejszą notatkę dzienną. W połączeniu ze skryptami powłoki umożliwia to zautomatyzowane przepływy codziennych podsumowań — hook może dołączać wygenerowane przez AI streszczenia do notatki dziennej.
  • Tworzenie notatek z szablonów. obsidian template list i obsidian template create generują notatki z szablonów Templater lub wbudowanych, umożliwiając agentom AI tworzenie ustrukturyzowanych wpisów w sejfie bez bezpośredniego pisania plików markdown.
  • Zarządzanie właściwościami. obsidian property set i obsidian property get odczytują i zapisują właściwości frontmatter, umożliwiając aktualizację metadanych ze skryptów bez parsowania YAML.
  • Sterowanie wtyczkami. obsidian plugin enable/disable/list zarządza wtyczkami programistycznie — przydatne do przełączania wtyczek indeksujących podczas operacji wsadowych.
  • Zarządzanie zadaniami. obsidian task list/add/complete zapewnia ustrukturyzowany dostęp do zadań, przydatny dla agentów AI zarządzających elementami pracy w sejfie.

CLI a MCP w kontekście dostępu AI

CLI i serwery MCP pełnią różne role i są komplementarne, nie konkurencyjne:

Aspekt Obsidian CLI Serwer MCP
Wywołujący Skrypty powłoki, hooki, zadania cron Agenci AI (Claude Code, Codex, Cursor)
Protokół Proces POSIX (stdin/stdout/stderr) MCP (JSON-RPC przez STDIO lub HTTP)
Mocna strona Natywne operacje Obsidian (szablony, wtyczki, właściwości) Niestandardowe wyszukiwanie (embeddingi, BM25, fuzja RRF)
Ograniczenie Brak wyszukiwania wektorowego, brak pipeline’u embeddingów Brak dostępu do wewnętrznych operacji Obsidian
Najlepsze do Skrypty automatyzacji, pipeline’y importu, akcje hooków Zapytania agentów AI w czasie rzeczywistym podczas sesji

Zalecenie: CLI najlepiej sprawdza się w automatyzacji importu (tworzenie notatek, zarządzanie właściwościami, natywne wyszukiwanie Obsidian), natomiast MCP — w wyszukiwaniu (hybrydowe wyszukiwanie z embeddingami). Hook PreToolUse może wywołać obsidian search:context jako szybkie wstępne sprawdzenie przed przejściem do pełnego retrievera MCP dla rankingowanych wyników.

Przykład: hook importu oparty na 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}"

Wtyczki agentów dla Obsidian

Rosnąca kategoria wtyczek Obsidian osadza agentów AI bezpośrednio w interfejsie sejfu, stanowiąc alternatywę dla konfiguracji zewnętrznego serwera MCP. Wtyczki te uruchamiają agenta AI w panelu bocznym Obsidian, zamiast łączyć się z zewnętrznego narzędzia.

Claudian

Claudian osadza Claude Code jako współpracownika AI w sejfie. Katalog sejfu staje się katalogiem roboczym Claude, zapewniając pełne możliwości agentowe: odczyt/zapis plików, wyszukiwanie, polecenia bash i wieloetapowe przepływy pracy.29

Kluczowe funkcje dla infrastruktury AI: - Prompty uwzględniające kontekst. Automatycznie dołącza aktywną notatkę, obsługuje wzmianki plików @nazwaNotatki, wykluczanie na podstawie tagów oraz zaznaczenie z edytora jako kontekst. - Obsługa obrazów. Analiza obrazów przez przeciągnij-i-upuść, wklejenie lub ścieżkę pliku — przydatne do przetwarzania zrzutów ekranu i diagramów przechwyconych w sejfie. - Polecenia slash. Tworzenie wielokrotnie używalnych szablonów promptów wywoływanych przez /polecenie, umożliwiających standaryzowane operacje na sejfie. - Tryby uprawnień. YOLO (automatyczna akceptacja), Safe (akceptacja każdej akcji) i Plan (tylko planowanie) z listą blokowania zabezpieczeń i ograniczeniem do sejfu.

Agent Client

Agent Client wprowadza Claude Code, Codex CLI i Gemini CLI do ujednoliconego panelu bocznego Obsidian za pośrednictwem Agent Client Protocol (ACP).30

Kluczowe funkcje: - Przełączanie między agentami. Rozmowa z Claude Code, Codex lub Gemini CLI z jednego panelu, z możliwością przełączania się między agentami w razie potrzeby. - Wzmianki notatek. Użycie @nazwaNotatki do dołączenia treści notatki w promptach, podobnie jak w Claudian, ale niezależnie od agenta. - Wykonywanie poleceń powłoki. Uruchamianie poleceń terminala bezpośrednio w czacie — skrypty budowania, polecenia git czy dowolne operacje terminala bez opuszczania konwersacji. - Akceptacja akcji. Szczegółowa kontrola nad odczytami plików, edycjami i wykonywaniem poleceń.

Kiedy używać wtyczek agentów, a kiedy zewnętrznego MCP

Scenariusz Wtyczka agenta Zewnętrzny MCP
Pisanie i edycja notatek sejfu z pomocą AI Lepsze — agent widzi kontekst edytora Działa, ale bez świadomości edytora
Rozwój kodu w wielu repozytoriach Ograniczone — zakres sejfu Lepsze — zakres projektu z pełnym dostępem do systemu plików
Wyszukiwanie w dużym zindeksowanym korpusie Tylko podstawowe wyszukiwanie Pełny pipeline hybrydowego wyszukiwania
Szybkie pytania do sejfu podczas tworzenia notatek Idealne — bez przełączania kontekstu Wymaga przejścia do terminala

Zalecenie: Wtyczki agentów sprawdzają się w przepływach pracy skoncentrowanych na sejfie (pisanie, organizacja, podsumowywanie notatek). Zewnętrzne serwery MCP są lepsze dla przepływów deweloperskich, gdzie agent AI potrzebuje pełnego pipeline’u wyszukiwania i dostępu do baz kodu poza sejfem. Oba podejścia mogą współistnieć — Claudian wewnątrz Obsidian do pracy z notatkami i Claude Code z MCP zewnętrznie do prac deweloperskich.


Schemat decyzyjny: Obsidian a alternatywy

Nie każdy przypadek użycia wymaga Obsidian. Ta sekcja pokazuje, kiedy Obsidian jest właściwym fundamentem, kiedy to przesada, a kiedy lepiej sprawdzi się inne rozwiązanie.

Drzewo decyzyjne

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.

Macierz porównawcza

Kryterium Obsidian Notion Apple Notes Zwykły system plików CLAUDE.md
Lokalność danych Tak Nie (chmura) Częściowo (iCloud) Tak Tak
Czysty tekst Tak (markdown) Nie (bloki) Nie (format własnościowy) Tak Tak
Struktura grafowa Tak (wiki-links) Częściowo (wzmianki) Nie Nie Nie
Indeksowalność przez AI Bezpośredni dostęp do plików Wymaga API Wymaga eksportu Bezpośredni dostęp do plików Już w kontekście
Ekosystem wtyczek 2500+ wtyczek Integracje Brak Nie dotyczy Nie dotyczy
Praca offline Pełna Tylko odczyt z pamięci podręcznej Częściowo Pełna Pełna
Skalowalność do 10 000+ notatek Tak Tak (z API) Pogarsza się Tak Nie (pojedynczy plik)
Koszt Bezpłatny (rdzeń) 10 USD/mies.+ Bezpłatny Bezpłatny Bezpłatny

Kiedy Obsidian to przesada

  • Kontekst jednego projektu. Jeśli AI potrzebuje kontekstu wyłącznie o bieżącym kodzie, wystarczy umieścić go w CLAUDE.md, AGENTS.md lub dokumentacji na poziomie projektu. Pliki te są częścią repozytorium i ładowane automatycznie.
  • Dane ustrukturyzowane. Jeśli treść to tabele, rekordy lub schematy, należy użyć bazy danych. Notatki w Obsidian są zorientowane na tekst ciągły. Dataview potrafi odpytywać pola frontmatter, ale prawdziwa baza danych radzi sobie z zapytaniami strukturalnymi znacznie lepiej.
  • Tymczasowe badania. Jeśli notatki zostaną usunięte po zakończeniu projektu, prostszy będzie katalog roboczy z plikami markdown. Nie warto budować infrastruktury wyszukiwania dla treści efemerycznych.

Kiedy Obsidian to właściwy wybór

  • Wiedza gromadzona przez miesiące lub lata. Wartość rośnie wraz z powiększaniem się korpusu. Skarbiec (vault) liczący 200 notatek, odpytywany codziennie przez sześć miesięcy, przynosi więcej korzyści niż skarbiec z 5000 notatek odpytywany jednorazowo.
  • Wiele dziedzin w jednym korpusie. Skarbiec zawierający notatki z programowania, architektury, bezpieczeństwa, projektowania i projektów osobistych czerpie korzyści z wyszukiwania międzydomenowego, którego nie zapewni CLAUDE.md ograniczony do jednego projektu.
  • Treści wrażliwe pod względem prywatności. Lokalność danych oznacza, że pipeline wyszukiwania nigdy nie przesyła treści do zewnętrznych usług. Skarbiec zawiera dokładnie to, co się w nim umieści — łącznie z treściami, których nie należałoby przesyłać do usługi chmurowej.

Model mentalny: trzy warstwy

System składa się z trzech warstw, które działają niezależnie, lecz w połączeniu wzajemnie się wzmacniają. Każda warstwa odpowiada za inny aspekt i ma odmienny tryb awarii.

┌─────────────────────────────────────────────────────┐
                 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 (przyjmowanie) określa, co trafia do skarbca. Bez selekcji skarbiec gromadzi szum: zrzuty ekranu tweetów, skopiowane artykuły bez adnotacji, niedokończone myśli pozbawione kontekstu. Warstwa intake odpowiada za kontrolę jakości w punkcie wejścia. Pipeline oceniający, konwencja tagowania lub ręczny proces weryfikacji — dowolny mechanizm zapewniający, że skarbiec zawiera treści warte wyszukiwania.

Retrieval (wyszukiwanie) sprawia, że skarbiec staje się odpytywalny. To silnik systemu: dzielenie notatek na jednostki wyszukiwania (chunking), osadzanie fragmentów w przestrzeni wektorowej (embeddings), indeksowanie pod kątem wyszukiwania słów kluczowych i semantycznego oraz fuzja wyników za pomocą RRF. Warstwa retrieval przekształca katalog plików w odpytywalną bazę wiedzy. Bez tej warstwy skarbiec jest dostępny jedynie przez ręczne przeglądanie i podstawowe wyszukiwanie, lecz nie programistycznie dla narzędzi AI.

Integration (integracja) łączy warstwę retrieval z narzędziami AI. Serwer MCP udostępnia wyszukiwanie jako wywoływalne narzędzie. Hooki automatycznie wstrzykują kontekst. Skille przechwytują nową wiedzę z powrotem do skarbca. Warstwa integracji stanowi interfejs między bazą wiedzy a agentami AI, którzy z niej korzystają.

Warstwy są celowo oddzielone. Pipeline oceniający intake nie wie nic o embeddingach. Retriever nie wie nic o regułach routingu sygnałów. Serwer MCP nie wie nic o tym, jak powstawały notatki. To rozdzielenie oznacza, że każdą warstwę można usprawniać niezależnie. Można wymienić model embeddingów bez zmiany pipeline’u intake. Można dodać nową funkcję MCP bez modyfikacji retrievera. Można zmienić heurystyki oceny sygnałów bez ingerencji w indeks.


Architektura vault do wykorzystania przez AI

Vault zoptymalizowany pod kątem wyszukiwania przez AI wymaga innych konwencji niż vault zoptymalizowany do osobistego przeglądania. Ta sekcja obejmuje strukturę folderów, schemat notatek, konwencje frontmatter oraz konkretne wzorce poprawiające jakość wyszukiwania.

Struktura folderów

Warto stosować numerowane prefiksy dla folderów najwyższego poziomu, tworząc przewidywalną hierarchię organizacyjną. Numery nie oznaczają priorytetu — grupują powiązane domeny i czynią strukturę łatwą do przeglądania.

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

Foldery, które powinny być indeksowane: wszystko, co zawiera treść w formacie markdown — projekty, obszary odpowiedzialności, zasoby, sygnały, notatki dzienne.

Foldery, które należy wykluczyć z indeksowania: szablony (zawierają zmienne zastępcze, nie treść), załączniki (pliki binarne), konfiguracja Obsidian oraz wszelkie foldery zawierające wrażliwe dane, których nie powinno być w indeksie wyszukiwania.

Plik .indexignore

Należy utworzyć plik .indexignore w katalogu głównym vault, aby jawnie wykluczyć ścieżki z indeksu wyszukiwania. Składnia odpowiada .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/

Indekser odczytuje ten plik przed skanowaniem i całkowicie pomija pasujące ścieżki. Pliki w wykluczonych ścieżkach nigdy nie są dzielone na fragmenty, nigdy nie otrzymują embeddings i nigdy nie pojawiają się w wynikach wyszukiwania.

Schemat notatek

Każda notatka powinna zawierać YAML frontmatter. Moduł wyszukiwania wykorzystuje pola frontmatter do filtrowania i wzbogacania kontekstu:

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

Wymagane pola dla wyszukiwania:

  • title — używane w wyświetlaniu wyników wyszukiwania i kontekście nagłówków dla BM25
  • type — umożliwia zapytania filtrowane po typie („pokaż tylko MOC” lub „tylko sygnały”)
  • tags — indeksowane w kontekście nagłówków FTS5 z wagą 0.3, zapewniające dopasowania słów kluczowych nawet gdy treść używa innej terminologii

Pola opcjonalne, ale wartościowe:

  • domain — umożliwia zapytania ograniczone do domeny („szukaj tylko w notatkach o bezpieczeństwie”)
  • source — atrybucja dla przechwyconych treści; moduł wyszukiwania może dołączać adresy URL źródeł do wyników
  • status — pozwala wykluczać zarchiwizowane lub szkicowe notatki z aktywnego wyszukiwania

Konwencje podziału na fragmenty

Moduł wyszukiwania dzieli treść na fragmenty (chunking) na granicach nagłówków H2 (##). Oznacza to, że struktura notatki bezpośrednio wpływa na granularność wyszukiwania:

Korzystne dla wyszukiwania:

## 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...

Trzy sekcje H2 dają trzy niezależnie przeszukiwalne fragmenty. Każdy fragment zawiera wystarczający kontekst, aby embedding uchwycił jego znaczenie. Zapytanie o „obsługę wygasłych tokenów” dopasowuje się konkretnie do trzeciego fragmentu.

Niekorzystne dla wyszukiwania:

# 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...

Jedna długa sekcja bez nagłówków H2 daje jeden duży fragment. Embedding uśrednia się po wszystkich tematach w sekcji. Zapytanie o dowolny podtemat dopasowuje się do całej notatki jednakowo.

Zasada ogólna: jeśli sekcja obejmuje więcej niż jedno zagadnienie, należy ją podzielić na podsekcje H2. Mechanizm podziału na fragmenty zajmie się resztą.

Czego nie umieszczać w notatkach

Treści obniżające jakość wyszukiwania:

  • Surowe kopie całych artykułów bez adnotacji. Moduł wyszukiwania indeksuje słowa kluczowe oryginalnego artykułu, rozmywając vault treścią, której nie napisaliśmy. Zamiast tego warto dodać podsumowanie, wyodrębnić kluczowe punkty lub zamieścić odnośnik do źródłowego URL.
  • Zrzuty ekranu bez opisu tekstowego. Moduł wyszukiwania indeksuje tekst markdown. Obraz bez tekstu alternatywnego lub otaczającego opisu jest niewidoczny zarówno dla BM25, jak i wyszukiwania wektorowego.
  • Ciągi uwierzytelniające. Klucze API, tokeny, hasła, ciągi połączeń. Nawet przy filtrowaniu poświadczeń najbezpieczniejszym podejściem jest nigdy nie wklejać sekretów do notatek. Zamiast tego należy odwoływać się do nich po nazwie („token API dla Cloudflare w ~/.env”).
  • Treści generowane automatycznie bez kuracji. Jeśli narzędzie generuje notatkę (transkrypcja spotkania, wyróżnienia z Readwise, import RSS), należy ją przejrzeć i opatrzyć adnotacjami przed włączeniem do stałego vault. Niekurowane automatyczne importy zwiększają objętość bez dodawania wartości wyszukiwawczej.

Ekosystem wtyczek dla przepływów pracy z AI

Wtyczki Obsidian poprawiające jakość vault pod kątem wyszukiwania AI dzielą się na trzy kategorie: strukturalne (wymuszają spójność), zapytaniowe (udostępniają metadane) i synchronizacyjne (utrzymują vault w aktualnym stanie).

Niezbędne wtyczki

Dataview. Umożliwia odpytywanie vault jak bazy danych z wykorzystaniem pól frontmatter. Pozwala tworzyć dynamiczne indeksy: „wszystkie notatki z tagiem security zaktualizowane w ostatnich 30 dniach” lub „wszystkie notatki projektowe ze statusem active.” Dataview nie wspomaga bezpośrednio wyszukiwania, ale pomaga identyfikować luki w pokryciu vault i znajdować notatki wymagające aktualizacji.

TABLE type, domain, updated
FROM "03-resources"
WHERE status = "active"
SORT updated DESC
LIMIT 20

Templater. Tworzy notatki z szablonów z dynamicznymi polami. Zapewnia, że każda nowa notatka zaczyna się od poprawnego frontmatter, wykorzystując szablon z wstępnie wypełnionymi polami created, type i domain. Spójny frontmatter poprawia filtrowanie w wyszukiwaniu.

<%* /* 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

## Źródła i odniesienia

Linter. Wymusza reguły formatowania w całym sejfie. Spójna hierarchia nagłówków (H1 dla tytułu, H2 dla sekcji, H3 dla podsekcji) sprawia, że chunker generuje przewidywalne wyniki. Reguły Lintera istotne dla wyszukiwania:

  • Inkrementacja nagłówków: wymuszanie sekwencyjnych poziomów nagłówków (bez przeskakiwania z H1 do H3)
  • YAML title: dopasowanie do nazwy pliku
  • Końcowe spacje: usuwanie (zapobiega artefaktom tokenizacji FTS5)
  • Kolejne puste linie: ograniczenie do 1 (czystsze fragmenty)

Integracja z Git. Kontrola wersji dla sejfu. Umożliwia śledzenie zmian w czasie, synchronizację między urządzeniami i odzyskiwanie przypadkowo usuniętych plików. Git dostarcza również dane mtime, które indekser wykorzystuje do przyrostowego wykrywania zmian.

Wtyczki wspierające indeksowanie

Smart Connections. Wtyczka Obsidian zapewniająca semantyczne wyszukiwanie wspierane przez AI bezpośrednio w Obsidian. Smart Connections v4 domyślnie tworzy lokalne embeddingi — po zaindeksowaniu sejfu semantyczne powiązania i wyszukiwanie działają całkowicie offline, bez wywołań API.23 Choć system wyszukiwania opisany w tym przewodniku jest zewnętrzny wobec Obsidian (działa jako potok Python), Smart Connections przydaje się do eksploracji semantycznych powiązań podczas pisania. Oba systemy indeksują tę samą treść, lecz służą różnym celom: Smart Connections do odkrywania powiązań w edytorze, zewnętrzny retriever do integracji z narzędziami AI przez MCP.

Metadata Menu. Zapewnia edycję strukturalnego frontmatter z autouzupełnianiem wartości pól. Redukuje literówki w polach type, domain i tags. Spójne metadane poprawiają dokładność filtrowania wyników wyszukiwania.

Wtyczki pogarszające indeksowanie

Excalidraw. Przechowuje rysunki jako JSON osadzony w plikach markdown. JSON jest składniowo poprawnym markdownem, ale po podziale na fragmenty i wygenerowaniu embeddingów daje bezużyteczne wyniki. Należy wykluczyć pliki Excalidraw z indeksu za pomocą .indexignore lub filtrowania po rozszerzeniu pliku.

Kanban. Przechowuje stan tablicy jako specjalnie sformatowany markdown. Format jest zaprojektowany pod renderowanie Kanban, nie pod wyszukiwanie prozy. Chunker generuje fragmenty tytułów kart i metadanych, które nie dają dobrych embeddingów. Należy wykluczyć tablice Kanban z indeksu.

Calendar. Tworzy notatki dzienne z minimalną treścią (często tylko nagłówek z datą). Puste lub prawie puste notatki generują niskojakościowe fragmenty. Jeśli korzysta się z notatek dziennych, warto umieszczać w nich treści merytoryczne lub wykluczyć folder notatek dziennych z indeksu.

Konfiguracja wtyczek, która ma znaczenie

File recovery → Włączone. Chroni przed przypadkowym usunięciem notatek. Nie jest bezpośrednio związane z wyszukiwaniem, ale ma kluczowe znaczenie dla bazy wiedzy, na której się polega.

Strict line breaks → Wyłączone. Standardowe łamanie wierszy w markdown (podwójna nowa linia dla akapitu) generuje czystsze fragmenty niż tryb ścisły Obsidian (pojedyncza nowa linia jako <br>).

Default new file location → Wyznaczony folder. Przekierowanie nowych plików do 00-inbox/ zapobiega zanieczyszczaniu folderów domenowych przez nieskategoryzowane notatki. Skrzynka odbiorcza pełni rolę strefy tymczasowej; pliki trafiają do folderów domenowych po segregacji.

Wiki-link format → Najkrótsza ścieżka, gdy to możliwe. Krótsze cele linków ułatwiają retrieverowi rozwiązywanie struktury powiązań podczas indeksowania.


Modele embeddingów: wybór i konfiguracja

Model embeddingów przekształca fragmenty tekstu w wektory numeryczne na potrzeby wyszukiwania semantycznego. Wybór modelu determinuje jakość wyszukiwania, rozmiar indeksu, szybkość generowania embeddingów oraz zależności środowiskowe. W tej sekcji wyjaśniono, dlaczego Model2Vec potion-base-8M jest domyślnym wyborem i kiedy warto rozważyć alternatywy.

Dlaczego Model2Vec potion-base-8M

Model: minishlab/potion-base-8M Parametry: 7,6 miliona Wymiary: 256 Rozmiar: ~30 MB Zależności: model2vec (tylko numpy, bez PyTorch) Inferencja: wyłącznie CPU, statyczne embeddingi słów (brak warstw atencji)

Model2Vec destyluje wiedzę transformera zdaniowego do statycznych embeddingów tokenów. Zamiast uruchamiać warstwy atencji na wejściu (jak robią to BERT, MiniLM i inne modele transformerowe), Model2Vec generuje wektory poprzez średnią ważoną wstępnie obliczonych embeddingów tokenów.5 Praktyczna konsekwencja: szybkość generowania embeddingów jest 50–500 razy wyższa niż w modelach opartych na transformerach, ponieważ nie występują obliczenia sekwencyjne.

W zestawie benchmarków MTEB potion-base-8M osiąga 89% wydajności all-MiniLM-L6-v2 (średnia 50,03 wobec 56,09).6 11-procentowa różnica jakości to kompromis za przewagę w szybkości i prostocie. W przypadku krótkich fragmentów markdown (średnio 200–400 słów w typowym sejfie) różnica jakościowa jest mniej wyraźna niż w dłuższych dokumentach, ponieważ oba modele zbiegają do podobnych reprezentacji dla krótkich, skoncentrowanych tekstów.

Konfiguracja

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

Leniwe ładowanie. Model jest ładowany przy pierwszym użyciu, nie podczas importu. Import modułu embeddera nie wiąże się z żadnym kosztem, gdy retriever działa w trybie awaryjnym wyłącznie z BM25 (np. gdy środowisko wirtualne embeddingów nie jest zainstalowane).

Izolowane środowisko wirtualne. Model działa w dedykowanym venv (np. ~/.claude/venvs/memory/), aby uniknąć konfliktów zależności z resztą narzędzi. Funkcja _activate_venv() dodaje site-packages tego środowiska do sys.path w czasie wykonywania.

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

Przetwarzanie wsadowe. Embedder przetwarza teksty w partiach po 64, aby zamortyzować narzut Model2Vec. Indekser przekazuje fragmenty do embed_batch() zamiast generować embeddingi pojedynczo.

Kiedy wybrać alternatywy

Model Wym. Rozmiar Szybkość Jakość (MTEB) Najlepszy do
potion-base-8M 256 30 MB 500x 50,03 Domyślny: lokalny, szybki, bez GPU
potion-base-32M 256 120 MB 400x 52,46 Wyższa jakość, nadal statyczny
potion-retrieval-32M 256 120 MB 400x 36,35 (retrieval) Zoptymalizowany pod retrieval, statyczny
potion-multilingual-128M 256 ~500 MB 300x Wielojęzyczne sejfy (101 języków)
all-MiniLM-L6-v2 384 80 MB 1x 56,09 Wyższa jakość, nadal lokalny
nomic-embed-text-v1.5 768 270 MB 0,5x 62,28 Najlepsza jakość lokalna
text-embedding-3-small 1536 API N/A 62,30 Oparty na API, najwyższa jakość

Wybierz potion-base-32M, gdy zależy na lepszej jakości niż potion-base-8M bez opuszczania rodziny statycznych embeddingów. Wydany w styczniu 2025, wykorzystuje większy słownik zdestylowany z baai/bge-base-en-v1.5, osiągając średnią MTEB 52,46 (5% poprawy wobec potion-base-8M), zachowując ten sam 256-wymiarowy wynik i zależność wyłącznie od numpy.20 Czterokrotnie większy plik modelu zwiększa zużycie pamięci, ale szybkość generowania embeddingów pozostaje o rzędy wielkości wyższa niż w modelach transformerowych.

Wybierz potion-retrieval-32M, gdy głównym zastosowaniem jest wyszukiwanie (a takim właśnie jest przeszukiwanie sejfu). Ten wariant jest dostrojony na podstawie potion-base-32M specjalnie pod kątem zadań wyszukiwania, osiągając 36,35 w benchmarkach retrieval MTEB wobec 33,52 dla modelu bazowego.20 Ogólna średnia MTEB spada do 49,73, ponieważ dostrajanie poświęca wydajność ogólną na rzecz zysków specyficznych dla wyszukiwania.

Wybierz potion-multilingual-128M, gdy sejf zawiera notatki w wielu językach. Wydany w maju 2025, ten 101-językowy model jest najlepiej działającym statycznym modelem embeddingów do zadań wielojęzycznych — generuje embeddingi dla dowolnego tekstu w dowolnym języku, zachowując tę samą zależność wyłącznie od numpy co inne modele potion.24 Większy plik modelu (~500 MB) to kompromis za możliwości międzyjęzykowe. Warto go użyć, gdy notatki są prowadzone po japońsku, chińsku, niemiecku lub w innych językach obok angielskiego.

Wybierz all-MiniLM-L6-v2, gdy jakość wyszukiwania jest ważniejsza od szybkości i PyTorch jest zainstalowany. Wektory 384-wymiarowe zwiększają rozmiar bazy SQLite o ~50% w porównaniu z wektorami 256-wymiarowymi. Szybkość generowania embeddingów spada z poniżej 1 minuty do ~10 minut przy pełnej reindeksacji 15 000 plików na sprzęcie z procesorami serii M.

Wybierz nomic-embed-text-v1.5, gdy potrzebna jest najlepsza możliwa lokalna jakość wyszukiwania przy akceptacji wolniejszego indeksowania. Wektory 768-wymiarowe mniej więcej potrajają rozmiar bazy. Wymaga PyTorch oraz nowoczesnego procesora lub GPU.

Wybierz text-embedding-3-small, gdy opóźnienia sieciowe i kwestie prywatności stanowią akceptowalny kompromis. API generuje embeddingi najwyższej jakości, ale wprowadza zależność od chmury, koszt per token (0,02 USD za milion tokenów) i wysyła treści na serwery OpenAI.

Zostań przy potion-base-8M we wszystkich pozostałych przypadkach. Przewaga szybkości jest kluczowa dla iteracyjnego indeksowania (reindeksacja podczas prac deweloperskich), zależność wyłącznie od numpy eliminuje złożoność instalacji PyTorch, a wektory 256-wymiarowe utrzymują kompaktowy rozmiar bazy danych.

Kwantyzacja i redukcja wymiarowości

Model2Vec w wersji 0.5.0+ obsługuje ładowanie modeli ze zredukowaną precyzją i liczbą wymiarów.20 Jest to przydatne przy wdrożeniach na sprzęcie o ograniczonych zasobach lub redukcji rozmiaru bazy bez zmiany modelu:

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)

Modele skwantyzowane zachowują niemal identyczną jakość wyszukiwania przy ułamku pierwotnego zużycia pamięci. Redukcja wymiarowości opiera się na metodzie truncation w stylu Matrioszki — pierwszych N wymiarów niesie najwięcej informacji. Redukcja z 256 do 128 wymiarów zmniejsza o połowę przestrzeń wektorową przy minimalnej utracie jakości w wyszukiwaniu krótkich tekstów.

Od maja 2025 Model2Vec obsługuje również tokenizery BPE i Unigram (oprócz WordPiece), co rozszerza zestaw transformerów zdaniowych, które można destylować do modeli statycznych.22

Dostrajanie embeddingów specyficznych dla sejfu

Model2Vec w wersji 0.4.0+ obsługuje trenowanie własnych modeli klasyfikacyjnych na bazie statycznych embeddingów, a wersja 0.7.0 dodaje kwantyzację słownika i konfigurowalny pooling do destylacji.22 Jest to istotne w przypadku sejfów ze specjalistycznym słownictwem (notatki medyczne, odniesienia prawne, żargon branżowy), gdzie domyślne modele potion mogą nie uchwycić niuansów semantycznych:

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")

W większości sejfów domyślny potion-base-8M zapewnia wystarczającą jakość wyszukiwania. Dostrajanie ma sens tylko wtedy, gdy wyszukiwanie systematycznie pomija powiązania specyficzne dla danej dziedziny, których model ogólnego przeznaczenia nie jest w stanie uchwycić.

Śledzenie hasza modelu

Indekser przechowuje hasz wyprowadzony z nazwy modelu i rozmiaru słownika. Jeśli model embeddingów zostanie zmieniony, indekser wykryje niezgodność przy następnym uruchomieniu przyrostowym i automatycznie uruchomi pełną reindeksację.

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]

Zapobiega to mieszaniu wektorów z różnych modeli w tej samej bazie danych, co prowadziłoby do bezsensownych wyników cosine similarity.

Tryby awarii

Błąd pobierania modelu. Pierwsze uruchomienie pobiera model z Hugging Face. Jeśli pobieranie się nie powiedzie (problem z siecią, firmowy firewall), retriever przełącza się na tryb wyłącznie BM25. Po pierwszym pobraniu model jest buforowany lokalnie.

Niezgodność wymiarów. Zmiana modelu bez wyczyszczenia bazy powoduje, że przechowywane wektory mają inny wymiar niż nowe embeddingi. Indekser wykrywa to za pomocą hasza modelu i uruchamia pełną reindeksację. Jeśli weryfikacja hasza zawiedzie (własny model bez prawidłowego hasza), sqlite-vec zgłosi błąd przy zapytaniach KNN z niedopasowanymi wymiarami.

Obciążenie pamięci w dużych sejfach. Generowanie embeddingów dla ponad 50 000 fragmentów w jednej partii może zużyć znaczną ilość pamięci. Indekser przetwarza dane w partiach po 64, aby ograniczyć szczytowe zużycie pamięci. Jeśli pamięć nadal stanowi problem, należy zmniejszyć rozmiar partii.


Wyszukiwanie pełnotekstowe z FTS5

Rozszerzenie FTS5 w SQLite zapewnia wyszukiwanie pełnotekstowe z rankingiem BM25. FTS5 stanowi komponent wyszukiwania słów kluczowych w potoku hybrid retrieval. W tej sekcji omówiono konfigurację FTS5, sytuacje, w których BM25 sprawdza się najlepiej, oraz jego konkretne tryby awarii.

Wirtualna tabela FTS5

CREATE VIRTUAL TABLE chunks_fts USING fts5(
    chunk_text,
    section,
    heading_context,
    content=chunks,
    content_rowid=id
);

Tryb synchronizacji treści. Parametr content=chunks informuje FTS5, że ma odwoływać się bezpośrednio do tabeli chunks, zamiast przechowywać duplikat tekstu. Zmniejsza to wymagania dotyczące pamięci o połowę, ale oznacza konieczność ręcznej synchronizacji FTS5 przy każdym wstawieniu, aktualizacji lub usunięciu fragmentów.

Kolumny. Indeksowane są trzy kolumny: - chunk_text — główna treść każdego fragmentu (waga BM25: 1.0) - section — tekst nagłówka H2 (waga BM25: 0.5) - heading_context — tytuł notatki, tagi i metadane (waga BM25: 0.3)

Ranking BM25

BM25 klasyfikuje dokumenty na podstawie częstości występowania terminów, odwrotnej częstości dokumentowej oraz normalizacji długości dokumentu. Funkcja pomocnicza bm25() w FTS5 przyjmuje wagi poszczególnych kolumn:

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;

Wagi kolumn (1.0, 0.5, 0.3) oznaczają: - Dopasowanie słowa kluczowego w chunk_text ma największy wpływ na wynik - Dopasowanie w section (nagłówek) ma o połowę mniejszy wpływ - Dopasowanie w heading_context (tytuł, tagi) stanowi 30% wpływu

Wagi te można dostosować. Jeśli skarbiec zawiera opisowe nagłówki, które dobrze przewidują jakość treści, warto zwiększyć wagę section. Jeśli tagi są kompletne i dokładne, warto zwiększyć wagę heading_context.

Kiedy BM25 wygrywa

BM25 sprawdza się doskonale w zapytaniach zawierających dokładne identyfikatory:

  • Nazwy funkcji: _rrf_fuse, embed_batch, get_stale_files
  • Flagi CLI: --incremental, --vault, --model
  • Klucze konfiguracyjne: bm25_weight, max_tokens, batch_size
  • Komunikaty błędów: SQLITE_LOCKED, ConnectionRefusedError
  • Specyficzne terminy techniczne: PostToolUse, PreToolUse, AGENTS.md

W przypadku takich zapytań BM25 natychmiast znajduje dokładne dopasowanie. Wyszukiwanie wektorowe zwróciłoby treści semantycznie powiązane, ale mogłoby uszeregować dokładne dopasowanie niżej niż ogólną dyskusję koncepcyjną.

Kiedy BM25 zawodzi

BM25 zawodzi w zapytaniach, które używają innej terminologii niż ta zawarta w przechowywanych treściach:

  • Zapytanie: „how to handle authentication failures” → Skarbiec zawiera notatki o „login error recovery” i „session expiration handling”. BM25 nie znajduje dopasowania, ponieważ słowa kluczowe się różnią.
  • Zapytanie: „what is the best way to manage state” → Skarbiec zawiera notatki o „Redux store patterns” i „context providers”. BM25 nie trafia, ponieważ „zarządzanie stanem” jest wyrażone przez konkretne nazwy technologii.

BM25 zawodzi również w przypadku kolizji słów kluczowych na dużą skalę. W skarbcu liczącym 15 000 plików wyszukiwanie „configuration” zwraca setki notatek, ponieważ niemal każda notatka projektowa wspomina o konfiguracji. Wyniki są technicznie poprawne, ale praktycznie bezużyteczne — ranking nie jest w stanie określić, która notatka o „konfiguracji” jest istotna dla bieżącego zapytania.

Tokenizer FTS5

FTS5 domyślnie używa tokenizera unicode61, który obsługuje tekst ASCII i Unicode. W przypadku skarbców z dużą ilością treści CJK (chiński, japoński, koreański) warto rozważyć tokenizer trigram:

-- For CJK-heavy vaults
CREATE VIRTUAL TABLE chunks_fts USING fts5(
    chunk_text, section, heading_context,
    content=chunks, content_rowid=id,
    tokenize='trigram'
);

Domyślny tokenizer unicode61 dzieli tekst na granicach wyrazów, co źle sprawdza się w językach bez spacji między wyrazami. Tokenizer trigram dzieli tekst co trzy znaki, umożliwiając wyszukiwanie podciągów kosztem rozmiaru indeksu (około 3 razy większego).

Konserwacja

FTS5 wymaga jawnej synchronizacji, gdy zmienia się bazowa tabela chunks:

# After inserting chunks
cursor.execute("""
    INSERT INTO chunks_fts(chunks_fts)
    VALUES('rebuild')
""")

Polecenie rebuild rekonstruuje indeks FTS5 na podstawie tabeli treści. Należy je uruchamiać po masowych wstawieniach (pełna reindeksacja), ale nie po pojedynczych aktualizacjach przyrostowych — w takim przypadku należy użyć INSERT INTO chunks_fts(rowid, chunk_text, section, heading_context) do synchronizacji poszczególnych wierszy.


Wyszukiwanie wektorowe z sqlite-vec

Rozszerzenie sqlite-vec wprowadza wyszukiwanie wektorowe KNN (K-Nearest Neighbors) do SQLite. Ta sekcja obejmuje konfigurację sqlite-vec, potok przetwarzania embeddings od notatki do przeszukiwalnego wektora oraz konkretne wzorce zapytań.

Tabela wirtualna sqlite-vec

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

Moduł vec0 przechowuje 256-wymiarowe wektory zmiennoprzecinkowe jako spakowane dane binarne. Kolumna id mapuje się 1:1 do tabeli chunks, umożliwiając złączenia między wynikami wektorowymi a metadanymi fragmentów.

Potok przetwarzania embeddings

Potok przebiega od notatki do przeszukiwalnego wektora:

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

Serializacja wektorów

Moduł struct w Python serializuje wektory zmiennoprzecinkowe do przechowywania w 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))

Zapytanie KNN

Zapytanie wyszukiwania wektorowego najpierw koduje zapytanie wejściowe, a następnie znajduje K najbliższych fragmentów według odległości kosinusowej:

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

Operator MATCH w sqlite-vec wykonuje przybliżone wyszukiwanie najbliższego sąsiada. Parametr k kontroluje liczbę zwracanych wyników. Kolumna distance zawiera odległość kosinusową (0 = identyczne, 2 = przeciwne).

Paginacja KNN z ograniczeniami odległości

Od wersji sqlite-vec v0.1.7 zapytania KNN obsługują ograniczenia WHERE distance < ?, co umożliwia paginację opartą na kursorze przez duże zbiory wyników bez ponownego skanowania wcześniejszych stron.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

Podejście to zastępuje wcześniejszy wzorzec pobierania dużej wartości k i przycinania wyników w Python, redukując zużycie pamięci przy eksploracyjnych zapytaniach w dużych skarbcach notatek.

Obsługa DELETE w tabelach vec0

sqlite-vec v0.1.7 dodał natywną obsługę DELETE dla tabel wirtualnych vec0.26 Wcześniej usunięcie wektorów wymagało porzucenia i ponownego utworzenia tabeli. Teraz ścieżka usuwania plików w indekserze może bezpośrednio usuwać wektory:

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

Upraszcza to przyrostowe reindeksowanie, gdy notatki są usuwane lub przenoszone. Indekser nie musi już utrzymywać pomocniczej tabeli „aktywnych identyfikatorów” ani wykonywać wsadowych przebudów.

Kiedy wyszukiwanie wektorowe wygrywa

Wyszukiwanie wektorowe sprawdza się najlepiej w zapytaniach, gdzie liczy się koncept, a nie konkretne słowa:

  • Zapytanie: „how to handle authentication failures” → Znajduje notatki o „login error recovery” (ta sama przestrzeń semantyczna, inne słowa kluczowe)
  • Zapytanie: „what patterns exist for caching” → Znajduje notatki o „memoization”, „Redis TTL strategies” i „HTTP cache headers” (powiązane koncepty, zróżnicowana terminologia)
  • Zapytanie: „approaches to testing asynchronous code” → Znajduje notatki o „pytest-asyncio fixtures”, „mock event loops” i „async test patterns” (ten sam koncept wyrażony przez szczegóły implementacyjne)

Kiedy wyszukiwanie wektorowe zawodzi

Wyszukiwanie wektorowe radzi sobie słabo z dokładnymi identyfikatorami:

  • Zapytanie: _rrf_fuse → Zwraca notatki o „fusion algorithms” i „rank merging”, ale może uszeregować właściwą definicję funkcji niżej niż konceptualne omówienia
  • Zapytanie: PostToolUse → Zwraca notatki o „tool lifecycle hooks” i „post-execution handlers” zamiast konkretnej nazwy hooka

Wyszukiwanie wektorowe radzi sobie również słabo z danymi strukturalnymi. Pliki konfiguracyjne JSON, bloki YAML i fragmenty kodu generują embeddings, które odzwierciedlają wzorce strukturalne, a nie znaczenie semantyczne. Plik JSON z "review": true jest kodowany inaczej niż prozaiczne omówienie przeglądu kodu.

Łagodna degradacja

Jeśli sqlite-vec nie załaduje się (brak rozszerzenia, niekompatybilna platforma, uszkodzona biblioteka), system wyszukiwania przełącza się na tryb wyłącznie 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

System wyszukiwania sprawdza vec_available przed próbą wykonania zapytań wektorowych. Gdy jest wyłączony, wszystkie wyszukiwania korzystają wyłącznie z BM25, a krok fuzji RRF jest pomijany.


Reciprocal Rank Fusion (RRF)

RRF łączy dwie uporządkowane listy wyników bez konieczności kalibracji punktacji. Ta sekcja omawia algorytm, szczegółowy przebieg przykładowego zapytania, dostrajanie parametru k oraz powody wyboru RRF zamiast alternatywnych metod. Interaktywny kalkulator z edytowalnymi pozycjami, gotowymi scenariuszami i wizualnym eksploratorem architektury dostępny jest w pogłębionym omówieniu hybrydowego retrievera.

Algorytm

RRF przypisuje każdemu dokumentowi wynik oparty wyłącznie na jego pozycji w każdej z list:

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

Gdzie: - k to stała wygładzająca (60, zgodnie z Cormack i in.3) - rank_i to pozycja dokumentu (liczona od 1) na liście wyników i - weight_i to opcjonalny mnożnik dla danej listy (domyślnie 1.0)

Dokumenty, które zajmują wysokie pozycje na wielu listach, otrzymują wyższe wyniki po fuzji. Dokumenty obecne tylko na jednej liście otrzymują wynik wyłącznie z tego jednego źródła.

Dlaczego RRF zamiast alternatyw

Ważona kombinacja liniowa wymaga kalibracji wyników BM25 względem odległości cosinusowych. Wyniki BM25 są nieograniczone i skalują się z rozmiarem korpusu. Odległości cosinusowe mieszczą się w przedziale [0, 2]. Ich połączenie wymaga normalizacji, a parametry normalizacji zależą od zbioru danych. RRF wykorzystuje wyłącznie pozycje rankingowe — zawsze liczby całkowite zaczynające się od 1, niezależnie od metody punktacji.

Uczone modele fuzji wymagają oznaczonych danych treningowych — par zapytanie-dokument z oceną trafności. W przypadku osobistej bazy wiedzy takie dane treningowe nie istnieją. Ręczna ocena setek par zapytanie-dokument byłaby konieczna do wytrenowania użytecznego modelu. RRF działa bez jakichkolwiek danych treningowych.

Metody głosowania Condorceta (metoda Bordy, metoda Schulzego) są teoretycznie eleganckie, ale bardziej złożone w implementacji i dostrajaniu. Oryginalny artykuł o RRF wykazał, że RRF przewyższa metody Condorceta na danych ewaluacyjnych TREC.3

Fuzja w praktyce

Zapytanie: „how does the review aggregator handle disagreements”

BM25 umieszcza review-aggregator.py na pozycji 3 (dokładne dopasowania słów kluczowych: „review”, „aggregator”, „disagreements”), ale dwa pliki konfiguracyjne wyżej (zawierają słowo „review” bardziej prominentnie). Wyszukiwanie wektorowe umieszcza ten sam fragment na pozycji 1 (dopasowanie semantyczne dotyczące rozwiązywania konfliktów). Po fuzji RRF:

Fragment BM25 Vec Wynik fuzji
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

Fragmenty o wysokich pozycjach w obu listach trafiają na szczyt. Fragmenty obecne tylko na jednej liście otrzymują wynik z jednego źródła i spadają poniżej wyników podwójnie rankingowanych. Właściwa logika rozwiązywania sporów wygrywa, ponieważ obie metody ją odnalazły — BM25 poprzez słowa kluczowe, wyszukiwanie wektorowe poprzez semantykę.

Pełny przebieg krok po kroku z obliczeniami RRF dla każdej pozycji, wraz z możliwością testowania różnych wartości k, dostępny jest w interaktywnym kalkulatorze RRF.

Implementacja

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

Dostrajanie k

Stała k kontroluje, jak duży wpływ mają wyniki z czołowych pozycji w porównaniu z niższymi pozycjami:

  • Niskie k (np. 10): Dominują wyniki z czołowych pozycji. Pozycja 1 otrzymuje wynik 1/11 = 0,091, pozycja 10 otrzymuje 1/20 = 0,050 (różnica 1,8x). Sprawdza się, gdy poszczególne metody rankingowe trafnie identyfikują najlepszy wynik.
  • Domyślne k (60): Zbalansowane. Pozycja 1 otrzymuje wynik 1/61 = 0,0164, pozycja 10 otrzymuje 1/70 = 0,0143 (różnica 1,15x). Różnice między pozycjami są skompresowane, co zwiększa wagę obecności na wielu listach.
  • Wysokie k (np. 200): Obecność na obu listach ma znacznie większe znaczenie niż pozycja rankingowa. Pozycja 1 otrzymuje wynik 1/201, pozycja 10 — 1/210 — wartości niemal identyczne. Warto stosować, gdy poszczególne metody rankingowe generują zaszumione wyniki, ale zgodność między listami jest wiarygodna.

Należy zacząć od k=60. Oryginalny artykuł o RRF wykazał odporność tej wartości na różnorodnych zbiorach danych TREC. Dostrajanie ma sens dopiero po zmierzeniu przypadków niepowodzeń na własnym rozkładzie zapytań.

Rozstrzyganie remisów

Gdy dwa fragmenty mają identyczne wyniki RRF (rzadkie, ale możliwe przy tej samej pozycji na jednej liście i braku obecności na drugiej), remisy rozstrzygane są następująco:

  1. Preferowane są fragmenty obecne na obu listach względem fragmentów obecnych tylko na jednej
  2. Wśród fragmentów na obu listach preferowany jest ten o niższej łącznej pozycji
  3. Wśród fragmentów na jednej liście preferowany jest ten o niższej pozycji na danej liście

Kompletny potok wyszukiwania

Ta sekcja śledzi zapytanie od wejścia do wyjścia przez cały potok: wyszukiwanie BM25, wyszukiwanie wektorowe, fuzja RRF, obcinanie budżetu tokenów i składanie kontekstu.

Przepływ od początku do końca

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

Całkowite opóźnienie: ~23 ms dla bazy danych zawierającej 49 746 fragmentów na sprzęcie Apple M3 Pro.

API wyszukiwania

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

Obcinanie budżetu tokenów

Parametr max_tokens zapobiega zwracaniu przez retriever większej ilości kontekstu, niż narzędzie AI jest w stanie przetworzyć. Szacowanie wykorzystuje 4 znaki na token (rozsądne przybliżenie dla prozy angielskiej). Wyniki są obcinane zachłannie: dodawane w kolejności rankingowej aż do wyczerpania budżetu.

To konserwatywna strategia. Bardziej wyrafinowane podejście uwzględniałoby oceny jakości poszczególnych wyników i preferowało krótsze, wyższej jakości rezultaty nad dłuższymi o niższej jakości. Zachłanne podejście jest prostsze i sprawdza się w praktyce, ponieważ ranking RRF już porządkuje wyniki według trafności.

Schemat bazy danych (kompletny)

-- 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
);

Ścieżka łagodnej degradacji

Full pipeline:     BM25 + Vector + RRF    Best results
No sqlite-vec:     BM25 only              Good results (no semantic)
No model download:  BM25 only              Good results (no semantic)
No FTS5:           Vector only             Decent results (no keyword)
No database:       Error                   Prompt user to run indexer

Retriever sprawdza dostępne możliwości podczas inicjalizacji i dostosowuje strategię zapytań. Brakujący komponent obniża jakość, ale nie powoduje błędów. Jedynym krytycznym przypadkiem jest brak pliku bazy danych.

Statystyki produkcyjne

Pomiary na sejfie (vault) zawierającym 16 894 pliki, 49 746 fragmentów, bazę danych SQLite o rozmiarze 83 MB, Apple M3 Pro:

Metryka Wartość
Łączna liczba plików 16 894
Łączna liczba fragmentów 49 746
Rozmiar bazy danych 83 MB
Opóźnienie zapytania BM25 (p50) 12 ms
Opóźnienie zapytania wektorowego (p50) 8 ms
Opóźnienie fuzji RRF 3 ms
Opóźnienie wyszukiwania od początku do końca (p50) 23 ms
Czas pełnej reindeksacji ~4 minuty
Czas przyrostowej reindeksacji <10 sekund
Model embeddingów potion-base-8M (256-dim)
Pula kandydatów BM25 30
Pula kandydatów wektorowych 30
Domyślny limit wyników 10
Domyślny budżet tokenów 4 000 tokenów

Hashowanie treści i wykrywanie zmian

Indekser musi wiedzieć, które pliki zmieniły się od ostatniego uruchomienia indeksacji. Ta sekcja omawia mechanizm wykrywania zmian oraz strategię hashowania.

Porównanie czasu modyfikacji pliku

Indekser przechowuje mtime_ns (czas modyfikacji pliku w nanosekundach) dla każdego fragmentu w tabeli chunks. Podczas przyrostowego uruchomienia indekser:

  1. Skanuje sejf (vault) w poszukiwaniu wszystkich plików .md w dozwolonych folderach
  2. Odczytuje mtime_ns każdego pliku z systemu plików
  3. Porównuje z zapisanym mtime_ns w bazie danych
  4. Identyfikuje trzy kategorie:
  5. Nowe pliki: ścieżka istnieje w systemie plików, ale nie w bazie danych
  6. Zmienione pliki: ścieżka istnieje w obu, ale mtime_ns się różni
  7. Usunięte pliki: ścieżka istnieje w bazie danych, ale nie w systemie plików
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)

Dlaczego mtime, a nie hash treści

Hashowanie treści (SHA-256 zawartości pliku) byłoby bardziej niezawodne niż porównanie mtime — pozwoliłoby wykryć przypadki, gdy plik został „dotknięty” bez faktycznej zmiany (np. git checkout przywracający oryginalny mtime). Hashowanie wymaga jednak odczytu każdego pliku przy każdym przyrostowym uruchomieniu. Dla 16 894 plików odczyt zawartości zajmuje 2–3 sekundy. Odczyt mtime z systemu plików zajmuje <100 ms.

Kompromis: porównanie mtime czasami wywołuje niepotrzebną reindeksację niezmienionych plików (fałszywe pozytywy), ale nigdy nie pomija rzeczywistych zmian. Fałszywe pozytywy kosztują kilka dodatkowych wywołań embeddingów na uruchomienie. Różnica w szybkości (100 ms vs 3 sekundy) sprawia, że mtime jest pragmatycznym wyborem dla systemu uruchamianego przy każdej interakcji z AI.

Obsługa usunięć

Gdy plik zostaje usunięty z sejfu (vault), indekser usuwa wszystkie jego fragmenty z bazy danych:

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

Instrukcja DELETE FROM chunk_vecs działa natywnie od wersji sqlite-vec v0.1.7.26 Wcześniejsze wersje wymagały obejść (usunięcie i ponowne utworzenie tabeli wirtualnej lub utrzymywanie zewnętrznego zestawu „aktywnych identyfikatorów”). W przypadku wersji starszej niż 0.1.7 warto zaktualizować przed poleganiem na bezpośrednich usunięciach.

Tabele FTS5 z synchronizacją treści wymagają jawnego usunięcia za pomocą INSERT INTO chunks_fts(chunks_fts, rowid, ...) VALUES('delete', ?, ...) dla każdego usuniętego wiersza. Indekser obsługuje to w ramach procesu usuwania pliku.


Indeksowanie przyrostowe a pełne

Indekser obsługuje dwa tryby: przyrostowy (szybki, do codziennego użytku) oraz pełny (wolniejszy, stosowany okazjonalnie). W tej sekcji omówiono, kiedy stosować każdy z nich, gwarancje idempotentności oraz odzyskiwanie po uszkodzeniach.

Indeksowanie przyrostowe

Kiedy stosować: Codzienne indeksowanie po edycji notatek. Tryb domyślny.

Jak działa: 1. Skanowanie sejfu w poszukiwaniu zmian w plikach (porównanie mtime) 2. Usuwanie fragmentów dla skasowanych plików 3. Ponowne dzielenie na fragmenty i generowanie embeddingów dla zmienionych plików 4. Wstawianie nowych fragmentów dla nowych plików 5. Synchronizacja indeksu FTS5

Typowy czas trwania: <10 sekund dla dziennych edycji w sejfie liczącym 16 000 plików.

python index_vault.py --incremental

Pełne indeksowanie

Kiedy stosować: - Po zmianie modelu embeddingów (wykryto niezgodność skrótu modelu) - Po migracji schematu (nowe kolumny, zmienione indeksy) - Po uszkodzeniu bazy danych (test integralności kończy się niepowodzeniem) - Gdy indeksowanie przyrostowe daje nieoczekiwane wyniki

Jak działa: 1. Usunięcie wszystkich istniejących danych (fragmentów, wektorów, wpisów FTS5) 2. Skanowanie całego sejfu 3. Podział wszystkich plików na fragmenty 4. Generowanie embeddingów dla wszystkich fragmentów 5. Budowanie indeksu FTS5 od podstaw

Typowy czas trwania: ~4 minuty dla 16 894 plików na Apple M3 Pro.

python index_vault.py --full

Idempotentność

Oba tryby są idempotentne — dwukrotne uruchomienie tego samego polecenia daje identyczny wynik. Indekser usuwa istniejące fragmenty danego pliku przed wstawieniem nowych, więc ponowne uruchomienie indeksowania przyrostowego na aktualnej bazie danych nie wprowadza żadnych zmian. Ponowne uruchomienie pełnego indeksowania tworzy identyczną bazę danych.

Odzyskiwanie po uszkodzeniach

Jeśli baza SQLite ulegnie uszkodzeniu (utrata zasilania podczas zapisu, błąd dysku, przerwanie procesu w trakcie transakcji):

# Check integrity
sqlite3 vectors.db "PRAGMA integrity_check;"

# If corruption detected, full reindex rebuilds from source files
python index_vault.py --full

Źródłem prawdy są zawsze pliki sejfu, nie baza danych. Baza danych to artefakt pochodny, który można odbudować w dowolnym momencie. To kluczowa właściwość projektowa — nigdy nie trzeba tworzyć kopii zapasowej bazy danych.

Flaga --incremental

Gdy indekser uruchamia się z flagą --incremental:

  1. Sprawdzenie skrótu modelu. Porównanie zapisanego skrótu modelu z aktualnym. W przypadku różnicy następuje automatyczne przełączenie na tryb pełnego indeksowania z ostrzeżeniem dla użytkownika.
  2. Skanowanie plików. Przejście dozwolonych folderów, zebranie ścieżek plików i znaczników czasu mtime.
  3. Wykrywanie zmian. Porównanie z zapisanymi danymi.
  4. Przetwarzanie wsadowe. Ponowne dzielenie na fragmenty i generowanie embeddingów dla zmienionych plików w partiach po 64.
  5. Raportowanie postępu. Wyświetlenie liczby przetworzonych plików i czasu trwania operacji.
  6. Łagodne zamykanie. Obsługa sygnału SIGINT poprzez dokończenie przetwarzania bieżącego pliku przed zatrzymaniem.

Filtrowanie poświadczeń i granice danych

Osobiste notatki zawierają sekrety: klucze API, tokeny bearer, ciągi połączeń do baz danych, klucze prywatne wklejone podczas sesji debugowania. Filtr poświadczeń zapobiega ich wprowadzeniu do indeksu wyszukiwania.

Problem

Notatka o debugowaniu integracji z OAuth może zawierać:

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

Bez filtrowania zarówno token JWT, jak i klucz API zostałyby podzielone na fragmenty, przetworzone na embeddingi i zapisane w bazie danych. Wyszukiwanie „authentication” zwróciłoby fragment zawierający prawdziwe sekrety. Co gorsza, jeśli moduł wyszukiwania przekazuje wyniki do narzędzia AI przez MCP, sekrety pojawiają się w oknie kontekstowym AI i potencjalnie w logach narzędzia.

Filtrowanie oparte na wzorcach

Filtr poświadczeń działa na każdym fragmencie przed zapisem, dopasowując 25 wzorców specyficznych dla dostawców oraz wzorce ogólne:

Wzorce specyficzne dla dostawców:

Wzorzec Przykład Regex
Klucz API OpenAI sk-... sk-[a-zA-Z0-9_-]{20,}
Klucz API Anthropic sk-ant-api03-... sk-ant-api\d{2}-[a-zA-Z0-9_-]{20,}
PAT GitHub ghp_... gh[ps]_[a-zA-Z0-9]{36,}
Klucz dostępu AWS AKIA... AKIA[0-9A-Z]{16}
Klucz Stripe sk_live_... [sr]k_(live\|test)_[a-zA-Z0-9]{24,}
Token Cloudflare ... Różne wzorce

Wzorce ogólne:

Wzorzec Wykrywanie
Tokeny JWT eyJ[a-zA-Z0-9_-]+\.eyJ[a-zA-Z0-9_-]+
Tokeny Bearer Bearer\s+[a-zA-Z0-9_\-\.]+
Klucze prywatne -----BEGIN (RSA\|EC\|OPENSSH) PRIVATE KEY-----
Ciągi base64 o wysokiej entropii Ciągi z entropią >4,5 bita/znak, powyżej 40 znaków
Przypisania haseł password\s*[:=]\s*["'][^"']+["']

Implementacja filtra

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

Kluczowe decyzje projektowe:

  1. Filtrowanie przed generowaniem embeddingów. To oczyszczony tekst jest przekształcany w embedding. Reprezentacja wektorowa nigdy nie koduje wzorców poświadczeń. Zapytanie o „klucz API” zwraca notatki omawiające zarządzanie kluczami API, a nie notatki zawierające rzeczywiste klucze.

  2. Zamiana zamiast usuwania. Token [REDACTED:pattern-name] zachowuje kontekst semantyczny otaczającego tekstu. Embedding oddaje informację, że „w tym miejscu znajdowało się coś przypominającego poświadczenie”, nie kodując samego poświadczenia.

  3. Logowanie wzorców, nie wartości. Filtr rejestruje, które wzorce zostały dopasowane (np. „Scrubbed 2 credential(s) from oauth-debug.md [jwt, bearer-token]”), ale nigdy nie loguje wartości poświadczenia.

Wykluczanie oparte na ścieżkach

Plik .indexignore zapewnia wykluczanie na poziomie ścieżek (gruboziarniste). Filtr poświadczeń zapewnia szczegółowe oczyszczanie wewnątrz indeksowanych plików. Oba mechanizmy są niezbędne:

  • .indexignore — dla całych folderów, o których wiadomo, że zawierają wrażliwe treści (notatki zdrowotne, dokumenty finansowe, materiały kariery zawodowej)
  • Filtr poświadczeń — dla sekretów przypadkowo osadzonych w treściach, które skądinąd powinny być indeksowane

Klasyfikacja danych

W przypadku sejfów zawierających zróżnicowane treści warto rozważyć klasyfikację notatek według poziomu wrażliwości:

Poziom Przykłady Indeksować? Filtrować?
Publiczny Szkice wpisów blogowych, notatki techniczne Tak Tak
Wewnętrzny Plany projektowe, decyzje architektoniczne Tak Tak
Wrażliwy Dane płacowe, dokumentacja medyczna Nie (.indexignore) Nie dotyczy
Zastrzeżony Poświadczenia, klucze prywatne Nie (.indexignore) Nie dotyczy

Architektura serwera MCP

Model Context Protocol (MCP) umożliwia udostępnianie retrievera jako narzędzia wywoływanego przez agentów AI. Ta sekcja obejmuje projekt serwera, zakres możliwości oraz granice uprawnień.

Wybór protokołu: STDIO vs HTTP

MCP obsługuje dwa tryby transportu:

STDIO — Narzędzie AI uruchamia serwer MCP jako proces potomny i komunikuje się przez stdin/stdout. Jest to standardowy tryb dla narzędzi lokalnych. Claude Code, Codex CLI oraz Cursor obsługują serwery MCP w trybie STDIO.

{
  "mcpServers": {
    "obsidian": {
      "command": "python",
      "args": ["/path/to/obsidian_mcp.py"],
      "env": {
        "VAULT_PATH": "/path/to/vault",
        "DB_PATH": "/path/to/vectors.db"
      }
    }
  }
}

HTTP — Serwer MCP działa jako samodzielna usługa HTTP. Przydatny w przypadku zdalnego dostępu, konfiguracji z wieloma klientami lub środowisk zespołowych, w których skarbiec znajduje się na współdzielonym serwerze.

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

Zalecenie: W przypadku osobistych skarbców warto korzystać z trybu STDIO. Jest prostszy, bezpieczniejszy (brak ekspozycji sieciowej), a cykl życia serwera zarządzany jest przez narzędzie AI. Tryb HTTP należy stosować wyłącznie wtedy, gdy wiele narzędzi lub wiele maszyn wymaga równoczesnego dostępu do tego samego skarbca.

Ewolucja specyfikacji MCP. Specyfikacja MCP z czerwca 2025 roku wprowadziła autoryzację OAuth 2.1, strukturalne wyjścia narzędzi (typowane schematy zwracanych wartości) oraz elicytację (inicjowane przez serwer zapytania do użytkownika). Wydanie z listopada 2025 dostarczyło Streamable HTTP jako pełnoprawny tryb transportu, odkrywanie .well-known URL umożliwiające automatyczne przeglądanie możliwości serwera, strukturalne adnotacje narzędzi deklarujące, czy narzędzie jest tylko do odczytu, czy modyfikujące, a także system standaryzacji warstw SDK.1821 Kolejne wydanie specyfikacji (wstępnie planowane na połowę 2026 roku) proponuje operacje asynchroniczne dla długotrwałych zadań, rozszerzenia protokołu specyficzne dla branż takich jak opieka zdrowotna i finanse oraz standardy komunikacji agent-agent dla przepływów wieloagentowych.21 W przypadku serwerów osobistych skarbców STDIO pozostaje najprostszą ścieżką. Transport Streamable HTTP oraz odkrywanie .well-known przynoszą korzyści przede wszystkim wdrożeniom enterprise HTTP z routingiem wielodostępnym i równoważeniem obciążenia. Warto śledzić plan rozwoju MCP pod kątem aktualizacji wpływających na wybór transportu.

Projektowanie możliwości

Serwer MCP powinien udostępniać minimalny zestaw narzędzi:

search — Podstawowe narzędzie. Uruchamia wyszukiwanie hybrydowe i zwraca uszeregowane wyniki.

{
  "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 — Odczytuje pełną zawartość konkretnej notatki na podstawie ścieżki. Przydatne, gdy agent chce zobaczyć pełny kontekst wyniku wyszukiwania.

{
  "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 — Wyświetla listę notatek pasujących do filtra (według folderu, tagu, typu lub zakresu dat). Przydatne do eksploracji, gdy agent nie ma konkretnego zapytania.

{
  "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 — Narzędzie pomocnicze, które uruchamia wyszukiwanie i formatuje wyniki jako blok kontekstu nadający się do wstrzyknięcia do konwersacji.

{
  "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 }
  }
}

Granice uprawnień

Serwer MCP powinien egzekwować ścisłe granice:

  1. Tylko odczyt. Serwer odczytuje skarbiec i bazę danych indeksu. Nie tworzy, nie modyfikuje ani nie usuwa notatek. Operacje zapisu (przechwytywanie nowych notatek) obsługiwane są przez osobne hooki lub skille, a nie przez serwer MCP.

  2. Ograniczenie do skarbca. Serwer odczytuje wyłącznie pliki w obrębie skonfigurowanej ścieżki skarbca. Próby przejścia ścieżki (../../etc/passwd) muszą być odrzucane.

  3. Filtrowanie poufnych danych na wyjściu. Nawet jeśli baza danych zawiera wstępnie przefiltrowaną zawartość, należy stosować filtrowanie poufnych danych na wyjściu jako dodatkową warstwę ochrony (defense-in-depth).

  4. Ograniczenie tokenów w odpowiedziach. Należy wymuszać max_tokens we wszystkich odpowiedziach narzędzi, aby zapobiec otrzymywaniu przez narzędzie AI nadmiernie dużych bloków kontekstu.

Obsługa błędów

Narzędzia MCP powinny zwracać strukturalne komunikaty o błędach, które pomagają narzędziu AI w odzyskaniu sprawności:

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

Integracja z Claude Code

Claude Code jest głównym konsumentem systemu wyszukiwania Obsidian. Ta sekcja obejmuje konfigurację MCP, integrację hooków oraz wzorzec obsidian_bridge.py.

Konfiguracja MCP

Należy dodać serwer MCP Obsidian do ~/.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"
      }
    }
  }
}

Po dodaniu konfiguracji należy ponownie uruchomić Claude Code. Serwer MCP wystartuje jako proces potomny. Aby zweryfikować jego działanie:

> What tools do you have from the obsidian MCP server?

Claude Code powinien wyświetlić listę dostępnych narzędzi (obsidian_search, obsidian_read_note itd.).

Integracja hooków

Hooki rozszerzają zachowanie Claude Code w określonych punktach cyklu życia. Dwa hooki są istotne dla integracji z Obsidian:

Hook PreToolUse — odpytuje skarbiec przed przetworzeniem wywołania narzędzia przez agenta. Automatycznie wstrzykuje odpowiedni kontekst.

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

Hook PostToolUse — przechwytuje istotne wyniki narzędzi z powrotem do skarbca w celu późniejszego wyszukiwania.

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

Wzorzec obsidian_bridge.py

Moduł pomostowy udostępnia Python API, które hooki i skille mogą wywoływać:

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

Skill /capture

Skill Claude Code do przechwytywania spostrzeżeń z powrotem do skarbca:

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

Skill tworzy nową notatkę w 00-inbox/ z odpowiednim frontmatter i uruchamia przyrostowe ponowne indeksowanie, dzięki czemu nowa notatka jest natychmiast wyszukiwalna.

Wzorce poleceń niestandardowych

Skille Claude Code mogą opakowywać operacje na skarbcu w nazwane polecenia. Praktycy zbudowali biblioteki poleceń specyficznych dla Obsidian, które traktują skarbiec zarówno jako źródło odczytu, jak i cel zapisu.

Skanowanie sygnałów. Polecenie /scan-intel odpytuje zewnętrzne źródła, ocenia wyniki pod kątem osobistych zainteresowań badawczych i zapisuje kwalifikujące się sygnały jako notatki w skarbcu z frontmatter:

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

Polecenie pobiera dane ze skonfigurowanych źródeł (arXiv, HN, RSS), stosuje model oceny (trafność, wykonalność, głębokość, autorytet) i zapisuje zakwalifikowane sygnały do folderów tematycznych w skarbcu. Skarbiec staje się odbiorcą końcowym zautomatyzowanego potoku wywiadowczego.

Dziennik kapitański. Polecenie /captains-log agreguje dzienną aktywność git ze wszystkich repozytoriów, zapisuje ustrukturyzowany wpis dziennikowy do skarbca i uwzględnia podjęte decyzje, spostrzeżenia oraz otwarte wątki:

/captains-log

Polecenie pobiera historię commitów z GitHub, grupuje według repozytorium i formatuje jako narracyjny wpis dziennikowy. Z biegiem czasu codzienne wpisy tworzą przeszukiwalny zapis tego, co zostało wdrożone i dlaczego.

Przechwytywanie do Obsidian. Polecenie /obsidian-capture pobiera spostrzeżenie z bieżącej sesji Claude Code i zapisuje je bezpośrednio do skarbca z odpowiednimi metadanymi:

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

Wzorzec rozciąga się na dowolną operację w skarbcu: tworzenie MOC, aktualizowanie notatek o statusie projektu, łączenie powiązanych sygnałów czy generowanie cotygodniowych podsumowań z zebranych dziennych wpisów.

Przykłady społeczności. Praktycy publikują swoje biblioteki poleceń. Jeden deweloper udostępnił 22 niestandardowe polecenia Obsidian + Claude Code obejmujące codzienne przeglądy, planowanie projektów, przechwytywanie badań i procesy tworzenia treści.1 Inny zbudował skill „Visual Explainer”, który generuje notatki z diagramami w skarbcu na podstawie analizy kodu.2 Polecenia różnią się między sobą, ale architektura jest spójna: skille Claude Code jako interfejs, notatki w skarbcu jako warstwa przechowywania oraz infrastruktura wyszukiwania jako silnik zapytań.

Zarządzanie oknem kontekstowym

Integracja powinna uwzględniać okno kontekstowe Claude Code:

  • Wstrzykiwany kontekst należy ograniczyć do 1500–2000 tokenów na zapytanie. Więcej konkuruje z pamięcią roboczą agenta.
  • Należy dołączać atrybucję źródła. Zawsze trzeba uwzględnić ścieżkę pliku i nagłówek sekcji, aby agent mógł odwołać się do źródła.
  • Tekst fragmentów należy przycinać. Długie fragmenty powinny być przycinane z wielokropkiem ... zamiast pomijane całkowicie. Pierwsze 300–500 znaków zwykle zawiera kluczowe informacje.
  • Nie należy wstrzykiwać kontekstu przy każdym wywołaniu narzędzia. Hook PreToolUse powinien selektywnie wstrzykiwać kontekst w zależności od wywoływanego narzędzia. Operacje odczytu nie potrzebują kontekstu ze skarbca. Operacje zapisu i edycji na nim korzystają.

Integracja Codex CLI

Codex CLI łączy się z serwerami MCP poprzez config.toml. Wzorzec integracji różni się od Claude Code składnią konfiguracji i sposobem dostarczania instrukcji.

Konfiguracja MCP

Należy dodać do .codex/config.toml lub ~/.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"

Wzorce AGENTS.md

Codex CLI odczytuje AGENTS.md w celu pobrania instrukcji na poziomie projektu. Należy uwzględnić wskazówki dotyczące wyszukiwania w skarbcu:

## 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"

Różnice względem Claude Code

Funkcja Claude Code Codex CLI
Konfiguracja MCP settings.json config.toml
Hooki ~/.claude/hooks/ Nieobsługiwane
Skille ~/.claude/skills/ Nieobsługiwane
Plik instrukcji CLAUDE.md AGENTS.md
Tryby zatwierdzania --dangerously-skip-permissions suggest / auto-edit / full-auto

Kluczowa różnica: Codex CLI nie obsługuje hooków. Wzorzec automatycznego wstrzykiwania kontekstu (hook PreToolUse) nie jest dostępny. Zamiast tego należy umieścić jawne instrukcje w AGENTS.md, nakazujące agentowi przeszukanie skarbca przed rozpoczęciem pracy.


Cursor i inne narzędzia

Cursor oraz inne narzędzia AI obsługujące MCP mogą łączyć się z tym samym serwerem MCP dla Obsidian. Ta sekcja obejmuje konfigurację dla popularnych narzędzi.

Cursor

Należy dodać następującą konfigurację do pliku .cursor/mcp.json w katalogu głównym projektu:

{
  "mcpServers": {
    "obsidian": {
      "command": "python",
      "args": ["/path/to/obsidian_mcp.py"],
      "env": {
        "VAULT_PATH": "/absolute/path/to/vault",
        "DB_PATH": "/absolute/path/to/vectors.db"
      }
    }
  }
}

Plik .cursorrules w Cursor może zawierać instrukcje dotyczące korzystania ze skarbca:

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.

Macierz kompatybilności

Narzędzie Obsługa MCP Transport Lokalizacja konfiguracji
Claude Code Pełna STDIO ~/.claude/settings.json
Codex CLI Pełna STDIO .codex/config.toml
Cursor Pełna STDIO .cursor/mcp.json
Windsurf Pełna STDIO .windsurf/mcp.json
Continue.dev Częściowa HTTP ~/.continue/config.json
Zed W trakcie STDIO Interfejs ustawień
Claudian (wtyczka Obsidian) N/D (wbudowane) Claude Code CLI Ustawienia wtyczki Obsidian
Agent Client (wtyczka Obsidian) N/D (wbudowane) ACP Ustawienia wtyczki Obsidian

Rozwiązanie zastępcze dla narzędzi bez obsługi MCP

W przypadku narzędzi nieobsługujących MCP moduł wyszukiwania można opakować jako 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 generuje ustrukturyzowany tekst, który można ręcznie wkleić do dowolnego narzędzia AI. To mniej eleganckie rozwiązanie niż integracja MCP, ale działa uniwersalnie.


Buforowanie promptów z notatek strukturalnych

Notatki strukturalne w skarbcu mogą służyć jako wielokrotnie używane bloki kontekstu, które zmniejszają zużycie tokenów w interakcjach z AI. Ta sekcja obejmuje projektowanie kluczy buforowania i zarządzanie budżetem tokenów.

Wzorzec

Zamiast wyszukiwać kontekst przy każdej interakcji, warto wcześniej zbudować bloki kontekstu z dobrze ustrukturyzowanych notatek i je buforować:

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

Unieważnianie bufora

Unieważnianie bufora opiera się na dwóch sygnałach:

  1. Wygaśnięcie TTL. Każdy blok kontekstu ma czas życia (time-to-live). Po wygaśnięciu TTL blok jest odbudowywany poprzez ponowne zapytanie do skarbca.
  2. Wykrywanie zmian w skarbcu. Gdy indekser wykryje zmiany w plikach, które przyczyniły się do buforowanego bloku kontekstu, blok jest natychmiast unieważniany.

Zarządzanie budżetem tokenów

Sesja rozpoczyna się z całkowitym budżetem kontekstu. Buforowane bloki zużywają część tego budżetu:

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)

Buforowane bloki ładują się na początku sesji. Dynamiczne wyniki wyszukiwania wypełniają pozostały budżet w zależności od zapytania. To podejście hybrydowe zapewnia agentowi bazowy zestaw często potrzebnego kontekstu, jednocześnie zachowując budżet na konkretne zapytania.

Porównanie zużycia tokenów

Bez buforowania: Każde istotne zapytanie uruchamia wyszukiwanie w skarbcu, zwracając 1500–2000 tokenów kontekstu. Przy 10 zapytaniach w sesji agent zużywa 15 000–20 000 tokenów kontekstu ze skarbca.

Z buforowaniem: Trzy wstępnie zbudowane bloki kontekstu zużywają łącznie 4500 tokenów. Dodatkowe wyszukiwania dodają 1500–2000 tokenów na unikalne zapytanie. Przy 10 zapytaniach, z których 6 jest pokrytych przez buforowane bloki, agent zużywa 4500 + (4 × 1500) = 10 500 tokenów — mniej więcej połowę zużycia bez buforowania.


Hooki PostToolUse do kompresji kontekstu

Dane wyjściowe narzędzi bywają rozwlekłe: ślady stosu, listy plików, wyniki testów. Hook PostToolUse może kompresować te dane przed zajęciem miejsca w oknie kontekstu.

Problem

Wywołanie narzędzia Bash uruchamiające testy może zwrócić:

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

Pełne dane wyjściowe to 5000 tokenów, ale istotna informacja mieści się w 2 liniach: 200 zaliczonych, 1 niezaliczony.

Implementacja hooka

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

Zapobieganie rekurencyjnemu uruchamianiu

Hook kompresji emitujący dane wyjściowe może uruchomić sam siebie, jeśli nie zostanie odpowiednio zabezpieczony:

# Guard against recursive invocation
if [ -n "$COMPRESS_HOOK_ACTIVE" ]; then
    exit 0
fi
export COMPRESS_HOOK_ACTIVE=1

Heurystyki kompresji

Typ danych wyjściowych Wykrywanie Strategia kompresji
Wyniki testów Słowa kluczowe PASSED / FAILED Zliczenie zaliczonych/niezaliczonych, wyświetlenie tylko błędów
Listy plików ls lub find w poleceniu Obcięcie do pierwszych 20 pozycji + liczba
Ślady stosu Słowo kluczowe Traceback Zachowanie pierwszej i ostatniej ramki + komunikat błędu
Status Git modified: / new file: Podsumowanie liczby według statusu
Dane wyjściowe kompilacji warning: / error: Usunięcie linii informacyjnych, zachowanie ostrzeżeń/błędów

Pipeline przyjmowania i segregacji sygnałów

Warstwa przyjmowania określa, co trafia do skarbca. Bez selekcji skarbiec gromadzi szum. Ta sekcja obejmuje pipeline oceniania, który kieruje sygnały do folderów domenowych.

Źródła

Sygnały pochodzą z wielu kanałów:

  • Kanały RSS: Blogi techniczne, poradniki bezpieczeństwa, informacje o wydaniach
  • Zakładki: Zakładki przeglądarki zapisywane za pomocą Obsidian Web Clipper lub bookmarkletu
  • Newslettery: Kluczowe fragmenty z newsletterów e-mailowych
  • Ręczne przechwytywanie: Notatki sporządzane podczas czytania, rozmów lub badań
  • Dane wyjściowe narzędzi: Istotne wyniki narzędzi AI przechwycone za pomocą hooków
  • Rozszerzenie udostępniania iOS: Aplikacja Obsidian na iOS (zaktualizowana na początku 2026 roku) zawiera rozszerzenie udostępniania, które zapisuje treści z Safari, sieci społecznościowych i innych aplikacji bezpośrednio do skarbca bez otwierania Obsidian.31 To tworzy ścieżkę przyjmowania na urządzeniach mobilnych z minimalnym oporem — wystarczy udostępnić artykuł z Safari, a pojawi się jako notatka w skarbcu gotowa do oceny.
  • Obsidian CLI: Skrypty powłoki i hooki mogą tworzyć notatki za pomocą obsidian file create lub dopisywać do istniejących notatek za pomocą obsidian file append, umożliwiając zautomatyzowane pipeline’y przyjmowania na komputerze.

Wymiary oceny

Każdy sygnał jest oceniany w czterech wymiarach (od 0,0 do 1,0 w każdym):

Wymiar Pytanie Niska ocena (0,0–0,3) Wysoka ocena (0,7–1,0)
Trafność Czy dotyczy to moich aktywnych dziedzin? Poboczne, poza zakresem Bezpośrednio związane z bieżącą pracą
Przydatność praktyczna Czy mogę wykorzystać tę informację? Czysta teoria, bez zastosowania Konkretna technika lub wzorzec do zastosowania
Głębokość Jak merytoryczna jest treść? Nagłówki, pobieżne streszczenie Szczegółowa analiza z przykładami
Wiarygodność Jak wiarygodne jest źródło? Anonimowy blog, niezweryfikowane Źródło pierwotne, recenzowane, uznany ekspert

Ocena złożona i kierowanie

composite = (relevance * 0.35) + (actionability * 0.25) +
            (depth * 0.25) + (authority * 0.15)
Zakres oceny Działanie
0,55+ Automatyczne przekierowanie do folderu domenowego
0,40–0,55 Kolejkowanie do ręcznego przeglądu
< 0,40 Odrzucenie (bez zapisu)

Kierowanie domenowe

Sygnały z oceną powyżej 0,55 trafiają do jednego z 12 folderów domenowych na podstawie dopasowania słów kluczowych i klasyfikacji tematycznej:

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

Statystyki produkcyjne

Na przestrzeni 14 miesięcy działania:

Metryka Wartość
Łączna liczba przetworzonych sygnałów 7 771
Automatycznie przekierowane (>0,55) 4 832 (62%)
W kolejce do przeglądu (0,40–0,55) 1 543 (20%)
Odrzucone (<0,40) 1 396 (18%)
Aktywne foldery domenowe 12
Średnia liczba sygnałów dziennie ~18

Wzorce grafu wiedzy

Graf wiki-linków w Obsidian koduje relacje między notatkami. Ta sekcja obejmuje semantykę linków, przechodzenie grafu w celu rozszerzania kontekstu oraz antywzorce obniżające jakość grafu.

Semantyka backlinków

Każdy wiki-link tworzy skierowaną krawędź w grafie. Obsidian śledzi zarówno linki wychodzące, jak i backlinki:

  • Link wychodzący: Notatka A zawiera [[Notatka B]] → A linkuje do B
  • Backlink: Notatka B pokazuje, że Notatka A się do niej odwołuje

Graf koduje różne typy relacji w zależności od kontekstu:

Wzorzec linku Semantyka Przykład
Link w tekście „Jest powiązany z” „Zobacz [[OAuth Token Rotation]], aby poznać szczegóły”
Link w nagłówku „Ma podtemat” „## Powiązane\n- [[Token Rotation]]\n- [[Session Management]]”
Link typu tag „Jest skategoryzowany jako” „[[type/reference]]”
Link MOC „Jest częścią” Notatka Maps of Content zawierająca listę powiązanych notatek

Maps of Content (MOC)

MOC to notatki indeksowe organizujące powiązane notatki w nawigowalne struktury:

---
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 wspierają wyszukiwanie na dwa sposoby:

  1. Bezpośrednie dopasowanie. Wyszukiwanie „authentication overview” trafia w sam MOC, dostarczając agentowi wyselekcjonowaną listę powiązanych notatek.
  2. Rozszerzanie kontekstu. Po znalezieniu konkretnej notatki retriever może sprawdzić, czy notatka pojawia się w którymś z MOC, i dołączyć strukturę MOC do wyników, dając agentowi mapę szerszego tematu.

Przechodzenie grafu w celu rozszerzania kontekstu

Przyszłe rozszerzenie retrievera: po znalezieniu najlepszych wyników rozszerzenie kontekstu poprzez podążanie za linkami:

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)

Nie jest to zaimplementowane w obecnym retrieverze, ale stanowi naturalne rozszerzenie struktury grafu.

Antywzorce

Osierocone klastry. Grupy notatek linkujących do siebie nawzajem, ale nieposiadających połączeń z resztą skarbca. Panel grafu w Obsidian uwidacznia je jako odłączone wyspy. Osierocone klastry wskazują na brakujące MOC lub brakujące linki międzydomenowe.

Rozrost tagów. Niespójne stosowanie tagów lub tworzenie zbyt wielu szczegółowych tagów. Skarbiec z 500 unikalnymi tagami na 5000 notatek daje średnio 1 notatkę na 10 tagów — tagi nie są przydatne do filtrowania. Warto skonsolidować je do 20–50 tagów wysokiego poziomu odpowiadających folderom domenowym.

Notatki bogate w linki, ubogie w treść. Notatki składające się wyłącznie z wiki-linków bez prozy. Takie notatki indeksują się źle, ponieważ chunker nie ma tekstu do wygenerowania embeddingów. Należy dodać przynajmniej akapit kontekstu wyjaśniający, dlaczego powiązane notatki są ze sobą związane.

Dwukierunkowe linki do wszystkiego. Nie każde odniesienie wymaga wiki-linka. Wzmianka o „OAuth” mimochodem nie wymaga [[OAuth 2.0 Overview]]. Wiki-linki warto rezerwować dla celowych, nawigowalnych relacji, w których kliknięcie linku dostarczyłoby użyteczny kontekst.


Przepisy na przepływy pracy programisty

Praktyczne przepływy pracy łączące wyszukiwanie w skarbcu z codziennymi zadaniami programistycznymi.

Poranny ładunek kontekstu

Rozpoczęcie dnia od załadowania odpowiedniego kontekstu:

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

Retriever zwraca ostatnie notatki dotyczące aktywnego projektu, zapewniając szybkie przypomnienie, na czym skończono poprzedniego dnia. Skuteczniejsze niż ponowne czytanie wczorajszych komunikatów commitów.

Przechwytywanie wiedzy podczas kodowania

Podczas implementacji funkcji można utrwalać spostrzeżenia bez opuszczania edytora:

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

Utrwalone spostrzeżenie jest natychmiast indeksowane i dostępne do przyszłego wyszukiwania. Z biegiem miesięcy te mikro-przechwycenia budują korpus wiedzy specyficznej dla implementacji.

Rozpoczęcie projektu

Przy rozpoczynaniu nowego projektu lub funkcji:

  1. Wyszukiwanie w skarbcu: „Co wiem o [technologii/wzorcu]?”
  2. Przegląd 5 najlepszych wyników pod kątem wcześniejszych decyzji i pułapek
  3. Sprawdzenie, czy istnieje MOC dla danej domeny; jeśli nie — utworzenie go
  4. Wyszukiwanie trybów awarii: „problemy z [technologią]”

Debugowanie z wyszukiwaniem w skarbcu

W przypadku napotkania błędu lub nieoczekiwanego zachowania:

Search my vault for [error message or symptom]

Wcześniejsze notatki z debugowania często zawierają przyczynę źródłową i poprawkę. Jest to szczególnie wartościowe w przypadku powtarzających się problemów między projektami — skarbiec pamięta to, co umyka pamięci.

Przygotowanie do przeglądu kodu

Przed przeglądem PR:

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

Skarbiec zwraca wcześniejsze decyzje, ograniczenia architektoniczne i standardy kodowania istotne dla recenzowanego kodu. Przegląd opiera się na wiedzy instytucjonalnej, a nie tylko na diffie.


Optymalizacja wydajności

Ta sekcja obejmuje strategie optymalizacji dla różnych rozmiarów skarbców i wzorców użytkowania.

Zarządzanie rozmiarem indeksu

Rozmiar skarbca Chunki Rozmiar BD Pełna reindeksacja Przyrostowa
500 notatek ~1 500 3 MB 15 sekund <1 sekunda
2 000 notatek ~6 000 12 MB 45 sekund 2 sekundy
5 000 notatek ~15 000 30 MB 2 minuty 4 sekundy
15 000 notatek ~50 000 83 MB 4 minuty <10 sekund
50 000 notatek ~150 000 250 MB 15 minut 30 sekund

Przy 50 000+ notatek warto rozważyć: - Zwiększenie batch_size z 64 do 128 w celu szybszego generowania embeddingów - Użycie trybu WAL (domyślnego) dla współbieżnego dostępu - Uruchamianie pełnej reindeksacji poza godzinami szczytu

Optymalizacja zapytań

Tryb WAL. Tryb Write-Ahead Logging w SQLite umożliwia współbieżne odczyty podczas zapisu przez indekser:

db.execute("PRAGMA journal_mode=WAL")

Ma to kluczowe znaczenie, gdy serwer MCP obsługuje zapytania w trakcie przyrostowej aktualizacji indeksera.

Pula połączeń. Serwer MCP powinien ponownie wykorzystywać połączenia z bazą danych zamiast otwierać nowe połączenie dla każdego zapytania. Pojedyncze długotrwałe połączenie w trybie WAL obsługuje współbieżne odczyty.

# 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

Wejście/wyjście z mapowaniem pamięci. Pragma mmap_size informuje SQLite o konieczności użycia I/O z mapowaniem pamięci dla pliku bazy danych. Dla bazy o rozmiarze 83 MB zmapowanie całego pliku do pamięci eliminuje większość odczytów z dysku.

Optymalizacja FTS5. Po pełnej reindeksacji należy uruchomić:

INSERT INTO chunks_fts(chunks_fts) VALUES('optimize');

Scala to wewnętrzne segmenty b-drzewa FTS5, zmniejszając opóźnienie zapytań przy kolejnych wyszukiwaniach.

Benchmarki skalowalności

Pomiary na Apple M3 Pro, 36 GB RAM, NVMe SSD:

Operacja 500 notatek 5 tys. notatek 15 tys. notatek 50 tys. notatek
Zapytanie BM25 2 ms 5 ms 12 ms 25 ms
Zapytanie wektorowe 1 ms 3 ms 8 ms 20 ms
Fuzja RRF <1 ms <1 ms 3 ms 5 ms
Pełne wyszukiwanie 3 ms 8 ms 23 ms 50 ms

Wszystkie benchmarki obejmują dostęp do bazy danych, wykonanie zapytania i formatowanie wyników. Opóźnienie sieciowe komunikacji MCP STDIO dodaje 1–2 ms.


Rozwiązywanie problemów

Desynchronizacja indeksu

Objaw: Wyszukiwanie zwraca nieaktualne wyniki lub pomija niedawno dodane notatki.

Przyczyna: Inkrementalny indekser nie został uruchomiony po dodaniu notatek lub znacznik czasu pliku (mtime) nie został zaktualizowany (np. plik zsynchronizowany z innego urządzenia z zachowanymi znacznikami czasu).

Rozwiązanie: Uruchomienie pełnej reindeksacji: python index_vault.py --full

Zmiana modelu embeddings

Objaw: Po zmianie modelu embeddings wyszukiwanie wektorowe zwraca bezsensowne wyniki.

Przyczyna: Stare wektory (z poprzedniego modelu) są porównywane z nowymi wektorami zapytań. Wymiary lub semantyka przestrzeni wektorowej są niekompatybilne.

Rozwiązanie: Indekser powinien wykryć niezgodność hasza modelu i automatycznie uruchomić pełną reindeksację. Jeśli tego nie zrobi, należy ręcznie wyczyścić bazę danych i przeprowadzić reindeksację:

rm vectors.db
python index_vault.py --full

Konserwacja FTS5

Objaw: Zapytania FTS5 zwracają nieprawidłowe lub niekompletne wyniki po wielu inkrementalnych aktualizacjach.

Przyczyna: Wewnętrzne segmenty FTS5 mogą ulec fragmentacji po wielu drobnych aktualizacjach.

Rozwiązanie: Przebudowa i optymalizacja:

INSERT INTO chunks_fts(chunks_fts) VALUES('rebuild');
INSERT INTO chunks_fts(chunks_fts) VALUES('optimize');

Przekroczenie limitu czasu MCP

Objaw: Narzędzie AI zgłasza przekroczenie limitu czasu serwera MCP.

Przyczyna: Pierwsze zapytanie uruchamia ładowanie modelu (leniwa inicjalizacja), co zajmuje 2–5 sekund. Domyślny limit czasu MCP w narzędziu AI może być krótszy.

Rozwiązanie: Wstępne załadowanie modelu przy starcie serwera:

# In MCP server initialization
retriever = HybridRetriever(db_path, vault_path)
retriever.search("warmup", limit=1)  # Trigger model load

Blokady plików SQLite

Objaw: Błędy SQLITE_BUSY lub SQLITE_LOCKED.

Przyczyna: Wiele procesów jednocześnie zapisuje do bazy danych. Tryb WAL umożliwia współbieżne odczyty, ale tylko jeden proces może zapisywać.

Rozwiązanie: Należy upewnić się, że tylko jeden proces (indekser) zapisuje do bazy danych. Serwer MCP i hooki powinny jedynie odczytywać dane. Jeśli konieczne są współbieżne zapisy, należy użyć trybu WAL i ustawić limit oczekiwania:

db.execute("PRAGMA busy_timeout=5000")  # Wait up to 5 seconds

Brak ładowania sqlite-vec

Objaw: Wyszukiwanie wektorowe jest wyłączone; retriever działa wyłącznie w trybie BM25.

Przyczyna: Rozszerzenie sqlite-vec nie jest zainstalowane, nie zostało znalezione w ścieżce bibliotek lub jest niekompatybilne z wersją SQLite.

Rozwiązanie:

# Install via pip
pip install sqlite-vec

# Or compile from source
git clone https://github.com/asg017/sqlite-vec
cd sqlite-vec && make

Weryfikacja ładowania rozszerzenia:

import sqlite3
db = sqlite3.connect(":memory:")
db.enable_load_extension(True)
db.load_extension("vec0")
print("sqlite-vec loaded successfully")

Problemy z pamięcią przy dużych sejfach

Objaw: Błędy braku pamięci podczas pełnej reindeksacji dużego sejfu (ponad 50 000 notatek).

Przyczyna: Rozmiar partii embeddings jest zbyt duży lub cała zawartość plików jest ładowana do pamięci jednocześnie.

Rozwiązanie: Zmniejszenie rozmiaru partii i inkrementalne przetwarzanie plików:

BATCH_SIZE = 32  # Reduce from 64

Należy również upewnić się, że indekser przetwarza pliki pojedynczo (odczytując, dzieląc na fragmenty i generując embeddings dla każdego pliku przed przejściem do następnego) zamiast ładować wszystkie pliki do pamięci.


Przewodnik migracji

Z Apple Notes

  1. Eksport Apple Notes za pomocą opcji „Eksportuj wszystko” (macOS) lub narzędzia migracyjnego, takiego jak apple-notes-liberator
  2. Konwersja eksportów HTML do markdown za pomocą markdownify lub pandoc
  3. Przeniesienie skonwertowanych plików do folderu 00-inbox/ w sejfie
  4. Przegląd i dodanie frontmatter do każdej notatki
  5. Przeniesienie notatek do odpowiednich folderów domenowych

Z Notion

  1. Eksport z Notion: Ustawienia → Eksport → Markdown i CSV
  2. Rozpakowanie eksportu do folderu 00-inbox/ w sejfie
  3. Naprawa artefaktów markdown specyficznych dla Notion:
  4. Notion używa - [ ] do list kontrolnych — to standardowy markdown
  5. Notion dołącza tabele właściwości jako HTML — należy je przekonwertować na frontmatter YAML
  6. Notion osadza obrazy jako ścieżki względne — należy skopiować obrazy do folderu załączników
  7. Dodanie standardowego frontmatter (type, domain, tags)
  8. Zastąpienie linków do stron Notion wiki-linkami Obsidian

Z Google Docs

  1. Użycie Google Takeout do eksportu wszystkich dokumentów
  2. Konwersja plików .docx do markdown: pandoc -f docx -t markdown input.docx -o output.md
  3. Konwersja wsadowa: for f in *.docx; do pandoc -f docx -t markdown "$f" -o "${f%.docx}.md"; done
  4. Przeniesienie do sejfu, dodanie frontmatter, organizacja w foldery

Ze zwykłych plików markdown (bez Obsidian)

Jeśli dysponujesz już katalogiem plików markdown:

  1. Otwarcie katalogu jako sejfu Obsidian (Obsidian → Open Vault → Open folder)
  2. Dodanie .obsidian/ do .gitignore, jeśli katalog jest wersjonowany
  3. Utworzenie szablonów frontmatter i zastosowanie ich do istniejących plików
  4. Rozpoczęcie łączenia notatek za pomocą [[wiki-links]] w miarę czytania i organizowania
  5. Natychmiastowe uruchomienie indeksera — system wyszukiwania działa od pierwszego dnia

Z innego systemu wyszukiwania

W przypadku migracji z innego systemu embeddings/wyszukiwania:

  1. Nie należy próbować migrować wektorów. Różne modele generują niekompatybilne przestrzenie wektorowe. Należy przeprowadzić pełną reindeksację z nowym modelem.
  2. Migracja treści, nie indeksu. Pliki sejfu stanowią źródło prawdy. Indeks jest artefaktem pochodnym.
  3. Weryfikacja po migracji. Warto uruchomić 10–20 zapytań, na które znana jest odpowiedź, i sprawdzić, czy wyniki odpowiadają oczekiwaniom.

Dziennik zmian

Data Zmiana
2026-04-01 Dodanie sekcji Obsidian CLI (polecenia v1.12 dla przepływów pracy AI). Dodanie sekcji wtyczek agentowych (Claudian, Agent Client). Dokumentacja podstawowej wtyczki Bases do organizacji sejfu. Aktualizacja liczby wtyczek do ponad 2 500. Dodanie iOS Share Extension jako źródła danych wejściowych. Aktualizacja macierzy kompatybilności z wbudowanymi wtyczkami agentowymi.
2026-03-30 MCPVault v0.11.0: narzędzie list_all_tags, obsługa .base/.canvas, zmiana nazwy na @bitbonsai/mcpvault. Obsidian Desktop v1.12.7 zawiera wbudowany plik binarny CLI dla szybszych interakcji terminalowych.
2026-03-23 Dokumentacja sqlite-vec v0.1.7 stable: obsługa DELETE dla tabel vec0, ograniczenia odległości KNN dla paginacji. Ogłoszenie indeksu DiskANN przybliżonego najbliższego sąsiada w nadchodzącym wydaniu.
2026-03-07 Dodanie potion-multilingual-128M (101 języków, maj 2025) do porównania modeli embeddings. sqlite-vec w wersji v0.1.7-alpha.10 (poprawki CI/CD, brak zmian funkcjonalnych). Potwierdzenie aktualności specyfikacji MCP i technik wyszukiwania.
2026-03-03 Aktualizacja ewolucji specyfikacji MCP (listopad 2025: Streamable HTTP, .well-known, adnotacje narzędzi). Dodanie fine-tuningu Model2Vec i obsługi tokenizera BPE/Unigram. Dodanie tabeli porównawczej serwerów MCP społeczności. Aktualizacja Smart Connections do v4.
2026-03-02 Dodanie potion-base-32M i potion-retrieval-32M do porównania modeli. Dodanie sekcji kwantyzacji/redukcji wymiarowości. Dodanie uwagi o ewolucji specyfikacji MCP.
2026-03-01 Pierwsze wydanie

Przypisy


  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. Wprowadza RRF z parametrem k=60 jako bezparametrową metodę łączenia list rankingowych. 

  4. OpenAI Embeddings Pricing. text-embedding-3-small: 0,02 USD za milion tokenów. Szacowany koszt pełnej reindeksacji skarbca: ~0,30 USD. 

  5. van Dongen, T. et al. Model2Vec: Turn any Sentence Transformer into a Small Fast Model. arXiv, 2025. Opisuje podejście destylacyjne generujące statyczne embeddingi z transformerów zdaniowych. 

  6. MTEB: Massive Text Embedding Benchmark. potion-base-8M uzyskuje średnio 50,03 w porównaniu z 56,09 dla all-MiniLM-L6-v2 (89% zachowanej jakości). 

  7. SQLite FTS5 Extension. FTS5 zapewnia wyszukiwanie pełnotekstowe z rankingiem BM25 i konfigurowalnymi wagami kolumn. 

  8. sqlite-vec: A vector search SQLite extension. Udostępnia wirtualne tabele vec0 do wyszukiwania wektorowego KNN w ramach SQLite. 

  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. Gęste reprezentacje przewyższają BM25 o 9–19% w zadaniach odpowiadania na pytania w domenie otwartej. 

  11. Reimers, N. and Gurevych, I. Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks. EMNLP, 2019. Fundamentalna praca nad gęstym podobieństwem semantycznym. 

  12. Luan, Y. et al. Sparse, Dense, and Attentional Representations for Text Retrieval. TACL, 2021. Wyszukiwanie hybrydowe (hybrid retrieval) konsekwentnie przewyższa podejścia jednomodalne na MS MARCO. 

  13. SQLite Write-Ahead Logging. Tryb WAL umożliwiający równoczesne odczyty przy jednym procesie zapisującym. 

  14. Gao, Y. et al. Retrieval-Augmented Generation for Large Language Models: A Survey. arXiv, 2024. Przegląd architektur RAG i strategii dzielenia na fragmenty (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. Oficjalna dokumentacja Obsidian. 

  18. Model Context Protocol Specification. Standard MCP do łączenia narzędzi AI ze źródłami danych. 

  19. Dane produkcyjne autora. 16 894 plików, 49 746 fragmentów, baza danych SQLite o rozmiarze 83,56 MB, 7 771 sygnałów przetworzonych w ciągu 14 miesięcy. Opóźnienie zapytań mierzone za pomocą time.perf_counter()

  20. Model2Vec Potion Models. Minish Lab, 2025. Potion-base-32M (MTEB 52,46), potion-retrieval-32M (MTEB retrieval 36,35) oraz funkcje kwantyzacji i redukcji wymiarowości w wersji v0.5.0+. 

  21. Update on the Next MCP Protocol Release. Wydanie z listopada 2025 wprowadza transport Streamable HTTP, wykrywanie przez .well-known URL, strukturalne adnotacje narzędzi oraz standaryzację poziomów SDK. Następne wydanie planowane wstępnie na połowę 2026 roku z operacjami asynchronicznymi, rozszerzeniami domenowymi oraz komunikacją agent-agent. 

  22. Model2Vec Releases. v0.4.0 (luty 2025): wsparcie trenowania i dostrajania. v0.5.0 (kwiecień 2025): przepisanie backendu, kwantyzacja, redukcja wymiarowości. v0.7.0 (październik 2025): kwantyzacja słownictwa, wsparcie tokenizatorów BPE/Unigram. 

  23. Smart Connections for Obsidian. Smart Connections v4: lokalne embeddingi AI z priorytetem offline, wyszukiwanie semantyczne działa bez połączenia po początkowym indeksowaniu. 

  24. potion-multilingual-128M. Minish Lab, maj 2025. Statyczny model embeddingów dla 101 języków, najlepiej działające wielojęzyczne embeddingi statyczne. Ta sama zależność wyłącznie od numpy co w pozostałych modelach potion. 

  25. MCPVault v0.11.0. Marzec 2026. Nowe narzędzie list_all_tags do skanowania tagów w frontmatter i hashtagów z licznikami. Ulepszona obsługa folderów z kropką, wsparcie plików .base i .canvas. Pakiet przemianowany na @bitbonsai/mcpvault w npm. 

  26. sqlite-vec v0.1.7 Release. 17 marca 2026. Stabilne wydanie: obsługa DELETE dla wirtualnych tabel vec0, ograniczenia odległości KNN do paginacji, ulepszenia testów fuzzingowych. Indeksowanie DiskANN przybliżonych najbliższych sąsiadów zapowiedziane na przyszłe wydanie. 

  27. Introduction to Bases. Podstawowy plugin Obsidian wprowadzony w wersji 1.9.10. Widoki bazodanowe (tabele, galerie, kalendarze, tablice kanban) nad plikami skarbca wykorzystujące właściwości frontmatter jako pola. Pliki zapisywane w formacie .base

  28. Obsidian 1.12 Desktop Changelog. 27 lutego 2026. Wprowadza CLI Obsidian do automatyzacji skarbca z poziomu terminala. Polecenia obejmują wyszukiwanie, notatki dzienne, szablony, właściwości, pluginy, zadania oraz narzędzia deweloperskie. Dokumentacja CLI

  29. Claudian. Plugin Obsidian osadzający Claude Code jako współpracownika AI w skarbcu. Zapewnia czat w panelu bocznym, prompty uwzględniające kontekst, wsparcie wizji, polecenia slash oraz tryby uprawnień. 

  30. Agent Client. Plugin Obsidian zapewniający zunifikowany interfejs dla Claude Code, Codex CLI i Gemini CLI poprzez Agent Client Protocol (ACP). Obsługuje odwołania do notatek, wykonywanie poleceń powłoki oraz zatwierdzanie akcji. 

  31. Obsidian iOS Changelog. Aktualizacje z początku 2026 obejmują Share Extension do zapisywania treści z innych aplikacji bezpośrednio do skarbca, poprawki widżetów Notatki Dziennej i Zakładek oraz ulepszenia odświeżania widżetu Podglądu Notatki. 

VAULT obsidian.md INDEXED