TechLead
Leccion 3 de 24
7 min de lectura
Agentes IA

Patrón de Agente ReAct

Domina el patrón de bucle Razonamiento + Acción para construir agentes que piensan antes de actuar

Entendiendo el Patrón ReAct

El patrón ReAct (Razonamiento + Acción) es una de las arquitecturas de agentes más influyentes en la IA moderna. Introducido por Yao et al. en su artículo de 2022 "ReAct: Synergizing Reasoning and Acting in Language Models", este patrón crea agentes que generan trazas de razonamiento verbal junto con acciones, produciendo una sinergia poderosa entre pensar y hacer.

A diferencia del prompting de cadena de pensamiento (que solo razona) o los agentes de solo acción (que actúan sin razonamiento explícito), ReAct intercala ambos. El agente declara explícitamente lo que está pensando, decide una acción, observa el resultado y actualiza su razonamiento — creando un proceso de toma de decisiones transparente y depurable.

El Ciclo ReAct

  • Pensamiento: El agente razona sobre la situación actual, qué información tiene y qué necesita
  • Acción: Basándose en el razonamiento, el agente selecciona una herramienta y proporciona parámetros de entrada
  • Observación: El agente recibe la salida de la herramienta y la incorpora a su contexto
  • Repetir: El ciclo continúa hasta que el agente tiene suficiente información para dar una respuesta final

Por Qué Funciona ReAct

El poder de ReAct proviene de las trazas de razonamiento explícitas. Cuando un agente piensa en voz alta antes de actuar, surgen varios beneficios:

  • Decisiones Fundamentadas: Las acciones están vinculadas a razonamiento explícito, reduciendo alucinaciones y llamadas aleatorias a herramientas
  • Recuperación de Errores: Cuando una acción falla, el agente puede razonar sobre por qué y ajustar su enfoque
  • Transparencia: Los desarrolladores pueden leer las trazas de pensamiento para entender y depurar el comportamiento del agente
  • Mantenimiento del Contexto: La traza de razonamiento actúa como memoria de trabajo, rastreando lo que el agente sabe y aún necesita
  • Auto-Corrección: El agente puede notar errores en su propio razonamiento y corregir el rumbo

Implementando ReAct desde Cero

Construyamos un agente ReAct completo desde cero para entender cada componente. Esta implementación muestra la mecánica sin depender de un framework.

// Complete ReAct agent implementation from scratch
import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic();

// Define tool interfaces
interface ToolDefinition {
  name: string;
  description: string;
  parameters: Record<string, { type: string; description: string }>;
  required: string[];
}

interface ToolResult {
  success: boolean;
  data: string;
}

// Implement tools
const tools: Record<string, {
  definition: ToolDefinition;
  execute: (params: Record<string, string>) => Promise<ToolResult>;
}> = {
  search: {
    definition: {
      name: "search",
      description: "Search the web for current information",
      parameters: {
        query: { type: "string", description: "Search query" },
      },
      required: ["query"],
    },
    execute: async (params) => {
      const res = await fetch(
        `https://api.tavily.com/search`,
        {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({
            api_key: process.env.TAVILY_API_KEY,
            query: params.query,
            max_results: 3,
          }),
        }
      );
      const data = await res.json();
      return {
        success: true,
        data: data.results
          .map((r: any) => `${r.title}: ${r.content}`)
          .join("\n\n"),
      };
    },
  },
  calculate: {
    definition: {
      name: "calculate",
      description: "Evaluate a mathematical expression",
      parameters: {
        expression: { type: "string", description: "Math expression to evaluate" },
      },
      required: ["expression"],
    },
    execute: async (params) => {
      try {
        const result = Function(`"use strict"; return (${params.expression})`)();
        return { success: true, data: String(result) };
      } catch (e) {
        return { success: false, data: `Error: ${e}` };
      }
    },
  },
};

// The ReAct agent loop
async function reactAgent(question: string, maxSteps = 8): Promise<string> {
  const toolDescriptions = Object.values(tools)
    .map(t => `- ${t.definition.name}: ${t.definition.description}`)
    .join("\n");

  const systemPrompt = `You are a ReAct agent. For each step, you MUST follow this format exactly:

Thought: [Your reasoning about what to do next]
Action: [tool_name]
Action Input: [JSON parameters for the tool]

When you have enough information to answer, use:
Thought: [Final reasoning]
Final Answer: [Your complete answer]

Available tools:
${toolDescriptions}

Important rules:
1. Always think before acting
2. Use observations to refine your approach
3. Never make up information - only use tool results
4. Provide a Final Answer when you have sufficient information`;

  let scratchpad = `Question: ${question}\n`;
  let steps = 0;

  while (steps < maxSteps) {
    const response = await client.messages.create({
      model: "claude-sonnet-4-20250514",
      max_tokens: 1024,
      system: systemPrompt,
      messages: [{ role: "user", content: scratchpad }],
    });

    const text = response.content[0].type === "text"
      ? response.content[0].text
      : "";

    scratchpad += text + "\n";

    // Check for final answer
    if (text.includes("Final Answer:")) {
      const answer = text.split("Final Answer:")[1].trim();
      return answer;
    }

    // Parse action and execute
    const actionMatch = text.match(/Action:\s*(.+)/);
    const inputMatch = text.match(/Action Input:\s*(.+)/);

    if (actionMatch && inputMatch) {
      const toolName = actionMatch[1].trim();
      const toolInput = JSON.parse(inputMatch[1].trim());
      const tool = tools[toolName];

      if (tool) {
        const result = await tool.execute(toolInput);
        scratchpad += `Observation: ${result.data}\n`;
      } else {
        scratchpad += `Observation: Tool "${toolName}" not found.\n`;
      }
    }

    steps++;
  }

  return "I was unable to find a complete answer within the step limit.";
}

