AI

Crea un agente de investigación de cuentas en Salesforce Agentforce con Bright Data

La investigación de cuentas antes de una llamada con un cliente suele llevar entre diez y quince minutos por representante de ventas. Aprende a usar Bright
38 min de lectura
Build an account research agent in Salesforce Agentforce with Bright Data

La investigación de cuentas antes de una llamada con un cliente suele llevar entre diez y quince minutos por representante de ventas. El flujo de trabajo es mayormente manual: el representante sale de Salesforce, abre Google, revisa varias pestañas y pega los hallazgos en un campo de notas. La mayor parte del trabajo consiste en búsqueda y síntesis.

Web Unlocker de Bright Data devuelve Markdown limpio desde la mayoría de las URLs públicas. Conectarlo a Salesforce Agentforce permite que un representante obtenga información sobre una cuenta a partir de un mensaje de chat, con fuentes atribuidas, sin salir de Salesforce. Bajo el capó, la implementación consiste en un subagente de Agentforce, tres clases de Apex y un pequeño proxy de Cloudflare Worker.

TL;DR

  • Un subagente de Agentforce toma un mensaje en lenguaje natural de un representante de ventas, llama a Bright Data Web Unlocker y devuelve un resumen de cuenta con fuentes atribuidas, todo dentro del chat de Salesforce.
  • El cliente HTTP de Apex falla sin advertencia en respuestas de transferencia fragmentada (chunked-transfer) superiores a unos pocos kilobytes (verificado en API v66.0), por lo que la integración se enruta a través de un pequeño Cloudflare Worker que almacena en búfer y reenvía con un encabezado Content-Length explícito.
  • La interfaz Canvas de Agentforce oculta el indicador YAML is_user_input: True necesario para que los agentes impulsados por chat reciban entradas extraídas del mensaje. La solución está en el modo Script.
  • El patrón actual de External Credential de Salesforce se divide en tres objetos (External Credential, Named Credential, Permission Set), con una casilla de verificación fácil de pasar por alto que devuelve 401 si se omite.
  • Agentforce redacta las URLs externas de las respuestas del agente de forma predeterminada. El agente las lee internamente, pero no las muestra a menos que el dominio esté en la lista de permitidos de Trusted URLs.
  • El tamaño total es de aproximadamente 6 KB de Apex, un Cloudflare Worker, tres objetos de credenciales de Salesforce y un subagente. Cada bloque de código fue probado en una organización activa de Salesforce Developer Edition.

Antes de comenzar

Necesitarás cuatro cuentas y herramientas, todas gratuitas para este tutorial:

  • Una cuenta de Bright Data con al menos una zona de Web Unlocker aprovisionada. Las cuentas nuevas incluyen créditos de prueba gratuitos, y el volumen de solicitudes del tutorial cabe cómodamente dentro de esos créditos.
  • Una cuenta de Cloudflare para el proxy Worker. No se requiere tarjeta de crédito para el nivel gratuito; elegirás un subdominio workers.dev en el primer uso.
  • Una organización de Salesforce Developer Edition habilitada para Agentforce. Las organizaciones de Developer Edition recientes incluyen Agentforce, Data Cloud y Agentforce Studio prehabilitados. Verifica que la aplicación Agentforce Studio aparezca en tu App Launcher antes de continuar más allá de la Parte 5; si no aparece, tienes una organización sin Agentforce y las partes posteriores no funcionarán.
  • Una forma de enviar una solicitud HTTP de prueba. La Parte 2 incluye un comando curl para verificar el Worker. macOS, Linux y Windows 11 incluyen curl. Si prefieres usar una interfaz gráfica, Postman o Insomnia funcionan con los mismos encabezados y cuerpo. Si no tienes ninguno de estos y no quieres instalar uno, puedes omitir la prueba independiente del Worker y validar de extremo a extremo desde Salesforce en la Parte 3.
  • Perfil de Administrador del Sistema (o uno con Author Apex, Modify All Data y Customize Application). Una organización de Developer Edition nueva te otorga esto automáticamente. Si estás trabajando en un sandbox empresarial con un perfil restringido, cambia a una organización de Developer Edition nueva.

Qué construirás

Construirás un agente de Agentforce llamado Account Briefing Agent. Un representante escribe una pregunta en lenguaje natural. El agente enruta el mensaje a un subagente personalizado, selecciona las herramientas adecuadas, llama a Bright Data a través de un proxy delgado de Cloudflare Worker, sintetiza un resumen de cuenta con fuentes atribuidas y publica el resumen en el chat. La arquitectura tiene cinco componentes:

  • Bright Data Web Unlocker como primitiva de datos web. Es un único endpoint que acepta la mayoría de las URLs y devuelve Markdown limpio.
  • Cloudflare Worker como proxy entre Salesforce y Bright Data. El nivel gratuito cubre un equipo pequeño.
  • Salesforce External Credential + Named Credential + Permission Set como capa de autenticación.
  • Apex con tres clases: un servicio compartido, más dos wrappers que usan @InvocableMethod (la anotación que los hace invocables desde Agentforce, uno por Agent Action).
  • Subagente de Agentforce con dos acciones adjuntas, un bloque de instrucciones y una descripción de clasificación.

Descripción general de la arquitectura

Este es el flujo de solicitudes desde el mensaje de un representante hasta un resumen:

Rep prompt in Agentforce
        │
        ▼
Agent Router  ──►  Account Web Intelligence subagent
                          │
                          ├─► Apex: BrightDataNewsAction
                          │     └─► Named Credential → Cloudflare Worker → Web Unlocker → Google News
                          │
                          └─► Apex: BrightDataFetchAction
                                └─► Named Credential → Cloudflare Worker → Web Unlocker → target URL
        │
        ▼
LLM synthesis  ──►  Briefing back to the rep

El Cloudflare Worker existe porque Salesforce Apex no puede consumir de forma fiable respuestas HTTP/1.1 con transferencia fragmentada (chunked-transfer), que es lo que Bright Data devuelve para cualquier carga útil no trivial. El Worker almacena la respuesta completa en un único ArrayBuffer y la reenvía con un encabezado Content-Length explícito. Sin él, cada llamada desde Apex devuelve un estado 200 y un cuerpo de cero bytes. La Parte 2 a continuación explica el proceso de depuración y la solución.

