Cymbal Transit: Un sistema multiagente que usa LangChain4J y el SDK de Java de MCP Toolbox

1. Descripción general

Los viajeros modernos esperan experiencias conversacionales. En lugar de navegar por filtros complejos de la IU, quieren preguntar: "¿Puedo llevar a mi perro en el autobús de las 9 a.m. a Boston?". Esto requiere un agente que pueda razonar a partir de datos no estructurados (políticas en PDF) y datos estructurados (programaciones en SQL).

En este lab, crearemos el agente de tránsito de Cymbal con los siguientes elementos:

  • LangChain4j: Es el principal framework de Java para la organización de la IA.
  • AlloyDB: Es una base de datos de alto rendimiento compatible con PostgreSQL.
  • SDK de Java de MCP Toolbox: Una forma estandarizada de conectar agentes de Java a herramientas y fuentes de datos externas.

Qué compilarás

e68388d533c9997e.png

Cymbal Bus Agent, una aplicación de Java Spring Boot que consta de lo siguiente:

  1. Base de datos de AlloyDB y SDK de Java de MCP Toolbox para la organización de herramientas con los agentes
  2. Cloud Run para la implementación y la aplicación de Toolbox (implementación del agente).
  3. Biblioteca de LangChain4J para el framework de LLM y el agente en una aplicación de Spring Boot con Java 17

Qué aprenderás

  • Cómo usar LangChain4j para crear agentes especializados y subagentes coordinados con el SDK de Java de MCP Toolbox for Databases
  • Cómo configurar y usar AlloyDB para datos y la IA
  • Cómo usar MCP Toolbox para conectar agentes a herramientas de datos de AlloyDB
  • Cómo implementar la solución con Cloud Run o ejecutarla de forma local

La arquitectura

  1. AlloyDB para PostgreSQL: Sirve como la base de datos operativa de alto rendimiento que contiene nuestros registros de rutas, políticas y reservas. Potencia la búsqueda y recuperación de vectores.
  2. SDK de Java de MCP Toolbox para bases de datos: Actúa como el "maestro de orquestación" y expone los datos de AlloyDB como herramientas ejecutables que los agentes pueden llamar.

El SDK de Java de MCP Toolbox permite orquestar agentes con tus herramientas de bases de datos sin esfuerzo para aplicaciones de nivel empresarial.

  1. LangChain4J: Es una biblioteca de Java de código abierto que simplifica la integración de modelos de lenguaje grandes (LLM) en aplicaciones de Java. Proporciona herramientas y abstracciones para compilar aplicaciones potenciadas por IA, incluidos chatbots, agentes y sistemas de generación aumentada por recuperación (RAG).
  2. Cloud Run: Una plataforma SIN SERVIDORES completamente administrada que te permite compilar e implementar apps o sitios web rápidamente en cualquier lenguaje, biblioteca o archivo binario. Puedes escribir código con el lenguaje, el framework y las bibliotecas que prefieras, empaquetarlo como un contenedor, ejecutar “gcloud run deploy” y tu app estará activa, ya que se le proporciona todo lo que necesita para ejecutarse en producción. Compilar un contenedor es completamente opcional. Si usas Go, Node.js, Python, Java, .NET Core o Ruby, puedes usar la opción de implementación basada en la fuente que compila el contenedor por ti, con las prácticas recomendadas para el lenguaje que usas.

Requisitos

  • Un navegador, como Chrome o Firefox
  • Un proyecto de Google Cloud con facturación habilitada.
  • Conocimientos básicos de SQL y Java

2. Antes de comenzar

Crea un proyecto

  1. En la página del selector de proyectos de la consola de Google Cloud, selecciona o crea un proyecto de Google Cloud.
  2. Asegúrate de que la facturación esté habilitada para tu proyecto de Cloud. Obtén información para verificar si la facturación está habilitada en un proyecto.
  1. Usarás Cloud Shell, un entorno de línea de comandos que se ejecuta en Google Cloud. Haz clic en Activar Cloud Shell en la parte superior de la consola de Google Cloud.

Imagen del botón Activar Cloud Shell

  1. Una vez que te conectes a Cloud Shell, verifica que ya te autenticaste y que el proyecto se configuró con tu ID del proyecto con el siguiente comando:
gcloud auth list
  1. En Cloud Shell, ejecuta el siguiente comando para confirmar que el comando gcloud conoce tu proyecto.
gcloud config list project
  1. Si tu proyecto no está configurado, usa el siguiente comando para hacerlo:
gcloud config set project <YOUR_PROJECT_ID>
  1. Habilita las APIs necesarias: Sigue el vínculo y habilita las APIs.

Como alternativa, puedes usar el comando de gcloud para esto. Consulta la documentación para ver los comandos y el uso de gcloud.

Problemas potenciales y solución de problemas

El "Proyecto Fantasma"

