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
- RAG local - Fundamentos de RAG.
- Chunking - División de textos.
- Modelos de embedding - Selección de modelos.
- Bases de datos vectoriales - Qdrant, ChromaDB.
- Reranking - Mejorar resultados.
- Análisis de documentos - Procesar documentos.
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?
¿Cómo construyo una base de conocimiento?
¿Qué modelo de embedding para alemán?
¿Qué tamaño deben tener los chunks?
¿Cómo evito alucinaciones?
¿Cómo mantengo la base de datos actualizada?
¿Cuánto cuesta?
¿Están seguros mis documentos?
Referencias y lecturas adicionales
- Qdrant - Base de datos vectorial.
- ChromaDB - Base de datos vectorial simple.
- Ollama - Servidor de modelos.
- RAG Guide - Técnicas de RAG.


