Uso de la computadora

La herramienta de uso de la computadora de Gemini te permite crear agentes de control de navegadores, dispositivos móviles y computadoras que interactúan con interfaces gráficas de usuario (GUI) y automatizan tareas en ellas. Cuando se habilita el uso de la computadora, el modelo usa capturas de pantalla para observar la pantalla y actuar generando acciones específicas de la IU, como clics del mouse, desplazamientos y entradas de teclado.

Gemini admite varias funciones avanzadas de uso de la computadora:

  • Compatibilidad con varios entornos: Crea agentes para entornos de navegador, dispositivos móviles y computadoras.
  • Acciones optimizadas con intents: Las acciones incluyen un campo intent que explica el razonamiento del modelo detrás de cada paso.
  • Políticas de seguridad configurables: Ajusta el comportamiento de seguridad con categorías y anulaciones de políticas integradas.
  • Detección de inyección de instrucciones: Análisis de capturas de pantalla opcional para detectar instrucciones adversas ocultas.

Puedes utilizar el uso de computadoras para crear agentes que puedan hacer lo siguiente:

  • Automatiza el ingreso de datos repetitivo o el llenado de formularios en sitios web y aplicaciones de escritorio.
  • Realiza pruebas automatizadas de los flujos de usuarios web, móviles y de escritorio.
  • Navegar por sitios web y aplicaciones para recopilar información (por ejemplo, comparar información de productos, precios y opiniones)
  • Ayudar a los usuarios realizando secuencias de acciones de varios pasos en todas las aplicaciones.

En esta guía, se tratan los siguientes temas:

Al igual que con la llamada a funciones, debes escribir el código de la aplicación del cliente para recibir la llamada a la función de uso de la computadora y ejecutar las acciones correspondientes.

En esta guía, se supone que usas el SDK de IA generativa de Google para Python y que conoces la API de Playwright.

Puedes encontrar la implementación de referencia para el uso de computadoras en GitHub.

Modelos compatibles

El uso de la computadora es compatible con los siguientes modelos:

Haz clic para expandir los modelos compatibles

Cómo funciona el uso de la computadora

En lugar de generar respuestas de texto, el uso de la computadora determina cuándo realizar acciones específicas de la IU, como clics del mouse, y devuelve los parámetros necesarios para ejecutar estas acciones. Debes escribir el código de la aplicación del cliente para recibir el function_call de uso de la computadora y ejecutar las acciones correspondientes.

Las interacciones de uso de la computadora siguen un proceso de bucle de agente:

  1. Envía una solicitud al modelo

    • Agrega el uso de la computadora (y, de forma opcional, cualquier herramienta adicional) a tu solicitud a la API.
    • Envía una instrucción al modelo con la solicitud del usuario y una captura de pantalla que represente el estado actual de la GUI.
  2. Recibe la respuesta del modelo

    • El modelo analiza la solicitud del usuario y la captura de pantalla, y genera una respuesta que incluye un function_call sugerido que representa una acción de la IU (por ejemplo, "haz clic en la coordenada (x,y)" o "escribe 'texto'"). Consulta Acciones admitidas para obtener la lista de todas las acciones que puedes usar con el modelo.
    • La respuesta de la API también puede incluir un safety_decision de un sistema de seguridad interno que haya verificado la acción propuesta por el modelo. Este safety_decision clasifica la acción de la siguiente manera:
      • Regular o permitida: La acción se considera segura. Esto también puede representarse por la ausencia de safety_decision.
      • Requiere confirmación: El modelo está a punto de realizar una acción que puede ser riesgosa (por ejemplo, hacer clic en un banner de "aceptar cookies").
  3. Ejecuta la acción recibida

    • Tu código del cliente recibe el function_call y cualquier safety_decision que lo acompañe.
    • Si el safety_decision indica que es normal o permitido (o si no hay ningún safety_decision presente), tu código del cliente puede ejecutar el function_call especificado en tu entorno de destino (como un navegador web).
    • Si el safety_decision indica que se requiere confirmación, tu aplicación debe solicitarle al usuario final que confirme antes de ejecutar el function_call. Si el usuario confirma, continúa con la ejecución de la acción. Si el usuario rechaza, no ejecutes la acción.
  4. Captura el estado del entorno nuevo

    • Si se ejecutó la acción, tu cliente captura una nueva captura de pantalla de la GUI y la URL actual para enviarla de vuelta al modelo como parte de un function_response.
    • Si el sistema de seguridad bloqueó una acción o el usuario rechazó la confirmación, tu aplicación podría enviar un tipo diferente de comentarios al modelo o finalizar la interacción.

Se envía una solicitud nueva al modelo con el estado actualizado. El proceso se repite desde el paso 2, con el modelo que usa la nueva captura de pantalla (si se proporciona) y el objetivo en curso para sugerir la siguiente acción. El bucle continúa hasta que se completa la tarea, se produce un error o se finaliza el proceso (por ejemplo, si los filtros de seguridad o la decisión del usuario bloquean una respuesta).

En el siguiente diagrama, se ilustra cómo funciona el uso de la computadora:

Diagrama de flujo que muestra un bucle cíclico de interacción de uso de la computadora. Comienza con una
"Tarea inicial" que lleva a "Captura de pantalla + Contexto anterior". Paso 1: "Envía una
solicitud al modelo" envía estos datos al "Modelo". Paso 2: "Recibe la respuesta del modelo" pasa una acción al "Entorno de la computadora (app cliente)". Paso 3:
"Ejecutar la acción recibida" actualiza el "Entorno de la computadora (app cliente)".
Paso 4: "Captura el nuevo estado del entorno" envía la captura de pantalla y el contexto actualizados al inicio del bucle.

Habilitar el uso de la computadora

Para habilitar el uso de la computadora, asegúrate de que tu aplicación use uno de los modelos compatibles y agrega el uso de la computadora a tu lista de herramientas habilitadas:

Python

from google import genai
from google.genai import types
from google.genai.types import Content, Part

client = genai.Client(
    enterprise=True,
    project="PROJECT_ID",
    location="global",
)

# Add computer use to the list of tools
generate_content_config = types.GenerateContentConfig(
    tools=[
        types.Tool(
            computer_use=types.ComputerUse(
                environment=types.Environment.ENVIRONMENT_BROWSER,
            )
        ),
    ]
)

contents = [
    Content(
        role="user",
        parts=[
            Part(text="Go to google.com and search for 'weather in New York'"),
        ],
    )
]