// Usage
const answer = await reactAgent(
  "What is the current population of Japan and what percentage of the world population is that?"
);
console.log(answer);
# Complete ReAct agent in Python
import anthropic
import json
import re

client = anthropic.Anthropic()

# Define tools
def search_web(query: str) -> str:
    """Search the web for information."""
    import requests
    response = requests.post(
        "https://api.tavily.com/search",
        json={"api_key": os.environ["TAVILY_API_KEY"], "query": query, "max_results": 3}
    )
    results = response.json().get("results", [])
    return "\n\n".join(f"{r['title']}: {r['content']}" for r in results)

def calculate(expression: str) -> str:
    """Evaluate a math expression safely."""
    try:
        # Use a safe evaluator
        allowed_names = {"__builtins__": {}}
        result = eval(expression, allowed_names)
        return str(result)
    except Exception as e:
        return f"Error: {e}"

TOOLS = {
    "search": {"fn": search_web, "desc": "Search the web for current information"},
    "calculate": {"fn": calculate, "desc": "Evaluate a mathematical expression"},
}

def react_agent(question: str, max_steps: int = 8) -> str:
    tool_desc = "\n".join(f"- {name}: {t['desc']}" for name, t in TOOLS.items())

    system = f"""You are a ReAct agent. Follow this format:

Thought: [reasoning]
Action: [tool_name]
Action Input: {{"param": "value"}}

When ready to answer:
Thought: [final reasoning]
Final Answer: [answer]

Tools:
{tool_desc}"""

    scratchpad = f"Question: {question}\n"

    for step in range(max_steps):
        response = client.messages.create(
            model="claude-sonnet-4-20250514",
            max_tokens=1024,
            system=system,
            messages=[{"role": "user", "content": scratchpad}],
        )

        text = response.content[0].text
        scratchpad += text + "\n"

        # Check for final answer
        if "Final Answer:" in text:
            return text.split("Final Answer:")[1].strip()

        # Parse and execute action
        action_match = re.search(r"Action:\s*(.+)", text)
        input_match = re.search(r"Action Input:\s*(.+)", text)

        if action_match and input_match:
            tool_name = action_match.group(1).strip()
            tool_input = json.loads(input_match.group(1).strip())

            if tool_name in TOOLS:
                result = TOOLS[tool_name]["fn"](**tool_input)
                scratchpad += f"Observation: {result}\n"
            else:
                scratchpad += f"Observation: Tool '{tool_name}' not found\n"

    return "Max steps reached without finding an answer."

# Usage
answer = react_agent("What are the top 3 AI frameworks in 2026 by GitHub stars?")
print(answer)

Ejemplo de Traza ReAct

Ejemplo de Traza de Ejecución ReAct

Pregunta: "¿Cuál es la capitalización de mercado de NVIDIA y cómo se compara con Apple?"

Pensamiento 1: Necesito encontrar la capitalización de mercado actual de NVIDIA. Déjame buscar esto.
Acción: search("NVIDIA market cap 2026")
Observación: La capitalización de mercado de NVIDIA es aproximadamente $3.2 billones a partir del Q1 2026...
Pensamiento 2: Ahora necesito la capitalización de mercado de Apple para compararlas.
Acción: search("Apple market cap 2026")
Observación: La capitalización de mercado de Apple es aproximadamente $3.8 billones a partir del Q1 2026...
Pensamiento 3: Ahora tengo ambos valores. Puedo calcular la comparación.
Respuesta Final: La capitalización de mercado de NVIDIA es ~$3.2T mientras que la de Apple es ~$3.8T. Apple es mayor por aproximadamente $600B (aproximadamente 19% más grande).

Errores Comunes de ReAct

  • Bucles Infinitos: El agente sigue buscando sin converger. Siempre establece un límite máximo de pasos.
  • Herramientas Alucinadas: El agente intenta usar herramientas que no existen. Valida los nombres de herramientas antes de la ejecución.
  • Deriva del Razonamiento: Los pensamientos del agente divergen de la pregunta original. Incluye la pregunta en cada indicación.
  • Sobre-pensamiento: El agente razona demasiados pasos cuando la respuesta es directa. Usa temperature=0.
  • Desbordamiento de Ventana de Contexto: Las libretas de notas largas exceden la ventana de contexto del modelo. Resume periódicamente.

Resumen

El patrón ReAct es la base del desarrollo moderno de agentes. Su fortaleza radica en hacer explícito y transparente el razonamiento del agente. Ya sea que lo implementes desde cero o uses un framework como LangGraph, el bucle central — pensar, actuar, observar — sigue siendo el mismo. Dominar ReAct te da los bloques de construcción para entender todas las demás arquitecturas de agentes.

Continuar Aprendiendo