El primer pipeline de generación de contenido que construí tenía un prompt de 3,000 tokens. Todo en uno: instrucciones de formato, ejemplos de estilo, reglas de negocio, contexto del dominio. El resultado era inconsistente — a veces excelente, a veces ignoraba la mitad de las instrucciones.
La migración a agentes especializados resolvió la inconsistencia. Este post es el diseño del pipeline: por qué los prompts monolíticos fallan a escala, cómo estructurar la cadena de agentes, y los detalles de implementación que no están en los ejemplos oficiales.
Por qué los prompts monolíticos fallan
Un prompt de 3,000 tokens le pide al modelo que haga muchas cosas al mismo tiempo: analizar el input, aplicar reglas de formato, mantener el tono correcto, verificar consistencia con ejemplos. La atención del modelo se distribuye entre todas esas tareas.
A medida que el prompt crece, la adherencia a instrucciones específicas baja. Es el fenómeno “lost in the middle” — el modelo presta menos atención a instrucciones en el medio del prompt que a las del inicio y el final.
La solución: agentes especializados. Cada agente tiene un prompt pequeño y enfocado, hace una sola cosa bien, y pasa el resultado al siguiente.
La cadena de agentes del pipeline
Input (datos brutos, keywords, brief)
↓
Agente 1: Research & Structure
→ Analiza el brief, identifica puntos clave, estructura outline
↓
Agente 2: Draft Writer
→ Escribe el primer borrador dado el outline
↓
Agente 3: Tone & Style Reviewer
→ Ajusta el borrador al tono y estilo especificado
↓
Agente 4: Fact Checker
→ Verifica claims factuales, marca los que necesitan fuente
↓
Output: borrador revisado + checklist de verificación
Cada agente recibe solo lo que necesita para su tarea. El Draft Writer no necesita las reglas de verificación de hechos. El Fact Checker no necesita las instrucciones de tono.
Implementación con OpenAI Agents SDK
from agents import Agent, Runner, handoff
from pydantic import BaseModel
from typing import Literal
# Modelos de datos para el paso entre agentes
class ContentOutline(BaseModel):
title: str
key_points: list[str]
target_audience: str
tone: Literal["formal", "conversational", "technical"]
estimated_length: int # palabras
class ContentDraft(BaseModel):
title: str
body: str
outline_used: ContentOutline
class ReviewedContent(BaseModel):
title: str
body: str
tone_notes: list[str] # cambios aplicados
fact_checks: list[dict] # {claim: str, verified: bool, note: str}
Los modelos Pydantic definen el contrato entre agentes. El Runner valida que cada agente retorna el tipo correcto.
Agente 1: Research & Structure
structure_agent = Agent(
name="ContentStructurer",
model="gpt-4o-mini", # Tarea de estructura — modelo más económico
instructions="""
Eres un experto en estructurar contenido. Tu tarea:
1. Analizar el brief recibido
2. Identificar los 3-5 puntos clave que el contenido debe cubrir
3. Determinar la audiencia objetivo y el tono adecuado
4. Retornar un outline estructurado
Sé específico en los key_points — no "hablar sobre X" sino "explicar por qué X causa Y en el contexto Z".
""",
output_type=ContentOutline,
)
gpt-4o-mini para tasks de estructuración — es suficientemente capaz y 10x más barato que gpt-4o para este step.
Agente 2: Draft Writer
draft_agent = Agent(
name="DraftWriter",
model="gpt-4o", # Generación — modelo más capaz
instructions="""
Eres un redactor de contenido especializado. Dado un outline, escribe el borrador completo.
Reglas:
- Usa el tono especificado en el outline
- Cubre todos los key_points del outline en el mismo orden
- Usa párrafos cortos (máx 3 oraciones)
- No uses listas de bullets a menos que sea técnicamente necesario
- Longitud objetivo: {estimated_length} palabras (±20%)
""",
output_type=ContentDraft,
)
Agente 3: Tone Reviewer
tone_agent = Agent(
name="ToneReviewer",
model="gpt-4o-mini",
instructions="""
Eres un editor de estilo. Tu tarea es revisar un borrador y ajustar el tono.
Para tono "conversational":
- Reemplaza construcciones pasivas por activas
- Acorta oraciones largas
- Usa contracciones donde sea natural
Para tono "technical":
- Asegura que los términos técnicos son precisos y consistentes
- Evita ambigüedades en descripciones de procesos
- Mantén estructura lógica clara
Retorna el texto revisado y una lista de los cambios aplicados.
""",
output_type=ReviewedContent,
)
Orquestador: conectar los agentes
from agents import Runner
import asyncio
async def run_content_pipeline(brief: str) -> ReviewedContent:
# Step 1: Estructurar
structure_result = await Runner.run(
structure_agent,
input=brief,
)
outline: ContentOutline = structure_result.final_output
# Step 2: Escribir borrador
draft_result = await Runner.run(
draft_agent,
input=f"""
Outline:
- Título: {outline.title}
- Key points: {chr(10).join(f' - {p}' for p in outline.key_points)}
- Audiencia: {outline.target_audience}
- Tono: {outline.tone}
- Longitud: {outline.estimated_length} palabras
Escribe el borrador completo.
""",
)
draft: ContentDraft = draft_result.final_output
# Step 3: Revisar tono
review_result = await Runner.run(
tone_agent,
input=f"""
Tono objetivo: {outline.tone}
Audiencia: {outline.target_audience}
Borrador:
{draft.body}
""",
)
return review_result.final_output
# Uso
brief = """
Artículo sobre manejo de errores en APIs REST.
Audiencia: backend developers con 2-3 años de experiencia.
Tono: técnico pero accesible.
Puntos a cubrir: HTTP status codes correctos, error response schemas,
retry-ability en errores 5xx, logging de errores para debugging.
"""
result = asyncio.run(run_content_pipeline(brief))
print(result.body)
Handoffs vs orquestación directa
El OpenAI Agents SDK soporta dos modelos de coordinación:
Orquestación directa (el patrón de arriba): el código Python es el orquestador. Llama a cada agente secuencialmente con el output del anterior. Control total, fácil de debuggear.
Handoffs nativos: un agente puede transferir el control a otro agente usando la función handoff(). El SDK maneja la transición.
# Patrón con handoffs — el agente decide cuándo pasar al siguiente
draft_agent_with_handoff = Agent(
name="DraftWriter",
instructions="...",
handoffs=[handoff(tone_agent)],
)
Para pipelines donde el flujo es siempre el mismo, la orquestación directa es más predecible. Los handoffs son útiles cuando el agente necesita decidir dinámicamente a qué agente pasar — por ejemplo, si el contenido es técnico ir al technical reviewer, si es editorial ir al tone reviewer.
Manejo de fallos y retry
Los modelos LLM pueden retornar output que no cumple el schema Pydantic. El SDK reintenta automáticamente cuando hay un error de parsing, pero hay límite:
from agents import Runner, RunConfig
result = await Runner.run(
structure_agent,
input=brief,
run_config=RunConfig(
max_retries=3, # Reintentos por error de schema
),
)
Para errores de red o rate limits, implementar retry a nivel del orquestador:
import tenacity
@tenacity.retry(
stop=tenacity.stop_after_attempt(3),
wait=tenacity.wait_exponential(multiplier=1, min=2, max=10),
retry=tenacity.retry_if_exception_type(Exception),
)
async def run_with_retry(agent, input_text: str):
return await Runner.run(agent, input=input_text)
Tracking de costos por step
Los pipelines multi-agente acumulan tokens. Loggear el uso por step permite identificar dónde está el costo:
result = await Runner.run(structure_agent, input=brief)
# El resultado incluye uso de tokens
usage = result.raw_responses[-1].usage
logger.info("agent.usage", extra={
"agent": "ContentStructurer",
"prompt_tokens": usage.prompt_tokens,
"completion_tokens": usage.completion_tokens,
"total_tokens": usage.total_tokens,
"estimated_cost_usd": (usage.prompt_tokens * 0.00015 + usage.completion_tokens * 0.0006) / 1000,
})
En producción, el gpt-4o-mini para los agentes de estructura y review versus gpt-4o solo para el draft writer reduce el costo total ~60% con impacto mínimo en calidad.
Lo que aprendí
El costo principal está en el draft, no en la estructura. El agente que genera texto es el más costoso. Los agentes que analizan y revisan usan modelos menores sin sacrificar calidad.
Pydantic como contrato entre agentes es no-negociable. Sin schema tipado, los errores entre agentes son strings mal formateados que fallan silenciosamente. Con Pydantic, el SDK valida y reintenta automáticamente.
La orquestación directa > handoffs para pipelines predecibles. Los handoffs son potentes para flujos dinámicos, pero agregan indirección difícil de debuggear. Para un pipeline lineal, el código Python orquestador es más claro y más fácil de testear.
Testear con inputs adversariales desde el inicio. Los inputs que rompen los agentes son: briefs ambiguos, briefs muy cortos, briefs en otro idioma que el esperado. Los agentes fallan gracefully solo si los probaste con esos casos antes de producción.