response = client.models.generate_content(
    model="gemini-3.8-flash",
    contents=contents,
    config=generate_content_config,
)
print(response)
      

Node.js

import { GoogleGenAI, Environment } from '@google/genai';

const ai = new GoogleGenAI({
  enterprise: true,
  project: 'PROJECT_ID',
  location: 'global',
});

const response = await ai.models.generateContent({
  model: 'gemini-3.8-flash',
  contents: "Go to google.com and search for 'weather in New York'",
  config: {
    tools: [
      {
        computerUse: {
          environment: Environment.ENVIRONMENT_BROWSER,
        },
      },
    ],
  },
});

console.log(response);
      

API de REST

curl -X POST "https://aiplatform.googleapis.com/v1/projects/PROJECT_ID/locations/global/publishers/google/models/gemini-3.8-flash:generateContent" \
  -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "Content-Type: application/json" \
  -d '{
    "contents": [
      {
        "role": "user",
        "parts": [
          {
            "text": "Go to google.com and search for '\''weather in New York'\''"
          }
        ]
      }
    ],
    "tools": [
      {
        "computer_use": {
          "environment": "ENVIRONMENT_BROWSER"
        }
      }
    ]
  }'
      

Configura tu entorno de ejecución

Antes de compilar con la herramienta de uso de computadoras, configura lo siguiente:

  • Un entorno de ejecución seguro: Ejecuta tu agente en una máquina virtual (VM) o un contenedor (como Docker) en una zona de pruebas para aislarlo de tu sistema host.
  • Un controlador de acciones del cliente: Implementa la lógica del cliente para ejecutar clics del mouse, pulsaciones del teclado y capturas de pantalla.

Primero, instala el SDK y las dependencias del navegador y, luego, configura tu entorno:

pip install --upgrade google-genai playwright termcolor
playwright install chromium

export GOOGLE_CLOUD_PROJECT="PROJECT_ID"
export GOOGLE_CLOUD_LOCATION="global"
export GOOGLE_GENAI_USE_ENTERPRISE=True

A continuación, inicializa una instancia del navegador Playwright con dimensiones de viewport explícitas para que las coordenadas normalizadas (0-1000) se ajusten con precisión:

from playwright.sync_api import sync_playwright

SCREEN_WIDTH = 1440
SCREEN_HEIGHT = 900

playwright = sync_playwright().start()
browser = playwright.chromium.launch(headless=False)

context = browser.new_context(
    viewport={"width": SCREEN_WIDTH, "height": SCREEN_HEIGHT}
)
page = context.new_page()
page.goto("https://www.google.com")

Envía una solicitud

Después de configurar la herramienta de uso de la computadora, envía una instrucción al modelo que incluya el objetivo del usuario y una captura de pantalla inicial de la GUI.

También puedes agregar lo siguiente de forma opcional:

  • Acciones excluidas: Si hay acciones de la lista de acciones de IU compatibles que no quieres que realice el modelo, especifícalas en excluded_predefined_functions.
  • Funciones definidas por el usuario: Es posible que quieras incluir funciones personalizadas definidas por el usuario, además del uso de la computadora.

En el siguiente código de muestra, se habilita el uso de la computadora y se envía la solicitud al modelo:

Python

from google import genai
from google.genai import types
from google.genai.types import Content, Part

client = genai.Client()

# Specify predefined functions to exclude (optional)
excluded_functions = ["drag_and_drop"]

# Configuration for computer use with browser environment
generate_content_config = genai.types.GenerateContentConfig(
    tools=[
        # 1. Computer Use tool with browser environment
        types.Tool(
            computer_use=types.ComputerUse(
                environment=types.Environment.ENVIRONMENT_BROWSER,
                # Optional: Exclude specific predefined functions
                excluded_predefined_functions=excluded_functions
                )
              ),
        # 2. Optional: Custom user-defined functions (need to defined above)
        # types.Tool(
           # function_declarations=custom_functions
           #   )
    ],
)

# Create the content with user message
contents: list[Content] = [
    Content(
        role="user",
        parts=[
            Part(text="Search for highly rated smart fridges with touchscreen, 2 doors, around 25 cu ft, priced below 4000 dollars on Google Shopping. Create a bulleted list of the 3 cheapest options in the format of name, description, price in an easy-to-read layout."),
            # Optional: include a screenshot of the initial state
            # Part.from_bytes(
                 # data=screenshot_image_bytes,
                 # mime_type='image/png',
            # ),
        ],
    )
]

# Generate content with the configured settings
response = client.models.generate_content(
    model='gemini-3.8-flash',
    contents=contents,
    config=generate_content_config,
)

# Print the response output
print(response.text)
      

También puedes incluir funciones definidas por el usuario personalizadas para extender la funcionalidad del modelo. Consulta Gemini 3.5 Flash o versiones posteriores y casos de uso para dispositivos móviles o computadoras para obtener información sobre cómo puedes configurar el uso de la computadora para casos de uso para dispositivos móviles agregando acciones como open_app, long_press_at y go_home, y excluyendo acciones específicas del navegador.

Recibe respuestas

El modelo responde con uno o más FunctionCalls si determina que se necesitan acciones de la IU o funciones definidas por el usuario para completar la tarea. El código de tu aplicación debe analizar estas acciones, ejecutarlas y recopilar los resultados. El uso de computadoras admite la llamada paralela a funciones, lo que significa que el modelo puede devolver varias acciones independientes en un solo turno.

Para Gemini 3.5 Flash y los modelos posteriores, cada acción incluye un argumento intent que explica el razonamiento del modelo detrás de ese paso:

{
  "content": {
    "parts": [
      {
        "function_call": {
          "name": "type",
          "args": {
            "x": 450,
            "y": 320,
            "text": "smart fridge touchscreen 2 doors 25 cu ft under $4000",
            "press_enter": true,
            "intent": "Type the search query into the Google search box and submit."
          }
        }
      }
    ]
  }
}

Según la acción, la respuesta de la API también puede devolver un safety_decision dentro de los argumentos de la llamada a función:

{
  "content": {
    "parts": [
      {
        "function_call": {
          "name": "click",
          "args": {
            "x": 60,
            "y": 100,
            "intent": "Click the 'I'm not a robot' verification checkbox.",
            "safety_decision": {
              "explanation": "I have encountered a CAPTCHA challenge that requires interaction. I need you to complete the challenge by clicking the 'I'm not a robot' checkbox and any subsequent verification steps.",
              "decision": "require_confirmation"
            }
          }
        }
      }
    ]
  }
}

