Analiza y etiqueta mensajes de Gmail con Gemini y Vertex AI

Esta solución usa Vertex AI y Gemini para analizar los mensajes de Gmail y etiquetarlos según su opinión.

Nivel de programación: Intermedio
Duración: 30 minutos
Tipo de proyecto: Complemento de Google Workspace

  • Es un complemento de Google Workspace que extiende Gmail en una barra lateral.
    Figura 1: El complemento Análisis de opiniones muestra una barra lateral en Gmail en la que los usuarios pueden pedirle a Gemini que analice y aplique etiquetas a los mensajes según la opinión.
  • Un mensaje de Gmail con sentimiento neutral.
    Figura 2: El complemento etiqueta un mensaje de Gmail con la etiqueta TONO NEUTRAL 😐.
  • Un mensaje de Gmail con un sentimiento positivo.
    Figura 3: El complemento etiqueta un mensaje de Gmail con la etiqueta TONO FELIZ 😊.
  • Un mensaje de Gmail con un sentimiento de molestia.
    Figura 4: El complemento etiqueta un mensaje de Gmail con la etiqueta TONO MOLESTO 😡.

Objetivos

  • Comprende qué hace la solución.
  • Comprende qué hacen los servicios de Google dentro de la solución.
  • Configura el entorno.
  • Configura el proyecto de Google Apps Script.
  • Ejecuta la secuencia de comandos.

Acerca de esta solución

Captura de pantalla del complemento de Google Workspace de Análisis de opiniones

Esta solución es un complemento de Google Workspace que aplica etiquetas según el sentimiento de los mensajes de Gmail. Para analizar el contenido del mensaje, el complemento usa Vertex AI para solicitarle al modelo Gemini 2.5 Flash que devuelva uno de los siguientes sentimientos:

  • Positivo
  • Negativo
  • Neutral

Con la respuesta de Gemini, el complemento aplica una etiqueta de Gmail correspondiente al mensaje.

Para limitar la solicitud a la API de Vertex AI, este complemento solo analiza y aplica etiquetas a los 10 mensajes más recientes de la carpeta Recibidos del usuario de Gmail. Para obtener más información sobre las cuotas y los límites, consulta la documentación de Vertex AI.

Cómo funciona

Esta solución se compiló en Google Apps Script y usa los siguientes servicios y productos de Google:

Requisitos previos

Configura tu entorno

En esta sección, se explica cómo configurar tu entorno en la consola de Google Cloud y en Apps Script.

Configura tu proyecto de Cloud en la consola de Google Cloud

En esta sección, se muestra cómo habilitar la API de Vertex AI y configurar la pantalla de consentimiento de OAuth en tu proyecto de Cloud.

Habilita la API de Vertex AI

  1. En la consola de Google Cloud, abre tu proyecto de Google Cloud y habilita la API de Vertex AI:

    Habilitar la API

  2. Confirma que habilitarás la API en el proyecto de Cloud correcto y, luego, haz clic en Siguiente.

  3. Confirma que habilitarás la API correcta y, luego, haz clic en Habilitar.

Cómo configurar la pantalla de consentimiento de OAuth

Los complementos de Google Workspace requieren una configuración de pantalla de consentimiento. Configurar la pantalla de consentimiento de OAuth de tu complemento define lo que Google muestra a los usuarios.

  1. En la consola de Google Cloud, ve a Menú > > Branding.

    Ir a Desarrollo de la marca

  2. Si ya configuraste , puedes configurar los siguientes parámetros de configuración de la pantalla de consentimiento de OAuth en Branding, Audience y Data Access. Si ves un mensaje que dice aún no se configuró, haz clic en Comenzar:
    1. En Información de la app, en Nombre de la app, ingresa un nombre para la app.
    2. En Correo electrónico de asistencia del usuario, elige una dirección de correo electrónico de asistencia a la que los usuarios puedan comunicarse contigo si tienen preguntas sobre su consentimiento.
    3. Haz clic en Siguiente.
    4. En Público, selecciona Interno.
    5. Haz clic en Siguiente.
    6. En Información de contacto, ingresa una dirección de correo electrónico en la que puedas recibir notificaciones sobre cualquier cambio en tu proyecto.
    7. Haz clic en Siguiente.
    8. En Finalizar, revisa la Política de Datos del Usuario de los Servicios de las APIs de Google y, si la aceptas, selecciona Acepto la Política de Datos del Usuario de los Servicios de las APIs de Google.
    9. Haz clic en Continuar.
    10. Haz clic en Crear.
  3. Por el momento, puedes omitir la adición de permisos. En el futuro, cuando crees una app para usarla fuera de tu organización de Google Workspace, deberás cambiar el Tipo de usuario a Externo. Luego, agrega los permisos de autorización que requiere tu app. Para obtener más información, consulta la guía completa Configura el consentimiento de OAuth.

