← Todos los artículos

Pregunta a tus documentos: búsqueda con IA que cita la fuente y no saca lo privado de casa

Cómo monté un buscador con IA sobre mis documentos escaneados y mis notas: búsqueda híbrida en un SQLite, embeddings locales, documentos sensibles que nunca llegan a un modelo en la nube y una evaluación que me hizo apagar una pieza cara. Y qué cambia si lo montas para una empresa.

En este artículo

Le pregunto a mi asistente "¿qué compré en Decathlon?" y en unos segundos me responde con el importe y el enlace al ticket escaneado. Yo no me acordaba, y el asistente no lo sabe de memoria: lo busca. El ticket llegó por correo, pasó por el OCR de Paperless y acabó troceado en un índice de búsqueda que vive en una Raspberry Pi.

Es de lo primero que sale cuando una empresa se plantea usar IA: "quiero preguntarle a mis documentos". Contratos, procedimientos, fichas técnicas, albaranes. Aquí te cuento cómo lo tengo montado para mí, con mis números reales, y qué cambiaría yo para montarlo en una pyme.

Lo que hace

  • Indexa dos fuentes: los documentos de Paperless (escaneados, con OCR) y mis notas de Obsidian.
  • Busca de dos formas a la vez, por palabras y por significado, y mezcla los resultados.
  • Devuelve trozos con título y enlace al documento original, para que la respuesta se pueda comprobar.
  • Si un documento es sensible (nóminas, salud, contratos, extractos), su contenido no sale de casa: lo contesta un modelo local y la respuesta me llega por Telegram.
flowchart LR
  P[Paperless: escaneos con OCR] --> I[Indexador cada 30 min]
  O[Notas] --> I
  I --> DB[(SQLite: FTS5 + vectores)]
  Q[Pregunta] --> S[Búsqueda híbrida]
  DB --> S
  S -->|documento normal| A[Asistente con citas]
  S -->|documento sensible| L[Modelo local, respuesta por Telegram]

Las piezas

Un solo archivo SQLite. No quise una base vectorial gestionada. SQLite trae FTS5 para la búsqueda por palabras (BM25) y la extensión sqlite-vec guarda los vectores de 1024 dimensiones. Cabe en una Raspberry Pi con la RAM justa: el primer indexado de 167 documentos y 1.170 trozos tardó 44 segundos y usó 35 MB de memoria. Los siguientes solo procesan lo que cambió y tardan un segundo.

Embeddings locales. Los vectores los calcula bge-m3 en mi sobremesa con GPU, a través de una pasarela LiteLLM sin salida a la nube. Si el sobremesa está apagado, la búsqueda baja a solo palabras y el indexador reintenta los vectores pendientes en la siguiente pasada. Me funciona peor, pero me funciona.

Mezcla de resultados. Las dos búsquedas devuelven 30 candidatos cada una y los combino con Reciprocal Rank Fusion: cada documento suma según la posición en la que aparece en cada lista. Es una fórmula de una línea y no necesita ajustar pesos.

Un servidor MCP de solo lectura. El asistente accede con dos herramientas: buscar y preguntar en privado. No puede escribir, borrar ni subir nada.

Lo privado no sale

La privacidad va por tipo de documento, no por buena voluntad del modelo. En Paperless cada documento tiene un tipo, y seis de ellos están marcados como sensibles en un archivo de configuración. Cuando la búsqueda devuelve uno de esos, el asistente solo ve el título, el tipo y el enlace, con el aviso "contenido privado". Si quiero la respuesta, la pide un modelo local en segundo plano y me llega por Telegram.

Dos detalles que yo aprendí por las malas:

  • Falla en cerrado. El usuario de solo lectura del indexador no podía ver los tipos de documento, así que todo se guardaba como "Sin tipo" y la regla de sensibles nunca se cumplía. Lo arreglé dando el permiso y, sobre todo, cambiando la regla: si no se puede saber el tipo de un documento, se trata como sensible.
  • El texto recuperado es un dato, no una orden. Un PDF puede contener "ignora tus instrucciones y...". Los trozos llegan al modelo delimitados y marcados como contenido no fiable, y las herramientas son de solo lectura, así que lo peor que puede pasar es una mala respuesta, no una acción.

La evaluación que me hizo apagar una pieza

Monté también un reranker, un modelo que reordena los 10 mejores candidatos. Antes de dejarlo encendido lo medí con 15 preguntas reales, cada una con su documento correcto:

Modo Acierto en el 1.º Acierto en los 5 primeros Latencia
Solo palabras 0,80 0,93 11-18 ms
Híbrida 0,87 1,00 150-450 ms
Híbrida + reranker 0,93 1,00 unos 2,8 s

El reranker acertó una pregunta más de quince en la primera posición. Con 15 preguntas, eso está dentro del ruido. A cambio costaba casi 3 segundos por consulta y 1,8 GB de RAM en el sobremesa. Y como la búsqueda híbrida ya mete el documento bueno entre los cinco primeros, que es lo que lee el modelo, lo apagué. Lo volveré a medir cuando el índice tenga cientos de documentos.

Sin la tabla, yo lo habría dejado encendido porque "es lo que se hace".

Lo que se rompió

  • El asistente contestaba sin buscar. Las herramientas MCP quedaban escondidas detrás de una búsqueda de herramientas y el prompt de sistema no las nombraba. El modelo improvisaba. Nombrarlas en el prompt lo arregló.
  • Una extensión de 32 bits. La versión de sqlite-vec que instalé primero traía una librería de 32 bits para ARM y no cargaba en la Pi. La siguiente versión menor sí.
  • El reranker se quedaba sin memoria al arrancar. Con la configuración por defecto el contenedor moría cargando los pesos. Bajé el tamaño del lote y lo dejé con un límite de memoria fijo.

Si lo montas para una empresa

Lo que cambia es el tamaño y quién manda sobre los datos, no la idea:

  • Fuentes. En vez de Paperless, la carpeta de SharePoint o el servidor de ficheros. Si ya tienen Microsoft 365, primero hay que mirar si Copilot con sus licencias les basta. A veces sí, y entonces no hace falta montar nada.
  • Dónde corre. En un servidor de la empresa o un VPS en la UE a su nombre. El modelo puede ser local o uno en región europea, según lo sensibles que sean los documentos.
  • Permisos. La búsqueda tiene que respetar quién puede ver qué. Es la parte que más trabajo da y la que no se puede improvisar.
  • Evaluación antes de entregar. Veinte o treinta preguntas reales del equipo, con su documento correcto. Si no se puede medir, no se puede saber si funciona.

Si tu equipo pierde tiempo buscando en carpetas, hablamos y vemos si compensa.