Bright Data tiene varios productos que se adaptan a este tipo de implementación: SERP API para resultados de Google analizados, scrapers dedicados para LinkedIn y Crunchbase, entre otros. Esta implementación usa únicamente Web Unlocker porque funciona a través del mismo endpoint para cualquier URL, lo que mantiene el lado de Apex simple. El proxy de Cloudflare Worker de la Parte 2 cubre todos los endpoints de la API de Bright Data por igual, por lo que cambiar a SERP API o a un scraper dedicado más adelante no modifica el cableado del lado de Salesforce.

Parte 1: Configurar Bright Data

Si no tienes una cuenta de Bright Data, crea una en la página de registro de Bright Data. La zona de Web Unlocker que usarás se encuentra en la sección Web Access API del panel de control.

Crear o anotar la zona de Web Unlocker

Abre el panel de control, ve a Web Access API en la navegación izquierda y confirma que existe una zona de Web Unlocker. Si tu cuenta no tiene una, haz clic en Create API (arriba a la derecha) y elige Unlocker API en el menú desplegable. Dale cualquier nombre (los nombres de zona no se pueden cambiar después de la creación, así que elige algo estable como agentforce_unlocker). Sea cual sea el nombre que elijas, anótalo. Lo introducirás en la constante UNLOCKER_ZONE en BrightDataService.cls en la Parte 4, y en la prueba de curl de la Parte 2.

Panel de control de Bright Data mostrando la lista de zonas de Web Access API

La zona de Web Unlocker es la primitiva que usan ambas acciones de Agentforce.

Crear un token de API

Haz clic en Settings (abajo a la izquierda) → pestaña Users and API keysAdd API key con permiso User. La clave se muestra una sola vez al generarse y luego queda enmascarada. Cópiala ahora y guárdala en un lugar seguro; la pegarás en Salesforce en la Parte 3.

Configuración de cuenta de Bright Data mostrando la sección de claves de API con una clave en estado Activo, permiso de Usuario y expiración Ilimitada.

Eso es todo lo que se necesita configurar en Bright Data.

Parte 2: Desplegar el proxy de Cloudflare Worker

Antes de configurar Salesforce, necesitas un proxy frente a Bright Data. La razón es una limitación en cómo Salesforce Apex lee las respuestas de transferencia fragmentada; es probable que cualquier desarrollador de Apex que realice llamadas HTTP no triviales se encuentre con esto.

El error

El cliente Http de Salesforce Apex admite HTTP estándar, con una brecha práctica: no analiza de forma fiable las respuestas HTTP/1.1 que usan codificación de transferencia fragmentada (chunked transfer encoding) sin encabezado Content-Length. En una respuesta fragmentada, la llamada devuelve Status Code = 200, Content-Type = null, Response Size = 0 bytes, sin excepción ni advertencia. Tanto getBody() como getBodyAsBlob().toString() devuelven cadenas vacías.

El endpoint /request de Bright Data usa codificación de transferencia fragmentada para respuestas superiores a unos pocos kilobytes. Una llamada a Web Unlocker en una página de prueba pequeña (el welcome.txt de Bright Data) se mantiene por debajo del umbral y devuelve una respuesta con content-length, que Apex analiza correctamente. Pero una página real (la página de inicio de una empresa, una búsqueda en Google News) supera el umbral y se fragmenta, y Apex devuelve un cuerpo vacío.

Dos cosas demuestran que esto es del lado de Apex, no de la red: una llamada curl al mismo endpoint con la misma carga útil devuelve un cuerpo de 9 KB correctamente, y la misma llamada desde la ejecución anónima de Apex devuelve 0 bytes con Transfer-Encoding: chunked en los encabezados de respuesta.

La solución es estructural, no un cambio de configuración: colocar un proxy de almacenamiento en búfer entre Salesforce y Bright Data. El proxy lee completamente el flujo fragmentado de Bright Data y luego reenvía la respuesta a Salesforce con un encabezado Content-Length explícito. Apex analiza esa respuesta correctamente.

Un Cloudflare Worker es una buena opción para alojar este proxy. Es gratuito para bajo volumen, se despliega en minutos, se ejecuta en el edge y todo el cuerpo cabe en una pantalla de JavaScript.

Crear el Worker

Regístrate en el panel de control de Cloudflare si no tienes una cuenta. En el panel, busca Workers (aparece en ComputeWorkers & Pages según la versión de tu panel). Haz clic en Create application, luego elige Hello World de las plantillas. En el primer uso, Cloudflare te pedirá que elijas un subdominio workers.dev; elige cualquiera (es tu subdominio de desarrollo gratuito). Nombra el Worker con algo fácil de recordar; esta implementación usa bd-proxy. Después de que se despliegue el marcador de posición, haz clic en Edit code.

Selecciona todo el código de marcador de posición en el editor y pega esto en su lugar:

/**
 * Bright Data to Salesforce Apex proxy.
 *
 * Salesforce Apex does not reliably consume HTTP/1.1 chunked-transfer
 * responses, which is what Bright Data returns for any non-trivial payload.
 * This Worker buffers the full response and re-serves it with an explicit
 * Content-Length header. Apex parses that response cleanly.
 *
 * Production deployments typically route external API calls through an
 * integration layer like MuleSoft or Heroku. This Worker is the minimal
 * stand-in for that role.
 */

export default {
  async fetch(request) {
    const url = new URL(request.url);
    const bdUrl = 'https://api.brightdata.com' + url.pathname + url.search;

    // Strip Cloudflare-injected headers we shouldn't forward upstream.
    const forwardHeaders = new Headers(request.headers);
    forwardHeaders.delete('host');
    forwardHeaders.delete('cf-connecting-ip');
    forwardHeaders.delete('cf-ray');
    forwardHeaders.delete('cf-visitor');
    forwardHeaders.delete('x-forwarded-for');
    forwardHeaders.delete('x-forwarded-proto');
    forwardHeaders.delete('x-real-ip');

    try {
      const bdResponse = await fetch(bdUrl, {
        method: request.method,
        headers: forwardHeaders,
        body: ['GET', 'HEAD'].includes(request.method)
          ? undefined
          : await request.arrayBuffer(),
      });

      // Buffer the entire response into a single ArrayBuffer. This collapses
      // chunked transfer into a buffer of known length.
      const bodyBuffer = await bdResponse.arrayBuffer();

      const responseHeaders = new Headers();
      const ct = bdResponse.headers.get('Content-Type');
      if (ct) responseHeaders.set('Content-Type', ct);
      responseHeaders.set('Content-Length', bodyBuffer.byteLength.toString());
      const brdStatus = bdResponse.headers.get('x-brd-status-code');
      if (brdStatus) responseHeaders.set('X-Brd-Status-Code', brdStatus);

      return new Response(bodyBuffer, {
        status: bdResponse.status,
        headers: responseHeaders,
      });
    } catch (err) {
      return new Response(
        JSON.stringify({ error: 'Proxy error', message: err.message }),
        { status: 502, headers: { 'Content-Type': 'application/json' } }
      );
    }
  },
};

