obsidian:~/vault$ search --hybrid obsidian

Obsidian MCP + recuperación híbrida: referencia de 2026

# Conecta Obsidian con Claude y otros agentes mediante MCP: configuración del servidor, recuperación híbrida BM25 + vectorial e indexación de una bóveda de 16.894 archivos, con configuraciones funcionales.

words: 17834 read_time: 82m updated: 2026-07-07 21:46
$ retriever search --hybrid obsidian

Obsidian no es una app para tomar notas. Es un corpus Markdown local-first, de texto plano y estructurado como grafo, que se convierte en un reservorio de contexto para IA cuando agregas infraestructura de recuperación. 16.894 archivos. 49.746 chunks. Consultas de 23 ms. Cero llamadas a API. Un archivo SQLite de 83 MB. Esta guía cubre el sistema completo: desde la arquitectura del vault hasta la recuperación hybrid, la integración con MCP y los flujos de trabajo operativos.


Puntos clave

Ingeniería de contexto, no toma de notas. El valor de un vault de Obsidian para AI no está en las notas en sí, sino en la capa de recuperación que permite consultarlas. Un vault de 16.000 archivos sin recuperación es una base de datos de solo escritura. Un vault de 200 archivos con búsqueda hybrid e integración con MCP es una base de conocimiento de AI. La infraestructura de recuperación es el producto. Las notas son la materia prima.

La recuperación hybrid supera a la búsqueda solo por palabras clave o solo semántica. BM25 captura identificadores exactos y nombres de funciones. La búsqueda vectorial captura sinónimos y coincidencias conceptuales entre terminologías distintas. Reciprocal Rank Fusion (RRF) combina ambos sin requerir calibración de puntajes. Ningún método por sí solo cubre ambos modos de falla. La investigación sobre ranking de pasajes en MS MARCO confirma el patrón: la recuperación hybrid supera de forma consistente a cualquiera de los métodos por separado.3 El análisis profundo del recuperador hybrid cubre la matemática de RRF, ejemplos resueltos con números reales, análisis de modos de falla y una calculadora interactiva de fusión.

MCP da acceso directo al vault a las herramientas de AI. Los servidores Model Context Protocol (MCP) exponen el recuperador como una herramienta que Claude Code, Codex CLI, Cursor y otras herramientas de AI pueden llamar directamente. El agente consulta el vault, recibe resultados ordenados con atribución de fuente y usa el contexto sin cargar archivos completos. El servidor MCP es una envoltura delgada alrededor del motor de recuperación.

Local-first significa cero costos de API y privacidad completa. Toda la pila se ejecuta en una sola máquina: SQLite para almacenamiento, Model2Vec para embeddings, FTS5 para búsqueda por palabras clave, sqlite-vec para KNN vectorial. Sin servicios en la nube, sin llamadas a API, sin dependencia de red. Las notas personales nunca salen de la máquina. Volver a generar embeddings para 49.746 chunks costaría aproximadamente $0,30 con los precios de API de OpenAI, pero los costos reales son la latencia, la exposición de privacidad y la dependencia de red para un sistema que debería funcionar sin conexión.4

La indexación incremental mantiene el sistema actualizado en menos de 10 segundos. La comparación de tiempos de modificación de archivos detecta cambios. Solo los archivos modificados se vuelven a dividir en chunks y a procesar con embeddings. Una reindexación completa toma alrededor de cuatro minutos en hardware Apple M-series. Las actualizaciones incrementales de las ediciones de un día típico se ejecutan en menos de diez segundos. El sistema se mantiene actualizado sin intervención manual.

La arquitectura escala de 200 a más de 20.000 notas. El mismo diseño de tres capas (entrada, recuperación, integración) funciona con cualquier tamaño de vault. Empieza con búsqueda solo BM25 sobre un vault pequeño. Agrega búsqueda vectorial cuando las colisiones de palabras clave se vuelvan un problema. Agrega fusión RRF cuando necesites coincidencias exactas y semánticas. Cada capa es útil de forma independiente y se puede quitar de forma independiente.


Cómo usar esta guía

Esta guía cubre el sistema completo. Tu punto de partida depende de dónde estés:

Estás… Empieza aquí Luego explora
Empezando con Obsidian + AI Por qué Obsidian para infraestructura de AI, Configuración de Obsidian MCP Arquitectura del vault, Arquitectura del servidor MCP
Tienes un vault existente y quieres acceso con AI Arquitectura del servidor MCP, Integración con Claude Code Modelos de embeddings, Búsqueda de texto completo
Estás construyendo un sistema de recuperación El pipeline completo de recuperación, Reciprocal Rank Fusion Ajuste de rendimiento, Solución de problemas
Trabajas en un contexto de equipo o empresa Marco de decisión, Patrones de Knowledge Graph Recetas de flujo de trabajo para desarrolladores, Guía de migración

Las secciones marcadas como Contrato incluyen detalles de implementación, bloques de configuración y modos de falla. Las secciones marcadas como Narrativa se enfocan en conceptos, decisiones de arquitectura y el razonamiento detrás de las decisiones de diseño. Las secciones marcadas como Receta ofrecen flujos de trabajo paso a paso.


Por qué Obsidian para infraestructura de AI

La tesis de esta guía: los vaults de Obsidian son el mejor sustrato para bases de conocimiento personales de AI porque son local-first, de texto plano, estructurados como grafo y el usuario controla cada capa de la pila.

Qué le da Obsidian a la AI que las alternativas no dan

Archivos markdown de texto plano. Cada nota es un archivo .md en tu sistema de archivos. Sin formato propietario, sin exportación de base de datos, sin API requerido para leer el contenido. Cualquier herramienta que lea archivos puede leer tu vault. grep, ripgrep, pathlib de Python, SQLite FTS5: todas funcionan directamente sobre los archivos fuente. Cuando construyes un sistema de recuperación, estás indexando archivos, no respuestas de API. El índice siempre es consistente con la fuente porque la fuente es el sistema de archivos.

Arquitectura local-first. El vault vive en tu máquina. Sin servidor, sin dependencia de sincronización en la nube, sin límites de tasa de API, sin términos de servicio que regulen cómo procesas tu propio contenido. Puedes generar embeddings, indexar, hacer chunking y buscar tus notas sin ningún servicio externo. Esto importa para la infraestructura de AI porque el pipeline de recuperación corre tan rápido como lo permita tu disco, no tan rápido como responda un endpoint de API. También importa para la privacidad: las notas personales que contienen credenciales, datos de salud, información financiera y reflexiones privadas nunca salen de tu máquina.

Estructura de grafo mediante wiki-links. La sintaxis [[wiki-link]] de Obsidian crea un grafo dirigido entre notas. Una nota sobre implementación de OAuth enlaza a notas sobre rotación de tokens, gestión de sesiones y seguridad de API. La estructura de grafo codifica relaciones entre conceptos curadas por una persona. Los embeddings vectoriales capturan similitud semántica, pero los wiki-links capturan conexiones intencionales que el autor hizo mientras pensaba sobre el tema. El grafo es una señal que los embeddings no pueden replicar.

Ecosistema de plugins. Obsidian tiene más de 2.500 plugins de la comunidad (a marzo de 2026, frente a más de 1.800 a mediados de 2025). Dataview consulta tu vault como una base de datos. Templater genera notas desde plantillas con lógica de JavaScript. La integración con Git sincroniza tu vault con un repositorio. Linter aplica consistencia de formato. El plugin principal Bases (introducido en v1.9.10) agrega vistas similares a bases de datos — tablas, galerías, calendarios y tableros kanban — sobre archivos del vault usando propiedades de frontmatter como campos, guardadas como archivos .base.15 Estos plugins agregan estructura al vault sin cambiar el formato subyacente de texto plano. El sistema de recuperación indexa la salida de estos plugins, no los plugins en sí.

Más de 5 millones de usuarios. Obsidian tiene una comunidad grande y activa que produce plantillas, flujos de trabajo, plugins y documentación. Cuando encuentras un problema con la organización del vault o la configuración de plugins, es probable que alguien ya haya documentado una solución. La comunidad también produce herramientas adyacentes a Obsidian: servidores MCP, scripts de indexación, pipelines de publicación y envolturas de API.

Qué no te da un sistema de archivos por sí solo

Un directorio de archivos markdown tiene la ventaja del texto plano, pero carece de tres cosas que Obsidian agrega:

  1. Enlaces bidireccionales. Obsidian rastrea backlinks automáticamente. Cuando enlazas de la Nota A a la Nota B, la Nota B muestra que la Nota A la referencia. El panel de grafo visualiza grupos de conexiones. Esta conciencia bidireccional es metadata que un sistema de archivos sin procesar no proporciona.

  2. Vista previa en vivo con renderizado de plugins. Las consultas de Dataview, los diagramas Mermaid y los bloques callout se renderizan en tiempo real. La experiencia de escritura es más rica que en un editor de texto, mientras que el formato de almacenamiento sigue siendo texto plano. Escribes y organizas en un entorno enriquecido; el sistema de recuperación indexa el markdown sin procesar.

  3. Infraestructura de comunidad. Descubrimiento de plugins, marketplace de temas, servicio de sincronización (opcional), servicio de publicación (opcional) y un ecosistema de documentación. Puedes replicar cualquier función individual con herramientas independientes, pero Obsidian las empaqueta en un flujo de trabajo coherente.

Qué NO hace Obsidian (y qué construyes tú)

Obsidian no incluye infraestructura de recuperación. Tiene búsqueda básica (texto completo, nombre de archivo, etiqueta), pero no tiene pipeline de embeddings, búsqueda vectorial, ranking por fusión, servidor MCP, filtrado de credenciales, estrategia de chunking ni hooks de integración para herramientas externas de AI. Esta guía cubre la infraestructura que construyes sobre Obsidian. El vault es el sustrato. El pipeline de recuperación, el servidor MCP y los hooks de integración son la infraestructura.

La arquitectura descrita aquí es markdown-first, no exclusiva de Obsidian. Si usas Logseq, Foam, Dendron o un directorio plano de archivos markdown, el pipeline de recuperación funciona de forma idéntica. El chunker lee archivos .md. El embedder procesa cadenas de texto. El indexer escribe en SQLite. Ninguno de estos componentes depende de funciones específicas de Obsidian. La contribución de Obsidian es el entorno de escritura y organización que produce los archivos markdown que indexa el recuperador.


Configuración de Obsidian MCP

Model Context Protocol (MCP) es la interfaz estándar que da a Claude Code, Codex CLI, Cursor y otras herramientas de AI acceso directo a un vault de Obsidian. Esta sección conecta un vault a una herramienta de AI en cinco minutos. Instalarás Obsidian, crearás un vault, instalarás un servidor MCP y ejecutarás tu primera consulta. El inicio rápido usa un servidor MCP de la comunidad para obtener resultados inmediatos. Las secciones posteriores cubren cómo crear un pipeline de recuperación personalizado para uso en producción.

Requisitos previos

  • macOS, Linux o Windows
  • Node.js 18+ (para el servidor MCP)
  • Obsidian 1.12+ (para la integración con CLI; 1.13.1 es la versión pública actual de escritorio; las versiones anteriores funcionan para configuraciones solo con MCP)
  • Claude Code, Codex CLI o Cursor instalado

Paso 1: Crea un vault

Descarga Obsidian desde obsidian.md y crea un vault nuevo. Elige una ubicación que recuerdes: el servidor MCP necesita la ruta absoluta.

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

Agrega algunas notas para que el sistema de recuperación tenga material con el cual trabajar. Incluso 10-20 notas bastan para ver resultados. Cada nota debe ser un archivo .md con un título significativo y al menos un párrafo de contenido.

Paso 2: Instala un servidor MCP

Varios servidores MCP de la comunidad ofrecen acceso inmediato al vault. El ecosistema creció de forma significativa durante 2025-2026. Uno destacado es MCPVault (npm @bitbonsai/mcpvault, repo bitbonsai/mcpvault), ahora en v0.12.1: un proyecto separado de MarkusPfundstein/mcp-obsidian, que aparece abajo, no un cambio de nombre. Su v0.11.0 (marzo de 2026) agregó list_all_tags para escanear frontmatter y hashtags con recuentos, mejoró el manejo de carpetas con puntos y añadió soporte para .base/.canvas. Se divulgaron dos avisos de severidad media (GHSA-9c83-rr99-vfwj y GHSA-j99q-93c9-h869) sobre su lista de denegación de directorios restringidos por filtro de rutas, así que ejecuta una versión actual.13

Cambio de abril de 2026: Obsidian CLI como puente preferido: Obsidian 1.12.0 introdujo CLI como función de primera clase, y el instalador público 1.12.7 (23 de marzo de 2026) incluyó el binario independiente, TUI y mejoras de archivo de socket que facilitaron la instalación y ejecución de flujos de trabajo en terminal.16 La versión pública actual de escritorio, 1.13.1 (canal público, 9 de junio de 2026), es una actualización de vigencia sobre 1.13.0: refinamientos de UX en configuración y una actualización de CodeMirror, sin nuevas capacidades de AI/automatización más allá de la superficie CLI de 1.12.x.2526 Las herramientas de la comunidad están migrando activamente del plugin Local REST API (que impulsaba mcp-obsidian) a la integración basada en CLI porque es más rápida y estable. El repo MarkusPfundstein/mcp-obsidian sigue mantenido: commits hasta mayo de 2026 agregaron herramientas como search_by_tag y get_frontmatter, aunque no publica releases etiquetados (instala desde un commit fijado). Sigue basado en Local-REST-API; para configuraciones nuevas, el puente CLI suele ser más rápido y estable, así que prefiérelo o usa las alternativas más recientes de la comunidad que se listan abajo.20 Consulta la sección “Obsidian CLI for AI Workflows” más adelante en esta guía para ver la configuración recomendada.

Servidor Autor Transporte Requiere plugin Función clave
obsidian-mcp-server StevenStavrakis STDIO No Ligero, basado en archivos
mcp-obsidian MarkusPfundstein STDIO Local REST API CRUD completo del vault vía REST, más search_by_tag/get_frontmatter: mantenido activamente (commits hasta mayo de 2026); sin releases etiquetados, fija un commit20
obsidian-mcp-tools jacksteamdev STDIO Sí (plugin) Búsqueda semántica + Templater
obsidian-claude-code-mcp iansinnott WebSocket Sí (plugin) Descubrimiento automático para Claude Code
obsidian-mcp-server cyanheads STDIO Local REST API Gestión de tags y frontmatter
Hybrid Search MCP comunidad STDIO No Servidor MCP con BM25 + búsqueda semántica + CLI. Nuevo y mantenido activamente desde abril de 2026.

Para el inicio rápido, la opción más simple es un servidor basado en archivos que lee archivos .md directamente:

npm install -g obsidian-mcp-server

Paso 3: Configura tu herramienta de AI

Claude Code: agrega esto a ~/.claude/settings.json:

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

Codex CLI: agrega esto a .codex/config.toml:

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

Cursor: agrega esto a .cursor/mcp.json:

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

Paso 4: Ejecuta tu primera consulta

Abre tu herramienta de AI y haz una pregunta que tus notas del vault puedan responder:

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

La herramienta de AI llama al servidor MCP, que busca en tu vault y devuelve contenido coincidente. Deberías ver resultados con rutas de archivo y extractos relevantes.

Qué puede hacer Claude una vez conectado

Los nombres exactos de las herramientas varían según el servidor, pero la superficie de capacidades central es consistente entre implementaciones:

Capacidad Herramienta típica Qué hace el agente con ella
Buscar en el vault obsidian_search / search Encuentra notas que coinciden con una consulta y devuelve extractos ordenados con rutas de archivo y atribución de fuente
Leer una nota completa obsidian_read_note / read_note Extrae el contenido completo de una nota cuando un extracto de búsqueda no es suficiente
Listar y explorar obsidian_list_notes / list_notes Explora notas por carpeta, tag o rango de fechas cuando no hay una consulta específica
Obtener contexto formateado obsidian_get_context Devuelve un bloque de contexto con forma de tema y ajustado a un presupuesto de tokens, listo para inyectarse en la conversación

En la práctica: Claude responde preguntas a partir de tus notas con atribución de fuente, incorpora decisiones previas y material de referencia en sesiones de programación, y explora la estructura del vault sin cargar archivos completos en el contexto. Algunos servidores de la comunidad también exponen operaciones de escritura (crear, anexar, gestionar tags y frontmatter); el servidor personalizado que se construye más adelante en esta guía es deliberadamente de solo lectura, con creación de notas manejada por hooks.

Profundizaciones: Arquitectura del servidor MCP para diseño de herramientas y permisos, Integración con Claude Code para hooks y el patrón de puente, Integración con Codex CLI y Cursor y otras herramientas para otros agentes.

Lo que acabas de crear

Conectaste una base de conocimiento local a una herramienta de AI mediante un protocolo estándar. El servidor MCP lee los archivos de tu vault, realiza una búsqueda básica y devuelve resultados. Esta es la versión mínima viable.

Lo que este inicio rápido NO te da: - Recuperación hybrid (BM25 + búsqueda vectorial + fusión RRF) - Búsqueda semántica basada en embeddings - Filtrado de credenciales - Indexación incremental - Inyección automática de contexto basada en hooks

El resto de esta guía cubre cómo crear cada una de estas capacidades. El inicio rápido demuestra el concepto. El pipeline completo ofrece recuperación de calidad de producción.


CLI de Obsidian para flujos de trabajo con AI

Obsidian 1.12 (febrero de 2026) introdujo una interfaz de línea de comandos integrada que abre una nueva superficie de integración para flujos de trabajo con AI; sigue vigente en la versión pública de escritorio 1.13.1 (canal público, 9 de junio de 2026), una actualización de UX de configuración y de versión de CodeMirror sin nuevas capacidades de CLI.162526 El CLI funciona como un control remoto para la GUI de Obsidian: Obsidian debe estar ejecutándose (o se iniciará automáticamente con el primer comando). Actívalo en Settings > General > Command line interface.

Por qué el CLI importa para la infraestructura de AI

El CLI ofrece acceso programático a operaciones nativas de Obsidian que antes requerían la GUI o los APIs de plugins. Para flujos de trabajo con AI, las capacidades clave son:

  • Búsqueda desde scripts y hooks. obsidian search "query" y obsidian search:context "query" ejecutan búsquedas en el vault desde cualquier shell script, hook o pipeline de automatización. La variante search:context devuelve las líneas coincidentes con contexto alrededor, útil para alimentar resultados en prompts de AI.
  • Automatización de notas diarias. obsidian daily abre o crea la nota diaria de hoy. Combinado con shell scripting, permite flujos de briefings diarios automatizados: un hook puede anexar resúmenes generados por AI a la nota diaria.
  • Creación de notas basada en plantillas. obsidian template list y obsidian template create generan notas desde Templater o plantillas principales, lo que permite que los agentes de AI creen entradas estructuradas en el vault sin escribir directamente archivos markdown.
  • Gestión de propiedades. obsidian property set y obsidian property get leen y escriben propiedades de frontmatter, lo que permite actualizar metadatos desde scripts sin parsear YAML.
  • Control de plugins. obsidian plugin enable/disable/list administra plugins de forma programática, útil para activar o desactivar plugins de indexación durante operaciones por lotes.
  • Gestión de tareas. obsidian task list/add/complete ofrece acceso estructurado a tareas, útil para agentes de AI que gestionan elementos de trabajo en el vault.

CLI vs MCP para acceso de AI

El CLI y los servidores MCP cumplen roles distintos y son complementarios, no competidores:

Aspecto CLI de Obsidian Servidor MCP
Invocador Shell scripts, hooks, cron jobs Agentes de AI (Claude Code, Codex, Cursor)
Protocolo Proceso POSIX (stdin/stdout/stderr) MCP (JSON-RPC sobre STDIO o HTTP)
Fortaleza Operaciones nativas de Obsidian (plantillas, plugins, propiedades) Recuperación personalizada (embeddings, BM25, fusión RRF)
Limitación Sin búsqueda vectorial, sin pipeline de embedding Sin acceso a operaciones internas de Obsidian
Mejor para Scripts de automatización, pipelines de ingesta, acciones de hooks Consultas de agentes de AI en tiempo real durante sesiones

Recomendación: Usa el CLI para automatización de ingesta (crear notas, gestionar propiedades, ejecutar búsquedas nativas de Obsidian) y MCP para recuperación (búsqueda hybrid con embeddings). Un hook PreToolUse puede llamar a obsidian search:context como una verificación previa rápida antes de recurrir al recuperador completo de MCP para obtener resultados ordenados.

Ejemplo: hook de ingesta impulsado por 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}"

Plugins de agentes para Obsidian

Una categoría creciente de plugins de Obsidian integra agentes de programación con AI directamente en la UI del vault, lo que ofrece una alternativa a configurar un servidor MCP externo. Estos plugins ejecutan el agente de AI dentro de la barra lateral de Obsidian en lugar de conectarse desde una herramienta externa.

Claudian

Claudian integra Claude Code como colaborador de AI en el vault. El directorio del vault se convierte en el directorio de trabajo de Claude, lo que le da capacidades agénticas completas: lectura/escritura de archivos, búsqueda, comandos bash y flujos de trabajo de varios pasos.17

Funciones clave para infraestructura de AI: - Prompts conscientes del contexto. Adjunta automáticamente la nota enfocada, admite menciones de archivos con @notename, exclusión basada en etiquetas y selección del editor como contexto. - Soporte de visión. Analiza imágenes mediante arrastrar y soltar, pegar o ruta de archivo; útil para procesar capturas de pantalla y diagramas capturados en el vault. - Slash commands. Crea plantillas reutilizables de prompts activadas por /command, lo que permite operaciones estandarizadas en el vault. - Modos de permisos. Modos YOLO (aprobación automática), Safe (aprobar cada acción) y Plan (solo plan) con una blocklist de seguridad y confinamiento al vault.

Agent Client

Agent Client lleva Claude Code, CLI de Codex y CLI de Gemini a una barra lateral unificada de Obsidian mediante Agent Client Protocol (ACP).18

Funciones clave: - Cambio entre múltiples agentes. Chatea con Claude Code, Codex o CLI de Gemini desde el mismo panel, cambiando entre agentes según sea necesario. - Menciones de notas. Usa @notename para incluir el contenido de notas en los prompts, similar a Claudian pero independiente del agente. - Ejecución de shell. Ejecuta comandos de terminal en línea dentro del chat: scripts de build, comandos git o cualquier operación de terminal sin salir de la conversación. - Aprobación de acciones. Control granular sobre lecturas de archivos, ediciones y ejecuciones de comandos.

Cuándo usar plugins de agentes vs MCP externo

Escenario Plugin de agente MCP externo
Escribir y editar notas del vault con asistencia de AI Mejor: el agente ve el contexto del editor Funciona, pero sin conciencia del editor
Desarrollo de código en múltiples repos Limitado: restringido al vault Mejor: acotado al proyecto con filesystem completo
Recuperación desde un corpus grande indexado Solo búsqueda básica Pipeline completo de recuperación hybrid
Preguntas y respuestas rápidas sobre el vault durante sesiones de toma de notas Ideal: sin cambio de contexto Requiere cambiar a la terminal

Recomendación: Usa plugins de agentes para flujos centrados en el vault (escribir, organizar y resumir notas). Usa servidores MCP externos para flujos de desarrollo donde el agente de AI necesita el pipeline completo de recuperación y acceso a codebases fuera del vault. Ambos enfoques pueden coexistir: ejecuta Claudian dentro de Obsidian para trabajo con notas y Claude Code con MCP externamente para desarrollo.


Marco de decisión: Obsidian vs alternativas

No todos los casos de uso necesitan Obsidian. Esta sección muestra cuándo Obsidian es la base adecuada, cuándo es excesivo y cuándo otra opción encaja mejor.

Árbol de decisión

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.

Matriz comparativa

Criterio Obsidian Notion Apple Notes Sistema de archivos plano CLAUDE.md
Local-first No (nube) Parcial (iCloud)
Texto plano Sí (markdown) No (bloques) No (propietario)
Estructura de grafo Sí (wiki-links) Parcial (menciones) No No No
Indexable por AI Acceso directo a archivos API requerido Requiere exportación Acceso directo a archivos Ya está en contexto
Ecosistema de plugins Más de 2.500 plugins Integraciones Ninguno N/A N/A
Funciona sin conexión Completo Caché de solo lectura Parcial Completo Completo
Escala a más de 10K notas Sí (con API) Se degrada No (archivo único)
Costo Gratis (núcleo) USD 10/mes+ Gratis Gratis Gratis

Cuándo Obsidian es excesivo

  • Contexto de un solo proyecto. Si la AI solo necesita contexto sobre el codebase actual, colócalo en CLAUDE.md, AGENTS.md o documentación a nivel de proyecto. Estos archivos viajan con el repo y se cargan automáticamente.
  • Datos estructurados. Si el contenido son tablas, registros o esquemas, usa una base de datos. Las notas de Obsidian priorizan la prosa. Dataview puede consultar campos de frontmatter, pero una base de datos real maneja mejor las consultas estructuradas.
  • Investigación temporal. Si las notas se descartarán cuando termine el proyecto, una carpeta temporal con archivos markdown es más simple. No construyas infraestructura de recuperación para contenido efímero.

Cuándo Obsidian es la opción correcta

  • Conocimiento acumulado durante meses o años. El valor se compone a medida que crece el corpus. Una bóveda de 200 notas consultada a diario durante seis meses aporta más valor que una bóveda de 5.000 notas consultada una sola vez.
  • Múltiples dominios en un solo corpus. Una bóveda con notas sobre programación, arquitectura, seguridad, diseño y proyectos personales se beneficia de la recuperación entre dominios, algo que un CLAUDE.md específico de proyecto no puede ofrecer.
  • Contenido sensible en términos de privacidad. Local-first significa que el pipeline de recuperación nunca envía contenido a servicios externos. La bóveda contiene lo que pongas en ella, incluido contenido que no subirías a un servicio en la nube.

Modelo mental: tres capas

El sistema tiene tres capas que operan de forma independiente, pero que se potencian cuando se combinan. Cada capa tiene una responsabilidad distinta y un modo de falla diferente.

┌─────────────────────────────────────────────────────┐
                 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 determina qué entra en la bóveda. Sin curación, la bóveda acumula ruido: capturas de pantalla de tweets, artículos copiados y pegados sin anotaciones, ideas a medio terminar sin contexto. La capa de intake es responsable del control de calidad en el punto de entrada. Un pipeline de puntuación, una convención de etiquetas o un proceso de revisión manual: cualquier mecanismo que asegure que la bóveda contenga contenido que valga la pena recuperar.

Retrieval vuelve consultable la bóveda. Este es el motor: dividir notas en unidades de búsqueda mediante chunking, convertir chunks en embeddings dentro de un espacio vectorial, indexar para búsqueda por palabras clave y búsqueda semántica, y fusionar resultados con RRF. La capa de retrieval transforma una carpeta de archivos en una base de conocimiento consultable. Sin esta capa, se puede navegar la bóveda mediante exploración manual y búsqueda básica, pero no queda accesible programáticamente para herramientas de AI.

Integration conecta la capa de retrieval con herramientas de AI. Un servidor MCP expone la recuperación como una herramienta invocable. Los hooks inyectan contexto automáticamente. Las skills capturan nuevo conocimiento de vuelta en la bóveda. La capa de integration es la interfaz entre la base de conocimiento y los agentes de AI que la consumen.

Las capas están desacopladas por diseño. El pipeline de puntuación de intake no sabe nada sobre embeddings. El retriever no sabe nada sobre reglas de enrutamiento de señales. El servidor MCP no sabe nada sobre cómo se crearon las notas. Este desacoplamiento significa que puedes mejorar cualquier capa de forma independiente. Reemplaza el modelo de embeddings sin cambiar el pipeline de intake. Agrega una nueva capacidad de MCP sin modificar el retriever. Cambia las heurísticas de puntuación de señales sin tocar el índice.


Arquitectura de la bóveda para consumo de IA

Una bóveda optimizada para la recuperación con IA sigue convenciones distintas a las de una bóveda optimizada para navegación personal. Esta sección cubre la estructura de carpetas, el esquema de notas, las convenciones de frontmatter y los patrones específicos que mejoran la calidad de recuperación.

Estructura de carpetas

Usa prefijos numerados para las carpetas de nivel superior y así crear una jerarquía organizativa predecible. Los números no implican prioridad: agrupan dominios relacionados y hacen que la estructura sea fácil de escanear.

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

Carpetas que deben indexarse: Todo lo que contenga prosa en markdown: proyectos, áreas, recursos, señales, notas diarias.

Carpetas que deben excluirse de la indexación: Templates (contienen variables de marcador de posición, no contenido), archivos adjuntos (archivos binarios), configuración de Obsidian y cualquier carpeta con contenido sensible que no quieras incluir en el índice de recuperación.

El archivo .indexignore

Crea un archivo .indexignore en la raíz de la bóveda para excluir rutas explícitamente del índice de recuperación. La sintaxis coincide con .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/

El indexador lee este archivo antes de escanear y omite por completo las rutas que coincidan. Los archivos en rutas excluidas nunca se dividen en chunks, nunca se convierten en embeddings y nunca aparecen en los resultados de búsqueda.

Esquema de notas

Cada nota debe tener frontmatter YAML. El sistema de recuperación usa los campos de frontmatter para filtrar y enriquecer el contexto:

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

Campos obligatorios para la recuperación:

  • title — Se usa en la visualización de resultados de búsqueda y como contexto de encabezado para BM25
  • type — Permite consultas filtradas por tipo (“muéstrame solo MOCs” o “solo señales”)
  • tags — Se indexan en el contexto de encabezado de FTS5 con un peso de 0,3, lo que aporta coincidencias de palabras clave incluso cuando el cuerpo usa otra terminología

Campos opcionales pero valiosos:

  • domain — Permite consultas acotadas por dominio (“busca solo notas de seguridad”)
  • source — Atribución para contenido capturado; el sistema de recuperación puede incluir URL de origen en los resultados
  • status — Permite excluir notas archivadas o en borrador de la búsqueda activa

Convenciones de chunking