Crea y configura tu proyecto de Apps Script

Para crear y configurar tu proyecto de Apps Script para el complemento, completa los siguientes pasos:

  1. Haz clic en el siguiente botón para abrir el proyecto de Apps Script Análisis de opiniones de Gmail con Gemini y Vertex AI.
    Abre el proyecto de Apps Script

  2. Haz clic en Descripción general .

  3. En la página de descripción general, haz clic en Crear una copia Ícono para crear una copia.

  4. Obtén el número de tu proyecto de Cloud:

    1. En la consola de Google Cloud, ve a Menú > IAM y administración > Configuración.

      Ir a Configuración de IAM y administración

    2. En el campo Número del proyecto, copia el valor.
  5. Conecta tu proyecto de Cloud con tu proyecto de Apps Script:

    1. En el proyecto de Apps Script que copiaste, haz clic en Configuración del proyecto El ícono de configuración del proyecto.
    2. En Proyecto de Google Cloud, haz clic en Cambiar proyecto.
    3. En Número de proyecto de GCP, pega el número de proyecto de Cloud.
    4. Haz clic en Establecer el proyecto.

Prueba el complemento

Para probar el complemento, instala una implementación de prueba y, luego, abre el complemento en Gmail:

  1. Crea e instala una implementación de prueba de Apps Script:
    1. En el proyecto de Apps Script que copiaste, haz clic en Editor .
    2. Abre el archivo Code.gs y haz clic en Ejecutar. Cuando se te solicite, autoriza la secuencia de comandos.
    3. Haz clic en Implementar > Probar implementaciones.
    4. Haz clic en Instalar > Listo.
  2. Abre Gmail.

    Ir a Gmail

  3. En la barra lateral derecha, abre el complemento Análisis de opiniones.

  4. Si se te solicita, autoriza el complemento.

  5. Opcional: Para crear mensajes con los que probar tu complemento, haz clic en Generar correos electrónicos de muestra. Aparecerán tres mensajes en tu carpeta de Recibidos. Si no los ves, actualiza la página.

  6. Para agregar etiquetas, haz clic en Analizar correos electrónicos.

El complemento revisa los 10 mensajes más recientes de tu bandeja de entrada y, luego, aplica una de las siguientes etiquetas según el contenido del mensaje:

  • TONO AMABLE 😊
  • TONO NEUTRO 😐
  • TONO DE MOLESTIA 😡

Revisa el código

Revisa el código de Apps Script para esta solución:

Ver el código fuente

Code.gs

gmail-sentiment-analysis/Code.gs
/*
Copyright 2024 Google LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

/**
 * Triggered when the add-on is opened from the Gmail homepage.
 *
 * @param {Object} e - The event object.
 * @returns {Card} - The homepage card.
 */
function onHomepageTrigger(e) {
  return buildHomepageCard();
}

Cards.gs

gmail-sentiment-analysis/Cards.gs
/*
Copyright 2024-2025 Google LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

/**
 * Builds the main card displayed on the Gmail homepage.
 *
 * @returns {Card} - The homepage card.
 */
function buildHomepageCard() {
  // Create a new card builder
  const cardBuilder = CardService.newCardBuilder();

  // Create a card header
  const cardHeader = CardService.newCardHeader();
  cardHeader.setImageUrl('https://fonts.gstatic.com/s/i/googlematerialicons/mail/v6/black-24dp/1x/gm_mail_black_24dp.png');
  cardHeader.setImageStyle(CardService.ImageStyle.CIRCLE);
  cardHeader.setTitle("Analyze your Gmail");

  // Add the header to the card
  cardBuilder.setHeader(cardHeader);

  // Create a card section
  const cardSection = CardService.newCardSection();

  // Create buttons for generating sample emails and analyzing sentiment
  const buttonSet = CardService.newButtonSet();

  // Create "Generate sample emails" button
  const generateButton = createFilledButton('Generate sample emails', 'generateSampleEmails', '#34A853');
  buttonSet.addButton(generateButton);

  // Create "Analyze emails" button
  const analyzeButton = createFilledButton('Analyze emails', 'analyzeSentiment', '#FF0000');
  buttonSet.addButton(analyzeButton);

  // Add the button set to the section
  cardSection.addWidget(buttonSet);

  // Add the section to the card
  cardBuilder.addSection(cardSection);

  // Build and return the card
  return cardBuilder.build();
}

