← Tots els articles

Pregunta als teus documents: cerca amb IA que cita la font i no treu el que és privat de casa

Com vaig muntar un cercador amb IA sobre els meus documents escanejats i les meves notes: cerca híbrida en un SQLite, embeddings locals, documents sensibles que mai arriben a un model al núvol i una avaluació que em va fer apagar una peça cara. I què canvia si el muntes per a una empresa.

En aquest article

Li pregunto al meu assistent "què vaig comprar a Decathlon?" i en uns segons em respon amb l'import i l'enllaç al tiquet escanejat. Jo no me'n recordava, i l'assistent no ho sap de memòria: ho busca. El tiquet va arribar per correu, va passar per l'OCR de Paperless i va acabar trossejat en un índex de cerca que viu en una Raspberry Pi.

És de les primeres coses que surten quan una empresa es planteja fer servir IA: "vull preguntar als meus documents". Contractes, procediments, fitxes tècniques, albarans. Aquí t'explico com ho tinc muntat per a mi, amb els meus números reals, i què canviaria jo per muntar-ho en una pime.

Què fa

  • Indexa dues fonts: els documents de Paperless (escanejats, amb OCR) i les meves notes d'Obsidian.
  • Busca de dues maneres alhora, per paraules i per significat, i barreja els resultats.
  • Retorna trossos amb títol i enllaç al document original, perquè la resposta es pugui comprovar.
  • Si un document és sensible (nòmines, salut, contractes, extractes), el seu contingut no surt de casa: el respon un model local i la resposta m'arriba per Telegram.
flowchart LR
  P[Paperless: escanejos amb OCR] --> I[Indexador cada 30 min]
  O[Notes] --> I
  I --> DB[(SQLite: FTS5 + vectors)]
  Q[Pregunta] --> S[Cerca híbrida]
  DB --> S
  S -->|document normal| A[Assistent amb cites]
  S -->|document sensible| L[Model local, resposta per Telegram]

Les peces

Un sol fitxer SQLite. No volia una base vectorial gestionada. SQLite porta FTS5 per a la cerca per paraules (BM25) i l'extensió sqlite-vec desa els vectors de 1024 dimensions. Hi cap en una Raspberry Pi amb la RAM justa: el primer indexat de 167 documents i 1.170 trossos va trigar 44 segons i va fer servir 35 MB de memòria. Els següents només processen el que ha canviat i triguen un segon.

Embeddings locals. Els vectors els calcula bge-m3 al meu ordinador de sobretaula amb GPU, a través d'una passarel·la LiteLLM sense sortida al núvol. Si el sobretaula està apagat, la cerca baixa a només paraules i l'indexador reintenta els vectors pendents a la passada següent. Em funciona pitjor, però em funciona.

Barreja de resultats. Les dues cerques retornen 30 candidats cadascuna i els combino amb Reciprocal Rank Fusion: cada document suma segons la posició en què apareix a cada llista. És una fórmula d'una línia i no cal ajustar pesos.

Un servidor MCP de només lectura. L'assistent hi accedeix amb dues eines: buscar i preguntar en privat. No pot escriure, esborrar ni pujar res.

El que és privat no surt

La privacitat va per tipus de document, no per la bona voluntat del model. A Paperless cada document té un tipus, i sis estan marcats com a sensibles en un fitxer de configuració. Quan la cerca en retorna un, l'assistent només veu el títol, el tipus i l'enllaç, amb l'avís "contingut privat". Si vull la resposta, la demana un model local en segon pla i m'arriba per Telegram.

Dos detalls que vaig aprendre a les males:

  • Falla en tancat. L'usuari de només lectura de l'indexador no podia veure els tipus de document, així que tot es desava com a "Sense tipus" i la regla de sensibles no es complia mai. Ho vaig arreglar donant el permís i, sobretot, canviant la regla: si no es pot saber el tipus d'un document, es tracta com a sensible.
  • El text recuperat és una dada, no una ordre. Un PDF pot contenir "ignora les teves instruccions i...". Els trossos arriben al model delimitats i marcats com a contingut no fiable, i les eines són de només lectura, així que el pitjor que pot passar és una mala resposta, no una acció.

L'avaluació que em va fer apagar una peça

També vaig muntar un reranker, un model que reordena els 10 millors candidats. Abans de deixar-lo encès el vaig mesurar amb 15 preguntes reals, cadascuna amb el seu document correcte:

Mode Encert a la 1a Encert als 5 primers Latència
Només paraules 0,80 0,93 11-18 ms
Híbrida 0,87 1,00 150-450 ms
Híbrida + reranker 0,93 1,00 uns 2,8 s

El reranker va encertar una pregunta més de quinze a la primera posició. Amb 15 preguntes, això és dins del soroll. A canvi costava gairebé 3 segons per consulta i 1,8 GB de RAM al sobretaula. I com que la cerca híbrida ja posa el document bo entre els cinc primers, que és el que llegeix el model, el vaig apagar. El tornaré a mesurar quan l'índex tingui centenars de documents.

Sense la taula, jo l'hauria deixat encès perquè "és el que es fa".

El que es va trencar

  • L'assistent responia sense buscar. Les eines MCP quedaven amagades darrere d'una cerca d'eines i el prompt de sistema no les anomenava. El model improvisava. Anomenar-les al prompt ho va arreglar.
  • Una extensió de 32 bits. La versió de sqlite-vec que vaig instal·lar primer portava una llibreria de 32 bits per a ARM i no carregava a la Pi. La versió menor següent sí.
  • El reranker es quedava sense memòria en arrencar. Amb la configuració per defecte el contenidor moria carregant els pesos. Vaig baixar la mida del lot i el vaig deixar amb un límit de memòria fix.

Si el muntes per a una empresa

El que canvia és la mida i qui mana sobre les dades, no la idea:

  • Fonts. En lloc de Paperless, la carpeta de SharePoint o el servidor de fitxers. Si ja tenen Microsoft 365, primer cal mirar si Copilot amb les seves llicències els n'hi ha prou. De vegades sí, i aleshores no cal muntar res.
  • On corre. En un servidor de l'empresa o un VPS a la UE a nom seu. El model pot ser local o un en una regió europea, segons com de sensibles siguin els documents.
  • Permisos. La cerca ha de respectar qui pot veure què. És la part que dona més feina i la que no es pot improvisar.
  • Avaluació abans de lliurar. Vint o trenta preguntes reals de l'equip, cadascuna amb el seu document correcte. Si no es pot mesurar, no es pot saber si funciona.

Si el teu equip perd temps buscant en carpetes, en parlem i mirem si compensa.