Ejecutar acciones recibidas

Después de recibir una respuesta, analiza el function_call, escala las coordenadas de la cuadrícula normalizada de 0 a 1000 a las dimensiones reales de píxeles del viewport y ejecuta la acción:

import time
from typing import Any, Dict, List, Tuple
import termcolor


def denormalize_x(x: int, screen_width: int) -> int:
    """Convert normalized x coordinate (0-1000) to actual pixel coordinate."""
    return int(x / 1000 * screen_width)


def denormalize_y(y: int, screen_height: int) -> int:
    """Convert normalized y coordinate (0-1000) to actual pixel coordinate."""
    return int(y / 1000 * screen_height)


def get_safety_confirmation(safety_decision: Dict[str, Any]) -> str:
    """Prompt user for confirmation when safety check is triggered."""
    termcolor.cprint("Safety service requires explicit confirmation!", color="red")
    print(safety_decision.get("explanation", ""))

    decision = ""
    while decision.lower() not in ("y", "n", "ye", "yes", "no"):
        decision = input("Do you wish to proceed? [Y]es/[N]o\n")

    if decision.lower() in ("n", "no"):
        return "TERMINATE"
    return "CONTINUE"


def execute_function_calls(
    response, page, screen_width: int, screen_height: int
) -> List[Tuple[str, Dict[str, Any]]]:
    """Extract and execute modern and legacy function calls from Gemini response."""
    candidate = response.candidates[0]
    function_calls = []
    thoughts = []

    for part in candidate.content.parts:
        if hasattr(part, "function_call") and part.function_call:
            function_calls.append(part.function_call)
        elif hasattr(part, "text") and part.text:
            thoughts.append(part.text)

    if thoughts:
        print(f"Model Reasoning: {' '.join(thoughts)}")

    results: List[Tuple[str, Dict[str, Any]]] = []
    for function_call in function_calls:
        fname = function_call.name
        args = function_call.args or {}
        action_result: Dict[str, Any] = {}

        print(f" -> Executing: {fname} (Intent: {args.get('intent', 'N/A')})")

        # Check for safety decision before executing
        if "safety_decision" in args:
            decision = get_safety_confirmation(args["safety_decision"])
            if decision == "TERMINATE":
                print("Terminating agent loop")
                break
            action_result["safety_acknowledgement"] = "true"

        try:
            if fname in ("open_web_browser", "open_app", "take_screenshot"):
                pass

            elif fname in (
                "click",
                "click_at",
                "double_click",
                "triple_click",
                "middle_click",
                "right_click",
                "mouse_down",
                "mouse_up",
                "move",
                "hover_at",
            ):
                actual_x = denormalize_x(args["x"], screen_width)
                actual_y = denormalize_y(args["y"], screen_height)

                if fname in ("click", "click_at"):
                    page.mouse.click(actual_x, actual_y)
                elif fname == "double_click":
                    page.mouse.dblclick(actual_x, actual_y)
                elif fname == "triple_click":
                    page.mouse.click(actual_x, actual_y, click_count=3)
                elif fname == "middle_click":
                    page.mouse.click(actual_x, actual_y, button="middle")
                elif fname == "right_click":
                    page.mouse.click(actual_x, actual_y, button="right")
                elif fname == "mouse_down":
                    page.mouse.move(actual_x, actual_y)
                    page.mouse.down()
                elif fname == "mouse_up":
                    page.mouse.move(actual_x, actual_y)
                    page.mouse.up()
                elif fname in ("move", "hover_at"):
                    page.mouse.move(actual_x, actual_y)

            elif fname in ("type", "type_text_at"):
                actual_x = denormalize_x(args["x"], screen_width) if "x" in args else None
                actual_y = denormalize_y(args["y"], screen_height) if "y" in args else None
                text = args["text"]
                press_enter = args.get("press_enter", False)
                clear_before_typing = args.get("clear_before_typing", True)

                if actual_x is not None and actual_y is not None:
                    page.mouse.click(actual_x, actual_y)
                    time.sleep(0.1)

                if clear_before_typing:
                    page.keyboard.press("ControlOrMeta+A")
                    page.keyboard.press("Backspace")

                page.keyboard.type(text)
                if press_enter:
                    page.keyboard.press("Enter")

            elif fname in ("scroll", "scroll_at", "scroll_document"):
                if "x" in args and "y" in args:
                    page.mouse.move(denormalize_x(args["x"], screen_width), denormalize_y(args["y"], screen_height))
                direction = args.get("direction", "down")
                magnitude = args.get("magnitude_in_pixels", args.get("magnitude", 300))
                dx, dy = 0, 0
                if direction == "down":
                    dy = magnitude
                elif direction == "up":
                    dy = -magnitude
                elif direction == "right":
                    dx = magnitude
                elif direction == "left":
                    dx = -magnitude
                page.mouse.wheel(dx, dy)

            elif fname == "drag_and_drop":
                start_x = denormalize_x(args.get("start_x", args.get("x", 0)), screen_width)
                start_y = denormalize_y(args.get("start_y", args.get("y", 0)), screen_height)
                end_x = denormalize_x(args.get("end_x", args.get("destination_x", 0)), screen_width)
                end_y = denormalize_y(args.get("end_y", args.get("destination_y", 0)), screen_height)
                page.mouse.move(start_x, start_y)
                page.mouse.down()
                page.mouse.move(end_x, end_y)
                page.mouse.up()

            elif fname == "press_key":
                page.keyboard.press(args["key"])
            elif fname == "key_down":
                page.keyboard.down(args["key"])
            elif fname == "key_up":
                page.keyboard.up(args["key"])
            elif fname == "hotkey":
                page.keyboard.press("+".join(args.get("keys", [])))
            elif fname == "key_combination":
                page.keyboard.press(args["keys"])
            elif fname == "navigate":
                page.goto(args["url"])
            elif fname == "search":
                page.goto("https://www.google.com")
            elif fname == "go_back":
                page.go_back()
            elif fname == "go_forward":
                page.go_forward()
            elif fname in ("wait", "wait_5_seconds"):
                time.sleep(5 if fname == "wait_5_seconds" else args.get("seconds", 1))
            else:
                action_result["warning"] = f"unhandled_function: {fname}"

            page.wait_for_load_state(timeout=5000)
            time.sleep(1)

        except Exception as e:
            print(f"Error executing {fname}: {e}")
            action_result["error"] = str(e)

        results.append((fname, action_result))

    return results

