Cómo crear un sistema multiagente

1. Introducción

Descripción general

En este lab, irás más allá de los chatbots simples y crearás un sistema multiagente distribuido.

Si bien un solo LLM puede responder preguntas, la complejidad del mundo real a menudo requiere roles especializados. No le pides a tu ingeniero de backend que diseñe la interfaz de usuario, ni le pides a tu diseñador que optimice las consultas a la base de datos. Del mismo modo, podemos crear agentes de IA especializados que se enfoquen en una tarea y se coordinen entre sí para resolver problemas complejos.

Construirás un Sistema de Creación de Cursos que consta de:

  1. Agente investigador: Usa google_search para encontrar información actualizada.
  2. Agente evaluador: Critica la investigación por su calidad y completitud.
  3. Agente creador de contenido: Convierte la investigación en un curso estructurado.
  4. Agente organizador: Administra el flujo de trabajo y la comunicación entre estos especialistas.

Requisitos previos

  • Conocimientos básicos de Python
  • Conocimientos sobre la consola de Google Cloud

Actividades

  • Define un agente que utilice herramientas (researcher) que pueda buscar en la web.
  • Implementa resultados estructurados con Pydantic para judge.
  • Conéctate a agentes remotos con el protocolo Agent-to-Agent (A2A).
  • Construye un LoopAgent para crear un ciclo de retroalimentación entre el investigador y el evaluador.
  • Ejecute el sistema distribuido localmente utilizando el ADK.
  • Implemente el sistema multiagente en Google Cloud Run.

Principios de arquitectura y organización

Antes de escribir código, comprendamos cómo trabajan juntos estos agentes. Estamos construyendo un canal de creación de cursos.

El diseño del sistema

Diagrama de arquitectura

Organización con agentes

Los agentes estándar (como el Investigador) funcionan. Los agentes organizadores (como LoopAgent o SequentialAgent) administran otros agentes. No tienen sus propias herramientas; su "herramienta" es la delegación.

  1. LoopAgent: Esto actúa como un bucle while en el código. Ejecuta una secuencia de agentes de forma repetida hasta que se cumple una condición (o se alcanza la cantidad máxima de iteraciones). Usamos esta información para la Investigación con Loop:
    • Investigador: Encuentra información.
    • El juez la critica.
    • Si Judge dice "Fallar", el EscalationChecker permite que el bucle continúe.
    • Si Judge dice "Pass", EscalationChecker interrumpe el bucle.
  2. SequentialAgent: Actúa como una ejecución de secuencia de comandos estándar. Ejecuta agentes uno tras otro. Usamos esto para la canalización de alto nivel:
    • Primero, ejecute el Bucle de investigación (hasta que termine con datos buenos).
    • Luego, ejecute el Content Builder (para escribir el curso).

Si combinamos estos elementos, creamos un sistema sólido que puede autocorregirse antes de generar el resultado final.

2. Configuración

Configuración del entorno

  1. Abre Cloud Shell: Haz clic en el ícono de Activar Cloud Shell en la parte superior derecha de la consola de Google Cloud.

Obtén el código de inicio

  1. Clona el repositorio inicial en tu directorio personal:
    cd ~
    git clone --depth 1 --filter=blob:none --sparse https://github.com/GoogleCloudPlatform/devrel-demos.git temp-repo && cd temp-repo && git sparse-checkout set agents/build-with-ai/production-ready-ai/prai-roadshow-lab-1-starter && cd .. && mv temp-repo/agents/build-with-ai/production-ready-ai/prai-roadshow-lab-1-starter . && rm -rf temp-repo
    cd prai-roadshow-lab-1-starter
    
  2. Habilitar API: Ejecute el siguiente comando para habilitar los servicios necesarios de Google Cloud:
    gcloud services enable \
        run.googleapis.com \
        artifactregistry.googleapis.com \
        cloudbuild.googleapis.com \
        aiplatform.googleapis.com \
        compute.googleapis.com
    
  3. Abre esta carpeta en tu editor.

Instala las dependencias

