Embedding con Ollama en la práctica: búsqueda vectorial local y RAG

Revisé más de 200 PDF buscando un detalle de un documento técnico de hace seis meses. Búsqueda por palabras clave no servía — recordaba la idea, no la frase exacta. Casi una hora hasta dar con él. Pensé: hace falta búsqueda que entienda significado.
Peor: eran documentos internos; subirlos a la nube para vectores estaba prohibido.
Ollama Embedding encaja: local, datos en casa, búsqueda semántica. Probé mxbai, nomic y Qwen3 y comparé bases vectoriales. Muchas trampas — modelo equivocado, base pequeña que no escala.
Este artículo condensa esa experiencia: RAG local de punta a punta, con código.
Modelos Embedding en Ollama
Ollama ofrece varios modelos Embedding; al principio tampoco sabía cuál elegir. La documentación oficial dice que mxbai supera a text-embedding-3-large de OpenAI — suena bien, pero ¿cómo se comporta en la práctica? Probé varios y aquí va la respuesta directa.
Primero la tabla comparativa:
| Modelo | Dimensión | Contexto | Tamaño | Notas |
|---|---|---|---|---|
| mxbai-embed-large | 1024 | 512 tokens | 670M | Opción general, top MTEB |
| nomic-embed-text | 768 | 8192 tokens | 274M | Textos largos, extensión de contexto |
| Qwen3 Embedding | 1024 | 8192 tokens | ~600M | Lanzado en 2026, bueno en chino |
mxbai-embed-large es el que más uso. ¿Por qué? Simple, estable. Sus vectores de 1024 dimensiones bastan en la mayoría de escenarios; en MTEB (Massive Text Embedding Benchmark) está muy arriba, de hecho un poco por encima de text-embedding-3-large de OpenAI. Para búsqueda en documentos o código, es una apuesta segura.
nomic-embed-text destaca por 8192 tokens de contexto. Si procesas artículos enteros o conversaciones largas, encaja bien. Es más ligero (274M) y corre más rápido que mxbai. Con 768 dimensiones la expresión semántica es algo menor en teoría; en textos cortos la diferencia es pequeña, en textos largos nomic gana.
Qwen3 Embedding salió en abril de 2026 (Alibaba). En chino va muy bien: probé con artículos técnicos y empareja «mecanismos de tolerancia a fallos en sistemas distribuidos» con «diseño de tolerancia a fallos», donde mxbai flojea. Si tu contenido es mayoritariamente chino, merece la pena.
¿Recomendación? Empieza con mxbai sin complicarte. Para textos largos, nomic; para chino, Qwen3. En pocas palabras: prueba los tres en media hora y decide con datos reales.
Elección de base de datos vectorial
Modelo elegido, ¿dónde guardar los datos? Elegir mal la base vectorial duele más que elegir mal el modelo: pequeña y luego escalas mal; grande y desperdicias recursos. Comparé tres opciones habituales:
| Base | Escenario | Escala | Notas |
|---|---|---|---|
| ChromaDB | Aprendizaje, proyectos personales | < 100k | Listo para usar, cero config |
| FAISS | Alto rendimiento en una máquina | 100k-1M | Meta, open source, muy rápido |
| Milvus | Producción, enterprise | Millones+ | Distribuido, escalable, completo |
ChromaDB es mi recomendación para empezar. Instalación en una línea: pip install chromadb. API clara: guardar y consultar en pocas líneas. Usa índice HNSW; para volúmenes pequeños va sobrado. Limitación: despliegue en una sola máquina; pasado ~100k registros el rendimiento cae.
FAISS es la herramienta clásica de Meta. C++ puro, velocidad real. Con 500k vectores la latencia se mantiene en milisegundos. Pero es más una librería de búsqueda que una base completa: persistencia e índices los gestionas tú. Ideal si te gusta afinar rendimiento o tienes requisitos exigentes.
Milvus sí está pensado para producción: despliegue distribuido, persistencia, varios tipos de índice y versión cloud (Zilliz Cloud). Configuración y coste operativo más altos. Compensa con millones de vectores, alta disponibilidad y equipos.
Mi estrategia: ChromaDB para arrancar rápido; FAISS si el rendimiento apremia; Milvus o cloud en producción seria. No cuentes con migrar de ChromaDB a Milvus sin dolor — formatos y APIs difieren.
Flujo RAG completo
Menos teoría y más código. Implementación local de punta a punta: de PDF a búsqueda semántica con Ollama + ChromaDB.
Preparar el entorno
Instala dependencias:
pip install ollama chromadb langchain langchain-community pypdf
Asegúrate de que Ollama esté en marcha y los modelos descargados:
ollama pull mxbai-embed-large
ollama pull qwen2.5:7b # para generar respuestas
Implementación en código
import ollama
from langchain_community.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
import chromadb
# 1. Cargar PDF
loader = PyPDFLoader("./your_document.pdf")
docs = loader.load()
# 2. Trocear documentos — chunks muy grandes fallan en retrieval; muy pequeños pierden contexto
splitter = RecursiveCharacterTextSplitter(
chunk_size=800, # 800 caracteres por fragmento
chunk_overlap=100, # 100 de solape para no perder info en los bordes
)
chunks = splitter.split_documents(docs)
# 3. Generar embeddings y guardar en ChromaDB
client = chromadb.Client()
collection = client.create_collection("my_docs")
for i, chunk in enumerate(chunks):
# Llamada a la API de Ollama
response = ollama.embed(
model="mxbai-embed-large",
input=chunk.page_content,
)
embedding = response["embeddings"][0]
collection.add(
ids=[str(i)],
embeddings=[embedding],
documents=[chunk.page_content],
metadatas=[{"source": chunk.metadata.get("source", "unknown")}],
)
print(f"Fragmentos indexados: {len(chunks)}")
# 4. Búsqueda semántica
query = "¿Qué mecanismos de tolerancia a fallos tiene un sistema distribuido?"
query_embedding = ollama.embed(
model="mxbai-embed-large",
input=query,
)["embeddings"][0]
results = collection.query(
query_embeddings=[query_embedding],
n_results=3, # top 3 fragmentos
)
# 5. Generar respuesta con el contexto recuperado
context = "\n\n".join(results["documents"][0])
response = ollama.chat(
model="qwen2.5:7b",
messages=[
{
"role": "system",
"content": "Responde basándote en el documento. Si no hay información, dilo claramente.",
},
{"role": "user", "content": f"Documento:\n{context}\n\nPregunta: {query}"},
],
)
print(f"Respuesta: {response['message']['content']}")
Pruébalo. El flujo es directo: trocear → embedding → almacenar → consultar → responder.
Trampas frecuentes:
Primero, no elijas chunk_size al azar. Con 200 caracteres por fragmento obtuve trozos inútiles que no armaban una respuesta. 500-1000 suele ir bien.
Segundo, procesamiento por lotes. Con muchos documentos, llamar a Ollama uno a uno es lento. Agrupa:
# Embeddings en lote — mejora notable de velocidad
batch_texts = [chunk.page_content for chunk in chunks[:50]]
batch_embeddings = ollama.embed(
model="mxbai-embed-large",
input=batch_texts,
)["embeddings"]
Tercero, umbral de similitud. ChromaDB devuelve n_results aunque no sean relevantes. A veces conviene filtrar:
# Filtrar por distancia
results = collection.query(
query_embeddings=[query_embedding],
n_results=10,
)
# Solo distancia < 0,3 (menor distancia = más similar)
filtered = [
doc for doc, dist in zip(results["documents"][0], results["distances"][0])
if dist < 0.3
]
Con este código tienes RAG local. Cambia el PDF y la pregunta; el resto igual.
Ajuste de rendimiento y buenas prácticas
Con el sistema en marcha, toca afinar. Estos parámetros marcan la diferencia.
¿Qué chunk_size?
500-1000 caracteres es mi rango habitual. Muy pequeño: semántica incompleta — una frase partida no encaja con la pregunta. Muy grande: ruido — un chunk mezcla varios temas.
Depende del tipo de documento. Docs técnicos estructurados: por párrafo; conversaciones fragmentadas: ~500 caracteres. Prueba en tu caso; no hay regla universal.
Batch para acelerar
Cada llamada suelta a Ollama paga ida y vuelta. Lotes de 50-100 textos multiplican la velocidad. No excedas el límite de longitud del modelo.
Umbral de similitud
Entre 0,7 y 0,85 según precisión vs recall. Alto (0,85): menos ruido, menos recall. Bajo (0,7): más recall, posible ruido. Corpus limpio y preguntas claras → umbral alto; preguntas abiertas → umbral más bajo.
Consejo: indexa 50-100 documentos, mide retrieval y luego escala. Ajustar con todo el corpus ya dentro cuesta más.
Resumen
En resumen:
Modelo según escenario — mxbai general, nomic textos largos, Qwen3 chino. Base: ChromaDB para empezar, FAISS si el rendimiento apremia, Milvus en producción. El código está listo para adaptar.
Ventajas claras: despliegue local, privacidad, modelos Ollama gratis, ChromaDB fácil de usar. Límites: techo en una sola máquina; millones de vectores piden otra arquitectura.
Corre el código del artículo con unos 50 documentos y mide calidad de retrieval y respuestas antes de escalar.
Para ir más allá con LangChain + Ollama (cadenas de conversación y tool calling), mira la guía de integración de la serie — esta entrega se centra en vectores; juntas cubren el camino completo de apps LLM locales.
Construir un sistema RAG local
Sistema de recuperación vectorial con Ollama + ChromaDB
⏱️ Estimated time: 30 min
- 1
Step 1: Instalar dependencias y modelos
Ejecuta:
```bash
pip install ollama chromadb langchain langchain-community pypdf
ollama pull mxbai-embed-large
ollama pull qwen2.5:7b
```
Asegúrate de que el servicio Ollama esté activo. - 2
Step 2: Cargar y trocear documentos
PyPDFLoader + RecursiveCharacterTextSplitter:
```python
loader = PyPDFLoader("./your_document.pdf")
docs = loader.load()
splitter = RecursiveCharacterTextSplitter(
chunk_size=800,
chunk_overlap=100,
)
chunks = splitter.split_documents(docs)
```
chunk_size recomendado: 500-1000 caracteres. - 3
Step 3: Generar vectores y guardar
API Ollama + ChromaDB:
```python
client = chromadb.Client()
collection = client.create_collection("my_docs")
for i, chunk in enumerate(chunks):
response = ollama.embed(
model="mxbai-embed-large",
input=chunk.page_content,
)
embedding = response["embeddings"][0]
collection.add(
ids=[str(i)],
embeddings=[embedding],
documents=[chunk.page_content],
)
```
Usa lotes si hay muchos documentos. - 4
Step 4: Recuperación semántica y respuesta
Consulta → vector → top-k → LLM:
```python
query_embedding = ollama.embed(
model="mxbai-embed-large",
input=query,
)["embeddings"][0]
results = collection.query(
query_embeddings=[query_embedding],
n_results=3,
)
context = "\n\n".join(results["documents"][0])
response = ollama.chat(
model="qwen2.5:7b",
messages=[
{"role": "system", "content": "Responde basándote en el documento"},
{"role": "user", "content": f"Documento: {context}\nPregunta: {query}"},
],
)
```
Ajusta umbral de similitud según necesidad.
FAQ
¿Qué modelo Embedding de Ollama es mejor?
• mxbai-embed-large: opción general estable
• nomic-embed-text: textos largos, 8192 tokens
• Qwen3 Embedding: mejor para chino (2026)
Prueba los tres y mide en tus datos.
¿ChromaDB, FAISS o Milvus?
• ChromaDB: empezar, cero config, < 100k registros
• FAISS: rendimiento en una máquina, hasta ~1M
• Milvus: producción distribuida, millones+
Proyectos personales: ChromaDB. Producción: Milvus.
¿Qué chunk_size usar?
¿Cómo acelerar la generación de embeddings?
¿Umbral de similitud recomendado?
¿Ventajas e inconvenientes del RAG local?
Inconvenientes: rendimiento limitado en una máquina; millones de vectores requieren otra arquitectura; mantenimiento propio.
7 min de lectura · Publicado el: 8 abr 2026 · Actualizado el: 21 ago 2026
Guía de Ollama local LLM
Si llegaste desde búsqueda, lo más rápido es ir al artículo anterior o siguiente de esta misma serie.
Anterior
Integración LangChain + Ollama: guía completa de desarrollo con LLM local
Métodos completos de integración LangChain y Ollama: ejemplos de Chat, RAG y Agent, estrategia de cambio entre OpenAI y Ollama, y cómo construir aplicaciones LLM locales de nivel empresarial.
Parte 14 de 18
Siguiente
Cuantización de modelos en Ollama: formato GGUF y pérdida de precisión
Principios de cuantización GGUF en Ollama, datos de evaluación Red Hat 500K+ sobre pérdida de precisión y recomendaciones por hardware para ejecutar LLM grandes en GPU de consumo.
Parte 16 de 18



Comentarios
Inicia sesión con GitHub para dejar un comentario