Ejecutaste gcloud config set project, pero, en realidad, estás viendo un proyecto diferente en la IU de la consola. Verifica el ID del proyecto en el menú desplegable de la esquina superior izquierda.

La barricada de facturación

Habilitaste el proyecto, pero olvidaste la cuenta de facturación. AlloyDB es un motor de alto rendimiento que no se iniciará si el "tanque de combustible" (facturación) está vacío.

Retraso de la propagación de la API

Hiciste clic en "Habilitar APIs", pero la línea de comandos aún dice Service Not Enabled. Espera 60 segundos. La nube necesita un momento para activar sus neuronas.

Cuags de cuota

Si usas una cuenta de prueba nueva, es posible que alcances una cuota regional para las instancias de AlloyDB. Si falla us-central1, prueba con us-east1.

Agente de servicio"oculto"

A veces, al agente de servicio de AlloyDB no se le otorga automáticamente el rol de aiplatform.user. Si tus consultas en SQL no pueden comunicarse con Gemini más adelante, este suele ser el problema.

3. Configuración de la base de datos

AlloyDB para PostgreSQL es el núcleo de nuestra aplicación. Aprovechamos sus potentes capacidades de vectores y su motor de columnas integrado para generar incorporaciones para más de 50,000 registros de la SCM. Esto permite realizar análisis de vectores casi en tiempo real, lo que permite a nuestros agentes identificar anomalías en el inventario o riesgos logísticos en conjuntos de datos masivos en milisegundos.

En este lab, usaremos AlloyDB como la base de datos para los datos de prueba. Utiliza clústeres para contener todos los recursos, como bases de datos y registros. Cada clúster tiene una instancia principal que proporciona un punto de acceso a los datos. Las tablas contendrán los datos reales.

Creemos un clúster, una instancia y una tabla de AlloyDB en los que se cargará el conjunto de datos de prueba.

  1. Haz clic en el botón o copia el siguiente vínculo en el navegador en el que accediste como usuario a la consola de Google Cloud.

Como alternativa, puedes ir a la terminal de Cloud Shell desde tu proyecto en el que canjeaste la cuenta de facturación, clonar el repositorio de GitHub y navegar al proyecto con los siguientes comandos:

git clone https://github.com/AbiramiSukumaran/easy-alloydb-setup

cd easy-alloydb-setup
  1. Una vez que se complete este paso, el repo se clonará en tu editor local de Cloud Shell y podrás ejecutar el siguiente comando desde la carpeta del proyecto (es importante que te asegures de estar en el directorio del proyecto):
sh run.sh
  1. Ahora usa la IU (haz clic en el vínculo de la terminal o en el vínculo "preview on web" de la terminal).
  2. Ingresa los detalles del ID del proyecto, el clúster y los nombres de las instancias para comenzar.
  3. Ve a tomar un café mientras se desplazan los registros y puedes leer cómo se hace esto en segundo plano aquí.

Problemas potenciales y solución de problemas

El problema de la "paciencia"

Los clústeres de bases de datos son una infraestructura pesada. Si actualizas la página o finalizas la sesión de Cloud Shell porque "parece que se detuvo", es posible que termines con una instancia "fantasma" que se aprovisionó parcialmente y que es imposible borrar sin intervención manual.

Incongruencia de región

Si habilitaste tus APIs en us-central1, pero intentas aprovisionar el clúster en asia-south1, es posible que tengas problemas de cuota o demoras en los permisos de la cuenta de servicio. Mantén una sola región para todo el lab.

Clústeres de zombis

Si antes usaste el mismo nombre para un clúster y no lo borraste, es posible que la secuencia de comandos indique que el nombre del clúster ya existe. Los nombres de los clústeres deben ser únicos dentro de un proyecto.

Tiempo de espera de Cloud Shell

Si tu descanso para tomar café dura 30 minutos, es posible que Cloud Shell entre en modo de suspensión y desconecte el proceso sh run.sh. Mantén la pestaña activa.

4. Aprovisionamiento de esquemas

Una vez que tengas en funcionamiento tu clúster y tu instancia de AlloyDB, dirígete al editor de SQL de AlloyDB Studio para habilitar las extensiones de IA y aprovisionar el esquema.

1e3ac974b18a8113.png

Es posible que debas esperar a que termine de crearse la instancia. Una vez que lo hagas, accede a AlloyDB con las credenciales que creaste cuando creaste el clúster. Usa los siguientes datos para autenticarte en PostgreSQL:

  • Nombre de usuario : "postgres"
  • Base de datos : "postgres"
  • Contraseña : "alloydb" (o la que hayas configurado en el momento de la creación)

Una vez que te autentiques correctamente en AlloyDB Studio, ingresa los comandos SQL en el editor. Puedes agregar varias ventanas del editor con el signo más que se encuentra a la derecha de la última ventana.

28cb9a8b6aa0789f.png

Ingresarás comandos para AlloyDB en ventanas del editor, y usarás las opciones Ejecutar, Formatear y Borrar según sea necesario.