/**
 * Creates a filled text button with the specified text, function, and color.
 *
 * @param {string} text - The text to display on the button.
 * @param {string} functionName - The name of the function to call when the button is clicked.
 * @param {string} color - The background color of the button.
 * @returns {TextButton} - The created text button.
 */
function createFilledButton(text, functionName, color) {
  // Create a new text button
  const textButton = CardService.newTextButton();

  // Set the button text
  textButton.setText(text);

  // Set the action to perform when the button is clicked
  const action = CardService.newAction();
  action.setFunctionName(functionName);
  textButton.setOnClickAction(action);

  // Set the button style to filled
  textButton.setTextButtonStyle(CardService.TextButtonStyle.FILLED);

  // Set the background color
  textButton.setBackgroundColor(color);

  return textButton;
}

/**
 * Creates a notification response with the specified text.
 *
 * @param {string} notificationText - The text to display in the notification.
 * @returns {ActionResponse} - The created action response.
 */
function buildNotificationResponse(notificationText) {
  // Create a new notification
  const notification = CardService.newNotification();
  notification.setText(notificationText);

  // Create a new action response builder
  const actionResponseBuilder = CardService.newActionResponseBuilder();

  // Set the notification for the action response
  actionResponseBuilder.setNotification(notification);

  // Build and return the action response
  return actionResponseBuilder.build();
}

Gmail.gs

gmail-sentiment-analysis/Gmail.gs
/*
Copyright 2024-2025 Google LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

/**
 * Analyzes the sentiment of the first 10 threads in the inbox
 * and labels them accordingly.
 *
 * @returns {ActionResponse} - A notification confirming completion.
 */
function analyzeSentiment() {
  // Analyze and label emails
  analyzeAndLabelEmailSentiment();

  // Return a notification
  return buildNotificationResponse("Successfully completed sentiment analysis");
}

/**
 * Analyzes the sentiment of emails and applies appropriate labels.
 */
function analyzeAndLabelEmailSentiment() {
  // Define label names
  const labelNames = ["HAPPY TONE 😊", "NEUTRAL TONE 😐", "UPSET TONE 😡"];

  // Get or create labels for each sentiment
  const positiveLabel = GmailApp.getUserLabelByName(labelNames[0]) || GmailApp.createLabel(labelNames[0]);
  const neutralLabel = GmailApp.getUserLabelByName(labelNames[1]) || GmailApp.createLabel(labelNames[1]);
  const negativeLabel = GmailApp.getUserLabelByName(labelNames[2]) || GmailApp.createLabel(labelNames[2]);

  // Get the first 10 threads in the inbox
  const threads = GmailApp.getInboxThreads(0, 10);

  // Iterate through each thread
  for (const thread of threads) {
    // Iterate through each message in the thread
    const messages = thread.getMessages();
    for (const message of messages) {
      // Get the plain text body of the message
      const emailBody = message.getPlainBody();

      // Analyze the sentiment of the email body
      const sentiment = processSentiment(emailBody);

      // Apply the appropriate label based on the sentiment
      if (sentiment === 'positive') {
        thread.addLabel(positiveLabel);
      } else if (sentiment === 'neutral') {
        thread.addLabel(neutralLabel);
      } else if (sentiment === 'negative') {
        thread.addLabel(negativeLabel);
      }
    }
  }
}

/**
 * Generates sample emails for testing the sentiment analysis.
 *
 * @returns {ActionResponse} - A notification confirming email generation.
 */