Usamos uv para administrar dependencias rápidamente.

  1. Instala las dependencias del proyecto:
    # Ensure you have uv installed: pip install uv
    uv sync
    
  2. Configurar variables de entorno.
    • Sugerencia: Puedes encontrar el ID del proyecto en el panel de la consola de Cloud o ejecutando gcloud config get-value project.
    Crearemos un archivo .env para almacenar estas variables, de modo que puedas volver a cargarlas fácilmente si se desconecta tu sesión.
    cat <<EOF > .env
    export GOOGLE_CLOUD_PROJECT=$(gcloud config get-value project)
    export GOOGLE_CLOUD_LOCATION=global
    export GOOGLE_GENAI_USE_VERTEXAI=true
    EOF
    
  3. Obtén las variables de entorno:
    source .env
    
    Advertencia: Las variables de entorno no se conservan entre nuevas sesiones de terminal. Si abres una nueva pestaña de terminal, ejecuta source .env para restaurarlas.

3. 🕵️ El agente de investigación

Agente de investigación

El Investigador es un especialista. Su único trabajo es encontrar información. Para ello, necesita acceder a una herramienta: la Búsqueda de Google.

¿Por qué separar al investigador?

Análisis detallado: ¿Por qué no tener solo un agente que haga todo?

Los agentes pequeños y enfocados son más fáciles de evaluar y depurar. Si la investigación es deficiente, itera sobre la instrucción del investigador. Si el formato del curso es incorrecto, debes iterar en el Creador de contenido. En una consigna monolítica que pretende "hacerlo todo", arreglar una cosa a menudo estropea otra.

  1. Si estás trabajando en Cloud Shell, ejecuta el siguiente comando para abrir el editor de Cloud Shell:
    cloudshell workspace .
    
    Si trabajas en tu entorno local, abre tu IDE favorito.
  2. Abre agents/researcher/agent.py.
  3. Verás un esqueleto con un TODO.
  4. Agrega el siguiente código para definir el agente researcher:
    # ... existing imports ...
    
    # Define the Researcher Agent
    researcher = Agent(
        name="researcher",
        model=MODEL,
        description="Gathers information on a topic using Google Search.",
        instruction="""
        You are an expert researcher. Your goal is to find comprehensive and accurate information on the user's topic.
        Summarize your findings clearly.
        If you receive feedback that your research is insufficient, use the feedback to refine your next search.
        DO NOT output any function calls. Provide your research directly as text.
        """,
    )
    
    root_agent = researcher
    

Concepto clave: Uso de herramientas

Cuando se usa Gemini 3, la herramienta de Búsqueda de Google está disponible automáticamente. Si usaras un modelo diferente, como Gemini 2.5, deberías pasar tools=[google_search] como un parámetro adicional al constructor Agent(). El ADK controla la complejidad de describir esta herramienta al LLM. Cuando el modelo decide que necesita información, genera una llamada a una herramienta estructurada, el ADK ejecuta la función de Python google_search y devuelve el resultado al modelo.

4. ⚖️ El agente juez

Agente de juez

El investigador trabaja duro, pero los licenciados en derecho pueden ser perezosos. Necesitamos un juez para que revise el trabajo. El juez acepta la investigación y devuelve una evaluación estructurada de aprobación o rechazo.

Resultado estructurado

Análisis detallado: Para automatizar los flujos de trabajo, necesitamos resultados predecibles. Una reseña de texto divagante es difícil de analizar mediante programación. Al aplicar un esquema JSON (con Pydantic), nos aseguramos de que Judge devuelva un valor booleano pass o fail sobre el que nuestro código pueda actuar de manera confiable.

  1. Abre agents/judge/agent.py.
  2. Define el esquema JudgeFeedback y el agente judge.
    # 1. Define the Schema
    class JudgeFeedback(BaseModel):
        """Structured feedback from the Judge agent."""
        status: Literal["pass", "fail"] = Field(
            description="Whether the research is sufficient ('pass') or needs more work ('fail')."
        )
        feedback: str = Field(
            description="Detailed feedback on what is missing. If 'pass', a brief confirmation."
        )
    
    # 2. Define the Agent
    judge = Agent(
        name="judge",
        model=MODEL,
        description="Evaluates research findings for completeness and accuracy.",
        instruction="""
        You are a strict editor.
        Evaluate the 'research_findings' against the user's original request.
        If the findings are missing key info, return status='fail'.
        If they are comprehensive, return status='pass'.
        """,
        output_schema=JudgeFeedback,
        # Disallow delegation because it should only output the schema
        disallow_transfer_to_parent=True,
        disallow_transfer_to_peers=True,
    )
    
    root_agent = judge
    

