Obsidian MCP + recuperación híbrida: referencia de 2026
# Usa la documentación oficial de Obsidian para los conceptos básicos de la app; usa la referencia de Blake para MCP, búsqueda híbrida e indexación de un vault de IA de 16.894 archivos.
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 una bóveda de Obsidian para la IA no está en las notas en sí, sino en la capa de recuperación que permite consultarlas. Una bóveda de 16.000 archivos sin recuperación es una base de datos de solo escritura. Una bóveda de 200 archivos con búsqueda hybrid y una integración con MCP es una base de conocimiento para IA. La infraestructura de recuperación es el producto. Las notas son la materia prima.
La recuperación híbrida (hybrid retrieval) supera a la búsqueda puramente por palabras clave o puramente semántica. BM25 detecta identificadores exactos y nombres de funciones. La búsqueda vectorial detecta sinónimos y coincidencias conceptuales entre terminologías distintas. Reciprocal Rank Fusion (RRF) combina ambas sin exigir calibración de puntuaciones. 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 híbrida supera de forma consistente a cualquiera de los dos métodos usados de manera aislada.3 El análisis profundo del recuperador híbrido cubre las matemáticas de RRF, ejemplos desarrollados con números reales, análisis de modos de falla y una calculadora interactiva de fusión.
MCP da acceso directo a la bóveda a las herramientas de IA. Los servidores de Model Context Protocol (MCP) exponen el recuperador como una herramienta que Claude Code, Codex CLI, Cursor y otras herramientas de IA pueden llamar directamente. El agente consulta la bóveda, recibe resultados ordenados con atribución de fuente y usa el contexto sin cargar archivos completos. El servidor MCP es una capa delgada alrededor del motor de recuperación.
Local-first significa cero costos de API y privacidad total. Todo el stack 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. Recalcular todos los embeddings de 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 en 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 del tiempo de modificación de archivos detecta cambios. Solo los archivos modificados se vuelven a dividir en chunks y a convertir en embeddings. Una reindexación completa tarda unos 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 (ingesta, recuperación, integración) funciona con bóvedas de cualquier tamaño. Empieza con una búsqueda solo con BM25 en una bóveda pequeña. Agrega búsqueda vectorial cuando las colisiones de palabras clave se vuelvan un problema. Agrega fusión con RRF cuando necesites coincidencias exactas y semánticas. Cada capa es útil por sí sola y también 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 + IA | Por qué Obsidian para infraestructura de IA, Inicio rápido | Arquitectura de la bóveda, Arquitectura del servidor MCP |
| Ya tienes una bóveda y quieres acceso con IA | 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 |
| Contexto de equipo o empresa | Marco de decisión, Patrones de grafo de conocimiento | 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 IA
La tesis de esta guía: las bóvedas de Obsidian son el mejor sustrato para bases de conocimiento personales de IA porque son local-first, de texto plano, estructuradas como grafo y el usuario controla cada capa del stack.
Qué le da Obsidian a la IA que otras 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 necesario para leer el contenido. Cualquier herramienta que lea archivos puede leer tu bóveda. 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. La bóveda 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 crear embeddings, indexar, dividir en chunks y buscar en tus notas sin ningún servicio externo. Esto importa para la infraestructura de IA 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 por 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 la 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 bóveda como una base de datos. Templater genera notas a partir de plantillas con lógica de JavaScript. La integración con Git sincroniza tu bóveda con un repositorio. Linter impone consistencia de formato. El plugin core Bases (introducido en v1.9.10) agrega vistas similares a bases de datos —tablas, galerías, calendarios y tableros kanban— sobre los archivos de la bóveda usando propiedades de frontmatter como campos, guardadas como archivos .base.15 Estos plugins agregan estructura a la bóveda sin cambiar el formato de texto plano subyacente. 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 te encuentras con un problema de organización de bóveda o 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 wrappers de API.
Lo que un sistema de archivos por sí solo no te da
Un directorio de archivos markdown tiene la ventaja del texto plano, pero carece de tres cosas que Obsidian agrega:
-
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 crudo no proporciona.
-
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 la de 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 crudo.
-
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.
Lo que Obsidian NO hace (y lo que construyes)
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 IA. Esta guía cubre la infraestructura que construyes sobre Obsidian. La bóveda 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 simple de archivos markdown, el pipeline de recuperación funciona de la misma manera. 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.
Inicio rápido: primera bóveda conectada a IA
Esta sección conecta una bóveda a una herramienta de IA en cinco minutos. Instalarás Obsidian, crearás una bóveda, instalarás un servidor MCP y ejecutarás tu primera consulta. El inicio rápido usa un servidor MCP comunitario para obtener resultados inmediatos. Más adelante, las secciones cubren cómo crear una canalización de recuperación personalizada 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; las versiones anteriores funcionan para configuraciones solo con MCP)
- Claude Code, Codex CLI o Cursor instalado
Paso 1: Crea una bóveda
Descarga Obsidian desde obsidian.md y crea una bóveda nueva. 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 recuperador tenga contenido con el cual trabajar. Incluso 10-20 notas son suficientes 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 comunitarios ofrecen acceso inmediato a la bóveda. El ecosistema creció de forma considerable durante 2025-2026. Entre las actualizaciones recientes destacadas está MCPVault v0.11.0 (marzo de 2026), que agregó list_all_tags para escanear frontmatter y hashtags con conteos, mejoró el manejo de carpetas con puntos y agregó soporte para archivos .base y .canvas.13 El paquete también cambió de nombre a @bitbonsai/mcpvault en npm.
Cambio de abril de 2026: Obsidian CLI como puente preferido: Obsidian 1.12.0 introdujo el CLI de primera clase, y el instalador público 1.12.7 (23 de marzo de 2026) incluyó el binario independiente + TUI + mejoras de archivo de socket que hicieron que los flujos de trabajo de terminal fueran más fáciles de instalar y ejecutar.16 Las herramientas comunitarias están migrando activamente del plugin Local REST API (que impulsaba
mcp-obsidian) hacia integraciones basadas en CLI porque son más rápidas y estables. El repositorioMarkusPfundstein/mcp-obsidianno ha tenido commits desde junio de 2025 y nunca ha tenido releases etiquetados: trátalo como en modo mantenimiento y prefiere servidores basados en CLI o las alternativas comunitarias más nuevas que aparecen abajo.20 Consulta la sección “Obsidian CLI para flujos de trabajo con IA” 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 de la bóveda mediante REST — modo mantenimiento, sin commits desde junio de 202520 |
| 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 | Etiquetas, gestión de frontmatter |
| Hybrid Search MCP | comunidad | STDIO | No | Servidor MCP de BM25 + búsqueda semántica + CLI. Nuevo y con mantenimiento activo a abril de 2026. |
Para el inicio rápido, la opción más sencilla es un servidor basado en archivos que lee archivos .md directamente:
npm install -g obsidian-mcp-server
Paso 3: Configura tu herramienta de IA
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 IA y haz una pregunta que las notas de tu bóveda puedan responder:
Search my Obsidian vault for notes about [topic you wrote about]
La herramienta de IA llama al servidor MCP, que busca en tu bóveda y devuelve contenido coincidente. Deberías ver resultados con rutas de archivo y fragmentos relevantes.
Lo que acabas de construir
Conectaste una base de conocimiento local a una herramienta de IA mediante un protocolo estándar. El servidor MCP lee los archivos de tu bóveda, 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 + vector search + RRF fusion) - 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 construir cada una de estas capacidades. El inicio rápido prueba el concepto. La canalización completa ofrece recuperación con calidad de producción.
Obsidian CLI para flujos de trabajo con IA
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 IA.16 El CLI actúa 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 IA
El CLI ofrece acceso programático a operaciones nativas de Obsidian que antes requerían la GUI o APIs de plugins. Para flujos de trabajo con IA, las capacidades clave son:
- Búsqueda desde scripts y hooks.
obsidian search "query"yobsidian search:context "query"ejecutan búsquedas en la bóveda desde cualquier shell script, hook o canalización de automatización. La variantesearch:contextdevuelve líneas coincidentes con contexto alrededor, útil para alimentar resultados en prompts de IA. - Automatización de notas diarias.
obsidian dailyabre o crea la nota diaria de hoy. Combinado con shell scripting, esto permite flujos de trabajo automatizados de resumen diario: un hook puede agregar resúmenes generados por IA a la nota diaria. - Creación de notas basada en plantillas.
obsidian template listyobsidian template creategeneran notas a partir de Templater o plantillas principales, lo que permite que los agentes de IA creen entradas estructuradas en la bóveda sin escribir archivos markdown directamente. - Gestión de propiedades.
obsidian property setyobsidian property getleen y escriben propiedades de frontmatter, lo que permite actualizar metadatos desde scripts sin analizar YAML. - Control de plugins.
obsidian plugin enable/disable/listgestiona 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/completeofrece acceso estructurado a tareas, útil para agentes de IA que gestionan elementos de trabajo en la bóveda.
CLI vs MCP para acceso de IA
El CLI y los servidores MCP cumplen funciones distintas y son complementarios, no competidores:
| Aspecto | Obsidian CLI | Servidor MCP |
|---|---|---|
| Llamador | Shell scripts, hooks, cron jobs | Agentes de IA (Claude Code, Codex, Cursor) |
| Protocolo | Proceso POSIX (stdin/stdout/stderr) | MCP (JSON-RPC por STDIO o HTTP) |
| Fortaleza | Operaciones nativas de Obsidian (plantillas, plugins, propiedades) | Recuperación personalizada (embeddings, BM25, RRF fusion) |
| Limitación | Sin vector search, sin canalización de embeddings | Sin acceso a operaciones internas de Obsidian |
| Ideal para | Scripts de automatización, canalizaciones de captura, acciones de hooks | Consultas de agentes de IA en tiempo real durante sesiones |
Recomendación: Usa el CLI para automatización de captura (crear notas, gestionar propiedades, ejecutar búsqueda nativa 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 rápida previa antes de recurrir al recuperador MCP completo para obtener resultados ordenados por relevancia.
Ejemplo: hook de captura 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 de Obsidian
Una categoría creciente de plugins de Obsidian integra agentes de IA para programación directamente en la interfaz de la bóveda, como alternativa a la configuración externa de servidores MCP. Estos plugins ejecutan el agente de IA dentro de la barra lateral de Obsidian, en lugar de conectarse desde una herramienta externa.
Claudian
Claudian integra Claude Code como colaborador de IA en la bóveda. El directorio de la bóveda 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 IA:
- Prompts conscientes del contexto. Adjunta automáticamente la nota enfocada, admite menciones de archivo 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 guardados en la bóveda.
- Slash commands. Crea plantillas de prompt reutilizables activadas por /command, lo que permite operaciones estandarizadas en la bóveda.
- Modos de permisos. Modos YOLO (aprobación automática), Safe (aprobar cada acción) y Plan (solo plan), con una lista de bloqueo de seguridad y confinamiento a la bóveda.
Agent Client
Agent Client incorpora Claude Code, Codex CLI y Gemini CLI en 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 Gemini CLI desde el mismo panel, alternando entre agentes según sea necesario.
- Menciones de notas. Usa @notename para incluir el contenido de una nota en los prompts, de forma similar a Claudian, pero independiente del agente.
- Ejecución de shell. Ejecuta comandos de terminal en línea dentro del chat: scripts de compilación, comandos git o cualquier operación de terminal sin salir de la conversación.
- Aprobación de acciones. Control detallado sobre lecturas de archivos, ediciones y ejecuciones de comandos.
Cuándo usar plugins de agentes frente a MCP externo
| Escenario | Plugin de agente | MCP externo |
|---|---|---|
| Escribir y editar notas de la bóveda con ayuda de IA | Mejor: el agente ve el contexto del editor | Funciona, pero sin conocimiento del editor |
| Desarrollo de código en varios repos | Limitado: restringido a la bóveda | Mejor: restringido al proyecto con sistema de archivos completo |
| Recuperación desde un corpus grande indexado | Solo búsqueda básica | Canalización completa de recuperación hybrid |
| Preguntas y respuestas rápidas sobre la bóveda durante sesiones de toma de notas | Ideal: sin cambio de contexto | Requiere cambiar a la terminal |
Recomendación: Usa plugins de agentes para flujos de trabajo centrados en la bóveda (escribir, organizar y resumir notas). Usa servidores MCP externos para flujos de trabajo de desarrollo donde el agente de IA necesita la canalización completa de recuperación y acceso a bases de código fuera de la bóveda. Ambos enfoques pueden coexistir: ejecuta Claudian dentro de Obsidian para trabajar con notas y Claude Code con MCP externamente para desarrollo.
Marco de decisión: Obsidian frente a alternativas
No todos los casos de uso necesitan Obsidian. Esta sección muestra cuándo Obsidian es la base correcta, cuándo es excesivo y cuándo encaja mejor otra opción.
Á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 simple | CLAUDE.md |
|---|---|---|---|---|---|
| Local-first | Sí | No (nube) | Parcial (iCloud) | Sí | Sí |
| Texto plano | Sí (markdown) | No (bloques) | No (propietario) | Sí | Sí |
| Estructura de grafo | Sí (wiki-links) | Parcial (menciones) | No | No | No |
| Indexable por IA | Acceso directo a archivos | Requiere API | 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 | Totalmente | Caché de solo lectura | Parcial | Totalmente | Totalmente |
| Escala a 10K+ notas | Sí | Sí (con API) | Se degrada | Sí | No (archivo único) |
| Costo | Gratis (núcleo) | $10/mes+ | Gratis | Gratis | Gratis |
Cuándo Obsidian es excesivo
- Contexto de un solo proyecto. Si la IA solo necesita contexto sobre la base de código actual, colócalo en
CLAUDE.md,AGENTS.mdo 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 de trabajo con archivos markdown es más simple. No construyas infraestructura de recuperación para contenido efímero.
Cuándo Obsidian es la elección correcta
- Conocimiento acumulado durante meses o años. El valor se multiplica 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.
- Varios dominios en un solo corpus. Una bóveda con notas sobre programación, arquitectura, seguridad, diseño y proyectos personales se beneficia de una recuperación entre dominios que un
CLAUDE.mdespecífico de proyecto no puede ofrecer. - Contenido sensible a la privacidad. Local-first significa que la canalización 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 cuyo valor se multiplica cuando se combinan. Cada capa tiene una preocupación distinta y un modo de fallo 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 │
└─────────────────────────────────────────────────────┘
Ingesta determina qué entra en la bóveda. Sin curaduría, 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 ingesta es responsable del control de calidad en el punto de entrada. Puede ser una canalización de puntuación, una convención de etiquetas o un proceso de revisión manual: cualquier mecanismo que garantice que la bóveda contenga contenido que valga la pena recuperar.
Recuperación hace que la bóveda se pueda consultar. Este es el motor: dividir notas en unidades de búsqueda mediante chunking, incrustar chunks en un espacio vectorial, indexar para búsqueda por palabras clave y semántica, y fusionar resultados con RRF. La capa de recuperación transforma un directorio de archivos en una base de conocimiento consultable. Sin esta capa, la bóveda se puede navegar mediante exploración manual y búsqueda básica, pero no es accesible programáticamente para herramientas de IA.
Integración conecta la capa de recuperación con herramientas de IA. 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 integración es la interfaz entre la base de conocimiento y los agentes de IA que la consumen.
Las capas están desacopladas por diseño. La canalización de puntuación de ingesta no sabe nada sobre embeddings. El recuperador 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 permite mejorar cualquier capa de forma independiente. Reemplaza el modelo de embedding sin cambiar la canalización de ingesta. Agrega una nueva capacidad de MCP sin modificar el recuperador. 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 la 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 deberían indexarse: Todo lo que contenga prosa en markdown: proyectos, áreas, recursos, señales y notas diarias.
Carpetas que deberían excluirse de la indexación: Templates (contienen variables de marcador de posición, no contenido), attachments (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 coincidentes. 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 recuperador 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 BM25type— Permite consultas filtradas por tipo (“muéstrame solo MOCs” o “solo señales”)tags— Se indexa en el contexto de encabezados de FTS5 con un peso de 0,3, lo que aporta coincidencias de palabras clave incluso cuando el cuerpo usa una terminología distinta
Campos opcionales, pero valiosos:
domain— Permite consultas acotadas por dominio (“busca solo notas de seguridad”)source— Atribución para contenido capturado; el recuperador puede incluir URL de origen en los resultadosstatus— Permite excluir notas archivadas o en borrador de la búsqueda activa
Convenciones de chunking
El recuperador divide en chunks en los límites de encabezados H2 (##). Esto significa que la estructura de tus notas afecta directamente la granularidad de 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 se pueden buscar 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 con toda la nota por igual.
Regla general: 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 sin procesar de artículos completos sin anotación. El recuperador indexa las palabras clave del artículo original, lo que diluye tu bóveda con contenido que no escribiste. Agrega un resumen, extrae los puntos clave o enlaza la URL de origen en su lugar.
- Capturas de pantalla sin descripción textual. El recuperador 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 aportar valor recuperable.
Ecosistema de plugins para flujos de trabajo de IA
Los plugins de Obsidian que mejoran la calidad de una bóveda para la recuperación de IA se dividen en tres categorías: estructurales (imponen consistencia), de consulta (exponen metadatos) 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 con la recuperación, pero sí te ayuda a identificar vacíos en la cobertura de tu bóveda y a encontrar notas que necesitan actualizarse.
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. Aplica 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: impone 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)
Git integration. Control de versiones para tu bóveda. Registra cambios con el tiempo, sincroniza entre máquinas y permite recuperar eliminaciones accidentales. Git también proporciona datos de mtime que el indexador usa para detectar cambios incrementales.
Plugins que ayudan a la indexación
Smart Connections. Un plugin de Obsidian que ofrece búsqueda semántica con IA 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 las búsquedas funcionan completamente offline, sin llamadas a API.11 v4.5.0 (5 de mayo de 2026) incorpora las conexiones de pie de página a Smart Connections Core, de modo 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 configurables para el dock, 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 atienden casos de uso distintos: Smart Connections para descubrimiento dentro del editor y el recuperador externo para integración con herramientas de IA mediante MCP.
Plugins AI-native lanzados en abril de 2026. Una ola de nuevos plugins de la comunidad apunta directamente al flujo de trabajo 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 para agentes, no solo como un almacén de notas |
| VaultSearch | 7 de abril | Búsqueda hybrid local-first: BM25 + semántica + fuzzy (solapamiento directo con el stack 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 estilo VS Code para edición de Obsidian con IA; orientado a flujos de trabajo de 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 (todavía nuevo) | Servidor MCP + CLI con BM25 + búsqueda semántica, creado específicamente para asistentes de IA |
Trata esto como un área emergente: es probable que varios de estos plugins se consoliden o sean absorbidos por Smart Connections / el núcleo de Obsidian durante los próximos trimestres. Si vas a elegir uno hoy, 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 histórico de consultas para Obsidian) 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. Los metadatos consistentes mejoran 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 el renderizado de Kanban, no para la recuperación de prosa. El chunker produce fragmentos de títulos de tarjetas y metadatos que no generan buenos embeddings. Excluye los tableros Kanban del índice.
Calendar. Crea notas diarias con contenido mínimo (a menudo solo un encabezado con la fecha). Las notas vacías o casi vacías producen chunks de baja calidad. Si usas notas diarias, escribe contenido sustancial en ellas o excluye del índice la carpeta de notas diarias.
Configuración de plugins que importa
File recovery → Enabled. Protege contra la eliminación accidental de notas. No está directamente relacionado con la recuperación, pero es crítico para una base de conocimiento de la que dependes.
Strict line breaks → Disabled. Los saltos de línea estándar de Markdown (doble línea nueva para un párrafo) producen chunks más limpios que el modo estricto de Obsidian (una sola línea nueva para <br>).
Default new file location → Designated folder. 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 del triage.
Wiki-link format → Shortest path when possible. 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 la recuperación, el tamaño del índice, la velocidad de generación de embeddings y las dependencias en tiempo de ejecución. Esta sección 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 de palabras estáticos (sin capas de atención)
Model2Vec destila el conocimiento de un sentence transformer en embeddings de tokens estáticos. En lugar de ejecutar capas de atención sobre la entrada (como hacen BERT, MiniLM y otros modelos transformer), Model2Vec produce vectores mediante el 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 computación secuencial.
En la página actual de resultados de Model2Vec, potion-base-8M alcanza alrededor del 92 % del puntaje de all-MiniLM-L6-v2 en todas las tareas (51,32 frente a 55,80), mientras se mantiene órdenes de magnitud más rápido.6 La brecha de calidad restante es la contrapartida de sus ventajas en velocidad y simplicidad. Para fragmentos breves de Markdown (un promedio de 200 a 400 palabras en una bóveda típica), la diferencia de calidad es menos marcada que en documentos más largos, porque ambos modelos convergen en representaciones similares para texto corto 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 de embeddings no tiene costo cuando el recuperador opera en modo de respaldo solo 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 toolchain. La función _activate_venv() agrega los 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 indexador envía fragmentos a embed_batch() en lugar de generar embeddings 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 (recuperación) | Estático optimizado para recuperación |
| potion-multilingual-128M | 256 | ~500 MB | 300x | — | Bóvedas 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/D | 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 un puntaje general de 52,83 en todas las tareas (aproximadamente 3 % más alto 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 órdenes de magnitud mayor que la de los modelos transformer.
Elige potion-retrieval-32M cuando tu caso de uso principal sea la recuperación (como lo es la búsqueda en la bóveda). Esta variante está ajustada a partir de potion-base-32M específicamente para tareas de recuperación, con un puntaje de 35,06 en la tabla de benchmarks de recuperación de Model2Vec, frente a 32,67 para potion-base-32M.8 La contrapartida es que está optimizada para recuperación en lugar de calidad de embeddings de propósito general.
Elige potion-multilingual-128M cuando tu bóveda 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 otros modelos potion.12 El archivo del modelo más grande (~500 MB) es la contrapartida de su capacidad multilingüe. Ú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 recuperación 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 menos 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 posible de recuperación local y aceptes una indexación más lenta. Los vectores de 768 dimensiones casi 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 contrapartidas aceptables. El API produce embeddings de la mayor calidad, pero introduce una dependencia de la nube, un costo por token ($0,02 por millón de tokens) y envía tu contenido a los servidores de OpenAI.
Mantén 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+ permite cargar modelos con precisión y dimensiones reducidas.8 Esto resulta ú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 recuperación casi idéntica con una fracción de la huella de memoria. La reducción de dimensionalidad sigue un truncamiento al estilo Matryoshka: las primeras N dimensiones contienen la mayor parte de la información. Reducir de 256 a 128 dimensiones divide a la mitad el almacenamiento de vectores con una pérdida mínima de calidad para la recuperación de textos breves.
Model2Vec v0.8.x actualiza los componentes internos de tokenización y persistencia, deja obsoleto el soporte para Python 3.9 y actualiza los resultados publicados a las tablas MTEB más recientes. Fija la versión o prueba model2vec antes de actualizar un indexador de producción, porque las actualizaciones de la biblioteca pueden cambiar las rutas de carga del modelo aunque el nombre del modelo de embeddings siga siendo el mismo.10
Ajuste fino de embeddings específicos de la bóveda
Model2Vec v0.4.0+ permite entrenar modelos de clasificación personalizados sobre embeddings estáticos, v0.7.0 agrega cuantización de vocabulario y pooling configurable para la destilación, y v0.8.x refactoriza el comportamiento de tokenización y persistencia.10 Esto es relevante para bóvedas con vocabulario especializado (notas médicas, referencias legales, jerga de dominio específico), donde los modelos potion predeterminados podrían no captar 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 las bóvedas, el potion-base-8M predeterminado produce una calidad de recuperación suficiente. El ajuste fino solo vale la pena cuando la recuperación omite de forma consistente conexiones específicas del dominio que un modelo de propósito general no puede captar.
Seguimiento del hash del modelo
El indexador almacena un hash derivado del nombre del modelo y del tamaño del vocabulario. Si cambias el modelo de embeddings, el indexador detecta la incompatibilidad 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 modelos distintos en la misma base de datos, lo que produciría puntajes 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 recuperador vuelve al modo solo BM25. El modelo se almacena localmente en caché después de la primera descarga.
Incompatibilidad de dimensiones. Si cambias de modelo sin vaciar la base de datos, los vectores almacenados tienen una dimensión distinta a la de los nuevos embeddings. El indexador detecta esto mediante el hash del modelo y activa una reindexación completa. Si la verificación del hash falla (modelo personalizado sin un hash adecuado), sqlite-vec producirá un error en las consultas KNN con dimensiones incompatibles.
Presión de memoria en bóvedas grandes. Generar embeddings para más de 50.000 fragmentos en un solo lote puede consumir mucha memoria. El indexador procesa en lotes de 64 para limitar el uso máximo 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 ofrece 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 BM25 destaca y sus modos de falla específicos.
Tabla virtual de 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 haga referencia directa a la tabla chunks en lugar de almacenar una copia duplicada del texto. Esto reduce a la mitad el requisito de almacenamiento, pero implica que FTS5 debe sincronizarse manualmente cuando se insertan, actualizan o eliminan fragmentos.
Columnas. Se indexan tres columnas:
- chunk_text — El contenido principal de cada fragmento (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 ordena documentos según la frecuencia de términos, la frecuencia inversa de documentos y la normalización por 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 un 30%
Estos pesos son ajustables. Si tu bóveda tiene encabezados descriptivos que predicen bien 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 semánticamente relacionado, pero podría posicionar la coincidencia exacta por debajo de una discusión conceptual.
Cuándo falla BM25
BM25 falla en consultas que usan una terminología distinta a la del contenido almacenado:
- Consulta: “cómo manejar fallas de autenticación” → La bóveda contiene notas sobre “recuperación de errores de inicio de sesión” y “manejo de vencimiento de sesión”. BM25 no coincide porque las palabras clave son diferentes.
- Consulta: “cuál es la mejor forma de gestionar el estado” → La bóveda contiene notas sobre “patrones de store de Redux” y “proveedores de contexto”. BM25 no las encuentra porque “gestión del estado” está expresado mediante nombres de tecnologías específicas.
BM25 también falla con colisión de palabras clave a escala. En una bóveda 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 inútiles en la práctica: el ranking no puede determinar qué nota de “configuración” es relevante para la consulta actual.
Tokenizer de FTS5
FTS5 usa el tokenizer unicode61 por defecto, que maneja texto ASCII y Unicode. Para bóvedas con mucho 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 separa por límites de palabra, lo que funciona mal en idiomas que no usan espacios entre palabras. El tokenizer trigram divide cada tres 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 la nota hasta el 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 asigna 1:1 a la tabla chunks, lo que permite hacer joins entre los resultados vectoriales y los metadatos de chunk.
Pipeline de embeddings
El pipeline fluye 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 un embedding de la consulta de entrada y luego encuentra los K chunks 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 una 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 páginas anteriores.14 Las versiones estables posteriores v0.1.8 y v0.1.9 son versiones de empaquetado y corrección de errores de DELETE, no versiones 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
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 recortar en Python, lo que reduce el uso de memoria para 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 borrar 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 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: “how to handle authentication failures” → Encuentra notas sobre “login error recovery” (mismo espacio semántico, distintas palabras clave)
- Consulta: “what patterns exist for caching” → Encuentra notas sobre “memoization”, “Redis TTL strategies” y “HTTP cache headers” (conceptos relacionados, terminología diversa)
- Consulta: “approaches to testing asynchronous code” → Encuentra notas sobre “pytest-asyncio fixtures”, “mock event loops” y “async test patterns” (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 “fusion algorithms” y “rank merging”, pero puede posicionar la definición real de la función por debajo de discusiones conceptuales - Consulta:
PostToolUse→ Devuelve notas sobre “tool lifecycle hooks” y “post-execution handlers” 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 se representa de forma distinta a una explicación en prosa sobre code review.
Degradación gradual
Si sqlite-vec no se puede cargar (extensión faltante, plataforma incompatible, biblioteca dañada), el retriever recurre 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 comprueba vec_available antes de intentar consultas vectoriales. Cuando está desactivado, todas las búsquedas usan solo BM25, y el paso de fusión RRF se omite.
Reciprocal Rank Fusion (RRF)
RRF combina dos listas ordenadas 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 rankings editables, presets de escenarios y un explorador visual de arquitectura, consulta el análisis profundo del recuperador hybrid.
El algoritmo
RRF asigna a cada documento un puntaje basado únicamente en su posición en el ranking 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 ranking del documento, basado en 1, dentro de la lista de resultados i
- weight_i es un multiplicador opcional por lista (valor predeterminado: 1.0)
Los documentos que se posicionan bien 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 alternativas
La combinación lineal ponderada requiere calibrar los puntajes de BM25 frente a las distancias de cosine similarity. Los puntajes de BM25 no tienen límite superior y escalan con el tamaño del corpus. Las distancias de cosine similarity están acotadas [0, 2]. Combinarlas requiere normalización, y los parámetros de normalización dependen del dataset. RRF usa solo las posiciones del ranking, que siempre son enteros que empiezan en 1, independientemente del método de puntuación.
Los modelos de fusión aprendidos requieren datos de entrenamiento etiquetados: pares de relevancia consulta-documento. 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 ubica review-aggregator.py en la posición 3 (coincidencias exactas de palabras clave en “review”, “aggregator” y “disagreements”), pero coloca dos archivos de configuración más arriba (coinciden con “review” de forma más prominente). La búsqueda vectorial ubica el mismo chunk en la posición 1 (coincidencia semántica con resolución de conflictos). Después de la fusión RRF:
| Chunk | 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 chunks que se posicionan bien en ambas listas suben hasta la parte superior. Los chunks que solo aparecen en una lista obtienen un puntaje de una sola fuente y quedan por debajo de los resultados con doble ranking. La lógica real de resolución de desacuerdos gana porque ambos métodos la encontraron: BM25 mediante palabras clave y la búsqueda vectorial mediante semántica.
Para ver el seguimiento completo paso a paso con los cálculos de RRF por ranking, 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 posicionados frente a los resultados en posiciones más bajas:
- k más bajo (por ejemplo, 10): Los resultados mejor posicionados dominan. El rank 1 obtiene 1/11 = 0.091, el rank 10 obtiene 1/20 = 0.050 (diferencia de 1.8x). Es útil cuando confías en que los rankers individuales acierten con el resultado principal.
- k predeterminado (60): Equilibrado. El rank 1 obtiene 1/61 = 0.0164, el rank 10 obtiene 1/70 = 0.0143 (diferencia de 1.15x). Las diferencias de ranking 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 en el ranking. El rank 1 obtiene 1/201, el rank 10 obtiene 1/210: casi idénticos. Úsalo cuando los rankers individuales generen rankings ruidosos, pero el acuerdo entre listas sea confiable.
Empieza con k=60. El artículo original de RRF encontró que este valor era robusto en diversos datasets TREC. Ajústalo solo después de medir casos fallidos en tu propia distribución de consultas.
Desempate
Cuando dos chunks tienen puntajes RRF idénticos (algo raro, pero posible con el mismo ranking en una lista y sin aparición en la otra), resuelve los empates así:
- Prefiere chunks que aparezcan en ambas listas sobre chunks que aparezcan solo en una
- Entre chunks que aparezcan en ambas listas, prefiere el que tenga el ranking combinado más bajo
- Entre chunks que aparezcan solo en una lista, prefiere el que tenga el ranking más bajo en esa lista
El pipeline de recuperación completo
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 extremo a extremo
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 Search API
class HybridRetriever:
def search(self, query, limit=10, max_tokens=4000,
bm25_weight=1.0, vec_weight=1.0):
"""
Search the vault using hybrid BM25 + vector retrieval.
Args:
query: Search query text
limit: Maximum results to return
max_tokens: Token budget for total result text
bm25_weight: Weight for BM25 results in RRF
vec_weight: Weight for vector results in RRF
Returns:
List of SearchResult with file_path, section,
chunk_text, rrf_score, bm25_rank, vec_rank
"""
# BM25 search
bm25_results = self._bm25_search(query, limit=30)
# Vector search (if available)
if self.index.vec_available:
vec_results = self._vector_search(query, limit=30)
fused = self._rrf_fuse(
bm25_results, vec_results,
bm25_weight, vec_weight,
)
else:
fused = bm25_results # BM25-only fallback
# Token budget truncation
results = []
token_count = 0
for r in fused[:limit]:
chunk_tokens = len(r["chunk_text"]) // 4
if token_count + chunk_tokens > max_tokens:
break
results.append(r)
token_count += chunk_tokens
return results
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 por 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 gradual
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, 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 extremo a extremo (p50) | 23ms |
| Tiempo de reindexación completa | ~4 minutos |
| Tiempo de reindexación incremental | <10 segundos |
| Modelo de embeddings | 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 tiempo de modificación de archivos
El indexador almacena mtime_ns (tiempo de modificación del archivo en nanosegundos) para cada chunk en la tabla chunks. En una ejecución incremental, el indexador:
- Escanea la bóveda en busca de todos los archivos
.mden carpetas permitidas - Lee el
mtime_nsde cada archivo desde el sistema de archivos - Lo compara con el
mtime_nsalmacenado en la base de datos - Identifica tres categorías:
- Archivos nuevos: la ruta existe en el sistema de archivos, pero no en la base de datos
- Archivos modificados: la ruta existe en ambos, pero
mtime_nsdifiere - Archivos eliminados: la ruta existe en la base de datos, pero no en el sistema de archivos
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 del archivo) sería más confiable que comparar mtime: detectaría casos donde un archivo fue tocado sin cambiar (por ejemplo, un git checkout que restaura el mtime original). Sin embargo, el hashing requiere leer todos los archivos en cada ejecución incremental. Para 16.894 archivos, leer el contenido de los archivos toma entre 2 y 3 segundos. Leer mtimes desde el sistema de archivos toma <100ms.
El trade-off: la comparación de mtime ocasionalmente activa reindexaciones innecesarias de archivos sin cambios (falsos positivos), pero nunca omite cambios reales. Los falsos positivos cuestan unas pocas llamadas adicionales de embeddings 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 a partir de sqlite-vec v0.1.7, con una corrección de bug 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 workarounds (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 la bóveda para detectar cambios en archivos (comparación de mtime) 2. Elimina fragmentos de archivos borrados 3. Vuelve a fragmentar y generar embeddings para los archivos modificados 4. Inserta nuevos fragmentos para archivos nuevos 5. Sincroniza el índice FTS5
Duración típica: <10 segundos para las ediciones de un día en una bóveda 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 (columnas nuevas, índices modificados) - Después de una corrupción de 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 (fragmentos, vectores, entradas FTS5) 2. Escanea toda la bóveda 3. Fragmenta todos los archivos 4. Genera embeddings para todos los fragmentos 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 fragmentos existentes de un archivo antes de insertar los nuevos, así que volver a ejecutar la indexación incremental en 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 de la bóveda, 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:
- Comprobación del hash del modelo. Compara el hash del modelo almacenado con el modelo actual. Si son distintos, cambia automáticamente al modo de reindexación completa y advierte al usuario.
- Escaneo de archivos. Recorre las carpetas permitidas, recopila rutas de archivos y mtimes.
- Detección de cambios. Compara contra los datos almacenados.
- Procesamiento por lotes. Vuelve a fragmentar y generar embeddings para los archivos modificados en lotes de 64.
- Reporte de progreso. Imprime el conteo de archivos procesados y el tiempo transcurrido.
- 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 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 en el í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 API se fragmentarían, se convertirían en embeddings y se almacenarían en la base de datos. Una búsqueda de “authentication” devolvería el fragmento que contiene secretos reales. Peor aún, si el recuperador entrega resultados a una herramienta de IA mediante MCP, los secretos aparecen en la ventana de contexto de la IA y potencialmente en los logs de la herramienta.
Filtrado basado en patrones
El filtro de credenciales se ejecuta en cada fragmento antes de almacenarlo, y compara 25 patrones específicos de proveedores más patrones genéricos:
Patrones específicos de proveedores:
| Patrón | Ejemplo | Regex |
|---|---|---|
| Clave API de OpenAI | sk-... |
sk-[a-zA-Z0-9_-]{20,} |
| Clave 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:
-
Filtrar antes de generar embeddings. El texto limpio es lo que se convierte en embeddings. La representación vectorial nunca codifica patrones de credenciales. Una consulta de “clave API” devuelve notas que hablan sobre la gestión de claves API, no notas que contienen claves reales.
-
Reemplazar, no eliminar. El token
[REDACTED:pattern-name]conserva el contexto semántico del texto que lo rodea. El embedding captura que “había algo parecido a una credencial aquí” sin codificar la credencial en sí. -
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 una exclusión general por ruta. El filtro de credenciales ofrece una depuración precisa dentro de los archivos indexados. Ambos son necesarios:
.indexignorepara 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 bóvedas con contenido diverso, considera clasificar las notas por sensibilidad:
| Nivel | Ejemplos | ¿Indexar? | ¿Filtrar? |
|---|---|---|---|
| Público | Borradores de blog, notas técnicas | Sí | Sí |
| Interno | Planes de proyecto, decisiones de arquitectura | Sí | Sí |
| 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 multi-cliente o entornos de equipo donde la bóveda está en un servidor compartido.
{
"mcpServers": {
"obsidian": {
"url": "http://localhost:3333/mcp"
}
}
}
Recomendación: Usa STDIO para bóvedas personales. Es más simple, más seguro (sin exposición a la red) y el ciclo de vida del servidor lo gestiona la herramienta de AI. Usa HTTP solo cuando varias herramientas o varias máquinas necesiten acceso concurrente a la misma bóveda.
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 (prompts de usuario iniciados por el servidor). La versión de noviembre de 2025 incorporó Streamable HTTP como un modo de transporte de primera clase, descubrimiento de URL
.well-knownpara 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 próxima versión de la especificación (tentativamente a mediados de 2026) propone operaciones asíncronas para tareas de larga duración, extensiones de protocolo específicas por dominio para sectores como salud y finanzas, y estándares de comunicación entre agentes para flujos de trabajo multi-agente.9 Para servidores de bóvedas personales, STDIO sigue siendo el camino más simple. El transporte Streamable HTTP y el descubrimiento.well-knownbenefician principalmente a despliegues HTTP empresariales con enrutamiento multi-tenant 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 híbrida (hybrid retrieval) 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 rango 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 da formato a los resultados como un bloque de contexto apto para insertarse 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 aplicar límites estrictos:
-
Solo lectura. El servidor lee la bóveda y la base de datos del índice. No crea, modifica ni elimina notas. Las operaciones de escritura (capturar notas nuevas) las manejan hooks o skills separados, no el servidor MCP.
-
Limitado a la bóveda. El servidor solo lee archivos dentro de la ruta configurada de la bóveda. Los intentos de path traversal (
../../etc/passwd) deben rechazarse. -
Salida filtrada por credenciales. Incluso si la base de datos contiene contenido prefiltrado, aplica filtrado de credenciales en la salida como medida de defensa en profundidad.
-
Respuestas limitadas por tokens. Aplica
max_tokensen 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 principal consumidor 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 proceso secundario. Verifica que esté en ejecución:
> What tools do you have from the obsidian MCP server?
Claude Code debería mostrar 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. Hay 2 hooks relevantes para la integración con Obsidian:
Hook PreToolUse — Consulta el vault antes de que el agente procese una llamada a una herramienta. Inyecta contexto relevante automáticamente.
#!/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 resultados significativos de herramientas de vuelta al vault para recuperarlos en el futuro.
#!/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 una API de Python que los hooks y las 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 insights de vuelta al vault:
/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 del vault en comandos con nombre. Los profesionales han creado bibliotecas de comandos específicos para Obsidian que tratan el vault tanto como una fuente de lectura como un destino de escritura.
Escaneo de señales. Un comando /scan-intel consulta fuentes externas, puntúa los hallazgos según intereses personales de investigación y escribe las señales que califican como notas del vault con frontmatter:
/scan-intel --topics "agent infrastructure, security" --lookback 7d
El comando obtiene datos de fuentes configuradas (arXiv, HN, RSS), aplica un modelo de puntuación (relevancia, accionabilidad, profundidad, autoridad) y escribe las señales aprobadas en carpetas del vault específicas por tema. El vault se convierte en el consumidor final de un pipeline de inteligencia automatizado.
Bitácora del capitán. Un comando /captains-log agrega la actividad diaria de git en todos los repositorios, escribe una entrada estructurada de diario en el vault e incluye decisiones tomadas, descubrimientos e hilos abiertos:
/captains-log
El comando extrae el historial de commits desde GitHub, agrupa por repositorio y lo formatea como una entrada narrativa de diario. Con el tiempo, las bitácoras diarias crean un registro consultable de qué se lanzó y por qué.
Captura en Obsidian. Un comando /obsidian-capture toma un insight de la sesión actual de Claude Code y lo escribe directamente en el vault 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 del vault: crear MOCs, actualizar notas de estado de proyectos, enlazar señales relacionadas o generar resúmenes semanales a partir de bitácoras diarias acumuladas.
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 con diagramas en el vault a partir del análisis de código.2 Los comandos varían, pero la arquitectura es consistente: las skills de Claude Code como interfaz, las notas del vault como capa de almacenamiento y la infraestructura de recuperación como motor de consulta.
Gestión de la ventana de contexto
La integración debe tener en cuenta 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 fuentes. Incluye siempre la ruta del archivo y el encabezado de la sección para que el agente pueda hacer referencia a 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 herramientas. El hook PreToolUse debe inyectar contexto de forma selectiva según la herramienta que se esté llamando. Las operaciones de lectura no necesitan contexto del vault. Las operaciones 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 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 obtener instrucciones de nivel de proyecto. Incluye orientación para buscar en el vault:
## 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 frente a 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 es compatible con 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 para indicarle al agente que busque en el vault antes de empezar a trabajar.
Cursor y otras herramientas
Cursor y otras herramientas de IA compatibles con MCP pueden conectarse al mismo servidor MCP de Obsidian. Esta sección cubre la configuración para 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 curso | STDIO | UI de configuración |
| Claudian (plugin de Obsidian) | No aplica (integrado) | Claude Code CLI | configuración del plugin de Obsidian |
| Agent Client (plugin de Obsidian) | No aplica (integrado) | ACP | configuración del plugin de Obsidian |
Alternativa para herramientas sin MCP
Para las herramientas que no son compatibles con MCP, el recuperador puede envolverse como un 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
El CLI genera texto estructurado que puede pegarse manualmente en la entrada de cualquier herramienta de IA. Es menos elegante que una integración con MCP, pero funciona de forma universal.
Caché de prompts a partir de notas estructuradas
Las notas estructuradas de la bóveda pueden servir como bloques de contexto reutilizables que reducen el uso de tokens en las interacciones con IA. 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, crea previamente 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 2 señales:
- Vencimiento de TTL. Cada bloque de contexto tiene un tiempo de vida. Cuando vence el TTL, el bloque se reconstruye consultando de nuevo la bóveda.
- Detección de cambios en la bóveda. Cuando el indexador 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 iniciar la sesión. Los resultados de búsqueda dinámicos 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 preserva presupuesto para consultas específicas.
Uso de tokens antes y 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. A lo largo de 10 consultas en 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 creados previamente consumen 4.500 tokens en total. Las búsquedas adicionales suman entre 1.500 y 2.000 tokens por consulta única. A lo largo de 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 y 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 tiene protección:
# 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 las fallas |
| Listados de archivos | ls o find en el comando |
Truncar a las primeras 20 entradas + conteo |
| Stack traces | palabra clave Traceback |
Mantener el primer y último frame + mensaje de error |
| Estado de Git | modified: / new file: |
Resumir conteos por estado |
| Salida de build | warning: / error: |
Quitar 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 lanzamientos de abril de 2026 la volvió mucho más útil para flujos de trabajo de AI:22
- 1.4.0 (9 de abril): UI interactiva para transcripciones de YouTube: fija el video, recorre la transcripción, activa el desplazamiento automático y resalta la posición actual. Además, incluye una opción predeterminada “Open in Reader” que envía una captura de un clic directamente al modo Reader.
- 1.5.0–1.5.1 (15 de abril): Visor de resaltados: navega y busca resaltados capturados en toda la bóveda. Transición con fade-in hacia Reader. Reproducción/pausa de YouTube más fluida. La versión 1.5.1 corrigió una regresión de compilación de webpack.
- 1.6.0–1.6.2 (21–23 de abril): Revisión completa de la UX del resaltador con soporte móvil. Defuddle 0.18 agrega extractores específicos por fuente para LinkedIn, Threads, Bluesky, Discourse y Medium. La versión 1.6.2 corrige una regresión del portapapeles en modo integrado 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 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 investigación
- Salida de herramientas: Resultados importantes de herramientas de AI capturados mediante hooks
- Extensión Compartir de iOS: La app de Obsidian para iOS (actualizada a comienzos de 2026) incluye una Extensión Compartir que guarda contenido desde 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 createo agregar contenido a notas existentes medianteobsidian 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 | ¿Se relaciona con mis dominios activos? | Tangencial, fuera de alcance | Directamente relevante para el trabajo activo |
| Accionabilidad | ¿Puedo usar esta información? | Pura teoría, sin aplicación | Técnica o patrón específico que puedo aplicar |
| Profundidad | ¿Qué tan sustantivo 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, revisada 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 | Poner en 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-links de Obsidian codifica relaciones entre notas. Esta sección cubre la semántica de enlaces, el recorrido del grafo para expandir contexto y los anti-patrones que degradan la calidad del grafo.
Semántica de backlinks
Cada wiki-link crea una arista dirigida en el grafo. Obsidian rastrea tanto enlaces directos como backlinks:
- Enlace directo: 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 | “Se relaciona con” | “Consulta [[OAuth Token Rotation]] para ver 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:
- Coincidencia directa. Una búsqueda de “authentication overview” coincide con el MOC en sí, lo que proporciona al agente una lista curada de notas relacionadas.
- Expansión de contexto. Después de encontrar una nota específica, el recuperador puede comprobar si la nota aparece en algún MOC e incluir la estructura del MOC en los resultados, dándole 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.
Anti-patrones
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 que faltan MOCs o enlaces entre dominios.
Proliferación de etiquetas. Usar etiquetas de forma inconsistente o crear demasiadas etiquetas de granularidad fina. 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 a 20-50 etiquetas de alto nivel que se correspondan con tus carpetas de dominio.
Notas cargadas de enlaces y con poco contenido. Notas que consisten enteramente en wiki-links sin prosa. Estas notas se indexan mal porque el chunker no tiene texto para convertir en embeddings. 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 aportaría contexto útil.
Recetas de flujo de trabajo para desarrolladores
Flujos de trabajo prácticos que combinan la recuperación de la bóveda 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 recuperador 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 iniciar un proyecto o una función nueva:
- Busca en la bóveda: “¿Qué sé sobre [tecnología/patrón]?”
- Revisa los 5 resultados principales para encontrar decisiones previas y problemas conocidos
- Comprueba si existe un MOC para el dominio; si no, crea uno
- Busca modos de falla: “problemas con [tecnología]”
Depuración con búsqueda en la bóveda
Cuando encuentres un error o comportamiento inesperado:
Search my vault for [error message or symptom]
Las notas de depuración previas suelen contener la causa raíz y la solución. Esto es especialmente valioso para problemas recurrentes entre proyectos: la bóveda 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]
La bóveda devuelve decisiones previas, restricciones arquitectónicas y estándares de codificación relevantes para el código en revisión. La revisión se basa en conocimiento institucional, no solo en el diff.
Optimización del rendimiento
Esta sección cubre estrategias de optimización para distintos tamaños de bóveda y patrones de uso.
Gestión del tamaño del índice
| Tamaño de bóveda | Chunks | Tamaño de DB | Reindexación completa | 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 más de 50.000 notas, considera: - Aumentar el tamaño del lote de 64 a 128 para generar embeddings más rápido - Usar modo WAL (predeterminado) para acceso concurrente - Ejecutar la reindexación completa fuera del horario de mayor uso
Optimización de consultas
Modo WAL. El modo Write-Ahead Logging de SQLite permite lecturas concurrentes mientras el indexador escribe:
db.execute("PRAGMA journal_mode=WAL")
Esto es crítico cuando el servidor MCP maneja consultas mientras el indexador 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 cada consulta. Una sola conexión de larga duración con modo WAL 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 mapeada en memoria. El pragma mmap_size le indica a SQLite que use I/O mapeada en memoria para el archivo de base de datos. Para 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 una reindexación completa, ejecuta:
INSERT INTO chunks_fts(chunks_fts) VALUES('optimize');
Esto fusiona los segmentos b-tree internos de FTS5 y reduce la latencia de consulta en búsquedas posteriores.
Benchmarks de escalamiento
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 formateo de resultados. La latencia de red para la comunicación MCP 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 indexador incremental no se ejecutó después de agregar notas, o el mtime de un archivo no se actualizó (por ejemplo, al sincronizarlo desde otra máquina con marcas de tiempo preservadas).
Solución: Ejecuta una reindexación completa: 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: Los vectores antiguos (del modelo anterior) se están comparando con vectores de consulta nuevos. Las dimensiones o la semántica del espacio vectorial son incompatibles.
Solución: El indexador debería detectar la discrepancia del hash del modelo y activar automáticamente una reindexación completa. Si no lo hace, limpia 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 cual toma de 2 a 5 segundos. El timeout MCP predeterminado de la herramienta de AI puede ser más corto.
Solución: Precalienta 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 de SQLite
Síntoma: Errores SQLITE_BUSY o SQLITE_LOCKED.
Causa: Varios procesos escriben en la base de datos simultáneamente. El modo WAL permite lecturas concurrentes, pero solo un escritor.
Solución: Asegúrate de que solo un proceso (el indexador) escriba en la base de datos. El servidor MCP y los hooks solo deberían leer. Si necesitas escrituras concurrentes, usa modo WAL y configura un timeout de espera:
db.execute("PRAGMA busy_timeout=5000") # Wait up to 5 seconds
sqlite-vec no se carga
Síntoma: La búsqueda vectorial está deshabilitada; el recuperador 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 bóvedas grandes
Síntoma: Errores de memoria insuficiente durante la reindexación completa de una bóveda grande (más de 50.000 notas).
Causa: El tamaño de lote de embeddings es demasiado grande, o todo el contenido de los archivos se carga en memoria simultáneamente.
Solución: Reduce el tamaño de lote y procesa los archivos de forma incremental:
BATCH_SIZE = 32 # Reduce from 64
Asegúrate también de que el indexador procese los archivos de uno en uno (leyendo, dividiendo en chunks 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
- Exporta Apple Notes mediante la opción “Export All” (macOS) o usa una herramienta de migración como
apple-notes-liberator - Convierte las exportaciones HTML a markdown con
markdownifyopandoc - Mueve los archivos convertidos a la carpeta
00-inbox/de tu bóveda - Revisa cada nota y agrega frontmatter
- Mueve las notas a las carpetas de dominio correspondientes
Desde Notion
- Exporta desde Notion: Settings → Export → Markdown & CSV
- Descomprime la exportación en la carpeta
00-inbox/de tu bóveda - Corrige artefactos de markdown específicos de Notion:
- Notion usa
- [ ]para listas de verificación; son markdown estándar - Notion incluye tablas de propiedades como HTML; conviértelas a frontmatter YAML
- Notion incrusta imágenes como rutas relativas; copia las imágenes a tu carpeta de adjuntos
- Agrega frontmatter estándar (
type,domain,tags) - Reemplaza los enlaces de página de Notion con wiki-links de Obsidian
Desde Google Docs
- Usa Google Takeout para exportar todos los documentos
- Convierte archivos
.docxa markdown:pandoc -f docx -t markdown input.docx -o output.md - Conversión por lotes:
for f in *.docx; do pandoc -f docx -t markdown "$f" -o "${f%.docx}.md"; done - Mueve los archivos a la bóveda, agrega frontmatter y organízalos en carpetas
Desde Markdown plano (sin Obsidian)
Si ya tienes un directorio de archivos markdown:
- Abre el directorio como bóveda de Obsidian (Obsidian → Open Vault → Open folder)
- Agrega
.obsidian/a.gitignoresi el directorio tiene control de versiones - Crea plantillas de frontmatter y aplícalas a los archivos existentes
- Empieza a enlazar notas con
[[wiki-links]]a medida que lees y organizas - Ejecuta el indexador de inmediato: el sistema de recuperación funciona desde el primer día
Desde otro sistema de recuperación
Si migras desde un sistema distinto de embeddings/búsqueda:
- No intentes migrar vectores. Los distintos modelos producen espacios vectoriales incompatibles. Ejecuta una reindexación completa con el modelo nuevo.
- Migra el contenido, no el índice. Los archivos de la bóveda son la fuente de verdad. El índice es un artefacto derivado.
- Verifica después de la migración. Ejecuta de 10 a 20 consultas cuyas respuestas ya conozcas y verifica que los resultados coincidan con tus expectativas.
Registro de cambios
| Fecha | Cambio |
|---|---|
| 2026-05-28 | Se lanzaron Obsidian 1.13.0 desktop + 1.13.0 mobile (Catalyst early-access). Desktop: panel de configuración renovado que se abre en su propia ventana, con búsqueda integrada y navegación por teclado; los Obsidian URIs 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 developer-API y errores. 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 tabletas para cambiar el tamaño de divisiones y barras laterales fijadas; Bases agrega una opción de menú para cambiar el tamaño de columnas en vistas de tabla; correcciones de errores en iOS y búsqueda. Implicaciones para flujos de trabajo con AI: el cuadro de confirmación en Obsidian URIs agrega una barrera deliberada para integraciones de MCP/agentes impulsadas por URI; el menú para cambiar el tamaño de columnas en 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 en iPhone (ya documentada como la vía principal de entrada) sea más rápida de conectar para pipelines de Claude/Codex. |
| 2026-05-06 | Actualización de vigencia verificada con fuentes: Smart Connections v4.5.0 movió las conexiones del pie a Core; las versiones 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 tokenizador/persistencia y tablas de benchmark; se corrige la cronología de Obsidian CLI de “1.12.7 introdujo CLI” a “1.12.0 introdujo CLI, 1.12.7 mejoró la instalación/el empaquetado en tiempo de ejecución.” |
| 2026-04-27 | Ciclo de abril de Web Clipper: 1.4.0 (UI interactiva de transcripción de YouTube + Open in Reader como opción predeterminada), 1.5.0 (visor de destacados), 1.6.0 (rediseño de UX de Highlighter + extractores de fuentes Defuddle 0.18 para LinkedIn/Threads/Bluesky/Discourse/Medium), 1.6.1 + 1.6.2 (correcciones para Reader y Safari). Se replantea Web Clipper como la ruta principal de entrada desde el navegador para flujos de trabajo con AI, en lugar de una mención pasajera de marcadores. No hubo lanzamientos de Obsidian desktop, Sync ni Bases en este periodo. |
| 2026-04-16 | Smart Connections v4.3.0 (vista de grafo, dock configurable, recuperación de block-embedding, entorno Substrate entre plugins). Documenta la ola de plugins AI-native de abril de 2026 (Cortex, VaultSearch, LLM Wiki, Drift, EngramQuest, Hybrid Search MCP). Marca MarkusPfundstein/mcp-obsidian como en modo mantenimiento (último commit en junio de 2025). Dataview está 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 | Agrega sección de Obsidian CLI (comandos v1.12 para flujos de trabajo con AI). Agrega sección de plugins de agente (Claudian, Agent Client). Documenta el plugin central Bases para organización del vault. Actualiza el conteo de plugins a más de 2.500. Agrega iOS Share Extension como fuente de entrada. Actualiza 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 más rápidas en la terminal. |
| 2026-03-23 | Documenta sqlite-vec v0.1.7 estable: soporte de DELETE para tablas vec0, restricciones de distancia KNN para paginación. Se anunció el índice DiskANN de vecinos más cercanos aproximados para un próximo lanzamiento. |
| 2026-03-07 | Agrega 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 vigentes. |
| 2026-03-03 | Actualiza la evolución de la especificación MCP (lanzada en noviembre de 2025: Streamable HTTP, .well-known, anotaciones de herramientas). Agrega fine-tuning de Model2Vec y soporte de tokenizador BPE/Unigram. Agrega tabla comparativa de servidores MCP de la comunidad. Actualiza Smart Connections a v4. |
| 2026-03-02 | Agrega potion-base-32M y potion-retrieval-32M a la comparación de modelos. Agrega sección de cuantización/reducción de dimensionalidad. Agrega nota sobre la evolución de la especificación MCP. |
| 2026-03-01 | Lanzamiento inicial |
Referencias
-
Internet Vin, “22 commands I use with Obsidian and Claude Code,” marzo de 2026, x.com/internetvin/status/2026461256677245131. ↩
-
Nicopreme, skill de agente “Visual Explainer” con comandos slash, x.com/nicopreme/status/2023495040258261460. ↩
-
Cormack, G.V., Clarke, C.L.A., y Buettcher, S. Reciprocal Rank Fusion outperforms Condorcet and individual Rank Learning Methods. SIGIR, 2009. Presenta RRF con k=60 como un método sin parámetros para combinar listas clasificadas. ↩↩↩
-
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. ↩
-
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. ↩
-
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), en comparación 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. ↩
-
Model Context Protocol Specification. El estándar MCP para conectar herramientas de AI con fuentes de datos. ↩
-
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 recuperación. ↩↩↩
-
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 por niveles de SDK. La próxima versión está prevista tentativamente para mediados de 2026, con operaciones asíncronas, extensiones específicas de dominio y comunicación entre agentes. ↩↩ -
Model2Vec Releases. v0.4.0 (feb. de 2025): soporte para entrenamiento/fine-tuning. v0.5.0 (abr. de 2025): reescritura del backend, cuantización y reducción de dimensionalidad. v0.7.0 (oct. de 2025): cuantización de vocabulario y soporte para tokenizer BPE/Unigram. v0.8.0/v0.8.1 (mar. de 2026): refactorizaciones de tokenizer y persistencia, deprecación de Python 3.9, actualizaciones de resultados MTEB V2 y compatibilidad con rutas de Windows. ↩↩
-
Smart Connections for Obsidian. Smart Connections v4: embeddings de AI con enfoque local-first; la búsqueda semántica funciona sin conexión después de la indexación inicial. ↩
-
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. ↩
-
MCPVault v0.11.0. Marzo de 2026. Nueva herramienta
list_all_tagspara escanear frontmatter y hashtags con conteos. Mejora del manejo de carpetas con puntos, y soporte para archivos.basey.canvas. El paquete pasó a llamarse@bitbonsai/mcpvaulten npm. ↩ -
sqlite-vec v0.1.7 Release. 17 de marzo de 2026. Versión estable: soporte para DELETE en tablas virtuales vec0, restricciones de distancia KNN para paginación y mejoras en fuzz testing. Se anunció la indexación DiskANN de vecinos más cercanos aproximados para una versión futura. ↩↩↩
-
Introduction to Bases. Plugin principal de Obsidian introducido en v1.9.10. Vistas similares a bases 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. ↩ -
Obsidian Desktop v1.12.0 Changelog y Obsidian Desktop v1.12.7 Changelog. v1.12.0 introdujo CLI para automatización del vault desde terminal; v1.12.7 mejoró el empaquetado de instalación/ejecución con un binario independiente, TUI y comportamiento de archivo socket. Consulta también la documentación de CLI. ↩↩
-
Claudian. Plugin de Obsidian que integra Claude Code como colaborador de AI en el vault. Ofrece chat en barra lateral, prompts conscientes del contexto, soporte de visión, comandos slash y modos de permisos. ↩
-
Agent Client. Plugin de Obsidian que ofrece una interfaz unificada para Claude Code, Codex CLI y Gemini CLI mediante Agent Client Protocol (ACP). Admite menciones de notas, ejecución de shell y aprobación de acciones. ↩
-
Obsidian iOS Changelog. Las actualizaciones de principios de 2026 incluyen Share Extension para guardar contenido de otras apps directamente en el vault, correcciones de widgets Daily Note y Bookmark, y mejoras en la actualización del widget View Note. ↩
-
MarkusPfundstein/mcp-obsidian. Último commit el 28 de junio de 2025. Sin versiones etiquetadas. Las discusiones del foro (abril de 2026) reportan que la comunidad migró a una integración basada en CLI desde que Obsidian 1.12.x incorporó CLI como función de primera clase. Se conserva en esta guía por contexto histórico y para usuarios con configuraciones existentes, pero no se recomienda para implementaciones nuevas. ↩↩
-
Smart Connections v4.5.0 Release. 5 de mayo de 2026. Las conexiones en el pie pasaron a ser una función Core; las versiones v4 recientes también incluyen vistas de grafo para listas de conexiones, ubicaciones configurables del panel de conexiones, recuperación mejorada de block-embedding, estado entre plugins con Substrate, correcciones de fallback de transformer y reducción de cálculos duplicados de conexiones. ↩
-
obsidianmd/obsidian-clipper releases — fuente primaria para el mapeo versión-función de Web Clipper. Ciclo de abril de 2026: 1.4.0 (9 de abr., interfaz de transcripción de YouTube + Open in Reader como opción predeterminada), 1.5.0 (15 de abr., visor de Highlights + aparición gradual de Reader), 1.5.1 (15 de abr., corrección de compilación con webpack), 1.6.0 (21 de abr., UX de Highlighter + Defuddle 0.18 con extractores para LinkedIn/Threads/Bluesky/Discourse/Medium), 1.6.1 (22 de abr., correcciones del esquema de Reader + búsqueda de highlights), 1.6.2 (23 de abr., corrección del portapapeles en modo incrustado de Safari). También aparece en Mozilla Add-ons store y Chrome Web Store. ↩
-
sqlite-vec v0.1.8, sqlite-vec v0.1.9 y sqlite-vec v0.1.10-alpha.3. v0.1.8 corrigió el empaquetado de npm; v0.1.9 corrigió un bug de DELETE para columnas de texto de metadatos de más de 12 caracteres; v0.1.10-alpha.3 agrega soporte adecuado para
INSERT OR REPLACE INTO, pero es una versión preliminar. ↩↩↩