function generateSampleEmails() {
  // Get the current user's email address
  const userEmail = Session.getActiveUser().getEmail();

  // Define sample emails
  const sampleEmails = [
    {
      subject: 'Thank you for amazing service!',
      body: 'Hi, I really enjoyed working with you. Thank you again!',
      name: 'Customer A'
    },
    {
      subject: 'Request for information',
      body: 'Hello, I need more information on your recent product launch. Thank you.',
      name: 'Customer B'
    },
    {
      subject: 'Complaint!',
      body: '',
      htmlBody: `<p>Hello, You are late in delivery, again.</p>
<p>Please contact me ASAP before I cancel our subscription.</p>`,
      name: 'Customer C'
    }
  ];

  // Send each sample email
  for (const email of sampleEmails) {
    GmailApp.sendEmail(userEmail, email.subject, email.body, {
      name: email.name,
      htmlBody: email.htmlBody
    });
  }

  // Return a notification
  return buildNotificationResponse("Successfully generated sample emails");
}

Vertex.gs

gmail-sentiment-analysis/Vertex.gs
/*
Copyright 2024-2025 Google LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Replace with your project ID
const PROJECT_ID = '[ADD YOUR GCP PROJECT ID HERE]';

// Location for your Vertex AI model
const VERTEX_AI_LOCATION = 'us-central1';

// Model ID to use for sentiment analysis
const MODEL_ID = 'gemini-2.5-flash';

/**
 * Sends the email text to Vertex AI for sentiment analysis.
 *
 * @param {string} emailText - The text of the email to analyze.
 * @returns {string} - The sentiment of the email ('positive', 'negative', or 'neutral').
 */
function processSentiment(emailText) {
  // Construct the API endpoint URL
  const apiUrl = `https://${VERTEX_AI_LOCATION}-aiplatform.googleapis.com/v1/projects/${PROJECT_ID}/locations/${VERTEX_AI_LOCATION}/publishers/google/models/${MODEL_ID}:generateContent`;

  // Prepare the request payload
  const payload = {
    contents: [
      {
        role: "user",
        parts: [
          {
            text: `Analyze the sentiment of the following message: ${emailText}`
          }
        ]
      }
    ],
    generationConfig: {
      temperature: 0.9,
      maxOutputTokens: 1024,
      responseMimeType: "application/json",
      // Expected response format for simpler parsing.
      responseSchema: {
        type: "object",
        properties: {
          response: {
            type: "string",
            enum: ["positive", "negative", "neutral"]
          }
        }
      }
    }
  };

  // Prepare the request options
  const options = {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${ScriptApp.getOAuthToken()}`
    },
    contentType: 'application/json',
    muteHttpExceptions: true, // Set to true to inspect the error response
    payload: JSON.stringify(payload)
  };

  // Make the API request
  const response = UrlFetchApp.fetch(apiUrl, options);

  // Parse the response. There are two levels of JSON responses to parse.
  const parsedResponse = JSON.parse(response.getContentText());
  const sentimentResponse = JSON.parse(parsedResponse.candidates[0].content.parts[0].text).response;

  // Return the sentiment
  return sentimentResponse;
}

appsscript.json

gmail-sentiment-analysis/appsscript.json
{
  "timeZone": "America/Toronto",
  "oauthScopes": [
    "https://www.googleapis.com/auth/cloud-platform",
    "https://www.googleapis.com/auth/gmail.addons.execute",
    "https://www.googleapis.com/auth/gmail.labels",
    "https://www.googleapis.com/auth/gmail.modify",
    "https://www.googleapis.com/auth/script.external_request",
    "https://www.googleapis.com/auth/userinfo.email"
  ],
  "addOns": {
    "common": {
      "name": "Sentiment Analysis",
      "logoUrl": "https://fonts.gstatic.com/s/i/googlematerialicons/sentiment_extremely_dissatisfied/v6/black-24dp/1x/gm_sentiment_extremely_dissatisfied_black_24dp.png"
    },
    "gmail": {
      "homepageTrigger": {
        "runFunction": "onHomepageTrigger",
        "enabled": true
      }
    }
  },
  "exceptionLogging": "STACKDRIVER",
  "runtimeVersion": "V8"
}

Limpia

Para evitar que se apliquen cargos a tu cuenta de Google Cloud por los recursos que usaste en este instructivo, te recomendamos que borres el proyecto de Cloud.

  1. En la consola de Google Cloud, ve a la página Administrar recursos. Haz clic en Menú > IAM y administración > Administrar recursos.

    Ir al administrador de recursos

  2. En la lista de proyectos, selecciona el proyecto que deseas borrar y haz clic en Borrar .
  3. En el diálogo, escribe el ID del proyecto y, luego, haz clic en Cerrar para borrar el proyecto.

Próximos pasos