Obsidian Hybrid Search

Servidor MCP local-first para pesquisar vaults privados do Obsidian com recuperação híbrida de texto completo, fuzzy, semântica e de grafo de wikilinks.

Documentação

Obsidian Hybrid Search

npm version Tests Total downloads

Obsidian Hybrid Search explains hybrid retrieval from Obsidian notes

Seu cofre do Obsidian já contém suas melhores ideias. O Obsidian Hybrid Search torna essas ideias mais fáceis de encontrar, reutilizar e incorporar ao trabalho assistido por IA.

Ele dá ao seu cofre um único mecanismo de recuperação e três formas práticas de usá-lo. O plugin nativo do Obsidian oferece busca rápida, pré-visualizações, notas semelhantes, descoberta de links e visualizações de grafo enquanto você escreve. O servidor MCP permite que agentes de IA pesquisem e leiam suas notas por meio de chamadas de ferramenta. A CLI oferece aos usuários avançados o mesmo mecanismo para indexação, filtragem, reordenação, leitura e script.

A busca entende como cofres reais são construídos. Ela combina busca semântica, texto completo BM25, correspondência difusa de títulos e aliases, tags, pastas, frontmatter, wikilinks, backlinks e consulta de notas semelhantes. Você pode pesquisar por ideia, frase, título, relacionamento ou metadados sem precisar lembrar as palavras exatas que escreveu.

Isso transforma o Obsidian em um sistema de conhecimento pessoal mais forte e em um melhor ponto de partida para o trabalho com IA. Os agentes podem começar pelas suas próprias notas, extrair contexto citado dos arquivos de origem, seguir material relacionado e trabalhar com conhecimento em que você já confia. O OHS roda localmente por padrão com SQLite, FTS5, sqlite-vec, ranqueamento RRF e APIs de embeddings opcionais compatíveis com OpenAI.

Qualidade da busca

Avaliado no cofre Obsidian Help (171 notas, 58 consultas, modelo local):

OHS (este projeto)qmd
nDCG@50.7330.659
MRR0.7880.665
Hit@10.7240.500
Tempo médio de consulta571 ms ¹754 ms ²
Download do modelo~117 MB~2.2 GB

¹ CPU (Apple Silicon), modo híbrido, sem reordenação. ² GPU (Apple Silicon Metal), expansão de consulta por LLM + reordenação.

O OHS usa Xenova/multilingual-e5-small. Como reproduzir → · Benchmark completo →

Benchmark de cofre de conhecimento real

O OHS também é avaliado nas notas públicas evergreen de Andy Matuschak, convertidas em um cofre do Obsidian com nomes de arquivo baseados em títulos, URLs de origem no frontmatter, anexos locais e 5,000+ links internos de notas em 1,357 notas.

O conjunto dourado curado inclui 78 consultas avaliadas manualmente abrangendo busca de itens conhecidos, paráfrases, fragmentos de citações, tópicos ambíguos, busca de citações e evidências em múltiplas notas.

Usando o modelo de embeddings local padrão, o OHS apresenta desempenho forte nessa rede densa de notas.

MétricaValor
nDCG@50.722
nDCG@100.753
MRR0.874
Hit@10.795
Hit@50.974
Recall@100.972
AllRel@100.949

O benchmark exercita a recuperação em um cofre de conhecimento real altamente conectado, incluindo consultas que não simplesmente repetem títulos de notas.

JSON de resultados · Reproduzir e interpretar →

Benchmark de memória em larga escala

Para testar a recuperação em um conjunto de dados público maior, o LongMemEval-S foi convertido em um cofre no estilo Obsidian com 22,419 notas e 470 consultas de recuperação. Usando embeddings baai/bge-m3, o OHS classificou fortemente as notas que contêm as respostas:

MétricaValor
nDCG@50.895
MRR0.920
Hit@10.889
Hit@50.968
Recall@100.950
AllRel@100.904

Para este benchmark, cada consulta usa o haystack fornecido pelo LongMemEval como escopo de busca. Isso torna o resultado reproduzível e fácil de inspecionar consulta por consulta, ao mesmo tempo em que exercita a recuperação em um grande cofre de memória gerado.

