Inicio / LLMOps / LLMOps: De Prototipo a Producción / APIs de LLMs

APIs de LLMs

OpenAI, Anthropic, retry, fallback, rate limiting y structured outputs.

Principiante

APIs de LLMs

Trabajar con APIs de Modelos

En LLMOps, la interacción con LLMs generalmente se realiza a través de APIs HTTP. Dominar las APIs es fundamental para construir aplicaciones robustas.


OpenAI API

Chat Completion

from openai import OpenAI

client = OpenAI(api_key="sk-...")

response = client.chat.completions.create(
    model="gpt-4o",
    messages=[
        {"role": "system", "content": "Eres un experto en Python."},
        {"role": "user", "content": "¿Qué es un decorador?"}
    ],
    temperature=0.7,
    max_tokens=500,
    top_p=0.9,
)

print(response.choices[0].message.content)
print(f"Tokens usados: {response.usage.total_tokens}")
print(f"Costo: ${response.usage.prompt_tokens * 0.0000025 + response.usage.completion_tokens * 0.00001:.6f}")

Streaming

stream = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "Explica microservicios"}],
    stream=True,
)

for chunk in stream:
    content = chunk.choices[0].delta.content
    if content:
        print(content, end="", flush=True)

Anthropic API (Claude)

import anthropic

client = anthropic.Anthropic(api_key="sk-ant-...")

message = client.messages.create(
    model="claude-sonnet-4-20250514",
    max_tokens=1024,
    system="Eres un arquitecto de software senior.",
    messages=[
        {"role": "user", "content": "Diseña una arquitectura de microservicios para un e-commerce."}
    ]
)

print(message.content[0].text)
print(f"Tokens input: {message.usage.input_tokens}")
print(f"Tokens output: {message.usage.output_tokens}")

Patrones de Producción con APIs

1. Retry con Exponential Backoff

import time
from openai import OpenAI, RateLimitError, APIError

client = OpenAI()

def call_llm_with_retry(messages, max_retries=3):
    for attempt in range(max_retries):
        try:
            response = client.chat.completions.create(
                model="gpt-4o",
                messages=messages,
                timeout=30,
            )
            return response.choices[0].message.content
        except RateLimitError:
            wait = 2 ** attempt
            print(f"Rate limited. Esperando {wait}s...")
            time.sleep(wait)
        except APIError as e:
            if attempt == max_retries - 1:
                raise
            time.sleep(1)
    raise Exception("Max retries exceeded")

2. Fallback entre Modelos

class LLMRouter:
    def __init__(self):
        self.openai = OpenAI()
        self.anthropic = anthropic.Anthropic()
    
    def complete(self, prompt, **kwargs):
        """Intentar OpenAI primero, fallback a Anthropic."""
        try:
            resp = self.openai.chat.completions.create(
                model="gpt-4o",
                messages=[{"role": "user", "content": prompt}],
                timeout=15,
                **kwargs,
            )
            return {"text": resp.choices[0].message.content, "model": "gpt-4o"}
        except Exception as e:
            print(f"OpenAI falló: {e}. Fallback a Claude...")
            msg = self.anthropic.messages.create(
                model="claude-sonnet-4-20250514",
                max_tokens=1024,
                messages=[{"role": "user", "content": prompt}],
            )
            return {"text": msg.content[0].text, "model": "claude-sonnet"}

3. Rate Limiting

import asyncio
from collections import deque

class TokenBucket:
    def __init__(self, tokens_per_minute=60000):
        self.capacity = tokens_per_minute
        self.tokens = tokens_per_minute
        self.last_refill = time.time()
    
    async def acquire(self, tokens_needed):
        while True:
            now = time.time()
            elapsed = now - self.last_refill
            self.tokens = min(
                self.capacity,
                self.tokens + elapsed * (self.capacity / 60)
            )
            self.last_refill = now
            
            if self.tokens >= tokens_needed:
                self.tokens -= tokens_needed
                return
            
            await asyncio.sleep(0.1)

Structured Outputs (JSON Mode)

from pydantic import BaseModel

class ProductReview(BaseModel):
    sentiment: str  # "positive", "negative", "neutral"
    score: float    # 0.0 a 1.0
    keywords: list[str]
    summary: str

response = client.beta.chat.completions.parse(
    model="gpt-4o",
    messages=[
        {"role": "system", "content": "Analiza la siguiente reseña."},
        {"role": "user", "content": "El producto es increíble, muy buena calidad."}
    ],
    response_format=ProductReview,
)

review = response.choices[0].message.parsed
print(f"Sentimiento: {review.sentiment}, Score: {review.score}")

Function Calling / Tool Use

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "Obtener el clima actual de una ciudad",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {"type": "string", "description": "Nombre de la ciudad"},
                    "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
                },
                "required": ["city"],
            },
        },
    }
]

response = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "¿Qué clima hace en Madrid?"}],
    tools=tools,
    tool_choice="auto",
)

tool_call = response.choices[0].message.tool_calls[0]
print(f"Función: {tool_call.function.name}")
print(f"Args: {tool_call.function.arguments}")

Gestión de API Keys

import os
from dotenv import load_dotenv

load_dotenv()

# NUNCA hardcodear API keys
# ✗ client = OpenAI(api_key="sk-abc123...")
# ✓ client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

# Variables de entorno necesarias:
# OPENAI_API_KEY=sk-...
# ANTHROPIC_API_KEY=sk-ant-...
# En producción: usar secretos de la nube (AWS Secrets Manager, etc.)

Resumen

Dominar las APIs de LLMs —retries, fallbacks, rate limiting, structured outputs y function calling— es la base de cualquier aplicación LLMOps en producción. La robustez del código que envuelve estas APIs determina la confiabilidad de toda la aplicación.

Ejercicio de práctica

Gestión de APIs y retry logic

Implementa funciones para gestionar llamadas a APIs de LLMs con manejo de errores.

# build_messages(system, user, history=None) → construir lista de mensajes
# Retorna lista de dicts con role/content
# Si history es lista de tuplas (role, content), incluirlos entre system y user

# calculate_cost(model, prompt_tokens, completion_tokens) → calcular costo
# Precios por 1M tokens:
# "gpt-4o-mini": input=$0.15, output=$0.60
# "gpt-4o": input=$2.50, output=$10.00
# "claude-3-haiku": input=$0.25, output=$1.25
# Retorna costo total en USD (float)

# should_retry(status_code, attempt, max_attempts=3) → decidir si reintentar
# Reintentar para: 429 (rate limit), 500, 502, 503 (server errors)
# Y solo si attempt < max_attempts
# Retorna True/False