Inicio / Artículos / ReAct desde cero: pensamiento, acción, pausa, observación sin un marco de referencia

ReAct desde cero: pensamiento, acción, pausa, observación sin un marco de referencia

Construya a mano un pequeño bucle Groq ReAct: primero observaciones manuales y luego llamadas a herramientas basadas en expresiones regulares, para comprender por qué los marcos de trabajo de agentes modernos funcionan de la manera en que lo hacen.

1373 palabras

Introducción

Puedes preguntarle a un modelo de lenguaje sencillo el precio actual de Nvidia y cuántas acciones se pueden comprar con 100,000 dólares. Sin herramientas adecuadas, este modelo inventa una cotización obsoleta a partir de los datos de entrenamiento y luego realiza cálculos correctos con entradas erróneas. No cuenta con una función integrada que indique “No lo sé; hay que buscarlo”, ya que realizar búsquedas no implica un único proceso de procesamiento.

A medida que mejoraba la calidad de las respuestas, los modelos resultaron útiles para el razonamiento de un solo intento. El mero planificación aún no garantiza autonomía. Las preguntas que definen a los agentes son: ¿qué está fuera del conocimiento del modelo?, ¿qué acción lo adquiere?, ¿cómo producir un resultado?, ¿cómo revisar el plan? y ¿cuándo detenerse?. Decisión → actuar → verificar → replanificar es un bucle, no una línea recta. Ese bucle es el origen de los agentes de IA modernos. ReAct (Razonar + Actuar) estableció este patrón. Las demostraciones originales no eran aplicaciones de LangChain: consistían en un LLM, una instrucción estructurada y una persona (o posteriormente un script) que ejecutaba acciones e informaba las observaciones obtenidas.

Llega ReAct: Razonamiento + Acción

Un ejemplo práctico, paso a paso:

from groq import Groq
import re
from dotenv import load_dotenv

_ = load_dotenv()
client = Groq()
message = client.chat.completions.create(
    model="openai/gpt-oss-120b",  # free, fast open-weight model hosted on Groq
    max_tokens=1000,
    messages=[
        {"role": "user", "content": "Hello, GPT!"}
    ],
)
print(message.choices[0].message.content) # Check the client
class Agent:
    def __init__(self, system=""):
        self.system = system
        self.messages = []
        if self.system:
            self.messages.append({"role": "system", "content": system})

    def __call__(self, message):
        self.messages.append({"role": "user", "content": message})
        result = self.execute()
        self.messages.append({"role": "assistant", "content": result})
        return result

    def execute(self):
        response = client.chat.completions.create(
            model="openai/gpt-oss-120b",
            max_tokens=1000,
            messages=self.messages,
        ).choices[0].message.content
        return response
prompt = """
You run in a loop of Thought, Action, PAUSE, Observation.
At the end of the loop you output an Answer
Use Thought to describe your thoughts about the question you have been asked.
Use Action to run one of the actions available to you - then return PAUSE.
Observation will be the result of running those actions.

Your available actions are:

calculate:
e.g. calculate: 4 * 7 / 3
Runs a calculation and returns the number - uses Python so be sure to use floating point syntax if necessary

fish_weight:
e.g. fish_weight: Shark
returns weight of a fish when given the breed

Example session:

Question: How much does a shark weigh?
Thought: I should look the fish weight using fish_weight
Action: fish_weight: Shark
PAUSE

You will be called again with this:

Observation: A Great white shark weights 41000 lbs

You then output:

Answer: A Great white shark weights 41000 lbs
""".strip()
def calculate(expression):
    return eval(expression)

def fish_weight(name):
    if "Shark" in name:
        return("Great white shark weighs 41000 lbs")
    elif "Puffer" in name:
        return("A puffer fish weighs 20 lbs")
    else:
        return("A fish can weight upto 47000 lbs")

known_actions = {
    "calculate": calculate,
    "fish_weight": fish_weight
}
abot = Agent(prompt)
result = abot("How much does a Shark weigh?")
print(result)
result = fish_weight("Shark")
result
next_prompt = "Observation: {}".format(result)
abot(next_prompt)
abot.messages # View the list of messages containing the conversation
# Present a compund query
abot = Agent(prompt)
# New query
question = """I have 2 fishes, a Shark and a puffer fish. \
What is their combined weight"""
abot(question)
next_prompt = "Observation: {}".format(fish_weight("Shark"))
print(next_prompt)
abot(next_prompt)
next_prompt = "Observation: {}".format(fish_weight("Puffer fish"))
print(next_prompt)
abot(next_prompt)
next_prompt = "Observation: {}".format(eval("41000 + 20"))
print(next_prompt)
abot(next_prompt)
## Add loop (automate the reasoning + act process)
action_re = re.compile('^Action: (\w+): (.*)
) # python regular expression to selection action
def query(question, max_turns=5):
    i = 0
    bot = Agent(prompt)
    next_prompt = question
    while i < max_turns:
        i += 1
        result = bot(next_prompt)
        print(result)
        actions = [
            action_re.match(a)
            for a in result.split('\n')
            if action_re.match(a)
        ]
        if actions:
            # There is an action to run
            action, action_input = actions[0].groups()
            if action not in known_actions:
                raise Exception("Unknown action: {}: {}".format(action, action_input))
            print(" -- running {} {}".format(action, action_input))
            observation = known_actions[action](action_input)
            print("Observation:", observation)
            next_prompt = "Observation: {}".format(observation)
        else:
            return