Si el safety_decision que se muestra es require_confirmation, debes pedirle al usuario que confirme antes de continuar con la ejecución de la acción. Según las condiciones del servicio, no se te permite omitir las solicitudes de confirmación humana.

Captura el estado nuevo

Después de ejecutar las acciones, empaqueta cada resultado de acción y la captura de pantalla actualizada en un FunctionResponse para devolverlo al modelo:

from google.genai import types
from google.genai.types import Content, FunctionResponse, Part


def get_function_responses(
    page, results: List[Tuple[str, Dict[str, Any]]]
) -> Content:
    """Capture screenshot and URL and package them as FunctionResponses."""
    screenshot = page.screenshot(type="png")
    current_url = page.url
    function_response_parts = []

    for name, action_result in results:
        function_response_parts.append(
            Part(
                function_response=FunctionResponse(
                    name=name,
                    response={
                        "url": current_url,
                        **action_result,
                    },
                    parts=[
                        types.FunctionResponsePart(
                            inline_data=types.FunctionResponseBlob(
                                mime_type="image/png",
                                data=screenshot,
                            )
                        )
                    ],
                )
            )
        )

    return Content(
        role="user",
        parts=function_response_parts,
    )

Crea un bucle de agentes

Combina estos pasos en un bucle de extremo a extremo que itere de forma continua entre el modelo y el entorno de ejecución:

Python

from google import genai
from google.genai import types
from google.genai.types import Content, Part
from playwright.sync_api import sync_playwright

def has_function_calls(response) -> bool:
    """Check if model response contains any function calls."""
    candidate = response.candidates[0]
    return any(
        hasattr(part, "function_call") and part.function_call
        for part in candidate.content.parts
    )

def main():
    client = genai.Client(
        enterprise=True,
        project="PROJECT_ID",
        location="global",
    )

    generate_content_config = types.GenerateContentConfig(
        tools=[
            types.Tool(
                computer_use=types.ComputerUse(
                    environment=types.Environment.ENVIRONMENT_BROWSER,
                    enable_prompt_injection_detection=True,
                )
            )
        ],
    )

    screen_width, screen_height = 1440, 900

    with sync_playwright() as p:
        browser = p.chromium.launch(headless=False)
        context = browser.new_context(
            viewport={"width": screen_width, "height": screen_height}
        )
        page = context.new_page()
        page.goto("https://www.google.com")

        initial_screenshot = page.screenshot(type="png")
        contents: list[Content] = [
            Content(
                role="user",
                parts=[
                    Part(text="Search for flights from San Francisco to Honolulu departing June 30 and returning July 6."),
                    Part.from_bytes(data=initial_screenshot, mime_type="image/png"),
                ],
            )
        ]

        # Agent loop: iterate until model provides a final text response
        for iteration in range(10):
            print(f"\n--- Iteration {iteration + 1} ---")

            response = client.models.generate_content(
                model="gemini-3.8-flash",
                contents=contents,
                config=generate_content_config,
            )

            contents.append(response.candidates[0].content)

            if not has_function_calls(response):
                print(f"FINAL RESPONSE:\n{response.text}")
                break

            results = execute_function_calls(response, page, screen_width, screen_height)
            if not results:
                break

            user_feedback_content = get_function_responses(page, results)
            contents.append(user_feedback_content)

        browser.close()

if __name__ == "__main__":
    main()
      

Gemini 3.5 Flash o versiones posteriores, y casos de uso para dispositivos móviles o computadoras

El uso de computadoras admite la configuración de entornos para dispositivos móviles (ENVIRONMENT_MOBILE), computadoras de escritorio (ENVIRONMENT_DESKTOP) o navegadores (ENVIRONMENT_BROWSER). El valor predeterminado es ENVIRONMENT_BROWSER.

En los siguientes ejemplos, se muestra cómo configurar el entorno de ejecución para el uso de la computadora con Gemini 3.5 Flash o versiones posteriores:

API de REST

Antes de usar cualquiera de los datos de solicitud a continuación, realiza los siguientes reemplazos:

  • PROJECT_ID: ID del proyecto

HTTP method and URL:

POST https://aiplatform.googleapis.com/v1/projects/PROJECT_ID/locations/global/publishers/google/models/gemini-3.5-flash:generateContent

Cuerpo JSON de la solicitud:

{
  "contents": [
    {
      "role": "user",
      "parts": {
        "text": "find me a flight from SF to Hawaii on Jun 30th, coming back on Jul 6th. start by navigating directly to flights.google.com"
      }
    }
  ],
  "generation_config": {
    "candidateCount": 1
  },
  "tools": [
    {
      "computer_use": {
        "environment": "ENVIRONMENT_BROWSER"
      }
    }
  ]
}

Para enviar tu solicitud, expande una de estas opciones:

Deberías recibir un código de estado exitoso (2xx) y una respuesta vacía.

Python

En el siguiente ejemplo, se requiere la versión 2.7.0 o posterior de python-genai.

from google import genai
from google.genai.types import (
    Part,
    GenerateContentConfig,
    Content,
    Tool,
    ComputerUse,
    Environment,
    ThinkingConfig,
)
client = genai.Client()

response = client.models.generate_content(
    model="gemini-3.6-flash",
    contents=[
        Content(
            role="user",
            parts=[
                Part(text="find me a flight from SF to Hawaii on Jun 30th, coming back on Jul 6th"),
            ],
        )
    ],
    config=GenerateContentConfig(
        temperature=1,
        top_p=0.95,
        top_k=40,
        max_output_tokens=8192,
        tools=[
            Tool(
                computer_use=ComputerUse(
                    environment=Environment.ENVIRONMENT_MOBILE
                ),
            ),
        ],
        thinking_config=ThinkingConfig(
            include_thoughts=True
        ),
    )
)

Funciones definidas por el usuario personalizadas

Puedes extender la funcionalidad del modelo combinando computer_use con funciones definidas por el usuario (function_declarations). Por ejemplo, en situaciones con intervención humana (HITL), puedes excluir acciones predefinidas específicas con excluded_predefined_functions y registrar una función yield_to_user personalizada para que el agente pueda devolver el control a un humano cuando encuentre mensajes de 2FA o pantallas ambiguas:

Python