Habilitar extensiones

Para compilar esta app, usaremos las extensiones pgvector y google_ml_integration. La extensión pgvector te permite almacenar y buscar embeddings de vectores. La extensión google_ml_integration proporciona funciones que usas para acceder a los extremos de predicción de Vertex AI y obtener predicciones en SQL. Habilita estas extensiones ejecutando los siguientes DDL:

CREATE EXTENSION IF NOT EXISTS google_ml_integration CASCADE;
CREATE EXTENSION IF NOT EXISTS vector;

Otorgar permiso

Ejecuta la siguiente instrucción para otorgar permiso de ejecución en la función "embedding":

GRANT EXECUTE ON FUNCTION embedding TO postgres;

Otorga el rol de usuario de Vertex AI a la cuenta de servicio de AlloyDB

En la consola de IAM de Google Cloud, otorga a la cuenta de servicio de AlloyDB (que se ve así: service-<<PROJECT_NUMBER>>@gcp-sa-alloydb.iam.gserviceaccount.com) acceso al rol "Usuario de Vertex AI". PROJECT_NUMBER tendrá tu número de proyecto.

Como alternativa, puedes ejecutar el siguiente comando desde la terminal de Cloud Shell:

PROJECT_ID=$(gcloud config get-value project)


gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:service-$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")@gcp-sa-alloydb.iam.gserviceaccount.com" \
--role="roles/aiplatform.user"

Crea una tabla

Puedes crear una tabla con la siguiente instrucción DDL en AlloyDB Studio:

DROP TABLE IF EXISTS transit_policies;
DROP TABLE IF EXISTS bus_schedules;
DROP TABLE IF EXISTS bookings;

-- Table 1: Transit Policies (Unstructured Data for RAG)
CREATE TABLE transit_policies (
    policy_id SERIAL PRIMARY KEY,
    category VARCHAR(50),
    policy_text TEXT,
    policy_embedding vector(768) 
);

-- Table 2: Intercity Bus Schedules (Structured Data)
CREATE TABLE bus_schedules (
    trip_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    origin_city VARCHAR(100),
    destination_city VARCHAR(100),
    departure_time TIMESTAMP,
    arrival_time TIMESTAMP,
    available_seats INT DEFAULT 50,
    ticket_price DECIMAL(6,2)
);

