Skip to content
BotServBotServ
Bot de documentosRAGPaperless-ngxOllamaChatbot empresarialGestión del conocimientoSelf-HostingProyecto práctico

Bot de Documentos Empresariales: Paperless-ngx + Ollama + RAG

Proyecto práctico: un bot interno responde preguntas de empleados desde documentos empresariales usando Paperless-ngx, Ollama y RAG completamente local.

S

schutzgeist

5 min read
Bot de Documentos Empresariales: Paperless-ngx + Ollama + RAG

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

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?

En gran medida, el prompt obliga a “responder solo desde el contexto” más referencias de fuente. Sin resultados → el bot dice “no encontrado” en lugar de adivinar. El reranking mejora aún más la calidad de los resultados.

¿Cuántos documentos soporta?

Decenas de miles sin problemas: Qdrant escala bien. El cuello de botella es más la estrategia de chunking: divide documentos muy largos en secciones para mejores resultados.

¿Qué hardware necesita?

32 GB de RAM son suficientes para modelos de respuesta 7B-14B + embeddings. Para mejor calidad, modelos 14B-30B, entonces necesitas 64 GB o una mini PC de 128 GB (MS-S1 Max). Consulta la guía Homelab.

¿Permisos por departamento?

Sí, colecciones Qdrant separadas por departamento, el bot verifica el rol del usuario desde el chat (Mattermost/Nextcloud lo proporcionan) y busca solo en las colecciones permitidas.

¿Funciona sin Python?

Sí, como workflow n8n: Webhook → Búsqueda en Paperless → Ollama → Respuesta al chat. Consulta el artículo de workflows RAG de n8n. Un poco menos flexible, pero sin código.

Fuentes y lectura complementaria

Volver al blog
Share:

Entradas relacionadas