question = """I have 2 fishes, a Shark and a puffer fish. \
What is their combined weight"""
query(question)
  1. Importe el cliente Groq, re para la coincidencia de patrones y load_dotenv; cargue los valores del entorno para que la clave de API no esté codificada directamente.
  2. Construya un cliente Groq que lea GROQ_API_KEY desde el entorno.
  3. Envíe un mensaje sencillo de “Hola” a un modelo configurado para confirmar la conectividad.
  4. Cree un Agent que mantenga una indicación del sistema y un historial de mensajes en constante crecimiento: cada llamada agrega el turno del usuario, envía todo el historial, agrega la respuesta del modelo y devuelve el texto.
  • Escriba el prompt del sistema que impone el proceso de Pensamiento → Acción → Pausa → Observación.
  • Registre herramientas como calculate() para cálculos aritméticos y fish_weight() para una pequeña tabla de referencias en known_actions.
  • Instancie el agente con el prompt del sistema ReAct.
  • Pregunte “¿Cuánto pesa un tiburón?”. Espere que se proponga Acción: fish_weight: Tiburón en lugar de un simple número.
  • Ejecute manualmente fish_weight("Tiburón") y guarde el resultado (primero en modo manual).
  • Proporcione nuevamente Observación: … para que el agente pueda responder.
  • Revise abot.messages para ver la transcripción completa que ve el modelo.
  • Inicie un agente nuevo con una pregunta compuesta (pesos de tiburón y pez globo). 13–15. Proporcione manualmente cada observación, incluyendo la suma total si es necesario.
  • Deje que el agente sintetice la respuesta combinada una vez que existan observaciones.
  • Agregue una expresión regular que detecte automáticamente Action: name: input.
  • Rodee el bucle con query(): nuevo agente, envíe la pregunta, imprima la salida, analice la acción, válidela contra known_actions, ejécutela, observe y repita hasta obtener la respuesta final o alcanzar el límite de intentos.
  • Ejecute query() sobre la pregunta compuesta y observe el proceso Thought → Action → Observation → Answer sin pasos manuales.
  • Conclusión

    Los agentes comienzan con un bucle, no con un marco de trabajo. El enfoque Chain-of-thought enseñó a los modelos a razonar en voz alta; ReAct estructura el proceso de pensar, actuar y observar tal como lo hacen las personas. Los marcos de trabajo populares para agentes industrializan esa idea. Construir el bucle manualmente cambia de forma permanente la percepción que se tiene de los marcos de trabajo. El artículo original está disponible en https://arxiv.org/abs/2210.03629.

    La lección práctica es que los frameworks ocultan el bucle detrás de los ejecutores y los nodos de herramienta, pero el contrato se mantiene idéntico: el modelo debe emitir una acción que pueda ser analizada, el entorno de ejecución solo debe ejecutar las herramientas permitidas, y las observaciones deben volver a formar parte del transcripción como mensajes de primera clase. Si se omite alguno de estos elementos, se obtiene un chatbot con efectos secundarios, y no un agente.

    La lección práctica es que los frameworks ocultan el bucle detrás de los ejecutores y los nodos de herramienta, pero el contrato se mantiene idéntico: el modelo debe emitir una acción que pueda ser analizada, el entorno de ejecución solo debe ejecutar las herramientas permitidas, y las observaciones deben volver a formar parte del transcripción como mensajes de primera clase. Si se omite alguno de estos elementos, se obtiene un chatbot con efectos secundarios, y no un agente.

    La lección práctica es que los frameworks ocultan el bucle detrás de los ejecutores y los nodos de herramienta, pero el contrato se mantiene idéntico: el modelo debe emitir una acción que pueda ser analizada, el entorno de ejecución solo debe ejecutar herramientas permitidas, y las observaciones deben volver a formar parte del transcripción como mensajes de primera clase. Si se omite alguno de estos elementos, se obtiene un chatbot con efectos secundarios, y no un agente.

    La lección práctica es que los frameworks ocultan el bucle detrás de los ejecutores y los nodos de herramienta, pero el contrato se mantiene idéntico: el modelo debe emitir una acción que pueda ser analizada, el entorno de ejecución solo debe ejecutar herramientas permitidas, y las observaciones deben volver a formar parte del transcripción como mensajes de primera clase. Si se omite alguno de estos elementos, se obtiene un chatbot con efectos secundarios, y no un agente.

    Lecturas relacionadas

  • Agentes ReAct en LangGraph: Pensamiento, Acción y Observación paso a paso — Implementa el bucle ReAct como nodos de gráfico explícitos con estado tipado, llamadas a herramientas y condiciones de detención que puedes probar.
  • Patrones de prompt para flujos de trabajo al estilo Astra de GPT-6 — Estructura el sistema, las herramientas y los ejemplos de modo que los prompts largos sigan siendo reutilizables en chats y hosts de agentes.