El sistema de recuperación divide en chunks en los límites de encabezados H2 (##). Esto significa que la estructura de tus notas afecta directamente la granularidad de la recuperación:

Bueno para la recuperación:

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

Tres secciones H2 producen tres chunks que pueden buscarse de forma independiente. Cada chunk tiene suficiente contexto para que el embedding capture su significado. Una consulta sobre “manejo de tokens vencidos” coincide específicamente con el tercer chunk.

Deficiente para la recuperación:

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

Una sección larga sin encabezados H2 produce un único chunk grande. El embedding promedia todos los temas de la sección. Una consulta sobre cualquier subtema coincide por igual con toda la nota.

Regla práctica: Si una sección cubre más de un concepto, divídela en subsecciones H2. El chunker se encarga del resto.

Qué no poner en las notas

Contenido que degrada la calidad de recuperación:

  • Copias y pegados sin procesar de artículos completos y sin anotación. El sistema de recuperación indexa las palabras clave del artículo original, lo que diluye tu bóveda con contenido que no escribiste. En su lugar, agrega un resumen, extrae los puntos clave o enlaza la URL de origen.
  • Capturas de pantalla sin descripción textual. El sistema de recuperación indexa texto markdown. Una imagen sin texto alternativo ni descripción alrededor es invisible tanto para BM25 como para la búsqueda vectorial.
  • Cadenas de credenciales. Claves API, tokens, contraseñas, cadenas de conexión. Incluso con filtrado de credenciales, el enfoque más seguro es nunca pegar secretos en las notas. En su lugar, haz referencia a ellos por nombre (“el token API de Cloudflare en ~/.env”).
  • Contenido generado automáticamente sin curación. Si una herramienta genera una nota (transcripción de reunión, destacados de Readwise, importación RSS), revísala y anótala antes de que entre en la bóveda permanente. Las importaciones automáticas sin curación agregan volumen sin sumar valor recuperable.

Ecosistema de plugins para flujos de trabajo de AI

Los plugins de Obsidian que mejoran la calidad de la bóveda para la recuperación con AI se dividen en tres categorías: estructurales (imponen consistencia), de consulta (exponen metadata) y de sincronización (mantienen la bóveda actualizada).

Plugins esenciales

Dataview. Consulta tu bóveda como una base de datos usando campos de frontmatter. Crea índices dinámicos: “todas las notas etiquetadas con security actualizadas en los últimos 30 días” o “todas las notas de proyecto con estado active.” Dataview no ayuda directamente a la recuperación, pero te ayuda a identificar vacíos en la cobertura de tu bóveda y a encontrar notas que necesitan actualización.

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

Templater. Crea notas a partir de plantillas con campos dinámicos. Asegúrate de que cada nota nueva comience con el frontmatter correcto usando una plantilla que complete previamente los campos created, type y domain. Un frontmatter consistente mejora el filtrado de recuperación.

<%* /* New Resource Note Template */ %>
---
title: "<% tp.file.cursor() %>"
type: note
domain: <% tp.system.suggester(["programming", "security", "ai-engineering", "design", "devops"], ["programming", "security", "ai-engineering", "design", "devops"]) %>
tags: []
created: <% tp.date.now("YYYY-MM-DD") %>
updated: <% tp.date.now("YYYY-MM-DD") %>
source: ""
status: active
---

## Key Points

## Details

## References

Linter. Impone reglas de formato en toda la bóveda. Una jerarquía de encabezados consistente (H1 para el título, H2 para secciones, H3 para subsecciones) garantiza que el chunker produzca resultados predecibles. Reglas de Linter que importan para la recuperación:

  • Incremento de encabezados: exige niveles de encabezado secuenciales (sin saltar de H1 a H3)
  • Título YAML: debe coincidir con el nombre del archivo
  • Espacios finales: eliminar (evita artefactos de tokenización de FTS5)
  • Líneas en blanco consecutivas: limitar a 1 (chunks más limpios)

Integración con Git. Control de versiones para tu bóveda. Registra cambios a lo largo del tiempo, sincroniza entre máquinas y permite recuperarte de eliminaciones accidentales. Git también proporciona datos de mtime que el indexador usa para la detección incremental de cambios.

Plugins que ayudan a la indexación

Smart Connections. Un plugin de Obsidian que ofrece búsqueda semántica impulsada por AI dentro de Obsidian. Smart Connections v4 crea embeddings locales de forma predeterminada: una vez que tu bóveda está indexada, las conexiones semánticas y la búsqueda funcionan completamente sin conexión, sin llamadas a API.11 v4.5.0 (5 de mayo de 2026) incorpora las conexiones del pie de página a Smart Connections Core, por lo que cualquier instalación puede mostrar conexiones con notas relacionadas en el pie de página sin abrir un panel lateral. Las versiones recientes de v4 también agregaron vistas de grafo para listas de conexiones, ubicaciones de dock configurables, mejor recuperación de block-embedding después de ejecuciones de indexación interrumpidas y “Substrate”, un entorno entre plugins que permite que Smart Connections, Smart Chat y Smart Composer compartan estado.21 Aunque el sistema de recuperación de esta guía es externo a Obsidian (se ejecuta como una pipeline Python), Smart Connections es útil para explorar relaciones semánticas mientras escribes. Los dos sistemas indexan el mismo contenido, pero sirven casos de uso distintos: Smart Connections para descubrimiento dentro del editor, el recuperador externo para integración con herramientas de AI mediante MCP.

Plugins AI-native lanzados en abril de 2026. Una ola de nuevos plugins comunitarios apunta directamente al flujo de trabajo de Claude Code / Codex / Gemini-CLI:

Plugin Lanzamiento Qué hace
Cortex 4 de abril Agente de bóveda impulsado por Claude Code: trata la bóveda como un espacio de trabajo de agente, no solo como un almacén de notas
VaultSearch 7 de abril Búsqueda hybrid local-first: BM25 + semántica + fuzzy (se superpone directamente con la pila de recuperación de esta guía)
LLM Wiki 9 de abril Convierte tu bóveda en una base de conocimiento consultable de forma privada
Drift 11 de abril Visor de diff al estilo VS Code para edición de Obsidian impulsada por AI; orientado a flujos de trabajo con Claude Code
EngramQuest 11 de abril Genera desafíos de memoria a partir de notas; incluye “AI Skills” para Claude Code / Gemini CLI / Cursor
Hybrid Search MCP Marzo (aún reciente) Servidor MCP + CLI con BM25 + búsqueda semántica, creado específicamente para asistentes de AI

Trata esto como una superficie emergente: es probable que varios de estos plugins se consoliden o sean absorbidos por Smart Connections / el núcleo de Obsidian en los próximos trimestres. Si hoy tienes que elegir uno, VaultSearch y Hybrid Search MCP son los más cercanos en filosofía al recuperador externo de esta guía.

Nota sobre Dataview: Dataview (el plugin de consultas de Obsidian de larga trayectoria) lanzó por última vez la versión 0.5.70 en abril de 2025 y, desde entonces, ha estado prácticamente inactivo. Para trabajos nuevos, la función integrada Bases de Obsidian (1.9+) es el sucesor implícito y la ruta recomendada.

Metadata Menu. Ofrece edición estructurada de frontmatter con autocompletado para valores de campos. Reduce errores tipográficos en los campos type, domain y tags. La metadata consistente mejora la precisión del filtrado de recuperación.

Plugins que perjudican la indexación

Excalidraw. Almacena dibujos como JSON incrustado en archivos markdown. El JSON es markdown sintácticamente válido, pero produce basura cuando se divide en chunks y se convierte en embeddings. Excluye los archivos de Excalidraw del índice mediante .indexignore o filtra por extensión de archivo.

Kanban. Almacena el estado del tablero como markdown con formato especial. El formato está diseñado para renderizar Kanban, no para recuperación de prosa. El chunker produce fragmentos de títulos de tarjetas y metadata que no se convierten bien en embeddings. Excluye los tableros Kanban del índice.

Calendar. Crea notas diarias con contenido mínimo (a menudo solo un encabezado de fecha). Las notas vacías o casi vacías producen chunks de baja calidad. Si usas notas diarias, escribe contenido sustantivo en ellas o excluye la carpeta de notas diarias del índice.

Configuración de plugins que importa

Recuperación de archivos → Activada. Protege contra la eliminación accidental de notas. No está directamente relacionada con la recuperación, pero es crítica para una base de conocimiento de la que dependes.

Saltos de línea estrictos → Desactivados. Los saltos de línea estándar de Markdown (doble salto de línea para párrafo) producen chunks más limpios que el modo estricto de Obsidian (un solo salto de línea para <br>).

Ubicación predeterminada de archivos nuevos → Carpeta designada. Envía los archivos nuevos a 00-inbox/ para que las notas sin categorizar no contaminen las carpetas de dominio. La bandeja de entrada es un área de preparación; los archivos se mueven a carpetas de dominio después de la revisión.

Formato de wiki-link → Ruta más corta cuando sea posible. Los destinos de enlace más cortos son más fáciles de resolver para el recuperador al indexar la estructura de enlaces.


Modelos de embeddings: elección y configuración

El modelo de embeddings convierte fragmentos de texto en vectores numéricos para la búsqueda semántica. La elección del modelo determina la calidad de recuperación, el tamaño del índice, la velocidad de generación de embeddings y las dependencias en tiempo de ejecución. En esta sección se explica por qué Model2Vec potion-base-8M es la opción predeterminada y cuándo conviene elegir alternativas.

Por qué Model2Vec potion-base-8M

Modelo: minishlab/potion-base-8M Parámetros: 7,6 millones Dimensiones: 256 Tamaño: ~30 MB Dependencias: model2vec (solo numpy, sin PyTorch) Inferencia: solo CPU, embeddings estáticos de palabras (sin capas de atención)

Model2Vec destila el conocimiento de un sentence transformer en embeddings estáticos de tokens. En lugar de ejecutar capas de atención sobre la entrada (como hacen BERT, MiniLM y otros modelos transformer), Model2Vec produce vectores mediante un promedio ponderado de embeddings de tokens precalculados.5 La consecuencia práctica: la velocidad de generación de embeddings es entre 50 y 500 veces mayor que la de los modelos basados en transformers porque no hay cómputo secuencial.

En la página actual de resultados de Model2Vec, potion-base-8M alcanza alrededor del 92% de la puntuación global de all-MiniLM-L6-v2 en todas las tareas (51,32 frente a 55,80), mientras se mantiene varios órdenes de magnitud más rápido.6 La brecha de calidad restante es el costo de las ventajas en velocidad y simplicidad. Para fragmentos cortos de markdown (un promedio de 200 a 400 palabras en un vault típico), la diferencia de calidad es menos marcada que en documentos más largos, porque ambos modelos convergen en representaciones similares para texto breve y enfocado.

Configuración

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

Carga diferida. El modelo se carga en el primer uso, no al momento de importar. Importar el módulo embedder no tiene costo cuando el retriever opera en modo de respaldo solo con BM25 (por ejemplo, cuando el venv de embeddings no está instalado).

Entorno virtual aislado. El modelo se ejecuta en un venv dedicado (por ejemplo, ~/.claude/venvs/memory/) para evitar conflictos de dependencias con el resto de la cadena de herramientas. La función _activate_venv() agrega el site-packages del venv a sys.path en tiempo de ejecución.

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

Procesamiento por lotes. El embedder procesa textos en lotes de 64 para amortizar la sobrecarga de Model2Vec. El indexer envía fragmentos a embed_batch() en lugar de generar el embedding de un fragmento a la vez.

Cuándo elegir alternativas

Modelo Dim Tamaño Velocidad Calidad (MTEB) Ideal para
potion-base-8M 256 30 MB 500x 51,32 Predeterminado: local, rápido, sin GPU
potion-base-32M 256 120 MB 400x 52,83 Mayor calidad, todavía estático
potion-retrieval-32M 256 120 MB 400x 35,06 (retrieval) Estático optimizado para retrieval
potion-multilingual-128M 256 ~500 MB 300x Vaults multilingües (101 idiomas)
all-MiniLM-L6-v2 384 80 MB 1x 55,80 Mayor calidad, todavía local
nomic-embed-text-v1.5 768 270 MB 0,5x 62,28 Mejor calidad local
text-embedding-3-small 1536 API N/A 62,30 Basado en API, máxima calidad

Elige potion-base-32M cuando quieras mejor calidad que potion-base-8M sin salir de la familia de embeddings estáticos. Usa un vocabulario más grande destilado de baai/bge-base-en-v1.5, con una puntuación global de 52,83 en todas las tareas (alrededor de un 3% más que potion-base-8M), mientras conserva la misma salida de 256 dimensiones y la dependencia solo de numpy.8 El archivo del modelo, 4 veces más grande, aumenta el uso de memoria, pero la velocidad de generación de embeddings sigue siendo varios órdenes de magnitud superior a la de los modelos transformer.

Elige potion-retrieval-32M cuando tu caso de uso principal sea retrieval (como lo es la búsqueda en el vault). Esta variante está ajustada a partir de potion-base-32M específicamente para tareas de retrieval, con una puntuación de 35,06 en la tabla de benchmarks de retrieval de Model2Vec, frente a 32,67 para potion-base-32M.8 La concesión es que está optimizada para retrieval en lugar de calidad de embeddings de propósito general.

Elige potion-multilingual-128M cuando tu vault contenga notas en varios idiomas. Lanzado en mayo de 2025, este modelo de 101 idiomas es el modelo de embeddings estáticos con mejor rendimiento para tareas multilingües; genera embeddings para cualquier texto en cualquier idioma y mantiene la misma dependencia solo de numpy que los demás modelos potion.12 El archivo de modelo más grande (~500 MB) es el costo de la capacidad interlingüística. Úsalo cuando tengas notas en japonés, chino, alemán u otros idiomas distintos del inglés junto con contenido en inglés.

Elige all-MiniLM-L6-v2 cuando la calidad de retrieval importe más que la velocidad y tengas PyTorch instalado. Los vectores de 384 dimensiones aumentan el tamaño de la base de datos SQLite en ~50% frente a los vectores de 256 dimensiones. La velocidad de generación de embeddings pasa de <1 minuto a ~10 minutos para una reindexación completa de 15.000 archivos en hardware M-series.

Elige nomic-embed-text-v1.5 cuando necesites la mejor calidad local posible de retrieval y aceptes una indexación más lenta. Los vectores de 768 dimensiones aproximadamente triplican el tamaño de la base de datos. Requiere PyTorch y una CPU moderna o GPU.

Elige text-embedding-3-small cuando la latencia de red y la privacidad sean concesiones aceptables. API produce embeddings de la mayor calidad, pero introduce una dependencia de la nube, costo por token ($0,02/millón de tokens) y envía tu contenido a los servidores de OpenAI.

Quédate con potion-base-8M en todos los demás casos. La ventaja de velocidad es crítica para la indexación iterativa (reindexar durante el desarrollo), la dependencia solo de numpy evita la complejidad de instalar PyTorch y los vectores de 256 dimensiones mantienen compacta la base de datos.

Cuantización y reducción de dimensionalidad

Model2Vec v0.5.0+ admite cargar modelos con precisión y dimensiones reducidas.8 Esto es útil para desplegar en hardware limitado o reducir el tamaño de la base de datos sin cambiar de modelo:

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)

Los modelos cuantizados conservan una calidad de retrieval casi idéntica con una fracción de la huella de memoria. La reducción de dimensionalidad sigue una truncación al estilo Matryoshka: las primeras N dimensiones contienen la mayor parte de la información. Reducir de 256 a 128 dimensiones reduce a la mitad el almacenamiento de vectores con una pérdida mínima de calidad para retrieval de textos cortos.

Model2Vec v0.8.x actualiza los componentes internos de tokenización/persistencia, depreca la compatibilidad con Python 3.9 y actualiza los resultados publicados a las tablas MTEB más recientes. Fija o prueba model2vec antes de actualizar un indexer de producción, porque las actualizaciones de la biblioteca pueden cambiar las rutas de carga de modelos incluso cuando el nombre del modelo de embeddings se mantiene igual.10