Las dos líneas que corrigen la integración son await bdResponse.arrayBuffer() (que lee todo el flujo fragmentado en memoria) y el encabezado Content-Length explícito establecido a partir de bodyBuffer.byteLength (Apex analiza el cuerpo correctamente a partir de eso). Todo lo demás gestiona el reenvío de encabezados: elimina los encabezados inyectados por Cloudflare en las solicitudes entrantes y preserva el código de estado upstream en las respuestas salientes.

Haz clic en Deploy (arriba a la derecha). Cloudflare te proporciona una URL como https://<worker-name>.<your-subdomain>.workers.dev. Cópiala; necesitarás esta URL para Salesforce en la Parte 3.

Editor de código de Cloudflare Worker mostrando el archivo worker.js de bd-proxy, con la lógica de almacenamiento en búfer (`const bodyBuffer = await bdResponse.arrayBuffer()`) y la reescritura del encabezado Content-Length visibles. El indicador de estado muestra que el Worker está Activo

Las líneas que importan: await bdResponse.arrayBuffer() lee todo el flujo fragmentado en memoria, y el encabezado Content-Length explícito en el objeto de respuesta significa que Apex puede analizar el cuerpo correctamente.

Verificar que el Worker funciona

Desde tu terminal local, ejecuta una prueba rápida contra el Worker. Reemplaza la URL con la tuya y usa tu propio token de API de Bright Data:

curl -i https://<your-worker>.workers.dev/request \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <your-bd-token>" \
  -d '{"zone":"mcp_unlocker","url":"https://www.salesforce.com","format":"raw","data_format":"markdown"}' \
  | head -20

Los encabezados de respuesta deben incluir un estado 200 y un encabezado content-length: <algún-número>. No deben incluir transfer-encoding: chunked. Esa es la prueba de que el proxy funciona correctamente. Estos son los fallos más comunes: 401 significa que tu token de Bright Data es incorrecto (vuelve a verificar el encabezado Authorization: Bearer ...); 502 del Worker significa que tu código del Worker no se desplegó (vuelve a verificar el paso Deploy); si el encabezado transfer-encoding: chunked sigue apareciendo, significa que omitiste las líneas arrayBuffer() + Content-Length en el código fuente del Worker.

En un despliegue empresarial, este Worker sería reemplazado por una capa de integración de nivel productivo: MuleSoft ejecutándose en Anypoint, un microservicio de Heroku o una API gateway personalizada con autenticación, observabilidad y limitación de velocidad. El Worker es un sustituto mínimo para ese rol, pero el mismo patrón funciona en esas configuraciones de producción.

Parte 3: Configurar las credenciales de Salesforce

El patrón de External Credential de Salesforce divide una credencial de terceros en tres objetos: un External Credential (almacena el token), un Named Credential (almacena el endpoint) y un Permission Set (otorga a los usuarios acceso al principal del External Credential).

Crear el External Credential

Haz clic en el icono de engranaje (arriba a la derecha de cualquier página de Salesforce) → Setup. En Setup, usa el cuadro de Quick Find en la parte superior del panel izquierdo y busca Named Credentials. Haz clic en el resultado. En la página que se carga, haz clic en la pestaña External Credentials y luego en New.

Completa los siguientes campos:

  • Label: Bright Data Cred
  • Name: Bright_Data_Cred (se completa automáticamente)
  • Authentication Protocol: Custom

Haz clic en Save.

En la página de detalles, busca la sección Principals y haz clic en New:

  • Parameter Name: BrightDataPrincipal
  • Sequence Number: 1
  • Identity Type: Named Principal

En la sección Authentication Parameters bajo el principal, agrega:

  • Name: api_key
  • Value: pega tu token de API de Bright Data

Haz clic en Save.

De vuelta en la página del External Credential, busca la sección Custom Headers y haz clic en New:

  • Name: Authorization
  • Value: Bearer {!$Credential.Bright_Data_Cred.api_key}
  • Sequence Number: 1

⚠️ El campo de combinación debe coincidir con los nombres que estableciste. Bright_Data_Cred en la fórmula debe coincidir con el nombre de API del External Credential. api_key debe coincidir con el nombre del parámetro de autenticación que estableciste en el Principal. Si cambiaste el nombre de alguno, edita la fórmula para que coincida.

Esto es fundamental: marca la casilla Allow Formulas in HTTP Header en este encabezado personalizado. Para encontrarla: después de guardar la fila del encabezado, haz clic en la fila para abrir su vista de detalles. La casilla está en esa página de detalles, no en la página principal del External Credential. Si la omites, Salesforce envía la cadena literal Bearer {!$Credential...} a Bright Data, que devuelve 401, y el mensaje de error no te indica qué casilla omitiste. Haz clic en Save.

⚠️ Encontrarás una casilla con el mismo nombre en la siguiente sección. “Allow Formulas in HTTP Header” aparece en dos lugares. La Casilla A es la que acabas de marcar (en la página de detalles del Custom Header). La Casilla B está en las Callout Options del Named Credential. Ambas deben estar marcadas. Si solo una está marcada, el campo de combinación se envía como texto literal y Bright Data devuelve 401.

Configuración de Salesforce mostrando la página de detalles del External Credential Bright_Data_Cred con el protocolo de autenticación establecido en Custom, un Principal llamado BrightDataPrincipal y un Custom Header con el valor 'Bearer {!$Credential.Bright_Data_Cred.api_key}' visible

El valor del campo de combinación del Custom Header es la parte que resuelve el token de API en el momento de la solicitud. La casilla Allow Formulas in HTTP Header (no visible a esta profundidad; está en la página de detalles del encabezado) debe estar marcada o el campo de combinación se enviará como texto literal.

Crear el Named Credential

