CULTIVA IA

Sistema Multi-Agente β€” Pipeline de Contenido

Microsoft Semantic Kernel Β· Python 3.11 Β· Azure OpenAI
Agentes-IA Produccion Avanzado
4
Agentes IA
2
Patrones orquestacion
8
Max. iteraciones
3
Tareas en paralelo
FastAPI
Capa API
Arquitectura del sistema
πŸ”
Investigador
Analiza sector, competidores y tendencias en paralelo
gpt-4o
🧠
Estratega
Define angulos y mensajes clave segun hallazgos
gpt-4o
✍️
Redactor
Produce borrador: posts, emails, copies de campana
gpt-4o
βœ…
Editor
Revisa tono, coherencia y calidad. Aprueba o itera.
gpt-4o-mini
PIPELINE SECUENCIAL (flujo base)
Input del cliente
β†’
Investigacion paralela
β†’
Estrategia
β†’
Redaccion
β†’
Edicion
β†’
Contenido aprobado
Implementacion Python
agents.py
orchestration.py
api.py
cultiva_agents/agents.py Python 3.11
# cultiva_agents/agents.py
# Sistema multi-agente para pipeline de contenido de marketing
# CULTIVA IA β€” Agencia de IA marca blanca

from semantic_kernel import Kernel
from semantic_kernel.agents import ChatCompletionAgent
from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion
from semantic_kernel.functions import kernel_function
import os

# ── Kernel compartido ────────────────────────────────────────────
def create_kernel(model: str = "gpt-4o") -> Kernel:
    kernel = Kernel()
    kernel.add_service(
        AzureChatCompletion(
            deployment_name=model,
            endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
            api_key=os.environ["AZURE_OPENAI_KEY"],
        )
    )
    return kernel


# ── Herramientas reutilizables ───────────────────────────────────
class MarketingTools:
    @kernel_function(description="Recupera datos de tendencias de busqueda para un sector")
    def get_trends(self, sector: str) -> str:
        # Integrar con SerpAPI / Google Trends en produccion
        return f"[Tendencias actuales para {sector}: IA generativa +340%, automatizacion +89%]"

    @kernel_function(description="Analiza competidores principales de un cliente")
    def analyze_competitors(self, company: str, sector: str) -> str:
        # Integrar con Semrush / Ahrefs API en produccion
        return f"[Competidores de {company} en {sector}: dominio_autoritad, backlinks, keywords clave]"


# ── Definicion de los 4 agentes ──────────────────────────────────
def build_agents() -> dict:
    kernel_full  = create_kernel("gpt-4o")
    kernel_mini  = create_kernel("gpt-4o-mini")

    # Anadir herramientas al kernel del investigador
    kernel_full.add_plugin(MarketingTools(), plugin_name="marketing_tools")

    investigador = ChatCompletionAgent(
        kernel=kernel_full,
        name="Investigador",
        instructions="""Eres un analista de mercado senior especializado en marketing digital B2B.
        Dado un cliente y sector, usa las herramientas disponibles para:
        - Identificar tendencias del sector (ultimos 6 meses)
        - Analizar 3 competidores principales
        - Detectar oportunidades de contenido poco exploradas
        Devuelve un informe estructurado con bullets. Maximo 400 palabras.""",
    )

    estratega = ChatCompletionAgent(
        kernel=kernel_full,
        name="Estratega",
        instructions="""Eres un estratega de contenido con enfoque en conversion B2B.
        Con el informe de investigacion, define:
        - 3 angulos narrativos diferenciadores
        - Mensaje clave para cada pieza de contenido
        - Tono y voz recomendados para la marca
        - Llamadas a la accion prioritarias
        SΓ© especifico y accionable.""",
    )

    redactor = ChatCompletionAgent(
        kernel=kernel_full,
        name="Redactor",
        instructions="""Eres un redactor creativo especializado en contenido de marketing para tecnologia.
        Con la estrategia definida, produce:
        - 1 post de LinkedIn (150-200 palabras, con hook potente)
        - 1 email de nurturing (asunto + cuerpo, 120 palabras)
        - 3 titulares alternativos para blog
        Usa el tono y angulos definidos por el Estratega. No uses emojis en exceso.""",
    )

    editor = ChatCompletionAgent(
        kernel=kernel_mini,  # gpt-4o-mini basta para revision
        name="Editor",
        instructions="""Eres un editor senior. Revisa el contenido del Redactor y verifica:
        1. Coherencia con la estrategia definida
        2. Claridad y fluidez de lectura
        3. Ausencia de clichΓ©s o lenguaje corporativo vacio
        4. Llamada a la accion clara y convincente
        Si todo esta correcto, responde EXACTAMENTE: "APROBADO"
        Si necesita cambios, responde: "REVISION: [lista de mejoras concretas]"
        Maximo 2 rondas de revision antes de aprobar.""",
    )

    return {
        "investigador": investigador,
        "estratega": estratega,
        "redactor": redactor,
        "editor": editor,
    }