Ajuste fino para embeddings específicos del vault

Model2Vec v0.4.0+ admite entrenar modelos de clasificación personalizados sobre embeddings estáticos, v0.7.0 agrega cuantización de vocabulario y pooling configurable para destilación, y v0.8.x refactoriza el comportamiento de tokenización y persistencia.10 Esto es relevante para vaults con vocabulario especializado (notas médicas, referencias legales, jerga específica de un dominio) donde los modelos potion predeterminados quizá no capturen los matices semánticos:

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

Para la mayoría de los vaults, potion-base-8M predeterminado produce una calidad de retrieval suficiente. El ajuste fino solo vale la pena cuando el retrieval omite de forma constante conexiones específicas del dominio que un modelo de propósito general no puede capturar.

Seguimiento del hash del modelo

El indexer almacena un hash derivado del nombre del modelo y del tamaño del vocabulario. Si cambias el modelo de embeddings, el indexer detecta la discrepancia en la siguiente ejecución incremental y activa automáticamente una reindexación completa.

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]

Esto evita mezclar vectores de distintos modelos en la misma base de datos, lo que produciría puntuaciones de cosine similarity sin sentido.

Modos de falla

Falla en la descarga del modelo. La primera ejecución descarga el modelo desde Hugging Face. Si la descarga falla (problema de red, firewall corporativo), el retriever vuelve al modo solo con BM25. El modelo queda almacenado localmente en caché después de la primera descarga.

Incompatibilidad de dimensiones. Si cambias de modelo sin limpiar la base de datos, los vectores almacenados tienen una dimensión distinta de los nuevos embeddings. El indexer detecta esto mediante el hash del modelo y activa una reindexación completa. Si la verificación del hash falla (modelo personalizado sin hash adecuado), sqlite-vec generará un error en las consultas KNN con dimensiones incompatibles.

Presión de memoria en vaults grandes. Generar embeddings para más de 50.000 fragmentos en un solo lote puede consumir mucha memoria. El indexer procesa en lotes de 64 para limitar el pico de uso de memoria. Si la memoria sigue siendo un problema, reduce el tamaño del lote.


Búsqueda de texto completo con FTS5

La extensión FTS5 de SQLite proporciona búsqueda de texto completo con ranking BM25. FTS5 es el componente de búsqueda por palabras clave del pipeline de recuperación hybrid. Esta sección cubre la configuración de FTS5, cuándo destaca BM25 y sus modos de falla específicos.

Tabla virtual FTS5

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

Modo de sincronización de contenido. El parámetro content=chunks le indica a FTS5 que referencie directamente la tabla chunks en lugar de almacenar una copia duplicada del texto. Esto reduce a la mitad el requisito de almacenamiento, pero significa que FTS5 debe sincronizarse manualmente cuando se insertan, actualizan o eliminan chunks.

Columnas. Se indexan 3 columnas: - chunk_text — El contenido principal de cada chunk (peso BM25: 1.0) - section — El texto del encabezado H2 (peso BM25: 0.5) - heading_context — Título de la nota, etiquetas y metadatos (peso BM25: 0.3)

Ranking BM25

BM25 clasifica documentos por frecuencia de términos, frecuencia inversa de documentos y normalización de longitud del documento. La función auxiliar bm25() en FTS5 acepta pesos por columna:

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;

Los pesos de columna (1.0, 0.5, 0.3) significan: - Una coincidencia de palabra clave en chunk_text aporta más al puntaje - Una coincidencia en section (encabezado) aporta la mitad - Una coincidencia en heading_context (título, etiquetas) aporta el 30%

Estos pesos se pueden ajustar. Si tu vault tiene encabezados descriptivos que predicen con fuerza la calidad del contenido, aumenta el peso de section. Si tus etiquetas son completas y precisas, aumenta el peso de heading_context.

Cuándo gana BM25

BM25 destaca en consultas que contienen identificadores exactos:

  • Nombres de funciones: _rrf_fuse, embed_batch, get_stale_files
  • Flags de CLI: --incremental, --vault, --model
  • Claves de configuración: bm25_weight, max_tokens, batch_size
  • Mensajes de error: SQLITE_LOCKED, ConnectionRefusedError
  • Términos técnicos específicos: PostToolUse, PreToolUse, AGENTS.md

Para estas consultas, BM25 encuentra la coincidencia exacta de inmediato. La búsqueda vectorial devolvería contenido relacionado semánticamente, pero podría clasificar la coincidencia exacta por debajo de una discusión conceptual.

Cuándo falla BM25

BM25 falla en consultas que usan una terminología distinta de la del contenido almacenado:

  • Consulta: “cómo manejar fallas de autenticación” → El vault contiene notas sobre “recuperación de errores de inicio de sesión” y “manejo de expiración de sesiones”. BM25 no encuentra coincidencias porque las palabras clave son distintas.
  • Consulta: “cuál es la mejor forma de gestionar estado” → El vault contiene notas sobre “patrones de Redux store” y “context providers”. BM25 no acierta porque “gestión de estado” está expresado mediante nombres de tecnologías específicas.

BM25 también falla con colisión de palabras clave a escala. En un vault de 15.000 archivos, una búsqueda de “configuración” coincide con cientos de notas porque casi todas las notas de proyecto mencionan configuración. Los resultados son técnicamente correctos, pero prácticamente inútiles: el ranking no puede determinar qué nota de “configuración” es relevante para la consulta actual.

Tokenizer FTS5

FTS5 usa el tokenizer unicode61 de forma predeterminada, que maneja texto ASCII y Unicode. Para vaults con una cantidad significativa de contenido CJK (chino, japonés, coreano), considera el 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'
);

El tokenizer predeterminado unicode61 divide por límites de palabras, lo que funciona mal para idiomas sin espacios entre palabras. El tokenizer trigram divide cada 3 caracteres, lo que permite coincidencias de subcadenas a costa del tamaño del índice (aproximadamente 3 veces más grande).

Mantenimiento

FTS5 requiere sincronización explícita cuando cambia la tabla chunks subyacente:

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

El comando rebuild reconstruye el índice FTS5 a partir de la tabla de contenido. Ejecútalo después de inserciones masivas (reindexación completa), pero no después de actualizaciones incrementales individuales; para esas, usa INSERT INTO chunks_fts(rowid, chunk_text, section, heading_context) para sincronizar filas individuales.


Búsqueda vectorial con sqlite-vec

La extensión sqlite-vec incorpora la búsqueda vectorial KNN (K-Nearest Neighbors) en SQLite. Esta sección cubre la configuración de sqlite-vec, el pipeline de embeddings desde una nota hasta un vector buscable, y los patrones de consulta específicos.

Tabla virtual sqlite-vec

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

El módulo vec0 almacena vectores float de 256 dimensiones como datos binarios empaquetados. La columna id se mapea 1:1 con la tabla chunks, lo que permite hacer joins entre los resultados vectoriales y los metadatos de los fragmentos.

Pipeline de embeddings

El pipeline va desde la nota hasta el vector buscable:

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

Serialización vectorial

El módulo struct de Python serializa vectores float para almacenarlos en 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))

Consulta KNN

Una consulta de búsqueda vectorial genera el embedding de la consulta de entrada y luego encuentra los K fragmentos más cercanos por distancia coseno:

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

El operador MATCH en sqlite-vec realiza búsqueda aproximada de vecinos más cercanos. El parámetro k controla cuántos resultados se devuelven. La columna distance contiene la distancia coseno (0 = idéntico, 2 = opuesto).

Paginación KNN con restricciones de distancia

A partir de sqlite-vec v0.1.7, las consultas KNN admiten restricciones WHERE distance < ?, lo que permite paginación basada en cursor a través de conjuntos de resultados grandes sin volver a escanear las páginas anteriores.14 Las versiones estables posteriores v0.1.8 y v0.1.9 son releases de empaquetado y corrección de bugs de DELETE, no releases con un nuevo modelo de consulta, por lo que v0.1.7 sigue siendo el límite de función para este patrón de paginación.23

En el horizonte, la línea v0.1.10-alpha (31 de marzo al 18 de mayo de 2026) es la primera en llevar sqlite-vec más allá del KNN de fuerza bruta: introduce tipos de índice de vecino más cercano aproximado — rescore, un índice experimental ivf (inverted-file) que no está habilitado de forma predeterminada, y un índice DiskANN basado en disco para bóvedas demasiado grandes como para mantener los vectores residentes en memoria.23 Esto cambiaría la historia de escalamiento para bóvedas muy grandes, pero la línea 0.1.10 todavía está en pre-release (alpha): trata la indexación ANN como experimental y sigue construyendo sobre la ruta estable de KNN de fuerza bruta de v0.1.9 para bóvedas de producción hasta que se publique una versión estable 0.1.10.

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

Esto reemplaza el patrón anterior de obtener un k grande y recortarlo en Python, lo que reduce el uso de memoria en consultas exploratorias sobre bóvedas grandes.

Compatibilidad con DELETE en tablas vec0

sqlite-vec v0.1.7 agregó compatibilidad nativa con DELETE para tablas virtuales vec0, y v0.1.9 corrigió una ruta de error de DELETE relacionada con columnas de texto de metadatos de más de 12 caracteres.1423 Antes, eliminar vectores requería descartar y recrear la tabla. Ahora la ruta de eliminación de archivos del indexador puede borrar vectores directamente:

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

Esto simplifica la reindexación incremental cuando se eliminan o se mueven notas. El indexador ya no necesita mantener una tabla paralela de “IDs activos” ni reconstrucciones por lotes.

Cuándo gana la búsqueda vectorial

La búsqueda vectorial destaca en consultas donde el concepto importa más que las palabras específicas:

  • Consulta: “cómo manejar fallos de autenticación” → Encuentra notas sobre “recuperación de errores de inicio de sesión” (mismo espacio semántico, distintas palabras clave)
  • Consulta: “qué patrones existen para caching” → Encuentra notas sobre “memoization”, “estrategias de TTL de Redis” y “encabezados de caché HTTP” (conceptos relacionados, terminología diversa)
  • Consulta: “enfoques para probar código asíncrono” → Encuentra notas sobre “fixtures de pytest-asyncio”, “bucles de eventos simulados” y “patrones de pruebas async” (el mismo concepto expresado mediante detalles de implementación)

Cuándo falla la búsqueda vectorial

La búsqueda vectorial tiene dificultades con identificadores exactos:

  • Consulta: _rrf_fuse → Devuelve notas sobre “algoritmos de fusión” y “combinación de rankings”, pero puede posicionar la definición real de la función por debajo de discusiones conceptuales
  • Consulta: PostToolUse → Devuelve notas sobre “hooks de ciclo de vida de herramientas” y “manejadores posteriores a la ejecución”, en lugar del nombre específico del hook

La búsqueda vectorial también tiene dificultades con datos estructurados. Los archivos de configuración JSON, los bloques YAML y los fragmentos de código producen embeddings que capturan patrones estructurales en lugar de significado semántico. Un archivo JSON con "review": true genera un embedding distinto al de una discusión en prosa sobre revisión de código.

Degradación gradual

Si sqlite-vec no se puede cargar (extensión faltante, plataforma incompatible, biblioteca corrupta), el retriever vuelve a búsqueda solo con 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

El retriever revisa vec_available antes de intentar consultas vectoriales. Cuando está deshabilitado, todas las búsquedas usan solo BM25, y el paso de fusión RRF se omite.


Reciprocal Rank Fusion (RRF)

RRF fusiona dos listas clasificadas sin requerir calibración de puntajes. Esta sección cubre el algoritmo, un seguimiento paso a paso de una consulta, el ajuste del parámetro k y por qué se elige RRF frente a otras alternativas. Para ver una calculadora interactiva con rangos editables, escenarios preconfigurados y un explorador visual de arquitectura, consulta el análisis profundo del hybrid retriever.

El algoritmo

RRF asigna a cada documento un puntaje basado únicamente en su posición de rango dentro de cada lista:

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

Donde: - k es una constante de suavizado (60, siguiendo a Cormack et al.3) - rank_i es el rango del documento con base 1 en la lista de resultados i - weight_i es un multiplicador opcional por lista (predeterminado 1.0)

Los documentos que tienen buen rango en varias listas reciben puntajes fusionados más altos. Los documentos que aparecen en una sola lista reciben un puntaje de esa única fuente.

Por qué RRF en lugar de otras alternativas

La combinación lineal ponderada requiere calibrar los puntajes de BM25 contra las distancias de coseno. Los puntajes de BM25 no tienen límite superior y escalan con el tamaño del corpus. Las distancias de coseno están acotadas en [0, 2]. Combinarlas requiere normalización, y los parámetros de normalización dependen del conjunto de datos. RRF usa solo posiciones de rango, que siempre son enteros que empiezan en 1 sin importar el método de puntuación.

Los modelos de fusión aprendidos requieren datos de entrenamiento etiquetados: pares consulta-documento con relevancia. Para una base de conocimiento personal, esos datos de entrenamiento no existen. Tendrías que evaluar manualmente cientos de pares consulta-documento para entrenar un modelo útil. RRF funciona sin datos de entrenamiento.

Los métodos de votación Condorcet (Borda count, Schulze method) son teóricamente elegantes, pero más complejos de implementar y ajustar. El artículo original de RRF demostró que RRF supera a los métodos Condorcet en datos de evaluación TREC.3

Fusión en la práctica

Consulta: “how does the review aggregator handle disagreements”

BM25 clasifica review-aggregator.py en la posición 3 (coincidencias exactas de palabras clave en “review”, “aggregator”, “disagreements”), pero coloca dos archivos de configuración más arriba (coinciden con “review” de forma más prominente). La búsqueda vectorial clasifica el mismo fragmento en la posición 1 (coincidencia semántica sobre resolución de conflictos). Después de la fusión con RRF:

Fragmento BM25 Vec Puntaje fusionado
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

Los fragmentos que tienen buen rango en ambas listas suben a los primeros lugares. Los fragmentos que solo aparecen en una lista obtienen un puntaje de una sola fuente y quedan por debajo de los resultados clasificados en dos listas. La lógica real de resolución de desacuerdos gana porque ambos métodos la encontraron: BM25 mediante palabras clave, la búsqueda vectorial mediante semántica.

Para ver el seguimiento completo paso a paso con los cálculos de RRF por rango, prueba distintos valores de k en la calculadora interactiva de RRF.

Implementación

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

Ajuste de k

La constante k controla cuánto peso se da a los resultados mejor clasificados frente a los resultados con rangos más bajos:

  • k más bajo (por ejemplo, 10): Los resultados mejor clasificados dominan. El rango 1 puntúa 1/11 = 0,091; el rango 10 puntúa 1/20 = 0,050 (diferencia de 1,8x). Es útil cuando confías en que los rankers individuales acierten con el primer resultado.
  • k predeterminado (60): Equilibrado. El rango 1 puntúa 1/61 = 0,0164; el rango 10 puntúa 1/70 = 0,0143 (diferencia de 1,15x). Las diferencias de rango se comprimen, lo que da más peso a aparecer en varias listas.
  • k más alto (por ejemplo, 200): Aparecer en ambas listas importa mucho más que la posición de rango. El rango 1 puntúa 1/201; el rango 10 puntúa 1/210: casi idénticos. Úsalo cuando los rankers individuales produzcan rankings ruidosos, pero la coincidencia entre listas sea confiable.