from google import genai
from google.genai import types
from google.genai.types import Content, Part

client = genai.Client()

def yield_to_user(reason: str) -> dict[str, str]:
    """Yields control back to the user for assistance or verification when an automated action is unsafe or ambiguous.

    Args:
        reason: The reason why the agent is yielding control to the human.
    """
    return {"status": "yielded", "reason": reason}

generate_content_config = types.GenerateContentConfig(
    tools=[
        types.Tool(
            computer_use=types.ComputerUse(
                environment=types.Environment.ENVIRONMENT_BROWSER,
                excluded_predefined_functions=["drag_and_drop"],
            )
        ),
        types.Tool(
            function_declarations=[
                types.FunctionDeclaration.from_callable(
                    client=client, callable=yield_to_user
                )
            ]
        ),
    ]
)

response = client.models.generate_content(
    model="gemini-3.8-flash",
    contents=[
        Content(
            role="user",
            parts=[
                Part(
                    text="Click the submit button. If you need a second-factor authentication code, ask me."
                ),
            ],
        )
    ],
    config=generate_content_config,
)
print(response)
      

Acciones compatibles

La herramienta de uso de la computadora permite que el modelo solicite las siguientes acciones con un FunctionCall. Tu código del cliente debe implementar la lógica de ejecución para estas acciones. Consulta la implementación de referencia para ver ejemplos. En la siguiente tabla, se muestran las acciones admitidas para el entorno del navegador. Los modelos Gemini 3 Flash y Gemini 3.5 Flash o versiones posteriores tienen diferentes conjuntos de nombres de FunctionCall.

Gemini 3.5 Flash o una versión posterior

Gemini 3.5 Flash: Tienes acceso a diferentes conjuntos de llamadas a funciones según el entorno que selecciones:

Entorno del navegador

Nombre del comando Descripción Argumentos (en la llamada a función)
click Hace clic con el botón izquierdo en la coordenada especificada. y: int (0-999)
x: int (0-999)
intent: str
double_click Hace doble clic en la coordenada especificada. y: int (0-999)
x: int (0-999)
intent: str
triple_click Hace clic tres veces en la coordenada especificada. y: int (0-999)
x: int (0-999)
intent: str
middle_click Hace clic con el botón central en la coordenada especificada. y: int (0-999)
x: int (0-999)
intent: str
right_click Hace clic con el botón derecho en la coordenada especificada. y: int (0-999)
x: int (0-999)
intent: str
mouse_down Mantiene presionado el botón del mouse en la coordenada. y: int (0-999)
x: int (0-999)
intent: str
mouse_up Suelta el botón del mouse en la coordenada. y: int (0-999)
x: int (0-999)
intent: str
move Mueve el cursor a la posición especificada. y: int (0-999)
x: int (0-999)
intent: str
type Escribe texto (opcionalmente, haz clic primero en x, y). y: int (0-999, opcional)
x: int (0-999, opcional)
text: str
press_enter: bool (opcional, el valor predeterminado es falso)
intent: str
drag_and_drop Arrastra un elemento de (start_x, start_y) a (end_x, end_y). start_y: int (0-999)
start_x: int (0-999)
end_y: int (0-999)
end_x: int (0-999)
intent: str
wait Espera la cantidad de segundos especificada. seconds: int (opcional, el valor predeterminado es 1)
intent: str
press_key Presiona la tecla especificada y la suelta. key: str
intent: str
key_down Mantiene presionada la tecla especificada. key: str
intent: str
key_up Suelta la tecla especificada. key: str
intent: str
hotkey Presiona la combinación de teclas especificada. keys: List[str]
intent: str
take_screenshot Muestra una captura de pantalla de la pantalla actual. intent: str
scroll Se desplaza hacia arriba, abajo, izquierda o derecha en una coordenada por una distancia de píxeles. y: int (0-999)
x: int (0-999)
direction: str ("up", "down", "left", "right")
magnitude_in_pixels: int (0-999, opcional, predeterminado 300)
intent: str
open_web_browser Abre el navegador web. intent: str
navigate Navega el navegador directamente a la URL especificada. url: str
intent: str
go_back Regresa a la página web anterior en el historial de navegación. intent: str
go_forward Navega hacia adelante a la siguiente página web en el historial de navegación. intent: str

Entorno para dispositivos móviles

Nombre del comando Descripción Argumentos (en la llamada a función)
open_app Abre una aplicación por su nombre. intent: str
click Hace clic con el botón izquierdo en la coordenada. y: int (0-999)
x: int (0-999)
intent: str
list_apps Muestra una lista de las aplicaciones disponibles en el dispositivo y devuelve sus nombres y nombres de paquetes. intent: str
wait Espera la cantidad de segundos especificada. intent: str
seconds: int(opcional, el valor predeterminado es 1)
go_back Vuelve a la página web anterior en el historial del navegador. intent: str
type Escribe texto. text: str
intent: str
press_enter: bool(Opcional, predeterminado: falso)
drag_and_drop Arrastra el elemento de start_y y start_x a end_y y end_x start_y: int (0-999)
start_x: int (0-999)
end_y: int (0-999)
end_x: int (0-999)
intent: str
long_press Mantiene presionada una coordenada específica y (0-999), x (0-999) en la pantalla. y: int (0-999)
x: int (0-999)
intent: str
seconds: int (opcional, valor predeterminado 2)
press_key Presiona la tecla especificada y la suelta. key: str
intent: str
take_screenshot Muestra una captura de pantalla de la pantalla actual. intent: str

Entorno de escritorio

