Proyecto: Construir un bot de documentos empresariales con Paperless-ngx + Ollama + RAG en el chat propio
Qué hace este proyecto
Los empleados preguntan en el chat: “¿Cuál es el período de preaviso en el contrato con el cliente X?” o “¿Dónde está el manual de operación de la máquina?”, el bot busca en el archivo empresarial y responde con referencias de fuente. Completamente local: ningún documento sale de la empresa.
El stack:
Papel/E-mail/PDF
└── Paperless-ngx (Escaneo + OCR + Archivo)
└── Tubería RAG (Embeddings + Qdrant)
└── Ollama (Modelo de respuesta)
└── Frontend de chat (Mattermost / Nextcloud Talk / Web)
Requisitos previos: Una mini PC/servidor (~32 GB de RAM es suficiente), las herramientas del artículo de stack de software, aproximadamente 2-3 horas de configuración.
Por qué esta arquitectura
- Paperless-ngx es la columna vertebral del archivo: OCR hace que los escaneos sean buscables, las etiquetas y remitentes estructuran el contenido. Consulta Paperless-ngx.
- RAG en lugar de entrenamiento: Los documentos no se incorporan al modelo, sino que se buscan mediante embeddings. Los documentos nuevos están disponibles inmediatamente, sin re-entrenamiento.
- Chat como frontend: Los empleados preguntan donde ya están: Mattermost o Nextcloud Talk, sin necesidad de una herramienta nueva.
- Limpio según RGPD: Todo local: los datos de mandatarios y contratos permanecen internamente.
Paso 1: Paperless-ngx como archivo
# docker-compose.yml (núcleo)
services:
paperless:
image: ghcr.io/paperless-ngx/paperless-ngx:latest
ports: ["127.0.0.1:8000:8000"]
environment:
- PAPERLESS_OCR_LANGUAGE=deu
- PAPERLESS_TIME_ZONE=Europe/Berlin
volumes:
- paperless_data:/usr/src/paperless/data
- ./consume:/usr/src/paperless/consume # Coloca documentos → OCR + Archivo
volumes:
paperless_data:
Coloca documentos en ./consume (o redirige el escáner directamente allí): Paperless realiza OCR y archiva. Consulta Integración Paperless-ngx.
Paso 2: Tubería RAG: Hacer documentos buscables
# ingest.py, Indexar documentos de Paperless en Qdrant
import requests, uuid
from qdrant_client import QdrantClient
from qdrant_client.models import PointStruct
qdrant = QdrantClient("localhost", port=6333)
def embed(text):
r = requests.post("http://localhost:11434/api/embeddings",
json={"model": "nomic-embed-text", "prompt": text})
return r.json()["embedding"]
# API de Paperless: obtener documentos
docs = requests.get("http://localhost:8000/api/documents/",
headers={"Authorization": "Token DEIN_TOKEN"}).json()["results"]
for doc in docs:
text = doc.get("content", "")[:8000] # Texto OCR
vec = embed(text)
qdrant.upsert("firma", [PointStruct(
id=str(uuid.uuid4()), vector=vec,
payload={"title": doc["title"], "id": doc["id"], "text": text[:2000]}
)])
nomic-embed-text como modelo de embedding (ollama pull nomic-embed-text), Qdrant como base de datos vectorial. Consulta RAG local.
Paso 3: El bot: pregunta → recuperación → respuesta
# bot.py, Bucle principal
import requests
def answer(question):
# 1. Encontrar documentos similares
qvec = requests.post("http://localhost:11434/api/embeddings",
json={"model": "nomic-embed-text", "prompt": question}
).json()["embedding"]
hits = qdrant.search("firma", query_vector=qvec, limit=5)
# 2. Construir contexto
context = "\n\n".join(h.payload["text"] for h in hits)
sources = [h.payload["title"] for h in hits]
# 3. Generar respuesta, SOLO desde el contexto
prompt = f"""Responde la pregunta SOLO a partir de estos documentos.
Documentos:
{context}
Pregunta: {question}
Si la respuesta no está en los documentos: sé honesto al respecto."""
r = requests.post("http://localhost:11434/api/generate",
json={"model": "qwen2.5:14b", "prompt": prompt, "stream": False})
return r.json()["response"], sources
El truco: SOLO a partir de estos documentos en el prompt evita alucinaciones, el bot cita el archivo en lugar de adivinar.
Paso 4: Conectar el frontend de chat
El bot responde donde la empresa chatea: ejemplos en los artículos de plataforma:
- Mattermost (Artículo): Bot WebSocket,
/wissen pregunta..., auto-hospedado, limpio según RGPD. - Nextcloud Talk (Artículo): Bot Webhook, directamente en la Nextcloud existente.
- Frontend web simple: Open WebUI con función RAG, el camino más rápido, sin código de bot necesario.
- Telegram (Artículo): Si también debe ser móvil.
Paso 5: Operación, indexación automática
Un trabajo cron o webhook de Paperless dispara ingest.py con cada documento nuevo, el archivo crece, el bot sabe automáticamente más:
# Paperless: post-consume-script o simplemente Cron
*/10 * * * * docker exec paperless python3 /scripts/ingest.py --new-only
Extensiones
- Permisos: Colecciones por departamento en Qdrant: ventas no ve los documentos de RRHH.
- Variante n8n: El mismo flujo como workflow n8n, visual, sin Python.
- Calidad de respuesta: Modelo reranking después de la recuperación de embedding, consulta Modelos de reranking.
- Enlaces a fuentes: El bot publica enlaces directos de Paperless al documento, no solo el título.
- Multilingüe: Contratos en idiomas extranjeros: elige un modelo de embedding multilingüe.
Qué aprenderás
- Arquitectura RAG en uso real (no solo teoría)
- OCR + archivo + búsqueda como tubería
- Protección contra alucinaciones mediante prompts “solo contexto”
- Bots de chat en contexto empresarial (permisos, RGPD)
Enlaces de referencia
- IRC-Coding.de: Tutoriales de programación: RAG, código de bots.
- RAG local: La tecnología en detalle.
- Paperless-ngx: Configuración del archivo.
- Bots Mattermost: Frontend de chat.
- Bot de conocimiento interno: Escenario relacionado.
- Homelab: El hardware.
Puntos clave:
- Bot de documentos empresariales = Paperless (archivo/OCR) + Qdrant (búsqueda vectorial) + Ollama (respuestas) + frontend de chat.
- El prompt “responder solo a partir de documentos” evita alucinaciones: el núcleo del diseño.
- Completamente local = limpio según RGPD para contratos y datos de mandatarios.
- Indexación por cron/webhook: el archivo crece, el bot aprende simultáneamente.
- Aproximadamente 2-3 horas de configuración en hardware existente.
FAQ
¿Evita las alucinaciones?
¿Cuántos documentos soporta?
¿Qué hardware necesita?
¿Permisos por departamento?
¿Funciona sin Python?
Fuentes y lectura complementaria
- Paperless-ngx: GitHub.
- Qdrant: Base de datos vectorial.
- IRC-Coding.de: Tutoriales de programación.