Concepto clave: Restricción del comportamiento del agente

Establecemos disallow_transfer_to_parent=True y disallow_transfer_to_peers=True. Esto obliga al juez a solo devolver el JudgeFeedback estructurado. No puede decidir "chatear" con el usuario ni delegar la tarea a otro agente. Esto lo convierte en un componente determinístico en nuestro flujo de lógica.

5. 🧪 Pruebas en aislamiento

Antes de conectarlos, podemos verificar que cada agente funcione. El ADK permite ejecutar agentes de forma individual.

Concepto clave: El tiempo de ejecución interactivo

adk run crea un entorno ligero donde usted es el "usuario". Esto te permite probar las instrucciones del agente y el uso de herramientas de forma aislada. Si el agente falla aquí (p.ej., no puede usar la Búsqueda de Google), definitivamente fallará en la orquestación.

  1. Ejecuta el Investigador de forma interactiva. Ten en cuenta que señalamos el directorio del agente específico:
    # This runs the researcher agent in interactive mode
    uv run adk run agents/researcher
    
  2. En la instrucción del chat, escribe lo siguiente:
    Find the population of Tokyo in 2020
    
    Debe usar la herramienta de Búsqueda de Google y devolver la respuesta.Nota: Si ves un error que indica que no se configuraron el proyecto, la ubicación y el uso de Vertex, asegúrate de que esté configurado el ID de tu proyecto y ejecuta lo siguiente:
    export GOOGLE_CLOUD_PROJECT=$(gcloud config get-value project)
    export GOOGLE_CLOUD_LOCATION=global
    export GOOGLE_GENAI_USE_VERTEXAI=true
    
  3. Salir del chat (Ctrl+C).
  4. Ejecuta el Juez de forma interactiva:
    uv run adk run agents/judge
    
  5. En el cuadro de diálogo del chat, simule la entrada:
    Topic: Tokyo. Findings: Tokyo is a city.
    
    Debería devolver status='fail' porque los hallazgos son demasiado breves.

6. ✍️ El agente creador de contenido

Creador de contenido

El Creador de Contenido es el escritor creativo. Toma la investigación aprobada y la convierte en un curso.

  1. Abre agents/content_builder/agent.py.
  2. Defina el agente content_builder.
    content_builder = Agent(
        name="content_builder",
        model=MODEL,
        description="Transforms research findings into a structured course.",
        instruction="""
        You are an expert course creator.
        Take the approved 'research_findings' and transform them into a well-structured, engaging course module.
    
        **Formatting Rules:**
        1. Start with a main title using a single `#` (H1).
        2. Use `##` (H2) for main section headings.
        3. Use bullet points and clear paragraphs.
        4. Maintain a professional but engaging tone.
    
        Ensure the content directly addresses the user's original request.
        """,
    )
    root_agent = content_builder
    

Concepto clave: Propagación del contexto

Quizás te preguntes: "¿Cómo sabe el Creador de contenido lo que encontró el Investigador?". En el ADK, los agentes en una canalización comparten un session.state. Más adelante, en el Orquestador, configuraremos al Investigador y al Juez para que guarden sus resultados en este estado compartido. El sistema de sugerencias del Creador de Contenido tiene acceso efectivo a este historial.

7. 🎻 El Orquestador

Agente organizador

El organizador es el administrador de nuestro equipo multiagente. A diferencia de los agentes especializados (Investigador, Juez, Creador de Contenido) que realizan tareas específicas, la función del Orquestador es coordinar el flujo de trabajo y garantizar que la información fluya correctamente entre ellos.

🌐 La arquitectura: Agent-to-Agent (A2A)

Arquitectura de A2A

En este lab, crearemos un sistema distribuido. En lugar de ejecutar todos los agentes en un único proceso de Python, los desplegamos como microservicios independientes. Esto permite que cada agente se adapte de forma independiente y falle sin que se colapse todo el sistema.

Para que esto sea posible, usamos el protocolo Agent-to-Agent (A2A).

El protocolo A2A

