Integración LangChain + Ollama: guía completa de desarrollo con LLM local

El mes pasado miré la factura de OpenAI: $52,3. Para un desarrollador individual que juega con IA de vez en cuando, duele. Entonces recordé Ollama en local: Llama 3.1 gratis, esperando.
El problema: llamar la API de Ollama a mano es verboso — formato de petición, parseo de respuesta, errores… LangChain unifica Chat, RAG y Agent y permite cambiar modelo en una línea.
Este artículo es la pieza de integración de frameworks de la serie Ollama LLM local: de langchain-ollama a Chat, RAG y Agent. Si ya leíste los primeros capítulos (API, multi-modelo), aquí conectamos todo en un marco de desarrollo.
Paquete langchain-ollama
Antes usaba langchain_community.llms.Ollama. Funcionaba, pero la documentación apuntaba a langchain-ollama — el paquete oficial independiente.
¿Por qué el paquete oficial?
Mejor tipado e IDE más útil. Mantenimiento alineado a LangChain. Los paquetes community pueden quedar obsoletos; el oficial es apuesta a largo plazo — aprendí eso a las malas.
Instalación en una línea:
pip install langchain-ollama
Tres clases principales:
| Clase | Uso | Escenario típico |
|---|---|---|
ChatOllama | Modelo conversacional | Chat multi-turn, Q&A |
OllamaLLM | Completado de texto | Generación puntual, continuación |
OllamaEmbeddings | Embeddings | RAG, búsqueda semántica |
En la práctica, el 90% de casos bastan con ChatOllama: multi-turn y streaming — letra a letra, mucho mejor que un bloque único.
Ejemplo mínimo:
from langchain_ollama import ChatOllama
# Inicializar modelo
llm = ChatOllama(
model="llama3.1:8b", # Descargar antes con ollama pull
temperature=0.7 # Aleatoriedad 0-1
)
# Enviar mensaje
response = llm.invoke("Hola, preséntate brevemente")
print(response.content)
Antes de ejecutar: ollama pull llama3.1:8b. Sin Ollama instalado, vuelve al primer artículo de la serie.
OllamaEmbeddings convierte texto en vectores; RAG lo usa en profundidad. Vista rápida:
from langchain_ollama import OllamaEmbeddings
embeddings = OllamaEmbeddings(model="nomic-embed-text")
# Embedding de una frase
vector = embeddings.embed_query("Texto de prueba")
print(f"Dimensión del vector: {len(vector)}") # Suele ser 768+
# Lote
vectors = embeddings.embed_documents([
"Primer texto",
"Segundo texto"
])
nomic-embed-text es un embedding orientado a recuperación semántica; dimensión alta (768+), mejor que modelos genéricos.
Chat en la práctica: multi-turn y streaming
Una llamada es fácil; el chat real es más rico — el usuario sigue preguntando y el modelo necesita contexto. LangChain usa listas de mensajes.
Multi-turn
Tres tipos:
SystemMessage: rol y comportamiento (p. ej. “eres un asistente técnico”)HumanMessage: entrada del usuarioAIMessage: respuesta del modelo
from langchain_ollama import ChatOllama
from langchain_core.messages import HumanMessage, AIMessage, SystemMessage
llm = ChatOllama(model="llama3.1:8b", temperature=0.7)
messages = [
SystemMessage(content="Eres un desarrollador que explica conceptos técnicos de forma clara y breve."),
HumanMessage(content="¿Qué es una REST API?"),
AIMessage(content="REST API es un estilo de interfaz web que usa métodos HTTP (GET/POST/PUT/DELETE) sobre recursos. En pocas palabras: accedes a datos por URL."),
HumanMessage(content="¿Y en qué se diferencia GraphQL?")
]
response = llm.invoke(messages)
print(response.content)
El modelo ve el historial y entiende que preguntas por GraphQL vs REST. Sin el AIMessage previo, podría empezar GraphQL desde cero y romper el hilo.
Streaming: respuesta que “cobra vida”
El usuario no mira una pantalla en blanco — el texto aparece poco a poco, como si alguien escribiera. Clave en respuestas largas.
from langchain_ollama import ChatOllama
llm = ChatOllama(model="llama3.1:8b")
print("Respuesta: ", end="", flush=True)
for chunk in llm.stream("Escribe quicksort en Python y explica la idea"):
print(chunk.content, end="", flush=True)
print()
stream() devuelve un iterador de fragmentos. flush=True evita buffer y muestra al instante.
En pruebas, el streaming se siente más rápido que un bloque único — sobre todo pasado ~100 caracteres. Parece que “piensa”, no que “se colgó”.
RAG en la práctica: base de conocimiento local
RAG (Retrieval-Augmented Generation) es de los usos más prácticos de LLM: recuperas fragmentos relevantes y el modelo responde con ese contexto — así “sabe” lo que no estaba en su entrenamiento.
Desglose del flujo RAG
Cinco pasos:
- Cargar documentos — PDF, TXT, Markdown, etc.
- Trocear texto — fragmentos manejables para búsqueda
- Generar vectores — embedding del texto
- Almacenar índice — base vectorial (aquí ChromaDB)
- Recuperar y generar — ante una pregunta, recuperar y responder
Código completo que probé y funciona:
from langchain_ollama import ChatOllama, OllamaEmbeddings
from langchain_chroma import Chroma
from langchain_community.document_loaders import PyPDFLoader, TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
# === 1. Cargar documentos ===
loader = TextLoader("./my_document.txt") # Tu ruta
docs = loader.load()
# === 2. Trocear ===
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200
)
splits = text_splitter.split_documents(docs)
# === 3 y 4. Vectores y almacenamiento ===
embeddings = OllamaEmbeddings(model="nomic-embed-text")
vectorstore = Chroma.from_documents(
documents=splits,
embedding=embeddings,
persist_directory="./chroma_db"
)
# === 5. Retriever ===
retriever = vectorstore.as_retriever(search_kwargs={"k": 4})
# === 6. RAG Chain ===
template = """Responde según el contexto. Si no hay información relevante, di claramente que no está en el documento.
Contexto:
{context}
Pregunta: {question}
"""
prompt = ChatPromptTemplate.from_template(template)
llm = ChatOllama(model="llama3.1:8b")
rag_chain = (
{"context": retriever, "question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
# === 7. Consulta ===
response = rag_chain.invoke("¿Cuál es el tema principal del documento?")
print(response)
Parece largo, pero el núcleo es rag_chain: LCEL (LangChain Expression Language) encadena retriever, prompt, modelo y parser con |.
Ajustes útiles:
chunk_size: 800 en docs técnicos densos; 1000-1500 en prosa.
k (fragmentos): 3-5 suele bastar; muchos diluyen relevancia, pocos pierden contexto.
persist_directory es obligatorio — sin él, cada reinicio re-embeddea y pierdes tiempo.
La primera vez no puse persistencia: cada cambio de código re-embeddeaba. Con persist_directory, cargas en segundos.
Agent en la práctica: herramientas con JSON
La diferencia clave del Agent: puede llamar herramientas externas.
Si preguntas “¿qué tiempo hace en Madrid?”, un chat normal puede inventar. Un Agent consulta una API de tiempo y responde con datos reales.
El límite del tool calling en Ollama
Con franqueza: Ollama no iguala a OpenAI en tools. GPT identifica cuándo y cómo llamar funciones; Llama 3.1 aún va por detrás.
Solución de LangChain: JSON Agent — el modelo emite JSON estructurado y el framework decide la herramienta. Funciona para tareas básicas, menos fluido que OpenAI nativo.
Herramientas personalizadas
from langchain_ollama import ChatOllama
from langchain_core.tools import tool
@tool
def get_weather(city: str) -> str:
"""Obtiene el tiempo de una ciudad."""
weather_data = {
"Madrid": "Soleado, 25°C, buena calidad del aire",
"Barcelona": "Nublado, 22°C, posible lluvia ligera",
"Valencia": "Caluroso, 30°C, UV alto"
}
return weather_data.get(city, f"Sin datos para {city}")
@tool
def calculate(expression: str) -> str:
"""Ejecuta un cálculo matemático."""
try:
result = eval(expression) # En producción: implementación más segura
return f"Resultado: {result}"
except:
return "Error en la expresión"
@tool
def search_local_docs(query: str) -> str:
"""Busca en la biblioteca local de documentos."""
return f"Búsqueda de '{query}': 3 registros relacionados"
@tool convierte funciones en herramientas LangChain; el docstring es la descripción que usa el modelo.
Crear JSON Agent:
from langchain.agents import AgentExecutor, create_tool_calling_agent
from langchain_ollama import ChatOllama
from langchain_core.prompts import ChatPromptTemplate
llm = ChatOllama(model="llama3.1:8b")
tools = [get_weather, calculate, search_local_docs]
prompt = ChatPromptTemplate.from_messages([
("system", "Eres un asistente útil que puede usar herramientas."),
("human", "{input}"),
("placeholder", "{agent_scratchpad}"),
])
agent = create_tool_calling_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
response = agent_executor.invoke({
"input": "Consulta el tiempo en Madrid y calcula 23 + 45"
})
print(response["output"])
verbose=True muestra el razonamiento del Agent — útil para depurar.
Experiencia real:
En pruebas, ~70-80% de acierto. Tiempo y cálculos simples van bien; varias herramientas a veces fallan — formato de parámetros o herramienta equivocada. Es el patrón habitual del Agent local.
Si necesitas más fiabilidad:
- Modelo más fuerte (Qwen 2.5, DeepSeek)
- Simplificar flujo y menos herramientas
- OpenAI nativo — más costo, mucha más estabilidad
OpenAI vs Ollama: cambiar en una línea
Pregunta frecuente: ¿puedo usar OpenAI y Ollama con el mismo código LangChain? Sí, y es más simple de lo que parece.
Opción 1: cambiar import
Código con OpenAI:
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4", temperature=0.7)
response = llm.invoke("Explica la computación cuántica")
A Ollama, un import:
from langchain_ollama import ChatOllama
llm = ChatOllama(model="llama3.1:8b", temperature=0.7)
response = llm.invoke("Explica la computación cuántica")
Prompts, chains y parsers no cambian. La abstracción de LangChain hace el cambio casi transparente.
Opción 2: API compatible con OpenAI
Ollama puede “fingir” ser OpenAI — sin cambiar import:
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="llama3.1:8b",
base_url="http://localhost:11434/v1",
api_key="ollama" # Cualquier valor; Ollama no valida
)
response = llm.invoke("Explica la computación cuántica")
Útil si el proyecto ya usa ChatOpenAI y quieres probar local sin reestructurar.
Tabla comparativa
| Aspecto | OpenAI (GPT-4) | Ollama (Llama 3.1) |
|---|---|---|
| Costo | $0.03/1K tokens input | Gratis (solo electricidad GPU local) |
| Privacidad | Datos en la nube | Procesamiento local |
| Tools | Nativo, estable | JSON Agent ~70-80% |
| Latencia | Rápida (1-3 s primer token) | Depende de GPU (3-10 s) |
| Capacidad | GPT-4 entre los mejores | Llama 3.1 8B sólido, no al nivel GPT-4 |
Recomendación:
- Aprendizaje y prototipos: Ollama, ahorro y libertad para experimentar
- Producción y alta concurrencia: OpenAI, estabilidad y velocidad
- Datos sensibles: Ollama, datos no salen del equipo
- Agent complejos: OpenAI, tools más fiables
Lo ideal: ambos — dev con Ollama, prod con OpenAI. El cambio cuesta una línea.
Resumen
Ya tienes el mapa completo de LangChain + Ollama.
Empezamos con langchain-ollama y las tres clases: ChatOllama, OllamaLLM, OllamaEmbeddings. Luego tres escenarios: Chat multi-turn (streaming mejora la UX), RAG (documentos locales → Q&A), Agent (JSON Agent como compromiso actual). Al final, estrategia OpenAI/Ollama — una línea de código, de ~$50/mes a gratis en local.
¿Cuándo Ollama?
Cuando quieres ahorrar, privacidad o aprender sin miedo a la factura.
¿Cuándo OpenAI?
Agent complejos, producción concurrente, exigencia de latencia y estabilidad. El local aún no sustituye del todo la experiencia cloud.
Si aún no lo probaste, empieza por Chat — código mínimo, efecto inmediato. Luego RAG con tus documentos. Agent puede esperar: más trampas y paciencia en depuración.
La serie sigue: multi-modelo, rendimiento y despliegue en producción. Si te interesa, continúa leyendo.
Comenta o abre issue en GitHub. Los ejemplos los ejecuté; si fallan, suele ser modelo sin pull o dependencias — sigue el mensaje de error.
Desarrollo con LangChain + Ollama
De instalación a Chat, RAG y Agent: domina el desarrollo local de apps LLM
⏱️ Estimated time: 60 min
- 1
Step 1: Instalar langchain-ollama
Ejecuta:
```bash
pip install langchain-ollama
```
Asegúrate de tener Ollama y un modelo descargado (p. ej. `ollama pull llama3.1:8b`). - 2
Step 2: Crear app Chat
Inicializa ChatOllama y envía mensajes:
```python
from langchain_ollama import ChatOllama
llm = ChatOllama(model="llama3.1:8b", temperature=0.7)
response = llm.invoke("Hola")
print(response.content)
```
Soporta multi-turn y streaming. - 3
Step 3: Construir base de conocimiento RAG
Cinco pasos:
• Cargar documentos (TextLoader / PyPDFLoader)
• Trocear texto (RecursiveCharacterTextSplitter)
• Generar vectores (OllamaEmbeddings)
• Almacenar índice (ChromaDB)
• Recuperar y generar (RAG Chain)
Parámetros clave: chunk_size=1000, k=4, persist_directory obligatorio. - 4
Step 4: Implementar Agent con herramientas
Define herramientas y crea JSON Agent:
```python
@tool
def get_weather(city: str) -> str:
"""Obtiene el tiempo de una ciudad"""
...
agent = create_tool_calling_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools)
```
~70-80% de éxito; tareas complejas mejor con OpenAI. - 5
Step 5: Alternar OpenAI / Ollama
Opción 1: cambiar import
```python
from langchain_ollama import ChatOllama # Ollama
from langchain_openai import ChatOpenAI # OpenAI
```
Opción 2: API compatible OpenAI (sin cambiar import)
```python
llm = ChatOpenAI(
base_url="http://localhost:11434/v1",
api_key="ollama"
)
```
FAQ
¿Qué diferencia hay entre langchain-ollama y langchain_community.llms.Ollama?
¿ChatOllama u OllamaLLM?
¿Cómo configurar chunk_size y k en RAG?
¿Por qué el tool calling de Ollama es menos estable que OpenAI?
¿Cómo alternar entre OpenAI y Ollama?
¿Ollama sirve en producción?
8 min de lectura · Publicado el: 7 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
Ollama API en la práctica: guía de clientes con Python y Node.js
Métodos de llamada a la API de Ollama: SDK nativos en Python y Node.js, respuestas en streaming, Agent Loop con tool calling, modo thinking y comparación con la compatibilidad OpenAI
Parte 13 de 18
Siguiente
Embedding con Ollama en la práctica: búsqueda vectorial local y RAG
Construye un sistema RAG local con Ollama: comparativa mxbai-embed-large vs nomic-embed-text, elección entre ChromaDB/FAISS/Milvus y código Python completo paso a paso.
Parte 15 de 18



Comentarios
Inicia sesión con GitHub para dejar un comentario