El bucle de siguiente token: un modelo mental de los LLM antes de interactuar con agentes
Aprenda cómo funcionan los tokens, las ventanas de contexto, el muestreo y el bucle de generación, a través de pequeños ejemplos en Python sin conexión que explican por qué existen RAG, ReAct y LangGraph.
Los ingenieros que interactúan con los LLM a través de un framework como LangChain a menudo no pueden determinar, cuando algo falla, si el problema radica en el framework, en la API que lo rodea o en el modelo en sí. La solución es contar con un modelo mental claro: un LLM es una función de predicción del siguiente token que se llama repetidamente en un bucle. Con esta visión, RAG es una forma de elegir qué se introduce en la función, ReAct es un método para analizar lo que sale y LangGraph es una forma de orquestar múltiples llamadas. Los ejemplos a continuación se ejecutan localmente con Python y una biblioteca pequeña; no se necesita clave de API.
La generación consiste en la predicción del siguiente token en un bucle
Dada una secuencia de tokens, el modelo devuelve una probabilidad para cada token siguiente posible. Se elige uno, se agrega y la secuencia más larga vuelve a introducirse en el proceso. Esto se repite hasta que aparece un token de detención o se alcanza un límite de longitud. La salida en flujo simplemente muestra este bucle token por token.
Se derivan dos consecuencias:
- No existe un plan. Cada token depende únicamente de los tokens que lo preceden.
- No hay memoria entre llamadas. Cada solicitud comienza exactamente con los tokens que envías, y esa es la razón por la cual existe la generación mejorada con recuperación de información.
Los tokens son fragmentos subpalabras, no palabras
Un modelo percibe números enteros, no texto. Un tokenizador mapea cadenas de texto a IDs de tokens y viceversa. Las palabras frecuentes suelen convertirse en un único token, mientras que las palabras poco comunes y el código se dividen en varios. Una estimación útil: un token equivale aproximadamente a tres cuartos de una palabra.
El fragmento carga o200k_base, la codificación de la familia gpt-4o, a través de tiktoken; codifica una oración y muestra cada ID junto con su fragmento de texto. Observa que “LangGraph” se convierte en dos tokens y que los espacios al inicio pertenecen al token siguiente.
import tiktoken
# tokenizer of the gpt-4o model family
enc = tiktoken.get_encoding("o200k_base")
ids = enc.encode("LangGraph orchestrates agents.")
print(ids)
# [30741, 9922, 109873, 1381, 19297, 13]
for i in ids:
print(i, repr(enc.decode([i])))
# 'Lang' 'Graph' ' orchestr' 'ates' ' agents' '.'
El entrenamiento y la inferencia son fases separadas
El entrenamiento ajusta los pesos al predecir los tokens siguientes a partir de grandes cantidades de texto. La inferencia ejecuta el modelo ya confeccionado. Tu prompt nunca modifica los pesos, por lo que la información que le falta al modelo debe incluirse en el prompt en el momento de la inferencia. Eso es exactamente lo que hace RAG.
La ventana de contexto es todo el mundo del modelo
Una sola llamada solo puede procesar una cierta cantidad de tokens, y ese límite, la ventana de contexto, abarca tanto el prompt como la salida generada. Todo lo que está fuera de ella no existe para esa llamada. La conversación parece continua solo porque la aplicación vuelve a enviar los mensajes anteriores cada vez, y RAG existe porque las colecciones de documentos no caben en la ventana, por lo que se recuperan solo algunos fragmentos relevantes.
El presupuesteo es una aritmética sencilla. La herramienta cuenta los tokens de entrada con el tokenizador mencionado anteriormente y los resta, además de una reserva para la respuesta, de un rango de 128,000 tokens, que es el valor indicado para gpt-4o-mini. Los límites difieren entre modelos y versiones, así que consulte la documentación actual de su proveedor.
def count_tokens(text: str) -> int:
return len(enc.encode(text))
prompt = "Summarize the attached design doc."
window = 128_000 # e.g. gpt-4o-mini
reserve = 1_000 # room for the answer
used = count_tokens(prompt)
print("left:", window - reserve - used)
La temperatura controla cómo se elige el siguiente token
El muestreo selecciona un token de la distribución del modelo, y la temperatura modifica esa distribución. Cerca de cero, casi siempre gana el token más probable. A 1.0 o más, los tokens menos probables tienen una verdadera oportunidad. Utilice una temperatura de 0 para la extracción, la salida estructurada y los agentes que llaman a herramientas, y aproximadamente de 0.7 a 1.0 para la redacción y el brainstorming. Nuestra guía sobre la temperatura, top-k y top-p aborda los otros parámetros de muestreo.
Construyendo el bucle en miniatura
El siguiente ejemplo combina un tokenizador real con un modelo falso creado a partir de una tabla de consulta. Es muy pequeño, pero sigue el mismo ciclo que un LLM en producción: codificar, predecir, añadir y detenerse.
Primero, se tokeniza un pequeño corpus y se cuenta cada par adyacente de IDs. Para cada token, la tabla solo conserva su sucesor más frecuente, lo que convierte a este en un predictor codicioso, equivalente a una temperatura de cero.
CORPUS = (
"the agent calls the model. "
"the model returns a token. "
"the agent calls the tool. "
"the tool returns a result."
)
ids = enc.encode(CORPUS)
counts = {}
for a, b in zip(ids, ids[1:]):
counts.setdefault(a, {})
counts[a][b] = counts[a].get(b, 0) + 1
# greedy "model": token -> likeliest successor
table = {
a: max(s, key=s.get)
for a, s in counts.items()
}
El bucle codifica una prompt, busca el sucesor más probable del último token, lo añade y muestra el texto decodificado. Si falta un sucesor, se devuelve None, que sirve como token de detención. La línea clave es la adición: la salida se convierte en entrada.
seq = enc.encode("the agent calls")
for _ in range(3):
nxt = table.get(seq[-1])
if nxt is None: # our toy "stop token"
break
seq.append(nxt) # output becomes input
print(enc.decode(seq))
La secuencia crece un token por paso:
the agent calls the
the agent calls the model
the agent calls the model.
Un detalle es ilustrativo. En el corpus, “the” va seguido por “model” y “tool” con la misma frecuencia, y max elige al que ve primero. Los modelos reales se enfrentan constantemente a tales empates, por lo que los ajustes de muestreo son importantes.
Ejecutar los ejemplos
Reúna los fragmentos en un único script, por ejemplo examples/part01_tokens.py, junto con un requirements.txt que incluya tiktoken. Este script imprime las divisiones de tokens, realiza los cálculos relacionados con el presupuesto y ejecuta el bucle de ejemplo en aproximadamente un segundo, de forma offline una vez que los datos del tokenizador están almacenados en caché. Luego cambie el corpus y observe cómo varía la salida.
pip install -r requirements.txt
python examples/part01_tokens.py
Conclusiones clave
- Un LLM es una función fija que convierte una secuencia de tokens en una distribución de tokens siguientes, ejecutada dentro de un bucle de retroalimentación.
Lecturas relacionadas
- Comprendiendo los agentes de IA: objetivos, herramientas, memoria y el bucle del agente — Una explicación adecuada para principiantes sobre cómo difieren los agentes de IA de los chatbots, que abarca componentes clave, el bucle de decisión, los niveles de autonomía y casos de uso en el mundo real.
- LangGraph vs Pydantic AI: Por qué el modelo, y no el framework, determinó la precisión — Una prueba de rendimiento controlada con 160 ejecuciones de LangGraph y Pydantic AI muestra una precisión idéntica en las llamadas a las herramientas, y revela que la elección del modelo y el diseño de la evaluación son mucho más importantes.
- Depuración de un pequeño GPT en PyTorch: Pruebas que aíslan cada error — Un flujo de trabajo paso a paso para depurar un GPT a nivel de carácter en PyTorch, desde los IDs de tokens y el desplazamiento de objetivos hasta los gradientes, las pérdidas NaN y los puntos de control.
- Presupuestos de tokens vs. límites semánticos: Cómo dividir correctamente los fragmentos RAG — Aprenda cómo difieren la tokenización y el chunking, cuál es su papel en un pipeline RAG, y cómo dividir documentos según su significado al mismo tiempo que mide el tamaño con el tokenizador real.