-- Table 3: Booking Ledger (Transactional Action Data)
CREATE TABLE bookings (
    booking_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    trip_id UUID REFERENCES bus_schedules(trip_id),
    passenger_id VARCHAR(100),
    status VARCHAR(20) DEFAULT 'CONFIRMED',
    booking_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

La columna policy_embedding permitirá el almacenamiento de los valores de vectores de algunos de los campos de texto.

Transferencia de datos

Ejecuta el siguiente conjunto de instrucciones de SQL para insertar registros de forma masiva en las tablas correspondientes:

  1. Inserta políticas no estructuradas y GENERA EMBEDDINGS REALES de forma nativa en AlloyDB
-- 1. Insert Unstructured Policies and GENERATE REAL EMBEDDINGS natively in AlloyDB

INSERT INTO transit_policies (category, policy_text, policy_embedding) 
VALUES 
('Pets', 'Service animals are always welcome. Small pets (under 25 lbs) are allowed in secure carriers for a $25 fee. Large dogs are not permitted on standard coaches.', embedding('text-embedding-005', 'Service animals are always welcome. Small pets (under 25 lbs) are allowed in secure carriers for a $25 fee. Large dogs are not permitted on standard coaches.')),
('Luggage', 'Each passenger is allowed one carry-on (up to 15 lbs) and two stowed bags (up to 50 lbs each) free of charge. Additional bags cost $15 each.', embedding('text-embedding-005', 'Each passenger is allowed one carry-on (up to 15 lbs) and two stowed bags (up to 50 lbs each) free of charge. Additional bags cost $15 each.')),
('Refunds', 'Tickets are fully refundable up to 24 hours before departure. Within 24 hours, tickets can be exchanged for travel credit only.', embedding('text-embedding-005', 'Tickets are fully refundable up to 24 hours before departure. Within 24 hours, tickets can be exchanged for travel credit only.'));
  1. Cómo generar más de 200 programas realistas para 7 días con generate_series
-- 2. Generate 200+ Realistic Schedules for the Next 7 Days using generate_series

INSERT INTO bus_schedules (origin_city, destination_city, departure_time, arrival_time, ticket_price, available_seats)
SELECT 
    origin,
    destination,
    -- Generate departures every 4 hours starting from tomorrow
    (CURRENT_DATE + 1) + (interval '4 hours' * seq) AS dep_time,
    (CURRENT_DATE + 1) + (interval '4 hours' * seq) + interval '4.5 hours' AS arr_time,
    ROUND((RANDOM() * 30 + 25)::numeric, 2) AS price, -- Random price between $25 and $55
    FLOOR(RANDOM() * 50 + 1) AS seats -- Random seats between 1 and 50
FROM 
    (VALUES 
        ('New York', 'Boston'), ('Boston', 'New York'),
        ('Philadelphia', 'Washington DC'), ('Washington DC', 'Philadelphia'),
        ('Seattle', 'Portland'), ('Portland', 'Seattle')
    ) AS routes(origin, destination)
CROSS JOIN generate_series(1, 40) AS seq; -- 6 routes * 40 time slots = 240 distinct trips ingested!

Genera embeddings

Los embeddings se incluyen automáticamente en la instrucción de inserción en la tabla transit_policies con la función "embedding('text-embedding-005', '<<policytext>>')".

Problemas potenciales y solución de problemas

El bucle de "amnesia de contraseña"

Si usaste la configuración "Con un clic" y no recuerdas tu contraseña, ve a la página Información básica de la instancia en la consola y haz clic en "Editar" para restablecer la contraseña de postgres.

El error "No se encontró la extensión"

Si CREATE EXTENSION falla, suele deberse a que la instancia aún se encuentra en estado de "Mantenimiento" o "Actualización" desde el aprovisionamiento inicial. Verifica si se completó el paso de creación de la instancia y espera unos segundos si es necesario.

Problemas de propagación de IAM

Ejecutaste el comando de IAM gcloud, pero el comando de SQL CALL sigue fallando con un error de permiso. Los cambios de IAM pueden tardar un poco en propagarse a través de la red troncal de Google. Respira.***CRÍTICO:

  1. A veces, es posible que tu cuenta de servicio de AlloyDB se vea diferente del formato existente que usamos en el paso de permisos. Para asegurarte al 100% de que la cuenta de servicio de AlloyDB tiene el rol de usuario de Vertex AI, ve a la página Clústeres de AlloyDB en la consola de Google Cloud. Haz clic en tu clúster y, en la pestaña Descripción general, busca un campo etiquetado como Cuenta de servicio.
    Copia el valor y, luego, ve a IAM y agrega el rol de usuario de Vertex AI.
  2. Además, si omitiste el paso “Habilitar API” en la sección “Antes de comenzar”, tendrás problemas para acceder a las incorporaciones de AlloyDB.

Vector Dimension Mismatch

La columna policy_embedding de la tabla transit_policies se establece en VECTOR(768). Si intentas usar un modelo diferente (como un modelo de 1536 dim), tus inserciones se expandirán. Mantén text-embedding-005.

Error de escritura en el ID del proyecto

En la llamada create_model, si dejas los corchetes « » o escribes mal el ID de tu proyecto, el registro del modelo parecerá exitoso, pero fallará durante la primera consulta real. Verifica la cadena.

5. Configuración de Tools & Toolbox

MCP Toolbox for Databases es un servidor de MCP de código abierto para bases de datos. Te permite desarrollar herramientas de forma más fácil, rápida y segura, ya que se encarga de las complejidades, como la agrupación de conexiones, la autenticación y mucho más. Toolbox te ayuda a crear herramientas de IA generativa que permiten que tus agentes accedan a los datos de tu base de datos.

Usamos la caja de herramientas del Protocolo de contexto del modelo (MCP) para bases de datos como el "director". Actúa como un middleware estandarizado entre nuestros agentes y AlloyDB. Si defines una configuración de tools.yaml, la caja de herramientas expone automáticamente operaciones complejas de la base de datos como herramientas limpias y ejecutables, como find-bus-schedules and routes o query-schedules for specific routes, y ejecuta acciones autónomas, como book-ticket. Esto elimina la necesidad de agrupar conexiones manualmente o de usar código SQL estándar dentro de la lógica del agente.

Instala el servidor de Toolbox

En la terminal de Cloud Shell, crea una carpeta para guardar el nuevo archivo YAML de herramientas y el archivo binario de la caja de herramientas:

mkdir cymbal-bus-toolbox

cd cymbal-bus-toolbox

Desde esa carpeta nueva, ejecuta el siguiente conjunto de comandos:

# see releases page for other versions
export VERSION=0.27.0
curl -L -o toolbox https://storage.googleapis.com/genai-toolbox/v$VERSION/linux/amd64/toolbox
chmod +x toolbox

A continuación, crea el archivo tools.yaml dentro de esa nueva carpeta. Para ello, navega al editor de Cloud Shell y copia el contenido de este archivo repo en el archivo tools.yaml.

... (Refer to entire file in the repo)

tools:

   find-bus-schedules:
    kind: postgres-sql
    source: alloydb
    description: Find all available bus schedules.
    statement: |
      SELECT CAST(trip_id AS TEXT) trip_id, departure_time, arrival_time, ticket_price, available_seats , origin_city, destination_city 
      FROM bus_schedules;

   query-schedules:
    kind: postgres-sql
    source: alloydb
    description: Find available bus schedules between an origin and destination city.
    parameters:
      - name: origin
        type: string
        description: The departure city name.
      - name: destination
        type: string
        description: The arrival city name.
    statement: |
      SELECT CAST(trip_id AS TEXT) trip_id, departure_time, arrival_time, ticket_price, available_seats 
      FROM bus_schedules 
      WHERE lower(origin_city) = lower($1) 
        AND lower(destination_city) = lower($2) 
        AND available_seats > 0 
      ORDER BY departure_time ASC 
      LIMIT 5;

   book-ticket:
    kind: postgres-sql
    source: alloydb
    description: Books a ticket for a specific trip, decrementing available seats and generating a confirmed booking record.
    parameters:
      - name: trip_id
        type: string
        description: The UUID of the trip schedule to book.
      - name: passenger_name
        type: string
        description: Name or ID of the passenger (Bound securely via backend or AuthToken).
        authServices:
          - name: google_auth
            field: sub
    statement: |
      WITH updated_schedule AS (
          UPDATE bus_schedules 
          SET available_seats = available_seats - 1 
          WHERE trip_id = CAST($1 AS UUID) AND available_seats > 0
          RETURNING trip_id
      )
      INSERT INTO bookings (trip_id, passenger_id)
      SELECT trip_id, $2 
      FROM updated_schedule
      RETURNING CAST(booking_id as TEXT) as booking_id, trip_id, passenger_id, status, booking_time;

   search-policies:
    kind: postgres-sql
    source: alloydb
    description: Semantic search for transit policies regarding luggage, pets, refunds, and general rules.
    parameters:
      - name: search_query
        type: string
        description: The user's question about transit policies to be embedded and searched.
    statement: |
      SELECT category, policy_text 
      FROM transit_policies 
      ORDER BY policy_embedding <=> CAST(embedding('text-embedding-005', $1) AS vector(768))
      LIMIT 2;

Nota:

  1. En la configuración de tools.yaml, no olvides incluir ipType: "private" en la configuración de la fuente de AlloyDB.
  2. También recuerda incluir la URL del servicio de MCP Toolbox en el parámetro clientId para la configuración de authServices. Es posible que solo obtengas el vínculo después de la implementación inicial, por lo que sí, deberás ejecutar los pasos de implementación dos veces para asegurarte de que funcione el caso de uso de las herramientas autenticadas.
  3. Las siguientes opciones para probar la caja de herramientas de forma local no funcionarán si tu conexión de AlloyDB está configurada como privada. Debes configurarla como pública para probarla de forma local o usar un proxy para la conexión. Pero no te preocupes. En nuestro caso, la implementaremos directamente en Cloud Run y, luego, la probaremos.

Para probar el archivo tools.yaml en el servidor local, haz lo siguiente:

./toolbox --tools-file "tools.yaml"

También puedes probarlo en la IU:

./toolbox --ui

Implementémoslo en Cloud Run de la siguiente manera.

Implementación de Cloud Run

  1. Configura la variable de entorno PROJECT_ID:
export PROJECT_ID="my-project-id"
  1. Inicializa la CLI de gcloud:
gcloud init
gcloud config set project $PROJECT_ID
  1. Debes tener habilitadas las siguientes APIs:
gcloud services enable run.googleapis.com \
                       cloudbuild.googleapis.com \
                       artifactregistry.googleapis.com \
                       iam.googleapis.com \
                       secretmanager.googleapis.com
  1. Crea una cuenta de servicio de backend si aún no tienes una:
gcloud iam service-accounts create toolbox-identity
  1. Otorga permisos para usar Secret Manager:
gcloud projects add-iam-policy-binding $PROJECT_ID \
    --member serviceAccount:toolbox-identity@$PROJECT_ID.iam.gserviceaccount.com \
    --role roles/secretmanager.secretAccessor
  1. Otorga permisos adicionales a la cuenta de servicio que son específicos de nuestra fuente de AlloyDB (roles/alloydb.client y roles/serviceusage.serviceUsageConsumer).
gcloud projects add-iam-policy-binding $PROJECT_ID \
    --member serviceAccount:toolbox-identity@$PROJECT_ID.iam.gserviceaccount.com \
    --role roles/alloydb.client


gcloud projects add-iam-policy-binding $PROJECT_ID \
    --member serviceAccount:toolbox-identity@$PROJECT_ID.iam.gserviceaccount.com \
    --role roles/serviceusage.serviceUsageConsumer
  1. Sube tools.yaml como un secreto:
gcloud secrets create tools-cymbal-transit --data-file=tools.yaml
  1. Si ya tienes un secreto y quieres actualizar su versión, ejecuta el siguiente comando:
gcloud secrets versions add tools-cymbal-transit --data-file=tools.yaml
  1. Establece una variable de entorno en la imagen de contenedor que deseas usar para Cloud Run:
export IMAGE=us-central1-docker.pkg.dev/database-toolbox/toolbox/toolbox:latest
  1. Implementa Toolbox en Cloud Run con el siguiente comando:

Si habilitaste el acceso público en tu instancia de AlloyDB, sigue el siguiente comando para la implementación en Cloud Run:

gcloud run deploy toolbox-cymbal-transit \
    --image $IMAGE \
    --service-account toolbox-identity \
    --region us-central1 \
    --set-secrets "/app/tools.yaml=tools-cymbal-transit:latest" \
    --args="--tools-file=/app/tools.yaml","--address=0.0.0.0","--port=8080" \
    --allow-unauthenticated

Si usas una red de VPC, usa el siguiente comando:

gcloud run deploy toolbox-cymbal-transit \
    --image $IMAGE \
    --service-account toolbox-identity \
    --region us-central1 \
    --set-secrets "/app/tools.yaml=tools-cymbal-transit:latest" \
    --args="--tools-file=/app/tools.yaml","--address=0.0.0.0","--port=8080" \
    --network <<YOUR_NETWORK_NAME>> \
    --subnet <<YOUR_SUBNET_NAME>> \
    --allow-unauthenticated

Nota: Una vez que se implemente, ve a la lista de servicios de Cloud Run y asegúrate de que, en la pestaña de seguridad de ese servicio, esté seleccionada la opción "Permitir acceso público".

6. Configuración de la aplicación del agente

Clona este repo en tu proyecto y analicémoslo.

Github Repo

Para clonar este proyecto, ejecuta el siguiente comando desde la terminal de Cloud Shell (en el directorio raíz o desde donde quieras crear este proyecto):

git clone https://github.com/googleapis/mcp-toolbox-sdk-java

El comando anterior clona todo el mcp-toolbox-sdk-java. Solo necesitamos el proyecto de muestra. Por lo tanto, navega al directorio raíz del proyecto dentro del repo:

cd mcp-toolbox-sdk-java/demo-applications/cymbal-transit
  1. Esto debería crear el proyecto, y puedes verificarlo en el editor de Cloud Shell.

a494664032904c77.png

  1. Abre CymbalTransitController.java y configura las variables de entorno:
  2. GCP_PROJECT_ID
  3. GCP_REGION
  4. GEMINI_MODEL_NAME
  5. MCP_TOOLBOX_URL

Como alternativa (solo para fines de desarrollo), también puedes reemplazar los marcadores de posición de los valores de resguardo respectivos.

7. Explicación del código

El CymbalTransitController actúa como punto de entrada para nuestro servicio de Cloud Run. Administra el flujo de la conversación y garantiza que el agente tenga acceso a la solicitud actual del usuario.

La implementación sigue una arquitectura en capas que separa la organización de la IA, la conexión de herramientas y la comunicación de MCP de bajo nivel.

1. Configuración del agente de IA (AgentConfiguration)

Esta clase usa @Configuration de Spring para iniciar los componentes de IA. Inicializa VertexAiGeminiChatModel y lo vincula a nuestra interfaz de Agent.

@Bean
ChatLanguageModel geminiChatModel() {
    return VertexAiGeminiChatModel.builder()
        .project(projectId)
        .location(region)
        .modelName(modelName)
        .build();
}

@Bean
TransitAgent transitAgent(ChatLanguageModel chatLanguageModel, TransitAgentTools tools) {
    return AiServices.builder(TransitAgent.class)
        .chatLanguageModel(chatLanguageModel)
        .chatMemoryProvider(memoryId -> MessageWindowChatMemory.withMaxMessages(20))
        .tools(tools) 
        .build();
}

Significado: AiServices vincula la interfaz al LLM. El MessageWindowChatMemory garantiza que el agente recuerde las preferencias del usuario (como un transportín para mascotas mencionado anteriormente) durante un máximo de 20 mensajes en una sola sesión.

2. La interfaz del agente de IA (TransitAgent)

La anotación @SystemMessage define la "persona" y las restricciones operativas, específicamente una estrategia de enrutamiento.

@SystemMessage({
    "You are the Cymbal Transit Concierge.",
    "CRITICAL INSTRUCTION: On your very first interaction, you MUST use the 'findAllSchedules' tool to fetch and memorize the broad bus routes.",
    "ONLY if the user asks a specifically narrowed-down question... should you route to the specific tools like 'querySchedules', 'bookTicket', 'searchPolicies'.",
    "Don't show any asterisks while listing results. Keep it formatted and numbered or bulleted."
})
String chat(@MemoryId String sessionId, @UserMessage String userMessage);

Importancia: Esta estrategia minimiza la latencia. Al recuperar primero datos amplios, el agente puede responder preguntas generales sobre rutas usando su contexto interno sin realizar llamadas redundantes al backend.

3. El puente de la caja de herramientas (TransitAgentTools)

Este servicio actúa como las "manos" del agente, ya que traduce las llamadas a herramientas de LangChain4j en lógica de ejecución.

@Tool("Fetches the initial, broad dataset of all available bus schedules and routes.")
public String findAllSchedules() {
    return mcpService.findAllSchedules().join();
}


@Tool("Book a ticket for a passenger using a specific trip ID.")
public String bookTicket(String tripId, String passengerName) {
    return mcpService.bookTicket(tripId, passengerName).join();
}

Ejecución síncrona: Si bien las llamadas a MCP son asíncronas (devuelven CompletableFuture), el LLM requiere un resultado antes de poder continuar con su proceso de "pensamiento". Usamos .join() para proporcionar resultados síncronos al agente.

4. El servicio de la caja de herramientas de MCP (McpToolboxService)

Esta es la capa de comunicación que usa el SDK de Java de MCP Toolbox para interactuar con el backend de AlloyDB.

// Identity Management: Fetching OIDC ID Token for Auth
GoogleCredentials credentials = GoogleCredentials.getApplicationDefault();
this.idToken = ((IdTokenProvider) credentials)
    .idTokenWithAudience(targetUrl, Collections.emptyList())
    .getTokenValue();

// Dynamic Invocation: Executing a tool by name
public CompletableFuture<String> findAllSchedules() {
    return mcpClient.invokeTool("find-bus-schedules", Collections.emptyMap()).thenApply(result -> {
        return result.content().stream()
            .map(content -> content.text())
            .collect(Collectors.joining(", ", "[", "]"));
    });
}

Importancia: McpToolboxClient se encarga de la mayor parte de la comunicación de JSON-RPC. El método bookTicket muestra específicamente la capacidad del SDK para vincular parámetros complejos de forma dinámica.

5. El controlador de REST (TransitAgentController)

El extremo final se simplifica radicalmente porque LangChain4j administra el estado y la lógica.

@PostMapping("/chat")
public ResponseEntity<String> handleUserChat(@RequestBody String userMessage, HttpSession session) {
    String sessionId = session.getId();
    String agentResponse = transitAgent.chat(sessionId, userMessage);
    return ResponseEntity.ok(agentResponse);
}

Importancia: Al asignar el ID de HttpSession a @MemoryId, nos aseguramos de que los planes de viaje de los diferentes usuarios no se mezclen, y mantenemos el código del controlador limpio y legible.

8. Caja de herramientas del MCP: importancia y SDK de Java

¿Qué es el MCP?

Piensa en el Protocolo de contexto del modelo (MCP) como un traductor universal para la IA. El MCP, creado para estandarizar la forma en que los modelos de IA se conectan a herramientas y conjuntos de datos externos, reemplaza los fragmentados y personalizados lenguajes de integración por un protocolo seguro y universal. Ya sea que tu agente necesite ejecutar una consulta SQL transaccional, buscar entre miles de documentos de políticas o activar una API de REST, MCP proporciona una interfaz única y unificada.

MCP Toolbox para bases de datos

Los equipos de ingeniería están dejando atrás los chatbots simples para crear sistemas basados en agentes que interactúan directamente con bases de datos fundamentales para la misión. Sin embargo, compilar estos agentes empresariales a menudo implica encontrarse con un muro de integración de código de unión personalizado, APIs frágiles y lógica de bases de datos compleja.

Para reemplazar estos cuellos de botella codificados con un plano de control seguro y unificado, nos complace anunciar el SDK de Java para la caja de herramientas del Protocolo de contexto del modelo (MCP) para bases de datos. Esta versión ofrece una orquestación de agentes de primer nivel y con seguridad de tipos para el ecosistema empresarial más adoptado del mundo. La arquitectura madura de Java está diseñada específicamente para estas exigencias rigurosas, ya que proporciona la alta simultaneidad, la estricta integridad transaccional y la sólida administración de estados que se requieren para escalar de forma segura los agentes de IA esenciales en producción.

¿Por qué usar el SDK de Java?

El SDK de Java de MCP Toolbox permite a los desarrolladores de Java hacer lo siguiente:

  1. Consume Tools: Conéctate a un servidor de MCP (como MCP Toolbox para AlloyDB) y convierte automáticamente sus capacidades en métodos de Java que LangChain4j comprende.
  2. Seguridad de tipos: Aprovecha el tipado sólido de Java para los parámetros de herramientas, lo que reduce los errores de "alucinación" en tiempo de ejecución en las llamadas a herramientas.
  3. Preparación para la empresa: Se integra fácilmente con Spring Boot, Quarkus, Micronaut, etcétera.
  4. Conéctate sin esfuerzo: Evita escribir código JSON-RPC estándar.
  5. Estandariza la autenticación: La compatibilidad nativa con los tokens de OIDC de Google Cloud garantiza la ejecución segura de las herramientas.

y mucho más.

Dependencias: Configuración de pom.xml

Agrega la siguiente dependencia a tu proyecto de Maven para incluir el SDK de Java más reciente de MCP Toolbox:

   <dependency>
        <groupId>com.google.cloud.mcp</groupId>
        <artifactId>mcp-toolbox-sdk-java</artifactId>
        <version>0.2.0</version>
    </dependency>

Agrega la siguiente dependencia a tu proyecto de Maven para incluir el artefacto de LangChain4j:

     <!-- LangChain4j Core & Gemini -->
    <dependency>
        <groupId>dev.langchain4j</groupId>
        <artifactId>langchain4j</artifactId>
        <version>0.35.0</version>
    </dependency>

¡Eso es todo! Clonamos correctamente el proyecto y analizamos los detalles del agente, el SDK de Java de MCP Toolbox y el contexto.

9. Ejecución local

Para probar el agente en tu máquina, debes dirigirlo a tu servidor de MCP Toolbox implementado.

  1. Establece las variables de entorno:
export GCP_PROJECT_ID="<<YOUR_PROJECT_ID>>"
export GCP_REGION="us-central1"
export GEMINI_MODEL_NAME="gemini-2.5-flash"
export MCP_TOOLBOX_URL="<<YOUR_TOOLBOX_ENDPOINT_URL>>/mcp"
  1. Ejecuta con Maven:
mvn compile

mvn spring-boot:run

Esto debería iniciar tu agente de forma local, y deberías poder probarlo.

10. Implementémosla en Cloud Run

Para implementarlo en Cloud Run, ejecuta el siguiente comando desde la terminal de Cloud Shell en la que se clonó el proyecto y asegúrate de estar dentro de la carpeta raíz del proyecto.

SI NO ESTÁS EN LA CARPETA ROOT DE NUESTRO PROYECTO ACTUAL, ejecuta lo siguiente en tu terminal de Cloud Shell:

cd cymbal-transit

Si ya estás en la raíz de cymbal-transit, ejecuta el siguiente comando para implementar la app directamente en Cloud Run:

gcloud run deploy cymbal-transit --source . --set-env-vars GCP_PROJECT_ID=<<YOUR_PROJECT_ID>>,GCP_REGION=us-central1,GEMINI_MODEL_NAME=gemini-2.5-flash,MCP_TOOLBOX_URL=<<YOUR_MCP_TOOLBOX_URL>> --allow-unauthenticated

Reemplaza los valores de los marcadores de posición <<YOUR_PROJECT>> and <<YOUR_MCP_TOOLBOX_URL>>.

Una vez que finalice el comando, se mostrará una URL de servicio. Cópiala.

Otorga el rol de cliente de AlloyDB a la cuenta de servicio de Cloud Run.Esto permite que tu aplicación sin servidores cree un túnel seguro hacia la base de datos.

Ejecuta este comando en tu terminal de Cloud Shell:

# 1. Get your Project ID and Project Number
PROJECT_ID=$(gcloud config get-value project)
PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")

# 2. Grant the AlloyDB Client role
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$PROJECT_NUMBER-compute@developer.gserviceaccount.com" \
--role="roles/alloydb.client"

Nota: Una vez que se implemente, ve a la lista de servicios de Cloud Run y asegúrate de que, en la pestaña de seguridad de ese servicio, esté seleccionada la opción "Permitir acceso público".

Ahora, usa la URL del servicio (el extremo de Cloud Run que copiaste antes) y prueba la app.

Nota: Si tienes un problema de servicio y se menciona la memoria como motivo, intenta aumentar el límite de memoria asignado a 1 GiB para probarlo.

11. Demostración

Pregúntale al agente: "Necesito ir de Nueva York a Boston mañana por la mañana. ¿Puedo llevar a mi Golden Retriever?" Observa cómo el agente:

  1. Busca políticas para perros grandes.
  2. Encuentra programas específicos.
  3. Resume el viaje más rápido con un ID de viaje.
  4. También reserva un boleto si realizas un seguimiento de esa solicitud de acción.

aa0408a81074d0fc.png

12. Limpia

Cuando termines este lab, no olvides borrar el clúster y la instancia de AlloyDB.

Debería limpiar el clúster junto con sus instancias.

13. Felicitaciones

Creaste correctamente un agente de tránsito sofisticado basado en Java. Al aprovechar LangChain4j para la organización y el SDK de Java de MCP Toolbox para la conectividad de datos, creaste un sistema que puede razonar en todos los agentes, herramientas y fuentes de datos. Si deseas comenzar a coordinar tus aplicaciones de agentes con MCP Toolbox for Databases en varias bases de datos, incluso en diferentes plataformas, comienza hoy mismo con el SDK de Java. Aquí se encuentra el blog del anuncio de lanzamiento que brinda información más detallada sobre la biblioteca. Si quieres crear más aplicaciones de este tipo de forma práctica, gratis, a tu propio ritmo y con un instructor, regístrate en Code Vipassana en https://codevipassana.dev.