Ubicación del vault de ejemplo
# Obsidian como infraestructura de IA: La referencia técnica definitiva
Conclusiones clave
Ingeniería de contexto, no toma de notas. El valor de un vault de Obsidian para la IA no son las notas en sí, sino la capa de recuperación que las hace consultables. Un vault de 16.000 archivos sin recuperación es una base de datos de solo escritura. Un vault de 200 archivos con búsqueda híbrida e integración 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 supera a la búsqueda puramente por palabras clave o puramente semántica. BM25 captura identificadores exactos y nombres de funciones. La búsqueda vectorial captura sinónimos y coincidencias conceptuales entre terminología diferente. Reciprocal Rank Fusion (RRF) fusiona ambos métodos sin necesidad de calibrar puntuaciones. Ninguno de los dos métodos por sí solo cubre ambos modos de fallo. La investigación sobre el ranking de pasajes en MS MARCO confirma el patrón: la recuperación híbrida supera consistentemente a cualquiera de los métodos por separado.3 La inmersión profunda en el recuperador híbrido cubre las matemáticas de RRF, ejemplos prácticos con números reales, análisis de modos de fallo y una calculadora interactiva de fusión.
MCP da a las herramientas de IA acceso directo al vault. 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 invocar directamente. El agente consulta el vault, recibe resultados clasificados con atribución de fuente y utiliza el contexto sin cargar archivos completos. El servidor MCP es un envoltorio ligero 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. La re-generación completa de embeddings de 49.746 chunks costaría aproximadamente $0,30 a precios de API de OpenAI, pero los costos reales son la latencia, la exposición de privacidad y la dependencia de red para un sistema que debería funcionar sin conexión.4
La indexación incremental mantiene el sistema actualizado en menos de 10 segundos. La comparación del tiempo de modificación de archivos detecta cambios. Solo los archivos modificados se re-fragmentan y re-embeben. Una reindexación completa toma aproximadamente 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 cualquier tamaño de vault. Comienza con búsqueda solo por BM25 en un vault pequeño. Agrega búsqueda vectorial cuando las colisiones de palabras clave se conviertan en un problema. Agrega fusión RRF cuando necesites tanto coincidencias exactas como semánticas. Cada capa es independientemente útil e independientemente removible.
Cómo usar esta guía
Esta guía cubre el sistema completo. Tu punto de partida depende de dónde te encuentras:
| Tú eres… | Comienza aquí | Luego explora |
|---|---|---|
| Nuevo en Obsidian + IA | Por qué Obsidian para infraestructura de IA, Inicio rápido | Arquitectura del vault, Arquitectura del servidor MCP |
| Vault existente, quieres acceso de IA | Arquitectura del servidor MCP, Integración con Claude Code | Modelos de embedding, Búsqueda de texto completo |
| Construyendo un sistema de recuperación | El pipeline de recuperación completo, 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 fallo. Las secciones marcadas como Narrativa se centran en conceptos, decisiones arquitectónicas y el razonamiento detrás de las elecciones de diseño. Las secciones marcadas como Receta proporcionan flujos de trabajo paso a paso.
Por qué Obsidian para infraestructura de IA
La tesis de esta guía: los vaults de Obsidian son el mejor sustrato para bases de conocimiento personales de IA porque son local-first, texto plano, tienen estructura de grafo y el usuario controla cada capa del stack.
Lo que Obsidian ofrece a la IA y las alternativas no
Archivos markdown en 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 necesaria para leer el contenido. Cualquier herramienta que lea archivos puede leer tu vault. grep, ripgrep, pathlib de Python, SQLite FTS5 — todos funcionan directamente sobre los archivos fuente. Cuando construyes un sistema de recuperación, estás indexando archivos, no respuestas de API. El índice siempre es consistente con la fuente porque la fuente es el sistema de archivos.
Arquitectura local-first. El vault vive en tu máquina. Sin servidor, sin dependencia de sincronización en la nube, sin límites de tasa de API, sin términos de servicio que regulen cómo procesas tu propio contenido. Puedes generar embeddings, indexar, fragmentar y buscar en tus notas sin ningún servicio externo. Esto importa para la infraestructura de IA porque el pipeline de recuperación se ejecuta tan rápido como tu disco lo permita, no tan rápido como responda un endpoint de API. También importa para la privacidad: 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 curadas por humanos entre conceptos. Los embeddings vectoriales capturan similitud semántica, pero los wiki-links capturan conexiones intencionales que el autor estableció mientras reflexionaba sobre el tema. El grafo es una señal que los embeddings no pueden replicar.
Ecosistema de plugins. Obsidian cuenta con más de 2.500 plugins comunitarios (a marzo de 2026, frente a más de 1.800 a mediados de 2025). Dataview consulta tu vault como una base de datos. Templater genera notas a partir de plantillas con lógica de JavaScript. La integración con Git sincroniza tu vault con un repositorio. Linter aplica consistencia de formato. El plugin central Bases (introducido en la v1.9.10) agrega vistas tipo base de datos — tablas, galerías, calendarios y tableros kanban — sobre los archivos del vault usando propiedades de frontmatter como campos, guardados como archivos .base.27 Estos plugins agregan estructura al vault sin cambiar el formato subyacente de texto plano. El sistema de recuperación indexa la salida de estos plugins, no los plugins en sí.
Más de 5 millones de usuarios. Obsidian tiene una gran comunidad activa que produce plantillas, flujos de trabajo, plugins y documentación. Cuando encuentres un problema con la organización del vault o la configuración de plugins, es probable que alguien ya haya documentado una solución. La comunidad también produce herramientas adyacentes a Obsidian: servidores MCP, scripts de indexación, pipelines de publicación y 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 desde la Nota A a la Nota B, la Nota B muestra que la Nota A la referencia. El panel de grafo visualiza clústeres de conexiones. Esta conciencia bidireccional es metadatos que un sistema de archivos sin procesar no proporciona.
-
Vista previa en vivo con renderizado de plugins. Las consultas de Dataview, los diagramas Mermaid y los bloques de callout se renderizan en tiempo real. La experiencia de escritura es más rica que un editor de texto, mientras que el formato de almacenamiento sigue siendo texto plano. Escribes y organizas en un entorno enriquecido; el sistema de recuperación indexa el markdown sin procesar.
-
Infraestructura comunitaria. 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 tú 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, ni búsqueda vectorial, ni ranking de fusión, ni servidor MCP, ni filtrado de credenciales, ni estrategia de chunking, ni hooks de integración para herramientas externas de IA. Esta guía cubre la infraestructura que construyes sobre Obsidian. El vault es el sustrato. El pipeline de recuperación, el servidor MCP y los hooks de integración son la infraestructura.
La arquitectura descrita aquí es markdown-first, no exclusiva de Obsidian. Si usas Logseq, Foam, Dendron o un directorio simple de archivos markdown, el pipeline de recuperación funciona de manera idéntica. El fragmentador lee archivos .md. El generador de embeddings procesa cadenas de texto. El indexador 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 el recuperador indexa.
Inicio rápido: Tu primer vault conectado a IA
Esta sección conecta un vault con una herramienta de IA en cinco minutos. Instalarás Obsidian, crearás un vault, instalarás un servidor MCP y ejecutarás tu primera consulta. Este inicio rápido usa un servidor MCP de la comunidad para obtener resultados inmediatos. Las secciones posteriores cubren la construcción de un pipeline de recuperación personalizado para uso en producción.
Requisitos previos
- macOS, Linux o Windows
- Node.js 18+ (para el servidor MCP)
- Obsidian 1.12+ (para integración con CLI; versiones anteriores funcionan para configuraciones solo con MCP)
- Claude Code, Codex CLI o Cursor instalados
Paso 1: Crear un vault
Descarga Obsidian desde obsidian.md y crea un nuevo vault. Elige una ubicación que recuerdes — el servidor MCP necesita la ruta absoluta.
# Example vault location
~/Documents/knowledge-base/
Agrega algunas notas para darle al recuperador algo con lo que 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: Instalar un servidor MCP
Varios servidores MCP de la comunidad proporcionan acceso inmediato al vault. El ecosistema ha crecido significativamente durante 2025-2026. Entre las actualizaciones recientes más destacadas se encuentra MCPVault v0.11.0 (marzo 2026), que añadió list_all_tags para escanear frontmatter y hashtags con conteos, mejoró el manejo de carpetas con punto y agregó soporte para archivos .base y .canvas.25 El paquete también fue renombrado a @bitbonsai/mcpvault en npm.
| Servidor | Autor | Transporte | Requiere plugin | Función clave |
|---|---|---|---|---|
| obsidian-mcp-server | StevenStavrakis | STDIO | No | Ligero, basado en archivos |
| mcp-obsidian | MarkusPfundstein | STDIO | REST local API | CRUD completo del vault vía REST |
| obsidian-mcp-tools | jacksteamdev | STDIO | Sí (plugin) | Búsqueda semántica + Templater |
| obsidian-claude-code-mcp | iansinnott | WebSocket | Sí (plugin) | Auto-descubrimiento para Claude Code |
| obsidian-mcp-server | cyanheads | STDIO | REST local API | Tags, gestión de frontmatter |
Para el inicio rápido, la opción más simple es un servidor basado en archivos que lee directamente los .md:
npm install -g obsidian-mcp-server
Paso 3: Configura tu herramienta de IA
Claude Code — agrega a ~/.claude/settings.json:
{
"mcpServers": {
"obsidian": {
"command": "obsidian-mcp-server",
"args": ["--vault", "/absolute/path/to/your/vault"]
}
}
}
Codex CLI — agrega a .codex/config.toml:
[mcp_servers.obsidian]
command = "obsidian-mcp-server"
args = ["--vault", "/absolute/path/to/your/vault"]
Cursor — agrega 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 tus notas del vault 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 vault y devuelve contenido relevante. Deberías ver resultados con rutas de archivos y extractos pertinentes.
Lo que acabas de construir
Conectaste una base de conocimiento local a una herramienta de IA a través de un protocolo estándar. El servidor MCP lee los archivos de tu vault, realiza búsquedas básicas y devuelve resultados. Esta es la versión mínima viable.
Lo que este inicio rápido NO te proporciona: - Recuperación híbrida (BM25 + búsqueda vectorial + fusión RRF) - Búsqueda semántica basada en embeddings - Filtrado de credenciales - Indexación incremental - Inyección automática de contexto mediante hooks
El resto de esta guía cubre la construcción de cada una de estas capacidades. El inicio rápido demuestra el concepto. El pipeline completo ofrece recuperación con calidad de producción.
CLI de Obsidian 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.28 El CLI actúa como un control remoto para la interfaz gráfica de Obsidian — Obsidian debe estar en ejecución (o se iniciará automáticamente con el primer comando). Habilítalo en Configuración > General > Command line interface.
Por qué el CLI importa para la infraestructura de IA
El CLI proporciona acceso programático a operaciones nativas de Obsidian que antes requerían la interfaz gráfica 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 el vault desde cualquier script de shell, hook o pipeline de automatización. La variantesearch:contextdevuelve líneas coincidentes con contexto circundante, útil para alimentar resultados en prompts de IA. - Automatización de notas diarias.
obsidian dailyabre o crea la nota diaria de hoy. Combinado con scripting de shell, esto permite flujos de trabajo automatizados de resúmenes diarios — un hook puede añadir 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 plantillas de Templater o plantillas del núcleo, lo que permite a los agentes de IA crear entradas estructuradas en el vault sin escribir directamente archivos markdown. - Gestión de propiedades.
obsidian property setyobsidian property getleen y escriben propiedades de frontmatter, permitiendo actualizaciones de 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/completeproporciona acceso estructurado a tareas, útil para agentes de IA que administran elementos de trabajo en el vault.
CLI vs MCP para acceso con IA
El CLI y los servidores MCP cumplen roles diferentes y son complementarios, no competidores:
| Aspecto | CLI de Obsidian | Servidor MCP |
|---|---|---|
| Quién lo llama | Scripts de shell, hooks, tareas cron | Agentes de IA (Claude Code, Codex, Cursor) |
| Protocolo | Proceso POSIX (stdin/stdout/stderr) | MCP (JSON-RPC sobre STDIO o HTTP) |
| Fortaleza | Operaciones nativas de Obsidian (plantillas, plugins, propiedades) | Recuperación personalizada (embeddings, BM25, fusión RRF) |
| Limitación | Sin búsqueda vectorial, sin pipeline de embeddings | Sin acceso a operaciones internas de Obsidian |
| Ideal para | Scripts de automatización, pipelines de ingesta, acciones de hooks | Consultas de agentes de IA en tiempo real durante sesiones |
Recomendación: Usa el CLI para automatización de ingesta (crear notas, gestionar propiedades, ejecutar búsquedas nativas de Obsidian) y MCP para recuperación (búsqueda híbrida con embeddings). Un hook PreToolUse puede llamar a obsidian search:context como verificación rápida previa antes de recurrir al recuperador MCP completo para resultados clasificados.
Ejemplo: Hook de ingesta con el CLI
#!/bin/bash
# Hook: append today's signals to daily note via CLI
DATE=$(date +%Y-%m-%d)
SUMMARY="$1"
obsidian daily # ensure daily note exists
obsidian file append "Daily Notes/${DATE}.md" "## AI Summary\n${SUMMARY}"
Plugins de agentes para Obsidian
Una categoría creciente de plugins de Obsidian integra agentes de IA de codificación directamente en la interfaz del vault, proporcionando una alternativa a la configuración de servidores MCP externos. 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 un colaborador de IA en el vault. El directorio del vault se convierte en el directorio de trabajo de Claude, otorgándole capacidades agénticas completas: lectura/escritura de archivos, búsqueda, comandos bash y flujos de trabajo de múltiples pasos.29
Funciones clave para infraestructura de IA:
- Prompts con reconocimiento de contexto. Adjunta automáticamente la nota enfocada, admite menciones de archivos con @notename, exclusión basada en tags 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 almacenados en el vault.
- Slash commands. Crea plantillas de prompts reutilizables activadas con /command, lo que permite operaciones estandarizadas en el vault.
- Modos de permisos. Modos YOLO (aprobación automática), Safe (aprobar cada acción) y Plan (solo planificación), con una lista de bloqueo de seguridad y confinamiento al vault.
Agent Client
Agent Client lleva Claude Code, Codex CLI y Gemini CLI a una barra lateral unificada de Obsidian a través del Agent Client Protocol (ACP).30
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 contenido de notas en los prompts, similar a Claudian pero agnóstico al agente.
- Ejecución de shell. Ejecuta comandos de terminal directamente en el chat — scripts de build, comandos git o cualquier operación de terminal sin abandonar la conversación.
- Aprobación de acciones. Control detallado sobre lecturas de archivos, ediciones y ejecuciones de comandos.
Cuándo usar plugins de agentes vs MCP externo
| Escenario | Plugin de agente | MCP externo |
|---|---|---|
| Escribir y editar notas del vault con asistencia de IA | Mejor — el agente ve el contexto del editor | Funciona, pero sin reconocimiento del editor |
| Desarrollo de código en múltiples repositorios | Limitado — alcance restringido al vault | Mejor — alcance del proyecto con acceso completo al sistema de archivos |
| Recuperación desde un corpus grande indexado | Solo búsqueda básica | Pipeline completo de recuperación híbrida |
| Preguntas rápidas al vault durante sesiones de toma de notas | Ideal — sin cambio de contexto | Requiere cambiar a la terminal |
Recomendación: Usa plugins de agentes para flujos de trabajo centrados en el vault (escribir, organizar, resumir notas). Usa servidores MCP externos para flujos de trabajo de desarrollo donde el agente de IA necesita el pipeline completo de recuperación y acceso a repositorios de código fuera del vault. Ambos enfoques pueden coexistir — ejecuta Claudian dentro de Obsidian para trabajo con notas y Claude Code con MCP externamente para desarrollo.
Marco de decisión: Obsidian vs alternativas
No todos los casos de uso necesitan Obsidian. Esta sección mapea cuándo Obsidian es el sustrato adecuado, cuándo es excesivo y cuándo otra herramienta encaja mejor.
Árbol de decisión
INICIO: ¿Cuál es tu tipo de contenido principal?
│
├─ Datos estructurados (tablas, registros, esquemas)
│ → Usa una base de datos. SQLite, PostgreSQL o una hoja de cálculo.
│ → Obsidian es para prosa, no para datos tabulares.
│
├─ Contexto efímero (proyecto actual, notas temporales)
│ → Usa CLAUDE.md / AGENTS.md en el repositorio del proyecto.
│ → Estos viajan con el código y se reinician por proyecto.
│
├─ Wiki de equipo (documentación compartida, onboarding)
│ → Evalúa Notion, Confluence o un repositorio git compartido.
│ → Los vaults de Obsidian son primero personales. La sincronización
│ en equipo es posible pero no nativa.
│
└─ Corpus de conocimiento personal en crecimiento
│
├─ < 50 notas
│ → Una carpeta de archivos markdown + grep es suficiente.
│ → Obsidian agrega valor principalmente a través del grafo de
│ enlaces, que necesita densidad para ser útil.
│
├─ 50 - 500 notas
│ → Obsidian agrega valor. Los wiki-links crean un grafo navegable.
│ → La búsqueda solo con BM25 (FTS5) es suficiente a esta escala.
│ → Omite la búsqueda vectorial y RRF hasta que aparezcan
│ colisiones de palabras clave.
│
├─ 500 - 5,000 notas
│ → La recuperación híbrida (hybrid retrieval) cobra valor. Las
│ colisiones de palabras clave aumentan. La búsqueda semántica
│ captura consultas que BM25 no encuentra.
│ → Agrega búsqueda vectorial + fusión RRF a esta escala.
│
└─ 5,000+ notas
→ El pipeline completo es esencial. BM25 solo devuelve
demasiado ruido.
→ El filtrado de credenciales se vuelve crítico (más notas =
más secretos pegados accidentalmente).
→ La indexación incremental importa (la reindexación completa
toma minutos).
→ La integración con MCP rinde dividendos en cada
interacción con IA.
Matriz de comparación
| Criterio | Obsidian | Notion | Apple Notes | Sistema de archivos | 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 | Se requiere API | Requiere exportación | Acceso directo a archivos | Ya en contexto |
| Ecosistema de plugins | 2,500+ plugins | Integraciones | Ninguno | N/A | N/A |
| Funciona sin conexión | Completo | Solo lectura en caché | Parcial | Completo | Completo |
| 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 el código actual, ponlo en
CLAUDE.md,AGENTS.mdo documentación a nivel de proyecto. Estos archivos viajan con el repositorio y se cargan automáticamente. - Datos estructurados. Si el contenido son tablas, registros o esquemas, usa una base de datos. Las notas de Obsidian son primero prosa. Dataview puede consultar campos de frontmatter, pero una base de datos real maneja consultas estructuradas mejor.
- Investigación temporal. Si las notas se descartarán al terminar el proyecto, un directorio temporal 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
- Acumulación de conocimiento durante meses o años. El valor se acumula conforme el corpus crece. Un vault de 200 notas consultado diariamente durante seis meses aporta más valor que uno de 5,000 notas consultado una sola vez.
- Múltiples dominios en un solo corpus. Un vault que contiene notas sobre programación, arquitectura, seguridad, diseño y proyectos personales se beneficia de la recuperación entre dominios, algo que un
CLAUDE.mdespecífico de proyecto no puede ofrecer. - Contenido sensible en términos de privacidad. Local-first significa que el pipeline de recuperación nunca envía contenido a servicios externos. El vault contiene lo que tú pongas en él, incluyendo 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 se potencian al combinarse. Cada capa tiene una responsabilidad diferente y un modo de fallo distinto.
┌─────────────────────────────────────────────────────┐
│ CAPA DE INTEGRACIÓN │
│ Servidores MCP, hooks, skills, inyección de contexto │
│ Responsabilidad: entregar contexto a herramientas IA │
│ Fallo: contexto erróneo, excesivo o desactualizado │
└──────────────────────┬──────────────────────────────┘
│ consulta + resultados rankeados
┌──────────────────────┴──────────────────────────────┐
│ CAPA DE RECUPERACIÓN │
│ BM25, vector KNN, fusión RRF, token budget │
│ Responsabilidad: encontrar el contenido correcto │
│ Fallo: ranking incorrecto, resultados faltantes, │
│ consultas lentas │
└──────────────────────┬──────────────────────────────┘
│ fragmentado, embebido, indexado
┌──────────────────────┴──────────────────────────────┐
│ CAPA DE INGESTA │
│ Creación de notas, triaje de señales, organización │
│ Responsabilidad: qué entra al vault y cómo se guarda │
│ Fallo: ruido, duplicados, estructura faltante │
└─────────────────────────────────────────────────────┘
Ingesta determina qué entra al vault. Sin curación, el vault acumula ruido: capturas de pantalla de tweets, artículos copiados y pegados sin anotación, pensamientos a medio terminar sin contexto. La capa de ingesta es responsable del control de calidad en el punto de entrada. Un pipeline de puntuación, una convención de etiquetado o un proceso de revisión manual — cualquier mecanismo que asegure que el vault contiene contenido que vale la pena recuperar.
Recuperación hace que el vault sea consultable. Este es el motor: fragmentar notas en unidades de búsqueda, generar embeddings de los fragmentos en espacio vectorial, indexar para búsqueda por palabras clave y semántica, fusionar resultados con RRF. La capa de recuperación transforma un directorio de archivos en una base de conocimiento consultable. Sin esta capa, el vault es navegable 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 las herramientas de IA. Un servidor MCP expone la recuperación como una herramienta invocable. Los hooks inyectan contexto automáticamente. Los skills capturan nuevo conocimiento de vuelta al vault. 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. El pipeline 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 significa que puedes mejorar cualquier capa de forma independiente. Reemplaza el modelo de embeddings sin cambiar el pipeline de ingesta. Agrega una nueva capacidad MCP sin modificar el recuperador. Cambia las heurísticas de puntuación de señales sin tocar el índice.
Arquitectura del vault para consumo por IA
Un vault optimizado para recuperación por IA sigue convenciones distintas a uno optimizado para navegación personal. Esta sección cubre la estructura de carpetas, el esquema de notas, las convenciones de frontmatter y los patrones específicos que mejoran la calidad de recuperación.
Estructura de carpetas
Usa prefijos numéricos para las carpetas de nivel superior para crear una jerarquía organizativa predecible. Los números no implican prioridad — agrupan dominios relacionados y hacen la estructura más fácil de recorrer.
vault/
├── 00-inbox/ # Capturas sin clasificar, pendientes de triaje
├── 01-projects/ # Notas de proyectos activos
├── 02-areas/ # Áreas de responsabilidad continuas
├── 03-resources/ # Material de referencia por tema
│ ├── programming/
│ ├── security/
│ ├── ai-engineering/
│ ├── design/
│ └── devops/
├── 04-archive/ # Proyectos completados, referencias antiguas
├── 05-signals/ # Ingesta de señales con puntuación
│ ├── ai-tooling/
│ ├── security/
│ ├── systems/
│ └── ...12 carpetas de dominio
├── 06-daily/ # Notas diarias (si se usan)
├── 07-templates/ # Plantillas de notas (excluidas del índice)
├── 08-attachments/ # Imágenes, PDFs (excluidos del índice)
├── .obsidian/ # Configuración de Obsidian (excluida del índice)
└── .indexignore # Rutas a excluir del índice de recuperación
Carpetas que deben indexarse: Todo lo que contenga prosa en markdown — proyectos, áreas, recursos, señales, notas diarias.
Carpetas que deben excluirse de la indexación: Plantillas (contienen variables de marcador de posición, no contenido), archivos adjuntos (archivos binarios), configuración de Obsidian y cualquier carpeta con contenido sensible que no quieras incluir en el índice de recuperación.
El archivo .indexignore
Crea un archivo .indexignore en la raíz del vault para excluir explícitamente rutas 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 fragmentan, nunca se generan embeddings de ellos 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 filtrado y enriquecimiento de 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 requeridos 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 (“mostrar solo MOCs” o “solo señales”)tags— Se indexan en el contexto de encabezado de FTS5 con peso 0.3, proporcionando coincidencias por palabras clave incluso cuando el cuerpo usa terminología diferente
Campos opcionales pero valiosos:
domain— Permite consultas con alcance de dominio (“buscar solo en notas de seguridad”)source— Atribución para contenido capturado; el recuperador puede incluir URLs de origen en los resultadosstatus— Permite excluir notas archivadas o en borrador de la búsqueda activa
Convenciones de fragmentación (chunking)
El recuperador fragmenta en los límites de encabezados H2 (##). Esto significa que la estructura de tu nota afecta directamente la granularidad de la recuperación:
Bueno para la recuperación:
## Token Rotation Strategy
The rotation interval depends on the threat model...
## Implementation with refresh_token
The OAuth 2.0 refresh token flow requires...
## Error Handling: Expired Tokens
When a token expires mid-request...
Tres secciones H2 producen tres fragmentos (chunks) buscables de forma independiente. Cada fragmento tiene suficiente contexto para que el embedding capture su significado. Una consulta sobre “manejo de tokens expirados” coincide específicamente con el tercer fragmento.
Malo 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 fragmento grande. El embedding promedia todos los temas de la sección. Una consulta sobre cualquier subtema coincide con la nota completa por igual.
Regla general: Si una sección cubre más de un concepto, divídela en subsecciones H2. El fragmentador se encarga del resto.
Qué no incluir en las notas
Contenido que degrada la calidad de recuperación:
- Copias y pegas sin procesar de artículos completos sin anotación. El recuperador indexa las palabras clave del artículo original, diluyendo tu vault con contenido que no escribiste. En su lugar, agrega un resumen, extrae los puntos clave o enlaza a la URL de origen.
- Capturas de pantalla sin descripción textual. El recuperador indexa texto en markdown. Una imagen sin texto alternativo o descripción circundante 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 el filtrado de credenciales, el enfoque más seguro es nunca pegar secretos en las notas. En su lugar, referiérelos por nombre (“el token API de Cloudflare en
~/.env”). - Contenido autogenerado sin curación. Si una herramienta genera una nota (transcripción de reunión, highlights de Readwise, importación RSS), revísala y anótala antes de que ingrese al vault permanente. Las importaciones automáticas sin curar agregan volumen sin agregar valor recuperable.
Ecosistema de plugins para flujos de trabajo con IA
Los plugins de Obsidian que mejoran la calidad del vault para la recuperación por IA se dividen en tres categorías: estructurales (imponen consistencia), de consulta (exponen metadatos) y de sincronización (mantienen el vault actualizado).
Plugins esenciales
Dataview. Consulta tu vault como una base de datos usando campos de frontmatter. Crea índices dinámicos: “todas las notas etiquetadas como security actualizadas en los últimos 30 días” o “todas las notas de proyecto con status active.” Dataview no ayuda directamente a la recuperación, pero te ayuda a identificar vacíos en la cobertura de tu vault y encontrar notas que necesitan actualización.
TABLE type, domain, updated
FROM "03-resources"
WHERE status = "active"
SORT updated DESC
LIMIT 20
Templater. Crea notas a partir de plantillas con campos dinámicos. Asegura que cada nota nueva comience con el frontmatter correcto usando una plantilla que precargue los campos created, type y domain. Un frontmatter consistente mejora el filtrado en la 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
## Referencias
Linter. Aplica reglas de formato en todo el vault. Una jerarquía de encabezados consistente (H1 para título, H2 para secciones, H3 para subsecciones) asegura que el chunker produzca resultados predecibles. Reglas de Linter que importan para la recuperación:
- Incremento de encabezados: aplicar niveles secuenciales (no saltar de H1 a H3)
- YAML title: que coincida con el nombre del archivo
- Espacios finales: eliminar (evita artefactos de tokenización en FTS5)
- Líneas en blanco consecutivas: limitar a 1 (chunks más limpios)
Integración con Git. Control de versiones para tu vault. Rastrea cambios a lo largo del tiempo, sincroniza entre máquinas y recupera notas eliminadas por accidente. Git también proporciona datos de mtime que el indexador usa para la detección incremental de cambios.
Plugins que ayudan a la indexación
Smart Connections. Un plugin de Obsidian que ofrece búsqueda semántica potenciada por IA dentro del propio Obsidian. Smart Connections v4 crea embeddings locales por defecto — una vez que tu vault está indexado, las conexiones semánticas y las búsquedas funcionan completamente sin conexión, sin llamadas a API.23 Aunque el sistema de recuperación de esta guía es externo a Obsidian (se ejecuta como un pipeline de Python), Smart Connections resulta útil para explorar relaciones semánticas mientras escribes. Ambos sistemas indexan el mismo contenido pero sirven para casos de uso distintos: Smart Connections para descubrimiento dentro del editor, el recuperador externo para integración con herramientas de IA vía MCP.
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. Unos metadatos consistentes mejoran la precisión del filtrado en la 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 al ser dividido en chunks y convertido en embeddings. Excluye los archivos de Excalidraw del índice mediante .indexignore o filtrando por extensión de archivo.
Kanban. Almacena el estado del tablero como markdown con formato especial. El formato está diseñado para la visualización 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 la carpeta de notas diarias del índice.
Configuración de plugins que importa
File recovery → Activado. 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 → Desactivado. Los saltos de línea estándar de markdown (doble salto para párrafo) producen chunks más limpios que el modo estricto de Obsidian (salto simple para <br>).
Default new file location → Carpeta designada. Dirige los archivos nuevos a 00-inbox/ para que las notas sin categorizar no contaminen las carpetas de dominio. La bandeja de entrada es un área de preparación; los archivos se mueven a carpetas de dominio después de la clasificación.
Wiki-link format → Ruta más corta cuando sea posible. Los destinos de enlace más cortos son más fáciles de resolver para el recuperador al indexar la estructura de enlaces.
Modelos de embeddings: elección y configuración
El modelo de embeddings convierte fragmentos de texto en vectores numéricos para la búsqueda semántica. La elección del modelo determina la calidad de recuperación, el tamaño del índice, la velocidad de generación de embeddings y las dependencias en tiempo de ejecución. Esta sección explica por qué potion-base-8M de Model2Vec 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 un promedio ponderado de embeddings de tokens precalculados.5 La consecuencia práctica: la velocidad de generación de embeddings es entre 50 y 500 veces más rápida que la de modelos basados en transformers, ya que no hay cómputo secuencial.
En el conjunto de benchmarks MTEB, potion-base-8M alcanza el 89% del rendimiento de all-MiniLM-L6-v2 (50,03 frente a 56,09 de promedio).6 La brecha de calidad del 11% es la contrapartida de las ventajas en velocidad y simplicidad. Para fragmentos cortos de markdown (entre 200 y 400 palabras en promedio en un vault típico), la diferencia de calidad es menos pronunciada que en documentos largos, porque ambos modelos convergen en representaciones similares para textos breves y enfocados.
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 importar el módulo. Importar el módulo del embedder no tiene costo alguno cuando el recuperador opera en modo de respaldo BM25 (por ejemplo, cuando el entorno virtual de embeddings no está instalado).
Entorno virtual aislado. El modelo se ejecuta en un venv dedicado (por ejemplo, ~/.claude/venvs/memory/) para evitar conflictos de dependencias con el resto de la cadena de herramientas. La función _activate_venv() agrega el directorio 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 alimenta 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 | 50,03 | Predeterminado: local, rápido, sin GPU |
| potion-base-32M | 256 | 120 MB | 400x | 52,46 | Mayor calidad, aún estático |
| potion-retrieval-32M | 256 | 120 MB | 400x | 36,35 (recuperación) | Estático optimizado para recuperación |
| potion-multilingual-128M | 256 | ~500 MB | 300x | — | Vaults multilingües (101 idiomas) |
| all-MiniLM-L6-v2 | 384 | 80 MB | 1x | 56,09 | Mayor calidad, aún local |
| nomic-embed-text-v1.5 | 768 | 270 MB | 0,5x | 62,28 | Mejor calidad local |
| text-embedding-3-small | 1536 | API | N/A | 62,30 | Basado en API, máxima calidad |
Elige potion-base-32M cuando quieras mejor calidad que potion-base-8M sin salir de la familia de embeddings estáticos. Lanzado en enero de 2025, utiliza un vocabulario más amplio destilado de baai/bge-base-en-v1.5, alcanzando un promedio MTEB de 52,46 (5% de mejora sobre potion-base-8M) manteniendo la misma salida de 256 dimensiones y la dependencia exclusiva de numpy.20 El archivo del modelo, 4 veces más grande, incrementa el uso de memoria, pero la velocidad de generación de embeddings sigue siendo órdenes de magnitud superior a la de los modelos transformer.
Elige potion-retrieval-32M cuando tu caso de uso principal sea la recuperación (que es exactamente lo que hace la búsqueda en un vault). Esta variante está ajustada a partir de potion-base-32M específicamente para tareas de recuperación, con una puntuación de 36,35 en los benchmarks de recuperación de MTEB frente a 33,52 del modelo base.20 El promedio general de MTEB baja a 49,73 porque el ajuste fino sacrifica rendimiento de propósito general a cambio de mejoras específicas en recuperación.
Elige potion-multilingual-128M cuando tu vault contenga notas en múltiples 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 manteniendo la misma dependencia exclusiva de numpy que los demás modelos potion.24 El archivo del modelo, más grande (~500 MB), es la contrapartida de la capacidad multilingüe. Úsalo cuando tengas notas en japonés, chino, alemán u otros idiomas además del 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 aproximadamente un 50% en comparación con vectores de 256 dimensiones. La velocidad de generación de embeddings pasa de menos de 1 minuto a unos 10 minutos para una reindexación completa de 15.000 archivos en hardware de la serie M.
Elige nomic-embed-text-v1.5 cuando necesites la mejor calidad de recuperación local posible y aceptes una indexación más lenta. Los vectores de 768 dimensiones triplican aproximadamente 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. La API produce los embeddings de mayor calidad, pero introduce una dependencia en la nube, un costo por token ($0,02 por millón de tokens) y envía tu contenido a los servidores de OpenAI.
Quédate con potion-base-8M en todos los demás casos. La ventaja de velocidad es crítica para la indexación iterativa (reindexar durante el desarrollo), la dependencia exclusiva de numpy evita la complejidad de instalar PyTorch, y los vectores de 256 dimensiones mantienen la base de datos compacta.
Cuantización y reducción de dimensionalidad
Model2Vec v0.5.0+ permite cargar modelos con precisión reducida y menos dimensiones.20 Esto resulta útil para despliegues en hardware con recursos limitados o para 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 del consumo de memoria. La reducción de dimensionalidad sigue un truncamiento estilo Matryoshka: las primeras N dimensiones contienen la mayor cantidad de información. Reducir de 256 a 128 dimensiones reduce el almacenamiento de vectores a la mitad con una pérdida de calidad mínima para la recuperación de textos cortos.
A partir de mayo de 2025, Model2Vec también admite tokenizadores BPE y Unigram (además de WordPiece), lo que amplía el conjunto de sentence transformers que pueden destilarse en modelos estáticos.22
Ajuste fino para embeddings específicos del vault
Model2Vec v0.4.0+ permite entrenar modelos de clasificación personalizados sobre embeddings estáticos, y la v0.7.0 agrega cuantización de vocabulario y pooling configurable para la destilación.22 Esto es relevante para vaults con vocabulario especializado (notas médicas, referencias legales, jerga de un dominio específico) donde los modelos potion predeterminados podrían no capturar los matices semánticos:
from model2vec import StaticModel
from model2vec.train import train_model
# Fine-tune on vault-specific data
model = StaticModel.from_pretrained("minishlab/potion-base-8M")
trained_model = train_model(model, train_texts, train_labels)
trained_model.save_pretrained("./vault-embeddings")
Para la mayoría de los vaults, potion-base-8M produce una calidad de recuperación suficiente. El ajuste fino solo vale la pena cuando la recuperación falla consistentemente en detectar conexiones específicas del dominio que un modelo de propósito general no puede capturar.
Seguimiento del hash del modelo
El indexador almacena un hash derivado del nombre del modelo y el tamaño del vocabulario. Si cambias el modelo de embeddings, el indexador detecta la discrepancia en la siguiente ejecución incremental y dispara una reindexación completa automáticamente.
def _compute_model_hash(self):
"""Hash model name + vocab size for compatibility tracking."""
key = f"{self._model_name}:{self._model.vocab_size}"
return hashlib.sha256(key.encode()).hexdigest()[:16]
Esto evita mezclar vectores de distintos modelos en la misma base de datos, lo que produciría puntuaciones de cosine similarity sin sentido.
Modos de fallo
Fallo 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 recurre al modo BM25. El modelo se almacena en caché localmente tras la primera descarga.
Discrepancia de dimensiones. Si cambias de modelo sin limpiar la base de datos, los vectores almacenados tienen una dimensión diferente a la de los nuevos embeddings. El indexador lo detecta mediante el hash del modelo y dispara una reindexación completa. Si la verificación del hash falla (modelo personalizado sin hash adecuado), sqlite-vec generará un error en las consultas KNN por discrepancia de dimensiones.
Presión de memoria en vaults grandes. Generar embeddings de más de 50.000 fragmentos en un solo lote puede consumir una cantidad significativa de 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 proporciona búsqueda de texto completo con ranking BM25. FTS5 es el componente de búsqueda por palabras clave del pipeline de recuperación híbrida (hybrid retrieval). Esta sección cubre la configuración de FTS5, cuándo BM25 sobresale y sus modos de fallo específicos.
Tabla virtual FTS5
CREATE VIRTUAL TABLE chunks_fts USING fts5(
chunk_text,
section,
heading_context,
content=chunks,
content_rowid=id
);
Modo content-sync. El parámetro content=chunks indica a FTS5 que haga referencia directa a la tabla chunks en lugar de almacenar una copia duplicada del texto. Esto reduce el almacenamiento a la mitad, pero implica que FTS5 debe sincronizarse manualmente cuando se insertan, actualizan o eliminan chunks.
Columnas. Se indexan tres columnas:
- chunk_text — El contenido principal de cada chunk (peso BM25: 1.0)
- section — El texto del encabezado H2 (peso BM25: 0.5)
- heading_context — Título de la nota, etiquetas y metadatos (peso BM25: 0.3)
Ranking BM25
BM25 clasifica los 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 contribuye más a la puntuación
- Una coincidencia en section (encabezado) contribuye la mitad
- Una coincidencia en heading_context (título, etiquetas) contribuye un 30%
Estos pesos son ajustables. Si tu vault tiene encabezados descriptivos que predicen con fuerza la calidad del contenido, aumenta el peso de section. Si tus etiquetas son completas y precisas, aumenta el peso de heading_context.
Cuándo BM25 gana
BM25 sobresale en consultas que contienen identificadores exactos:
- Nombres de funciones:
_rrf_fuse,embed_batch,get_stale_files - CLI flags:
--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 clasificar la coincidencia exacta por debajo de una discusión conceptual.
Cuándo BM25 falla
BM25 falla en consultas que utilizan terminología diferente a la del contenido almacenado:
- Consulta: “how to handle authentication failures” → El vault contiene notas sobre “login error recovery” y “session expiration handling”. BM25 no encuentra coincidencias porque las palabras clave difieren.
- Consulta: “what is the best way to manage state” → El vault contiene notas sobre “Redux store patterns” y “context providers”. BM25 falla porque “state management” se expresa a través de nombres de tecnologías específicas.
BM25 también falla con la colisión de palabras clave a escala. En un vault de 15.000 archivos, una búsqueda de “configuration” coincide con cientos de notas porque prácticamente todas las notas de proyectos mencionan configuración. Los resultados son técnicamente correctos pero prácticamente inútiles: el ranking no puede determinar qué nota de “configuration” es relevante para la consulta actual.
Tokenizador FTS5
FTS5 usa el tokenizador unicode61 por defecto, que maneja texto ASCII y Unicode. Para vaults con contenido significativo en CJK (chino, japonés, coreano), considera el tokenizador 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 tokenizador predeterminado unicode61 divide por límites de palabras, lo cual funciona mal para idiomas sin espacios entre palabras. El tokenizador trigram divide cada tres caracteres, lo que permite coincidencias de subcadenas a costa del tamaño del índice (aproximadamente 3 veces mayor).
Mantenimiento
FTS5 requiere sincronización explícita cuando la tabla subyacente chunks cambia:
# 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 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 flotantes de 256 dimensiones como datos binarios empaquetados. La columna id se mapea 1:1 con la tabla chunks, permitiendo joins entre los resultados vectoriales y los metadatos de cada fragmento.
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 de vectores
El módulo struct de Python serializa vectores flotantes para el almacenamiento en sqlite-vec:
import struct
def _serialize_vector(vec):
"""Pack float list into binary for sqlite-vec."""
return struct.pack(f"{len(vec)}f", *vec)
def _deserialize_vector(blob, dim=256):
"""Unpack binary blob to float list."""
return list(struct.unpack(f"{dim}f", blob))
Consulta KNN
Una consulta de búsqueda vectorial genera el embedding de la consulta de entrada y luego encuentra los K fragmentos más cercanos por distancia coseno:
def _vector_search(self, query_text, limit=30):
query_vec = self.embedder.embed_batch([query_text])[0]
packed = _serialize_vector(query_vec)
results = self.db.execute("""
SELECT
cv.id,
cv.distance,
c.file_path,
c.section,
c.chunk_text
FROM chunk_vecs cv
JOIN chunks c ON cv.id = c.id
WHERE embedding MATCH ?
AND k = ?
ORDER BY distance
""", [packed, limit]).fetchall()
return results
El operador MATCH en sqlite-vec realiza una búsqueda aproximada de vecinos más cercanos. El parámetro k controla cuántos resultados devolver. 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 soportan restricciones WHERE distance < ?, lo que permite paginación basada en cursor a través de grandes conjuntos de resultados sin volver a escanear páginas anteriores.26
def _paginated_vector_search(self, query_vec, page_size=20, max_distance=None):
"""Paginate through KNN results using distance constraints."""
packed = _serialize_vector(query_vec)
constraint = f"AND distance < {max_distance}" if max_distance else ""
results = self.db.execute(f"""
SELECT cv.id, cv.distance, c.file_path, c.chunk_text
FROM chunk_vecs cv
JOIN chunks c ON cv.id = c.id
WHERE embedding MATCH ?
AND k = ?
{constraint}
ORDER BY distance
""", [packed, page_size]).fetchall()
# Use last result's distance as cursor for next page
next_cursor = results[-1][1] if results else None
return results, next_cursor
Esto reemplaza el patrón anterior de obtener un k grande y recortar en Python, reduciendo el uso de memoria para consultas exploratorias sobre vaults grandes.
Soporte de DELETE en tablas vec0
sqlite-vec v0.1.7 añadió soporte nativo de DELETE para tablas virtuales vec0.26 Anteriormente, eliminar vectores requería eliminar 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 auxiliar de “IDs activos” ni reconstrucciones por lotes.
Cuándo la búsqueda vectorial gana
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, palabras clave diferentes)
- 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 a través de detalles de implementación)
Cuándo la búsqueda vectorial falla
La búsqueda vectorial tiene dificultades con identificadores exactos:
- Consulta:
_rrf_fuse→ Devuelve notas sobre “algoritmos de fusión” y “combinación de rankings”, pero puede clasificar la definición real de la función por debajo de discusiones conceptuales - Consulta:
PostToolUse→ Devuelve notas sobre “hooks del ciclo de vida de herramientas” y “manejadores post-ejecución” en lugar del nombre específico del hook
La búsqueda vectorial también tiene dificultades con datos estructurados. Los archivos de configuración JSON, los bloques YAML y los fragmentos de código producen embeddings que capturan patrones estructurales en lugar de significado semántico. Un archivo JSON con "review": true genera un embedding diferente al de una discusión en prosa sobre revisión de código.
Degradación elegante
Si sqlite-vec no logra cargarse (extensión faltante, plataforma incompatible, biblioteca corrupta), el sistema de recuperación recurre a búsqueda exclusiva 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 sistema de recuperación verifica vec_available antes de intentar consultas vectoriales. Cuando está deshabilitado, todas las búsquedas usan exclusivamente BM25 y se omite el paso de fusión RRF.
Reciprocal Rank Fusion (RRF)
RRF combina dos listas ordenadas sin necesidad de calibrar puntuaciones. Esta sección cubre el algoritmo, un rastreo detallado de consulta, el ajuste del parámetro k y por qué se elige RRF sobre otras alternativas. Para una calculadora interactiva con rangos editables, escenarios preconfigurados y un explorador visual de arquitectura, consulta la inmersión profunda en el recuperador híbrido.
El algoritmo
RRF asigna a cada documento una puntuación basada únicamente en su posición de rango en cada lista:
score(d) = Σ (weight_i / (k + rank_i))
Donde:
- k es una constante de suavizado (60, siguiendo a Cormack et al.3)
- rank_i es el rango del documento (base 1) en la lista de resultados i
- weight_i es un multiplicador opcional por lista (valor predeterminado 1.0)
Los documentos que ocupan buenas posiciones en múltiples listas reciben puntuaciones fusionadas más altas. Los documentos que aparecen en una sola lista reciben una puntuación de esa única fuente.
¿Por qué RRF sobre las alternativas?
La combinación lineal ponderada requiere calibrar las puntuaciones de BM25 contra las distancias de coseno. Las puntuaciones de BM25 no tienen límite superior y escalan con el tamaño del corpus. Las distancias de coseno están acotadas en [0, 2]. Combinarlas requiere normalización, y los parámetros de normalización dependen del conjunto de datos. RRF utiliza solo posiciones de rango, que siempre son enteros comenzando 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, estos datos de entrenamiento no existen. Necesitarías evaluar manualmente cientos de pares consulta-documento para entrenar un modelo útil. RRF funciona sin ningún dato de entrenamiento.
Los métodos de votación Condorcet (conteo Borda, método Schulze) son teóricamente elegantes pero más complejos de implementar y ajustar. El artículo original de RRF demostró que RRF supera a los métodos Condorcet en datos de evaluación TREC.3
Fusión en la práctica
Consulta: “how does the review aggregator handle disagreements”
BM25 clasifica review-aggregator.py en la posición 3 (coincidencias exactas de palabras clave en “review”, “aggregator”, “disagreements”) pero coloca dos archivos de configuración más arriba (coinciden con “review” de forma más prominente). La búsqueda vectorial clasifica el mismo fragmento en la posición 1 (coincidencia semántica en resolución de conflictos). Después de la fusión RRF:
| Fragmento | BM25 | Vec | Puntuación fusionada |
|---|---|---|---|
| review-aggregator.py “Disagreement Resolution” | #3 | #1 | 0,0323 |
| code-review-patterns.md “Multi-Reviewer” | #4 | #2 | 0,0317 |
| deliberation-config.json “Review Weights” | #1 | — | 0,0164 |
Los fragmentos que ocupan buenas posiciones en ambas listas suben a los primeros lugares. Los fragmentos que solo aparecen en una lista obtienen una puntuación de fuente única y caen por debajo de los resultados con rango dual. La lógica real de resolución de desacuerdos gana porque ambos métodos la encontraron — BM25 a través de palabras clave, la búsqueda vectorial a través de semántica.
Para el rastreo completo paso a paso con las matemáticas de RRF por rango, prueba diferentes 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 otorga a los resultados mejor clasificados frente a los de clasificación inferior:
- k bajo (p. ej., 10): Los resultados mejor clasificados dominan. El rango 1 obtiene 1/11 = 0,091, el rango 10 obtiene 1/20 = 0,050 (diferencia de 1,8x). Útil cuando confías en que los clasificadores individuales aciertan con el resultado principal.
- k predeterminado (60): Equilibrado. El rango 1 obtiene 1/61 = 0,0164, el rango 10 obtiene 1/70 = 0,0143 (diferencia de 1,15x). Las diferencias de rango se comprimen, dando más peso a aparecer en múltiples listas.
- k alto (p. ej., 200): Aparecer en ambas listas importa mucho más que la posición de rango. El rango 1 obtiene 1/201, el rango 10 obtiene 1/210 — casi idénticos. Úsalo cuando los clasificadores individuales producen rankings ruidosos pero la concordancia entre listas es confiable.
Comienza con k=60. El artículo original de RRF encontró que este valor es robusto en diversos conjuntos de datos TREC. Ajusta solo después de medir casos de falla en tu propia distribución de consultas.
Desempate
Cuando dos fragmentos tienen puntuaciones RRF idénticas (poco frecuente pero posible con el mismo rango en una lista y sin aparición en la otra), desempata de la siguiente manera:
- Prefiere fragmentos que aparecen en ambas listas sobre fragmentos que aparecen en solo una
- Entre fragmentos en ambas listas, prefiere el que tenga el rango combinado más bajo
- Entre fragmentos en solo una lista, prefiere el que tenga el rango más bajo en esa lista
El pipeline completo de recuperación
Esta sección traza una consulta desde la entrada hasta la salida a través del pipeline completo: búsqueda BM25, búsqueda vectorial, fusión RRF, truncamiento por 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.
La API de búsqueda
class HybridRetriever:
def search(self, query, limit=10, max_tokens=4000,
bm25_weight=1.0, vec_weight=1.0):
"""
Search the vault using hybrid BM25 + vector retrieval.
Args:
query: Search query text
limit: Maximum results to return
max_tokens: Token budget for total result text
bm25_weight: Weight for BM25 results in RRF
vec_weight: Weight for vector results in RRF
Returns:
List of SearchResult with file_path, section,
chunk_text, rrf_score, bm25_rank, vec_rank
"""
# BM25 search
bm25_results = self._bm25_search(query, limit=30)
# Vector search (if available)
if self.index.vec_available:
vec_results = self._vector_search(query, limit=30)
fused = self._rrf_fuse(
bm25_results, vec_results,
bm25_weight, vec_weight,
)
else:
fused = bm25_results # BM25-only fallback
# Token budget truncation
results = []
token_count = 0
for r in fused[:limit]:
chunk_tokens = len(r["chunk_text"]) // 4
if token_count + chunk_tokens > max_tokens:
break
results.append(r)
token_count += chunk_tokens
return results
Truncamiento por presupuesto de tokens
El parámetro max_tokens evita que el recuperador devuelva más contexto del que la herramienta de IA puede utilizar. La estimación usa 4 caracteres por token (una aproximación razonable para prosa en inglés). Los resultados se truncan de forma voraz: se añaden resultados en orden de relevancia hasta agotar el presupuesto.
Esta es una estrategia conservadora. Un enfoque más sofisticado consideraría puntuaciones de calidad por resultado y preferiría resultados más cortos y de mayor calidad sobre resultados más largos y de menor calidad. El enfoque voraz 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 recuperador verifica las capacidades disponibles durante la inicialización y adapta su estrategia de consulta. Un componente faltante degrada la calidad pero no provoca errores. El único fallo irrecuperable es la ausencia del archivo de base de datos.
Estadísticas de producción
Medidas en un vault de 16.894 archivos, 49.746 chunks, base de datos SQLite de 83 MB, Apple M3 Pro:
| Métrica | Valor |
|---|---|
| Total de archivos | 16.894 |
| Total de chunks | 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 por defecto | 10 |
| Presupuesto de tokens por defecto | 4.000 tokens |
Hashing de contenido y detección de cambios
El indexador necesita saber qué archivos han cambiado 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 del 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. Durante una ejecución incremental, el indexador:
- Escanea el vault en busca de todos los archivos
.mden las carpetas permitidas - Lee el
mtime_nsde cada archivo desde el sistema de archivos - Compara contra 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 el
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 la comparación por mtime —detectaría casos donde un archivo fue tocado sin modificarse (por ejemplo, un git checkout que restaura el mtime original)—. No obstante, el hashing requiere leer cada archivo en cada ejecución incremental. Para 16.894 archivos, leer el contenido toma entre 2 y 3 segundos. Leer los mtimes del sistema de archivos toma menos de 100ms.
La compensación: la comparación por mtime ocasionalmente dispara la reindexación innecesaria 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 vs 3 segundos) hace que mtime sea la opción pragmática para un sistema que se ejecuta en cada interacción con IA.
Manejo de eliminaciones
Cuando un archivo se elimina del vault, el indexador remueve 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 sentencia DELETE FROM chunk_vecs funciona de forma nativa a partir de sqlite-vec v0.1.7.26 Las versiones anteriores requerían soluciones alternativas (eliminar y recrear la tabla virtual, o mantener un conjunto externo de “IDs activos”). Si usas una versión anterior a la 0.1.7, actualiza antes de depender de eliminaciones directas.
Las tablas FTS5 con sincronización de contenido 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: Para la indexación diaria después de editar notas. Es el modo predeterminado.
Qué hace: 1. Escanea el vault en busca de cambios en archivos (comparación de mtime) 2. Elimina los chunks de archivos borrados 3. Vuelve a dividir en chunks y generar embeddings de los archivos modificados 4. Inserta nuevos chunks para archivos nuevos 5. Sincroniza el índice FTS5
Duración típica: <10 segundos para las ediciones de un día en un vault de 16.000 archivos.
python index_vault.py --incremental
Reindexación completa
Cuándo usarla: - Después de cambiar el modelo de embeddings (se detecta discrepancia en el hash del modelo) - Después de una migración de esquema (nuevas columnas, índices modificados) - Después de una corrupción de base de datos (la verificación de integridad falla) - Cuando la indexación incremental produce resultados inesperados
Qué hace: 1. Elimina todos los datos existentes (chunks, vectores, entradas FTS5) 2. Escanea el vault completo 3. Divide todos los archivos en chunks 4. Genera embeddings de todos los chunks 5. Reconstruye el índice FTS5 desde cero
Duración típica: ~4 minutos para 16.894 archivos en Apple M3 Pro.
python index_vault.py --full
Idempotencia
Ambos modos son idempotentes: ejecutar el mismo comando dos veces produce el mismo resultado. El indexador elimina los chunks existentes de un archivo antes de insertar los nuevos, por lo que una segunda ejecución de la indexación incremental sobre una base de datos ya actualizada no genera ningún cambio. Una segunda ejecución de 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 la escritura, error de disco, proceso terminado a mitad de una transacción):
# Check integrity
sqlite3 vectors.db "PRAGMA integrity_check;"
# If corruption detected, full reindex rebuilds from source files
python index_vault.py --full
La fuente de verdad siempre son los archivos del vault, no la base de datos. La base de datos es un artefacto derivado que puede reconstruirse en cualquier momento. Esta es una propiedad de diseño fundamental: nunca necesitas hacer respaldo de la base de datos.
El flag --incremental
Cuando el indexador se ejecuta con --incremental:
- Verificación del hash del modelo. Compara el hash del modelo almacenado con el del modelo actual. Si difieren, 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 dividir en chunks y generar embeddings de los archivos modificados en lotes de 64.
- Reporte de progreso. Muestra la cantidad de archivos procesados y el tiempo transcurrido.
- Apagado controlado. Maneja SIGINT finalizando el archivo en curso antes de detenerse.
Filtrado de credenciales y límites de datos
Las notas personales contienen secretos: claves API, tokens bearer, cadenas de conexión a bases de datos, claves privadas pegadas durante sesiones de depuración. El filtro de credenciales evita que estos entren al índice de recuperación.
El problema
Una nota sobre la depuración de una integración con 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 dividirían en chunks, se convertirían en embeddings y se almacenarían en la base de datos. Una búsqueda de “autenticación” devolvería el chunk que contiene secretos reales. Peor aún, si el recuperador alimenta los resultados a una herramienta de IA a través de MCP, los secretos aparecen en la ventana de contexto de la IA y potencialmente en los registros de la herramienta.
Filtrado basado en patrones
El filtro de credenciales se ejecuta en cada chunk antes del almacenamiento, comparando contra 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,} |
| Clave de acceso AWS | 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_-]+ |
| Tokens bearer | 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ñas | 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 de diseño clave:
-
Filtrar antes de generar embeddings. El texto limpio es lo que se convierte en embedding. La representación vectorial nunca codifica patrones de credenciales. Una consulta sobre “clave API” devuelve notas que discuten la gestión de claves API, no notas que contienen claves reales.
-
Reemplazar, no eliminar. El token
[REDACTED:pattern-name]preserva el contexto semántico del texto circundante. El embedding captura que “aquí había algo similar a una credencial” 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 exclusión gruesa por ruta. El filtro de credenciales proporciona depuración fina dentro de los archivos indexados. Ambos son necesarios:
.indexignorepara carpetas completas que sabes que contienen contenido sensible (notas de salud, registros financieros, documentos de carrera)- Filtro de credenciales para secretos incrustados accidentalmente en contenido que de otro modo sería indexable
Clasificación de datos
Para vaults 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 médicos | 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 IA pueden invocar. 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 soporta dos modos de transporte:
STDIO — La herramienta de IA inicia el servidor MCP como un proceso hijo y se comunica a través de stdin/stdout. Este es el modo estándar para herramientas locales. Claude Code, Codex CLI y Cursor soportan servidores MCP 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. Resulta útil para acceso remoto, configuraciones con múltiples clientes o equipos donde la bóveda se encuentra en un servidor compartido.
{
"mcpServers": {
"obsidian": {
"url": "http://localhost:3333/mcp"
}
}
}
Recomendación: Usa STDIO para bóvedas personales. Es más sencillo, más seguro (sin exposición de red) y el ciclo de vida del servidor lo gestiona la herramienta de IA. Usa HTTP solo cuando múltiples herramientas o múltiples 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 añadió autorización OAuth 2.1, salidas de herramientas estructuradas (esquemas de retorno tipados) y elicitación (prompts iniciados por el servidor hacia el usuario). La versión de noviembre de 2025 incorporó Streamable HTTP como modo de transporte de primera clase, descubrimiento de URL
.well-knownpara navegación automática de capacidades del servidor, anotaciones de herramientas estructuradas que declaran si una herramienta es de solo lectura o mutante, y un sistema de estandarización de niveles SDK.1821 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 industrias como salud y finanzas, y estándares de comunicación agente a agente para flujos de trabajo multiagente.21 Para servidores de bóvedas personales, STDIO sigue siendo la ruta más sencilla. El transporte Streamable HTTP y el descubrimiento.well-knownbenefician principalmente a despliegues HTTP empresariales con enrutamiento multitenant y balanceo de carga. Consulta el roadmap de MCP para 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 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. Ú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 coincidan con un filtro (por carpeta, etiqueta, tipo o rango de fechas). Útil para exploración cuando el agente no tiene una consulta específica.
{
"name": "obsidian_list_notes",
"description": "List notes matching filters",
"parameters": {
"folder": { "type": "string", "description": "Folder path within vault" },
"tag": { "type": "string", "description": "Tag to filter by" },
"limit": { "type": "integer", "default": 20 }
}
}
get_context — Una herramienta de conveniencia que ejecuta una búsqueda y formatea los resultados como un bloque de contexto adecuado para inyección 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 nuevas notas) se gestionan mediante hooks o skills separados, no a través del servidor MCP.
-
Alcance limitado a la bóveda. El servidor solo lee archivos dentro de la ruta de bóveda configurada. Los intentos de traversal de ruta (
../../etc/passwd) deben rechazarse. -
Salida con filtrado de credenciales. Incluso si la base de datos contiene contenido prefiltrado, aplica filtrado de credenciales en la salida como medida de defensa en profundidad.
-
Respuestas con límite de tokens. Aplica
max_tokensen todas las respuestas de herramientas para evitar que la herramienta de IA reciba bloques de contexto excesivamente grandes.
Manejo de errores
Las herramientas MCP deben devolver mensajes de error estructurados que ayuden a la herramienta de IA 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 con 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 con hooks y el patrón obsidian_bridge.py.
Configuración de MCP
Añade 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 añadir la configuración, reinicia Claude Code. El servidor MCP se iniciará como un proceso hijo. Verifica que esté en ejecución:
> What tools do you have from the obsidian MCP server?
Claude Code debería listar las herramientas disponibles (obsidian_search, obsidian_read_note, etc.).
Integración con Hooks
Los hooks extienden el comportamiento de Claude Code en puntos definidos del ciclo de vida. Dos hooks son relevantes para la integración con Obsidian:
Hook PreToolUse — Consulta la bóveda antes de que el agente procese una llamada a herramienta. Inyecta contexto relevante de forma automática.
#!/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 las salidas significativas de herramientas de vuelta a la bóveda para su recuperación futura.
#!/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 skills pueden invocar:
# 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)
El skill /capture
Un skill de Claude Code para capturar conocimientos de vuelta a la bóveda:
/capture "OAuth token rotation requires both access and refresh token invalidation"
--domain security
--tags oauth,tokens
El skill crea una nueva nota en 00-inbox/ con frontmatter adecuado y activa una reindexación incremental para que la nueva nota sea inmediatamente localizable.
Patrones de comandos personalizados
Los skills de Claude Code pueden encapsular operaciones de la bóveda en comandos con nombre. Los profesionales han construido bibliotecas de comandos específicos para Obsidian que tratan la bóveda tanto como fuente de lectura como destino de escritura.
Escaneo de señales. Un comando /scan-intel consulta fuentes externas, puntúa los hallazgos según intereses de investigación personales y escribe las señales que califican como notas de la bóveda con frontmatter:
/scan-intel --topics "agent infrastructure, security" --lookback 7d
El comando obtiene 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 temáticas de la bóveda. La bóveda 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 de diario estructurada en la bóveda e incluye decisiones tomadas, descubrimientos y temas abiertos:
/captains-log
El comando extrae el historial de commits de GitHub, agrupa por repositorio y formatea como una entrada de diario narrativa. Con el tiempo, los registros diarios crean un historial consultable de qué se entregó y por qué.
Captura en Obsidian. Un comando /obsidian-capture toma un hallazgo de la sesión actual de Claude Code y lo escribe directamente en la bóveda con los metadatos apropiados:
/obsidian-capture "SAST gates in agent loops increase security degradation"
--folder AI-Tools --tags security,agents
El patrón se extiende a cualquier operación de la bóveda: crear MOCs, actualizar notas de estado de proyectos, vincular señales relacionadas o generar resúmenes semanales a partir de los registros diarios acumulados.
Ejemplos de la comunidad. Los profesionales están publicando sus bibliotecas de comandos. Un desarrollador compartió 22 comandos personalizados de Obsidian + Claude Code que cubren revisiones diarias, planificación de proyectos, captura de investigación y flujos de trabajo de contenido.1 Otro construyó un skill “Visual Explainer” que genera notas con diagramas en la bóveda a partir del análisis de código.2 Los comandos varían, pero la arquitectura es consistente: skills de Claude Code como interfaz, notas de la bóveda como capa de almacenamiento e infraestructura de recuperación como motor de consultas.
Gestión de la ventana de contexto
La integración debe ser consciente de la ventana de contexto de Claude Code:
- Limita el contexto inyectado a 1.500-2.000 tokens por consulta. Más que esto compite con la memoria de trabajo del agente.
- Incluye atribución de fuente. Siempre incluye la ruta del archivo y el encabezado de sección para que el agente pueda referenciar la fuente.
- Trunca el texto de los fragmentos. Los fragmentos largos deben truncarse con
...en lugar de omitirse por completo. Los primeros 300-500 caracteres generalmente contienen la información clave. - No inyectes en cada llamada a herramienta. El hook PreToolUse debe inyectar contexto selectivamente según la herramienta que se esté invocando. Las operaciones de lectura no necesitan contexto de la bóveda. Las operaciones de escritura y edición se benefician de él.
Integración con Codex CLI
Codex CLI se conecta a servidores MCP a través de 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
Añade 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 en AGENTS.md
Codex CLI lee AGENTS.md para instrucciones a nivel de proyecto. Incluye orientación sobre la búsqueda en la bóveda:
## Available Tools
### Obsidian Vault (MCP: obsidian)
Use the `obsidian_search` tool to find relevant context from the knowledge base.
Search the vault when you need:
- Background on a concept or pattern
- Prior decisions or rationale
- Reference material for implementation
Example queries:
- "authentication patterns in FastAPI"
- "how does the review aggregator work"
- "sqlite-vec configuration"
Diferencias con Claude Code
| Función | Claude Code | Codex CLI |
|---|---|---|
| Configuración MCP | settings.json |
config.toml |
| Hooks | ~/.claude/hooks/ |
No soportado |
| Skills | ~/.claude/skills/ |
No soportado |
| Archivo de instrucciones | CLAUDE.md |
AGENTS.md |
| Modos de aprobación | --dangerously-skip-permissions |
suggest / auto-edit / full-auto |
Diferencia clave: Codex CLI no soporta 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 indicando al agente que busque en la bóveda antes de comenzar a trabajar.
Cursor y otras herramientas
Cursor y otras herramientas de IA que soportan MCP pueden conectarse al mismo servidor MCP de Obsidian. Esta sección cubre la configuración para las herramientas más comunes.
Cursor
Agrega lo siguiente 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 | Soporte MCP | Transporte | Ubicación de configuración |
|---|---|---|---|
| Claude Code | Completo | STDIO | ~/.claude/settings.json |
| Codex CLI | Completo | STDIO | .codex/config.toml |
| Cursor | Completo | STDIO | .cursor/mcp.json |
| Windsurf | Completo | STDIO | .windsurf/mcp.json |
| Continue.dev | Parcial | HTTP | ~/.continue/config.json |
| Zed | En desarrollo | STDIO | Interfaz de configuración |
| Claudian (plugin de Obsidian) | N/A (integrado) | Claude Code CLI | Configuración del plugin de Obsidian |
| Agent Client (plugin de Obsidian) | N/A (integrado) | ACP | Configuración del plugin de Obsidian |
Alternativa para herramientas sin MCP
Para herramientas que no soportan MCP, el recuperador puede empaquetarse como 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 la integración MCP, pero funciona de forma universal.
Caché de prompts a partir de notas estructuradas
Las notas estructuradas en la bóveda pueden servir como bloques de contexto reutilizables que reducen el consumo 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, puedes pre-construir bloques de contexto a partir de notas bien estructuradas de la bóveda y almacenarlos en caché:
# cache_keys.py
CONTEXT_BLOCKS = {
"auth-patterns": {
"vault_query": "authentication patterns implementation",
"max_tokens": 1500,
"ttl_hours": 24, # Rebuild daily
},
"api-conventions": {
"vault_query": "API design conventions REST patterns",
"max_tokens": 1000,
"ttl_hours": 168, # Rebuild weekly
},
"project-architecture": {
"vault_query": "current project architecture decisions",
"max_tokens": 2000,
"ttl_hours": 12, # Rebuild twice daily
},
}
Invalidación de caché
La invalidación de caché se basa en dos señales:
- Expiración del TTL. Cada bloque de contexto tiene un tiempo de vida (time-to-live). Cuando el TTL expira, el bloque se reconstruye volviendo a consultar 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 almacenado en caché, el bloque se invalida de inmediato.
Gestión del presupuesto de tokens
Una sesión comienza con un presupuesto total de contexto. Los bloques en caché consumen parte de ese presupuesto:
Total context budget: 8,000 tokens
├─ System prompt: 1,500 tokens
├─ Cached blocks: 3,000 tokens (pre-loaded)
├─ Dynamic search: 2,000 tokens (on-demand)
└─ Conversation: 1,500 tokens (remaining)
Los bloques en caché se cargan al inicio de la sesión. Los resultados de búsqueda dinámica llenan el presupuesto restante según cada consulta. Este enfoque híbrido proporciona al agente una base de contexto frecuentemente necesario mientras preserva presupuesto para consultas específicas.
Antes y después del uso de tokens
Sin caché: Cada consulta relevante activa una búsqueda en la bóveda, devolviendo 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 pre-construidos consumen 4.500 tokens en total. Las búsquedas adicionales agregan 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: trazas de pila, 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 ocupa 5.000 tokens, pero la señal está en 2 líneas: 200 pasadas, 1 fallida.
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 se protege:
# 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 fallidas |
| Listados de archivos | ls o find en el comando |
Truncar a las primeras 20 entradas + conteo |
| Trazas de pila | Palabra clave Traceback |
Conservar el primer y último frame + mensaje de error |
| Estado de Git | modified: / new file: |
Resumir conteos por estado |
| Salida de compilación | warning: / error: |
Eliminar líneas informativas, conservar advertencias/errores |
Pipeline de ingesta y clasificación de señales
La capa de ingesta determina qué entra en la bóveda. Sin curación, la bóveda acumula ruido. Esta sección cubre el pipeline de puntuación que enruta señales a carpetas de dominio.
Fuentes
Las señales provienen de múltiples canales:
- Feeds RSS: Blogs técnicos, avisos de seguridad, notas de versión
- Marcadores: Marcadores del navegador guardados mediante Obsidian Web Clipper o bookmarklet
- Boletines: Extractos clave de boletines por correo electrónico
- Captura manual: Notas escritas durante la lectura, conversaciones o investigación
- Salida de herramientas: Salidas significativas de herramientas de IA capturadas mediante hooks
- Extensión para compartir en iOS: La app de iOS de Obsidian (actualizada a principios de 2026) incluye una extensión para compartir que guarda contenido desde Safari, redes sociales y otras apps directamente en la bóveda sin abrir Obsidian.31 Esto crea una vía 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 ser puntuada.
- CLI de Obsidian: Los scripts de shell y hooks pueden crear notas mediante
obsidian file createo agregar contenido a notas existentes medianteobsidian file append, habilitando 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 al trabajo activo |
| Accionabilidad | ¿Puedo usar esta información? | Teoría pura, sin aplicación | Técnica o patrón específico que puedo aplicar |
| Profundidad | ¿Qué tan sustancial es el contenido? | Titulares, resumen superficial | Análisis detallado con ejemplos |
| Autoridad | ¿Qué tan creíble es la fuente? | Blog anónimo, sin verificar | Fuente primaria, 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 basándose en 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
A lo largo de 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 grafos de conocimiento
El grafo de wiki-links de Obsidian codifica las relaciones entre notas. Esta sección cubre la semántica de enlaces, el recorrido del grafo para expansión de contexto y los antipatrones que degradan la calidad del grafo.
Semántica de backlinks
Cada wiki-link crea una arista dirigida en el grafo. Obsidian rastrea tanto los enlaces directos como los backlinks:
- Enlace directo: La Nota A contiene
[[Nota B]]→ A enlaza a B - Backlink: La Nota B muestra que la Nota A la referencia
El grafo codifica diferentes tipos de relaciones según el contexto:
| Patrón de enlace | Semántica | Ejemplo |
|---|---|---|
| Enlace en línea | “Está relacionado con” | “Consulta [[OAuth Token Rotation]] para más detalles” |
| Enlace de encabezado | “Tiene subtema” | ”## Relacionados\n- [[Token Rotation]]\n- [[Session Management]]” |
| Enlace tipo etiqueta | “Está categorizado como” | ”[[type/reference]]” |
| Enlace MOC | “Es parte de” | Una nota Maps of Content que lista 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 “resumen de autenticación” coincide con el MOC en sí, proporcionando al agente una lista curada de notas relacionadas.
- Expansión de contexto. Tras encontrar una nota específica, el recuperador puede verificar si la nota aparece en algún MOC e incluir la estructura del MOC en los resultados, 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: tras encontrar los mejores resultados, expandir el contexto siguiendo los enlaces:
def expand_context(results, depth=1):
"""Follow wiki-links from top results to find related context."""
expanded = set()
for result in results:
# Parse wiki-links from chunk text
links = extract_wiki_links(result["chunk_text"])
for link_target in links:
# Resolve link to file path
target_path = resolve_wiki_link(link_target)
if target_path and target_path not in expanded:
expanded.add(target_path)
# Include target's most relevant chunk
target_chunks = get_chunks_for_file(target_path)
# ... rank and include best chunk
return results + list(expanded_results)
Esto no está implementado en el recuperador actual, pero representa una extensión natural de la estructura del grafo.
Antipatrones
Clústeres huérfanos. Grupos de notas que se enlazan entre sí pero no tienen conexiones con el resto del vault. 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 granulares. Un vault con 500 etiquetas únicas en 5.000 notas promedia 1 nota por cada 10 etiquetas — las etiquetas no resultan útiles para filtrar. Consolida a 20-50 etiquetas de alto nivel que correspondan a tus carpetas de dominio.
Notas con muchos enlaces y poco contenido. Notas que consisten enteramente en wiki-links sin prosa. Estas notas se indexan mal porque el chunker no tiene texto para generar embeddings. Agrega al menos un párrafo de contexto explicando por qué las notas enlazadas están relacionadas.
Enlaces bidireccionales para todo. No toda referencia necesita ser un wiki-link. Mencionar “OAuth” de paso no requiere [[OAuth 2.0 Overview]]. Reserva los wiki-links para relaciones intencionales y navegables donde hacer clic en el enlace proporcionaría contexto útil.
Recetas de flujos de trabajo para desarrolladores
Flujos de trabajo prácticos que combinan la recuperación del vault con tareas de desarrollo diarias.
Carga de contexto matutina
Comienza 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, dándote un repaso rápido de dónde lo dejaste. Más efectivo que releer los mensajes de commit de ayer.
Captura de investigación durante la programación
Mientras implementas una función, captura ideas 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
La idea capturada se indexa de inmediato y queda disponible para futuras recuperaciones. Con el tiempo, estas microcapturas construyen un corpus de conocimiento específico de implementación.
Inicio de proyecto
Al comenzar un nuevo proyecto o función:
- Busca en el vault: “¿Qué sé sobre [tecnología/patrón]?”
- Revisa los 5 mejores resultados en busca de decisiones previas y problemas encontrados
- Verifica si existe un MOC para el dominio; si no, crea uno
- Busca modos de fallo: “problemas con [tecnología]”
Depuración con búsqueda en el vault
Cuando encuentras un error o comportamiento inesperado:
Search my vault for [error message or symptom]
Las notas de depuración previas a menudo contienen la causa raíz y la solución. Esto es particularmente valioso para problemas recurrentes entre proyectos — el vault recuerda lo que tú olvidas.
Preparación para revisión de código
Antes de revisar un PR:
Search my vault for patterns and conventions about [module being changed]
El vault devuelve decisiones previas, restricciones arquitectónicas y estándares de código relevantes para el código bajo revisión. La revisión se informa con conocimiento institucional, no solo con el diff.
Ajuste de rendimiento
Esta sección cubre estrategias de optimización para diferentes tamaños de vault y patrones de uso.
Gestión del tamaño del índice
| Tamaño del vault | Chunks | Tamaño de BD | 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 batch size de 64 a 128 para generar embeddings más rápido - Usar el modo WAL (predeterminado) para acceso concurrente - Ejecutar la reindexación completa fuera de horario
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.
Pool de conexiones. El servidor MCP debería reutilizar conexiones a la base de datos en lugar de abrir una nueva conexión por consulta. Una sola conexión persistente con modo WAL soporta 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
E/S con mapeo de memoria. El pragma mmap_size indica a SQLite que use E/S con mapeo de memoria para el archivo de la base de datos. Para una base de datos de 83 MB, mapear el archivo completo 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 internos del b-tree de FTS5, reduciendo la latencia de consultas en búsquedas posteriores.
Benchmarks de escalabilidad
Medidos en Apple M3 Pro, 36 GB RAM, NVMe SSD:
| 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 añade 1-2ms.
Solución de problemas
Desfase del índice
Síntoma: La búsqueda devuelve resultados desactualizados u omite notas añadidas recientemente.
Causa: El indexador incremental no se ejecutó tras agregar notas, o el mtime de un archivo no se actualizó (por ejemplo, al sincronizar 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 los nuevos vectores de consulta. Las dimensiones o la semántica del espacio vectorial son incompatibles.
Solución: El indexador debería detectar la discrepancia en el hash del modelo y activar una reindexación completa automáticamente. Si no lo hace, limpia la base de datos manualmente y reindexa:
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 tras 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 IA reporta que el servidor MCP agotó el tiempo de espera.
Causa: La primera consulta activa la carga del modelo (inicialización diferida), lo que toma entre 2 y 5 segundos. El timeout predeterminado de MCP en la herramienta de IA 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 en SQLite
Síntoma: Errores SQLITE_BUSY o SQLITE_LOCKED.
Causa: Múltiples procesos escribiendo en la base de datos simultáneamente. El modo WAL permite lecturas concurrentes, pero solo un escritor a la vez.
Solución: Asegúrate de que solo un proceso (el indexador) escriba en la base de datos. El servidor MCP y los hooks solo deben leer. Si necesitas escrituras concurrentes, usa el modo WAL y establece un tiempo de espera por ocupación:
db.execute("PRAGMA busy_timeout=5000") # Wait up to 5 seconds
sqlite-vec no se carga
Síntoma: La búsqueda vectorial está desactivada; el recuperador funciona en modo solo 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 se cargue correctamente:
import sqlite3
db = sqlite3.connect(":memory:")
db.enable_load_extension(True)
db.load_extension("vec0")
print("sqlite-vec loaded successfully")
Problemas de memoria con bóvedas grandes
Síntoma: Errores de falta de memoria durante la reindexación completa de una bóveda grande (más de 50.000 notas).
Causa: El tamaño del lote de embeddings es demasiado grande, o todos los contenidos de los archivos se cargan en memoria simultáneamente.
Solución: Reduce el tamaño del 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, segmentando y generando embeddings de 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 usando
markdownifyopandoc - Mueve los archivos convertidos a la carpeta
00-inbox/de tu bóveda - Revisa y añade frontmatter a cada nota
- 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 los 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 embebe imágenes con rutas relativas — copia las imágenes a tu carpeta de adjuntos
- Añade 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 los 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, añade frontmatter y organízalos en carpetas
Desde markdown plano (sin Obsidian)
Si ya tienes un directorio de archivos markdown:
- Abre el directorio como una bóveda de Obsidian (Obsidian → Open Vault → Open folder)
- Añade
.obsidian/a.gitignoresi el directorio tiene control de versiones - Crea plantillas de frontmatter y aplícalas a los archivos existentes
- Comienza a enlazar notas con
[[wiki-links]]a medida que leas y organices - Ejecuta el indexador de inmediato — el sistema de recuperación funciona desde el primer día
Desde otro sistema de recuperación
Si estás migrando desde un sistema diferente de embeddings o búsqueda:
- No intentes migrar los vectores. Diferentes modelos producen espacios vectoriales incompatibles. Ejecuta una reindexación completa con el nuevo modelo.
- 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 entre 10 y 20 consultas cuyas respuestas conozcas y verifica que los resultados coincidan con tus expectativas.
Registro de cambios
| Fecha | Cambio |
|---|---|
| 2026-04-01 | Añadida sección de Obsidian CLI (comandos v1.12 para flujos de trabajo con IA). Añadida sección de plugins de agentes (Claudian, Agent Client). Documentado el plugin principal Bases para organización de bóvedas. Actualizado el conteo de plugins a más de 2.500. Añadida extensión de compartir iOS como fuente de ingesta. Actualizada 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 de CLI para interacciones más rápidas con la terminal. |
| 2026-03-23 | Documentado sqlite-vec v0.1.7 estable: soporte DELETE para tablas vec0, restricciones de distancia KNN para paginación. Índice de vecinos más cercanos aproximado DiskANN anunciado para próxima versión. |
| 2026-03-07 | Añadido potion-multilingual-128M (101 idiomas, mayo 2025) a la comparación de modelos de embeddings. sqlite-vec en v0.1.7-alpha.10 (correcciones de CI/CD, sin cambios funcionales). Especificación MCP y técnicas de recuperación confirmadas como vigentes. |
| 2026-03-03 | Actualizada la evolución de la especificación MCP (noviembre 2025 lanzado: Streamable HTTP, .well-known, anotaciones de herramientas). Añadido soporte de fine-tuning de Model2Vec y tokenizador BPE/Unigram. Añadida tabla comparativa de servidores MCP de la comunidad. Actualizado Smart Connections a v4. |
| 2026-03-02 | Añadidos potion-base-32M y potion-retrieval-32M a la comparación de modelos. Añadida sección de cuantización/reducción de dimensionalidad. Añadida nota sobre la evolución de la especificación MCP. |
| 2026-03-01 | Versión inicial |
Referencias
-
Internet Vin, “22 commands I use with Obsidian and Claude Code,” March 2026, x.com/internetvin/status/2026461256677245131. ↩
-
Nicopreme, “Visual Explainer” agent skill with slash commands, 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. Introduce RRF con k=60 como método libre de parámetros para combinar listas ordenadas. ↩↩↩
-
OpenAI Embeddings Pricing. text-embedding-3-small: $0,02 por millón de tokens. Costo estimado del vault por reindexación completa: ~$0,30. ↩
-
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. ↩
-
MTEB: Massive Text Embedding Benchmark. potion-base-8M obtiene 50,03 de promedio frente a 56,09 de all-MiniLM-L6-v2 (89% de retención). ↩
-
SQLite FTS5 Extension. FTS5 proporciona búsqueda de texto completo con ranking BM25 y pesos de columna configurables. ↩
-
sqlite-vec: A vector search SQLite extension. Proporciona tablas virtuales
vec0para búsqueda vectorial KNN dentro de SQLite. ↩ -
Robertson, S. y Zaragoza, H. The Probabilistic Relevance Framework: BM25 and Beyond. Foundations and Trends in Information Retrieval, 2009. ↩
-
Karpukhin, V. et al. Dense Passage Retrieval for Open-Domain Question Answering. EMNLP, 2020. Las representaciones densas superan a BM25 en un 9-19% en QA de dominio abierto. ↩
-
Reimers, N. y Gurevych, I. Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks. EMNLP, 2019. Trabajo fundacional sobre similitud semántica densa. ↩
-
Luan, Y. et al. Sparse, Dense, and Attentional Representations for Text Retrieval. TACL, 2021. La recuperación híbrida supera consistentemente los enfoques de modalidad única en MS MARCO. ↩
-
SQLite Write-Ahead Logging. Modo WAL para lecturas concurrentes con un único escritor. ↩
-
Gao, Y. et al. Retrieval-Augmented Generation for Large Language Models: A Survey. arXiv, 2024. Revisión de arquitecturas RAG y estrategias de chunking. ↩
-
Thakur, N. et al. BEIR: A Heterogeneous Benchmark for Zero-shot Evaluation of Information Retrieval Models. NeurIPS, 2021. ↩
-
Model2Vec: Distill a Small Fast Model from any Sentence Transformer. Minish Lab, 2024. ↩
-
Obsidian Documentation. Documentación oficial de Obsidian. ↩
-
Model Context Protocol Specification. El estándar MCP para conectar herramientas de IA con fuentes de datos. ↩
-
Datos de producción del autor. 16.894 archivos, 49.746 fragmentos, 83,56 MB de base de datos SQLite, 7.771 señales procesadas a lo largo de 14 meses. Latencia de consultas medida con
time.perf_counter(). ↩ -
Model2Vec Potion Models. Minish Lab, 2025. Potion-base-32M (MTEB 52,46), potion-retrieval-32M (MTEB retrieval 36,35) y funciones de cuantización/reducción de dimensionalidad en v0.5.0+. ↩↩↩
-
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 de niveles SDK. La próxima versión, tentativamente a mediados de 2026, incluirá operaciones asíncronas, extensiones específicas de dominio y comunicación entre agentes. ↩↩
-
Model2Vec Releases. v0.4.0 (feb. 2025): soporte para entrenamiento/ajuste fino. v0.5.0 (abr. 2025): reescritura del backend, cuantización, reducción de dimensionalidad. v0.7.0 (oct. 2025): cuantización de vocabulario, soporte para tokenizadores BPE/Unigram. ↩↩
-
Smart Connections for Obsidian. Smart Connections v4: embeddings de IA local-first, la búsqueda semántica funciona sin conexión tras la indexación inicial. ↩
-
potion-multilingual-128M. Minish Lab, mayo 2025. Modelo de embeddings estáticos para 101 idiomas, el de mejor rendimiento entre los embeddings estáticos multilingües. Misma dependencia exclusiva de numpy que los demás modelos potion. ↩
-
MCPVault v0.11.0. Marzo 2026. Nueva herramienta
list_all_tagspara escanear frontmatter y hashtags con conteos. Manejo mejorado de carpetas con punto, soporte para archivos.basey.canvas. Paquete renombrado a@bitbonsai/mcpvaulten npm. ↩ -
sqlite-vec v0.1.7 Release. 17 de marzo de 2026. Versión estable: soporte DELETE para tablas virtuales vec0, restricciones de distancia KNN para paginación, mejoras en pruebas de fuzzing. Indexación DiskANN de vecinos más cercanos aproximados anunciada para una versión futura. ↩↩↩
-
Introduction to Bases. Plugin nativo de Obsidian introducido en v1.9.10. Vistas tipo base de datos (tablas, galerías, calendarios, tableros kanban) sobre archivos del vault utilizando propiedades de frontmatter como campos. Archivos guardados en formato
.base. ↩ -
Obsidian 1.12 Desktop Changelog. 27 de febrero de 2026. Introduce el CLI de Obsidian para automatización del vault desde la terminal. Los comandos incluyen búsqueda, notas diarias, plantillas, propiedades, plugins, tareas y herramientas de desarrollo. Documentación del CLI. ↩
-
Claudian. Plugin de Obsidian que integra Claude Code como colaborador de IA dentro del vault. Ofrece chat en la barra lateral, prompts con contexto, soporte de visión, comandos slash y modos de permisos. ↩
-
Agent Client. Plugin de Obsidian que proporciona una interfaz unificada para Claude Code, Codex CLI y Gemini CLI a través del Agent Client Protocol (ACP). Soporta menciones de notas, ejecución de shell y aprobación de acciones. ↩
-
Obsidian iOS Changelog. Las actualizaciones de principios de 2026 incluyen la extensión para compartir (Share Extension) que permite guardar contenido de otras apps directamente en el vault, correcciones en los widgets de nota diaria y marcadores, y mejoras en la actualización del widget de vista de notas. ↩