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.