Empieza con k=60. El artículo original de RRF encontró que este valor era robusto en diversos conjuntos de datos TREC. Ajústalo solo después de medir casos de fallo en tu propia distribución de consultas.

Desempate

Cuando dos fragmentos tienen puntajes RRF idénticos (raro, pero posible con el mismo rango en una lista y sin aparición en la otra), rompe los empates así:

  1. Prefiere los fragmentos que aparecen en ambas listas sobre los que aparecen en una sola
  2. Entre fragmentos que aparecen en ambas listas, prefiere el que tenga el rango combinado más bajo
  3. Entre fragmentos que aparecen en una sola lista, prefiere el que tenga el rango más bajo en esa lista

El pipeline completo de retrieval

Esta sección sigue una consulta desde la entrada hasta la salida a través de todo el pipeline: búsqueda BM25, búsqueda vectorial, fusión RRF, truncamiento del presupuesto de tokens y ensamblaje de contexto.

Flujo de principio a fin

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

Latencia total: ~23ms para una base de datos de 49.746 chunks en hardware Apple M3 Pro.

El API de búsqueda

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

Truncamiento del presupuesto de tokens

El parámetro max_tokens evita que el retriever devuelva más contexto del que la herramienta de AI puede usar. La estimación usa 4 caracteres por token (una aproximación razonable para prosa en inglés). Los resultados se truncan de forma codiciosa: se agregan resultados en orden de ranking hasta agotar el presupuesto.

Esta es una estrategia conservadora. Un enfoque más sofisticado consideraría las puntuaciones de calidad de cada resultado y preferiría resultados más cortos y de mayor calidad por encima de resultados más largos y de menor calidad. El enfoque codicioso es más simple y funciona bien en la práctica porque el ranking RRF ya ordena los resultados por relevancia.

Esquema de base de datos (completo)

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

Ruta de degradación elegante

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

El retriever verifica las capacidades durante la inicialización y adapta su estrategia de consulta. Un componente faltante degrada la calidad, pero no provoca errores. La única falla crítica es que falte el archivo de base de datos.

Estadísticas de producción

Medido en una bóveda de 16.894 archivos, 49.746 chunks, una base de datos SQLite de 83 MB, Apple M3 Pro:

Métrica Valor
Archivos totales 16.894
Chunks totales 49.746
Tamaño de la base de datos 83 MB
Latencia de consulta BM25 (p50) 12ms
Latencia de consulta vectorial (p50) 8ms
Latencia de fusión RRF 3ms
Latencia de búsqueda de principio a fin (p50) 23ms
Tiempo de reindexación completa ~4 minutos
Tiempo de reindexación incremental <10 segundos
Modelo de embedding potion-base-8M (256-dim)
Pool de candidatos BM25 30
Pool de candidatos vectoriales 30
Límite de resultados predeterminado 10
Presupuesto de tokens predeterminado 4.000 tokens

Hashing de contenido y detección de cambios

El indexador necesita saber qué archivos cambiaron desde la última ejecución del índice. Esta sección cubre el mecanismo de detección de cambios y la estrategia de hashing.

Comparación de hora de modificación de archivos

El indexador almacena mtime_ns (hora de modificación del archivo en nanosegundos) para cada chunk en la tabla chunks. En una ejecución incremental, el indexador:

  1. Escanea la bóveda en busca de todos los archivos .md en las carpetas permitidas
  2. Lee el mtime_ns de cada archivo desde el filesystem
  3. Lo compara con el mtime_ns almacenado en la base de datos
  4. Identifica tres categorías:
  5. Archivos nuevos: la ruta existe en el filesystem, pero no en la base de datos
  6. Archivos modificados: la ruta existe en ambos, pero mtime_ns difiere
  7. Archivos eliminados: la ruta existe en la base de datos, pero no en el filesystem
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)

Por qué mtime y no hash de contenido

El hashing de contenido (SHA-256 del contenido de los archivos) sería más confiable que la comparación de mtime: detectaría casos en los que un archivo se tocó sin cambiar (por ejemplo, un git checkout que restaura el mtime original). Sin embargo, hacer hash requiere leer todos los archivos en cada ejecución incremental. Para 16.894 archivos, leer el contenido de los archivos toma 2-3 segundos. Leer mtimes desde el filesystem toma <100ms.

El compromiso: la comparación de mtime ocasionalmente activa una reindexación innecesaria de archivos sin cambios (falsos positivos), pero nunca pierde cambios reales. Los falsos positivos cuestan unas pocas llamadas extra de embedding por ejecución. La diferencia de velocidad (100ms frente a 3 segundos) hace que mtime sea la opción pragmática para un sistema que se ejecuta en cada interacción con AI.

Manejo de eliminaciones

Cuando se elimina un archivo de la bóveda, el indexador elimina todos sus chunks de la base de datos:

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

La instrucción DELETE FROM chunk_vecs funciona de forma nativa desde sqlite-vec v0.1.7, con una corrección de error en v0.1.9 para operaciones DELETE contra tablas vec0 con columnas de texto de metadatos más largas.1423 Las versiones anteriores requerían soluciones alternativas (eliminar y recrear la tabla virtual, o mantener un conjunto externo de “IDs activos”). Si estás ejecutando una versión anterior a 0.1.9, actualiza antes de depender de eliminaciones directas en esquemas con muchos metadatos.

Las tablas de sincronización de contenido FTS5 requieren eliminación explícita mediante INSERT INTO chunks_fts(chunks_fts, rowid, ...) VALUES('delete', ?, ...) para cada fila eliminada. El indexador maneja esto como parte del proceso de eliminación de archivos.


Reindexación incremental vs completa

El indexador admite dos modos: incremental (rápido, para uso diario) y completo (lento, ocasional). Esta sección cubre cuándo usar cada uno, las garantías de idempotencia y la recuperación ante corrupción.

Reindexación incremental

Cuándo usarla: Indexación diaria después de editar notas. Es el modo predeterminado.

Qué hace: 1. Escanea el vault para detectar cambios en archivos (comparación de mtime) 2. Elimina chunks de archivos borrados 3. Vuelve a dividir en chunks y a generar embeddings de los archivos modificados 4. Inserta nuevos chunks para archivos nuevos 5. Sincroniza el índice FTS5

Duración típica: <10 segundos para las ediciones de un día en un vault de 16.000 archivos.

python index_vault.py --incremental

Reindexación completa

Cuándo usarla: - Después de cambiar el modelo de embeddings (se detecta una discrepancia en el hash del modelo) - Después de una migración de esquema (nuevas columnas, índices modificados) - Después de corrupción en la base de datos (falla la comprobación de integridad) - Cuando la indexación incremental produce resultados inesperados

Qué hace: 1. Elimina todos los datos existentes (chunks, vectores, entradas FTS5) 2. Escanea todo el vault 3. Divide todos los archivos en chunks 4. Genera embeddings de todos los chunks 5. Construye el índice FTS5 desde cero

Duración típica: ~4 minutos para 16.894 archivos en Apple M3 Pro.

python index_vault.py --full

Idempotencia

Ambos modos son idempotentes: ejecutar el mismo comando dos veces produce el mismo resultado. El indexador elimina los chunks existentes de un archivo antes de insertar los nuevos, así que volver a ejecutar la indexación incremental sobre una base de datos ya actualizada produce cero cambios. Volver a ejecutar la indexación completa produce una base de datos idéntica.

Recuperación ante corrupción

Si la base de datos SQLite se corrompe (pérdida de energía durante una escritura, error de disco, proceso terminado a mitad de transacción):

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

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

La fuente de verdad siempre son los archivos del vault, no la base de datos. La base de datos es un artefacto derivado que puede reconstruirse en cualquier momento. Esta es una propiedad de diseño crítica: nunca necesitas hacer copias de seguridad de la base de datos.

La flag --incremental

Cuando el indexador se ejecuta con --incremental:

  1. Comprobación del hash del modelo. Compara el hash del modelo almacenado con el modelo actual. Si es diferente, cambia automáticamente al modo de reindexación completa y advierte al usuario.
  2. Escaneo de archivos. Recorre las carpetas permitidas y recopila rutas de archivo y mtimes.
  3. Detección de cambios. Compara contra los datos almacenados.
  4. Procesamiento por lotes. Vuelve a dividir en chunks y a generar embeddings de los archivos modificados en lotes de 64.
  5. Reporte de progreso. Imprime la cantidad de archivos procesados y el tiempo transcurrido.
  6. Apagado ordenado. Maneja SIGINT terminando el archivo actual antes de detenerse.

Filtrado de credenciales y límites de datos

Las notas personales contienen secretos: claves de API, bearer tokens, cadenas de conexión a bases de datos, claves privadas pegadas durante sesiones de depuración. El filtro de credenciales evita que entren al índice de recuperación.

El problema

Una nota sobre la depuración de una integración de OAuth podría contener:

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

Sin filtrado, tanto el JWT como la clave de API se dividirían en chunks, se convertirían en embeddings y se almacenarían en la base de datos. Una búsqueda de “authentication” devolvería el chunk que contiene secretos reales. Peor aún, si el recuperador envía resultados a una herramienta de AI mediante MCP, los secretos aparecen en la ventana de contexto de la AI y potencialmente en los logs de la herramienta.

Filtrado basado en patrones

El filtro de credenciales se ejecuta en cada chunk antes del almacenamiento, comparando 25 patrones específicos de proveedores más patrones genéricos:

Patrones específicos de proveedores:

Patrón Ejemplo Regex
Clave de API de OpenAI sk-... sk-[a-zA-Z0-9_-]{20,}
Clave de API de Anthropic sk-ant-api03-... sk-ant-api\d{2}-[a-zA-Z0-9_-]{20,}
PAT de GitHub ghp_... gh[ps]_[a-zA-Z0-9]{36,}
AWS Access Key AKIA... AKIA[0-9A-Z]{16}
Clave de Stripe sk_live_... [sr]k_(live\|test)_[a-zA-Z0-9]{24,}
Token de Cloudflare ... Varios patrones

Patrones genéricos:

Patrón Detección
Tokens JWT eyJ[a-zA-Z0-9_-]+\.eyJ[a-zA-Z0-9_-]+
Bearer tokens Bearer\s+[a-zA-Z0-9_\-\.]+
Claves privadas -----BEGIN (RSA\|EC\|OPENSSH) PRIVATE KEY-----
base64 de alta entropía Cadenas con >4,5 bits/carácter de entropía, 40+ caracteres
Asignaciones de contraseña password\s*[:=]\s*["'][^"']+["']

Implementación del filtro

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

Decisiones clave de diseño:

  1. Filtrar antes de generar embeddings. El texto limpio es el que se convierte en embeddings. La representación vectorial nunca codifica patrones de credenciales. Una consulta de “clave de API” devuelve notas que hablan sobre la gestión de claves de API, no notas que contienen claves reales.

  2. Reemplazar, no eliminar. El token [REDACTED:pattern-name] conserva el contexto semántico del texto circundante. El embedding captura que “había algo parecido a una credencial aquí” sin codificar la credencial en sí.

  3. Registrar patrones, no valores. El filtro registra qué patrones coincidieron (por ejemplo, “Scrubbed 2 credential(s) from oauth-debug.md [jwt, bearer-token]”), pero nunca registra el valor de la credencial.

Exclusión basada en rutas

El archivo .indexignore proporciona exclusión general por ruta. El filtro de credenciales ofrece limpieza detallada dentro de archivos indexados. Ambos son necesarios:

  • .indexignore para carpetas completas que sabes que contienen contenido sensible (notas de salud, registros financieros, documentos profesionales)
  • Filtro de credenciales para secretos incrustados accidentalmente en contenido que, por lo demás, sí se puede indexar

Clasificación de datos

Para vaults que contienen contenido diverso, considera clasificar las notas por sensibilidad:

Nivel Ejemplos ¿Indexar? ¿Filtrar?
Público Borradores de blog, notas técnicas
Interno Planes de proyecto, decisiones de arquitectura
Sensible Datos salariales, registros de salud No (.indexignore) N/A
Restringido Credenciales, claves privadas No (.indexignore) N/A

Arquitectura del servidor MCP

Los servidores Model Context Protocol (MCP) exponen el recuperador como una herramienta que los agentes de AI pueden llamar. Esta sección cubre el diseño del servidor, la superficie de capacidades y los límites de permisos.

Elección de protocolo: STDIO vs HTTP

MCP admite dos modos de transporte:

STDIO — La herramienta de AI inicia el servidor MCP como un proceso hijo y se comunica por stdin/stdout. Este es el modo estándar para herramientas locales. Claude Code, Codex CLI y Cursor admiten servidores MCP por STDIO.

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

HTTP — El servidor MCP se ejecuta como un servicio HTTP independiente. Es útil para acceso remoto, configuraciones con varios clientes o configuraciones de equipo donde el vault está en un servidor compartido.

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

Recomendación: Usa STDIO para vaults personales. Es más simple, más seguro (sin exposición a la red) y el ciclo de vida del servidor lo administra la herramienta de AI. Usa HTTP solo cuando varias herramientas o varias máquinas necesiten acceso concurrente al mismo vault.

Evolución de la especificación MCP. La especificación MCP de junio de 2025 agregó autorización OAuth 2.1, salidas estructuradas de herramientas (esquemas de retorno tipados) y elicitation (solicitudes al usuario iniciadas por el servidor). La versión de noviembre de 2025 incorporó Streamable HTTP como modo de transporte de primera clase, descubrimiento de URL .well-known para explorar automáticamente las capacidades del servidor, anotaciones estructuradas de herramientas que declaran si una herramienta es de solo lectura o muta datos, y un sistema de estandarización por niveles SDK.79 La siguiente revisión ya es concreta: la especificación 2026-07-28 entró en Release Candidate el 21 de mayo de 2026, la mayor revisión de MCP desde su lanzamiento. Sus cambios principales son un núcleo de protocolo sin estado (se eliminan el handshake initialize y el encabezado Mcp-Session-Id, por lo que los servidores ya no rastrean estado de sesión por conexión), Apps MCP (los servidores pueden devolver HTML renderizado por el servidor y mostrado en iframes de cliente aislados), Tasks pasa del núcleo experimental a una extensión oficial (tasks/get, tasks/update, tasks/cancel para operaciones de larga duración), autorización OAuth 2.0 / OIDC reforzada y una política de ciclo de vida de 12 meses para la obsolescencia de funciones. La especificación final se publica el 28 de julio de 2026.24 Para servidores de vaults personales, STDIO sigue siendo la ruta más simple, y el núcleo sin estado hace que los servidores STDIO de un solo usuario sean aún más ligeros. El transporte Streamable HTTP, el descubrimiento .well-known y las Apps MCP benefician sobre todo a implementaciones HTTP empresariales con enrutamiento multiinquilino y balanceo de carga. Monitorea el roadmap de MCP para ver actualizaciones que afecten tu elección de transporte.

Diseño de capacidades

El servidor MCP debe exponer un conjunto mínimo de herramientas:

search — La herramienta principal. Ejecuta recuperación hybrid y devuelve resultados clasificados.