Análisis detallado: En un sistema de producción, los agentes se ejecutan en diferentes servidores (o incluso en diferentes nubes). El protocolo A2A crea una forma estándar para que se descubran y se comuniquen entre sí a través de HTTP. RemoteA2aAgent es el cliente del ADK para este protocolo.

  1. Abre agents/orchestrator/agent.py.
  2. Ubica el comentario # TODO: Define connections to remote agents o la sección para las definiciones de agentes remotos.
  3. Agrega el siguiente código para definir las conexiones. Asegúrate de colocarlo después de las importaciones y antes de cualquier otra definición del agente.
    # ... existing code ...
    
    # Connect to the Researcher (Localhost port 8001)
    researcher_url = os.environ.get("RESEARCHER_AGENT_CARD_URL", "http://localhost:8001/a2a/agent/.well-known/agent-card.json")
    researcher = RemoteA2aAgent(
        name="researcher",
        agent_card=researcher_url,
        description="Gathers information using Google Search.",
        # IMPORTANT: Save the output to state for the Judge to see
        after_agent_callback=create_save_output_callback("research_findings"),
        # IMPORTANT: Use authenticated client for communication
        httpx_client=create_authenticated_client(researcher_url)
    )
    
    # Connect to the Judge (Localhost port 8002)
    judge_url = os.environ.get("JUDGE_AGENT_CARD_URL", "http://localhost:8002/a2a/agent/.well-known/agent-card.json")
    judge = RemoteA2aAgent(
        name="judge",
        agent_card=judge_url,
        description="Evaluates research.",
        after_agent_callback=create_save_output_callback("judge_feedback"),
        httpx_client=create_authenticated_client(judge_url)
    )
    
    # Content Builder (Localhost port 8003)
    content_builder_url = os.environ.get("CONTENT_BUILDER_AGENT_CARD_URL", "http://localhost:8003/a2a/agent/.well-known/agent-card.json")
    content_builder = RemoteA2aAgent(
        name="content_builder",
        agent_card=content_builder_url,
        description="Builds the course.",
        httpx_client=create_authenticated_client(content_builder_url)
    )
    

8. 🛑 El verificador de derivaciones

Un bucle necesita una forma de detenerse. Si el juez dice "Aprobado", queremos salir del bucle inmediatamente y pasar al Creador de contenido.

Lógica personalizada con BaseAgent

Análisis detallado: No todos los agentes usan LLMs. A veces, necesitas una lógica de Python simple. BaseAgent te permite definir un agente que solo ejecuta código. En este caso, verificamos el estado de la sesión y usamos EventActions(escalate=True) para indicarle a LoopAgent que se detenga.

  1. Aún en agents/orchestrator/agent.py.
  2. Busca el marcador de posición TODO de EscalationChecker.
  3. Reemplázala por la siguiente implementación:
    class EscalationChecker(BaseAgent):
        """Checks the judge's feedback and escalates (breaks the loop) if it passed."""
    
        async def _run_async_impl(
            self, ctx: InvocationContext
        ) -> AsyncGenerator[Event, None]:
            # Retrieve the feedback saved by the Judge
            feedback = ctx.session.state.get("judge_feedback")
            print(f"[EscalationChecker] Feedback: {feedback}")
    
            # Check for 'pass' status
            is_pass = False
            if isinstance(feedback, dict) and feedback.get("status") == "pass":
                is_pass = True
            # Handle string fallback if JSON parsing failed
            elif isinstance(feedback, str) and '"status": "pass"' in feedback:
                is_pass = True
    
            if is_pass:
                # 'escalate=True' tells the parent LoopAgent to stop looping
                yield Event(author=self.name, actions=EventActions(escalate=True))
            else:
                # Continue the loop
                yield Event(author=self.name)
    
    escalation_checker = EscalationChecker(name="escalation_checker")
    

Concepto clave: Controlar el flujo mediante eventos.

Los agentes se comunican no solo con texto, sino también con Eventos. Al generar un evento con escalate=True, este agente envía una señal a su padre (el LoopAgent). El LoopAgent está programado para capturar esta señal y finalizar el bucle.

9. 🔁 El ciclo de investigación

Ciclo de investigación

Necesitamos un ciclo de retroalimentación: Investigación -> Evaluación -> (Falla) -> Investigación -> …

  1. Aún en agents/orchestrator/agent.py.
  2. Agrega la definición research_loop. Coloca este elemento después de la clase EscalationChecker y la instancia escalation_checker.
    research_loop = LoopAgent(
        name="research_loop",
        description="Iteratively researches and judges until quality standards are met.",
        sub_agents=[researcher, judge, escalation_checker],
        max_iterations=3,
    )
    

