Cómo funciona un LLM sin matemáticas pesadas
Doña Rosa lleva veinte años entregando café en la Cooperativa Cafetalera Altos de Tacaná, en Unión Juárez, Chiapas. Un día le preguntó a un chatbot en su celular: "¿Hasta qué fecha recibe café el centro de acopio de la cooperativa?". El chatbot contestó con mucha seguridad: "Hasta el 30 de abril". Falso: el reglamento dice marzo. El modelo no mintió a propósito; simplemente nunca leyó ese reglamento y completó la frase con lo que le pareció más probable.
Para construir un asistente que conteste bien a Doña Rosa, primero tienes que entender por qué pasó eso. En esta lección abrimos la caja negra lo suficiente para tomar buenas decisiones técnicas, sin ecuaciones complicadas.
Al terminar podrás:
- explicar qué es un token y por qué importa para el costo y los límites;
- describir cómo un LLM genera texto prediciendo un token a la vez;
- explicar con tus palabras qué hace el mecanismo de atención;
- calcular si un documento cabe en la ventana de contexto;
- predecir el efecto de subir o bajar la temperatura.
Un LLM es un predictor de la siguiente pieza de texto
Un modelo de lenguaje grande (LLM, por sus siglas en inglés) es un programa entrenado con enormes cantidades de texto para hacer una sola cosa: dado un texto de entrada, estimar qué pieza de texto es más probable que siga. Cuando le pides una respuesta larga, repite esa operación muchas veces. Agrega una pieza, vuelve a leer todo lo que hay y calcula la siguiente.
Piensa en el autocompletado del teclado de tu celular, pero con dos diferencias enormes: el modelo vio muchísimo más texto y puede tener en cuenta miles de palabras anteriores, no solo las últimas tres.
De aquí sale la primera lección práctica: el modelo no consulta una base de datos al responder. Lo que "sabe" quedó comprimido en sus parámetros durante el entrenamiento. Si el reglamento de la cooperativa no estaba en ese texto, o si cambió después, el modelo solo puede adivinar. Por eso existe RAG, que es el tema central de este curso: buscar el fragmento correcto del reglamento y ponerlo frente al modelo antes de que conteste.
Tokens: las piezas con las que trabaja el modelo
Los modelos no leen letras ni palabras completas. Leen tokens: fragmentos de texto que pueden ser una palabra entera, una parte de palabra, un signo de puntuación o un espacio con letras. Una palabra frecuente como "café" puede ser un solo token; una rara como "Motozintla" puede partirse en varios.
Esto importa por tres razones:
- Costo. Las API cobran por tokens de entrada y de salida.
- Límites. La ventana de contexto y el máximo de salida se miden en tokens.
- Idioma. Los tokenizadores suelen estar más optimizados para el inglés, así que el mismo contenido en español puede ocupar algo más de tokens. Mídelo con tus propios textos en lugar de suponer.
Cohere tiene un endpoint para ver cómo se parte un texto. Lo usarás después de crear tu clave en la lección 1.2; por ahora mira la forma:
# tokens.py (requiere la clave que crearás en la lección 1.2)
import cohere
from dotenv import load_dotenv
MODELO = "command-a-03-2025"
load_dotenv()
co = cohere.ClientV2() # lee la clave de la variable de entorno CO_API_KEY
texto = "El centro de acopio de Unión Juárez recibe café pergamino de noviembre a marzo."
resultado = co.tokenize(text=texto, model=MODELO)
print("tokens:", len(resultado.tokens))
print(resultado.token_strings)
tokenize devuelve dos listas paralelas: tokens (los números que el modelo realmente procesa) y token_strings (el texto de cada pieza). Fíjate en cómo se parten los nombres propios y las palabras con acento.
Cómo se elige la siguiente pieza
En cada paso, el modelo produce una puntuación para cada token posible de su vocabulario. Esas puntuaciones se convierten en probabilidades con una función llamada softmax. Después se elige un token según esas probabilidades.
Vamos a simularlo con números inventados. Supón que el texto hasta ahora es "El centro de acopio recibe café de noviembre a" y el modelo duda entre cuatro continuaciones:
# siguiente_token.py
import numpy as np
candidatos = ["marzo", "abril", "diciembre", "mayo"]
puntuaciones = np.array([3.2, 2.1, 1.0, 0.3]) # valores inventados para el ejemplo
def softmax(x: np.ndarray, temperatura: float = 1.0) -> np.ndarray:
z = x / temperatura
z = z - z.max() # estabilidad numérica
e = np.exp(z)
return e / e.sum()
for t in (0.2, 1.0, 2.0):
probs = softmax(puntuaciones, t)
detalle = ", ".join(f"{c}={p:.2f}" for c, p in zip(candidatos, probs))
print(f"temperatura {t}: {detalle}")
Este script corre sin clave ni internet. La salida es aproximadamente esta:
temperatura 0.2: marzo=1.00, abril=0.00, diciembre=0.00, mayo=0.00
temperatura 1.0: marzo=0.67, abril=0.22, diciembre=0.07, mayo=0.04
temperatura 2.0: marzo=0.47, abril=0.27, diciembre=0.16, mayo=0.11
Ahí está la intuición de la temperatura: divide las puntuaciones antes de convertirlas en probabilidades. Con temperatura baja, la opción favorita se lleva casi toda la probabilidad y el modelo se vuelve predecible. Con temperatura alta, las opciones menos probables ganan terreno y aparecen respuestas más variadas, pero también más errores.
Ahora fíjate en el detalle incómodo: aun con temperatura baja, si el modelo nunca vio el reglamento, "abril" podría tener la puntuación más alta. La temperatura controla qué tan arriesgado es el muestreo, no qué tan correcto es lo que el modelo sabe. Para respuestas sobre documentos conviene una temperatura baja y, sobre todo, darle al modelo el texto correcto.
En la API de chat de Cohere la temperatura es un parámetro de la solicitud; la referencia de la API v2 indica un valor predeterminado de 0.3. Además existen p y k, que recortan la lista de candidatos antes de elegir. Los usarás en la lección 1.3.
Atención: cómo el modelo decide qué palabras importan
Lee esta oración: "El técnico revisó la parcela de Don Arturo y le recomendó podar los cafetos porque tenían roya". ¿Quién tenía roya? Los cafetos, no el técnico. Para saberlo relacionaste "tenían" con "cafetos", aunque hay varias palabras en medio.
Los LLM modernos se basan en la arquitectura transformer, cuyo componente central es la atención. En términos simples: para procesar cada token, el modelo calcula qué tan relevante es cada uno de los tokens anteriores y mezcla su información en esa proporción. Lo hace en muchas capas y con muchas "cabezas" en paralelo, de modo que unas aprenden relaciones gramaticales, otras relaciones de tema, otras de formato.
Un ejemplo de juguete con numpy, solo para que veas que la atención es una mezcla ponderada:
# atencion_juguete.py
import numpy as np
palabras = ["técnico", "parcela", "cafetos", "tenían"]
# vectores inventados de 3 dimensiones para cada palabra
vectores = np.array([
[0.9, 0.1, 0.0],
[0.2, 0.8, 0.1],
[0.1, 0.3, 0.9],
[0.1, 0.2, 0.8],
])
consulta = vectores[-1] # la palabra que estamos procesando: "tenían"
puntuaciones = vectores @ consulta # qué tan parecida es a cada palabra
pesos = np.exp(puntuaciones) / np.exp(puntuaciones).sum()
for p, w in zip(palabras, pesos):
print(f"{p:10s} peso de atención = {w:.2f}")
mezcla = pesos @ vectores # nueva representación de "tenían"
print("representación mezclada:", np.round(mezcla, 2))
En un modelo real los vectores tienen miles de dimensiones y se aprenden durante el entrenamiento, pero la idea es la misma: cada token "mira" a los demás y toma más información de los que le resultan relevantes. Esta idea de representar texto como vectores y compararlos con productos punto volverá con fuerza en el módulo 2, cuando hablemos de embeddings.
La ventana de contexto: todo lo que el modelo puede ver a la vez
La ventana de contexto es el número máximo de tokens que el modelo puede considerar en una sola solicitud: tus instrucciones, el historial de la conversación, los documentos que le pases y la respuesta que genera. Cada modelo tiene su límite, y además un máximo separado para la salida.
Por ejemplo, en la página de modelos de Cohere (consultada en octubre de 2026), command-a-03-2025 aparece con 256 mil tokens de contexto y 8 mil de salida, y command-a-plus-05-2026 con 128 mil de contexto y 64 mil de salida. Revisa siempre la tabla actualizada antes de diseñar tu sistema, porque los modelos cambian.
Una ventana grande no significa que debas meter todo. Tres razones:
- Costo y latencia. Pagas y esperas por cada token de entrada, en cada llamada.
- Calidad. Con mucho texto irrelevante es más fácil que el modelo se distraiga o mezcle datos.
- Actualización. Si tu reglamento cambia, es más fácil reemplazar fragmentos indexados que reconstruir un prompt enorme.
Un cálculo rápido para decidir si algo cabe:
# cabe_en_contexto.py
def cabe(tokens_documentos: int, tokens_instrucciones: int,
tokens_historial: int, tokens_respuesta: int,
ventana: int) -> bool:
total = tokens_documentos + tokens_instrucciones + tokens_historial + tokens_respuesta
print(f"total estimado: {total:,} de {ventana:,} tokens ({total / ventana:.0%})")
return total <= ventana
# reglamento completo de la cooperativa: unos 18 000 tokens medidos con tokenize
cabe(18_000, 600, 2_500, 1_000, ventana=128_000)
Aunque el reglamento quepa entero, enviarlo en cada pregunta multiplica el costo por el número de preguntas. RAG resuelve eso: solo mandas los tres a cinco fragmentos relevantes.
Lo que un LLM hace bien y lo que no
Con lo anterior ya puedes predecir el comportamiento:
- Hace bien: redactar, resumir, reformular, traducir, extraer datos de un texto que tiene enfrente, seguir un formato.
- Hace mal sin ayuda: citar datos específicos que no vio (fechas, montos, artículos de un reglamento privado), hacer cálculos largos de memoria, saber qué pasó después de su entrenamiento.
- Riesgo constante: las alucinaciones, respuestas fluidas y falsas. No es un error raro: es la consecuencia natural de elegir la continuación más probable cuando falta información.
La estrategia del curso se resume en una frase: el modelo redacta, tu sistema aporta los hechos. Los embeddings y la búsqueda encuentran los hechos; el reordenamiento elige los mejores; el chat con documentos los convierte en una respuesta con citas que Doña Rosa puede verificar.
Práctica
Dedica 25 minutos. Solo necesitas Python con numpy (pip install numpy o uv add numpy).
- Ejecuta
siguiente_token.py. Cambia las puntuaciones para que "abril" quede muy cerca de "marzo" (por ejemplo 3.2 y 3.1) y observa qué pasa con temperatura 0.2 y 1.0. - Agrega una temperatura de 0.01 y otra de 5.0. Describe en una oración qué le pasa a la distribución en cada extremo.
- En
atencion_juguete.py, cambia el vector de "tenían" a[0.9, 0.1, 0.1]y explica por qué ahora "técnico" recibe mucha más atención que "cafetos". - Escribe en un archivo de texto tres preguntas reales que un socio le haría a la cooperativa. Para cada una, anota si un LLM podría contestarla sin el reglamento. Guárdalas: las usarás en la lección 3.4 como parte del conjunto de evaluación.
- Cuando tengas tu clave (lección 1.2), vuelve y ejecuta
tokens.pycon un párrafo en español y su traducción al inglés. Compara el número de tokens.
Resumen
- Un LLM predice la siguiente pieza de texto y repite el proceso; no consulta una base de datos al responder.
- Los tokens son las unidades de costo y de límite; mide tus textos en español con
tokenize. - La atención permite que cada token tome información de los demás según su relevancia.
- La ventana de contexto incluye instrucciones, historial, documentos y respuesta; que algo quepa no significa que convenga mandarlo.
- La temperatura cambia qué tan arriesgado es el muestreo, no qué tan correcto es el conocimiento del modelo.