{
  "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 — Lee el contenido completo de una nota específica por ruta. Es útil cuando el agente quiere ver el contexto completo de un resultado de búsqueda.

{
  "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 — Lista notas que coinciden con un filtro (por carpeta, etiqueta, tipo o intervalo de fechas). Es útil para explorar cuando el agente no tiene una consulta específica.

{
  "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 — Una herramienta de conveniencia que ejecuta una búsqueda y formatea los resultados como un bloque de contexto adecuado para inyectarlo en una conversación.

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

Límites de permisos

El servidor MCP debe imponer límites estrictos:

  1. Solo lectura. El servidor lee el vault y la base de datos del índice. No crea, modifica ni elimina notas. Las operaciones de escritura (capturar notas nuevas) se manejan mediante hooks o skills separados, no desde el servidor MCP.

  2. Limitado al vault. El servidor solo lee archivos dentro de la ruta configurada del vault. Los intentos de path traversal (../../etc/passwd) deben rechazarse.

  3. Salida filtrada por credenciales. Aunque la base de datos contenga contenido prefiltrado, aplica filtrado de credenciales en la salida como medida de defensa en profundidad.

  4. Respuestas limitadas por tokens. Aplica max_tokens a todas las respuestas de herramientas para evitar que la herramienta de AI reciba bloques de contexto excesivamente grandes.

Manejo de errores

Las herramientas MCP deben devolver mensajes de error estructurados que ayuden a la herramienta de AI a recuperarse:

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

Integración de Claude Code

Claude Code es el consumidor principal del sistema de recuperación de Obsidian. Esta sección cubre la configuración de MCP, la integración de hooks y el patrón obsidian_bridge.py.

Configuración de MCP

Agrega el servidor MCP de Obsidian a ~/.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"
      }
    }
  }
}

Después de agregar la configuración, reinicia Claude Code. El servidor MCP se iniciará como un proceso hijo. Verifica que esté en ejecución:

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

Claude Code debería listar las herramientas disponibles (obsidian_search, obsidian_read_note, etc.).

Integración de hooks

Los hooks amplían el comportamiento de Claude Code en puntos definidos del ciclo de vida. Dos hooks son relevantes para la integración con Obsidian:

Hook PreToolUse — Consulta la bóveda antes de que el agente procese una llamada a herramienta. Inyecta automáticamente contexto relevante.

#!/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 — Captura salidas significativas de herramientas de vuelta en la bóveda para futuras recuperaciones.

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

El patrón obsidian_bridge.py

Un módulo puente proporciona un Python API que los hooks y skills pueden llamar:

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

La skill /capture

Una skill de Claude Code para capturar ideas de vuelta en la bóveda:

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

La skill crea una nota nueva en 00-inbox/ con frontmatter adecuado y activa una reindexación incremental para que la nota nueva se pueda buscar de inmediato.

Patrones de comandos personalizados

Las skills de Claude Code pueden envolver operaciones de la bóveda en comandos con nombre. Los profesionales han creado bibliotecas de comandos específicos para Obsidian que tratan la bóveda como fuente de lectura y destino de escritura.

Escaneo de señales. Un comando /scan-intel consulta fuentes externas, puntúa hallazgos según intereses personales de investigación y escribe las señales que califican como notas de la bóveda con frontmatter:

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

El comando obtiene información de fuentes configuradas (arXiv, HN, RSS), aplica un modelo de puntuación (relevancia, accionabilidad, profundidad, autoridad) y escribe las señales aprobadas en carpetas temáticas específicas de la bóveda. La bóveda se convierte en el consumidor descendente de un pipeline automatizado de inteligencia.

Bitácora del capitán. Un comando /captains-log agrega la actividad diaria de git en todos los repositorios, escribe una entrada de diario estructurada en la bóveda e incluye decisiones tomadas, descubrimientos e hilos abiertos:

/captains-log

El comando extrae el historial de commits de GitHub, agrupa por repositorio y formatea el resultado como una entrada narrativa de diario. Con el tiempo, los registros diarios crean un historial consultable de qué se lanzó y por qué.

Captura en Obsidian. Un comando /obsidian-capture toma una idea de la sesión actual de Claude Code y la escribe directamente en la bóveda con los metadatos adecuados:

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

El patrón se extiende a cualquier operación de la bóveda: crear MOCs, actualizar notas de estado de proyectos, vincular señales relacionadas o generar resúmenes semanales a partir de registros diarios acumulados.

Ejemplos de la comunidad. Los profesionales están publicando sus bibliotecas de comandos. Un desarrollador compartió 22 comandos personalizados de Obsidian + Claude Code que cubren revisiones diarias, planificación de proyectos, captura de investigación y flujos de trabajo de contenido.1 Otro creó una skill “Visual Explainer” que genera notas de diagramas en la bóveda a partir del análisis de código.2 Los comandos varían, pero la arquitectura es consistente: skills de Claude Code como interfaz, notas de la bóveda como capa de almacenamiento e infraestructura de recuperación como motor de consulta.

Gestión de la ventana de contexto

La integración debe tener presente la ventana de contexto de Claude Code:

  • Limita el contexto inyectado a 1.500-2.000 tokens por consulta. Más que eso compite con la memoria de trabajo del agente.
  • Incluye atribución de fuente. Incluye siempre la ruta del archivo y el encabezado de la sección para que el agente pueda referenciar la fuente.
  • Trunca el texto de los chunks. Los chunks largos deben truncarse con ... en lugar de omitirse por completo. Los primeros 300-500 caracteres suelen contener la información clave.
  • No inyectes en cada llamada a herramienta. El hook PreToolUse debe inyectar contexto de forma selectiva según la herramienta llamada. Las operaciones de lectura no necesitan contexto de la bóveda. Las operaciones de Write y Edit sí se benefician de él.

Integración de Codex CLI

Codex CLI se conecta a servidores MCP mediante config.toml. El patrón de integración difiere de Claude Code en la sintaxis de configuración y en la entrega de instrucciones.

Configuración de MCP

Agrega esto a .codex/config.toml o ~/.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"

Patrones de AGENTS.md

Codex CLI lee AGENTS.md para instrucciones a nivel de proyecto. Incluye orientación para buscar en la bóveda:

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

Diferencias con Claude Code

Función Claude Code Codex CLI
Configuración de MCP settings.json config.toml
Hooks ~/.claude/hooks/ No compatible
Skills ~/.claude/skills/ No compatible
Archivo de instrucciones CLAUDE.md AGENTS.md
Modos de aprobación --dangerously-skip-permissions suggest / auto-edit / full-auto

Diferencia clave: Codex CLI no admite hooks. El patrón de inyección automática de contexto (hook PreToolUse) no está disponible. En su lugar, incluye instrucciones explícitas en AGENTS.md que le indiquen al agente que busque en la bóveda antes de empezar a trabajar.

Cursor y otras herramientas

Cursor y otras herramientas de AI compatibles con MCP pueden conectarse al mismo servidor MCP de Obsidian. Esta sección cubre la configuración de herramientas comunes.

Cursor

Agrega esto a .cursor/mcp.json en la raíz de tu proyecto:

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

El archivo .cursorrules de Cursor puede incluir instrucciones para usar la bóveda:

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.

Matriz de compatibilidad

Herramienta Compatibilidad con MCP Transporte Ubicación de configuración
Claude Code Completa STDIO ~/.claude/settings.json
Codex CLI Completa STDIO .codex/config.toml
Cursor Completa STDIO .cursor/mcp.json
Windsurf Completa STDIO .windsurf/mcp.json
Continue.dev Parcial HTTP ~/.continue/config.json
Zed En progreso STDIO Settings UI
Claudian (plugin de Obsidian) N/A (integrado) Claude Code CLI Configuración del plugin de Obsidian
Agent Client (plugin de Obsidian) N/A (integrado) ACP Configuración del plugin de Obsidian

Alternativa para herramientas sin MCP

Para herramientas que no son compatibles con MCP, el retriever puede envolverse como una 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

La CLI genera texto estructurado que puedes pegar manualmente en la entrada de cualquier herramienta de AI. Es menos elegante que una integración con MCP, pero funciona de forma universal.


Prompt caching a partir de notas estructuradas

Las notas estructuradas en la bóveda pueden servir como bloques de contexto reutilizables que reducen el uso de tokens en distintas interacciones con AI. Esta sección cubre el diseño de claves de caché y la gestión del presupuesto de tokens.

El patrón

En lugar de buscar contexto en cada interacción, preconstruye bloques de contexto a partir de notas bien estructuradas de la bóveda y guárdalos en caché:

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

Invalidación de caché

La invalidación de caché se basa en dos señales:

  1. Vencimiento del TTL. Cada bloque de contexto tiene un tiempo de vida. Cuando vence el TTL, el bloque se reconstruye consultando de nuevo la bóveda.
  2. Detección de cambios en la bóveda. Cuando el indexer detecta cambios en archivos que contribuyeron a un bloque de contexto en caché, el bloque se invalida de inmediato.

Gestión del presupuesto de tokens

Una sesión comienza con un presupuesto total de contexto. Los bloques en caché consumen parte de ese presupuesto:

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)

Los bloques en caché se cargan al inicio de la sesión. Los resultados dinámicos de búsqueda completan el presupuesto restante según cada consulta. Este enfoque híbrido le da al agente una base de contexto que se necesita con frecuencia, mientras conserva presupuesto para consultas específicas.

Uso de tokens antes/después

Sin caché: Cada consulta relevante activa una búsqueda en la bóveda, que devuelve entre 1.500 y 2.000 tokens de contexto. En 10 consultas dentro de una sesión, el agente consume entre 15.000 y 20.000 tokens de contexto de la bóveda.

Con caché: Tres bloques de contexto preconstruidos consumen 4.500 tokens en total. Las búsquedas adicionales suman entre 1.500 y 2.000 tokens por consulta única. En 10 consultas donde 6 están cubiertas por bloques en caché, el agente consume 4.500 + (4 * 1.500) = 10.500 tokens: aproximadamente la mitad del uso sin caché.


Hooks PostToolUse para compresión de contexto

Las salidas de herramientas pueden ser extensas: stack traces, listados de archivos, resultados de pruebas. Un hook PostToolUse puede comprimir estas salidas antes de que consuman espacio en la ventana de contexto.

El problema

Una llamada a la herramienta Bash que ejecuta pruebas podría devolver:

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

La salida completa tiene 5.000 tokens, pero la señal está en 2 líneas: 200 pasaron, 1 falló.

Implementación del hook

#!/bin/bash
# ~/.claude/hooks/post-tool-use/compress-output.sh
# Compress verbose tool outputs to preserve context window

TOOL_NAME="$1"
OUTPUT="$2"
OUTPUT_LEN=${#OUTPUT}

# Only compress large outputs
if [ "$OUTPUT_LEN" -lt 2000 ]; then
    exit 0  # Pass through unchanged
fi

case "$TOOL_NAME" in
    Bash)
        # Compress test output
        if echo "$OUTPUT" | grep -q "PASSED\|FAILED"; then
            PASSED=$(echo "$OUTPUT" | grep -c "PASSED")
            FAILED=$(echo "$OUTPUT" | grep -c "FAILED")
            FAILURES=$(echo "$OUTPUT" | grep "FAILED")
            echo "Tests: $PASSED passed, $FAILED failed"
            if [ "$FAILED" -gt 0 ]; then
                echo "Failures:"
                echo "$FAILURES"
            fi
        fi
        ;;
esac

Prevención de activación recursiva

Un hook de compresión que emite salida podría activarse a sí mismo si no está protegido:

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

Heurísticas de compresión

Tipo de salida Detección Estrategia de compresión
Resultados de pruebas Palabras clave PASSED / FAILED Contar aprobadas/fallidas, mostrar solo los fallos
Listados de archivos ls o find en el comando Truncar a las primeras 20 entradas + conteo
Stack traces Palabra clave Traceback Conservar el primer y último frame + mensaje de error
Git status modified: / new file: Resumir conteos por estado
Salida de build warning: / error: Eliminar líneas informativas, conservar advertencias/errores

Pipeline de ingesta y clasificación de señales

La capa de ingesta determina qué entra en la bóveda. Sin curación, la bóveda acumula ruido. Esta sección cubre el pipeline de puntuación que dirige las señales a carpetas de dominio.

Fuentes

Las señales provienen de varios canales:

  • Feeds RSS: Blogs técnicos, avisos de seguridad, notas de lanzamiento
  • Marcadores mediante Web Clipper: La extensión oficial Obsidian Web Clipper (Chrome, Firefox, Safari) es la ruta de ingesta de mayor fidelidad para capturas desde el navegador. El ciclo de lanzamiento de abril de 2026 la volvió materialmente más útil para flujos de trabajo de AI:22
    • 1.4.0 (9 de abril): UI interactiva de transcripción de YouTube: fija el video, desplázate por la transcripción, usa desplazamiento automático y resalta la posición actual. Además, una opción predeterminada “Open in Reader” que envía una captura con un clic directamente al modo Reader.
    • 1.5.0–1.5.1 (15 de abril): Visor de resaltados: explora y busca resaltados capturados en toda la bóveda. Transición con aparición gradual hacia Reader. Reproducción/pausa de YouTube más fluida. 1.5.1 corrigió una regresión de compilación de webpack.
    • 1.6.0–1.6.2 (21–23 de abril): Rediseño de UX del resaltador con soporte móvil. Defuddle 0.18 agrega extractores específicos por fuente para LinkedIn, Threads, Bluesky, Discourse y Medium. 1.6.2 corrige una regresión del portapapeles en modo incrustado de Safari. Configura plantillas por dominio de origen para que las transcripciones de YouTube, los README de GitHub y los artículos largos terminen cada uno en una nota con un nombre sensato y el frontmatter correcto para el pipeline de puntuación de abajo.
  • Newsletters: Extractos clave de newsletters por correo electrónico
  • Captura manual: Notas escritas durante lecturas, conversaciones o investigaciones
  • Salida de herramientas: Salidas importantes de herramientas de AI capturadas mediante hooks
  • iOS Share Extension: La app de iOS de Obsidian (actualizada a comienzos de 2026) incluye una Share Extension que guarda contenido de Safari, redes sociales y otras apps directamente en la bóveda sin abrir Obsidian.19 Esto crea una ruta de ingesta móvil de baja fricción: comparte un artículo desde Safari y llega como una nota de la bóveda lista para puntuarse.
  • Obsidian CLI: Los scripts de shell y hooks pueden crear notas mediante obsidian file create o anexar contenido a notas existentes mediante obsidian file append, lo que habilita pipelines de ingesta automatizados en escritorio.

Dimensiones de puntuación

Cada señal se puntúa en cuatro dimensiones (de 0,0 a 1,0 cada una):

Dimensión Pregunta Puntuación baja (0,0-0,3) Puntuación alta (0,7-1,0)
Relevancia ¿Esto se relaciona con mis dominios activos? Tangencial, fuera de alcance Directamente relevante para el trabajo activo
Accionabilidad ¿Puedo usar esta información? Teoría pura, sin aplicación Técnica o patrón específico que puedo aplicar
Profundidad ¿Qué tan sustancial es el contenido? Titulares, resumen superficial Análisis detallado con ejemplos
Autoridad ¿Qué tan creíble es la fuente? Blog anónimo, sin verificar Fuente primaria, revisado por pares, experto reconocido

Puntuación compuesta y enrutamiento

composite = (relevance * 0.35) + (actionability * 0.25) +
            (depth * 0.25) + (authority * 0.15)
Rango de puntuación Acción
0.55+ Enrutar automáticamente a carpeta de dominio
0.40 - 0.55 Enviar a cola para revisión manual
< 0.40 Descartar (no almacenar)

Enrutamiento por dominio

Las señales con puntuación superior a 0.55 se enrutan a una de 12 carpetas de dominio según coincidencia de palabras clave y clasificación temática:

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

Estadísticas de producción

Durante 14 meses de operación:

Métrica Valor
Total de señales procesadas 7.771
Enrutadas automáticamente (>0.55) 4.832 (62%)
En cola para revisión (0.40-0.55) 1.543 (20%)
Descartadas (<0.40) 1.396 (18%)
Carpetas de dominio activas 12
Promedio de señales por día ~18

Patrones de Knowledge Graph

El grafo de wiki-link de Obsidian codifica relaciones entre notas. Esta sección cubre la semántica de enlaces, el recorrido del grafo para expansión de contexto y los antipatrones que degradan la calidad del grafo.

Cada wiki-link crea una arista dirigida en el grafo. Obsidian rastrea tanto enlaces hacia adelante como backlinks:

  • Enlace hacia adelante: La Nota A contiene [[Note B]] → A enlaza a B
  • Backlink: La Nota B muestra que la Nota A la referencia

El grafo codifica distintos tipos de relaciones según el contexto:

Patrón de enlace Semántica Ejemplo
Enlace en línea “Está relacionado con” “Consulta [[OAuth Token Rotation]] para más detalles”
Enlace de encabezado “Tiene subtema” ”## Related\n- [[Token Rotation]]\n- [[Session Management]]”
Enlace tipo etiqueta “Está categorizado como” ”[[type/reference]]”
Enlace MOC “Forma parte de” Una nota Map of Content que enumera notas relacionadas

Maps of Content (MOCs)

Los MOCs son notas índice que organizan notas relacionadas en una estructura navegable:

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

Los MOCs benefician la recuperación de dos maneras:

  1. Coincidencia directa. Una búsqueda de “authentication overview” coincide con el MOC en sí, lo que le da al agente una lista curada de notas relacionadas.
  2. Expansión de contexto. Después de encontrar una nota específica, el recuperador puede verificar si la nota aparece en algún MOC e incluir la estructura del MOC en los resultados, lo que le da al agente un mapa del tema más amplio.

Recorrido del grafo para expansión de contexto

Una mejora futura para el recuperador: después de encontrar los resultados principales, expandir el contexto siguiendo enlaces:

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)

Esto no está implementado en el recuperador actual, pero representa una extensión natural de la estructura del grafo.

Antipatrones

Clústeres huérfanos. Grupos de notas que se enlazan entre sí, pero no tienen conexiones con el resto de la bóveda. El panel de grafo en Obsidian los hace visibles como islas desconectadas. Los clústeres huérfanos indican MOCs faltantes o enlaces entre dominios faltantes.

Proliferación de etiquetas. Usar etiquetas de forma inconsistente o crear demasiadas etiquetas de grano fino. Una bóveda con 500 etiquetas únicas en 5.000 notas promedia 1 nota por cada 10 etiquetas: las etiquetas no son útiles para filtrar. Consolida en 20-50 etiquetas de alto nivel que se correspondan con tus carpetas de dominio.

Notas con muchos enlaces y poco contenido. Notas que consisten por completo en wiki-links sin prosa. Estas notas se indexan mal porque el chunker no tiene texto para incrustar. Agrega al menos un párrafo de contexto que explique por qué las notas enlazadas están relacionadas.

Enlaces bidireccionales para todo. No toda referencia necesita ser un wiki-link. Mencionar “OAuth” de pasada no requiere [[OAuth 2.0 Overview]]. Reserva los wiki-links para relaciones intencionales y navegables donde hacer clic en el enlace aporte contexto útil.


Recetas de flujo de trabajo para desarrolladores

Flujos de trabajo prácticos que combinan la recuperación del vault con tareas diarias de desarrollo.

Carga de contexto matutina

Empieza el día cargando contexto relevante:

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

El retriever devuelve notas recientes sobre tu proyecto activo y te da un repaso rápido de dónde te quedaste. Es más efectivo que releer los mensajes de commit de ayer.

Captura de investigación mientras programas

Mientras implementas una función, captura hallazgos sin salir del editor:

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

El hallazgo capturado se indexa de inmediato y queda disponible para recuperarlo en el futuro. Con el paso de los meses, estas microcapturas construyen un corpus de conocimiento específico de implementación.

Inicio de proyecto

Al empezar un proyecto o una función nueva:

  1. Busca en el vault: “What do I know about [technology/pattern]?”
  2. Revisa los 5 resultados principales para encontrar decisiones previas y problemas conocidos
  3. Verifica si existe un MOC para el dominio; si no, crea uno
  4. Busca modos de falla: “problems with [technology]”

Debugging con búsqueda en el vault

Cuando encuentres un error o un comportamiento inesperado:

Search my vault for [error message or symptom]

Las notas previas de debugging suelen contener la causa raíz y la solución. Esto es especialmente valioso para problemas recurrentes entre proyectos: el vault recuerda lo que tú olvidas.

Preparación para revisión de código

Antes de revisar un PR:

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

El vault devuelve decisiones previas, restricciones arquitectónicas y estándares de código relevantes para el código en revisión. La revisión se informa con conocimiento institucional, no solo con el diff.


Ajuste de rendimiento

Esta sección cubre estrategias de optimización para distintos tamaños de vault y patrones de uso.

Gestión del tamaño del índice

Tamaño del vault Chunks Tamaño de DB Reindexado completo Incremental
500 notas ~1,500 3 MB 15 segundos <1 segundo
2,000 notas ~6,000 12 MB 45 segundos 2 segundos
5,000 notas ~15,000 30 MB 2 minutos 4 segundos
15,000 notas ~50,000 83 MB 4 minutos <10 segundos
50,000 notas ~150,000 250 MB 15 minutos 30 segundos

Con 50,000+ notas, considera: - Aumentar el tamaño de batch de 64 a 128 para acelerar los embeddings - Usar WAL mode (predeterminado) para acceso concurrente - Ejecutar el reindexado completo fuera del horario de mayor uso

Optimización de consultas

WAL mode. El modo Write-Ahead Logging de SQLite permite lecturas concurrentes mientras el indexer escribe:

db.execute("PRAGMA journal_mode=WAL")

Esto es crítico cuando el servidor MCP gestiona consultas mientras el indexer ejecuta una actualización incremental.

Connection pooling. El servidor MCP debería reutilizar conexiones a la base de datos en lugar de abrir una conexión nueva por consulta. Una sola conexión de larga duración con WAL mode admite lecturas concurrentes.

# MCP server initialization
db = sqlite3.connect(DB_PATH, check_same_thread=False)
db.execute("PRAGMA journal_mode=WAL")
db.execute("PRAGMA mmap_size=268435456")  # 256 MB mmap

I/O con mapeo de memoria. El pragma mmap_size le indica a SQLite que use I/O con mapeo de memoria para el archivo de base de datos. En una base de datos de 83 MB, mapear todo el archivo en memoria elimina la mayoría de las lecturas de disco.

Optimización de FTS5. Después de un reindexado completo, ejecuta:

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

Esto fusiona los segmentos b-tree internos de FTS5 y reduce la latencia de las consultas en búsquedas posteriores.

Benchmarks de escalado

Medido en Apple M3 Pro, 36 GB de RAM, SSD NVMe:

Operación 500 notas 5K notas 15K notas 50K notas
Consulta BM25 2ms 5ms 12ms 25ms
Consulta vectorial 1ms 3ms 8ms 20ms
Fusión RRF <1ms <1ms 3ms 5ms
Búsqueda completa 3ms 8ms 23ms 50ms

Todos los benchmarks incluyen acceso a la base de datos, ejecución de consultas y formato de resultados. La latencia de red para la comunicación MCP por STDIO agrega 1-2ms.


Solución de problemas

Desfase del índice

Síntoma: La búsqueda devuelve resultados obsoletos o no encuentra notas agregadas recientemente.

Causa: El indexer incremental no se ejecutó después de agregar notas, o no se actualizó el mtime de un archivo (por ejemplo, se sincronizó desde otra máquina conservando timestamps).

Solución: Ejecuta un reindexado completo: python index_vault.py --full

Cambio de modelo de embeddings

Síntoma: Después de cambiar el modelo de embeddings, la búsqueda vectorial devuelve resultados sin sentido.

Causa: Se están comparando vectores antiguos (del modelo anterior) contra nuevos vectores de consulta. Las dimensiones o la semántica del espacio vectorial son incompatibles.

Solución: El indexer debería detectar la discrepancia del hash del modelo y activar automáticamente un reindexado completo. Si no lo hace, borra manualmente la base de datos y vuelve a indexar:

rm vectors.db
python index_vault.py --full

Mantenimiento de FTS5

Síntoma: Las consultas FTS5 devuelven resultados incorrectos o incompletos después de muchas actualizaciones incrementales.

Causa: Los segmentos internos de FTS5 pueden fragmentarse después de muchas actualizaciones pequeñas.

Solución: Reconstruye y optimiza:

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

Timeout de MCP

Síntoma: La herramienta de AI informa que el servidor MCP agotó el tiempo de espera.

Causa: La primera consulta activa la carga del modelo (inicialización diferida), lo que tarda de 2 a 5 segundos. El timeout predeterminado de MCP de la herramienta de AI puede ser más corto.

Solución: Precarga el modelo al iniciar el servidor:

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

Bloqueos de archivo en SQLite

Síntoma: Errores SQLITE_BUSY o SQLITE_LOCKED.

Causa: Varios procesos escriben en la base de datos al mismo tiempo. WAL mode permite lecturas concurrentes, pero solo un escritor.

Solución: Asegúrate de que solo un proceso (el indexer) escriba en la base de datos. El servidor MCP y los hooks solo deberían leer. Si necesitas escrituras concurrentes, usa WAL mode y configura un busy timeout:

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

sqlite-vec no carga

Síntoma: La búsqueda vectorial está deshabilitada; el retriever se ejecuta solo en modo BM25.

Causa: La extensión sqlite-vec no está instalada, no se encuentra en la ruta de bibliotecas o es incompatible con la versión de SQLite.

Solución:

# Install via pip
pip install sqlite-vec

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

Verifica que la extensión cargue:

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

Problemas de memoria en vaults grandes

Síntoma: Errores de falta de memoria durante el reindexado completo de un vault grande (50,000+ notas).

Causa: El tamaño de batch de embeddings es demasiado grande, o todo el contenido de los archivos se carga en memoria al mismo tiempo.

Solución: Reduce el tamaño de batch y procesa los archivos de forma incremental:

BATCH_SIZE = 32  # Reduce from 64

También asegúrate de que el indexer procese los archivos de uno en uno (leyendo, haciendo chunking y generando embeddings para cada archivo antes de pasar al siguiente) en lugar de cargar todos los archivos en memoria.


Guía de migración

Desde Apple Notes

  1. Exporta Apple Notes con la opción “Export All” (macOS) o usa una herramienta de migración como apple-notes-liberator
  2. Convierte las exportaciones HTML a markdown con markdownify o pandoc
  3. Mueve los archivos convertidos a la carpeta 00-inbox/ de tu vault
  4. Revisa y agrega frontmatter a cada nota
  5. Mueve las notas a las carpetas de dominio correspondientes

Desde Notion

  1. Exporta desde Notion: Settings → Export → Markdown & CSV
  2. Descomprime la exportación en la carpeta 00-inbox/ de tu vault
  3. Corrige los artefactos markdown específicos de Notion:
  4. Notion usa - [ ] para listas de verificación; son markdown estándar
  5. Notion incluye tablas de propiedades como HTML; conviértelas a frontmatter YAML
  6. Notion incrusta imágenes como rutas relativas; copia las imágenes a tu carpeta de adjuntos
  7. Agrega frontmatter estándar (type, domain, tags)
  8. Reemplaza los enlaces de páginas de Notion con wiki-links de Obsidian

Desde Google Docs

  1. Usa Google Takeout para exportar todos los documentos
  2. Convierte archivos .docx a markdown: pandoc -f docx -t markdown input.docx -o output.md
  3. Conversión por lotes: for f in *.docx; do pandoc -f docx -t markdown "$f" -o "${f%.docx}.md"; done
  4. Mueve al vault, agrega frontmatter y organiza en carpetas

Desde markdown plano (sin Obsidian)

Si ya tienes un directorio de archivos markdown:

  1. Abre el directorio como un vault de Obsidian (Obsidian → Open Vault → Open folder)
  2. Agrega .obsidian/ a .gitignore si el directorio está bajo control de versiones
  3. Crea plantillas de frontmatter y aplícalas a los archivos existentes
  4. Empieza a enlazar notas con [[wiki-links]] mientras lees y organizas
  5. Ejecuta el indexer de inmediato: el sistema de recuperación funciona desde el primer día

Desde otro sistema de recuperación

Si estás migrando desde otro sistema de embeddings/búsqueda:

  1. No intentes migrar vectores. Distintos modelos producen espacios vectoriales incompatibles. Ejecuta un reindexado completo con el nuevo modelo.
  2. Migra el contenido, no el índice. Los archivos del vault son la fuente de verdad. El índice es un artefacto derivado.
  3. Verifica después de migrar. Ejecuta 10-20 consultas cuyas respuestas conozcas y comprueba que los resultados coincidan con tus expectativas.

Registro de cambios