Concepto clave: LoopAgent

El LoopAgent recorre sus sub_agents en orden.

  1. researcher: Busca datos.
  2. judge: Evalúa datos.
  3. escalation_checker: Decide si yield Event(escalate=True). Si sucede escalate=True, el bucle se interrumpe antes. De lo contrario, se reinicia en el investigador (hasta max_iterations).

10. 🔗 La canalización final

Tubería final

Finalmente, únelo todo.

  1. Aún en agents/orchestrator/agent.py.
  2. Define el root_agent en la parte inferior del archivo. Asegúrate de que este reemplazo se aplique a cualquier marcador de posición root_agent = None existente.
    root_agent = SequentialAgent(
        name="course_creation_pipeline",
        description="A pipeline that researches a topic and then builds a course from it.",
        sub_agents=[research_loop, content_builder],
    )
    

Concepto clave: Composición jerárquica

Nótese que research_loop es en sí mismo un agente (un LoopAgent). Lo tratamos igual que cualquier otro subagente en el SequentialAgent. Esta capacidad de composición te permite crear lógica compleja anidando patrones simples (bucles dentro de secuencias, secuencias dentro de routers, etcétera).

11. 💻 Ejecuta de forma local

Antes de ejecutar todo, veamos cómo el ADK simula el entorno distribuido de forma local.

Análisis detallado: Cómo funciona el desarrollo local

En una arquitectura de microservicios, cada agente se ejecuta como su propio servidor. Al realizar la implementación, dispondrá de 4 servicios diferentes de Cloud Run. Simular esto de forma local puede ser doloroso si tienes que abrir 4 pestañas de terminal y ejecutar 4 comandos.

Esta secuencia de comandos inicia los procesos de uvicorn para el investigador (puerto 8001), el evaluador (8002) y el creador de contenido (8003). Establece variables de entorno, como RESEARCHER_AGENT_CARD_URL, y las pasa al organizador (puerto 8004). Así es exactamente como lo configuraremos en la nube más adelante.

App en ejecución

  1. Ejecutar el script de orquestación:
    perl -pi -e 's/us-central1/global/g' run_local.sh
    ./run_local.sh
    
    Esto inicia 4 procesos separados.
  2. Pruébalo:
    • Si usas Cloud Shell: Haz clic en el botón Vista previa en la Web (en la parte superior derecha de la terminal) -> Vista previa en el puerto 8080 -> Cambiar puerto a 8000.
    • Si ejecutas el servidor de forma local: Abre http://localhost:8000 en tu navegador.
    • Indicación: "Crea un curso sobre la historia del café."
    • Observación: El agente de organización llamará al investigador. El resultado se envía al juez. Si el juez la rechaza, el bucle continúa.
    Solución de problemas:
    • "Internal Server Error" o errores de autenticación: Si ves errores de autenticación (p.ej., relacionados con google-auth), asegúrate de haber ejecutado gcloud auth application-default login si se ejecuta en una máquina local. En Cloud Shell, asegúrate de que la variable de entorno GOOGLE_CLOUD_PROJECT esté configurada correctamente.
    • Errores de terminal: Si el comando falla en una nueva ventana de terminal, recuerda volver a exportar tus variables de entorno (GOOGLE_CLOUD_PROJECT, etcétera).
  3. Prueba de agentes de forma aislada: Incluso cuando el sistema completo está en funcionamiento, puedes probar agentes específicos segmentando sus puertos directamente. Esto es útil para depurar un componente específico sin activar toda la cadena.Nota: Estos son extremos de API, no páginas web. No se puede acceder a ellos a través de un navegador. En su lugar, usa curl para verificar que se estén ejecutando (p.ej., recuperando su tarjeta de agente).
    • Researcher Only (Port 8001):
      • Compruebe el estado (y encuentre el punto final url):
        curl http://localhost:8001/a2a/agent/.well-known/agent-card.json
        
      • Envía una consulta (con el protocolo JSON-RPC de A2A):
        curl -X POST http://localhost:8001/a2a/agent \
          -H "Content-Type: application/json" \
          -d '{
            "jsonrpc": "2.0",
            "method": "message/send",
            "id": 1,
            "params": {
              "message": {
                "message_id": "test-1",
                "role": "user",
                "parts": [
                  {
                    "text": "What is the capital of France?",
                    "kind": "text"
                  }
                ]
              }
            }
          }'
        
    • Solo para jueces (Puerto 8002):
      • Verifica el estado:
        curl http://localhost:8002/a2a/agent/.well-known/agent-card.json
        
      • Enviar una consulta:
        curl -X POST http://localhost:8002/a2a/agent \
          -H "Content-Type: application/json" \
          -d '{
            "jsonrpc": "2.0",
            "method": "message/send",
            "id": 1,
            "params": {
              "message": {
                "message_id": "test-2",
                "role": "user",
                "parts": [
                  {
                    "text": "Topic: Tokyo. Findings: Tokyo is the capital of Japan.",
                    "kind": "text"
                  }
                ]
              }
            }
          }'
        
    • Solo Content Builder (puerto 8003):
      curl http://localhost:8003/a2a/agent/.well-known/agent-card.json
      
    • Orquestador (Puerto 8004):
      curl http://localhost:8004/a2a/agent/.well-known/agent-card.json
      

