Inicio / LLMOps / LLMOps: De Prototipo a Producción / Agentes y Tool Calling

Agentes y Tool Calling

Arquitectura de agentes, function calling, ReAct y multi-agent.

Intermedio Funciones
🔒 Solo lectura
📖

Estás en modo lectura

Puedes leer toda la lección, pero para marcar progreso, hacer ejercicios y ganar XP necesitas una cuenta Pro.

Desbloquear por $9/mes

Agentes y Tool Calling

¿Qué es un Agente LLM?

Un agente es un LLM que puede tomar decisiones y ejecutar acciones de forma autónoma. En lugar de solo generar texto, el agente puede usar herramientas (APIs, bases de datos, código) para resolver tareas complejas.


Arquitectura de un Agente

┌─────────────────────────────────────────┐
│               AGENTE LLM                │
│                                          │
│  ┌──────────────────────────────┐       │
│  │         LLM (cerebro)        │       │
│  │  - Razona sobre la tarea     │       │
│  │  - Decide qué herramienta   │       │
│  │  - Interpreta resultados    │       │
│  └──────────┬───────────────────┘       │
│             │                            │
│  ┌──────────▼───────────────────┐       │
│  │      HERRAMIENTAS            │       │
│  │  ┌──────┐ ┌─────┐ ┌──────┐ │       │
│  │  │ API  │ │ SQL │ │ Code │ │       │
│  │  └──────┘ └─────┘ └──────┘ │       │
│  │  ┌──────┐ ┌─────┐ ┌──────┐ │       │
│  │  │Search│ │File │ │ HTTP │ │       │
│  │  └──────┘ └─────┘ └──────┘ │       │
│  └──────────────────────────────┘       │
│                                          │
│  ┌──────────────────────────────┐       │
│  │         MEMORIA              │       │
│  │  - Historial de conversación │       │
│  │  - Resultados de tools       │       │
│  │  - Estado de la tarea        │       │
│  └──────────────────────────────┘       │
└─────────────────────────────────────────┘

Function Calling (Tool Use)

Definir Herramientas

