Skip to content
BotServBotServ
Base de ConocimientosRAGBúsqueda SemánticaQ&ADocumentos

Base de Conocimientos desde Documentos

Construye una base de conocimientos con RAG, búsqueda semántica, sistemas Q&A y ejemplos prácticos.

S

schutzgeist

5 min read
Base de Conocimientos desde Documentos

Base de conocimientos a partir de documentos

Qué cubre este artículo sobre bases de conocimiento

  • Cómo construir una base de conocimiento desde tus documentos.
  • Cómo funcionan RAG, embeddings y búsqueda semántica.
  • Cómo hacer preguntas sobre tus documentos.
  • Ejemplos prácticos para wikis internos, sistemas de FAQ y soporte.
  • Mejores prácticas para calidad, rendimiento y mantenimiento.

Introducción: Base de conocimiento explicada

Una base de conocimiento hace que tus documentos sean buscables y consultables. En lugar de buscar en carpetas, preguntas: “¿Cómo funciona la solicitud de vacaciones?” y el sistema encuentra el documento correcto y proporciona una respuesta con referencias.

Este artículo está dirigido a usuarios que quieren construir una base de conocimiento basada en documentos. Encontrarás fundamentos en RAG local y Análisis de documentos.

¿Por qué necesito una base de conocimiento?

Imagina que tienes 500 documentos: manuales, contratos, informes. En lugar de revisarlos todos, preguntas al sistema: “¿Qué dice el contrato sobre rescisión?” y obtienes la respuesta con fuente. Eso es RAG: Retrieval-Augmented Generation.

Base de conocimiento en breve

Documentos → Fragmentos → Embeddings → Base de datos vectorial. Al formular una pregunta: Embedding → fragmentos similares → contexto → LLM → respuesta con fuentes. Todo localmente con Ollama + Qdrant.

La idea central es: primero buscar, luego responder.

Para quién es este artículo

  • Gestores de conocimiento, que necesitan hacer documentos buscables.
  • Equipos de soporte, que quieren automatizar FAQ.
  • Empresas, que buscan hacer accesible el conocimiento interno.
  • Desarrolladores, que construyen sistemas de preguntas y respuestas.

Términos clave

  • RAG - Retrieval-Augmented Generation. Cuándo es útil: el concepto.
  • Embeddings - Texto a vector. Cuándo es útil: para búsqueda.
  • Base de datos vectorial - Almacenamiento. Cuándo es útil: para embeddings.
  • Chunking - Dividir texto. Cuándo es útil: para mejor búsqueda.
  • Ollama - Servidor de modelos. Cuándo es útil: para embeddings + LLM.

Arquitectura

INDEXING:
Documentos (PDF/Word/Text)
    │
    ▼
Extraer texto → Chunking (500-800 caracteres)
    │
    ▼
Embeddings (Ollama: multilingual-e5)
    │
    ▼
Base de datos vectorial (Qdrant/ChromaDB)

QUERY:
Pregunta
    │
    ▼
Embedding → Búsqueda vectorial → Top-K fragmentos
    │
    ▼
Contexto + Pregunta → LLM (Ollama: llama3.1)
    │
    ▼
Respuesta + Referencias

Setup: Qdrant + Ollama

version: "3.8"

services:
  qdrant:
    image: qdrant/qdrant:latest
    ports:
      - "6333:6333"
    volumes:
      - qdrant_data:/qdrant/storage

  ollama:
    image: ollama/ollama:latest
    ports:
      - "11434:11434"
    volumes:
      - ollama_data:/root/.ollama

volumes:
  qdrant_data:
  ollama_data:
# Descargar modelo de embedding
ollama pull multilingual-e5

# Descargar LLM
ollama pull llama3.1

Ejemplo práctico: Base de conocimiento

import chromadb
import requests
import uuid

class KnowledgeBase:
    def __init__(self):
        self.chroma = chromadb.HttpClient(host="chromadb", port=8000)
        self.collection = self.chroma.get_or_create_collection("wissen")

    def get_embedding(self, text):
        response = requests.post("http://ollama:11434/api/embeddings", json={
            "model": "multilingual-e5",
            "prompt": text
        })
        return response.json()["embedding"]

    def add_document(self, text, source, metadata=None):
        """Indexar documento"""
        chunks = self.chunk_text(text, 500)
        for i, chunk in enumerate(chunks):
            embedding = self.get_embedding(chunk)
            self.collection.add(
                ids=[f"{source}_{i}"],
                documents=[chunk],
                embeddings=[embedding],
                metadatas=[{
                    "source": source,
                    "chunk": i,
                    **(metadata or {})
                }]
            )

    def ask(self, question):
        """Hacer pregunta a la base de conocimiento"""
        # Buscar fragmentos similares
        query_embedding = self.get_embedding(question)
        results = self.collection.query(
            query_embeddings=[query_embedding],
            n_results=5
        )

        context = "\n\n".join(results["documents"][0])
        sources = [m["source"] for m in results["metadatas"][0]]

        # Generar respuesta
        response = requests.post("http://ollama:11434/api/chat", json={
            "model": "llama3.1",
            "messages": [
                {"role": "system", "content": "Beantworte die Frage basierend auf dem Kontext. Gib Quellen an. Wenn der Kontext nicht reicht, sage das ehrlich."},
                {"role": "user", "content": f"Kontext:\n{context}\n\nFrage: {question}"}
            ],
            "stream": False
        })

        return {
            "answer": response.json()["message"]["content"],
            "sources": list(set(sources))
        }