12. 🚀 Implementa en Cloud Run

La validación final se ejecuta en la nube. Implementaremos cada agente como un servicio independiente.

Información sobre la configuración de la implementación

Cuando implementamos agentes en Cloud Run, pasamos varias variables de entorno para configurar su comportamiento y conectividad:

  • GOOGLE_CLOUD_PROJECT: Garantiza que el agente use el proyecto de Google Cloud correcto para el registro y las llamadas a Vertex AI.
  • GOOGLE_GENAI_USE_VERTEXAI: Indica al framework del agente (ADK) que use Vertex AI para la inferencia del modelo en lugar de llamar directamente a las APIs de Gemini.
  • GOOGLE_CLOUD_LOCATION: Indica al framework del agente (ADK) qué extremo usar.
  • [AGENT]_AGENT_CARD_URL: Es fundamental para el orquestador. Le indica al orquestador dónde encontrar los agentes remotos. Si establecemos este parámetro en la URL de Cloud Run implementada (específicamente, la ruta de acceso a la tarjeta del agente), permitimos que el orquestador descubra a los agentes de investigador, evaluador y creador de contenido, y se comunique con ellos a través de Internet.
  1. Implementa los subagentes (en paralelo): Para ahorrar tiempo, implementaremos el investigador, el juez y el creador de contenido de forma simultánea.Abre tres pestañas nuevas de la terminal. En cada pestaña nueva, ejecuta el siguiente comando para configurar tu entorno:
    cd ~/prai-roadshow-lab-1-starter
    source .env
    
    Pestaña 1: Ejecuta la implementación de Researcher:
    gcloud run deploy researcher \
      --source agents/researcher/ \
      --region us-west1 \
      --allow-unauthenticated \
      --labels dev-tutorial=prod-ready-1 \
      --set-env-vars GOOGLE_CLOUD_PROJECT=$GOOGLE_CLOUD_PROJECT \
      --set-env-vars GOOGLE_CLOUD_LOCATION=$GOOGLE_CLOUD_LOCATION \
      --set-env-vars GOOGLE_GENAI_USE_VERTEXAI="true"
    
    Pestaña 2: Ejecuta la implementación de Judge:
    gcloud run deploy judge \
      --source agents/judge/ \
      --region us-west1 \
      --allow-unauthenticated \
      --labels dev-tutorial=prod-ready-1 \
      --set-env-vars GOOGLE_CLOUD_PROJECT=$GOOGLE_CLOUD_PROJECT \
      --set-env-vars GOOGLE_CLOUD_LOCATION=$GOOGLE_CLOUD_LOCATION \
      --set-env-vars GOOGLE_GENAI_USE_VERTEXAI="true"
    
    Pestaña 3: Ejecuta la implementación de Content Builder:
    gcloud run deploy content-builder \
      --source agents/content_builder/ \
      --region us-west1 \
      --allow-unauthenticated \
      --labels dev-tutorial=prod-ready-1 \
      --set-env-vars GOOGLE_CLOUD_PROJECT=$GOOGLE_CLOUD_PROJECT \
      --set-env-vars GOOGLE_CLOUD_LOCATION=$GOOGLE_CLOUD_LOCATION \
      --set-env-vars GOOGLE_GENAI_USE_VERTEXAI="true"
    
  2. Captura las URLs: Una vez que finalicen las tres implementaciones, vuelve a la terminal original (en la que implementarás el orquestador). Ejecuta los siguientes comandos para capturar las URLs del servicio:
    RESEARCHER_URL=$(gcloud run services describe researcher --region us-west1 --format='value(status.url)')
    JUDGE_URL=$(gcloud run services describe judge --region us-west1 --format='value(status.url)')
    CONTENT_BUILDER_URL=$(gcloud run services describe content-builder --region us-west1 --format='value(status.url)')
    
    echo "Researcher: $RESEARCHER_URL"
    echo "Judge: $JUDGE_URL"
    echo "Content Builder: $CONTENT_BUILDER_URL"
    
  3. Implementa el Orchestrator: Usa las variables de entorno capturadas para configurar el Orchestrator.
    gcloud run deploy orchestrator \
      --source agents/orchestrator/ \
      --region us-west1 \
      --allow-unauthenticated \
      --labels dev-tutorial=prod-ready-1 \
      --set-env-vars RESEARCHER_AGENT_CARD_URL=$RESEARCHER_URL/a2a/agent/.well-known/agent-card.json \
      --set-env-vars JUDGE_AGENT_CARD_URL=$JUDGE_URL/a2a/agent/.well-known/agent-card.json \
      --set-env-vars CONTENT_BUILDER_AGENT_CARD_URL=$CONTENT_BUILDER_URL/a2a/agent/.well-known/agent-card.json \
      --set-env-vars GOOGLE_CLOUD_PROJECT=$GOOGLE_CLOUD_PROJECT \
      --set-env-vars GOOGLE_CLOUD_LOCATION=$GOOGLE_CLOUD_LOCATION \
      --set-env-vars GOOGLE_GENAI_USE_VERTEXAI="true"
    
    Captura la URL:
    ORCHESTRATOR_URL=$(gcloud run services describe orchestrator --region us-west1 --format='value(status.url)')
    echo $ORCHESTRATOR_URL
    
  4. Implementa el frontend:
    gcloud run deploy course-creator \
        --source app \
        --region us-west1 \
        --allow-unauthenticated \
        --labels dev-tutorial=prod-ready-1 \
        --set-env-vars AGENT_SERVER_URL=$ORCHESTRATOR_URL \
        --set-env-vars GOOGLE_CLOUD_PROJECT=$GOOGLE_CLOUD_PROJECT
    
  5. Prueba la implementación remota: Abre la URL del Orchestrator implementado. Ahora se ejecuta completamente en la nube y utiliza la infraestructura sin servidores de Google para escalar tus agentes.Sugerencia: Encontrarás todos los microservicios y sus URLs en la interfaz de Cloud Run.