JSON de resultados · Reproduzir e interpretar →

Recursos

  • Busca híbrida
    • BM25 + título difuso + embeddings semânticos, fundidos com RRF
  • Busca por alias
    • notas com aliases: no frontmatter são indexadas e pesquisáveis por qualquer alias; correspondências de alias são reforçadas no BM25 (peso 5×) e na pontuação de título difuso
  • Quatro modos de busca
    • hybrid, semantic, fulltext, title (para consultas de texto)
  • Consulta de notas semelhantes
    • passe --path para encontrar notas semanticamente relacionadas usando embeddings de trechos armazenados, com fallback de título + conteúdo
  • Travessia de grafo
    • --path --related mostra notas vinculadas em profundidade configurável; filtre por --direction outgoing|backlinks|both
  • Links e backlinks
    • cada resultado inclui links de saída e backlinks
  • Filtragem de escopo
    • restrinja a subpasta(s); suporta múltiplos valores e exclusões (-notes/dev/)
  • Filtragem por tags
    • filtre por tag(s); suporta múltiplos valores e exclusões (-category/cs)
  • Controle de trechos
    • --snippet-length define a janela de contexto; trechos vazios sempre recorrem ao conteúdo da nota
  • Saída estendida
    • --extended adiciona uma coluna TAGS/ALIASES à tabela da CLI mostrando tags do frontmatter (#tag) e aliases
  • Indexação incremental
    • reindexa apenas arquivos alterados; observa edições em tempo real
  • Fan-out de múltiplas consultas
    • passe múltiplas consultas de uma vez (ohs "q1" "q2" ou queries[] no MCP); os resultados são mesclados via RRF, então uma nota que se classifica bem em qualquer consulta sobe para o topo; útil quando a nota pode usar vocabulário diferente da consulta
  • Reordenação por cross-encoder
    • --rerank re-pontua resultados com bge-reranker-v2-m3 (ONNX int8, download único de ~570 MB); melhora a precisão para consultas conceituais e multilíngues; aplicado após a mesclagem de múltiplas consultas
  • Embeddings locais
    • funciona offline via @huggingface/transformers (sem necessidade de chave de API); modelo padrão: Xenova/multilingual-e5-small, 100+ idiomas
  • Embeddings remotos
    • API compatível com OpenAI (OpenRouter, Ollama, etc.)
  • Leitura de notas
    • read busca uma ou mais notas por caminho relativo ao cofre; retorna o conteúdo completo com título, aliases, tags, links e backlinks; em caso de caminho não encontrado, retorna as 3 principais sugestões difusas
  • Padrões de ignorar
    • exclua pastas, extensões ou arquivos específicos
  • Plugin do Obsidian

Instalação

npm install -g obsidian-hybrid-search

Uso da CLI

Início rápido

A configuração recomendada é definir OBSIDIAN_VAULT_PATH uma vez em ~/.zshrc ou ~/.bashrc. Isso permite executar a CLI de qualquer diretório.

export OBSIDIAN_VAULT_PATH="/path/to/your/vault"

Abra um novo terminal e indexe o cofre uma vez.

ohs reindex

Agora você pode pesquisar de qualquer diretório.

ohs "zettelkasten"

Executar a partir de um cofre

Alternativamente, execute a CLI sem variável de ambiente a partir de qualquer diretório dentro do seu cofre. Ela encontra a raiz do cofre subindo até a pasta .obsidian/ mais próxima.

cd /path/to/your/vault
ohs reindex
ohs "zettelkasten"

Fora do cofre, defina OBSIDIAN_VAULT_PATH ou passe --db /path/to/vault/.obsidian-hybrid-search.db explicitamente.

Embeddings remotos opcionais

Por padrão, a CLI usa o modelo local Xenova/multilingual-e5-small. Ele funciona offline sem chave de API, baixa cerca de 117 MB no primeiro uso e suporta mais de 100 idiomas.

Para usar uma API remota, adicione suas configurações ao seu perfil de shell.

export OPENAI_API_KEY="sk-..."

# Override the default API base for another provider
# export OPENAI_BASE_URL="https://openrouter.ai/api/v1"  # OpenRouter
# export OPENAI_BASE_URL="http://localhost:11434/v1"     # Ollama (no key needed)
# export OPENAI_BASE_URL="http://localhost:1234/v1"      # LM Studio (no key needed)

# Override the default text-embedding-3-small model
# export OPENAI_EMBEDDING_MODEL="text-embedding-3-small"

Modos de busca

A CLI suporta quatro modos de busca chamados hybrid, fulltext, semantic e title, além de travessia de grafo para notas vinculadas. Os comandos abaixo mostram como usá-los, aplicar filtros, reordenar resultados e controlar a saída.

# Hybrid search (default)
ohs "zettelkasten atomic notes"

# Fulltext BM25 search
ohs "permanent notes" --mode fulltext

# Fuzzy title search (fast, typo-tolerant)
ohs "zettleksten" --mode title

# Semantic / vector search
ohs "how to build a knowledge graph" --mode semantic

# Limit results and set a score threshold
ohs "productivity systems" --limit 5 --threshold 0.3

# Restrict to a subfolder
ohs "daily review" --scope notes/periodic/
ohs "daily review" --folder notes/periodic/    # alias for --scope

# Restrict to multiple subfolders (OR)
ohs "productivity" --scope notes/pkm/ --scope notes/2024/

# Exclude a subfolder
ohs "programming" --scope notes/ --scope -notes/archive/

# Filter by tag
ohs "productivity" --tag pkm
ohs "machine learning" --tag note/basic/primary

# Filter by multiple tags (AND include, exclude with -)
ohs "learning" --tag pkm --tag work

# Filter by frontmatter / properties (exact match, case-insensitive)
ohs "notes" --frontmatter status:todo
ohs "notes" --prop priority:high          # --prop is alias for --frontmatter

# Filter by multiple frontmatter fields (AND)
ohs "notes" --frontmatter status:todo --frontmatter priority:high

# Exclude by frontmatter value
ohs "notes" --frontmatter -status:done

# Filter-only mode: no query, just filters (returns all matching notes sorted by title)
ohs --frontmatter status:todo
ohs --folder notes/2024/
ohs --tag pkm
ohs --frontmatter status:done --tag archived

# Unlimited results in filter-only mode (default limit is 10)
ohs --folder notes/ --limit 0

# Find semantically similar notes
ohs --path notes/pkm/zettelkasten.md

# Graph traversal: show notes linked to/from this note
# Results show depth: -1/-2 = backlinks, 0 = source, +1/+2 = outgoing links
ohs --path notes/pkm/zettelkasten.md --related
ohs --path notes/pkm/zettelkasten.md --related --depth 2

# Only outgoing links (what this note references)
ohs --path notes/pkm/zettelkasten.md --related --direction outgoing

# Only backlinks (who references this note)
ohs --path notes/pkm/zettelkasten.md --related --direction backlinks

# Traverse standard Markdown note links instead of Obsidian wikilinks
ohs --path notes/pkm/zettelkasten.md --related --link-type markdown

# Traverse both wikilinks and standard Markdown note links
ohs --path notes/pkm/zettelkasten.md --related --link-type all

# Longer context around each link
ohs --path notes/pkm/zettelkasten.md --related --snippet-length 500

# Rerank results with a cross-encoder model (improves precision, ~1-3s extra latency)
# Downloads bge-reranker-v2-m3 ONNX (~570 MB) on first use, cached in ~/.cache/huggingface/
ohs "zettelkasten atomic notes" --rerank

# Show tags and aliases alongside results
ohs "zettelkasten" --extended

# JSON output (for scripting)
ohs "spaced repetition" --json

# Output only paths (one per line) — useful for piping into read
ohs --frontmatter id:OHS-4 --only-paths
ohs read ${(f)"$(ohs search --frontmatter status:todo --only-paths)"}  # zsh: read all matching notes

# Output absolute filesystem paths
ohs "zettelkasten" --only-absolute-paths

# Open results in Obsidian (each in a new tab)
ohs "zettelkasten" --open

# Reindex the vault
ohs reindex

# Force full reindex
ohs reindex --force

# Reindex a single file
ohs reindex notes/pkm/zettelkasten.md

# Retry only the notes whose chunks failed to embed
ohs reindex --errors

# Show indexing status
ohs status

# Show recent indexing activity
ohs status --recent

# Show chunks that failed to embed
ohs status --errors

# Read a note by path (outputs body content without frontmatter)
ohs read notes/pkm/zettelkasten.md

# Read raw file from vault (with frontmatter, like cat)
ohs read notes/pkm/zettelkasten.md --raw

# Read multiple notes (separator between each)
ohs read notes/pkm/zettelkasten.md notes/pkm/evergreen-notes.md

# Cap content length
ohs read notes/pkm/zettelkasten.md --snippet-length 2000

# Structured output with all metadata
ohs read notes/pkm/zettelkasten.md --json

Aliases de shell

Adicione ao seu ~/.zshrc ou ~/.bashrc para acesso rápido:

alias ohss='ohs --mode semantic'
alias ohst='ohs --mode title'
alias ohsf='ohs --mode fulltext'
alias ohsr='ohs read'
alias ohsi='ohs reindex'
alias ohsst='ohs status'

Em seguida, recarregue (source ~/.zshrc) e use:

ohs "zettelkasten"                        # hybrid search
ohss "how to build a knowledge graph"     # semantic
ohst "zettelkasten"                       # fuzzy title (typo-tolerant)
ohsf "permanent notes"                    # fulltext BM25
ohsr "notes/pkm/zettelkasten.md"          # read note by path
ohsi                                      # reindex vault
ohsst                                     # show status
ohsst --recent                            # show recent indexing activity
ohsst --errors                            # show chunks that failed to embed

Exemplo de saída

A busca híbrida retorna uma tabela com pontuações e trechos. As pontuações são codificadas por cores de acordo com a relevância:

PontuaçãoCorSignificado
0.8 – 1.0verdeAltamente relevante
0.5 – 0.8amareloModeradamente relevante
0.2 – 0.5sem corParcialmente relevante
0.0 – 0.2esmaecidoBaixa relevância
┌───────┬───────────────────────────────┬────────────────────────────────────────────┐
│ SCORE │ PATH                          │ SNIPPET                                    │
├───────┼───────────────────────────────┼────────────────────────────────────────────┤
│  0.98 │ notes/pkm/zettelkasten.md     │ A note-taking method developed by Niklas   │
│       │                               │ Luhmann. Each note contains one atomic...  │
├───────┼───────────────────────────────┼────────────────────────────────────────────┤
│  0.72 │ notes/pkm/evergreen-notes.md  │ Evergreen notes are written to evolve over │
│       │                               │ time. Unlike fleeting notes, they are...   │
└───────┴───────────────────────────────┴────────────────────────────────────────────┘

Com --extended, uma coluna TAGS/ALIASES é adicionada. As tags são prefixadas com #, os aliases são exibidos como estão:

┌───────┬───────────────────────────────┬──────────────────┬──────────────────────────────┐
│ SCORE │ PATH                          │ TAGS/ALIASES     │ SNIPPET                      │
├───────┼───────────────────────────────┼──────────────────┼──────────────────────────────┤
│  0.98 │ notes/pkm/zettelkasten.md     │ #pkm             │ A note-taking method...      │
│       │                               │ ЗК               │                              │
│       │                               │ slip-box         │                              │
├───────┼───────────────────────────────┼──────────────────┼──────────────────────────────┤
│  0.72 │ notes/pkm/evergreen-notes.md  │ #pkm             │ Evergreen notes are written  │
│       │                               │ #writing         │ to evolve over time...       │
└───────┴───────────────────────────────┴──────────────────┴──────────────────────────────┘

O modo de título omite a coluna de trechos automaticamente.

Servidor MCP

A maioria dos assistentes de IA opera sem acesso ao seu conhecimento pessoal e só consegue trabalhar com o que você cola na conversa. Adicionar este servidor dá a qualquer assistente compatível com MCP um índice persistente e pesquisável de todo o seu cofre. Isso se torna uma chamada de ferramenta, não uma sessão de copiar e colar: o assistente consulta suas notas da mesma forma que chama qualquer outra ferramenta, obtém resultados classificados com trechos e links e pode navegar pelo seu grafo de conhecimento sob demanda.

Adicione à sua configuração MCP (.mcp.json, claude_desktop_config.json ou equivalente para o seu cliente).

Configuração mínima (embeddings locais, sem chave de API)

Usa o modelo integrado Xenova/multilingual-e5-small. Funciona totalmente offline e suporta 100+ idiomas. Baixa ~117 MB na primeira execução.

{
  "mcpServers": {
    "obsidian-hybrid-search": {
      "command": "npx",
      "args": ["-y", "-p", "obsidian-hybrid-search@latest", "obsidian-hybrid-search-mcp"],
      "env": {
        "OBSIDIAN_VAULT_PATH": "/path/to/your/vault"
      }
    }
  }
}

Configuração completa (OpenRouter)

{
  "mcpServers": {
    "obsidian-hybrid-search": {
      "command": "npx",
      "args": ["-y", "-p", "obsidian-hybrid-search@latest", "obsidian-hybrid-search-mcp"],
      "env": {
        "OBSIDIAN_VAULT_PATH": "/path/to/your/vault",
        "OBSIDIAN_PREFIX": "myvault_",
        "OBSIDIAN_RESPECT_GITIGNORE": "true",
        "OBSIDIAN_IGNORE_PATTERNS": ".obsidian/**,templates/**,*.canvas",
        "OBSIDIAN_INCLUDE_PATTERNS": "private/notes/**",
        "OPENAI_API_KEY": "sk-or-v1-...",
        "OPENAI_BASE_URL": "https://openrouter.ai/api/v1",
        "OPENAI_EMBEDDING_MODEL": "openai/text-embedding-3-small"
      }
    }
  }
}

Nota: Na primeira execução, npx instalará o pacote automaticamente. Os padrões de ignorar são persistidos no banco de dados e restaurados em toda inicialização subsequente, mesmo que a variável de ambiente esteja ausente.

Servidor HTTP compartilhado

Use isto quando vários clientes MCP precisarem compartilhar um único processo de busca/indexação de longa duração.

Inicie ou reutilize o servidor em segundo plano:

OBSIDIAN_VAULT_PATH="/path/to/your/vault" ohs serve

serve inicia o servidor MCP via HTTP por padrão; serve --http é o equivalente explícito. O comando imprime a URL do servidor, o PID, o caminho do log e um trecho de configuração do cliente. O endereço de bind padrão é 127.0.0.1:3939.

Em seguida, adicione isto a uma configuração de cliente MCP baseada em URL (.mcp.json, claude_desktop_config.json ou equivalente):

{
  "mcpServers": {
    "obsidian-hybrid-search": {
      "url": "http://127.0.0.1:3939/mcp"
    }
  }
}

Gerencie o servidor:

ohs serve status
ohs serve stop
ohs serve --foreground
ohs serve --http --foreground

O modo HTTP usa MCP Streamable HTTP sem estado. Ele não emite nem valida Mcp-Session-Id, então um cliente MCP pode continuar fazendo chamadas de ferramenta após o daemon reiniciar, mesmo que ainda envie um cabeçalho de sessão obsoleto. Este modo é destinado às ferramentas de requisição/resposta do servidor e não fornece streams SSE persistentes, notificações iniciadas pelo servidor ou retomada de SSE.

Se a porta 3939 já estiver em uso, o comando sai com um erro em vez de escolher outra porta automaticamente. Use --port para cofres separados.

Ao vincular além do localhost, permita todos os hostnames ou endereços que os clientes MCP usarão.

ohs serve \
  --host 0.0.0.0 \
  --allowed-host 192.168.1.20:3939 \
  --allowed-host notes.example.com:3939

Repita --allowed-host para múltiplos valores. Você também pode definir uma lista separada por vírgulas com OBSIDIAN_MCP_ALLOWED_HOSTS. A opção --allow-any-host desativa a proteção do cabeçalho Host para redes confiáveis.

Ferramentas MCP disponíveis

FerramentaDescrição
searchPesquise no vault. Use query para busca de texto (mode: hybrid/semantic/fulltext/title) ou path para similaridade semântica. Combine path com related: true para travessia de grafo. Passe queries[] para fan-out de múltiplas consultas (busca paralela, fusão RRF). Suporta scope, tag, limit, threshold, depth, direction, snippet_length, rerank
readBusca uma ou mais notas por caminho relativo ao vault. Retorna conteúdo completo, título, aliases, tags, links e backlinks. Em caso de caminho não encontrado: retorna found: false com as 3 principais sugestões difusas. Aceita um único caminho ou um array. Use snippet_length para limitar o tamanho do conteúdo
reindexReindexa o vault ou um arquivo específico
statusMostra o total de notas, a contagem indexada e o horário da última indexação

Defina OBSIDIAN_PREFIX para adicionar um prefixo a cada nome de ferramenta. Por exemplo, myvault_ produz myvault_search e myvault_read. O prefixo fica vazio por padrão.

Configuração

Variável de ambientePadrãoDescrição
OBSIDIAN_VAULT_PATHObrigatório para MCP; a CLI detecta automaticamenteCaminho absoluto para o seu vault
OBSIDIAN_PREFIX""Prefixo opcional de ferramenta MCP, ex.: myvault_myvault_search, myvault_read
OBSIDIAN_IGNORE_PATTERNS.obsidian/**,templates/**,*.canvasPadrões de ignorar separados por vírgula
OBSIDIAN_RESPECT_GITIGNOREtrueLê arquivos .gitignore raiz e aninhados; defina como false para desativar
OBSIDIAN_INCLUDE_PATTERNS""Padrões separados por vírgula para re-incluir notas ignoradas apenas por .gitignore
OPENAI_API_KEYNenhumChave de API; omita para usar embeddings de modelo local ou servidores sem chave (Ollama, LM Studio)
OPENAI_BASE_URLhttps://api.openai.com/v1URL base da API
OPENAI_EMBEDDING_MODELtext-embedding-3-smallNome do modelo de embedding

Padrões de ignorar

  • Use folder/** para ignorar um diretório e todo o seu conteúdo.
  • Use *.canvas para ignorar arquivos por extensão.
  • Use exact/path.md para ignorar um arquivo específico.

Arquivos .gitignore raiz e aninhados são respeitados por padrão. Defina OBSIDIAN_RESPECT_GITIGNORE=false para desativar esse comportamento. Use OBSIDIAN_INCLUDE_PATTERNS para re-incluir notas Markdown que são ignoradas apenas por .gitignore. Os padrões de inclusão não substituem OBSIDIAN_IGNORE_PATTERNS nem exclusões internas.

O banco de dados armazena a configuração de ignorar e a restaura quando o servidor reinicia, mesmo que a variável de ambiente esteja ausente.

Como funciona

  1. Indexação divide as notas por títulos com um fallback de janela deslizante, cria embeddings e armazena os resultados em SQLite com FTS5 e sqlite-vec.
  2. Busca executa BM25, busca difusa de títulos e aliases por trigramas e busca vetorial KNN em paralelo. O BM25 usa pesos de 10× para títulos, 5× para aliases e 1× para conteúdo. Em seguida, o RRF dá aos resultados semânticos e BM25 um peso de 1,5× cada, correspondências exatas de alias 2× e correspondências difusas parciais 0,25×. As pontuações finais variam de 0 a 1, onde pontuações mais altas significam maior relevância.
  3. Links são resolvidos a partir de wikilinks como [[note]], mapeados para caminhos de notas e armazenados. Cada resultado de busca inclui arrays links e backlinks.
  4. Observador usa chokidar para detectar alterações em arquivos e atualizar o índice em segundo plano.

Contribuindo

Issues e pull requests são bem-vindos. Consulte CONTRIBUTING.md para instruções de configuração e verificações do projeto.

Licença

MIT