tools = [
    {
        "type": "function",
        "function": {
            "name": "search_database",
            "description": "Buscar productos en la base de datos por nombre o categoría",
            "parameters": {
                "type": "object",
                "properties": {
                    "query": {"type": "string", "description": "Texto de búsqueda"},
                    "category": {
                        "type": "string",
                        "enum": ["electronics", "books", "clothing"],
                        "description": "Categoría del producto"
                    },
                    "max_results": {
                        "type": "integer",
                        "default": 5,
                        "description": "Número máximo de resultados"
                    }
                },
                "required": ["query"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "get_order_status",
            "description": "Obtener el estado de un pedido por su ID",
            "parameters": {
                "type": "object",
                "properties": {
                    "order_id": {"type": "string", "description": "ID del pedido (ej: ORD-12345)"}
                },
                "required": ["order_id"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "send_email",
            "description": "Enviar un email al cliente",
            "parameters": {
                "type": "object",
                "properties": {
                    "to": {"type": "string", "description": "Email del destinatario"},
                    "subject": {"type": "string"},
                    "body": {"type": "string"}
                },
                "required": ["to", "subject", "body"]
            }
        }
    }
]

Implementar el Loop del Agente

import json

# Implementaciones reales de las tools
TOOL_IMPLEMENTATIONS = {
    "search_database": lambda **kwargs: [{"name": "Laptop Pro", "price": 999}],
    "get_order_status": lambda **kwargs: {"status": "shipped", "eta": "2 días"},
    "send_email": lambda **kwargs: {"sent": True},
}

def run_agent(user_message: str, tools: list, max_iterations: int = 10):
    messages = [
        {"role": "system", "content": "Eres un asistente de e-commerce. Usa las herramientas disponibles."},
        {"role": "user", "content": user_message},
    ]
    
    for i in range(max_iterations):
        response = client.chat.completions.create(
            model="gpt-4o",
            messages=messages,
            tools=tools,
            tool_choice="auto",
        )
        
        msg = response.choices[0].message
        messages.append(msg)
        
        # Si no hay tool calls, el agente terminó
        if not msg.tool_calls:
            return msg.content
        
        # Ejecutar cada tool call
        for tool_call in msg.tool_calls:
            fn_name = tool_call.function.name
            fn_args = json.loads(tool_call.function.arguments)
            
            print(f"  🔧 Ejecutando: {fn_name}({fn_args})")
            
            # Ejecutar la función
            result = TOOL_IMPLEMENTATIONS[fn_name](**fn_args)
            
            # Agregar resultado al historial
            messages.append({
                "role": "tool",
                "tool_call_id": tool_call.id,
                "content": json.dumps(result),
            })
    
    return "Max iteraciones alcanzadas"

# Uso
answer = run_agent("¿Cuál es el estado de mi pedido ORD-12345?", tools)

Agentes con LangChain

from langchain_openai import ChatOpenAI
from langchain.agents import AgentExecutor, create_tool_calling_agent
from langchain_core.tools import tool
from langchain_core.prompts import ChatPromptTemplate

@tool
def calculator(expression: str) -> str:
    """Evalúa una expresión matemática y retorna el resultado."""
    try:
        return str(eval(expression))
    except Exception as e:
        return f"Error: {e}"

@tool
def search_web(query: str) -> str:
    """Busca información en la web."""
    return f"Resultados para '{query}': [resultado simulado]"

# Crear agente
llm = ChatOpenAI(model="gpt-4o")
prompt = ChatPromptTemplate.from_messages([
    ("system", "Eres un asistente útil con acceso a herramientas."),
    ("human", "{input}"),
    ("placeholder", "{agent_scratchpad}"),
])

agent = create_tool_calling_agent(llm, [calculator, search_web], prompt)
executor = AgentExecutor(agent=agent, tools=[calculator, search_web], verbose=True)

result = executor.invoke({"input": "¿Cuánto es 15% de 2500?"})

Patrones de Agentes

ReAct (Reasoning + Acting)

El agente alterna entre pensar y actuar:

Thought: Necesito buscar el pedido del cliente
Action: get_order_status(order_id="ORD-12345")
Observation: {"status": "shipped", "eta": "2 días"}
Thought: Ya tengo la información, puedo responder
Answer: Su pedido está en camino, llegará en 2 días.

Multi-Agent Systems

class MultiAgentSystem:
    def __init__(self):
        self.agents = {
            "router": RouterAgent(),      # Decide qué agente usar
            "support": SupportAgent(),     # Soporte técnico
            "sales": SalesAgent(),         # Ventas
            "billing": BillingAgent(),     # Facturación
        }
    
    def process(self, message: str) -> str:
        # Router decide qué agente especializados
        target = self.agents["router"].route(message)
        return self.agents[target].handle(message)

Seguridad en Agentes

class SafeAgentExecutor:
    DANGEROUS_FUNCTIONS = {"delete_user", "drop_database", "send_bulk_email"}
    
    def execute_tool(self, fn_name: str, args: dict, requires_approval: bool = False):
        # 1. Validar que la función existe
        if fn_name not in TOOL_IMPLEMENTATIONS:
            raise ValueError(f"Tool desconocida: {fn_name}")
        
        # 2. Bloquear funciones peligrosas
        if fn_name in self.DANGEROUS_FUNCTIONS:
            raise PermissionError(f"Tool bloqueada: {fn_name}")
        
        # 3. Sanitizar inputs
        args = self._sanitize_args(args)
        
        # 4. Rate limiting
        if not self._check_rate_limit(fn_name):
            raise Exception("Rate limit excedido")
        
        # 5. Ejecutar con timeout
        return self._execute_with_timeout(fn_name, args, timeout=30)

Resumen

Los agentes LLM son la evolución natural: modelos que no solo generan texto sino que actúan en el mundo. Function calling es el mecanismo core, el loop ReAct es el patrón dominante, y la seguridad (validación, rate limiting, aprobación) es crítica en producción.

🔒

Ejercicio práctico disponible

Sistema de herramientas para agentes

Desbloquear ejercicios
// Sistema de herramientas para agentes
// Desbloquea Pro para acceder a este ejercicio
// y ganar +50 XP al completarlo

function ejemplo() {
    // Tu código aquí...
}

¿Te gustó esta lección?

Con Pro puedes marcar progreso, hacer ejercicios, tomar quizzes, ganar XP y obtener tu constancia.

Ver planes desde $9/mes