13. Resumen

¡Felicitaciones! Creaste e implementaste correctamente un sistema multiagente distribuido listo para producción.

Qué logramos

  • Descomposición de una tarea compleja: En lugar de una única indicación gigante, dividimos el trabajo en roles especializados (Investigador, Juez, Creador de contenido).
  • Control de calidad implementado: Utilizamos un LoopAgent y un Judge estructurado para garantizar que solo la información de alta calidad llegue al paso final.
  • Creado para la producción: Con el protocolo Agent-to-Agent (A2A) y Cloud Run, creamos un sistema en el que cada agente es un microservicio independiente y escalable. Esto es mucho más sólido que ejecutar todo en una sola secuencia de comandos de Python.
  • Orquestación: Usamos SequentialAgent y LoopAgent para definir patrones claros de flujo de control.

Próximos pasos

Ahora que tienes la base, puedes extender este sistema:

  • Agregar más herramientas: Otorga al investigador acceso a documentos internos o APIs.
  • Mejora el juez: Agrega criterios más específicos o incluso un paso de "interacción humana".
  • Intercambiar modelos: Intente usar diferentes modelos para diferentes agentes (por ejemplo, un modelo más rápido para el Juez, un modelo más fuerte para el Redactor de contenido).

Ahora puedes crear flujos de trabajo complejos y confiables basados en agentes en Google Cloud.