Fecha Cambio
2026-07-07 Correcciones de precisión. MCPVault se aclaró como su propio proyecto (npm @bitbonsai/mcpvault, repo bitbonsai/mcpvault), ahora en v0.12.1, con dos avisos de seguridad de severidad media sobre filtrado de rutas (GHSA-9c83-rr99-vfwj, GHSA-j99q-93c9-h869): el enlace anterior [^24] apuntaba al repo equivocado (MarkusPfundstein/mcp-obsidian). Se corrigió el estado de MarkusPfundstein/mcp-obsidian: está mantenido activamente (commits hasta el 15 de mayo de 2026, con search_by_tag/get_frontmatter), no “inactivo desde junio de 2025”; aún no publica releases etiquetados. Verificado contra el historial de commits de GitHub, los Security Advisories de GitHub y npm.
2026-07-06 Reestructuración editorial para mejorar la facilidad de descubrimiento: se cambió el título de “Quick Start: First AI-Connected Vault” a Configuración de Obsidian MCP (anchor #obsidian-mcp-setup) y se agregó un resumen de capacidades “Qué puede hacer Claude una vez conectado” (buscar, leer, listar, contexto formateado; límite de solo lectura con escrituras manejadas por hooks), consolidado desde la sección de arquitectura del servidor MCP. Sin datos nuevos; se actualizaron los enlaces internos.
2026-06-10 Actualización de vigencia de versión. Obsidian 1.13.1 desktop llegó al canal público (9 de junio de 2026): una mejora de UX de configuración y actualización de CodeMirror sobre 1.13.0, sin cambios importantes de AI/automatización. Las referencias de versión actual en el cuerpo pasaron de 1.13.0 a 1.13.1 (pública, 9 de junio de 2026).
2026-06-09 Actualización del ecosistema. La especificación MCP 2026-07-28 entró en Release Candidate (anunciada el 21 de mayo de 2026): la revisión más grande de MCP desde su lanzamiento, con núcleo de protocolo sin estado (elimina el handshake initialize y Mcp-Session-Id), MCP Apps (HTML renderizado por servidor en iframes con sandbox), Tasks que pasan del núcleo experimental a una extensión oficial, refuerzo de OAuth 2.0/OIDC y una política de ciclo de vida de deprecación de 12 meses (especificación final el 28 de julio de 2026); se reemplazó el encuadre especulativo de hoja de ruta “tentativamente a mediados de 2026” en la nota de evolución de la especificación MCP por el RC concreto. sqlite-vec v0.1.10-alpha (31 de marzo al 18 de mayo de 2026) agrega tipos de índice approximate-nearest-neighbor (rescore, ivf experimental, DiskANN basado en disco) más allá de KNN por fuerza bruta, marcado como próximo/experimental porque la línea 0.1.10 sigue en pre-release. Obsidian 1.13.0 desktop (early access, 28 de mayo de 2026) se actualizó como versión actual en todas las referencias del cuerpo; es una release de UX, seguridad y herramientas para desarrolladores sin nuevas capacidades de AI/automatización.
2026-06-08 Revisión de mantenimiento. Model2Vec v0.8.2 (29 de mayo de 2026) publicada: una release de mantenimiento que agrega una opción de pesos congelados para entrenamiento, además de correcciones de tokens multipalabra, una refactorización de entrenamiento y correcciones de manejo de pesos no cuantizados; se actualizó la nota al pie. Nada más reciente que la línea base existente: la última versión de Obsidian sigue siendo 1.13.0 (28 de mayo, ya documentada abajo), sqlite-vec estable sigue en v0.1.9 (v0.1.10 aún alpha) y la especificación MCP sigue siendo la revisión del 25 de noviembre de 2025. Sin cambios en el cuerpo más allá de la nota de versión de Model2Vec.
2026-05-28 Obsidian 1.13.0 desktop + 1.13.0 mobile (Catalyst early-access) publicados. Desktop: panel de configuración renovado que se abre en su propia ventana, con búsqueda integrada y navegación por teclado; las URI de Obsidian ahora muestran un cuadro de confirmación antes de ejecutar acciones; nueva advertencia antes de cargar recursos HTML desde unidades de red; se agregó búsqueda a la vista Bookmarks; manejo mejorado de imágenes en el editor; mejoras en File Explorer / Properties / Sync; numerosas correcciones para API de desarrolladores y bugs. Mobile: nueva iOS Share Sheet con ubicaciones de destino configurables; reordenamiento de pestañas desde el selector de pestañas; gestos de mantener presionado en tablets para redimensionar divisiones y barras laterales fijadas; Bases agrega un elemento de menú para redimensionar columnas en vistas de tabla; correcciones de bugs en iOS y búsqueda. Implicaciones para flujos de trabajo de AI: el cuadro de confirmación en las URI de Obsidian agrega una compuerta deliberada a integraciones MCP/agente impulsadas por URI; el menú para redimensionar columnas de Bases hace que Bases sea más usable como índice frontal del vault que consultan los agentes; el destino configurable de iOS Share Sheet hace que la ruta de captura desde iPhone (ya documentada como entrada principal) sea más rápida de conectar para pipelines de Claude/Codex.
2026-05-06 Actualización de vigencia verificada por fuentes: Smart Connections v4.5.0 trasladó las conexiones de pie de página a Core; las releases estables sqlite-vec v0.1.8/v0.1.9 actualizaron el empaquetado y el comportamiento de DELETE; Model2Vec v0.8.x actualizó componentes internos de tokenizer/persistencia y tablas de benchmarks; se corrige la cronología de Obsidian CLI de “1.12.7 introdujo CLI” a “1.12.0 introdujo CLI, 1.12.7 mejoró el empaquetado de instalación/runtime”.
2026-04-27 Ciclo de abril de Web Clipper: 1.4.0 (UI interactiva de transcripción de YouTube + Open in Reader como valor predeterminado), 1.5.0 (visor de destacados), 1.6.0 (renovación de UX de Highlighter + extractores de fuentes Defuddle 0.18 para LinkedIn/Threads/Bluesky/Discourse/Medium), 1.6.1 + 1.6.2 (correcciones de Reader y Safari). Se replantea Web Clipper como la ruta principal de entrada desde el navegador para flujos de trabajo de AI, en lugar de una mención pasajera como marcador. Sin releases de Obsidian desktop, Sync ni Bases en el periodo.
2026-04-16 Smart Connections v4.3.0 (vista de grafo, dock configurable, recuperación de block-embedding, entorno cross-plugin Substrate). Documentada la ola de plugins AI-native de abril de 2026 (Cortex, VaultSearch, LLM Wiki, Drift, EngramQuest, Hybrid Search MCP). Se marca MarkusPfundstein/mcp-obsidian como en modo mantenimiento (último commit en junio de 2025). Dataview inactivo; Bases es el sucesor para trabajo nuevo. Obsidian CLI 1.12.7 sigue siendo el puente preferido para asistentes de AI.
2026-04-01 Agregar sección de Obsidian CLI (comandos v1.12 para flujos de trabajo de AI). Agregar sección de plugins de agentes (Claudian, Agent Client). Documentar el plugin core Bases para organización del vault. Actualizar el conteo de plugins a más de 2.500. Agregar iOS Share Extension como fuente de entrada. Actualizar la matriz de compatibilidad con plugins de agentes integrados.
2026-03-30 MCPVault v0.11.0: herramienta list_all_tags, soporte para .base/.canvas, renombrado a @bitbonsai/mcpvault. Obsidian Desktop v1.12.7 incluye el binario CLI para interacciones de terminal más rápidas.
2026-03-23 Documentar sqlite-vec v0.1.7 estable: soporte de DELETE para tablas vec0, restricciones de distancia KNN para paginación. Índice DiskANN approximate nearest neighbor anunciado para una próxima release.
2026-03-07 Agregar potion-multilingual-128M (101 idiomas, mayo de 2025) a la comparación de modelos de embeddings. sqlite-vec en v0.1.7-alpha.10 (correcciones de CI/CD, sin cambios de funciones). Especificación MCP y técnicas de recuperación confirmadas como actuales.
2026-03-03 Actualizar evolución de la especificación MCP (noviembre de 2025 publicado: Streamable HTTP, .well-known, anotaciones de herramientas). Agregar fine-tuning de Model2Vec y soporte para tokenizer BPE/Unigram. Agregar tabla comparativa de servidores MCP de la comunidad. Actualizar Smart Connections a v4.
2026-03-02 Agregar potion-base-32M y potion-retrieval-32M a la comparación de modelos. Agregar sección de cuantización/reducción de dimensionalidad. Agregar nota de evolución de la especificación MCP.
2026-03-01 Release inicial

Referencias


  1. Internet Vin, “22 commands I use with Obsidian and Claude Code,” marzo de 2026, x.com/internetvin/status/2026461256677245131

  2. Nicopreme, skill de agente “Visual Explainer” con 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. Presenta RRF con k=60 como método sin parámetros para combinar listas clasificadas. 

  4. OpenAI Embeddings Pricing. text-embedding-3-small: 0,02 USD por millón de tokens. Costo estimado del vault por reindexación completa: ~0,30 USD. 

  5. van Dongen, T. et al. Model2Vec: Turn any Sentence Transformer into a Small Fast Model. arXiv, 2025. Describe el enfoque de destilación que produce embeddings estáticos a partir de sentence transformers. 

  6. potion-base-8M Model Card y Model2Vec results. Las tablas publicadas actuales reportan potion-base-8M con 51.32 Avg (All) / 51.08 Avg (MTEB), comparado con all-MiniLM-L6-v2 con 55.80 Avg (All) / 55.93 Avg (MTEB), o aproximadamente 92% de retención en la puntuación de todas las tareas. 

  7. Model Context Protocol Specification. El estándar MCP para conectar herramientas de AI con fuentes de datos. 

  8. Model2Vec Potion Models, potion-base-32M y potion-retrieval-32M. Las model cards actuales reportan potion-base-32M con 52.83 Avg (All) y potion-retrieval-32M con 35.06 en la tabla de retrieval. 

  9. Update on the Next MCP Protocol Release. La versión de noviembre de 2025 incluyó transporte Streamable HTTP, descubrimiento de URL .well-known, anotaciones estructuradas de herramientas y estandarización del nivel SDK. La próxima versión está prevista tentativamente para mediados de 2026, con operaciones async, extensiones específicas por dominio y comunicación agent-to-agent. 

  10. Model2Vec Releases. v0.4.0 (febrero de 2025): soporte de entrenamiento/fine-tuning. v0.5.0 (abril de 2025): reescritura del backend, cuantización y reducción de dimensionalidad. v0.7.0 (octubre de 2025): cuantización de vocabulario, soporte para tokenizer BPE/Unigram. v0.8.0/v0.8.1 (marzo de 2026): refactors de tokenizer y persistencia, descontinuación de Python 3.9, actualizaciones de resultados MTEB V2 y compatibilidad con rutas de Windows. v0.8.2 (29 de mayo de 2026): versión de mantenimiento que agrega una opción de pesos congelados para entrenamiento, además de correcciones de tokens multipalabra, un refactor de entrenamiento y correcciones en el manejo de pesos no cuantizados. 

  11. Smart Connections for Obsidian. Smart Connections v4: embeddings de AI local-first; la búsqueda semántica funciona offline después de la indexación inicial. 

  12. potion-multilingual-128M. Minish Lab, mayo de 2025. Modelo de embeddings estáticos para 101 idiomas, los embeddings estáticos multilingües con mejor rendimiento. Usa la misma dependencia solo de numpy que otros modelos potion. 

  13. MCPVault — bitbonsai/mcpvault. npm @bitbonsai/mcpvault, última versión v0.12.1 (publicada el 23 de junio de 2026); es un proyecto distinto de MarkusPfundstein/mcp-obsidian, no un cambio de nombre. v0.11.0 (marzo de 2026) agregó la herramienta list_all_tags para escanear frontmatter y hashtags con conteos, mejoró el manejo de carpetas con puntos y añadió soporte para archivos .base/.canvas. Dos Security Advisories de GitHub de severidad media afectan su filtro de rutas: GHSA-9c83-rr99-vfwj (los directorios restringidos se denegaban solo en la raíz del vault, no si estaban anidados) y GHSA-j99q-93c9-h869 (evasión de deny-list mediante equivalencia de mayúsculas/minúsculas y punto/espacio final): usa v0.12.1 o posterior. 

  14. sqlite-vec v0.1.7 Release. 17 de marzo de 2026. Versión estable: soporte de DELETE para tablas virtuales vec0, restricciones de distancia KNN para paginación y mejoras en fuzz testing. Se anunció indexación aproximada de vecinos más cercanos DiskANN para una versión futura. 

  15. Introduction to Bases. Plugin core de Obsidian introducido en v1.9.10. Vistas tipo base de datos (tablas, galerías, calendarios, tableros kanban) sobre archivos del vault usando propiedades de frontmatter como campos. Los archivos se guardan en formato .base

  16. Obsidian Desktop v1.12.0 Changelog y Obsidian Desktop v1.12.7 Changelog. v1.12.0 introdujo el CLI para automatización del vault desde terminal; v1.12.7 mejoró el empaquetado de instalación/runtime con un binario independiente, TUI y comportamiento de socket-file. Consulta también la documentación de CLI

  17. Claudian. Plugin de Obsidian que integra Claude Code como colaborador de AI dentro del vault. Ofrece chat en barra lateral, prompts con contexto, soporte de visión, comandos slash y modos de permisos. 

  18. Agent Client. Plugin de Obsidian que ofrece una interfaz unificada para Claude Code, Codex CLI y Gemini CLI mediante Agent Client Protocol (ACP). Soporta menciones de notas, ejecución de shell y aprobación de acciones. 

  19. Obsidian iOS Changelog. Las actualizaciones de inicios de 2026 incluyen Share Extension para guardar contenido de otras apps directamente en el vault, correcciones en los widgets Daily Note y Bookmark, y mejoras en la actualización del widget View Note. 

  20. MarkusPfundstein/mcp-obsidian. Con mantenimiento activo: commits hasta el 15 de mayo de 2026, con trabajo reciente que agrega herramientas como search_by_tag y get_frontmatter, además de cobertura de pruebas ampliada (verificado contra el historial de commits del repositorio y tools.py). Todavía no publica releases etiquetados, así que instálalo desde un commit fijado. Basado en Local-REST-API; discusiones del foro (abril de 2026) reportan una migración de la comunidad hacia el bridge CLI de primera clase de Obsidian (1.12.x) para configuraciones nuevas, pero mcp-obsidian sigue siendo una opción funcional y actualizada para despliegues existentes con REST-API. 

  21. Smart Connections v4.5.0 Release. 5 de mayo de 2026. Las conexiones del footer pasaron a ser una función Core; las versiones recientes de v4 también incluyen vistas de grafo para listas de conexiones, ubicaciones configurables para el panel de conexiones, mejor recuperación de block-embedding, estado Substrate entre plugins, correcciones de fallback de transformer y menos cálculos duplicados de conexiones. 

  22. obsidianmd/obsidian-clipper releases — fuente primaria para el mapeo entre versiones y funciones de Web Clipper. Ciclo de abril de 2026: 1.4.0 (9 de abril, UI de transcripción de YouTube + Open in Reader por defecto), 1.5.0 (15 de abril, visor de Highlights + fade-in de Reader), 1.5.1 (15 de abril, corrección de compilación de webpack), 1.6.0 (21 de abril, UX de Highlighter + Defuddle 0.18 con extractores para LinkedIn/Threads/Bluesky/Discourse/Medium), 1.6.1 (22 de abril, correcciones de outline en Reader + búsqueda de highlights), 1.6.2 (23 de abril, corrección de portapapeles en modo incrustado de Safari). También aparece en Mozilla Add-ons store y Chrome Web Store

  23. sqlite-vec v0.1.8, sqlite-vec v0.1.9, sqlite-vec v0.1.10-alpha.3 y sqlite-vec v0.1.10-alpha.4. v0.1.8 corrigió el empaquetado de npm; v0.1.9 corrigió un bug de DELETE para columnas de texto de metadata con más de 12 caracteres; v0.1.10-alpha.3 agrega soporte correcto para INSERT OR REPLACE INTO; v0.1.10-alpha.4 (18 de mayo de 2026) corrige fallas de ALTER TABLE RENAME en tablas vec0 que usan las nuevas funciones ivf/diskann y un bug de limpieza de cached statements en DiskANN. La línea 0.1.10 sigue en prerelease. 

  24. MCP 2026-07-28 Specification Release Candidate. Anunciada el 21 de mayo de 2026; la especificación final se publica el 28 de julio de 2026. La mayor revisión de MCP desde su lanzamiento: núcleo de protocolo stateless (elimina el handshake initialize y el header Mcp-Session-Id), MCP Apps (HTML renderizada por el servidor en iframes de cliente en sandbox), Tasks pasa del núcleo experimental a una extensión oficial (tasks/get, tasks/update, tasks/cancel), endurecimiento de autorización OAuth 2.0 / OIDC y una política de ciclo de vida de deprecación de funciones de 12 meses. 

  25. Obsidian Desktop v1.13.0 Changelog. Early access, 28 de mayo de 2026. Versión de UX/seguridad/herramientas para desarrolladores: panel Settings renovado que se abre en su propia ventana con búsqueda y navegación por teclado, diálogos de confirmación antes de ejecutar Obsidian URIs, un nuevo API de Settings para desarrolladores de plugins y una corrección de CLI para instalaciones flatpak. No incluye grandes capacidades nuevas de AI/automatización más allá de la superficie CLI de 1.12.x. 

  26. Obsidian Changelog. Obsidian 1.13.1 desktop llegó al canal público el 9 de junio de 2026: una mejora de UX en configuración y actualización de CodeMirror sobre 1.13.0, sin nuevas capacidades de AI/automatización. 

VAULT obsidian.md INDEXED