⬇️
Patron Secuencial
orchestration.py β€” flujo pipeline
# orchestration.py
from semantic_kernel.agents import AgentGroupChat
from semantic_kernel.agents.strategies import (
    SequentialSelectionStrategy,
    KernelFunctionTerminationStrategy,
)
from semantic_kernel.functions import KernelFunction

async def run_sequential_pipeline(
    agents: dict,
    client_brief: str,
    kernel: Kernel,
) -> str:

    # Funcion de terminacion: el Editor dice "APROBADO"
    termination_fn = KernelFunction.from_prompt(
        """Revisa el ultimo mensaje.
Si contiene exactamente "APROBADO", responde: si
De lo contrario, responde: no
Ultimo mensaje: {{$last_message}}""",
        kernel=kernel,
    )

    chat = AgentGroupChat(
        agents=[
            agents["investigador"],
            agents["estratega"],
            agents["redactor"],
            agents["editor"],
        ],
        selection_strategy=SequentialSelectionStrategy(
            agents=[
                agents["investigador"],
                agents["estratega"],
                agents["redactor"],
                agents["editor"],
            ]
        ),
        termination_strategy=KernelFunctionTerminationStrategy(
            agents=[agents["editor"]],
            function=termination_fn,
            result_parser=lambda r: r.value[0].content.strip() == "si",
            max_iterations=8,
        ),
    )

    await chat.add_chat_message(message=client_brief)
    outputs = []

    async for response in chat.invoke():
        outputs.append(
            f"\n[{response.name}]\n{response.content}"
        )

    return "\n".join(outputs)
⚑
Patron Paralelo
orchestration.py β€” investigacion fan-out
# Investigacion en paralelo: sector + competidores + tendencias
import asyncio
from semantic_kernel.agents import ChatCompletionAgent

async def parallel_research(
    kernel: Kernel,
    client: str,
    sector: str,
) -> str:
    # 3 agentes especializados se lanzan simultaneamente
    sector_agent = ChatCompletionAgent(
        kernel=kernel,
        name="AnalistaSector",
        instructions="Analiza el estado actual del sector. 200 palabras max.",
    )
    competitor_agent = ChatCompletionAgent(
        kernel=kernel,
        name="AnalistaCompetencia",
        instructions="Compara estrategia de contenido de 3 competidores. 200 palabras max.",
    )
    trend_agent = ChatCompletionAgent(
        kernel=kernel,
        name="AnalistaTendencias",
        instructions="Identifica 5 tendencias emergentes del sector. 200 palabras max.",
    )

    prompt = f"Cliente: {client} | Sector: {sector}"

    # Fan-out: los 3 corren en paralelo
    sector_res, comp_res, trend_res = await asyncio.gather(
        sector_agent.invoke(prompt),
        competitor_agent.invoke(prompt),
        trend_agent.invoke(prompt),
    )

    # Fan-in: sintetizador agrega los 3 resultados
    synthesizer = ChatCompletionAgent(
        kernel=kernel,
        name="Sintetizador",
        instructions="Sintetiza los tres analisis en un informe unificado. Resalta las oportunidades.",
    )

    synthesis = await synthesizer.invoke(
        f"SECTOR:\n{sector_res.content}\n\n"
        f"COMPETIDORES:\n{comp_res.content}\n\n"
        f"TENDENCIAS:\n{trend_res.content}"
    )
    return synthesis.content