En la misma sección de Named Credentials, vuelve a la pestaña Named Credentials y haz clic en New:

  • Label: Bright Data API
  • Name: Bright_Data_API
  • URL: pega la URL de tu Cloudflare Worker (por ejemplo https://bd-proxy.<your-subdomain>.workers.dev)
  • Enabled for Callouts: marcado
  • External Credential: selecciona Bright Data Cred

En Callout Options, establece lo siguiente:

  • Generate Authorization Header: desmarcado (estás proporcionando el tuyo propio a través del Custom Header)
  • Allow Formulas in HTTP Header: marcado (para que el campo de combinación se resuelva)
  • Allow Formulas in HTTP Body: marcado (para que funcionen los cuerpos JSON dinámicos)

Haz clic en Save.

Página de detalles del Named Credential 'Bright Data API' de Salesforce mostrando la URL apuntando al Cloudflare Worker, el External Credential establecido en Bright Data Cred, Generate Authorization Header desmarcado, Allow Formulas in HTTP Header marcado y Allow Formulas in HTTP Body marcado

La URL apunta al Cloudflare Worker, no directamente a api.brightdata.com. Así es como funciona la corrección de la transferencia fragmentada. Los tres estados de las casillas importan de forma independiente; tener cualquiera de ellas incorrecta rompe la llamada sin advertencia.

Crear el Permission Set

Salesforce bloquea incluso a los Administradores del Sistema el uso del principal de un External Credential hasta que un Permission Set otorgue acceso explícitamente. Si omites este paso, Apex devuelve un error INVALID_OPERATION sin diagnóstico útil.

En Setup, busca Permission Sets y haz clic en New:

  • Label: Bright Data Access
  • API Name: Bright_Data_Access
  • License: dejar en blanco

Haz clic en Save.

En la página de detalles, desplázate hasta External Credential Principal Access y haz clic en Edit. Mueve Bright_Data_Cred - BrightDataPrincipal de la lista Available a la lista Enabled. Haz clic en Save.

De vuelta en la página de detalles, haz clic en Manage Assignments en la parte superior, luego en Add Assignments, selecciona tu propio usuario y completa la asignación.

Permission Set 'Bright Data Access' de Salesforce mostrando la sección External Credential Principal Access con Bright_Data_Cred - BrightDataPrincipal concedido, y el botón Manage Assignments visible en la parte superior de la página

El Permission Set es el mecanismo de control que te permite definir qué usuarios pueden ejecutar código que llama a Bright Data. En una organización empresarial, esto se asignaría a través de un Permission Set Group a usuarios de servicio o perfiles específicos, no a administradores individuales.

Verificar el cableado antes de continuar

Haz clic en el icono de engranaje (arriba a la derecha) → Developer Console. Se abre en una nueva ventana del navegador. Una vez que esa ventana esté enfocada, abre Anonymous Apex mediante Debug → Open Execute Anonymous Window. Pega el código a continuación y haz clic en Execute:

HttpRequest req = new HttpRequest();
req.setEndpoint('callout:Bright_Data_API/request');
req.setMethod('POST');
req.setHeader('Content-Type', 'application/json');
req.setBody('{"zone":"mcp_unlocker","url":"https://geo.brdtest.com/welcome.txt?product=unlocker&method=api","format":"raw"}');
req.setTimeout(60000);
HttpResponse res = new Http().send(req);
System.debug('STATUS: ' + res.getStatusCode());
System.debug('BODY: ' + res.getBody().left(500));

Después de hacer clic en Execute, aparece una nueva fila de registro en el panel inferior de la Developer Console. Haz doble clic en esa fila para abrir el visor de registros y luego marca Debug Only en la parte inferior (o escribe USER_DEBUG en el cuadro de filtro). Deberías ver dos líneas que imprimen tus valores de STATUS y BODY. Busca STATUS: 200 y un cuerpo que contenga el texto de bienvenida de Bright Data. Si ves 401, vuelve a verificar la casilla “Allow Formulas in HTTP Header” del Custom Header (la que está en la página de detalles del encabezado, y la que está en las Callout Options del Named Credential). Si ves INVALID_OPERATION, vuelve a verificar la asignación del Permission Set.

Parte 4: Escribir la capa de Apex

Apex registra solo un @InvocableMethod por clase como acción invocable desde Agentforce. Por eso la integración usa tres clases en lugar de una: un servicio compartido para la infraestructura HTTP y una clase por Agent Action.

Pega cada bloque tal como está. La línea principal que podrías querer cambiar es private static final String UNLOCKER_ZONE = 'mcp_unlocker'; en BrightDataService.cls si tu zona de Bright Data tiene un nombre diferente (Parte 1).

En Setup, busca Apex Classes en Quick Find y haz clic en el resultado. Haz clic en New. El editor se abre con una clase de marcador de posición como public class YourClassName {}. Haz clic en el área de código (el cuadro de texto grande, no el panel de Version Settings a la derecha), selecciona todo el texto de marcador de posición, presiona Delete y luego pega el código fuente a continuación. El nombre de la clase se toma del código fuente, por lo que no necesitas completar ningún otro campo. Haz clic en Save.

Crea las tres clases en este orden, porque BrightDataNewsAction y BrightDataFetchAction hacen referencia a BrightDataService. El servicio debe guardarse primero:

BrightDataService.cls: la capa compartida de HTTP y análisis

Esta clase contiene la infraestructura HTTP y los dos métodos auxiliares (searchNews y fetchUrlAsMarkdown) que llaman ambas Agent Actions. No hay @InvocableMethod aquí; esa anotación está en las clases wrapper de acción a continuación. Esta es la clase:

public with sharing class BrightDataService {

    private static final String BD_ENDPOINT      = 'callout:Bright_Data_API/request';
    private static final String UNLOCKER_ZONE    = 'mcp_unlocker';
    private static final Integer CALLOUT_TIMEOUT = 60000;
    private static final Integer MAX_RESPONSE_CHARS = 50000;

    /**
     * Fetches the Google News results page for `companyName` (past month) as
     * clean Markdown via Bright Data Web Unlocker. The LLM downstream is
     * responsible for extracting individual articles, sources, and dates.
     */
    public static String searchNews(String companyName) {
        String googleNewsUrl =
            'https://www.google.com/search?q='
            + EncodingUtil.urlEncode(companyName, 'UTF-8')
            + '&tbm=nws&tbs=qdr:m';

        Map<String, Object> body = new Map<String, Object>{
            'zone'        => UNLOCKER_ZONE,
            'url'         => googleNewsUrl,
            'format'      => 'raw',
            'data_format' => 'markdown'
        };

        HttpResponse res = sendRequest(JSON.serialize(body));

        if (res.getStatusCode() != 200) {
            return 'Bright Data returned status '
                + res.getStatusCode() + ': ' + res.getBody().left(300);
        }

        String content = res.getBody();
        if (String.isBlank(content)) {
            return 'No content returned for "' + companyName
                + '". The page may have been empty or blocked.';
        }
        if (content.length() > MAX_RESPONSE_CHARS) {
            content = content.left(MAX_RESPONSE_CHARS)
                + '\n\n[Content truncated at ' + MAX_RESPONSE_CHARS + ' characters]';
        }

        return 'Google News results for "' + companyName
            + '" (past month). Extract article titles, sources, '
            + 'publication dates, and URLs from the Markdown below:\n\n'
            + content;
    }

    /**
     * Fetches any URL via Bright Data Web Unlocker and returns the page as
     * clean Markdown.
     */
    public static String fetchUrlAsMarkdown(String url) {
        Map<String, Object> body = new Map<String, Object>{
            'zone'        => UNLOCKER_ZONE,
            'url'         => url,
            'format'      => 'raw',
            'data_format' => 'markdown'
        };

        HttpResponse res = sendRequest(JSON.serialize(body));

        if (res.getStatusCode() != 200) {
            return 'Web Unlocker returned status '
                + res.getStatusCode() + ': ' + res.getBody().left(300);
        }

        String content = res.getBody();
        if (content.length() > MAX_RESPONSE_CHARS) {
            content = content.left(MAX_RESPONSE_CHARS)
                + '\n\n[Content truncated at ' + MAX_RESPONSE_CHARS + ' characters]';
        }
        return content;
    }

    private static HttpResponse sendRequest(String jsonBody) {
        HttpRequest req = new HttpRequest();
        req.setEndpoint(BD_ENDPOINT);
        req.setMethod('POST');
        req.setHeader('Content-Type', 'application/json');
        req.setBody(jsonBody);
        req.setTimeout(CALLOUT_TIMEOUT);
        return new Http().send(req);
    }
}

La clase no tiene @InvocableMethod, por diseño. Es la capa HTTP compartida que usan las dos clases de acción.

BrightDataNewsAction.cls: la acción de búsqueda de noticias

Agentforce llama a este wrapper invocable delgado cuando el agente decide buscar noticias. Valida la entrada, delega el trabajo HTTP a BrightDataService.searchNews() y devuelve el resultado en la forma de Response que espera Agentforce. Esta es la clase:

public with sharing class BrightDataNewsAction {

    public class Request {
        @InvocableVariable(
            required=true
            label='Company Name'
            description='The name of the company to search news about. E.g. "Salesforce" or "Acme Corp".')
        public String companyName;
    }

    public class Response {
        @InvocableVariable(
            label='News Results'
            description='Formatted summary of recent news with titles, sources, dates, URLs, and snippets.')
        public String newsResults;
    }

    @InvocableMethod(
        label='Search Recent Company News (Bright Data)'
        description='Searches Google News via Bright Data for recent (past month) articles about a specific named company. Use this whenever the user asks about recent news, announcements, press releases, funding rounds, acquisitions, leadership changes, or current events for a named company.'
        callout=true)
    public static List<Response> searchCompanyNews(List<Request> requests) {
        List<Response> responses = new List<Response>();
        for (Request req : requests) {
            Response resp = new Response();
            try {
                resp.newsResults = String.isBlank(req.companyName)
                    ? 'Error: A company name is required.'
                    : BrightDataService.searchNews(req.companyName);
            } catch (Exception e) {
                resp.newsResults = 'Error fetching news for ' + req.companyName + ': ' + e.getMessage();
            }
            responses.add(resp);
        }
        return responses;
    }
}

El motor de razonamiento de Agentforce lee el campo description de la anotación @InvocableMethod para decidir cuándo llamar a esta acción.

BrightDataFetchAction.cls: la acción de obtención de URL

El segundo wrapper invocable sigue el mismo patrón que la acción de noticias, pero obtiene cualquier URL que mencione el representante. El bloque de validación también rechaza entradas malformadas antes de la llamada. Esta es la clase:

public with sharing class BrightDataFetchAction {

    public class Request {
        @InvocableVariable(
            required=true
            label='URL to Fetch'
            description='The full URL of a web page to retrieve. Must start with http:// or https://.')
        public String url;
    }

    public class Response {
        @InvocableVariable(
            label='Page Content'
            description='Clean Markdown representation of the page content.')
        public String pageContent;
    }

    @InvocableMethod(
        label='Fetch Web Page as Markdown (Bright Data)'
        description='Retrieves the content of any web URL via Bright Data Web Unlocker and returns it as clean Markdown. Use this when you need to read a specific URL: a company homepage, blog post, press release, or any link the user mentions.'
        callout=true)
    public static List<Response> fetchUrlAsMarkdown(List<Request> requests) {
        List<Response> responses = new List<Response>();
        for (Request req : requests) {
            Response resp = new Response();
            try {
                if (String.isBlank(req.url)
                    || (!req.url.startsWithIgnoreCase('http://')
                        && !req.url.startsWithIgnoreCase('https://'))) {
                    resp.pageContent = 'Error: A valid URL starting with http:// or https:// is required.';
                } else {
                    resp.pageContent = BrightDataService.fetchUrlAsMarkdown(req.url);
                }
            } catch (Exception e) {
                resp.pageContent = 'Error fetching ' + req.url + ': ' + e.getMessage();
            }
            responses.add(resp);
        }
        return responses;
    }
}

Después de guardar las tres, Setup → Apex Classes debería mostrarlas como Active.

Lista de Apex Classes de Salesforce filtrada por 'Bright', mostrando BrightDataFetchAction (1.820 caracteres, Active), BrightDataNewsAction (1.787 caracteres, Active) y BrightDataService (2.820 caracteres, Active), todas en API Version 66.0

El tamaño total de Apex es de aproximadamente 6,4 KB en tres clases. El servicio compartido más una clase de acción por Agent Action es el patrón estándar de Salesforce cuando se necesita más de un invocable.

Probar las acciones

Antes de conectar las acciones a Agentforce, confirma que funcionan de extremo a extremo. En Anonymous Apex:

BrightDataNewsAction.Request r = new BrightDataNewsAction.Request();
r.companyName = 'Salesforce';
List<BrightDataNewsAction.Response> out =
    BrightDataNewsAction.searchCompanyNews(new List<BrightDataNewsAction.Request>{ r });
System.debug('LENGTH: ' + out[0].newsResults.length());
System.debug('PREVIEW: ' + out[0].newsResults.left(800));

Se espera que LENGTH esté entre 5.000 y 10.000, con una vista previa que comience con el prefijo de la clase de servicio y luego el Markdown de Google News. Si ves LENGTH: 0 o una cadena de error, vuelve al paso de verificación de la Parte 3.

Parte 5: Registrar las acciones como Agentforce Assets

Las clases de Apex no son visibles para Agentforce de forma predeterminada. Cada @InvocableMethod debe registrarse como un Agent Action (etiqueta más reciente: Agentforce Asset) antes de que el agente pueda llamarlo.

En Setup, busca Agent Actions (o Agentforce Assets, según la etiqueta que tenga tu organización) y haz clic en New Agent Action. Harás esto dos veces, una por cada clase de acción.

Para la acción de noticias, completa:

  • Reference Action Type: Apex
  • Reference Action Category: Invocable Methods
  • Reference Action: BrightDataNewsAction.searchCompanyNews

En la siguiente pantalla, completa estos campos:

  • Agent Action Label: mantén el Search Recent Company News (Bright Data) que se completa automáticamente
  • Agent Action Description: mantén la descripción que se completa automáticamente (proviene de la anotación @InvocableMethod)
  • Show loading text for this action: marcado
  • Loading Text: Searching recent news…

Salesforce detecta automáticamente la entrada (companyName, requerida, String) y la salida (newsResults, String). Deja el mapeo detectado automáticamente. Haz clic en Finish.

Repite para la acción de obtención:

  • Reference Action: BrightDataFetchAction.fetchUrlAsMarkdown
  • Loading Text: Fetching web page…
  • La entrada es url (requerida, String). La salida es pageContent (String).

Después de guardar ambas, la lista de Agent Actions muestra las dos con estado Active.

Biblioteca de Agentforce Assets de Salesforce mostrando la pestaña Actions con dos acciones personalizadas de Bright Data en la parte superior de la lista (Fetch Web Page as Markdown y Search Recent Company News, ambas con Source: Custom, Reference Type: Apex), intercaladas con acciones estándar de la plantilla Employee

Las acciones personalizadas aparecen en la misma Asset Library que las estándar que se incluyen con la plantilla Employee. El motor de razonamiento de Agentforce trata ambas por igual; la columna de origen es metadatos, no un interruptor de comportamiento.

La división en dos capas (clase Apex vs. Agent Action) es intencional. Es un punto de control de gobernanza que permite a un administrador de Salesforce otorgar o revocar la capacidad de un agente sin modificar Apex. En una organización regulada, por ejemplo, el Apex permanece sin cambios entre versiones; el registro del Agent Action es lo que se audita y controla por versiones.

Parte 6: Construir el agente

Con ambas acciones registradas, puedes conectarlas en un agente de Agentforce funcional. Abre el App Launcher (la cuadrícula de nueve puntos, arriba a la izquierda) y busca Agentforce Studio. Dentro de Agentforce Studio, haz clic en New Agent.

La

Agentforce Builder mostrando la sección de Acciones Disponibles para Razonamiento del subagente Account Web Intelligence con ambas acciones de Bright Data adjuntas: Search Recent Company News (Bright Data) con `With input: Company Name = Agent Populated` y Fetch Web Page as Markdown (Bright Data) con `With input: URL to Fetch = Agent Populated`

“Agent Populated” es la etiqueta que usa Salesforce cuando una entrada es completada por el razonamiento del LLM en tiempo de ejecución. Este es el estado que necesita la integración para los agentes impulsados por chat.

Parte 7: Probar el agente

En el Agent Builder, haz clic en la pestaña Preview. La interfaz de chat se abre con un banner amarillo que incluye un botón Reset Simulator. Si aparece el banner, haz clic en él. El reinicio es importante porque la memoria de conversación del simulador es por sesión, y reiniciar entre pruebas es la forma más sencilla de obtener trazas independientes. Las cuatro pruebas a continuación verifican el enrutamiento, las llamadas de acción única, las llamadas paralelas y el modo de fallo honesto.

Si la pestaña Preview aparece en gris, busca un interruptor Activate en la parte superior del lienzo y actívalo. Activarlo aquí solo habilita el agente para el Preview/Simulator; los usuarios finales no pueden acceder a él hasta que también lo asignes a través de Setup → Agentforce Studio → Connections, lo cual está fuera del alcance de esta construcción.

Prueba 1: Solo búsqueda de noticias

Para verificar que la acción de noticias se ejecuta de principio a fin, escribe:

What's new at Salesforce in the past month?

El agente debería enrutar a Account Web Intelligence, mostrar un estado de carga “Searching recent news…” y luego devolver un resumen de noticias temático con los nombres de las fuentes citados en línea. Abre la pestaña Trace en la parte inferior del lienzo para ver la cadena de razonamiento completa. El panel Interaction Summary a la derecha de la vista previa muestra la misma cadena en forma compacta: qué subagente eligió el Router, qué acciones se invocaron y cómo razonó el agente.

Vista previa de Agentforce mostrando el prompt '¿Qué hay de nuevo en Salesforce en el último mes?' respondido con un resumen de noticias temático agrupado bajo 'AI & Product Expansion' y 'Strategic Partnerships' con fuentes citadas (Forbes, UC Today, PR Newswire). El Interaction Summary a la derecha muestra Input → Transition to Subagent: Account Web Intelligence → Reasoning → Action: Search Recent Company News Bright Data → Reasoning. Las URL de origen en la respuesta aparecen como URL_Redacted

Esta es una ejecución limpia de acción única. El Router enrutó correctamente, el agente extrajo companyName="Salesforce" del prompt en lenguaje natural, llamó a la acción y sintetizó un resumen temático. Los marcadores de posición URL_Redacted son la política de confianza de URL de Salesforce en acción (explicada en la sección “Gobernanza empresarial” más adelante).

El enrutamiento está impulsado por LLM, por lo que en raras ocasiones la descripción del subagente Account Web Intelligence puede perder frente a una similar en la plantilla (p. ej., General FAQ). Si tu primera prueba enruta al subagente incorrecto, añade palabras clave más específicas a la descripción (“company news”, “press release”, “recent funding”, “fetch URL”), guarda, haz clic en Reset Simulator y vuelve a intentarlo. El router reclasifica en cada sesión nueva.

Prueba 2: Solo obtención de URL

Para verificar que un prompt con URL directa enruta a la acción de obtención, haz clic en Reset Simulator y escribe:

Read https://www.snowflake.com and tell me what they do

El agente debería llamar a la acción Fetch, no a la acción de noticias. Un prompt diferente enruta a una herramienta diferente.

Vista previa de Agentforce mostrando el prompt 'Read https://www.snowflake.com and tell me what they do' respondido con un resumen de dos oraciones sobre Snowflake como AI Data Cloud, seguido de una aclaración honesta de que el contenido recuperado fue limitado. El Interaction Summary a la derecha muestra solo una acción: Fetch Web Page as Markdown Bright Data. La URL en la respuesta aparece como URL_Redacted

Es el mismo agente, pero un prompt diferente enruta a una herramienta diferente. El Agent Router clasificó el prompt como una obtención de URL (no una búsqueda de noticias) y llamó solo a la acción de obtención. Esto es evidencia de que el agente está razonando, no ejecutando un pipeline fijo.

Prueba 3: Ambas acciones en paralelo

La prueba 3 tiene éxito cuando se invocan ambas acciones, independientemente de si ambas devuelven 200. Si ambas tienen éxito, verás un informe combinado limpio. Si una falla (como en la captura de pantalla a continuación), el agente debería producir igualmente un informe usando la que tuvo éxito. Esto demuestra la regla 5. Después de Reset Simulator, escribe:

Give me a briefing on Anthropic, recent news plus what their homepage says about their product

El agente debería llamar a ambas acciones y luego sintetizar un informe combinado. La traza muestra dos filas de Acción separadas en la misma cadena de razonamiento.

Parte superior de la vista previa de Agentforce mostrando un informe sobre Anthropic con cinco noticias temáticas que incluyen SAP Partnership, Claude for Small Business, Claude Design y lanzamientos de Financial Services. Todas las fuentes citadas (SAP News Center, Anthropic, CNBC). El Interaction Summary a la derecha muestra ambas acciones invocadas: Search Recent Company News Bright Data Y Fetch Web Page as Markdown Bright Data. Las URL de origen en la respuesta aparecen como URL_Redacted

El agente llamó a ambas acciones desde un único prompt de usuario. La sección de noticias de Anthropic proviene de Web Unlocker de Bright Data llamando a Google News.

La misma respuesta continúa a continuación con la sección de la página de inicio, donde esta ejecución encontró un fallo parcial que vale la pena ver en detalle:

Parte inferior de la vista previa de Agentforce de la misma respuesta mostrando la sección 'What Their Homepage Says'. El agente informa honestamente un problema técnico al recuperar el contenido de la página de inicio y recurre a caracterizar el posicionamiento de Anthropic basándose en las noticias que acaba de recuperar. El Interaction Summary indica explícitamente 'Fetch_Web_Page_as_Markdown_Bright_Data for https://www.anthropic.com (which returned a 520 error page)' y que el agente 'produced a summarized briefing with redacted source URLs'

La obtención de la página de inicio devolvió un 520, el estado que usa Web Unlocker cuando un sitio de destino no puede recuperarse en un intento determinado. El agente no inventó contenido de la página de inicio; reconoció el fallo, usó los datos de noticias que acababa de recibir para describir lo que hace la empresa y ofreció reintentar.

La degradación elegante ante un fallo parcial de herramienta es el comportamiento en producción que la regla 5 de las instrucciones del subagente está diseñada para producir. Esto importa porque la web pública es adversarial: los sitios de destino cambian sus defensas, las CDN fallan ocasionalmente, y cualquier agente que llame a URL en vivo tiene que tolerar el ocasional no-200. Cuando un agente dice “la obtención de la página de inicio falló, esto es lo que tengo de las noticias”, ese es un comportamiento de nivel de producción.

Prueba 4: Verificación de no alucinación

Para verificar que el agente falla honestamente cuando no hay una respuesta real, haz clic en Reset Simulator y escribe:

What's the latest news about Triposat Industries?

Este es un nombre de empresa falso, por lo que no hay noticias reales que encontrar. Bright Data puede devolver un 520, un resultado vacío o fragmentos irrelevantes, ninguno de los cuales es una respuesta real. El agente no debe inventar noticias, independientemente de lo que devuelva. El criterio de éxito aquí es el comportamiento, no el código de estado: las respuestas aceptables incluyen “no se encontraron noticias recientes”, “no pude recuperar resultados” o cualquier divulgación honesta. Un fallo aquí sería que el agente inventara un artículo de Forbes o una ronda de financiación que no existe.

Vista previa de Agentforce mostrando el prompt '¿Cuáles son las últimas noticias sobre Triposat Industries?' respondido con una divulgación honesta: 'Intenté recuperar las últimas noticias sobre Triposat Industries, pero hubo un problema al acceder a la fuente de noticias y no pude obtener actualizaciones recientes en este momento.' El agente ofrece reintentar o cambiar a un aspecto específico. El Interaction Summary confirma que se invocó la acción de búsqueda y el resumen en lenguaje natural indica explícitamente 'received an error-like result from the provider (status 520 with partial HTML)'

El agente no fabricó ninguna noticia ni inventó ninguna fuente. Invocó la acción, recibió un resultado deficiente y lo divulgó. La regla 5 de las instrucciones del subagente funcionó según lo previsto.

Redacción de URL en Agentforce

Cada respuesta de demostración en las capturas de pantalla anteriores muestra las URL de origen como URL_Redacted. Esta es la política de confianza de URL integrada de Salesforce Agentforce.

De forma predeterminada, Agentforce elimina las URL externas arbitrarias de las respuestas del agente antes de que lleguen al usuario final. Sin embargo, internamente, el agente sigue leyendo las URL reales cuando la herramienta devuelve resultados y las usa para el razonamiento; simplemente no puede incluirlas en la salida del chat a menos que el dominio esté en una lista de permitidos explícita.

Es configurable: en Setup, busca Trusted URLs y añade los dominios que deseas permitir. Para un agente de informes de ventas, una lista de permitidos realista incluye dominios de Google, los propios dominios de marketing de la empresa y un conjunto seleccionado de fuentes de noticias (Forbes, Reuters, Bloomberg, TechCrunch).

Mantén la redacción predeterminada habilitada. Hace la demostración más sólida: el agente cita fuentes por nombre (Forbes, TechAfrica News, SAP News Center), y la redacción hace visible la capa de gobernanza de Salesforce. Una demostración con URL sin procesar es indistinguible de un chatbot de consumo; la versión redactada hace visible la capa de gobernanza.

Cuánto cuesta

Bright Data cobra solo por las respuestas exitosas de Web Unlocker en sus planes de pago por uso y escalonados; las respuestas no exitosas no se facturan. Al precio de lista, un informe que llama a ambas acciones se sitúa en el rango de fracción de centavo; los planes escalonados reducen aún más el costo por solicitud.

Para proyectar tu propio gasto, la fórmula es:

requests/month = reps × briefings_per_rep_per_day × workdays × actions_per_briefing

Para un equipo de 100 representantes que ejecutan 5 informes cada uno en 22 días laborables con 2 acciones por informe, eso son 22.000 solicitudes al mes. Multiplica eso por la tarifa actual por solicitud de Bright Data (de la página de precios) para obtener tu costo mensual.

En el lado de Cloudflare, el Worker permanece dentro de su nivel gratuito en volúmenes típicos de equipos de ventas; consulta los precios de Workers antes de escalar más allá de un solo equipo.

En el lado de Salesforce, los costos se deducen de la asignación de créditos de IA generativa Einstein existente de tu organización, el mismo grupo medido que usa cualquier acción de Agentforce. Consulta Setup → Einstein Generative AI → Usage para ver la asignación y el uso actuales de tu organización.

Próximos pasos

La construcción anterior es una unidad mínima de una capa de inteligencia de cuentas. Aquí hay tres cosas que hacer antes de que esto pase a producción:

  • Mueve el Cloudflare Worker detrás de un dominio personalizado (tu propio subdominio en tu propio DNS) y luego enrútalo a través del API gateway de tu organización. El Worker encaja en este rol de proxy; un dominio personalizado más gateway es lo que lo hace operativamente tuyo.
  • Bloquea la zona de Bright Data a tu rango de IP de salida. En el panel de Bright Data, edita la zona de Web Unlocker y añade las IP salientes del Cloudflare Worker (Cloudflare las publica) a la lista de permitidos de la zona. Esto evita que el token de API sea utilizable fuera de tu integración.
  • Añade una clase de prueba Apex para BrightDataService usando HttpCalloutMock. Tres métodos de prueba (ruta de éxito, cuerpo vacío, no-200) cubren los modos de fallo realistas y cumplen el requisito de cobertura del 75% de Salesforce. Los documentos de Salesforce HttpCalloutMock muestran el patrón.

Cuando superes Web Unlocker para un sitio de destino, cambia a uno de los scrapers prediseñados de Bright Data. El mismo proxy Cloudflare Worker los maneja también; usan el mismo endpoint /request con diferentes parámetros zone y dataset_id. Por ejemplo, los scrapers dedicados de LinkedIn Company Profile, LinkedIn Jobs y Crunchbase devuelven JSON estructurado en lugar de Markdown, lo que permite al agente omitir el paso de extracción del LLM y escribir directamente en campos personalizados de Salesforce. En términos más amplios, las Web Scraper APIs de Bright Data cubren scrapers prediseñados para cientos de sitios.

Trata la construcción anterior como andamiaje. La capa de credenciales, el proxy, la estructura del subagente, el cableado de acciones: la base permanece igual cuando cambias a un producto diferente de Bright Data en el mismo endpoint /request. Elige el tipo de inteligencia de cuentas que tus representantes realmente necesitan; cambia los prompts en las instrucciones del subagente. El agente permanece; las preguntas que responde cambian.

Preguntas frecuentes

¿Por qué mi callout de Apex devuelve un cuerpo vacío?

El cliente HTTP de Apex no analiza de forma fiable las respuestas HTTP/1.1 que usan codificación de transferencia fragmentada sin un encabezado Content-Length. Bright Data y muchas API modernas fragmentan las respuestas por encima de unos pocos kilobytes. La solución es enrutar la llamada a través de un proxy de almacenamiento en búfer que reenvíe la respuesta con un Content-Length explícito.

¿Puede esta construcción usar el SERP API de Bright Data?

Sí. El proxy Cloudflare Worker funciona para todos los endpoints de API de Bright Data alojados en api.brightdata.com, incluidos SERP API, Web Scraper API y disparadores de conjuntos de datos. Cambia el valor de zone en la clase de servicio Apex al nombre de la zona SERP y el parámetro URL a una URL de búsqueda de Google con brd_json=1.

¿Por qué usar Apex InvocableMethods en lugar de MCP?

Esta construcción expone Bright Data a través de Apex InvocableMethods porque cada acción se convierte en una Agent Action auditable con su propia gobernanza de Permission Set. Si tu organización tiene servidores MCP (Model Context Protocol) alojados en Salesforce habilitados (Beta a partir de Spring ’26), también puedes exponer Bright Data a través del propio servidor MCP de Bright Data. Ambos caminos funcionan. El camino InvocableMethod mostrado aquí te da ganchos de gobernanza nativos de Salesforce; el camino MCP es más ligero porque Bright Data opera el servidor por ti.

¿Cómo leen las acciones de Agentforce la entrada del usuario?

Las acciones de Agentforce leen la entrada del usuario a través de un indicador YAML llamado is_user_input: True, establecido en cada entrada en el modo Script. La interfaz Canvas oculta este indicador y establece las entradas como variables estáticas de forma predeterminada, por lo que cambias al modo Script en el Agent Builder y editas el YAML directamente para cambiar el valor.

¿Por qué Agentforce oculta las URL de origen?

Las URL externas se eliminan de las respuestas del agente a menos que su dominio se añada a Setup → Trusted URLs. Es una capa de gobernanza integrada. Aun así, el agente sigue leyendo las URL reales internamente y las usa en el razonamiento. Para permitir que URL específicas lleguen a los usuarios, añade los dominios de origen (Forbes, Reuters, tus propios sitios) a la lista de permitidos.

¿Qué es un error 520 de Bright Data?

Un 520 es el estado que devuelve Web Unlocker cuando un sitio de destino no pudo recuperarse en un intento determinado, generalmente porque las defensas del sitio bloquearon la solicitud. La regla 5 de las instrucciones del subagente prohíbe fabricar contenido cuando una herramienta falla, por lo que el agente informa el fallo honestamente y ofrece reintentar.

¿Qué otros casos de uso cubre este patrón?

Los informes de riesgo de abandono, inteligencia competitiva y riesgo de renovación encajan todos en este patrón. Esto se debe a que las dos acciones Apex (buscar noticias, obtener URL) cubren la mayoría de los tipos de inteligencia de cuentas. Para el riesgo de abandono, usa la búsqueda de noticias para rastrear despidos y cambios de liderazgo. Para la inteligencia competitiva, obtén las páginas de precios del competidor. Solo cambian los prompts.