openaiagentspythonllmcontent-generationaipatterns

Mi prompt de 3000 tokens daba resultados inconsistentes — cómo lo resolví con agentes

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.

Volver al blog