Ejecuta LLMs localmente con Ollama para inferencia privada
Instala y usa Ollama para ejecutar LLMs open-source localmente con Python, incluyendo streaming, embeddings, function calling y gestion de modelos sin costos de API
Nota para desarrolladores hispanohablantes: Esta guía incluye ejemplos y convenciones de nomenclatura adaptadas a equipos que trabajan en español. Cuando existen diferencias significativas en terminología técnica entre el inglés y el español, se indican explícitamente para facilitar la comunicación en equipos multiculturales.
Ejecuta LLMs localmente con Ollama para inferencia privada
Ollama ejecuta LLMs open-source (Llama 3, Mistral, Phi-3, etc.) localmente en tu maquina. Sin API keys, sin costos por token, sin datos saliendo de tu red. A continuacion: instalar Ollama, ejecutar modelos con Python, streaming de respuestas, generar embeddings y usar function calling — todo localmente.
Cuando Usar Esto
-
For alternatives, see a Local RAG Pipeline with ChromaDB and Sentence Transformers.
-
Aplicaciones sensibles a privacidad donde los datos no pueden salir de tu maquina
-
Desarrollo y prototipado sin costos de API
-
Entornos offline o air-gapped
-
Ejecutar modelos personalizados o fine-tuneados
Requisitos Previos
- Python 3.10+
- Ollama instalado (
curl -fsSL https://ollama.com/install.sh | shen Linux/macOS, o descarga desde ollama.com para Windows) - 8GB+ RAM (16GB+ recomendado para modelos mas grandes)
Solucion
1. Instalar Ollama y descargar un modelo
# Instalar Ollama (Linux/macOS)
curl -fsSL https://ollama.com/install.sh | sh
# Descargar un modelo (lo guarda en tu maquina)
ollama pull llama3
# Listar modelos instalados
ollama list
2. Instalar cliente Python
pip install ollama
3. Chat basico
import ollama
response = ollama.chat(
model="llama3",
messages=[
{"role": "system", "content": "You are a helpful coding assistant."},
{"role": "user", "content": "Explain Redis cache-aside pattern in 2 sentences."},
],
)
print(response["message"]["content"])
4. Streaming de respuestas
def stream_chat(model: str, message: str) -> None:
"""Stream tokens from Ollama in real-time."""
stream = ollama.chat(
model=model,
messages=[{"role": "user", "content": message}],
stream=True,
)
for chunk in stream:
content = chunk["message"]["content"]
print(content, end="", flush=True)
print()
stream_chat("llama3", "Write a Python function to reverse a linked list.")
5. Conversacion multi-turno
class OllamaChat:
def __init__(self, model: str = "llama3", system_prompt: str = ""):
self.model = model
self.messages = []
if system_prompt:
self.messages.append({"role": "system", "content": system_prompt})
def chat(self, user_input: str) -> str:
"""Send a message and get a response, maintaining history."""
self.messages.append({"role": "user", "content": user_input})
response = ollama.chat(
model=self.model,
messages=self.messages,
)
assistant_msg = response["message"]["content"]
self.messages.append({"role": "assistant", "content": assistant_msg})
return assistant_msg
def clear_history(self) -> None:
"""Clear conversation history."""
system = self.messages[0] if self.messages and self.messages[0]["role"] == "system" else None
self.messages = []
if system:
self.messages.append(system)
# Uso
chat = OllamaChat(model="llama3", system_prompt="You are a Python expert.")
print(chat.chat("What is a decorator?"))
print(chat.chat("Show me an example."))
print(chat.chat("How do I pass arguments to it?"))
6. Generar embeddings
def generate_embedding(text: str, model: str = "nomic-embed-text") -> list[float]:
"""Generate embeddings using a local Ollama model.
Args:
text: Input text.
model: Embedding model name.
Returns:
Embedding vector.
"""
response = ollama.embeddings(model=model, prompt=text)
return response["embedding"]
# Primero descargar el modelo de embedding
# ollama pull nomic-embed-text
embedding = generate_embedding("Redis is an in-memory data store.")
print(f"Embedding dimensions: {len(embedding)}")
7. Function calling con Ollama
import json
def function_calling_chat(model: str, message: str, tools: list[dict]) -> dict:
"""Chat with function calling support.
Args:
model: Ollama model name.
message: User message.
tools: List of tool definitions.
Returns:
Response dict with content or tool calls.
"""
response = ollama.chat(
model=model,
messages=[{"role": "user", "content": message}],
tools=tools,
)
return response["message"]
# Definir herramientas
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get current weather for a city",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name"},
},
"required": ["city"],
},
},
},
]
result = function_calling_chat("llama3", "What's the weather in Madrid?", tools)
if result.get("tool_calls"):
for call in result["tool_calls"]:
print(f"Tool: {call['function']['name']}")
print(f"Args: {call['function']['arguments']}")
else:
print(f"Response: {result['content']}")
8. Gestion de modelos
def list_models() -> list[dict]:
"""List all installed Ollama models."""
response = ollama.list()
return [
{
"name": m["name"],
"size": m["size"] / 1e9, # GB
"modified": m["modified_at"],
}
for m in response["models"]
]
def pull_model(model_name: str) -> None:
"""Pull (download) a model."""
for progress in ollama.pull(model_name, stream=True):
if progress.get("status"):
print(f"\r{progress['status']}", end="", flush=True)
print()
def delete_model(model_name: str) -> None:
"""Delete a model to free disk space."""
ollama.delete(model_name)
print(f"Deleted {model_name}")
# Listar modelos instalados
for model in list_models():
print(f"{model['name']}: {model['size']:.1f} GB")
Como Funciona
- Servidor Ollama corre como un proceso local (puerto por defecto 11434). El cliente Python envia peticiones HTTP a este servidor.
- Descarga de modelos descarga archivos de modelo GGUF (cuantizados) a
~/.ollama/models/. La cuantizacion reduce el tamanio del modelo y uso de memoria manteniendo calidad razonable. - Streaming usa Server-Sent Events — el servidor envia tokens a medida que se generan, y el cliente los produce como chunks.
- Embeddings usan modelos de embedding especializados (como
nomic-embed-text) que estan optimizados para producir representaciones vectoriales, no generacion de texto. - Function calling — Ollama parsea definiciones de herramientas y puede retornar peticiones estructuradas de llamadas a herramientas. El modelo decide si llamar una herramienta o responder directamente.
Variantes
Usar con LangChain
from langchain_ollama import ChatOllama, OllamaEmbeddings
llm = ChatOllama(model="llama3", temperature=0.3)
embeddings = OllamaEmbeddings(model="nomic-embed-text")
# Usar en cadenas LangChain
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
chain = (
ChatPromptTemplate.from_template("Explain {topic} briefly.")
| llm
| StrOutputParser()
)
result = chain.invoke({"topic": "vector databases"})
API async
import asyncio
from ollama import AsyncClient
async def async_chat():
client = AsyncClient()
response = await client.chat(
model="llama3",
messages=[{"role": "user", "content": "Hello!"}],
)
return response["message"]["content"]
result = asyncio.run(async_chat())
Modelfile personalizado (modelos fine-tuneados)
# Crear un Modelfile
cat > Modelfile <<EOF
FROM llama3
SYSTEM "You are a senior DevOps engineer. Answer concisely."
PARAMETER temperature 0.3
PARAMETER top_p 0.9
EOF
# Construir modelo personalizado
ollama create devops-assistant -f Modelfile
response = ollama.chat(
model="devops-assistant",
messages=[{"role": "user", "content": "How to optimize Docker build times?"}],
)
print(response["message"]["content"])
Procesamiento batch
def batch_generate(
prompts: list[str],
model: str = "llama3",
) -> list[str]:
"""Generate responses for multiple prompts."""
results = []
for prompt in prompts:
response = ollama.generate(
model=model,
prompt=prompt,
options={"temperature": 0},
)
results.append(response["response"])
return results
summaries = batch_generate([
"Summarize: Redis is an in-memory data structure store.",
"Summarize: PostgreSQL is a relational database with ACID compliance.",
])
Mejores Practicas
- Empieza con
llama3(8B) — buen balance de calidad y velocidad; necesita ~5GB RAM - Usa
temperature=0para tareas factuales — generacion de codigo, extraccion de datos, clasificacion - Descarga modelos de embedding por separado —
nomic-embed-textesta optimizado para embeddings, no chat - Monitorea el uso de RAM — los modelos se cargan en memoria; ejecutar multiples modelos simultaneamente puede causar OOM
Errores Comunes
- No descargar el modelo primero —
ollama pulldebe ejecutarse antes de usar un modelo en Python - Usar un modelo demasiado grande — Llama 3 70B necesita 40GB+ RAM; empieza con modelos 8B
- No establecer
stream=Truepara salidas largas — sin streaming espera la respuesta completa, lo que puede tomar minutos - Mezclar modelos de embedding y chat — los modelos de chat producen malos embeddings; usa modelos de embedding dedicados
FAQ
Q: Con que modelo debo empezar?
A: llama3 (8B) para chat general, phi3 (3.8B) para tareas ligeras, mistral (7B) para codigo, nomic-embed-text para embeddings.
Q: Cuanta RAM necesito? A: Modelos 8B necesitan ~5GB, modelos 13B necesitan ~8GB, modelos 70B necesitan ~40GB. Los modelos cuantizados (Q4) usan menos.
Q: Puedo usar Ollama en produccion? A: Si. Ollama esta listo para produccion. Para setups multi-usuario, ejecuta Ollama en un servidor GPU dedicado y conecta via HTTP.
Q: Como compara Ollama con OpenAI? A: Ollama es gratis y privado pero mas lento y menos capaz que GPT-4o. Usa Ollama para privacidad, ahorro de costos o uso offline. Usa OpenAI para maxima calidad.
¿Esta solución está lista para producción?
Sí. Los ejemplos de código arriba muestran implementaciones probadas. Adapta el manejo de errores y la configuración a tu entorno específico antes de desplegar.
¿Cuáles son las características de rendimiento?
El rendimiento depende de tu volumen de datos e infraestructura. Las soluciones mostradas priorizan claridad. Para escenarios de alto throughput, añade caching, batching y connection pooling según sea necesario.
¿Cómo depuro problemas con este enfoque?
Empieza con el ejemplo mínimo de arriba. Añade logging en cada paso. Prueba con entradas pequeñas primero, luego escala. Usa el debugger de tu lenguaje para revisar los edge cases.
Recursos Relacionados
Stream LLM Output with Server-Sent Events (SSE)
Stream LLM responses to clients in real-time using Server-Sent Events with FastAPI, OpenAI streaming, and async generators for token-by-token output
Recipea Local RAG Pipeline with ChromaDB and Sentence Transformers
Implement retrieval-augmented generation locally with ChromaDB, sentence-transformers embeddings, and LLM generation without external API dependencies
RecipeCompose LCEL Chains in LangChain for Multi-Step LLM
Build composable LLM pipelines with LangChain Expression Language (LCEL) using pipes, parallel execution, and custom runnable components
RecipeBuild Stateful AI Agents with LangGraph State Machines
Create multi-step AI agents with LangGraph using state machines, conditional edges, tool calling, and human-in-the-loop checkpoints for production workflows
GuideComplete Guide to Local LLM Deployment
Deploy LLMs locally and on-premise. Covers Ollama, vLLM, llama.cpp, LM Studio, model quantization, GPU requirements, serving with API servers, performance tuning, and choosing between local and cloud LLM deployment.