Capa API β€” FastAPI + integracion n8n/Make
cultiva_agents/api.py FastAPI
# api.py β€” endpoint listo para conectar con n8n, Make o webhook de Slack
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from contextlib import asynccontextmanager
from .agents import build_agents, create_kernel
from .orchestration import run_sequential_pipeline, parallel_research

# ── Modelos de entrada / salida ──────────────────────────────────
class ContentRequest(BaseModel):
    cliente:       str     # ej. "Legalia Tech β€” plataforma LegalTech B2B"
    sector:        str     # ej. "legaltech"
    tipo_contenido: str   # ej. "post LinkedIn + email nurturing"
    objetivo:      str     # ej. "generar leads de despachos medianos"
    tono:          str = "profesional-cercano"

class ContentResponse(BaseModel):
    status:   str          # "ok" | "error"
    pipeline: str          # transcripcion completa de todos los agentes
    resumen:  str          # extraccion del contenido final aprobado

# ── App lifecycle ────────────────────────────────────────────────
_agents = {}
_kernel = None

@asynccontextmanager
async def lifespan(app: FastAPI):
    global _agents, _kernel
    _agents = build_agents()           # Crea agentes una vez al arrancar
    _kernel = create_kernel("gpt-4o")   # Kernel compartido para orquestacion
    yield
    # Cleanup si fuera necesario

app = FastAPI(
    title="CULTIVA IA β€” Content Pipeline API",
    description="Sistema multi-agente para produccion automatica de contenido de marketing",
    version="1.0.0",
    lifespan=lifespan,
)

# ── Endpoint principal ───────────────────────────────────────────
@app.post("/pipeline/content", response_model=ContentResponse)
async def content_pipeline(req: ContentRequest):
    """
    Ejecuta el pipeline completo:
    1. Investigacion paralela (sector + competidores + tendencias)
    2. Pipeline secuencial (estrategia -> redaccion -> edicion)
    Retorna el contenido final aprobado por el Editor.
    """
    try:
        # Paso 1: investigacion paralela (fan-out)
        research = await parallel_research(
            _kernel, req.cliente, req.sector
        )

        # Paso 2: pipeline secuencial con hallazgos de investigacion
        brief = (
            f"CLIENTE: {req.cliente}\n"
            f"SECTOR: {req.sector}\n"
            f"OBJETIVO: {req.objetivo}\n"
            f"TIPO CONTENIDO: {req.tipo_contenido}\n"
            f"TONO: {req.tono}\n\n"
            f"INVESTIGACION PREVIA:\n{research}"
        )

        pipeline_output = await run_sequential_pipeline(
            _agents, brief, _kernel
        )

        # Extraer bloque final aprobado (ultimo mensaje del Redactor antes de APROBADO)
        lines = pipeline_output.split("\n")
        final_content = "\n".join(
            l for l in lines
            if not l.startswith("[Editor]") and "APROBADO" not in l
        ).strip()

        return ContentResponse(
            status="ok",
            pipeline=pipeline_output,
            resumen=final_content,
        )

    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

# Iniciar: uvicorn cultiva_agents.api:app --reload --port 8000
Ejemplo de ejecucion β€” Legalia Tech (LegalTech B2B)
REQUEST POST /pipeline/content
{
  "cliente": "Legalia Tech",
  "sector": "legaltech B2B EspaΓ±a",
  "tipo_contenido": "post LinkedIn + email",
  "objetivo": "leads despachos medianos",
  "tono": "profesional-cercano"
}
RESPONSE (200 OK) β€” contenido generado
// [Investigador] β€” sector + competidores
El mercado legaltech en EspaΓ±a crece al 23% anual.
Solo el 18% de los despachos medianos ha adoptado
IA para gestion documental. Oportunidad clave: ROI
demostrable en due diligence (-40% tiempo).

// [Estratega] β€” angulos y mensajes
Angulo 1: "El despacho que no adopta IA pierde
eficiencia frente a los grandes"
Angulo 2: Caso de uso concreto: due diligence en 2h
CTA principal: Demo gratuita de 30 min

// [Redactor] β€” contenido final
LinkedIn: "Los despachos que automatizan due diligence
con IA cierran operaciones un 40% mas rapido..."

Email subject: "ΒΏCuanto tiempo pierde tu equipo en
revision de contratos?"

// [Editor] β€” APROBADO