Nombre del comando Descripción Argumentos (en la llamada a función)
click Hace clic con el botón izquierdo en la coordenada. y: int (0-999)
x: int (0-999)
intent: str
double_click Hace doble clic en la coordenada. y: int (0-999)
x: int (0-999)
intent: str
triple_click Hace clic tres veces en la coordenada. y: int (0-999)
x: int (0-999)
intent: str
middle_click Hace clic con el botón central en la coordenada. y: int (0-999)
x: int (0-999)
intent: str
right_click Hace clic con el botón derecho en la coordenada. y: int (0-999)
x: int (0-999)
intent: str
mouse_down Mantiene presionado el botón del mouse en la coordenada. y: int (0-999)
x: int (0-999)
intent: str
mouse_up Suelta el botón del mouse en la coordenada. y: int (0-999)
x: int (0-999)
intent: str
move Mueve el cursor a la posición especificada. y: int (0-999)
x: int (0-999)
intent: str
type Escribe texto. text: str
intent: str
press_enter: bool(Opcional, falso de forma predeterminada)
drag_and_drop Arrastra el elemento de start_y y start_x a end_y y end_x. start_y: int (0-999)
start_x: int (0-999)
end_y: int (0-999)
end_x: int (0-999)
intent: str
wait Espera la cantidad de segundos especificada. intent: str
seconds: int(opcional, el valor predeterminado es 1)
press_key Presiona la tecla especificada y la suelta. key: str
intent: str
key_down Mantiene presionada la tecla especificada. key: str
intent: str
key_up Suelta la tecla especificada. key: str
intent: str
hotkey Presiona la combinación de teclas especificada. keys: List[str]
intent: str
take_screenshot Muestra una captura de pantalla de la pantalla actual. intent: str
scroll Se desplaza hacia arriba, abajo, izquierda o derecha en una coordenada por una distancia de píxeles. y: int (0-999)
x: int (0-999)
direction: str ("up", "down", "left", "right")
intent: str
magnitude_in_pixels: int (0-999, opcional, valor predeterminado 300)

Gemini 3 Flash

Nombre del comando Descripción Argumentos (en la llamada a función) Ejemplo de llamada a función
open_web_browser Abre el navegador web. Ninguno {"name": "open_web_browser", "args": {}}
wait_5_seconds Pausa la ejecución durante 5 segundos para permitir que se cargue el contenido dinámico o que se completen las animaciones. Ninguno {"name": "wait_5_seconds", "args": {}}
go_back Navega a la página anterior en el historial del navegador. Ninguno {"name": "go_back", "args": {}}
go_forward Navega a la página siguiente del historial del navegador. Ninguno {"name": "go_forward", "args": {}}
search Navega a la página principal del motor de búsqueda predeterminado (por ejemplo, Google). Es útil para iniciar una nueva tarea de búsqueda. Ninguno {"name": "search", "args": {}}
navigate Navega el navegador directamente a la URL especificada. url: str {"name": "navigate", "args": {"url": "https://www.wikipedia.org"}}
click_at Hace clic en una coordenada específica de la página web. Los valores x e y se basan en una cuadrícula de 1,000 x 1,000 y se ajustan a las dimensiones de la pantalla. y: int (0-999), x: int (0-999) {"name": "click_at", "args": {"y": 300, "x": 500}}
hover_at 521963309 Coloca el mouse sobre una coordenada específica de la página web. Es útil para revelar submenús. x e y se basan en una cuadrícula de 1000 × 1000. y: int (0-999) x: int (0-999) {"name": "hover_at", "args": {"y": 150, "x": 250}}
type_text_at Escribe texto en una coordenada específica. De forma predeterminada, primero se borra el campo y se presiona INTRO después de escribir, pero estas acciones se pueden inhabilitar. x e y se basan en una cuadrícula de 1000 x 1000. y: int (0-999), x: int (0-999), text: str, press_enter: bool (opcional, valor predeterminado True), clear_before_typing: bool (opcional, valor predeterminado True) {"name": "type_text_at", "args": {"y": 250, "x": 400, "text": "search query", "press_enter": false}}
key_combination Presiona teclas o combinaciones de teclado, como "Control+C" o "Intro". Es útil para activar acciones (como enviar un formulario con "Intro") o realizar operaciones en el portapapeles. keys: str (por ejemplo, 'enter', 'control+c'). Consulta la referencia de la API para ver la lista completa de claves permitidas. {"name": "key_combination", "args": {"keys": "Control+A"}}
scroll_document Desplaza toda la página web "hacia arriba", "hacia abajo", "hacia la izquierda" o "hacia la derecha". direction: str ("up", "down", "left" o "right") {"name": "scroll_document", "args": {"direction": "down"}}
scroll_at Desplaza un elemento o un área específicos en la coordenada (x, y) en la dirección especificada según una magnitud determinada. Las coordenadas y la magnitud (800 de forma predeterminada) se basan en una cuadrícula de 1000 x 1000. y: int (0-999), x: int (0-999), direction: str ("up", "down", "left", "right"), magnitude: int (0-999, opcional, predeterminado 800) {"name": "scroll_at", "args": {"y": 500, "x": 500, "direction": "down", "magnitude": 400}}
drag_and_drop Arrastra un elemento desde una coordenada de inicio (x, y) y lo suelta en una coordenada de destino (destination_x, destination_y). Todas las coordenadas se basan en una cuadrícula de 1000 × 1000. y: int (0-999), x: int (0-999), destination_y: int (0-999), destination_x: int (0-999) {"name": "drag_and_drop", "args": {"y": 100, "x": 100, "destination_y": 500, "destination_x": 500}}

Seguridad y protección

En esta sección, se describen las protecciones que tiene la herramienta de uso de la computadora para mejorar el control y la seguridad de los usuarios. También se describen las prácticas recomendadas para mitigar los posibles riesgos nuevos que podría presentar la herramienta.

Reconoce la decisión de seguridad

Según la acción, la respuesta del modelo puede incluir un safety_decision de un sistema de seguridad interno. Esta decisión verifica la acción que propone la herramienta para garantizar la seguridad.

{
  "content": {
    "parts": [
      {
        "text": "I have evaluated step 2. It seems Google detected unusual traffic and is asking me to verify I'm not a robot. I need to click the 'I'm not a robot' checkbox located near the top left (y=98, x=95)."
      },
      {
        "function_call": {
          "name": "click_at",
          "args": {
            "x": 60,
            "y": 100,
            "safety_decision": {
              "explanation": "I have encountered a CAPTCHA challenge that requires interaction. I need you to complete the challenge by clicking the 'I'm not a robot' checkbox and any subsequent verification steps.",
              "decision": "require_confirmation"
            }
          }
        }
      }
    ]
  }
}

Si el safety_decision es require_confirmation, debes pedirle al usuario final que confirme antes de continuar con la ejecución de la acción.

En la siguiente muestra de código, se solicita la confirmación del usuario final antes de ejecutar la acción. Si el usuario no confirma la acción, el bucle finaliza. Si el usuario confirma la acción, esta se ejecuta y el campo safety_acknowledgement se marca como True.

import termcolor

