Razonar sin actuar es alucinar con estilo
Pide a un LLM que resuelva un problema real de un tirón y verás dos fallos recurrentes. Si le dejas pensar en voz alta pero sin tocar el mundo, produce un razonamiento impecable construido sobre datos inventados. Y si le das herramientas pero le prohíbes deliberar, encadena llamadas sin criterio hasta que se pierde. El primero alucina con estilo; el segundo actúa a ciegas.
ReAct —de Reasoning and Acting— parte de una idea simple: no hay que elegir. En cada paso, el modelo escribe un pensamiento, decide una acción, la ejecuta contra una herramienta y lee el resultado antes de volver a pensar. El razonamiento guía la siguiente acción; la acción corrige el siguiente razonamiento. Ese bucle es la base de casi todos los agentes que hoy funcionan en producción, aunque los frameworks lo escondan detrás de abstracciones.
El bucle en tres verbos
El ciclo tiene tres momentos que se repiten hasta que la tarea se cierra.
El pensamiento (thought) es texto donde el modelo razona sobre en qué punto está y qué le falta. No se ejecuta; sirve para orientar la decisión y para dejar una traza legible de por qué hizo lo que hizo.
La acción (action) es una llamada a herramienta con sus argumentos: buscar en un índice, consultar una API, ejecutar una consulta SQL. Es el único punto donde el agente toca datos reales.
La observación (observation) es lo que devuelve esa herramienta y vuelve al contexto. Aquí el mundo real contradice o confirma lo que el modelo suponía, y ese choque es justo lo que evita que el razonamiento se despegue de los hechos.
La clave está en el orden: observar antes de volver a pensar. Un agente que planifica los diez pasos de golpe y luego los ejecuta a ciegas no es ReAct; es una lista de la compra que nadie revisa. ReAct intercala, y por eso se recupera de los errores en lugar de arrastrarlos.
Cómo se ve por dentro
Los frameworks tipo LangChain o LlamaIndex traen ReAct empaquetado, pero conviene haberlo escrito a mano una vez para saber qué estás delegando. El núcleo cabe en unas pocas líneas.
def agente_react(pregunta, herramientas, modelo, max_pasos=6):
# Historial acumulado: pensamiento, acción y observación de cada paso
scratchpad = ""
for paso in range(max_pasos):
prompt = (
f"Pregunta: {pregunta}\n{scratchpad}\n"
"Piensa el siguiente paso y responde con Accion: <tool>[args] "
"o con Respuesta: <final> si ya lo sabes."
)
salida = modelo.generar(prompt, stop=["Observacion:"]) # corta antes de inventarse la observación
scratchpad += salida
if "Respuesta:" in salida:
return salida.split("Respuesta:")[-1].strip()
# Parsea la acción y ejecútala de verdad; la observación NO la escribe el modelo
nombre, args = parsear_accion(salida)
observacion = herramientas[nombre](args)
scratchpad += f"\nObservacion: {observacion}\n"
return "Sin respuesta tras agotar los pasos."
Fíjate en dos detalles que separan una demo de algo usable. El stop=["Observacion:"] impide que el modelo se invente el resultado de la herramienta: el texto de la observación lo escribe tu código, no el LLM. Y el max_pasos es un cortacircuitos: sin él, un agente confundido entra en bucle y te vacía la cuenta de la API. Ninguno de los dos es opcional.
ReAct frente a las alternativas
No todo problema pide un agente ReAct. Elegir mal encarece y ralentiza sin ganar nada.
| Enfoque | Cómo decide | Cuándo encaja |
|---|---|---|
| Llamada directa | Una sola generación, sin herramientas | La respuesta cabe en lo que el modelo ya sabe |
| Cadena fija | Pasos predefinidos en orden | El flujo es siempre el mismo y lo conoces de antemano |
| Plan y ejecución | Planifica todo, luego ejecuta | Tareas largas y estables donde replanificar sale caro |
| ReAct | Piensa y actúa alternando cada paso | El camino depende de lo que vayas descubriendo |
La regla práctica: usa ReAct cuando no puedas dibujar el diagrama de flujo por adelantado porque la ruta correcta depende de datos que solo obtienes al ejecutar. Si sí puedes dibujarlo, una cadena fija será más barata, más rápida y más fácil de depurar. Meter un agente donde bastaba una función es el error de diseño más común que veo.
Dónde se rompe
ReAct es robusto, pero tiene tres modos de fallo que conviene anticipar.
El primero es el bucle de terquedad: el modelo repite la misma acción fallida esperando otro resultado. La observación le dice “no encontrado” y él vuelve a buscar igual. Se ataja detectando acciones repetidas y forzando un cambio de estrategia o una salida limpia.
El segundo es la traza que crece sin límite. Cada paso añade pensamiento, acción y observación al contexto; en tareas largas eso infla el coste por token y diluye la atención del modelo. Resumir observaciones antiguas o descartar las irrelevantes mantiene el scratchpad a raya.
El tercero es la observación envenenada. Si una herramienta devuelve contenido de fuentes externas —una página web, un correo—, ese texto entra directo en el razonamiento del agente y puede contener instrucciones inyectadas. La observación no es un dato neutro; es una superficie de ataque. Trátala como entrada no confiable y no dejes que decida acciones sensibles sin una comprobación intermedia.
Qué llevarte
ReAct no es un framework ni una librería: es un patrón de control que puedes implementar en un bucle for. Entenderlo te da tres cosas. Sabes qué hacen por dentro LangChain, LlamaIndex o el harness que uses, y puedes depurarlos cuando fallan. Distingues cuándo un problema pide agente y cuándo pide una simple cadena, que es la mitad de la batalla del diseño. Y ves los puntos de fractura —bucles, contexto inflado, observaciones envenenadas— antes de que aparezcan en producción.
Si vas a montar tu primer agente, escríbelo a mano una vez con el bucle de arriba. Pon el cortacircuitos de pasos desde el minuto uno, corta la generación antes de la observación y trata todo lo que devuelvan las herramientas como entrada hostil. Cuando eso te quede natural, adopta un framework: ya sabrás qué te está resolviendo y qué sigue siendo tu responsabilidad.