# Uso
kb = KnowledgeBase()
kb.add_document("Urlaubsantrag: Formular HR-2024 ausfüllen...", "urlaub.pdf")
result = kb.ask("Wie beantrage ich Urlaub?")
print(result["answer"])
print(f"Quellen: {result['sources']}")

Ejemplo práctico: Indexar documentos de una carpeta

import os
from pathlib import Path

def index_folder(kb, folder):
    """Indexar todos los documentos en una carpeta"""
    for filepath in Path(folder).rglob("*.txt"):
        with open(filepath) as f:
            text = f.read()
        kb.add_document(text, filepath.name)
        print(f"Indexado: {filepath.name}")

# Indexar todos los archivos .txt
index_folder(kb, "./dokumente/")

Mejorar la calidad

Reranking

def rerank_chunks(question, chunks, top_k=3):
    """Reordenar fragmentos por relevancia"""
    scores = []
    for chunk in chunks:
        response = requests.post("http://ollama:11434/api/chat", json={
            "model": "llama3.1",
            "messages": [
                {"role": "user", "content": f"Bewerte die Relevanz dieses Textes für die Frage '{question}' auf einer Skala 0-10. Nur Zahl.\n\nText: {chunk}"}
            ],
            "stream": False
        })
        score = float(response.json()["message"]["content"].strip())
        scores.append((score, chunk))

    scores.sort(reverse=True)
    return [c for s, c in scores[:top_k]]

Referencias

def format_answer(result):
    """Formatear respuesta con fuentes"""
    answer = result["answer"]
    sources = result["sources"]

    formatted = f"{answer}\n\n---\nQuellen:\n"
    for s in sources:
        formatted += f"- {s}\n"
    return formatted

Notas de seguridad

  • Control de acceso: ¿Quién puede consultar qué documentos? Filtra por permisos.
  • Prompt Injection: Los documentos pueden contener inyecciones. Consulta Prompt Injection.
  • Calidad de datos: Documentos malos = respuestas malas. Consulta Preparar documentos.
  • Protección de datos: Todos los datos se mantienen localmente. Consulta Protección de datos.

Errores comunes

  • Chunks demasiado grandes: Por encima de 1000 caracteres pierden precisión. Lo óptimo está entre 300-800.
  • Pocos chunks: Los mejores 5 resultados no siempre son suficientes. Recupera los mejores 20 y reordena a los mejores 5.
  • Modelo de embedding incorrecto: Para alemán, usa multilingual-e5 o bge-m3.
  • Sin fuentes: Una respuesta sin fuentes no se puede verificar.
  • Alucinaciones: Sin un buen retrieval, el modelo alucina.

Enlaces relacionados

Puntos clave:

  • Base de conocimiento: Documentos → Embeddings → Base de datos vectorial → Preguntas y respuestas.
  • multilingual-e5 para documentos en alemán.
  • La estrategia de chunking y el reranking determinan la calidad.
  • Siempre incluye fuentes, nunca confíes ciegamente.
  • Local con Ollama + Qdrant: todos tus datos permanecen privados.

FAQ

¿Qué es una base de conocimiento?

Un sistema que hace que los documentos sean consultables y que generen respuestas. Haces una pregunta, el sistema encuentra documentos relevantes y responde con la fuente citada.

¿Cómo construyo una base de conocimiento?

Documentos → Extrae texto → Chunking → Embeddings → Base de datos vectorial. Para responder: Embedding → Búsqueda → Contexto → LLM → Respuesta.

¿Qué modelo de embedding para alemán?

multilingual-e5 o bge-m3. Ambos soportan alemán muy bien. nomic-embed-text está optimizado solo para inglés.

¿Qué tamaño deben tener los chunks?

300-800 caracteres con 50-100 caracteres de solapamiento. Chunks muy pequeños pierden contexto, los muy grandes pierden precisión.

¿Cómo evito alucinaciones?

Buen retrieval (chunks correctos), prompts de sistema claros (“solo desde el contexto”), exigir citas de fuentes y permitir “no sé” ante la incertidumbre.

¿Cómo mantengo la base de datos actualizada?

Flujo: Al agregar un documento nuevo, indexa automáticamente. Para actualizaciones, borra los chunks antiguos e indexa de nuevo. Consulta actualizar la base de conocimiento.

¿Cuánto cuesta?

Solo costos de hardware. Ollama, Qdrant y ChromaDB son Open Source. Sin costos de API por documento o consulta.

¿Están seguros mis documentos?

Sí, si utilizas herramientas locales. Todos los datos permanecen en tu servidor. Con APIs en la nube, tus documentos salen del servidor, así que mantente local si tienes documentos confidenciales.

Referencias y lecturas adicionales

Volver al blog
Share:

Entradas relacionadas