def get_safety_confirmation(safety_decision):
    """Prompt user for confirmation when safety check is triggered."""
    termcolor.cprint("Safety service requires explicit confirmation!", color="red")
    print(safety_decision["explanation"])

    decision = ""
    while decision.lower() not in ("y", "n", "ye", "yes", "no"):
        decision = input("Do you wish to proceed? [Y]es/[N]o\n")

    if decision.lower() in ("n", "no"):
        return "TERMINATE"
    return "CONTINUE"

def execute_function_calls(response, page, screen_width: int, screen_height: int):

    # ... Extract function calls from response ...

    for function_call in function_calls:
        extra_fr_fields = {}

        # Check for safety decision
        if 'safety_decision' in function_call.args:
            decision = get_safety_confirmation(function_call.args['safety_decision'])
            if decision == "TERMINATE":
                print("Terminating agent loop")
                break
            extra_fr_fields["safety_acknowledgement"] = "true" # Safety acknowledgement

        # ... Execute function call and append to results ...

Si el usuario confirma, debes incluir el reconocimiento de seguridad en tu FunctionResponse.

function_response_parts.append(
    FunctionResponse(
        name=name,
        response={"url": current_url,
                  **extra_fr_fields},  # Include safety acknowledgement
        parts=[
            types.FunctionResponsePart(
                inline_data=types.FunctionResponseBlob(
                    mime_type="image/png", data=screenshot
                )
             )
           ]
         )
       )

Detección de inyección de instrucciones

El uso de la computadora en Gemini 3.5 Flash o versiones posteriores admite un mecanismo de seguridad avanzado para detectar ataques de inyección de instrucciones. Cuando está habilitada, esta función verifica si una captura de pantalla incluida tiene el potencial de introducir un ataque de inyección de instrucciones. Puedes establecer esta función en la configuración de uso de la computadora. El valor predeterminado es false.

En los siguientes ejemplos, se muestra cómo habilitar la configuración de uso de la computadora para habilitar la detección de inyección de instrucciones:

API de REST

Antes de usar cualquiera de los datos de solicitud a continuación, realiza los siguientes reemplazos:

  • PROJECT_ID: ID del proyecto

HTTP method and URL:

POST https://aiplatform.googleapis.com/v1/projects/PROJECT_ID/locations/global/publishers/google/models/gemini-3.5-flash:generateContent

Cuerpo JSON de la solicitud:

{
  "contents": [
    {
      "role": "user",
      "parts": {
        "text": "find me a flight from SF to Hawaii on Jun 30th, coming back on Jul 6th. start by navigating directly to flights.google.com"
      }
    }
  ],
  "generation_config": {
    "candidateCount": 1
  },
  "tools": [
    {
      "computer_use": {
        "environment": "ENVIRONMENT_BROWSER",
        "enable_prompt_injection_detection": true
      }
    }
  ]
}

Para enviar tu solicitud, expande una de estas opciones:

Deberías recibir un código de estado exitoso (2xx) y una respuesta vacía.

Python

En el siguiente ejemplo, se requiere la versión 2.7.0 o posterior de python-genai.

from google import genai
from google.genai.types import (
    Part,
    GenerateContentConfig,
    Content,
    Tool,
    ComputerUse,
    Environment,
    ThinkingConfig,
)
client = genai.Client()

response = client.models.generate_content(
    model="gemini-3.6-flash",
    contents=[
        Content(
            role="user",
            parts=[
                Part(text="find me a flight from SF to Hawaii on Jun 30th, coming back on Jul 6th"),
            ],
        )
    ],
    config=GenerateContentConfig(
        temperature=1,
        top_p=0.95,
        top_k=40,
        max_output_tokens=8192,
        tools=[
            Tool(
                computer_use=ComputerUse(
                    environment=Environment.ENVIRONMENT_MOBILE,
                    enable_prompt_injection_detection=True
                ),
            ),
        ],
        thinking_config=ThinkingConfig(
            include_thoughts=True
        ),
    )
)

Prácticas recomendadas de seguridad

El uso de computadoras presenta riesgos que los desarrolladores deben tener en cuenta:

  • Contenido no confiable y estafas: Mientras el modelo intenta alcanzar el objetivo del usuario, puede depender de fuentes de información e instrucciones no confiables de la pantalla.
  • Acciones ocasionales no deseadas: El modelo puede malinterpretar el objetivo de un usuario o el contenido de una página web, lo que hace que realice acciones incorrectas, como hacer clic en el botón equivocado o completar el formulario incorrecto.
  • Incumplimientos de políticas: Las funciones de la API podrían dirigirse a actividades que incumplen las condiciones y políticas de Google, como omitir CAPTCHA o comprometer la integridad del sistema.

Para abordar estos riesgos, debes implementar las siguientes medidas de seguridad y prácticas recomendadas:

  1. Con intervención humana (HITL):
    • Implementa la confirmación del usuario: Cuando la respuesta de seguridad indique require_confirmation, debes implementar la confirmación del usuario antes de la ejecución.
    • Proporciona instrucciones de seguridad personalizadas: Además de las verificaciones de confirmación del usuario integradas, los desarrolladores pueden agregar de forma opcional una instrucción del sistema personalizada que aplique sus propias políticas de seguridad, ya sea para bloquear ciertas acciones del modelo o requerir la confirmación del usuario antes de que el modelo realice ciertas acciones irreversibles de alto riesgo.

    Haz clic para ver un ejemplo de instrucción del sistema de seguridad personalizada

    ## **RULE 1: Seek User Confirmation (USER_CONFIRMATION)**
    
    This is your first and most important check. If the next required action falls
    into any of the following categories, you MUST stop immediately, and seek the
    user's explicit permission.
    
    **USER_CONFIRMATION Categories:**
    *   **Consent and Agreements:** Terms of Service, Privacy Policies, Cookie banners.
    *   **Robot Detection:** CAPTCHAs of any kind.
    *   **Financial Transactions:** Completing purchases or moving funds.
    *   **Sending Communications:** Emails, forum posts, or chat messages.
    *   **Accessing/Modifying Sensitive Information:** Healthcare, financial, or ID records.
    *   **User Data Management:** Downloading files, transferring user data.
    *   **Browser Data Usage:** Chrome history, bookmarks, autofill passwords.
    *   **Security and Identity:** Logging into accounts or impersonation.
    
    ## **RULE 2: Default Behavior (ACTUATE)**
    If an action does NOT fall under USER_CONFIRMATION, proactively perform all
    necessary steps to move the user's request forward.
        
  2. Entorno de ejecución seguro: Ejecuta tu agente en un entorno seguro y aislado para limitar su impacto potencial (por ejemplo, una máquina virtual (VM) aislada, un contenedor (como Docker) o un perfil de navegador dedicado con permisos limitados).
  3. Limpieza de entradas: Limpia todo el texto generado por el usuario en las instrucciones para mitigar el riesgo de instrucciones no deseadas o inyección de instrucciones. Esta es una capa de seguridad útil, pero no reemplaza un entorno de ejecución seguro.
  4. Protecciones de contenido: Usa protecciones y APIs de seguridad de contenido (como Model Armor) para evaluar las entradas de los usuarios, las entradas y salidas de las herramientas, y las respuestas del agente en busca de adecuación, inyección de instrucciones y detección de jailbreak.
  5. Listas de entidades permitidas y bloqueadas: Implementa mecanismos de filtrado para controlar por dónde puede navegar el modelo y qué puede hacer. Una lista de entidades bloqueadas de sitios web prohibidos es un buen punto de partida, mientras que una lista de entidades permitidas más restrictiva es aún más segura.
  6. Observabilidad y registro: Mantén registros detallados para la depuración, la auditoría y la respuesta ante incidentes. Tu cliente debe registrar las instrucciones, las capturas de pantalla, las acciones sugeridas por el modelo (function_call), las respuestas de seguridad y todas las acciones que finalmente ejecute el cliente.
  7. Administración del entorno: Asegúrate de que el entorno de la GUI sea coherente. Las ventanas emergentes inesperadas, las notificaciones del sistema o los cambios de diseño pueden confundir al modelo. Comienza desde un estado conocido y limpio para cada tarea nueva cuando sea posible.

Confirmaciones con intervención humana (HITL)

Las confirmaciones con intervención humana (HITL) son el proceso de solicitar la confirmación del usuario antes de ejecutar una acción sugerida por el modelo. Este proceso es necesario cuando el modelo no puede realizar una acción debido a problemas de seguridad.

La herramienta de uso de la computadora de Gemini proporciona los siguientes mecanismos para controlar las confirmaciones de HITL:

Anula las confirmaciones de HITL

Puedes anular las siguientes confirmaciones de HITL predeterminadas:

  • FINANCIAL_TRANSACTIONS: Es la finalización del último paso de una transacción financiera (como pagos, transferencias de fondos, emisión de una orden de pago, inicio o modificación de una suscripción, o finalización de una reserva).
  • SENSITIVE_DATA_MODIFICATION: Modificación de información sensible, como registros de atención médica, financieros o gubernamentales.
  • COMMUNICATION_TOOL: Acceso a herramientas de comunicación o uso de ellas para enviar, compartir, publicar o transmitir comunicaciones de cualquier otra forma
  • ACCOUNT_CREATION: Es la interacción con los flujos de registro o de acceso que crean o aprovisionan cuentas de servicio o de usuario nuevas.
  • DATA_MODIFICATION: Modificación, administración, uso compartido, almacenamiento, envío o eliminación de datos de navegación o archivos del navegador.
  • USER_CONSENT_MANAGEMENT: Es la interacción con los diálogos o banners de consentimiento del usuario para tomar una decisión.
  • LEGAL_TERMS_AND_AGREEMENTS: Interacción con diálogos de acuerdos legalmente vinculantes, como la aceptación de las condiciones del servicio, los diálogos de la política de privacidad o los avisos legales.

Puedes anular algunas o todas estas confirmaciones de HITL si las incluyes en el campo disabled_safety_policies de la configuración de uso de la computadora:

Python

API de GenerateContent

generate_content_config = genai.types.GenerateContentConfig(
    tools=[
        types.Tool(
            computer_use=types.ComputerUse(                
                environment=types.Environment.ENVIRONMENT_MOBILE,
                disabled_safety_policies=[
                    types.SafetyPolicy.FINANCIAL_TRANSACTIONS,
                    types.SafetyPolicy.COMMUNICATION_TOOL,
                ]
            )
        ),
    ],
)

contents = [
    types.Content(
        role="user",
        parts=[
            types.Part(text="Open the Maps app and find the nearest coffee shop."),
        ],
    )
]

response = client.models.generate_content(
    model='gemini-3.5-flash',
    contents=contents,
    config=generate_content_config,
)

API de Interactions

from google import genai
client = genai.Client()

interaction = client.interactions.create(
    model='gemini-3.5-flash',
    input='Open the Maps app and find the nearest coffee shop.',
    tools=[
        {
            'type': 'computer_use',
            'environment': 'mobile',
            'disabled_safety_policies': [
                'financial_transactions',
                'communication_tool',
            ],
        }
    ]
)

REST

API de GenerateContent

{
  "contents": [
    {
      "role": "user",
      "parts": [
        {
          "text": "Open the Maps app and find the nearest coffee shop."
        }
      ]
    }
  ],
  "tools": [
    {
      "computer_use": {
        "environment": "ENVIRONMENT_MOBILE",
        "disabled_safety_policies" : [
          "FINANCIAL_TRANSACTIONS",
          "COMMUNICATION_TOOL"
         ]
      }
    }
  ]
}

API de Interactions

{
  "model": "gemini-3.5-flash",
  "input": "Open the Maps app and find the nearest coffee shop.",
  "tools": [
    {
      "type": "computer_use",
      "environment": "mobile",
      "disabled_safety_policies": [
        "financial_transactions",
        "communication_tool"
      ]
    }
  ]
}

Crea políticas de la organización

Si eres administrador de la organización, puedes crear una política a nivel de la organización con el servicio de Política de la organización que impida que los desarrolladores individuales inhabiliten algunas o todas las confirmaciones de HITL. Esta política reemplaza cualquier parámetro de configuración a nivel del desarrollador.

Puedes crear una política con la consola deGoogle Cloud o con Google Cloud CLI. El ID de la restricción es vertexai.configurableComputerUseToolSafetyPolicies. Los cambios en la política de la organización pueden tardar hasta una hora en propagarse.

Para obtener todos los detalles sobre cómo crear y administrar políticas de la organización, consulta Crea políticas de la organización.

Precios

El uso de la computadora tiene el mismo precio que Gemini y usa los mismos SKU. Para dividir los costos de uso de procesamiento, usa etiquetas de metadatos personalizadas. Para obtener más información sobre el uso de etiquetas de metadatos personalizados para la supervisión de costos, consulta Etiquetas de metadatos personalizados.