<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title><![CDATA[AI agéntica - Elasticsearch Labs]]></title>
    <description><![CDATA[Articles and tutorials from the Search team at Elastic]]></description>
    <copyright><![CDATA[© 2026. Elasticsearch B.V. All Rights Reserved]]></copyright>
    <image>
      <title><![CDATA[AI agéntica - Elasticsearch Labs]]></title>
      <url>https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1121c0bf0e8a6e65/6a88da6340a1841030ef456f/search-labs-thumbnail.png</url>
      <link>https://www.elastic.co/es/search-labs/blog/category/agentic-ai</link>
    </image>
    <link>https://www.elastic.co/es/search-labs/blog/category/agentic-ai</link>
    <atom:link href="https://www.elastic.co/es/search-labs/rss/category/agentic-ai.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[es]]></language>
    <lastBuildDate>Mon, 28 Sep 2026 22:11:08 GMT</lastBuildDate>
  <item>
    <title><![CDATA[137 000 personas, cero decisiones humanas: respuesta agéntica ante desastres con Elasticsearch]]></title>
    <description><![CDATA[Descubre cómo una regla de detección de Kibana, un flujo de trabajo y un agente de AI reubicaron automáticamente a 137 000 miembros del personal militar en siete instalaciones cuando azotó un huracán, sin necesidad de despachadores.]]></description>
    <content:encoded><![CDATA[<p>Elastic acaba de coordinar la evacuación automatizada de 137 000 militares en siete instalaciones, sin intervención humana. Un huracán de categoría 4 azota la costa de Hampton Roads. El enriquecimiento geoespacial de Elasticsearch identifica cada instalación en la zona de impacto al momento de la indexación. Se activa una regla de detección de Kibana. Un flujo de trabajo inicia una conversación con un agente de AI. El agente analiza la capacidad, la distancia y la compatibilidad entre ramas de las fuerzas armadas, y luego envía 16 notificaciones de evacuación y recepción en una sola pasada. De un evento de GDACS sin procesar a una acción coordinada, automáticamente.</p><p>Cada año, los desastres naturales obligan a los gestores de emergencias, comandantes militares y oficiales de seguridad pública a tomar decisiones críticas en plazos ajustados. Estas decisiones suelen depender de cadenas de llamadas, hojas de cálculo y conocimiento institucional distribuido entre decenas de personas. La coordinación por sí sola consume un tiempo valioso.</p><p>Esta publicación demuestra cómo Elastic puede potenciar un sistema de coordinación agéntico con capacidad de respuesta ante desastres, que detecta amenazas, analiza la logística y actúa automáticamente. Para hacerlo concreto, creamos una simulación: un huracán ficticio de categoría 4 que amenaza la costa de Hampton Roads desencadena la reubicación automatizada de más de 137 000 personas en siete instalaciones militares.</p><p><strong>Descargo de responsabilidad:</strong> <strong>Este es un escenario totalmente ficticio creado con fines de demostración. </strong>El huracán ELARA-26 no existe. Las ubicaciones de las instalaciones se basan en datos geográficos reales y disponibles públicamente (el set de datos Military Installations, Ranges, and Training Areas [MIRTA] del Departamento de Defensa de los EE. UU. [DoD]), pero todos los datos operativos, como el recuento de personal, la capacidad de alojamiento, los activos, los correos electrónicos de contacto y los perfiles de misión, son completamente ficticios. Nada en esta demostración refleja la preparación, la capacidad o los procedimientos operativos militares reales.</p><h2>Por qué la respuesta automatizada ante desastres requiere coordinación geoespacial y agéntica</h2><p>Cuando un desastre natural amenaza la infraestructura crítica, el desafío de coordinación es inmediato:</p><ul><li><p>¿Qué instalaciones están en la zona de impacto?</p></li><li><p>¿Cuánto personal necesita trasladarse?</p></li><li><p>¿A dónde pueden ir? ¿Esas instalaciones tienen capacidad?</p></li><li><p>¿A quién se necesita notificar en este momento?</p></li></ul><p>Estas preguntas no esperan. Tampoco deberían hacerlo las respuestas.</p><h2>Despliega el pipeline: requisitos previos y configuración</h2><p>Sigue las instrucciones <a href="https://github.com/tehbooom/elastic_natural_disaster/blob/main/README.md">aquí en el repositorio de ejemplo</a> para desplegar un cluster local de Elastic con Elastic Inference Service (EIS) mediante <a href="https://www.elastic.co/docs/explore-analyze/elastic-inference/connect-self-managed-cluster-to-eis#set-up-eis-with-cloud-connect">Cloud Connect</a>.</p><h2>Cómo funciona el pipeline de respuesta ante desastres con agentes de Elasticsearch</h2><p>El pipeline tiene siete capas que trabajan juntas de extremo a extremo:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf09bfae87ab35bec/6a4693ef31bdbbe3ef8b33ae/61814cddea0409162fb057c2113e0a496c105238-1999x275.png" alt="Pipeline flowchart Alt text: Horizontal flowchart with seven labeled boxes connected by arrows: GDACS feed, ingest pipeline, enrich (geo_shape), detection rule, workflow, AI agent, and email." /><ol><li><p><strong>Ingesta de datos:</strong> eventos de desastres del Sistema Mundial de Alerta y Coordinación de Desastres (GDACS) enviados a Elasticsearch</p></li><li><p>P<strong>ipeline de ingesta</strong>: GeoJSON se ingesta y normaliza a Elastic Common Schema (ECS).</p></li><li><p><strong>Enriquecimiento geoespacial:</strong> el polígono del área afectada del evento se compara con los límites indexados de las instalaciones militares.</p></li><li><p><strong>Alerting:</strong> una regla de detección de Kibana se activa cuando un desastre interseca cualquier instalación.</p></li><li><p><strong>Automatización de flujos de trabajo:</strong> la alerta activa un flujo de trabajo de Kibana que inicia una conversación con un agente de AI.</p></li><li><p><strong>Razonamiento de AI:</strong> el agente analiza las instalaciones afectadas, sus activos y las instalaciones de apoyo más cercanas para determinar cómo reubicar todos los activos y al personal.</p></li><li><p><strong>Notificaciones por correo electrónico</strong>: el agente envía correos electrónicos a todos los destinatarios sobre la llegada y salida de personal o activos.</p></li></ol><p>Revisemos cada capa.</p><h2>Paso 1: indexar instalaciones militares con límites geográficos</h2><p>La base es el set de datos MIRTA del DoD de <a href="https://source.coop/seerai/hifld/military-installations-ranges-and-training-areas-mirta-dod-sites---boundaries">source.coop/seerai/hifld</a>. Este set de datos proporciona un geo_shape de tipo Point para cada instalación; coordenadas de centroide en lugar de polígonos de límites completos.</p><p>Cada documento de instalación en el índice mitra-facilities se enriquece con datos de perfil operativo (todos ficticios), más allá de lo que proporciona MIRTA:</p>{
  "entity_name": "Naval Station Norfolk",
  "branch_of_service": "Navy",
  "mission_function_type": "fleet_support",
  "personnel_count": 50000,
  "housing_capacity": 55000,
  "temporary_housing_capacity": 10000,
  "logistics_capabilities": ["fuel", "airlift", "sealift", "medical"],
  "available_assets": [
    { "type": "helicopters", "count": 24 },
    { "type": "transport_vehicles", "count": 150 }
  ],
  "contact_email": "norfolk.ops@navy.mil.gov.fake",
  "operational_status": "act",
  "is_joint_base": false,
  "entity_geo_location": { "type": "polygon", "coordinates": [...] }
}<p>Este índice enriquecido es lo que le permite al agente de AI tomar decisiones inteligentes de asignación; no solo “aquí hay bases cercanas”, sino “aquí hay bases con capacidad de alojamiento disponible, tipos de misión compatibles y la logística para recibir activos entrantes”.</p><h2>Paso 2: ingestar y normalizar eventos de GDACS</h2><p>GDACS publica GeoJSON en tiempo real para terremotos, ciclones tropicales, inundaciones, incendios forestales, volcanes y sequías. Ingestamos este feed en un flujo de datos (logs-gdacs.events-*) mediante un pipeline de ingesta personalizado que normaliza el GeoJSON sin procesar a campos de ECS.</p><p>El pipeline de ingesta de GDACS hace varias cosas que vale la pena destacar:</p><p><strong>Extracción de geometría:</strong> el centroide se almacena como geo_point para la visualización en mapas, y el polígono de impacto se almacena como geo_shape en gdacs.affected_area, que es el campo que se utiliza para búsquedas de intersección más adelante.</p><p><strong>Normalización de la gravedad:</strong> cada tipo de desastre tiene una escala de gravedad diferente. Un ciclón tropical se mide en velocidad del viento en km/h; un terremoto, en magnitud de Richter. El pipeline los mapea a todos a una puntuación normalizada de 0–100:</p>// Painless snippet from the ingest pipeline
if (type == 'TC') {
  norm = Math.min(100.0, Math.max(0.0, (val - 40.0) / 2.6));
} else if (type == 'EQ') {
  norm = Math.min(100.0, Math.max(0.0, (val - 4.0) * 20.0));
}<p>Luego, la puntuación de gravedad normalizada se mapea a una etiqueta severity_level (low, medium, high, critical) utilizada para el mapeo de gravedad de las alertas en la regla de detección.</p><p><strong>Alineación con ECS:</strong> event.kind: alert, event.category: threat, marcas de tiempo asignadas a event.start/event.end, y un _id estable basado en una huella digital para la deduplicación.</p><h2>Paso 3: enriquecimiento geoespacial: búsqueda de instalaciones afectadas al momento de la indexación</h2><p>La política de enriquecimiento geo_match de Elasticsearch compara el polígono del desastre con el límite de cada instalación al momento de la indexación, sin necesidad de una unión en tiempo de búsqueda. En lugar de consultar durante la búsqueda, usamos un <strong>procesador de enriquecimiento</strong> en el pipeline de ingesta para comparar el polígono de impacto del desastre con el límite de cada instalación <em>a medida que se indexa el documento</em>.</p><p>La política de enriquecimiento es una política geo_match:</p>{
  "geo_match": {
    "indices": "mitra-facilities",
    "match_field": "entity_geo_location",
    "enrich_fields": [
      "entity_name",
      "entity_type",
      "entity_station_number",
      "entity_geo_city_name",
      "entity_geo_region_name"
    ]
  }
}<p>El procesador se ejecuta al final del pipeline de ingesta:</p>{
  "enrich": {
    "policy_name": "facilities-geo",
    "field": "gdacs.affected_area",
    "target_field": "affected_facilities",
    "shape_relation": "INTERSECTS",
    "max_matches": 128
  }
}<p>INTERSECTS detecta cualquier instalación cuyo límite toque o se superponga con el polígono de desastre, incluidas las intersecciones parciales. El resultado es que cada documento de evento de GDACS se almacena con un arreglo anidado affected_facilities que nos indica exactamente qué instalaciones se encuentran en la zona de impacto. No se necesita una búsqueda de unión.</p><h2>Paso 4: regla de detección: alertas sobre el impacto en las instalaciones</h2><p>Una regla de detección de Kibana monitorea el flujo de datos logs-gdacs.events-* y se activa cuando un evento de GDACS se ha enriquecido con información sobre al menos una instalación afectada:</p>Búsqueda: affected_facilities: { entity_name: * }<p>La regla se ejecuta cada hora (cubre un intervalo de now-1h a now) y usa un mapeo dinámico de gravedad; el campo gdacs.severity_level calculado por el pipeline de ingesta determina automáticamente la gravedad de la alerta.</p><p>La gravedad de la alerta también determina la puntuación de riesgo mediante el mapeo de campos:</p>"risk_score_mapping": [
  {
    "field": "gdacs.normalized_severity",
    "operator": "equals",
    "value": ""
  }
]<p>Cuando la regla se activa, envía todo el contexto de la alerta, incluido el arreglo enriquecido affected_facilities con los nombres, tipos y ubicaciones de las instalaciones, a un flujo de trabajo de Kibana para su procesamiento posterior.</p><h2>Paso 5: automatización del flujo de trabajo: conectar la alerta con el agente</h2><p>Los flujos de trabajo de Kibana gestionan la transferencia de la detección a la respuesta. El flujo de trabajo de respuesta ante desastres naturales se activa mediante la alerta:</p>triggers:
  - type: alert
steps:
  - name: start_convo
    type: kibana.request
    with:
      method: "POST"
      path: "/api/agent_builder/converse"
      body:
        agent_id: "mitra.response"
        input: "Nueva alerta de desastre natural: {{ event.alerts | json }}"<p>Toda la carga útil de la alerta (tipo de desastre, gravedad, área afectada y la lista de instalaciones afectadas) se reenvía al agente de AI como contexto inicial. El agente se encarga del resto.</p><h2>Paso 6: el agente de AI: de los datos a la acción coordinada</h2><p>El agente mitra.response toma la carga útil completa de la alerta y, en un único ciclo agéntico, evalúa el alcance, encuentra instalaciones receptoras, asigna personal y envía notificaciones de evacuación y recepción, todo sin intervención humana.</p><p>El agente tiene dos herramientas disponibles:</p><ul><li><p><strong>mitra.nearest_facility</strong>realiza una búsqueda en el índice mitra-facilities mediante una búsqueda geo_shape, ordenada por distancia desde una coordenada dada, y devuelve hasta 50 instalaciones activas cercanas con capacidad disponible.</p></li><li><p><strong>mitra.send_email</strong> itera sobre una arreglo JSON de objetos de instalaciones y envía notificaciones de evacuación o recepción con formato.</p></li></ul><p>El conjunto de instrucciones del agente define un flujo de trabajo claro:</p><ol><li><p><strong>Evalúa la situación.</strong> Parsea la alerta, identifica las instalaciones afectadas y determina el alcance del desastre.</p></li><li><p><strong>Haz un inventario de lo que se debe mover.</strong> Recuentos de personal, activos críticos, requisitos de alojamiento por instalación.</p></li><li><p><strong>Encuentra instalaciones de destino.</strong> Invoca a mitra.nearest_facility para cada instalación afectada, sin incluir las que aún están en la zona de peligro.</p></li><li><p><strong>Toma decisiones de asignación.</strong> Analiza soluciones que involucren una o varias instalaciones, la compatibilidad entre ramas de las fuerzas armadas, la capacidad de alojamiento y el apoyo para los activos.</p></li><li><p><strong>Envía correos electrónicos de coordinación.</strong> Envía órdenes de evacuación a las instalaciones de origen y notificaciones de recepción a las instalaciones receptoras.</p></li><li><p><strong>Genera un reporte resumido. </strong>Genera un breve resumen de todas las instalaciones afectadas, el total de personal, los activos trasladados, las instalaciones de destino y cualquier inquietud en el chat para su revisión.</p></li></ol><p>La lógica de asignación del agente sigue restricciones del mundo real: no exceder la capacidad de alojamiento, dar preferencia a las reubicaciones dentro de la misma rama de las fuerzas armadas cuando sea posible, usar bases conjuntas para alojar al personal de distintas ramas que exceda la capacidad de otras instalaciones y priorizar la distancia para minimizar el tiempo de traslado.</p><h3>La herramienta de instalación más cercana</h3><p>La búsqueda subyacente del flujo de trabajo usa geo_shape con un filtro de círculo y ordenamiento por _geo_distance:</p>"query": {
  "bool": {
    "filter": [
      {
        "geo_shape": {
          "entity_geo_location": {
            "shape": {
              "type": "circle",
              "coordinates": [{{ inputs.lon }}, {{ inputs.lat }}],
              "radius": "5000km"
            },
            "relation": "intersects"
          }
        }
      },
      { "term": { "operational_status.keyword": "act" } }
    ]
  }
},
"sort": [
  {
    "_geo_distance": {
      "entity_geo_point": { "lat": {{ inputs.lat }}, "lon": {{ inputs.lon }} },
      "order": "asc",
      "unit": "km"
    }
  }
],
"script_fields": {
  "available_capacity": {
    "script": {
      "source": "Math.max(0, doc['housing_capacity'].value - doc['personnel_count'].value)"
    }
  }
}<p>La capacidad disponible se calcula en el momento de la búsqueda mediante un campo de script que calcula la capacidad de alojamiento menos el recuento de personal actual. El agente usa esto para asignar personal entre destinos sin exceder los límites.</p><h2>Huracán ELARA-26: coordinación agéntica de 137 000 efectivos, de extremo a extremo</h2><p>El huracán ELARA-26 es una tormenta de categoría 4 (vientos máximos de 213 km/h) que se prevé que toque tierra en el área de Hampton Roads de Virginia. Cuando se ingiere el evento de GDACS, el polígono del área afectada interseca siete instalaciones militares principales de la región. La regla de detección se activa. El flujo de trabajo inicia una conversación del agente.</p><p>Dentro de un solo ciclo agéntico, el agente:</p><ul><li><p>Identificó siete instalaciones en la zona de impacto, con un total combinado de 137 372 miembros del personal.</p></li><li><p>Llamó a mitra.nearest_facility para encontrar instalaciones receptoras fuera de la trayectoria de la tormenta.</p></li><li><p>Distribuyó al personal entre nueve instalaciones receptoras en función de la capacidad de alojamiento disponible y la distancia.</p></li><li><p>Generó y despachó órdenes de evacuación a las siete instalaciones afectadas.</p></li><li><p>Generó y despachó notificaciones de recepción a las nueve instalaciones receptoras.</p></li><li><p>Generó un resumen completo de coordinación, similar al siguiente:</p></li></ul><p><strong>Instalaciones evacuadas:</strong></p><p>Instalación</p><p>Personal</p><p>Naval Station Norfolk</p><p>50 000</p><p>Joint Expeditionary Base Little Creek-Fort Story</p><p>18 000</p><p>Naval Air Station Oceana</p><p>15 355</p><p>Naval Air Station Oceana Dam Neck Annex</p><p>17 509</p><p>NG State Military Reservation Camp Pendleton</p><p>9707</p><p>Joint Base Langley-Eustis</p><p>15 000</p><p>Naval Weapons Station Yorktown</p><p>11 801</p><p><strong>Instalaciones receptoras:</strong></p><p>Instalación</p><p>Distancia</p><p>Personal entrante</p><p>Fort Gregg-Adams</p><p>97 km</p><p>~40 000</p><p>Marine Corps Base Quantico</p><p>148 km</p><p>~30 000</p><p>Naval Support Facility Indian Head</p><p>151 km</p><p>~30 000</p><p>Joint Base Andrews</p><p>180 km</p><p>~30 000</p><p>Naval Air Station Patuxent River</p><p>141 km</p><p>~10 000</p><p>NG MTA Camp Butner</p><p>174 km</p><p>~5000</p><p>NG Bethany Beach Training Site</p><p>209 km</p><p>~4707</p><p>Rivanna Station</p><p>140 km</p><p>~7500</p><p>Def Gen Supply Center</p><p>22 km</p><p>~6000</p><p>Los activos reubicados incluyen vehículos de transporte, helicópteros, lanchas patrulleras, unidades médicas, vehículos de ingeniería, generadores, remolques de agua, kits de refugio y sistemas de comunicación.</p><h3>Notificaciones por correo electrónico automatizadas</h3><p>Una vez que el agente finalizó su plan de asignación, invocó a mitra.send_email y envió 16 correos electrónicos en una sola pasada; es decir, órdenes de evacuación a las siete instalaciones afectadas y notificaciones de recepción a las nueve instalaciones receptoras. Cada mensaje incluía la instalación de destino, el recuento de personal entrante, los activos por trasladar y un contacto de coordinación. Lo que habría requerido horas de cadenas de llamadas se completó automáticamente en cuanto el agente terminó de razonar.</p><h3>Ampliación de la respuesta agéntica ante desastres con RAG y fundamentación en políticas</h3><p>Esta demostración se basa exclusivamente en datos estructurados, como cifras de capacidad, distancias y estado operativo. Las funcionalidades de búsqueda semántica y Retrieval Augmented Generation (RAG) de Elastic pueden hacer que el agente sea significativamente más inteligente, con dos adiciones:</p><p><strong>Recuperación de respuestas históricas:</strong> indexa reportes posteriores a la acción, resúmenes de incidentes de la Agencia Federal para el Manejo de Emergencias (FEMA) y registros de respuesta ante desastres anteriores como incrustaciones vectoriales. Cuando se activa un evento nuevo, el agente puede recuperar semánticamente cómo se gestionaron eventos similares, lo que fundamenta las decisiones de asignación con conocimiento institucional en lugar de basarse únicamente en cálculos de capacidad.</p><p><strong>Fundamentación en políticas y doctrina:</strong> indexa las directivas de gestión de emergencias del DoD, los planes de continuidad de las operaciones de la instalación y las directrices del comandante. El agente puede recuperar y citar las políticas reales que rigen una respuesta, lo que garantiza que cada decisión esté fundamentada en la doctrina en lugar de en inferencias.</p><p>Ambos siguen el mismo enfoque nativo de Elastic: un pipeline de inferencia genera incrustaciones al momento de la indexación, y se expone una herramienta de búsqueda semántica al agente. El pipeline de coordinación permanece igual. El agente simplemente se vuelve más inteligente.</p><h2>Por qué Elasticsearch es la plataforma adecuada para la respuesta agéntica del sector público</h2><p>Esto no es un chatbot. No es un dashboard. Es un sistema de flujo de trabajo agéntico con capacidad de respuesta, uno que detectó una amenaza, razonó sobre un problema logístico complejo y coordinó la reubicación de 137 000 personas sin intervención humana. Ese tipo de resultado solo es posible porque cada funcionalidad de la que depende reside en una sola plataforma unificada.</p><p>El soporte geoespacial de Elasticsearch (geo_point, geo_shape, políticas de enriquecimiento y ordenación basada en la distancia) gestiona el razonamiento espacial que hace posible la detección de intersecciones y la búsqueda de instalaciones a escala. La búsqueda semántica y las incrustaciones vectoriales proporcionan a los agentes una base factual, lo que garantiza que el razonamiento de la AI se apoye en el contenido real de tus datos y no en suposiciones producto de alucinaciones. El motor de detección de Kibana, Workflows, Agent Builder y las herramientas de Agent Builder lo integran todo en un pipeline que va desde el evento sin procesar hasta la acción coordinada, sin necesidad de código de integración externo.</p><p>Ninguna otra plataforma reúne esto como lo hace Elastic. La combinación de indexación en tiempo real, precisión geoespacial, recuperación semántica y orquestación agéntica, todo en un solo stack, con seguridad y observabilidad de nivel empresarial integradas, es lo que diferencia a Elastic de las herramientas que hacen bien una de estas cosas pero requieren que unas el resto por tu cuenta.</p><h2>Respuesta geoespacial agéntica para la gestión de emergencias, bomberos, fuerzas del orden y salud pública</h2><p>La misma arquitectura se aplica dondequiera que se crucen personas, instalaciones y eventos en tiempo real. Los datos específicos cambian. El pipeline no.</p><p><strong>Gestión de emergencias:</strong> FEMA y las oficinas estatales de gestión de emergencias pueden mapear las ubicaciones de los refugios, las áreas de preparación y las poblaciones vulnerables con respecto a los polígonos de fenómenos meteorológicos adversos entrantes del Servicio Meteorológico Nacional (NWS), lo que activa el posicionamiento previo automatizado de recursos antes de que una tormenta toque tierra.</p><p><strong>Servicios de bomberos y emergencias médicas:</strong> los departamentos de bomberos pueden superponer las ubicaciones de las unidades y las zonas de respuesta sobre los perímetros de incendios forestales o las agrupaciones de incendios estructurales, y dirigir automáticamente las solicitudes de ayuda mutua a las unidades disponibles más cercanas que cuenten con el equipo adecuado.</p><p><strong>Fuerzas del orden:</strong> las agencias pueden correlacionar las ubicaciones de incidentes activos con zonas escolares, infraestructura crítica y posiciones de los oficiales, lo cual activa notificaciones de confinamiento con reconocimiento geográfico o el despacho de recursos sin esperar la clasificación manual.</p><p><strong>Seguridad en las escuelas públicas:</strong> los distritos escolares pueden monitorear datos de amenazas en tiempo real y compararlos con los límites de los recintos escolares. Cuando una amenaza cruza el perímetro de una escuela, un agente puede notificar de inmediato a la administración, iniciar las comunicaciones de cierre de emergencia y coordinar la respuesta de las fuerzas del orden, todo antes de que un despachador atienda el teléfono.</p><p><strong>Salud pública:</strong> los departamentos de salud pueden cotejar datos de vigilancia de enfermedades o zonas de riesgo ambiental con ubicaciones de clínicas, capas de densidad poblacional e inventarios de depósitos de suministros para dirigir los recursos donde más se necesitan.</p><p>Sector</p><p>Caso de uso</p><p>Capacidad de Elastic</p><p>Gestión de emergencias</p><p>Hacer coincidir las ubicaciones de los refugios con los polígonos de fenómenos meteorológicos adversos del NWS</p><p>Enriquecimiento de geo_shape + flujos de trabajo de Kibana</p><p>Bomberos y EMS</p><p>Superponer las ubicaciones de las unidades sobre los perímetros de incendios forestales</p><p>enrutamiento geoespacial + búsqueda de la instalación más cercana</p><p>Fuerzas del orden</p><p>Correlacionar incidentes con zonas escolares y posiciones de oficiales</p><p>reglas de alerta con reconocimiento geográfico + envío de recursos mediante el agente</p><p>Seguridad en las escuelas públicas</p><p>Monitorear las fuentes de amenazas contra los perímetros del campus</p><p>reglas de detección + notificación automatizada</p><p>Salud pública</p><p>Comparar las zonas de peligro con las ubicaciones de las clínicas y los depósitos de suministros</p><p>búsqueda semántica + enriquecimiento geoespacial</p><p>Los datos son diferentes en cada escenario. El patrón subyacente de ingestar, enriquecer en tiempo de indexación, detectar intersecciones, activar una respuesta agéntica y actuar es exactamente el mismo. Elastic brinda a las organizaciones del sector público la plataforma para crearlo una vez y adaptarlo en todas partes.</p><p><em>El lanzamiento de cualquiera de las características o funcionalidades descritas en esta publicación, así como su fecha, quedan a exclusivo criterio de Elastic. Es posible que algunas características o funcionalidades que no estén disponibles en este momento no se lancen a tiempo o no se lancen en absoluto.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-agentic-disaster-response</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-agentic-disaster-response</guid>
    <category><![CDATA[AI agéntica]]></category>
    <category><![CDATA[Kibana]]></category>
    <dc:creator><![CDATA[Alec Carpenter]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt969cad2694920de4/6a4693f37746672ad42675b5/cb292a501835472598dee30bef25c77afc54db6c-720x420.png" length="0" type="image/png"/>
    <pubDate>Thu, 04 Jun 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Cómo construir aplicaciones de IA con agentes con Mastra y Elasticsearch]]></title>
    <description><![CDATA[Aprende a construir aplicaciones de IA agéntica usando Mastra y Elasticsearch a través de un ejemplo práctico.]]></description>
    <content:encoded><![CDATA[<p>En este artículo, haremos un recorrido por cómo usar el marco de trabajo <a href="https://mastra.ai/">Mastra</a> TypeScript para crear aplicaciones agénticas que interactúen con <a href="https://www.elastic.co/elasticsearch">Elasticsearch</a>.</p><p>Recientemente, contribuimos al proyecto de <a href="https://github.com/mastra-ai/mastra">mastra-ai/mastra</a> open source agregando soporte para Elasticsearch como base de datos vectorial. Con esta nueva característica, puedes usar Elasticsearch de forma nativa en Mastra para almacenar incrustaciones. Además de los vectores, Elasticsearch ofrece un conjunto de funciones avanzadas para satisfacer todas tus necesidades de ingeniería de contexto (por ejemplo, <a href="https://www.elastic.co/search-labs/blog/context-engineering-hybrid-search-evolution-agentic-ai">búsqueda híbrida y reordenamiento</a>).</p><p>En este artículo, se detalla la creación de un agente para implementar una arquitectura de Retrieval-Augmented Generation (RAG) con Elasticsearch. Te mostraremos un proyecto de demostración en el que se utiliza un enfoque agéntico para interactuar con un corpus de datos de películas de ciencia ficción almacenados en Elasticsearch. El proyecto está disponible en <a href="https://github.com/elastic/mastra-elasticsearch-example">elastic/mastra-elasticsearch-example</a>.</p><h2>Mastra</h2><p>Mastra es un marco de trabajo de TypeScript para crear aplicaciones de IA agéntica.</p><p>Una estructura de proyecto en Mastra se ve así:</p>src/
├── mastra/
│   ├── agents/
│   │   └── weather-agent.ts
│   ├── tools/
│   │   └── weather-tool.ts
│   ├── workflows/
│   │   └── weather-workflow.ts
│   ├── scorers/
│   │   └── weather-scorer.ts
│   └── index.ts
├── .env.example
├── package.json
└── tsconfig.json<p>En Mastra, puedes crear <a href="https://mastra.ai/docs/agents/overview">agentes</a>, <a href="https://mastra.ai/docs/agents/using-tools">herramientas</a>, <a href="https://mastra.ai/docs/workflows/overview">flujos de trabajo</a> y <a href="https://mastra.ai/docs/evals/overview">puntajes</a>.</p><p>Un <strong>agente</strong> es una clase que acepta un mensaje como entrada y produce una respuesta como salida. Un agente puede usar herramientas, modelos de lenguaje a gran escala (LLM) y una memoria (figura 1).</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0f7484f7501997dc/6a170417cdacbffb5a7d28f6/f6aca2dcc7fcc45d25e06681649be1b2b7eb6781-706x721.png" alt="Un diagrama que muestra cómo funciona un agente en Mastra." /><p>Las <strong>herramientas</strong> de un agente le permiten interactuar con el "mundo externo", como comunicarse con una API web o hacer una operación interna, como consultar Elasticsearch. El componente de <strong>memoria</strong> es crucial para almacenar el historial de conversaciones, incluidas las entradas y salidas pasadas. Este contexto almacenado permite que el agente proporcione respuestas más informadas y relevantes a preguntas futuras mediante el uso de sus interacciones pasadas.</p><p>Los <strong>flujos de trabajo</strong> te permiten definir secuencias complejas de tareas mediante pasos claros y estructurados, en lugar de depender del razonamiento de un solo agente (figura 2). Te brindan control total sobre cómo se desglosan las tareas, cómo se mueven los datos entre ellas y qué se ejecuta y cuándo. Los flujos de trabajo se ejecutan con el motor de ejecución integrado de forma predeterminada o se pueden desplegar en <a href="https://mastra.ai/docs/deployment/workflow-runners">ejecutores de flujos de trabajo</a>.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd82ea661569e1018/6a170419dc55de3adde00cc9/0dce161cf7891207015dc87532b5b90df1822432-880x252.png" alt="Ejemplo de flujo de trabajo en Mastra." /><p>En Mastra, también puedes definir puntuaciones, que son pruebas automatizadas que evalúan las salidas de los agentes mediante métodos calificados por modelos, basados en reglas y estadísticos. Los evaluadores devuelven <em>puntuaciones</em>: valores numéricos (normalmente entre 0 y 1) que cuantifican qué tan bien una salida cumple con tus criterios de evaluación. Estas puntuaciones te permiten hacer un seguimiento objetivo del rendimiento, comparar diferentes enfoques e identificar áreas de mejora en tus sistemas de IA. Los evaluadores pueden personalizarse con tus propias solicitudes y funciones de puntuación.</p><h2>Elasticsearch</h2><p>Para ejecutar el proyecto de demostración, necesitamos tener una instancia de Elasticsearch en ejecución. Puedes activar una prueba gratis en <a href="https://www.elastic.co/cloud">Elastic Cloud</a> o instalarlo localmente usando el script <a href="https://github.com/elastic/start-local"><code>start-local</code></a>:</p>curl -fsSL https://elastic.co/start-local | sh<p>Se instalarán Elasticsearch y Kibana en tu computadora y se generará una clave API que se utilizará para configurar la integración de Mastra.</p><p>La clave de API se mostrará como salida del comando anterior y se almacenará en un archivo <strong>.env</strong> en la carpeta elastic-start-local.</p><h2>Instalar y configurar la demo</h2><p>Creamos un repositorio <a href="https://github.com/elastic/mastra-elasticsearch-example">elastic/mastra-elasticsearch-example</a> que contiene el código fuente del proyecto de demostración. El ejemplo reportado en el repositorio ilustra cómo crear un agente en Mastra que implemente una arquitectura RAG para recuperar documentos de Elasticsearch.</p><p>Proporcionamos un set de datos para la demostración sobre películas de ciencia ficción. Extrajimos 500 películas de los <a href="https://www.kaggle.com/datasets/rajugc/imdb-movies-dataset-based-on-genre/versions/2?select=scifi.csv">sets de datos</a> de IMDb en Kaggle.</p><p>El primer paso es instalar las dependencias del proyecto con npm, usando el siguiente comando:</p>npm install<p>Luego tenemos que configurar el archivo <strong>.env</strong> que contendrá la configuración. Podemos generar este archivo copiando la estructura del archivo <strong>.env.example</strong> , usando el siguiente comando:</p>cp .env.example .env<p>Ahora podemos editar el archivo .env, agregando la información faltante:</p>OPENAI_API_KEY=
ELASTICSEARCH_URL=
ELASTICSEARCH_API_KEY=
ELASTICSEARCH_INDEX_NAME=scifi-movies<p>El nombre del índice de Elasticsearch es <strong><code>scifi-movies</code></strong>. Si quieres, puedes cambiarlo usando la variable de entorno <code>ELASTICSEARCH_INDEX_NAME</code>.</p><p>Usamos OpenAI como servicio de incrustación, lo que significa que necesitas proporcionar una clave API para OpenAI en la variable <code>OPENAI_API_KEY</code> .env.</p><p>El modelo de incrustación empleado en el ejemplo es <a href="https://developers.openai.com/api/docs/models/text-embedding-3-small">openai/text-embedding-3-small</a>, con una dimensión de incrustación de 1536.</p><p>Para generar la respuesta final, empleamos el modelo <a href="https://developers.openai.com/api/docs/models/gpt-5-nano">openai/gpt-5-nano</a> para reducir costos.</p><p>La arquitectura RAG te permite usar un modelo LLM final menos potente (y generalmente menos costoso) porque el trabajo pesado de fundamentar la respuesta está a cargo del componente de recuperación (Elasticsearch en este caso).</p><p>El LLM más pequeño solo se encarga de dos tareas principales:</p><ul><li><p><strong>Reformulación/incrustación de la consulta:</strong> Convertir la pregunta en lenguaje natural del usuario en una incrustación de vectores para la búsqueda semántica.</p></li><li><p><strong>Sintetizar la respuesta:</strong> Se toman los fragmentos de contexto muy relevantes y recuperados (documentos/películas) y se los sintetiza en una respuesta coherente, final y legible por seres humanos, siguiendo las instrucciones del prompt proporcionado.</p></li></ul><p>Dado que el proceso RAG <strong>proporciona el contexto fáctico exacto</strong> necesario para la respuesta, el LLM final no necesita ser masivo o muy complejo y no necesita poseer todo el conocimiento requerido dentro de sus propios parámetros (que es donde sobresalen los modelos grandes y caros). Esencialmente actúa como un sofisticado resumidor y formateador de texto para el contexto proporcionado por Elasticsearch, en lugar de ser una base de conocimientos completa en sí mismo. Esto permite el uso de modelos como <code>gpt-5-nano</code> para optimizar costos y latencia.</p><p>Después de la configuración del archivo .env, puedes ingestar las películas a Elasticsearch usando el siguiente comando:</p>npx tsx src/utility/store.ts<p>Deberías ver una salida como la siguiente:</p>🚀 Starting ingestion of 500 movies from 500_scifi_movies.jsonl...
Ingesting ░░░░░░░░░░░░░░░░░░░░░░░░ 1/500 (0%) | ok:1 | fail:0 | chunks:1 | eta:19m 33s | current:Capricorn One
Ingesting ░░░░░░░░░░░░░░░░░░░░░░░░ 2/500 (0%) | ok:2 | fail:0 | chunks:2 | eta:10m 32s | current:Doghouse
Ingesting ░░░░░░░░░░░░░░░░░░░░░░░░ 3/500 (1%) | ok:3 | fail:0 | chunks:3 | eta:7m 33s | current:Dinocroc
Ingesting ░░░░░░░░░░░░░░░░░░░░░░░░ 4/500 (1%) | ok:4 | fail:0 | chunks:7 | eta:6m 10s | current:Back to the Future           
Ingesting ░░░░░░░░░░░░░░░░░░░░░░░░ 5/500 (1%) | ok:5 | fail:0 | chunks:9 | eta:5m 14s | current:The Projected Man            
Ingesting ░░░░░░░░░░░░░░░░░░░░░░░░ 6/500 (1%) | ok:6 | fail:0 | chunks:11 | eta:4m 41s | current:I, Robot
...
✅ Ingestion complete in 1m 46s. Success: 500, Failed: 0, Chunks: 693.<p>El mapping del índice de películas de ciencia ficción contiene los siguientes campos:</p><ul><li><p><strong>incrustación</strong>, dense_vector con 1536 dimensiones, similitud del coseno.</p></li><li><p><strong>descripción</strong>, texto que contiene la descripción de la película.</p></li><li><p><strong>director</strong>, texto que contiene el nombre del director.</p></li><li><p><strong>título</strong>, texto que contiene el título de la película.</p></li></ul><p>Generamos las incrustaciones usando el título + la descripción. Como el título y la descripción son dos campos separados, la concatenación de ambos asegura que el vector de incrustación resultante capture tanto la identidad única y específica (título) como el contexto rico y descriptivo (descripción) de la película, lo que lleva a resultados de búsqueda semántica más precisos y completos. Esta entrada combinada le brinda al modelo de incrustación una mejor representación del contenido del documento para la búsqueda por similitud.</p><h2>Ejecuta la demostración</h2><p>Puedes ejecutar la demo con el siguiente comando:</p>npm run dev<p>Este comando iniciará una aplicación web en <strong>localhost:4111</strong> para acceder a Mastra Studio (figura 3).</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8a539df677a33b36/6a17041a47d49c36842d88a0/1567e309df21a12bcf1dfef4429f82342549956c-1705x1079.png" alt="Captura de pantalla de Mastra Studio con el ejemplo del agente de Elasticsearch." /><p><a href="https://mastra.ai/docs/getting-started/studio">Mastra Studio</a> ofrece una UI interactiva para crear y probar tus agentes, junto con una API REST que expone tu aplicación Mastra como un servicio local. Esto te permite comenzar a construir de inmediato sin preocuparte por la integración.</p><p>Proporcionamos un <strong>Agente Elasticsearch</strong> que emplea <a href="https://mastra.ai/reference/tools/vector-query-tool">createVectorQueryTool</a> de Mastra como herramienta para ejecutar búsqueda semántica usando Elasticsearch. Este agente emplea el enfoque RAG para buscar documentos relevantes (es decir, películas) que respondan a la pregunta del usuario.</p><p>Este agente usa el siguiente prompt:</p>You are a helpful assistant that answers questions based on the provided context.
Follow these steps for each response:

1. First, carefully analyze the retrieved context chunks and identify key information.
2. Break down your thinking process about how the retrieved information relates to the query.
3. Draw conclusions based only on the evidence in the retrieved context.
4. If the retrieved chunks don't contain enough information, explicitly state what's missing.

Format your response as:
THOUGHT PROCESS:
- Step 1: [Initial analysis of retrieved chunks]
- Step 2: [Reasoning based on chunks]

FINAL ANSWER:
[Your concise answer based on the retrieved context]

Important: When asked to answer a question, please base your answer only on the context provided in the tool. 
If the context doesn't contain enough information to fully answer the question, please state that explicitly and stop it.
Do not add more information than what is present in the retrieved chunks.
Remember: Explain how you're using the retrieved information to reach your conclusions.<p>Si haces clic en el menú <code>Mastra Studio &gt; Agents</code> y seleccionas <strong>Agente de Elasticsearch</strong>, puedes probar el agente usando un sistema de chat. Por ejemplo, puedes pedir información sobre películas de ciencia ficción con una pregunta como esta:</p><p><em>Encuentra 5 películas o series de TV sobre ovnis</em>.</p><p>Notarás que el agente ejecutará el vectorQueryTool. Puedes hacer clic en la herramienta invocada para ver la entrada y la salida. Al final de la ejecución, el LLM responderá tu pregunta, dado el contexto que proviene del índice de películas de ciencia ficción de Elasticsearch (figura 4).</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltda92a9b6e56528d3/6a17041c2b835f9724f4b0d6/d9998d4f687984de98845dae52d1288166abf448-1344x1071.png" alt=" Respuesta de LLM usando el agente de Elasticsearch." /><p>Mastra ejecuta los siguientes pasos internamente:</p><ol><li><p><strong>Conversión en vectores:</strong> La pregunta del usuario, <em>Encuentra 5 películas o series de televisión sobre ovnis,</em> se convierte en una incrustación de vectores mediante el modelo <code>openai/text-embedding-3-small</code> de OpenAI.</p></li><li><p><strong>Búsqueda de vectores:</strong> Esta incrustación se usa luego para hacer una búsqueda en Elasticsearch mediante una búsqueda de vectores.</p></li><li><p><strong>Recuperación de resultados:</strong> Elasticsearch devuelve una serie de 10 películas muy relevantes para la consulta (es decir, aquellas con vectores más cercanos al vector de consulta del usuario).</p></li><li><p><strong>Generación de respuestas:</strong> Las películas recuperadas y la pregunta original del usuario se envían al LLM, específicamente <code>openai/gpt-5-nano</code>. El LLM procesa esta información y genera una respuesta final, asegurando que se cumpla la solicitud del usuario para cinco resultados.</p></li></ol><h2>El agente de Elasticsearch</h2><p>Aquí presentamos el código fuente del agente de Elasticsearch.</p>import { Agent } from "@mastra/core/agent";
import { ElasticSearchVector } from '@mastra/elasticsearch';
import { createVectorQueryTool } from '@mastra/rag';
import { ModelRouterEmbeddingModel } from "@mastra/core/llm";
import { Memory } from "@mastra/memory";

const es_url = process.env.ELASTICSEARCH_URL;
const es_apikey = process.env.ELASTICSEARCH_API_KEY;
const es_index_name = process.env.ELASTICSEARCH_INDEX_NAME;
const prompt = 'insert here the previous prompt';

const esVector = new ElasticSearchVector({
  id: 'elasticsearch-vector',
  url: es_url,
  auth: {
    apiKey : es_apikey
  }
});

const vectorQueryTool = createVectorQueryTool({
  vectorStore: esVector,
  indexName: es_index_name,
  model: new ModelRouterEmbeddingModel("openai/text-embedding-3-small")
});

export const elasticsearchAgent = new Agent({
  id: "elasticsearch-agent",
  name: "Elasticsearch Agent",
  instructions: prompt,
  model: 'openai/gpt-5-nano',
  tools: { vectorQueryTool },
  memory: new Memory(),
});<p>El <strong>vectorQueryTool</strong> es la herramienta que se invoca para implementar la parte de recuperación del ejemplo de RAG. Emplea la implementación de <a href="https://mastra.ai/reference/vectors/elasticsearch">ElasticSearchVector</a> que Elastic aportó a Mastra.</p><p>El agente es un objeto de la clase agente que utiliza la herramienta vectorQueryTool, el prompt y una memoria. Como puedes ver, el código que necesitamos implementar para conectar Elasticsearch a un agente es muy sencillo.</p><h2>Conclusión</h2><p>En este artículo, se demostró la simplicidad y el poder de integrar Elasticsearch con el marco de trabajo Mastra para construir sofisticadas aplicaciones de IA agéntica. En concreto, te mostramos cómo crear un agente RAG capaz de efectuar búsquedas semánticas sobre un corpus de datos de películas de ciencia ficción indexados en Elasticsearch.</p><p>Una de las principales conclusiones es la contribución directa de Elastic al proyecto de código abierto Mastra, que ofrece compatibilidad nativa con Elasticsearch como almacén de vectores. Esta integración reduce considerablemente las barreras de acceso, como se puede ver en el código fuente del <strong>agente de Elasticsearch</strong>. Usando el <code>ElasticSearchVector</code> y <code>createVectorQueryTool</code>, la configuración completa para conectar Elasticsearch a tu agente requiere solo un número mínimo de líneas de código de configuración.</p><p>Elasticsearch ofrece varias características avanzadas para mejorar la relevancia de los resultados. Por ejemplo, la <a href="https://www.elastic.co/elasticsearch/hybrid-search">búsqueda híbrida</a> aumenta significativamente la precisión combinando la búsqueda léxica con la búsqueda de vectores. Otra característica interesante es la reclasificación con los últimos <a href="https://www.elastic.co/search-labs/tutorials/jina-tutorial/jina-reranker-v3">modelos de Jina</a>, que puede aplicarse al final de la búsqueda híbrida. Para obtener más información sobre estas técnicas, consulta los siguientes artículos de Elasticsearch Labs:</p><ul><li><p><a href="https://www.elastic.co/search-labs/blog/hybrid-search-elasticsearch">Búsqueda híbrida Elasticsearch</a> de Valentin Crettaz</p></li><li><p><a href="https://www.elastic.co/search-labs/blog/jina-models-elasticsearch-guide">Introducción a los modelos de Jina, su funcionalidad y usos en Elasticsearch</a> de Scott Martens</p></li></ul><p>También te animamos a consultar el ejemplo proporcionado y comenzar a crear tus propios agentes basados en datos con Mastra y Elasticsearch. Para más información sobre Mastra, puedes consultar la documentación oficial <a href="https://mastra.ai/docs">aquí</a>.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/build-agentic-ai-applications-mastra-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/build-agentic-ai-applications-mastra-elasticsearch</guid>
    <category><![CDATA[AI agéntica]]></category>
    <dc:creator><![CDATA[Enrico Zimuel]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt083181bab7c0e2d0/6a17041eacf0880b70be99f5/ab30baf2f908534840c5d71a46705773807baf54-1280x720.png" length="0" type="image/png"/>
    <pubDate>Wed, 08 Apr 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Cómo crear un servidor MCP de Elasticsearch con TypeScript]]></title>
    <description><![CDATA[Aprende a crear un servidor MCP de Elasticsearch con TypeScript y Claude Desktop.]]></description>
    <content:encoded><![CDATA[<p>Cuando se trabaja con grandes bases de conocimiento en Elasticsearch, encontrar información es solo la mitad de la batalla. Los ingenieros suelen necesitar sintetizar resultados de varios documentos, generar resúmenes y rastrear las respuestas hasta sus fuentes. El protocolo de contexto de modelo (MCP) proporciona una manera estandarizada de conectar Elasticsearch con aplicaciones basadas en modelos de lenguaje grande (LLM) para lograr esto. Mientras que Elastic ofrece soluciones oficiales, como Elastic Agent Builder (que incluye un <a href="https://www.elastic.co/docs/solutions/search/agent-builder/mcp-server">endpoint MCP</a> entre sus características), construir un servidor MCP personalizado te brinda control total sobre la lógica de búsqueda, el formato de resultados y cómo se pasa el contenido recuperado a un LLM para síntesis, resúmenes y citas.</p><p>En este artículo, exploraremos las ventajas de construir un servidor MCP personalizado de Elasticsearch y mostraremos cómo crear uno en TypeScript que conecte Elasticsearch con aplicaciones impulsadas por LLM.</p><h2>¿Por qué construir un servidor MCP personalizado de Elasticsearch?</h2><p>Elastic ofrece algunas alternativas para los <a href="https://www.elastic.co/docs/solutions/search/mcp">servidores MCP</a>:</p><ul><li><p><a href="https://www.elastic.co/docs/solutions/search/agent-builder/mcp-server">Servidor MCP de Elastic Agent Builder para Elasticsearch 9.2+</a></p></li><li><p><a href="https://github.com/elastic/mcp-server-elasticsearch?tab=readme-ov-file#elasticsearch-mcp-server">Servidor MCP de Elasticsearch para versiones anteriores (Python)</a></p></li></ul><p>Si necesitas más control sobre cómo tu servidor MCP interactúa con Elasticsearch, construir tu propio servidor personalizado te da la flexibilidad de adaptarlo exactamente a tus necesidades. Por ejemplo, el endpoint MCP de Agent Builder está limitado a las consultas de lenguaje de búsqueda (ES|QL) de Elasticsearch, mientras que un servidor personalizado te permite usar el DSL de consulta completo. También obtienes control sobre cómo se formatean los resultados antes de pasarlos al LLM y puedes integrar pasos de procesamiento adicionales, como el resumen impulsado por OpenAI que implementaremos en este tutorial.</p><p>Al final de este artículo, tendrás un servidor MCP en TypeScript que busca información almacenada en un índice de Elasticsearch, la resume y proporciona citas. Usaremos Elasticsearch para la recuperación, el modelo <code>gpt-4o-mini</code> de OpenAI para resumir y generar citas, y Claude Desktop como cliente MCP y UI para recibir las búsquedas de los usuarios y dar respuestas. El resultado final es un asistente de conocimiento interno que ayuda a los ingenieros a descubrir y sintetizar las mejores prácticas en los documentos técnicos de su organización.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltad9133cb083ad352/6a170c19b0367d411e72bd5b/ec5771a874cf9740d4cac6888622cbe8cd6aede7-1999x1133.png" alt="Creación de un servidor MCP de Elastic con TypeScript y Claude Desktop." /><h2>Requisitos previos:</h2><ul><li><p>Node.js 20 +</p></li><li><p>Elasticsearch</p></li><li><p>Clave de API de OpenAI</p></li><li><p>Claude Desktop</p></li></ul><h3>¿Qué es MCP?</h3><p><a href="https://www.elastic.co/what-is/mcp">MCP</a> es un estándar abierto, creado por <a href="https://www.anthropic.com/news/model-context-protocol">Anthropic</a>, que ofrece conexiones seguras y bidireccionales entre los modelos de lenguaje grande (LLM) y sistemas externos, como Elasticsearch. Puedes leer más sobre el estado actual del MCP en <a href="https://www.elastic.co/search-labs/blog/mcp-current-state">este artículo</a>.</p><p>El panorama de MCP <a href="https://www.elastic.co/search-labs/blog/mcp-current-state#mcp-project-updates:-transport,-elicitation,-and-structured-tooling">evoluciona cada día</a>, con servidores disponibles para una amplia gama de casos de uso. Además de eso, desarrollar tu propio servidor MCP personalizado es fácil, como te mostraremos en este artículo.</p><h3>Clientes del MCP</h3><p>Hay una larga <a href="https://modelcontextprotocol.io/clients">lista de clientes del MCP disponibles</a>, cada uno con sus propias características y limitaciones. Por su sencillez y popularidad, usaremos <a href="https://claude.ai/download">Claude Desktop</a> como nuestro cliente MCP. Servirá como interfaz de chat en la que los usuarios podrán hacer preguntas en lenguaje natural e invocar automáticamente las herramientas expuestas por nuestro servidor MCP para buscar documentos y generar resúmenes.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt06fd7a02042094e1/6a170c1b14b2700024e3c651/66eb0b11473347b6cf2d85718251eeac38d6249d-1999x1491.png" alt="Página de Claude Sonnet 4.5, con la nota: &quot;¿Hora del café con Claude? ¿Cómo puedo ayudarte hoy?&quot;" /><h2>Cómo crear un servidor MCP de Elasticsearch</h2><p>Con el <a href="https://github.com/modelcontextprotocol/typescript-sdk">SDK de TypeScript</a>, podemos crear fácilmente un servidor que entiende cómo hacer búsquedas en nuestros datos de Elasticsearch con base en la entrada de búsqueda del usuario.</p><p>Estos son los pasos en este artículo para integrar el servidor MCP de Elasticsearch con el cliente Claude Desktop:</p><ol><li><p><a href="https://www.elastic.co/search-labs/blog/elastic-mcp-server-typescript-claude#configure-mcp-server-for-elasticsearch">Configurar el servidor MCP para Elasticsearch.</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/elastic-mcp-server-typescript-claude#load-the-mcp-server-into-claude-desktop">Carga el servidor MCP en Claude Desktop.</a></p></li><li><p><a href="https://www.elastic.co/search-labs/blog/elastic-mcp-server-typescript-claude#test-it-out">Pruébalo.</a></p></li></ol><h3>Configurar el servidor MCP para Elasticsearch</h3><p>Para comenzar, inicialicemos una aplicación de nodo:</p>npm init -y<p>Esto creará un archivo <code>package.json</code> y, con él, podremos empezar a instalar las dependencias necesarias para esta aplicación.</p>npm install @elastic/elasticsearch @modelcontextprotocol/sdk openai zod &amp;&amp; npm install --save-dev ts-node @types/node typescript<ul><li><p><strong>@elastic/elasticsearch</strong> nos dará acceso a la biblioteca de Elasticsearch para Node.js.</p></li><li><p><strong>@modelcontextprotocol/sdk</strong> proporciona las herramientas básicas para crear y administrar un servidor MCP, registrar herramientas y manejar la comunicación con los clientes de MCP.</p></li><li><p><strong>openAI</strong> permite la interacción con modelos OpenAI para generar resúmenes o respuestas en lenguaje natural.</p></li><li><p><a href="https://zod.dev/"><strong>zod</strong></a>ayuda a definir y validar esquemas estructurados para los datos de entrada y salida en cada herramienta.</p></li></ul><p><code>ts-node</code>, <code>@types/node</code> y <code>typescript</code> se usarán durante el desarrollo para escribir el código y compilar los scripts.</p><h4>Configura los sets de datos</h4><p>Para proporcionar los datos que Claude Desktop puede consultar con nuestro servidor de MCP, utilizaremos un <a href="https://github.com/Delacrobix/typescript-elasticsearch-mcp/blob/main/dataset.json">conjunto de datos de base de conocimiento interna simulado</a>. Así es como se verá un documento de este sets de datos:</p>{
    "id": 5,
    "title": "Logging Standards for Microservices",
    "content": "Consistent logging across microservices helps with debugging and tracing. Use structured JSON logs and include request IDs and timestamps. Avoid logging sensitive information. Centralize logs in Elasticsearch or a similar system. Configure log rotation to prevent storage issues and ensure logs are searchable for at least 30 days.",
    "tags": ["logging", "microservices", "standards"]
}<p>Para cargar los datos, preparamos un script que cree un índice en Elasticsearch y cargue el set de datos en él. Puedes encontrarlo <a href="https://github.com/Delacrobix/typescript-elasticsearch-mcp/blob/main/setup.ts">aquí</a>.</p><h4>Servidor MCP</h4><p>Crea un archivo llamado <a href="https://github.com/Delacrobix/typescript-elasticsearch-mcp/blob/main/index.ts"><code>index.ts</code></a> y agrega el siguiente código para importar las dependencias y gestionar las variables de entorno:</p>// index.ts
import { z } from "zod";
import { Client } from "@elastic/elasticsearch";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import OpenAI from "openai";

const ELASTICSEARCH_ENDPOINT =
  process.env.ELASTICSEARCH_ENDPOINT ?? "http://localhost:9200";
const ELASTICSEARCH_API_KEY = process.env.ELASTICSEARCH_API_KEY ?? "";
const OPENAI_API_KEY = process.env.OPENAI_API_KEY ?? "";
const INDEX = "documents";<p>Además, preparemos a los clientes para que gestionen las llamadas a Elasticsearch y OpenAI:</p>const openai = new OpenAI({
  apiKey: OPENAI_API_KEY,
});

const _client = new Client({
  node: ELASTICSEARCH_ENDPOINT,
  auth: {
    apiKey: ELASTICSEARCH_API_KEY,
  },
});<p>Para hacer nuestra implementación más robusta y asegurar entradas y salidas estructuradas, definiremos esquemas usando <a href="https://zod.dev/"><code>zod</code></a>. Esto nos permite validar los datos en tiempo de ejecución, detectar errores a tiempo y facilitar el procesamiento de las respuestas de la herramienta mediante código:</p>const DocumentSchema = z.object({
  id: z.number(),
  title: z.string(),
  content: z.string(),
  tags: z.array(z.string()),
});

const SearchResultSchema = z.object({
  id: z.number(),
  title: z.string(),
  content: z.string(),
  tags: z.array(z.string()),
  score: z.number(),
});

type Document = z.infer&lt;typeof DocumentSchema&gt;;
type SearchResult = z.infer&lt;typeof SearchResultSchema&gt;;<p>Descubre más sobre las salidas estructuradas <a href="https://www.elastic.co/search-labs/blog/structured-outputs-elasticsearch-guide">aquí</a>.</p><p>Ahora vamos a inicializar el servidor MCP:</p>const server = new McpServer({
  name: "Elasticsearch RAG MCP",
  description:
    "A RAG server using Elasticsearch. Provides tools for document search, result summarization, and source citation.",
  version: "1.0.0",
});<h4>Definición de las herramientas MCP</h4><p>Ahora que ya tenemos todo configurado, podemos empezar a desarrollar las herramientas que ofrecerá nuestro servidor MCP. Este servidor ofrece dos herramientas:</p><ul><li><p><strong><code>search_docs</code></strong><strong>: </strong>Búsquedas de documentos en Elasticsearch mediante la búsqueda de texto.</p></li><li><p><strong><code>summarize_and_cite</code></strong><strong>:</strong> Resume y sintetiza información de documentos previamente recuperados para responder a una pregunta del usuario. Esta herramienta también agrega citas que hacen referencia a los documentos originales.</p></li></ul><p>Juntas, estas herramientas forman un flujo de trabajo simple de “recuperación y resumen”, donde una herramienta busca documentos relevantes y la otra usa esos documentos para generar una respuesta resumida y citada.</p><h4>Formato de respuesta de herramienta</h4><p>Cada herramienta puede aceptar parámetros de entrada arbitrarios, pero debe responder con la siguiente estructura:</p><ul><li><p><strong>Contenido:</strong> esta es la respuesta de la herramienta en un formato no estructurado. Este campo se suele usar para mostrar texto, imágenes, audio, enlaces o contenido incrustado. Para esta aplicación, se utilizará para devolver texto formateado con la información generada por las herramientas.</p></li><li><p><strong>structuredContent: </strong>este es un retorno opcional que se usa para proporcionar los resultados de cada herramienta en un formato estructurado. Esto es útil para fines programáticos. Aunque no se usa en este servidor de MCP, puede ser útil si quieres desarrollar otras herramientas o procesar los resultados mediante programación.</p></li></ul><p>Con esa estructura en mente, comencemos con cada herramienta en detalle.</p><h4>Herramienta Search_docs</h4><p>Esta herramienta realiza una <a href="https://www.elastic.co/docs/solutions/search/full-text">búsqueda de texto completo</a> en el índice de Elasticsearch para recuperar los documentos más relevantes según la consulta del usuario. Destaca los resultados clave y ofrece una visión general rápida con puntuaciones de relevancia.</p>server.registerTool(
  "search_docs",
  {
    title: "Search Documents",
    description:
      "Search for documents in Elasticsearch using full-text search. Returns the most relevant documents with their content, title, tags, and relevance score.",
    inputSchema: {
      query: z
        .string()
        .describe("The search query terms to find relevant documents"),
      max_results: z
        .number()
        .optional()
        .default(5)
        .describe("Maximum number of results to return"),
    },
    outputSchema: {
      results: z.array(SearchResultSchema),
      total: z.number(),
    },
  },
  async ({ query, max_results }) =&gt; {
    if (!query) {
      return {
        content: [
          {
            type: "text",
            text: "Query parameter is required",
          },
        ],
        isError: true,
      };
    }

    try {
      const response = await _client.search({
        index: INDEX,
        size: max_results,
        query: {
          bool: {
            must: [
              {
                multi_match: {
                  query: query,
                  fields: ["title^2", "content", "tags"],
                  fuzziness: "AUTO",
                },
              },
            ],
            should: [
              {
                match_phrase: {
                  title: {
                    query: query,
                    boost: 2,
                  },
                },
              },
            ],
          },
        },
        highlight: {
          fields: {
            title: {},
            content: {},
          },
        },
      });

      const results: SearchResult[] = response.hits.hits.map((hit: any) =&gt; {
        const source = hit._source as Document;

        return {
          id: source.id,
          title: source.title,
          content: source.content,
          tags: source.tags,
          score: hit._score ?? 0,
        };
      });

      const contentText = results
        .map(
          (r, i) =&gt;
            `[${i + 1}] ${r.title} (score: ${r.score.toFixed(
              2,
            )})\n${r.content.substring(0, 200)}...`,
        )
        .join("\n\n");

      const totalHits =
        typeof response.hits.total === "number"
          ? response.hits.total
          : (response.hits.total?.value ?? 0);

      return {
        content: [
          {
            type: "text",
            text: `Found ${results.length} relevant documents:\n\n${contentText}`,
          },
        ],
        structuredContent: {
          results: results,
          total: totalHits,
        },
      };
    } catch (error: any) {
      console.log("Error during search:", error);

      return {
        content: [
          {
            type: "text",
            text: `Error searching documents: ${error.message}`,
          },
        ],
        isError: true,
      };
    }
  }
);<p><em>Configuramos </em><a href="https://www.elastic.co/docs/reference/query-languages/query-dsl/query-dsl-fuzzy-query"><em><code>fuzziness</code></em></a><em><code>: “AUTO”</code></em><em> para que tenga una tolerancia tipográfica variable basada en la longitud del token que se está analizando. También establecemos </em><em><code>title^2</code></em><em> para aumentar la puntuación de los documentos donde se produce la coincidencia en el campo de título.</em></p><h4>herramienta de resumen y cita: summarize_and_cite</h4><p>Esta herramienta genera un resumen basado en los documentos recuperados en la búsqueda anterior. Usa el modelo <code>gpt-4o-mini</code> de OpenAI para sintetizar la información más relevante y responder a la pregunta del usuario para obtener respuestas derivadas directamente de los resultados de búsqueda. Además del resumen, también devuelve metadatos de citas para los documentos fuente utilizados.</p>server.registerTool(
  "summarize_and_cite",
  {
    title: "Summarize and Cite",
    description:
      "Summarize the provided search results to answer a question and return citation metadata for the sources used.",
    inputSchema: {
      results: z
        .array(SearchResultSchema)
        .describe("Array of search results from search_docs"),
      question: z.string().describe("The question to answer"),
      max_length: z
        .number()
        .optional()
        .default(500)
        .describe("Maximum length of the summary in characters"),
      max_docs: z
        .number()
        .optional()
        .default(5)
        .describe("Maximum number of documents to include in the context"),
    },
    outputSchema: {
      summary: z.string(),
      sources_used: z.number(),
      citations: z.array(
        z.object({
          id: z.number(),
          title: z.string(),
          tags: z.array(z.string()),
          relevance_score: z.number(),
        })
      ),
    },
  },
  async ({ results, question, max_length, max_docs }) =&gt; {
    if (!results || results.length === 0 || !question) {
      return {
        content: [
          {
            type: "text",
            text: "Both results and question parameters are required, and results must not be empty",
          },
        ],
        isError: true,
      };
    }

    try {
      const used = results.slice(0, max_docs);

      const context = used
        .map(
          (r: SearchResult, i: number) =&gt;
            `[Document ${i + 1}: ${r.title}]\\n${r.content}`
        )
        .join("\n\n---\n\n");

      // Generate summary with OpenAI
      const completion = await openai.chat.completions.create({
        model: "gpt-4o-mini",
        messages: [
          {
            role: "system",
            content:
              "You are a helpful assistant that answers questions based on provided documents. Synthesize information from the documents to answer the user's question accurately and concisely. If the documents don't contain relevant information, say so.",
          },
          {
            role: "user",
            content: `Question: ${question}\\n\\nRelevant Documents:\\n${context}`,
          },
        ],
        max_tokens: Math.min(Math.ceil(max_length / 4), 1000),
        temperature: 0.3,
      });

      const summaryText =
        completion.choices[0]?.message?.content ?? "No summary generated.";

      const citations = used.map((r: SearchResult) =&gt; ({
        id: r.id,
        title: r.title,
        tags: r.tags,
        relevance_score: r.score,
      }));

      const citationText = citations
        .map(
          (c: any, i: number) =&gt;
            `[${i + 1}] ID: ${c.id}, Title: "${c.title}", Tags: ${c.tags.join(
              ", ",
            )}, Score: ${c.relevance_score.toFixed(2)}`,
        )
        .join("\n");

      const combinedText = `Summary:\\n\\n${summaryText}\\n\\nSources used (${citations.length}):\\n\\n${citationText}`;

      return {
        content: [
          {
            type: "text",
            text: combinedText,
          },
        ],
        structuredContent: {
          summary: summaryText,
          sources_used: citations.length,
          citations: citations,
        },
      };
    } catch (error: any) {
      return {
        content: [
          {
            type: "text",
            text: `Error generating summary and citations: ${error.message}`,
          },
        ],
        isError: true,
      };
    }
  }
);<p>Finalmente, necesitamos iniciar el servidor a través de <a href="https://github.com/modelcontextprotocol/typescript-sdk?tab=readme-ov-file#stdio">stdio</a>. Esto significa que el cliente MCP se comunicará con nuestro servidor leyendo y escribiendo en sus flujos estándar de entrada y salida. stdio es la opción de transporte más sencilla y funciona bien para servidores MCP locales lanzados como subprocesos por el cliente. Agrega el siguiente código al final del archivo:</p>const transport = new StdioServerTransport();
server.connect(transport);<p>Ahora compila el proyecto usando el siguiente comando:</p>npx tsc index.ts --target ES2022 --module node16 --moduleResolution node16 --outDir ./dist --strict --esModuleInterop<p>Esto creará una carpeta <code>dist</code> y, dentro de ella, un archivo <code>index.js</code>.</p><h3>Carga el servidor MCP en Claude Desktop</h3><p>Sigue <a href="https://modelcontextprotocol.io/docs/develop/connect-local-servers">esta guía</a> para configurar el servidor MCP con Claude Desktop. En el archivo de configuración de Claude, tienes que establecer los siguientes valores:</p>{
  "mcpServers": {
    "elasticsearch-rag-mcp": {
      "command": "node",
      "args": [   "/Users/user-name/app-dir/dist/index.js"
      ],
      "env": {
        "ELASTICSEARCH_ENDPOINT": "your-endpoint-here",
        "ELASTICSEARCH_API_KEY": "your-api-key-here",
        "OPENAI_API_KEY": "your-openai-key-here"
      }
    }
  }
}<p>El valor <code>args</code> debe apuntar al archivo compilado en la carpeta <code>dist</code>. También es necesario configurar las variables de entorno en el archivo de configuración con los mismos nombres exactos definidos en el código.</p><h3>Pruébalo</h3><p>Antes de ejecutar cada herramienta, haz clic en <strong>Búsqueda y herramientas</strong> para asegurarte de que las herramientas estén habilitadas. Aquí también puedes habilitar o deshabilitar cada una de ellas:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt395a7337021f9820/6a170c1c67045bb74d45c228/172981c2a54adabc70d5819013c3007670935605-1999x1002.png" alt="Página de Claude Sonnet 4.5, con la nota: &quot;Buenas tardes, Jeff.&quot; ¿Cómo puedo ayudarte hoy?&quot;" /><p>Finalmente, probemos el servidor MCP desde el chat de Claude Desktop y comencemos a hacer preguntas:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf4ac458dc0206271/6a170c1e66c4f91328f8c072/03654c0f8c53c714f801fba8b25747071179209b-1999x1353.png" alt="Solicitud de búsqueda del usuario en el chat de Claude Desktop para documentos sobre métodos de autenticación y control de acceso basado en roles, junto con las respuestas de Claude." /><p>Para la consulta<strong>"Buscar documentos sobre métodos de autenticación y control de acceso basado en roles"</strong>, se ejecuta la herramienta <code>search_docs</code> y arroja los siguientes resultados:</p>Most Relevant Documents:
Access Control and Role Management (highest relevance) - This document covers role-based access control (RBAC) principles, including ensuring users only have necessary permissions, regular auditing of user roles, revoking inactive accounts, and implementing just-in-time access for sensitive operations.
User Authentication with OAuth 2.0 - This document explains OAuth 2.0 authentication, which enables secure delegated access without credential sharing. It covers configuring identity providers, token management with limited scope and lifetime, and secure storage of refresh tokens.
Container Security Guidelines - While primarily about container security, this document touches on access control aspects like running containers as non-root users and avoiding embedded credentials.
Incident Response Playbook - This mentions role assignment during incidents (incident commander, communications lead, etc.), which relates to access control in emergency scenarios.
Logging Standards for Microservices - This document includes guidance on avoiding logging sensitive information, which is relevant to authentication security.<p>La respuesta es: “¡Genial! Encontré 5 documentos relevantes sobre métodos de autenticación y control de acceso basado en roles. Esto es lo que se encontró:”</p><p>La llamada a la herramienta devuelve los documentos de origen como parte de su carga útil de respuesta, que luego se utilizan para generar citas.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbaf48a9468770ce2/6a170c21509168bffee1bb14/25ff4c7e9563d99752f95540dafdc7fd211a66e3-800x530.gif" alt="Página de Claude 4.5 Sonnet con respuestas desplazables que incluyen los cinco documentos relevantes sobre los métodos de autenticación y el control de acceso basado en roles." /><p>También es posible encadenar varias herramientas en una sola interacción. En este caso, Claude Desktop analiza la pregunta del usuario y determina que primero debe llamar a <code>search_docs</code> para recuperar documentos relevantes y luego pasar esos resultados a <code>summarize_and_cite</code> para generar la respuesta final, todo eso sin requerir indicaciones separadas del usuario:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta46ff45274e64192/6a170c230c4857a91501aac1/e6a8a46acb4236e77058f18bcd2f0737b5882c05-1999x1101.png" alt="Chat de escritorio de Claude con la nota: &quot;Jeff regresa&quot;, además de una nueva pregunta de un usuario: &quot;¿Cuáles son las principales recomendaciones para mejorar la autenticación y el control de acceso en todos nuestros sistemas? Incluye referencias.&quot;" /><p>En este caso, para la búsqueda: "<strong>¿Cuáles son las principales recomendaciones para mejorar la autenticación y el control de acceso en todos nuestros sistemas? Incluye referencias.</strong>", obtuvimos los siguientes resultados:</p>Based on the documentation, here are the main recommendations to improve authentication and access control across your systems:

Key Recommendations

1. Implement Role-Based Access Control (RBAC)
Ensure users have only the permissions necessary for their job functions. This principle of least privilege minimizes security risks by limiting access to what's truly needed. [1]

2. Regular Access Audits
Conduct regular audits of user roles and promptly revoke access for inactive accounts. This helps prevent unauthorized access from dormant or compromised accounts. [1]

3. Just-in-Time (JIT) Access
Implement just-in-time access for sensitive operations, where permissions are granted only when needed and for a limited duration. This reduces the attack surface by minimizing standing privileges. [1]

4. OAuth 2.0 for Secure Authentication
Use OAuth 2.0 to enable secure delegated access without sharing user credentials. This protocol allows third-party applications to access resources on behalf of users without exposing passwords. [2]

5. Token Security and Management
Configure your identity provider to issue tokens with limited scope and lifetime

Store refresh tokens securely
Validate access tokens consistently to maintain security [2]
References

Access Control and Role Management (Tags: security, access-control)
User Authentication with OAuth 2.0 (Tags: authentication, oauth)
These recommendations work together to create a defense-in-depth approach, where multiple security layers protect your systems from unauthorized access.<p>Al igual que en el paso anterior, podemos ver la respuesta de cada herramienta a esta pregunta:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8f633c518e708a99/6a170c25ab7f082991db9ed6/cb606d356b2f7d5e4878a5eff71bc881869ac0ee-800x585.gif" alt="Página de chat de Claude Desktop con texto desplazado que incluye la respuesta de cada herramienta a la pregunta: “¿Cuáles son las principales recomendaciones para mejorar la autenticación y el control de acceso en todos nuestros sistemas? Incluye referencias.”" /><p><em>Nota: si aparece un submenú que pregunta si apruebas el uso de cada herramienta, selecciona </em><em><strong>Permitir siempre</strong></em><em> o </em><em><strong>Permitir una vez</strong></em><em>.</em></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6627ee0bff1862df/6a170c266f7f040f6f91488c/aea942ba9b0037526ea215bec65690f1a5c3099c-1522x250.png" alt="Claude Desktop te ofrece las opciones &quot;Permitir siempre&quot; y &quot;Permitir una sola vez&quot; para que el usuario elija." /><h2>Conclusión</h2><p>Los servidores MCP representan un paso significativo hacia la estandarización de las herramientas LLM para aplicaciones tanto locales como remotas. Aunque la compatibilidad total todavía está en proceso, nos estamos moviendo rápido en esa dirección.</p><p>En este artículo, aprendimos cómo construir un servidor MCP personalizado en TypeScript que conecta Elasticsearch con aplicaciones impulsadas por modelos LLM. Nuestro servidor expone dos herramientas: <code>search_docs</code> para recuperar documentos relevantes con Query DSL y <code>summarize_and_cite</code> para generar resúmenes con citas a través de modelos de OpenAI y Claude Desktop como client UI.</p><p>El futuro de la compatibilidad entre los distintos proveedores de clientes y servidores parece prometedor. Los próximos pasos consisten en agregar más funcionalidades y flexibilidad a tu agente. Hay un <a href="https://www.elastic.co/search-labs/blog/llm-functions-elasticsearch-intelligent-query">artículo</a> práctico sobre cómo puedes agregar parámetros a tus consultas a través de plantillas de búsqueda para ganar precisión y flexibilidad.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elastic-mcp-server-typescript-claude</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elastic-mcp-server-typescript-claude</guid>
    <category><![CDATA[AI agéntica]]></category>
    <category><![CDATA[Integraciones]]></category>
    <dc:creator><![CDATA[Jeffrey Rengifo]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5600198cb47666a5/6a170c28509168ce3ae1bb18/0bb24c05fff391f42070c2883182ea6fe9cb9680-1280x720.png" length="0" type="image/png"/>
    <pubDate>Fri, 27 Mar 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[La herramienta de shell no es una solución mágica para la ingeniería de contexto]]></title>
    <description><![CDATA[Aprenda qué herramientas de recuperación de contexto existen para la ingeniería de contexto, cómo funcionan y sus compensaciones.]]></description>
    <content:encoded><![CDATA[<p>Las herramientas más importantes con las que cuenta un agente son las herramientas de búsqueda que puede usar para construir su propio contexto. Publicaciones recientes de <a href="https://www.llamaindex.ai/blog/files-are-all-you-need">LlamaIndex</a> y <a href="https://x.com/hwchase17/status/2011814697889316930">LangChain</a> han desatado una discusión: <em>¿Son una herramienta shell y un sistema de archivos todo lo que un agente necesita para la ingeniería de contexto? </em>Desafortunadamente, la discusión se desvió rápidamente hacia el enfoque equivocado: filesystem versus base de datos.</p><p>Esta publicación vuelve a centrarse en la pregunta:<em>¿Cuáles son las interfaces de búsqueda adecuadas que necesita un agente para construir su propio contexto?</em> Primero, cubre las disyuntivas entre las herramientas de shell y las herramientas de base de datos especiales. A partir de ahí, te ofrece un marco de trabajo práctico para encontrar las interfaces adecuadas a las necesidades de tu agente.</p><h2>¿Qué significa realmente "construir contexto" para un agente?</h2><p>En las primeras <a href="https://www.elastic.co/what-is/retrieval-augmented-generation">pipelines de retrieval augmented generation (RAG)</a>, el desarrollador diseñó un pipeline de recuperación fija y el modelo de lenguaje grande (LLM) era un receptor pasivo del contexto. Esta era una limitación fundamental: el contexto se recuperaba en cada consulta, fuera o no necesario, sin verificar que realmente ayudara.</p><p>Con el cambio a la RAG agéntica, los agentes ahora tienen acceso a un conjunto de herramientas de búsqueda para crear su propio contexto. Por ejemplo, tanto Claude Code [1] como Cursor [2] permiten que el agente elija entre diferentes herramientas de búsqueda e incluso las combine para consultas encadenadas, dependiendo de lo que la tarea realmente requiera.</p><h2>¿Qué interfaces de búsqueda existen para la ingeniería del contexto?</h2><p>El contexto puede estar en diferentes lugares, como en la web, en un sistema de archivos local o en una base de datos. Un agente puede interactuar con cada una de estas fuentes de datos fuera de contexto mediante diferentes herramientas:</p><ul><li><p>Las <strong>herramientas de shell</strong> pueden ejecutar comandos de shell y tener acceso al sistema de archivos local. Algunos ejemplos de herramientas de shell integradas son <a href="https://platform.claude.com/docs/en/agents-and-tools/tool-use/bash-tool">la herramienta bash de Claude API</a>, <a href="https://docs.openclaw.ai/tools/exec">la herramienta ejecutiva de OpenClaw</a> y <a href="https://docs.langchain.com/oss/python/integrations/tools/bash">la herramienta de shell de LangChain</a>.</p></li><li><p>Las <strong>herramientas de base de datos especiales,</strong> como las herramientas de un servidor Model Context Protocol (MCP) (p. ej., el <a href="https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/mcp-server">servidor MCP de Elastic Agent Builder)</a> o las herramientas personalizadas (p. ej., <code>run_esql(query)</code> o <code>db_list_index()</code>), pueden consultar bases de datos.</p></li><li><p><strong>Las herramientas especiales de búsqueda de archivos</strong> pueden buscar y leer archivos locales (o subidos) (sin acceso completo al shell). Algunos ejemplos de herramientas de búsqueda de archivos integradas son <a href="https://ai.google.dev/gemini-api/docs/file-search">Herramienta de búsqueda de archivos de Gemini API</a> o <a href="https://developers.openai.com/api/docs/guides/tools-file-search">Herramienta de búsqueda de archivos de OpenAI</a>.</p></li><li><p>Las <strong>herramientas de búsqueda web</strong> pueden recuperar información de la web.</p></li><li><p>Las <strong>herramientas de memoria</strong> almacenan y recuperan de la memoria a largo plazo (independientemente de cómo se almacene).</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2c5d083815149773/6a170acb964cea61a108bb80/115f20c8ded259e508f51524b2c06bdc702d70ab-1999x1050.png" alt="Diagrama que muestra cómo un agente utiliza diferentes herramientas de recuperación de contexto para acceder a archivos locales, datos propios, la web y la memoria a largo plazo." /><p>Como puedes ver, la herramienta shell es versátil y se puede usar para recuperar contexto de diferentes fuentes de datos, incluyendo:</p><ul><li><p><strong>Sistema de archivos:</strong> el agente explora la estructura de directorios (ls, find), busca contenido relevante (grep, cat) y repite hasta que ha construido suficiente contexto.</p></li><li><p><strong>Base de datos:</strong> el agente puede usar herramientas de interfaz de línea de comandos (CLI) para bases de datos (por ejemplo, <a href="https://www.elastic.co/docs/reference/query-languages/sql/sql-cli"><code>elasticsearch-sql-cli</code></a>), llamar al HTTP de la API mediante curl o ejecutar scripts, lo cual resulta especialmente útil en combinación con las habilidades del agente, que son ejemplos reutilizables y documentados que se incorporan al contexto del agente para guiar el uso correcto de las herramientas (por ejemplo, <a href="https://github.com/elastic/agent-skills">Elastic Agent Skills para Elasticsearch</a>).</p></li><li><p><strong>Web: </strong>el agente puede ejecutar búsquedas web mediante un comando curl a través de la API de un proveedor de búsqueda.</p></li></ul><p>Sin embargo, la herramienta de shell proporciona acceso directo al sistema y, por lo tanto, requiere medidas de seguridad, como ejecutarse en un entorno sandbox aislado y el logging de todos los comandos ejecutados.</p><h2>Cuándo deberías usar ciertas interfaces de búsqueda</h2><p>La interfaz de búsqueda adecuada depende de tus datos, tus patrones de consulta y tu caso de uso. Esta sección sirve como un punto de partida práctico.</p><h3>Los sistemas de archivos no hacen que las bases de datos sean obsoletas.</h3><p>La discusión entre sistemas de archivos y bases de datos no es sobre la capa de almacenamiento. Por ejemplo, LangChain explica que <a href="https://x.com/hwchase17/status/2011814697889316930">su sistema de memoria</a> en realidad no almacena la memoria en un verdadero sistema de archivos. En su lugar, almacena la memoria en una base de datos y la <em>representa</em> como un conjunto de archivos para el agente [3].</p><p>Los sistemas de archivos son una opción natural para casos de uso nativos de archivos, como los agentes de codificación. También funcionan bien como bloc de notas temporal o memoria de trabajo, y en situaciones con un solo usuario o un solo agente en las que la concurrencia no es un problema. En estos casos, un sistema de archivos físico o representar los datos como un sistema de archivos te da flexibilidad antes de comprometerte con una interfaz diseñada específicamente para ello.</p><p>Pero el almacenamiento en sistemas de archivos tiene desventajas reales, como una concurrencia limitada, la aplicación manual de esquemas y las transacciones atómicas. Estos se vuelven más evidentes cuando tu aplicación necesita escalar o pasar a un escenario de múltiples agentes. Cualquiera que ignore estas desventajas está condenado a <a href="https://dx.tips/oops-database">reinventar dolorosamente bases de datos peores</a> sin las décadas de ingeniería detrás de la seguridad de transacciones o el control de acceso que las bases de datos de producción ya proporcionan. Además, en la mayoría de contextos empresariales, no eliges si usar una base de datos porque ya está ahí, almacenando datos críticos para el negocio.</p><h3>Herramienta de shell + sistema de archivos</h3><p>Una herramienta shell es el punto de partida natural para la búsqueda en sistemas de archivos. En la actualidad, los agentes de codificación están impulsando muchos avances en este campo. Como trabajan con código en archivos locales, son, por naturaleza, casos de uso que implican un gran volumen de archivos. Por lo tanto, los LLM se ajustan en la etapa posterior al entrenamiento para tareas de codificación. Es por eso que muchos LLMs no solo son buenos para escribir código, sino también para usar comandos de shell y navegar por sistemas de archivos.</p><p>Usar una herramienta de shell con CLI integradas, como <code>ls</code> y <code>grep</code>, para encontrar archivos es efectivo. Con grep, una consulta como "Encontrar todos los archivos que importan <code>matplotlib</code>" es rápida, precisa y económica. Pero cuando el agente necesita manejar consultas conceptuales, como "¿Cómo maneja nuestra app la falla de autenticación?", la coincidencia de patrones con grep puede alcanzar un límite rápidamente. Han surgido varias alternativas que incorporan capacidades de búsqueda semántica a la línea de comandos para cubrir esta carencia, incluidas <a href="https://github.com/jina-ai/jina-grep-cli"><code>jina-grep</code></a>.</p><p>Sin embargo, grep y muchas de sus alternativas de búsqueda semántica se ejecutan en O(n) sobre el corpus. Para casos de uso sobre bases de código, esto podría estar bien. Sin embargo, si tus datos crecen, la latencia se notará. En este caso, un almacén de datos indexado se vuelve necesario para mantener el rendimiento.</p><h3>Herramienta de shell + base de datos</h3><p>Otra forma de agregar más capacidades de búsqueda, como la búsqueda semántica o híbrida, a tus datos es almacenarlos en una base de datos, como hace Cursor, por ejemplo. Además, cuando los datos requieren uniones relacionales o agregaciones complejas, una interfaz de base de datos es imprescindible.</p><p>Cuando los datos se almacenan en una base de datos en lugar de en el sistema de archivos, una herramienta de shell puede servir como una interfaz ligera de base de datos para ciertos casos de uso. Si tus consultas son lo suficientemente simples para una CLI o una llamada curl, una herramienta de base de datos especial podría añadir una complejidad innecesaria.</p><p>Este enfoque también es adecuado en las etapas iniciales de exploración, cuando aún no sabes qué patrones de consulta desarrollará tu agente. En este caso, Agent Skills puede darle al agente suficiente estructura para consultar correctamente sin comprometerse con una herramienta específica. Sin embargo, cuando el agente requiere muchas iteraciones para encontrar la forma correcta de consultar en la base de datos para tareas repetidas, la sobrecarga de tokens de usar una herramienta de línea de comandos como interfaz ya no justifica el beneficio de la simplicidad de evitar una herramienta adicional.</p><h3>Herramienta especial de base de datos</h3><p>Especialmente cuando los patrones de consulta repetidos son estructurados o analíticos, se hacen necesarias herramientas de base de datos especiales. Una <a href="https://vercel.com/blog/testing-if-bash-is-all-you-need">publicación de blog de Vercel y Braintrust</a> comparó a los agentes con diferentes conjuntos de herramientas de búsqueda para tareas de recuperación del mundo real en lugar de datos estructurados, como tickets de atención al cliente y transcripciones de llamadas de ventas (por ejemplo, “¿Cuántos problemas abiertos mencionan 'seguridad'?" o "¿Encontraste problemas en los que alguien reportó un error y luego alguien envió un PR diciendo que lo había arreglado?") [4].</p><p>Los agentes con herramientas de base de datos especiales utilizaron menos tokens, fueron más rápidos y cometieron menos errores que los agentes con solo una herramienta de shell y un sistema de archivos. La conclusión es que las herramientas de bases de datos directas son la opción correcta cuando la consulta requiere razonamiento analítico sobre datos semiestructurados.</p><h3>Combinar interfaces de búsqueda</h3><p>Ninguna interfaz de búsqueda gestiona bien todas las consultas. Por ejemplo, Cursor combina herramientas de shell (para búsquedas con grep) y herramientas de búsqueda semántica, y permite que el agente seleccione la herramienta correcta según el mensaje del usuario. Informa que el agente elige grep para hacer coincidir símbolos o textos específicos, búsqueda semántica para preguntas conceptuales o de comportamiento, y ambos para tareas exploratorias.</p><p>El experimento de Vercel presenta el mismo reporte: su agente híbrido con acceso tanto a una herramienta de shell como a una herramienta dedicada para bases de datos logró el mejor rendimiento de todos los agentes probados al usar primero las herramientas dedicadas para bases de datos y luego verificar los resultados mediante búsquedas con grep en el sistema de archivos. Sin embargo, este enfoque utiliza más tokens y tiempo para razonar sobre la elección de herramientas y la verificación.</p><p>El patrón en ambos ejemplos es el mismo: La combinación es superior a cualquier interfaz individual, pero conlleva un costo y una latencia adicionales.</p><h2>Recomendaciones prácticas para encontrar el conjunto adecuado de herramientas</h2><p>El conjunto adecuado de interfaces de búsqueda es pequeño, intencionado y específico para los patrones de consulta reales de tu agente. Las mejores prácticas actuales son tener un agente con la menor cantidad de herramientas posible en lugar de tener un agente con cientos de herramientas MCP. Esto se debe a que la desventaja de exponer todas las herramientas posibles de antemano es que infla la ventana de contexto y confunde al agente sobre qué herramienta usar realmente. Por ejemplo, se dice que Claude Code solo tiene unas 20 herramientas.</p><p>En cambio, la idea de la divulgación progresiva es comenzar con un conjunto mínimo de herramientas y dejar que el agente descubra capacidades adicionales solo cuando sea necesario. Investigaciones de Anthropic [5] y Cursor [6] demostraron que este enfoque genera un ahorro de tokens entre el 47%–85%. Claude Code, por ejemplo, implementa esto directamente, lo que permite al agente descubrir de forma incremental cómo consultar una API o una base de datos, sin que ese conocimiento consuma contexto en cada llamada al LLM.</p><p>Una vez que te familiarices con los patrones de búsqueda del agente, puedes volver a revisar el conjunto de herramientas de búsqueda a las que el agente tiene acceso de forma predeterminada. Una forma útil de pensar en este compromiso es el <a href="https://www.elastic.co/search-labs/blog/database-retrieval-tools-context-engineering#building-the-right-database-retrieval-tools-%5C(%E2%80%9Clow-floor,-high-ceiling%E2%80%9D%5C">"principio de piso bajo, techo alto"</a> para decidir qué herramientas son las adecuadas. Las herramientas de alto nivel no limitan el potencial del agente. Por ejemplo, una herramienta de shell versátil permite al agente escribir consultas de base de datos completas, incluidas las ambiguas, pero a costa de una mayor sobrecarga de razonamiento, latencia más alta y menor confiabilidad.</p><p>Las herramientas de bajo umbral son todo lo contrario. Son herramientas especializadas que encapsulan búsquedas específicas y son inmediatamente accesibles para el agente con una mínima sobrecarga de razonamiento, ofreciendo menor costo y mayor confiabilidad. No obstante, requieren ingeniería previa, no pueden cubrir cada posible consulta y pueden dificultar que el agente elija la herramienta correcta.</p><p>Piensa en cada herramienta en un espectro: las herramientas de bajo nivel son fáciles de usar correctamente para el agente, pero tienen un alcance limitado. Las herramientas de alto potencial son versátiles pero requieren más razonamiento para usarlas bien.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt72deecc6781e3499/6a170acd5091682f4fe1baba/e6d1b973be4b0a0a25c99c74f02a47e98395a3f7-1200x630.png" alt="Diagrama que compara tres enfoques de diseño de agentes (piso alto/techo alto, piso bajo/techo bajo y piso bajo/techo alto), mostrando cómo diferentes estrategias de herramientas afectan la forma en que los agentes manejan consultas ambiguas, versátiles y predecibles." /><p>La mayoría de los agentes necesitan una combinación de diferentes herramientas de búsqueda. Pero cada herramienta necesita aportar algo. Recomendamos comenzar con una herramienta de búsqueda multiuso (por ejemplo, una herramienta <code>search_database()</code> o una herramienta de shell). Luego, reutiliza los registros de comandos que ya conservas por motivos de seguridad para rastrear lo que realmente hace tu agente, incluidas las llamadas a herramientas, los reintentos y el número de llamadas por consulta de usuario. Y, cuando ves que un patrón de consulta se repite o falla, esa es la señal para construir una herramienta especialmente diseñada para ello.</p><h2>Resumen</h2><p>El debate entre sistema de archivos y base de datos distrae de la pregunta real que los ingenieros deben hacerse: <em>¿Cuáles son las interfaces de búsqueda adecuadas que un agente necesita para construir su propio contexto?</em> La respuesta es muy probable, <em>no una sola</em>.</p><p>Una herramienta de shell es una herramienta versátil para interactuar con diferentes fuentes fuera de contexto y, por lo tanto, un buen punto de partida. Sin embargo, resulta menos eficiente y precisa para casos de uso con consultas analíticas estructuradas que las herramientas de bases de datos especializadas.</p><p>El objetivo es encontrar el conjunto mínimo de herramientas de búsqueda que manejen bien los patrones reales de consulta de tu agente. Empieza con una herramienta de shell y registra lo que realmente hace tu agente. Cuando veas un patrón de consulta que se repite y falla, es momento de diseñar herramientas especializadas.</p><h2>Referencias</h2><p>1. Thariq (Anthropic). <a href="https://x.com/trq212/status/2027463795355095314">Lessons from Building Claude Code: Seeing like an Agent</a> (2026).</p><p>2. Cursor: Documentation. <a href="https://cursor.com/docs/agent/tools/search">Semantic &amp; agentic search</a> (2026).</p><p>3. Harrison Chase (LangChain). <a href="https://x.com/hwchase17/status/2011814697889316930">Cómo construimos el sistema de memoria de Agent Builder</a> (2026).</p><p>4. Ankur Goyal (Braintrust) y Andrew Qu (Vercel). <a href="https://vercel.com/blog/testing-if-bash-is-all-you-need">Testing if "bash is all you need"</a> (2026).</p><p>5. Anthropic. <a href="https://www.anthropic.com/engineering/advanced-tool-use">Introducing advanced tool use on the Claude Developer Platform</a> (2025).</p><p>6. Cursor. <a href="https://cursor.com/blog/dynamic-context-discovery">Dynamic context discovery</a> (2026).</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/search-tools-context-engineering</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/search-tools-context-engineering</guid>
    <category><![CDATA[AI agéntica]]></category>
    <dc:creator><![CDATA[Leonie Monigatti]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1b9bbbff55c09fa4/6a170acecdacbff1167d29fd/f91e4d07915ba7bf3b7abf15fac8fab3350f7df2-1280x720.png" length="0" type="image/png"/>
    <pubDate>Wed, 25 Mar 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Uso de la API de inferencia de Elasticsearch junto con modelos de Hugging Face]]></title>
    <description><![CDATA[Aprende a conectar Elasticsearch a modelos de Hugging Face usando endpoints de inferencia y crea un sistema multilingüe de recomendación de blogs con búsqueda semántica y finalización de chat.]]></description>
    <content:encoded><![CDATA[<p>En actualizaciones recientes, Elasticsearch introdujo una integración nativa para conectar a modelos hospedados en el <a href="https://endpoints.huggingface.co/">servicio de inferencia Hugging Face</a>. En esta publicación, veremos cómo configurar esta integración y realizar inferencias mediante llamadas a la API sencillas utilizando un modelo de lenguaje grande (LLM). Usaremos <a href="https://huggingface.co/HuggingFaceTB/SmolLM3-3B">SmolLM3-3B</a>, un modelo ligero de propósito general con un buen equilibrio entre el uso de recursos y la calidad de las respuestas.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9094997548bd70f8/6a170d6a839dfa0ad6dcff54/7ddadf1976421a860a7d62087239adb9150d808b-1999x1388.png" alt="Diagrama de dispersión que muestra varios modelos de lenguaje pequeños graficados por tamaño del modelo (en miles de millones de parámetros) en el eje X y tasa de victorias (porcentaje) en el eje Y. SmolLM3‑3B aparece cerca de la parte superior de la tendencia de eficiencia, con una tasa de victorias más alta que otros modelos de tamaño similar." /><h2>Requisitos previos</h2><ul><li><p><strong>Elasticsearch 9.3 o Elastic Cloud Serverless: </strong>puedes crear un despliegue en la cloud siguiendo <a href="https://www.elastic.co/search-labs/tutorials/install-elasticsearch/elastic-cloud">estas instrucciones</a>, o bien puedes utilizar la guía de inicio rápido de <a href="https://www.elastic.co/docs/deploy-manage/deploy/self-managed/local-development-installation-quickstart#local-dev-quick-start"><code>start-local</code></a>.</p></li><li><p><strong>Python 3.12: </strong>Descarga Python <a href="https://www.python.org/">aquí</a>.</p></li><li><p><a href="https://huggingface.co/docs/hub/en/security-tokens">Token de acceso </a><strong>Hugging Face</strong>.</p></li></ul><h2>Finalización de chat usando un endpoint de inferencia de Hugging Face</h2><p>Primero, vamos a crear un ejemplo práctico que conecte Elasticsearch con un <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-inference-put">endpoint de inferencia</a> de Hugging Face para generar recomendaciones basadas en IA a partir de una colección de publicaciones de blog. Para la base de conocimientos de la app, usaremos un set de datos de artículos del blog de la compañía, que contiene información valiosa pero a menudo difícil de navegar.</p><p>Con este endpoint, <a href="https://www.elastic.co/docs/solutions/search/semantic-search">la búsqueda semántica</a> recupera los artículos más relevantes para una consulta dada, y un LLM de Hugging Face genera recomendaciones breves y contextuales basadas en esos resultados.</p><p>Echemos un vistazo a una visión general de alto nivel del flujo de información que vamos a construir:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf217b7b7db4e1e6c/6a170d6ca929cf8022ae0a3b/1dfbc2323438feaaa42e13ab242dd1f7166f74aa-1200x676.png" alt="Diagrama de flujo que muestra un índice de Elasticsearch que envía resultados de búsqueda semántica a un endpoint de inferencia, el cual devuelve recomendaciones de artículos." /><p>En este artículo, probaremos la <strong>capacidad de SmolLM3-3B </strong>paracombinar su tamaño compacto con fuertes capacidades multilingües de razonamiento y llamadas a herramientas. A partir de una búsqueda, enviaremos todo el contenido correspondiente (en inglés y español) al LLM para generar una lista de artículos recomendados con una descripción personalizada basada en la búsqueda y los resultados.</p><p>Así podría ser la UI de un sitio web de artículos con un sistema de generación de recomendaciones basado en inteligencia artificial.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt20e69b9a06fecd65/6a170d6e839dfa6f97dcff58/8d3b86b212f28ff279f2da67a33e6134039f0e4e-1999x949.png" alt="UI de un sitio web de artículos con un sistema de recomendaciones basado en IA; se incluyen tres ejemplos, con el texto en inglés y los títulos en inglés o en español." /><p>Puedes encontrar la implementación completa de esta aplicación en el <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/elasticsearch-inference-api-and-hugging-face/notebook.ipynb">cuaderno</a> adjunto.</p><h3>Configurar endpoints de inferencia de Elasticsearch</h3><p>Para usar el <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-inference-put-hugging-face">endpoint de inferencia Elasticsearch Hugging Face</a>, necesitamos dos elementos importantes: una clave API de Hugging Face y una URL del endpoint de Hugging Face en funcionamiento. Debería verse así:</p>PUT _inference/chat_completions/hugging-face-smollm3-3b
{
    "service": "hugging_face",
    "service_settings": {
        "api_key": "hugging-face-access-token", 
        "url": "url-endpoint" 
    }
}<p>El endpoint de inferencia Hugging Face en Elasticsearch admite diferentes tipos de tareas: <code>text_embedding</code>, <code>completion</code>, <code>chat_completion</code> y <code>rerank</code>. En esta publicación de blog, usamos <code>chat_completion</code> porque necesitamos que el modelo genere recomendaciones conversacionales basadas en los resultados de búsqueda y una solicitud del sistema. Este endpoint nos permite realizar finalizaciones de chat directamente desde Elasticsearch de una manera sencilla empleando la API de Elasticsearch:</p>POST _inference/chat_completion/hugging-face-smollm3-3b/_stream
{
  "messages": [
      { "role": "user", "content": "&lt;user prompt&gt;" }
  ]
}<p>Esto servirá como núcleo de la aplicación, recibirá la indicación y los resultados de búsqueda que pasarán por el modelo. Ya abordamos la teoría, ahora comencemos a implementar la aplicación.</p><h4>Configuración de un endpoint de inferencia en Hugging Face</h4><p>Para desplegar el modelo de Hugging Face, vamos a usar <a href="https://huggingface.co/inference-endpoints/dedicated">despliegues con un clic de Hugging Face</a>, un servicio fácil y rápido para desplegar endpoints de modelos. Ten en cuenta que este es un servicio de pago y que su uso puede generar costos adicionales. En este paso se creará la instancia del modelo que se usará para generar las recomendaciones de artículos.</p><p>Puedes elegir un modelo del catálogo de un clic:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta7bdfa43d6766324/6a170d6fb339d59e5476a039/b816e9fba1fe172687bf58f5143fb1f838c1077f-549x331.png" alt="Vista de la interfaz de un catálogo de modelos filtrado por “smoll3”, que muestra un modelo llamado “smollm3‑3b” con generación de texto, vLLM, GPU 1× NVIDIA L4 y un precio de lista de $0.8, además de una nota que sugiere ampliar la búsqueda a todos los modelos de Hugging Face." /><p>Vamos a elegir el modelo <strong>SmolLM3-3B</strong>:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdb0a2e6ffd7deb20/6a170d710c48574b7401aafc/610d3aba0429f3666c2df3616d513eb6a4397c0c-502x478.png" alt="Interfaz para crear un endpoint para el modelo SmolLM3‑3B, muestra el nombre del modelo, una nota de &quot;verificado por Hugging Face&quot;, un campo de nombre de endpoint, un costo de $0.80 por hora por réplica en ejecución, una opción de cURL y un botón de &quot;Crear Endpoint&quot;." /><p>Desde aquí, copia la URL del endpoint de Hugging Face:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt25714021711ed6ff/6a170d72c1e8a54853f88336/025094ddb2cfbd1f0f216a5ec4e119b0f4fa2c42-646x328.png" alt="Vista del dashboard de un endpoint de inferencia de Hugging Face llamado “smollm3‑3b‑pnz”, que muestra un estado de ejecución verde, una réplica activa, cero solicitudes en la última hora, pestañas de navegación y la URL del endpoint." /><p>Como se menciona en la <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-inference-put-hugging-face">documentación de endpoints de inferencia Hugging Face</a> de Elasticsearch, la generación de texto requiere un modelo compatible con la API de OpenAI. Por esa razón, necesitamos anexar la ruta secundaria <code>/v1/chat/completions</code> a la URL del endpoint de Hugging Face. El resultado final se verá así:</p>https://j2g31h0futopfkli.us-east-1.aws.endpoints.huggingface.cloud/v1/chat/completions<p>Ahora que está todo listo, podemos empezar a programar en un cuaderno de Python.</p><h4>Generando clave API de Hugging Face</h4><p>Crea una <a href="https://huggingface.co/join">cuenta en Hugging Face</a> y obtén un token de API siguiendo <a href="https://huggingface.co/docs/hub/en/security-tokens#user-access-tokens">estas instrucciones</a>. Puedes elegir entre tres tipos de token: <em>detallado</em> (recomendado para producción, ya que proporciona acceso solo a recursos específicos); <em>lectura</em> (para acceso de solo lectura); o <em>escritura</em> (para acceso de lectura y escritura). Para este tutorial, un token de lectura es suficiente, ya que solo necesitamos llamar al endpoint de inferencia. Guarda esta clave para el siguiente paso.</p><h4>Configuración del endpoint de inferencia de Elasticsearch</h4><p>Primero, declaremos un cliente de Python para Elasticsearch:</p>os.environ["ELASTICSEARCH_API_KEY"] = "your-elasticsearch-api-key"
os.environ["ELASTICSEARCH_URL"] = "https://xxxx.us-central1.gcp.cloud.es.io:443"

es_client = Elasticsearch(
    os.environ["ELASTICSEARCH_URL"], api_key=os.environ["ELASTICSEARCH_API_KEY"]
)<p>A continuación, vamos a crear un endpoint de inferencia de Elasticsearch que use el modelo Hugging Face. Este endpoint nos permitirá generar respuestas basadas en las entradas del blog y en el prompt que se pasó al modelo.</p>INFERENCE_ENDPOINT_ID = "smollm3-3b-pnz"

os.environ["HUGGING_FACE_INFERENCE_ENDPOINT_URL"] = (
 "https://j2g31h0futopfkli.us-east-1.aws.endpoints.huggingface.cloud/v1/chat/completions"
)
os.environ["HUGGING_FACE_API_KEY"] = "hf_xxxxx"

resp = es_client.inference.put(
        task_type="chat_completion",
        inference_id=INFERENCE_ENDPOINT_ID,
        body={
            "service": "hugging_face",
            "service_settings": {
                "api_key": os.environ["HUGGING_FACE_API_KEY"],
                "url": os.environ["HUGGING_FACE_INFERENCE_ENDPOINT_URL"],
            },
        },
    )<h3>Set de datos</h3><p>El conjunto de datos contiene las <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/elasticsearch-inference-api-and-hugging-face/dataset.json">publicaciones de blog</a> que se consultarán, representando un conjunto de contenido multilingüe utilizado a lo largo del flujo de trabajo:</p>// Articles dataset document example: 
{
    "id": "6",
    "title": "Complete guide to the new API: Endpoints and examples",
    "author": "Tomas Hernandez",
    "date": "2025-11-06",
    "category": "tutorial",
    "content": "This guide describes in detail all endpoints of the new API v2. It includes code examples in Python, JavaScript, and cURL for each endpoint. We cover authentication, resource creation, queries, updates, and deletion. We also explain error handling, rate limiting, and best practices. Complete documentation is available on our developer portal."
  }<h4>Mappings de Elasticsearch</h4><p>Una vez definido el set de datos, necesitamos crear un esquema de datos que se ajuste correctamente a la estructura de la publicación de blog. Se emplearán las siguientes <a href="https://www.elastic.co/docs/manage-data/data-store/mapping">mappings de índices</a> para almacenar los datos en Elasticsearch:</p>INDEX_NAME = "blog-posts"

mapping = {
    "mappings": {
        "properties": {
            "id": {"type": "keyword"},
            "title": {
                "type": "object",
                "properties": {
                    "original": {
                        "type": "text",
                        "copy_to": "semantic_field",
                        "fields": {"keyword": {"type": "keyword"}},
                    },
                    "translated_title": {
                        "type": "text",
                        "fields": {"keyword": {"type": "keyword"}},
                    },
                },
            },
            "author": {"type": "keyword", "copy_to": "semantic_field"},
            "category": {"type": "keyword", "copy_to": "semantic_field"},
            "content": {"type": "text", "copy_to": "semantic_field"},
            "date": {"type": "date"},
            "semantic_field": {"type": "semantic_text"},
        }
    }
}


es_client.indices.create(index=INDEX_NAME, body=mapping)<p>Aquí, podemos ver mejor cómo se estructuran los datos. Usaremos la búsqueda semántica para recuperar resultados basados en lenguaje natural, junto con la propiedad <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/copy-to"><code>copy_to</code></a> para copiar el contenido del campo en el campo <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/semantic-text"><code>semantic_text</code></a>. Además, el campo <code>title</code> contiene dos subcampos: el subcampo <code>original</code> almacena el título en inglés o español, dependiendo del idioma original del artículo; y el subcampo <code>translated_title</code> está presente solo para artículos en español y contiene la traducción al inglés del título original.</p><h3>Ingesta de datos</h3><p>El siguiente fragmento de código ingesta el conjunto de datos de las publicaciones de blog en Elasticsearch mediante la <a href="https://www.elastic.co/docs/reference/elasticsearch/clients/javascript/bulk_examples">API de bulk</a>:</p>def build_data(json_file, index_name):
    with open(json_file, "r") as f:
        data = json.load(f)

    for doc in data:
        action = {"_index": index_name, "_source": doc}
        yield action


try:
    success, failed = helpers.bulk(
        es_client,
        build_data("dataset.json", INDEX_NAME),
    )
    print(f"{success} documents indexed successfully")

    if failed:
        print(f"Errors: {failed}")
except Exception as e:
    print(f"Error: {str(e)}")<p>Ahora que tenemos los artículos ingeridos en Elasticsearch, necesitamos crear una función capaz de buscar en el campo <code>semantic_text</code> :</p>def perform_semantic_search(query_text, index_name=INDEX_NAME, size=5):
    try:
        query = {
            "query": {
                "match": {
                    "semantic_field": {
                        "query": query_text,
                    }
                }
            },
            "size": size,
        }

        response = es_client.search(index=index_name, body=query)
        hits = response["hits"]["hits"]

        return hits
    except Exception as e:
        print(f"Semantic search error: {str(e)}")
        return []<p>También necesitamos una función que llame al endpoint de inferencia. En este caso, llamaremos al endpoint usando el tipo de tarea <strong><code>chat_completion</code></strong>para obtener respuestas de transmisión:</p>def stream_chat_completion(messages: list, inference_id: str = INFERENCE_ENDPOINT_ID):
    url = f"{ELASTICSEARCH_URL}/_inference/chat_completion/{inference_id}/_stream"
    payload = {"messages": messages}
    headers = {
        "Authorization": f"ApiKey {ELASTICSEARCH_API_KEY}",
        "Content-Type": "application/json",
    }

    try:
        response = requests.post(url, json=payload, headers=headers, stream=True)
        response.raise_for_status()

        for line in response.iter_lines(decode_unicode=True):
            if line:
                line = line.strip()

                if line.startswith("event:"):
                    continue

                if line.startswith("data: "):
                    data_content = line[6:]

                    if not data_content.strip() or data_content.strip() == "[DONE]":
                        continue

                    try:
                        chunk_data = json.loads(data_content)

                        if "choices" in chunk_data and len(chunk_data["choices"]) &gt; 0:
                            choice = chunk_data["choices"][0]
                            if "delta" in choice and "content" in choice["delta"]:
                                content = choice["delta"]["content"]
                                if content:
                                    yield content

                    except json.JSONDecodeError as json_err:
                        print(f"\nJSON decode error: {json_err}")
                        print(f"Problematic data: {data_content}")
                        continue

    except requests.exceptions.RequestException as e:
        yield f"Error: {str(e)}"<p>Ahora podemos escribir una función que llame a la función de búsqueda semántica, junto con el endpoint de inferencia <code>chat_completions</code> y el endpoint de recomendaciones, para generar los datos que se asignarán en las tarjetas:</p>def recommend_articles(search_query, index_name=INDEX_NAME, max_articles=5):
    print(f"\n{'='*80}")
    print(f"🔍 Search Query: {search_query}")
    print(f"{'='*80}\n")

    articles = perform_semantic_search(search_query, index_name, size=max_articles)

    if not articles:
        print("❌ No relevant articles found.")
        return None, None

    print(f"✅ Found {len(articles)} relevant articles\n")

    # Build context with found articles
    context = "Available blog articles:\n\n"
    for i, article in enumerate(articles, 1):
        source = article.get("_source", article)
        context += f"Article {i}:\n"
        context += f"- Title: {source.get('title', 'N/A')}\n"
        context += f"- Author: {source.get('author', 'N/A')}\n"
        context += f"- Category: {source.get('category', 'N/A')}\n"
        context += f"- Date: {source.get('date', 'N/A')}\n"
        context += f"- Content: {source.get('content', 'N/A')}\n\n"

    system_prompt = """You are an expert content curator that recommends blog articles.

    Write recommendations in a conversational style starting with phrases like:
    - "If you're interested in [topic], this article..."
    - "This post complements your search with..."
    - "For those looking into [topic], this article provides..."


    FORMAT REQUIREMENTS:
    - Return ONLY a JSON array
    - Each element must have EXACTLY these three fields: "article_number", "title", "recommendation"
    - If the original title is in spanish, use the "translated_title" subfield in the "title" field

    Keep each recommendation concise (2-3 sentences max) and focused on VALUE to the reader.

    EXAMPLE OF CORRECT FORMAT:
    [
        {"article_number": 1, "title": "Article title in english", "recommendation": "If you are interested in [topic], this article provides..."},
        {"article_number": 2, "title": "Article title in english", "recommendation": " for those looking into [topic], this article provides..."}
    ]

    Return ONLY the JSON array following this exact structure."""

    user_prompt = f"""Search query: "{search_query}"

    Generate recommendations for the following articles: {context}
    """

    messages = [
        {"role": "system", "content": "/no_think"},
        {"role": "system", "content": system_prompt},
        {"role": "user", "content": user_prompt},
    ]

    # LLM generation
    print(f"{'='*80}")
    print("🤖 Generating personalized recommendations...\n")

    full_response = ""

    for chunk in stream_chat_completion(messages):
        print(chunk, end="", flush=True)
        full_response += chunk

    return context, articles, full_response<p>Por último, extrae la información y dale formato para imprimirla:</p>def display_recommendation_cards(articles, recommendations_text):
    print("\n" + "=" * 100)
    print("📇 RECOMMENDED ARTICLES".center(100))
    print("=" * 100 + "\n")

    # Parse JSON recommendations - clean tags and extract JSON
    recommendations_list = []
    try:

        # Clean up &lt;think&gt; tags
        cleaned_text = re.sub(
            r"&lt;think&gt;.*?&lt;/think&gt;", "", recommendations_text, flags=re.DOTALL
        )
        # Remove markdown code blocks ( ... ``` or ``` ... ```)
        cleaned_text = re.sub(r"```(?:json)?", "", cleaned_text)
        cleaned_text = cleaned_text.strip()

        parsed = json.loads(cleaned_text)

        # Extract recommendations from list format
        for item in parsed:
            article_number = item.get("article_number")
            title = item.get("title", "")
            rec_text = item.get("recommendation", "")

            if article_number and rec_text:
                recommendations_list.append(
                    {
                        "article_number": article_number,
                        "title": title,
                        "recommendation": rec_text,
                    }
                )
    except json.JSONDecodeError as e:
        print(f"⚠️  Could not parse recommendations as JSON: {e}")
        return

    for i, article in enumerate(articles, 1):
        source = article.get("_source", article)

        # Card border
        print("┌" + "─" * 98 + "┐")

        # Find recommendation and title for this article number
        recommendation = None
        title = None
        for rec in recommendations_list:
            if rec.get("article_number") == i:
                recommendation = rec.get("recommendation")
                title = rec.get("title")
                break

        # Print title
        title_lines = textwrap.wrap(f"📌 {title}", width=94)
        for line in title_lines:
            print(f"│  {line}".ljust(99) + "│")

        # Card border
        print("├" + "─" * 98 + "┤")

        # Print recommendation
        if recommendation:
            recommendation_lines = textwrap.wrap(recommendation, width=94)
            for line in recommendation_lines:
                print(f"│  {line}".ljust(99) + "│")

        # Card bottom
        print("└" + "─" * 98 + "┘")<p>Pongámoslo a prueba haciendo una pregunta sobre las publicaciones de blog de seguridad:</p>search_query = "Security and vulnerabilities"

context, articles, recommendations = recommend_articles(search_query)

print("\nElasticsearch context:\n", context)

# Display visual cards
display_recommendation_cards(articles, recommendations)<p>Aquí podemos ver las tarjetas de la consola generadas por el flujo de trabajo:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4aa221a08a51aeb3/6a170d7460084be1413c45d6/730d35212594bb3db30447c3ea7e2a92857287b7-1999x1515.png" alt="Sección titulada “Artículos recomendados” que muestra cinco resúmenes de artículos en cajas, incluso temas sobre una vulnerabilidad del sistema de autenticación, riesgos de migración, mejoras en el rendimiento y autenticación de la API REST v2, cambios en el sistema de notificaciones y una guía completa de la nueva API." /><p>Puedes ver los resultados completos, incluso todas las coincidencias y la respuesta del LLM, en <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/elasticsearch-inference-api-and-hugging-face/results.md">este archivo</a>.</p><p>Estamos solicitando artículos relacionados con: “Seguridad y vulnerabilidades”. Esta pregunta se emplea como consulta de búsqueda en los documentos almacenados en Elasticsearch. Los resultados obtenidos se pasan al modelo, el cual genera recomendaciones basadas en su contenido. Como podemos ver, el modelo hizo un excelente trabajo generando textos cortos y atractivos que pueden motivar al lector a hacer clic en ellos.</p><h2>Conclusión</h2><p>Este ejemplo muestra cómo se pueden combinar Elasticsearch y Hugging Face para crear un sistema centralizado rápido y eficiente para aplicaciones de IA. Este enfoque reduce el esfuerzo manual y proporciona flexibilidad, gracias al extenso catálogo de modelos de Hugging Face. El uso de SmolLM3-3B, en particular, muestra cómo los modelos compactos y multilingües aún pueden ofrecer razonamiento significativo y generación de contenido cuando se combinan con la búsqueda semántica. En conjunto, estas herramientas ofrecen una base escalable y eficaz para construir análisis de contenidos inteligentes y aplicaciones multilingües.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/hugging-face-elasticsearch-inference-api</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/hugging-face-elasticsearch-inference-api</guid>
    <category><![CDATA[AI agéntica]]></category>
    <category><![CDATA[Integraciones]]></category>
    <dc:creator><![CDATA[Jeffrey Rengifo]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5f961af4cb26ec97/6a170d767d8d6790c770e790/1417d6ff033712206c9bd4bcc22074ee3437ce96-1999x1125.png" length="0" type="image/png"/>
    <pubDate>Mon, 23 Mar 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Extensión CLI de Gemini para Elasticsearch con herramientas y habilidades]]></title>
    <description><![CDATA[Te presentamos la extensión de Elastic para la CLI de Gemini de Google, que te permite hacer búsquedas, recuperar y analizar datos de Elasticsearch en flujos de trabajo de desarrollo y de agentes.
]]></description>
    <content:encoded><![CDATA[<p>Nos complace anunciar el lanzamiento de nuestra extensión Elastic para la CLI de Gemini de Google, que trae todo el poder de <a href="https://www.elastic.co/elasticsearch">Elasticsearch</a> y <a href="https://www.elastic.co/elasticsearch/agent-builder">Elastic Agent Builder</a> directamente a tu flujo de trabajo de desarrollo de IA. Esta extensión también ofrece varias funciones de agente desarrolladas recientemente para interactuar con Elasticsearch.</p><p>La extensión está disponible como proyecto de open source <a href="https://github.com/elastic/gemini-cli-elasticsearch">aquí</a>.</p><h2>¿Qué es Gemini CLI y cómo lo instalas?</h2><p><a href="https://geminicli.com/">Gemini CLI</a> es un agente open source de IA que lleva los modelos Gemini de Google directamente a la línea de comando. Permite a los desarrolladores interactuar con la IA desde la terminal para hacer tareas como generar código, editar archivos, ejecutar comandos de shell y obtener información de la web.</p><p>A diferencia de las interfaces de chat típicas, Gemini CLI se integra con tu entorno local de desarrollo, lo que significa que puede entender el contexto del proyecto, modificar archivos, ejecutar compilaciones o pruebas, y automatizar flujos de trabajo directamente dentro de la terminal. Esto lo hace útil para desarrolladores, ingenieros de confiabilidad del sitio (SRE) e ingenieros que buscan codificación y automatización asistidas por IA sin salir de su flujo de trabajo de línea de comandos.</p><p>Gemini CLI puede instalarse usando diversos gestores de paquetes. El método más común es a través de npm:</p>npm install -g @google/gemini-cli<p>Si quieres conocer opciones de instalación alternativas, consulta la <a href="https://geminicli.com/docs/get-started/installation/">página de instalación oficial</a>.</p><p>Luego de la instalación, inicia la CLI ejecutando:</p>gemini<p>Verás una pantalla, como se muestra en la Figura 1:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4ff43abe550941b4/6a17072ba29299fc2ad00fa4/6dfcec4a77b3dc83bf0d974417bf2e211abb1f4f-876x468.png" alt="Captura de pantalla de Gemini CLI." /><h2>Configurar Elasticsearch</h2><p>Es necesario tener una instancia de Elasticsearch en funcionamiento. Si quieres usar el servidor Model Context Protocol (MCP), también necesitas instalar Kibana 9.3+. Para emplear la habilidad del lenguaje de búsqueda Elasticsearch (ES|QL) (<code>esql</code>) descrito más abajo, Kibana no es obligatorio.</p><p>Puedes activar una prueba gratis en <a href="https://www.elastic.co/cloud">Elastic Cloud</a> o instalarlo localmente usando el script <a href="https://github.com/elastic/start-local"><code>start-local</code></a>:</p>curl -fsSL https://elastic.co/start-local | sh<p>Se instalarán Elasticsearch y Kibana en tu computadora y se generará una clave API que se utilizará para configurar Gemini CLI.</p><p>La clave de API se mostrará como salida del comando anterior y se almacenará en un archivo <strong>.env</strong> en la carpeta <strong><code>elastic-start-local</code></strong>.</p><p>Si usas Elasticsearch local (por ejemplo, usar <code>start-local</code>), y quieres usar Elastic Agent Builder con MCP, también necesitas conectar un modelo de lenguaje grande (LLM). Puedes leer <a href="https://www.elastic.co/docs/explore-analyze/ai-features/llm-guides/llm-connectors">esta página de documentación</a> para entender las diferentes opciones.</p><p>Si usas Elastic Cloud (o sin servidor), ya tienes una conexión con el LLM preconstruida.</p><h2>Instala la extensión de Elasticsearch</h2><p>Puedes instalar la extensión de Elasticsearch para Gemini CLI con el siguiente comando:</p>gemini extensions install https://github.com/elastic/gemini-cli-elasticsearch<p>Puedes verificar que las extensiones se hayan instalado correctamente abriendo Gemini y ejecutando el siguiente comando:</p>/extensions list<p>Deberías ver la extensión de Elasticsearch disponible.</p><p>Si quieres usar la integración de MCP, necesitas tener instalada una versión de Elasticsearch 9.3 o superior. Necesitas la URL de tu servidor MCP de <a href="https://www.elastic.co/kibana">Kibana</a>:</p><ul><li><p>Obtén la URL del servidor MCP desde Agentes &gt; Ver todas las herramientas &gt; Administrar MCP &gt; Copiar URL del servidor MCP.</p></li><li><p>La URL tendrá este formato: https://your-kibana-instance/api/agent_builder/mcp</p></li></ul><p>Necesitas la URL del endpoint de Elasticsearch. Esta generalmente se informa en la parte superior de la página de Kibana Elasticsearch. Si estás ejecutando Elasticsearch con <code>start-local</code>, ya tienes el endpoint en la clave <code>ES_LOCAL_URL</code>en el archivo <code>start-local</code>.env .</p><p>También necesitas una clave API. Si estás ejecutando Elasticsearch con <code>start-local</code>, ya tienes <code>ES_LOCAL_API_KEY</code> en el archivo <code>start-local</code> .env . De lo contrario, puedes crear una clave API desde la interfaz de Kibana, como se indica <a href="https://www.elastic.co/docs/deploy-manage/api-keys/elasticsearch-api-keys">aquí</a>:</p><ul><li><p>En Kibana: Stack Management &gt; Seguridad &gt; Claves de API &gt; Crear clave de API.</p></li><li><p>Sugerimos establecer solo los privilegios de lectura para la clave API, habilitando el privilegio <code>feature_agentBuilder.read</code> como <a href="https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/permissions#grant-access-with-roles">se informa aquí</a>.</p></li><li><p>Copia el valor de la clave API codificada.</p></li></ul><p>Configura las variables de entorno requeridas en tu shell:</p>export ELASTIC_URL="your-elasticsearch-url"
export ELASTIC_MCP_URL="your-elasticsearch-mcp-url"
export ELASTIC_API_KEY="your-encoded-api-key"<h2>Instala el set de datos de ejemplo</h2><p>Puedes instalar el set de datos de <strong>pedidos de comercio electrónico </strong>disponible desde Kibana. Incluye un único índice llamado <strong><code>kibana_sample_data_ecommerce</code></strong>, que contiene información sobre 4,675 pedidos de un sitio web de comercio electrónico. Para cada pedido, tenemos la siguiente información:</p><ul><li><p>Información del cliente (nombre, identificación, fecha de nacimiento, correo electrónico y más).</p></li><li><p>Fecha del pedido.</p></li><li><p>ID de pedido.</p></li><li><p>Productos (lista de todos los productos con precio, cantidad, identificación, categoría, descuento y otros detalles).</p></li><li><p>SKU.</p></li><li><p>Precio total (sin impuestos, con impuestos).</p></li><li><p>Cantidad total.</p></li><li><p>Información geográfica (ciudad, país, continente, ubicación, región).</p></li></ul><p>Para instalar los datos de muestra, abre la página <strong>Integraciones</strong> en Kibana (busca "Integración" en la barra superior de búsqueda) e instala los <strong>Datos de muestra</strong>. Para más detalles, consulta la documentación <a href="https://www.elastic.co/docs/explore-analyze/#gs-get-data-into-kibana">aquí</a>.</p><p>El objetivo de este artículo es mostrar lo fácil que es configurar la CLI Gemini para conectarse a Elasticsearch e interactuar con el índice <strong><code>kibana_sample_data_ecommerce</code></strong> .</p><h2>Cómo usar el MCP de Elasticsearch</h2><p>Puedes comprobar la conexión usando el siguiente comando en Gemini:</p>/mcp list<p>Deberías ver el <strong><code>elastic-agent-builder</code></strong> activado, como se muestra en la Figura 2:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt52b85e7255360f3b/6a17072da929cf33d3ae08f5/1508423bc1d1bc3c04a1cb01e2d59495a3516ed1-1465x844.png" alt="Servidor MCP &quot;elastic-agent-builder&quot; con la lista de herramientas." /><p>Elasticsearch proporciona un conjunto predeterminado de herramientas. Ve la descripción <a href="https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/tools/builtin-tools-reference">aquí</a>.</p><p>Con estas herramientas, puedes interactuar con Elasticsearch y hacer preguntas como:</p><ul><li><p><code>Give me the list of all the indexes available in Elasticsearch.</code></p></li><li><p><code>How many customers are based in the USA in the kibana_sample_data_ecommerce index of Elasticsearch?</code></p></li></ul><p>Dependiendo de la pregunta, Gemini usará una o varias de las herramientas disponibles para intentar responderla.</p><h2>Los comandos /elastic</h2><p>En la extensión Elasticsearch para Gemini CLI, también agregamos comandos<strong><code>/elastic</code></strong>.</p><p>Si ejecutas el comando <strong><code>/help</code></strong>, verás todas las opciones <code>/elastic</code> disponibles (Figura 3):</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt741c7451ecab10d2/6a17072ea6c2b9ccd6e79643/5b2a0727ce7a04354878dd048253d3f4d062324b-1983x230.png" alt="Los comandos disponibles de `/elastic`." /><p>Estos comandos pueden ser útiles si quieres ejecutar directamente una herramienta específica del servidor MCP <code>elastic-agent-builder</code>. Por ejemplo, usando el siguiente comando, puedes obtener el mapping del <code>kibana_sample_data_ecommerce</code>:</p>/elastic:get-mapping kibana_sample_data_ecommerce<p>Estos comandos son esencialmente atajos para ejecutar herramientas específicas, en lugar de depender del modelo Gemini para determinar qué herramienta debe invocarse.</p><h2>Cómo usar las habilidades de Elasticsearch</h2><p>Esta extensión también incluye una <a href="https://github.com/elastic/gemini-cli-elasticsearch/tree/main/skills/esql">habilidad de agente para ES|QL</a>, el <a href="https://www.elastic.co/docs/explore-analyze/discover/try-esql">lenguaje de búsqueda Elasticsearch</a> disponible en Elasticsearch. <a href="https://agentskills.io/home">Habilidades del Agente</a> es un formato abierto que proporciona a los agentes de codificación de IA, como Gemini CLI, instrucciones personalizadas para tareas específicas. Utilizan un concepto llamado <em>revelación progresiva</em>, lo que significa que solo se agrega una breve descripción de la habilidad a la indicación inicial del sistema. Cuando le pides al agente que haga una tarea, como consultar Elasticsearch, hace coincidir la solicitud con la habilidad relevante y carga dinámicamente las instrucciones detalladas. Esta es una forma eficaz de gestionar los presupuestos de tokens y, al mismo tiempo, proporcionar a la IA exactamente el contexto que necesita.</p><p>La <strong>habilidad</strong><strong><code>esql</code></strong> está diseñada para permitir que la CLI de Gemini escriba y ejecute consultas en ES|QL directamente en tu cluster. ES|QL es un poderoso lenguaje de búsqueda con barras verticales que hace que la exploración de datos, el análisis de logs y las agregaciones sean sumamente intuitivas. Con esta habilidad activada, no necesitas buscar la sintaxis de ES|QL; simplemente puedes hacer preguntas en lenguaje natural a la CLI de Gemini sobre tus datos, y el agente se encargará del resto.</p><p>Las ejecuciones se efectúan mediante comandos <a href="https://curl.se/">curl</a> sencillos ejecutados en una terminal. Esto es posible porque Elasticsearch ofrece un amplio conjunto de API REST que se pueden utilizar fácilmente para integrar el sistema en cualquier arquitectura.</p><p><strong>Lo que la </strong> habilidad<strong><code>esql</code></strong><strong> ofrece:</strong></p><ul><li><p><strong>Detección de índices y esquemas:</strong> El agente puede usar las herramientas integradas de la habilidad para mostrar los índices disponibles y obtener el mapping de campos. Por ejemplo, antes de escribir una consulta para el set de datos de comercio electrónico, el agente puede ejecutar una verificación de esquema en <strong><code>kibana_sample_data_ecommerce</code></strong> para comprender los campos disponibles, como <strong><code>taxful_total_price</code></strong> o <strong><code>category</code></strong>.</p></li><li><p><strong>Traducción perfecta al lenguaje natural:</strong> la habilidad le da al agente algo más que un simple manual de referencia; proporciona una guía específica para interpretar la intención del usuario. Cuando escribes solicitudes en lenguaje natural, como "Mostrar el tiempo de respuesta promedio agrupado por servicio", el agente utiliza la función de coincidencia de patrones integrada en la habilidad para traducir al instante tus palabras en las agregaciones, filtros y comandos en ES|QL correctos.</p></li><li><p><strong>Autocorrección:</strong> Si una consulta falla (por ejemplo, debido a una incompatibilidad de tipos o un error de sintaxis), la habilidad devuelve la consulta generada junto con el mensaje de error exacto de Elasticsearch, lo que permite al agente corregirla al instante y volver a intentarlo sin que tengas que intervenir.</p></li></ul><p>Dado que la habilidad <code>esql</code> también está disponible como una herramienta en el servidor MCP <code>elastic-agent-builder</code>, necesitamos deshabilitar este servidor momentáneamente. Puedes usar el siguiente comando para desactivarlo:</p>/mcp disable elastic-agent-builder<p>Entonces puedes simplemente escribir un mensaje como este en tu CLI de Gemini:</p>Find the top 5 product categories by total sales revenue in the kibana_sample_data_ecommerce index<p>El agente deberá:</p><ul><li><p>Reconoce la necesidad de la habilidad <code>esql</code> .</p></li><li><p>Verifica el esquema de <strong><code>kibana_sample_data_ecommerce</code></strong>.</p></li><li><p>Construye una consulta en ES|QL, como: <code>FROM kibana_sample_data_ecommerce | STATS total_revenue = SUM(taxful_total_price) BY category.keyword | SORT total_revenue DESC | LIMIT 5</code>.</p></li><li><p>Ejecutar la consulta en la API de Elasticsearch.</p></li><li><p>Presentar la respuesta final directamente en la terminal.</p></li></ul><p>Aquí, presentamos un ejemplo de respuesta de Gemini al mensaje anterior:</p>───────────────────────────────────────────────────────────
&gt; Find the top 5 product categories by total sales revenue in the kibana_sample_data_ecommerce index   ───────────────────────────────────────────────────────────
✓  Activate Skill "esql": Interact with Elasticsearch using ES|QL and curl. Use when querying, managing indices,  …
Skill esql activated. Resources loaded from /home/user/.gemini/extensions/elasticsearch/skills/esql: 
Showing up to 200 items (files + folders).                                                                     /home/user/.gemini/extensions/elasticsearch/skills/esql/                                                            ├───SKILL.md 
└───references/
├───esql-reference.md 
├───generation-tips.md 
└───query-patterns.md

✦ I will start by exploring the mapping of the kibana_sample_data_ecommerce index to identify the fields for product category and sales revenue. Then, I will construct and execute an ES|QL query to determine the top 5 product categories by total sales revenue.

✓  Shell curl -s "${ELASTIC_URL%/}/kibana_sample_data_ecommerce/_mapping" -H "Authorization: ApiKey $(printenv ELAS…                                                                                                                       {  
   "kibana_sample_data_ecommerce": { 
     "mappings": { 
       "properties": {
         "category": { 
           "type": "text", 
           "fields": {
             "keyword": {
               "type": "keyword"
             }
           }
         }, 
         "currency": {
           "type": "keyword"
         }, 

✦ Okay, I have the mapping. The category is products.category.keyword and sales revenue is taxful_total_price. I'll now execute the ES|QL query to get the top 5 categories by revenue.

✓  Shell curl -s -X POST "${ELASTIC_URL%/}/_query" \  -H "Authorization: ApiKey $(printenv ELASTIC_API_KEY)" \  -H …
{ 
   "took": 62,
   "is_partial": false, 
   "completion_time_in_millis": 1772805644164,
   "documents_found": 4675,
   "values_loaded": 12084, 
   "start_time_in_millis": 1772805644102, 
   "expiration_time_in_millis": 1773237644087,  
   "columns": [ 
     { 
       "name": "total_revenue",    
       "type": "double"  
     }, 
     {   
       "name": "products.category.keyword",

✦ The query for top 5 product categories by revenue was successful. I'll format the returned JSON values into a   user-friendly list.

 ✓  Shell 
                             
 Top 5 product categories by total sales revenue: 
 1. Men's Clothing: 149393.91
 2. Women's Clothing: 135099.91
 3. Women's Shoes: 105479.17
 4. Men's Shoes: 91797.92
 5. Women's Accessories: 60830.31 

✦ I've found the answer; now I'll format it for clarity.

 ✓  Shell 

 Here are the top 5 product categories by total sales revenue:                                                        
                                                                      
 | Rank | Category              | Total Revenue | 
 |------|-----------------------|---------------| 
 | 1    | Men's Clothing        | 149,393.91    |
 | 2    | Women's Clothing      | 135,099.91    |
 | 3    | Women's Shoes         | 105,479.17    |
 | 4    | Men's Shoes           | 91,797.92     | 
 | 5    | Women's Accessories   | 60,830.31     | 

✦ I've formatted and presented the answer. I'm finished.
 ───────────────────────────────────────────────────────────<p>Es interesante ver cómo el modelo Gemini genera la respuesta final mostrando todos los pasos que sigue. Aquí, puedes ver claramente la influencia de la habilidad en el proceso de razonamiento del modelo. La primera vez que el modelo reconoce que necesita usar una habilidad o ejecutar un comando de shell, pide permiso empleando el enfoque de intervención humana.</p><p>Al manejar el trabajo pesado de descubrimiento de esquemas, generación de consultas y ejecución, la habilidad <code>esql</code> te permite concentrarte completamente en las respuestas en lugar de en la mecánica para obtenerlas. Obtendrás los datos que necesitas, correctamente formateados y directamente en tu terminal, todo sin escribir una sola línea de sintaxis o cambiar de contexto a otra aplicación.</p><h2>Conclusión</h2><p>En este artículo, presentamos la extensión Elasticsearch para Gemini CLI que lanzamos recientemente. Esta extensión te permite interactuar con tu instancia de Elasticsearch mediante Gemini y el servidor MCP de Elasticsearch que ofrece Elastic Agent Builder, disponible a partir de la versión 9.3.0, así como el comando <code>/elastic</code> .</p><p>Además, la extensión también incluye una habilidad <code>esql</code> que convierte la solicitud del usuario de lenguaje natural en una consulta en ES|QL. Esta habilidad puede resultar especialmente útil cuando no se puede usar el servidor MCP, ya que la comunicación subyacente se gestiona mediante simples comandos curl ejecutados en una terminal. Elasticsearch ofrece un amplio conjunto de API REST que se pueden integrar fácilmente en cualquier proyecto. Esto es especialmente útil cuando se desarrollan aplicaciones de AI agéntica.</p><p>Para más información sobre nuestra extensión Gemini CLI, visita el repositorio del proyecto <a href="https://github.com/elastic/gemini-cli-elasticsearch">aquí</a>.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/gemini-cli-extension-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/gemini-cli-extension-elasticsearch</guid>
    <category><![CDATA[Integraciones]]></category>
    <category><![CDATA[AI agéntica]]></category>
    <dc:creator><![CDATA[Walter Rafelsberger,Enrico Zimuel]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4ff43abe550941b4/6a17072ba29299fc2ad00fa4/6dfcec4a77b3dc83bf0d974417bf2e211abb1f4f-876x468.png" length="0" type="image/png"/>
    <pubDate>Tue, 17 Mar 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Habilidades de agente para Elastic: convierte a tu agente de IA en un experto en Elastic]]></title>
    <description><![CDATA[Otorga a tu agente de codificación de IA el conocimiento para buscar, visualizar, asegurar y automatizar con Elastic Agent Skills.]]></description>
    <content:encoded><![CDATA[<p>Todos los desarrolladores, ingenieros de confiabilidad del sitio (SRE) o analistas que han intentado usar un agente de programación basado en IA con una plataforma especializada se han enfrentado al mismo obstáculo. Pides al agente que escriba una búsqueda, configure una alerta o realice una investigación y se acerca, pero no es del todo correcto. Elastic tiene una ventaja aquí: más de una década de documentación, publicaciones de blog y respuestas de la comunidad significa que los agentes de IA ya conocen Elastic mejor que la mayoría de las plataformas de datos. Pero esa profundidad viene con ruido. Las API obsoletas coexisten con las actuales. Los patrones desactualizados tienen tanta importancia como las mejores prácticas. El agente reproduce con confianza un enfoque que funcionó hace tres versiones, porque en sus datos de entrenamiento sí funcionó. El resultado es un costo de corrección: los usuarios ingresan manualmente la documentación en el contexto, corrigen la sintaxis alucinada y trabajan alrededor del agente en lugar de con él. Lo que es peor: las funciones avanzadas no se usan en absoluto, no porque los usuarios no las necesiten, sino porque el agente no sabe que existen.</p><p>Por eso estamos liberando el código de <a href="https://github.com/elastic/agent-skills">Elastic Agent Skills</a> como open-source: experiencia nativa en plataformas para Elasticsearch, Kibana, Elastic Observability y Elastic Security. Insértalo en el tiempo de ejecución del agente que ya usas y cambia a tu agente de “generalista” que adivina mucha de la sintaxis a darle experiencia, como, por ejemplo, poder usar muchos de los estándares arquitectónicos como los propios equipos de ingeniería de Elastic. Esta versión inicial de vista previa técnica se centra en las habilidades con máxima compatibilidad para <a href="https://www.elastic.co/cloud/serverless">Elastic Cloud Serverless</a>, pero evolucionará rápidamente para incluir un mejor soporte para versiones anteriores del stack.</p><p>Además, Elastic está abordando este problema desde ambos frentes. Para los agentes en la plataforma de Elastic, <a href="https://www.elastic.co/search-labs/blog/agent-builder-elastic-ga">Elastic Agent Builder</a> (ahora disponible en general) te permite crear y chatear con agentes de IA que heredan los controles de acceso de tus datos, usan herramientas integradas de búsqueda y análisis, y trabajan en contexto junto a tus dashboards, alertas e investigaciones. Estamos trabajando intensamente para asegurar experiencias agénticas increíbles en la plataforma de Elastic. Pero no todos los agentes están dentro de Elastic. Tu equipo ya usa Cursor, Claude Code u otros entornos de ejecución, y esos agentes también necesitan que Elastic funcione bien. Ahí es donde entra en juego Agent Skills.</p><h2>Por qué los agentes tienen dificultades con plataformas especializadas</h2><p>Los modelos de lenguaje grandes (LLM) son generalistas con capacidades extraordinarias. Pueden escribir Python, explicar los manifiestos de Kubernetes y refactorizar los componentes de React porque sus datos de entrenamiento están llenos de ejemplos. Pero cuando se trata de trabajo específico de la plataforma, del tipo que implica lenguajes de búsqueda patentados, superficies de API profundas y mejores prácticas específicas del dominio, muestran limitaciones previsibles.</p><p>En el caso de Elasticsearch, la diferencia se nota claramente:</p><ul><li><p><strong>El lenguaje de búsqueda de Elasticsearch (ES|QL) es un nuevo territorio.</strong> Los LLM están muy entrenados en SQL, pero ES|QL es un lenguaje de búsquedas con barras verticales, con sintaxis, funciones y semántica diferentes. Los agentes suelen escribir búsquedas que parecen razonables, pero no se pueden parsear. Confunden <code>WHERE</code> con <code>| WHERE</code>, inventan funciones que no existen y pierden por completo el modelo de composición basado en barras verticales.</p></li><li><p>Las <strong>superficies de API son anchas y profundas.</strong> Elasticsearch, Kibana y Elastic Security ofrecen cientos de API para búsqueda, ingesta, alertas, reglas de detección, gestión de casos, dashboards y mucho más. Un agente que solo cuenta con datos de entrenamiento generales tiene que adivinar a qué endpoint debe enviar la solicitud, cómo es el cuerpo de la solicitud y cómo gestionar la respuesta. Sus suposiciones son incorrectas con demasiada frecuencia como para erosionar la confianza.</p></li><li><p><strong>Las mejores prácticas no están en los datos de entrenamiento.</strong> ¿Cuándo deberías usar <code>semantic_text</code> frente a una pipeline de incrustación personalizada? ¿Cómo deberías estructurar una pipeline de ingesta para un CSV de 10GB? ¿Cuál es la sintaxis correcta de reglas de detección para una técnica <a href="https://www.elastic.co/docs/solutions/security/detect-and-alert/mitre-attandckr-coverage">MITRE ATT&amp;CK®</a>? Los agentes de uso general no tienen, por defecto, conocimientos específicos de Elastic seleccionados y estructurados de forma confiable. Tendrían que buscarlo, y aunque lo hicieran, la documentación sin procesar no siempre codifica los juicios y mejores prácticas que ofrecen los profesionales calificados.</p></li></ul><p>El resultado: los desarrolladores pasan más tiempo corrigiendo el resultado del agente que el que habrían dedicado a escribir el código ellos mismos. Esa es la experiencia para la que nadie se registró.</p><h2>Agent Skills: conocimiento de la plataforma, empaquetado para los agentes</h2><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2099e0ccdf446fee/6a17074bd7c022e3e1de63d4/8d16ec00d16e70a916c5eef0aaa23fcc735b7186-1067x1280.png" alt="npx skills add elastic/agent-skills" /><p>Agent Skills son directorios independientes que contienen instrucciones, scripts y material de referencia que los entornos de ejecución de agentes pueden cargar dinámicamente. Cuando una habilidad está activa, el agente tiene acceso al contexto adecuado en el momento oportuno: sintaxis de búsqueda, patrones de API, lógica de validación y ejemplos prácticos, lo que le permite completar las tareas correctamente desde el primer intento.</p><p>Cada habilidad sigue la especificación abierta <a href="https://agentskills.io">agentskills.io</a>: una carpeta con un archivo <code>SKILL.md</code> que contiene metadatos e instrucciones estructuradas. Sin formato propietario, sin dependencia del proveedor. Las habilidades funcionan en diferentes entornos de ejecución de agentes; esto incluye Cursor, Claude Code, GitHub Copilot, Windsurf, Gemini CLI, Cline, Codex y <a href="https://agentskills.io">muchos más</a>.</p><h3>¿Qué hay en la versión inicial v0.1.0?</h3><p>El primer conjunto de habilidades abarca cinco áreas del Elastic Stack:</p><ul><li><p>Interactuar con las API de Elasticsearch (búsqueda, indexación, gestión de clústeres)</p></li><li><p>Crear y gestionar contenido de Kibana como dashboards, alertas, conectores y más</p></li><li><p>Especialización en el dominio para Elastic Observability</p></li><li><p>Pericia en la materia para Elastic Security</p></li><li><p>Cómo crear agentes eficaces en Agent Builder</p></li></ul><h3>Las habilidades son componibles</h3><p>Las habilidades no son homogéneas. Son modulares por diseño. Tu agente carga solo las habilidades relevantes para la tarea en cuestión. ¿Estás trabajando en una búsqueda ES|QL? Se activa la habilidad ES|QL. ¿Necesitas crear un dashboard a partir de esos resultados? Aparece la habilidad de dashboard. ¿Estás evaluando el estado de tu aplicación? La habilidad de estado del servicio entra en juego. ¿Estás investigando una alerta de seguridad? La habilidad de triaje se asocia con habilidades de gestión de casos y respuesta a medida que avanza la investigación.</p><p>Esta capacidad de composición significa que no necesitas un único aviso masivo que intente cubrir todo. Cada habilidad lleva exactamente el contexto que tu dominio requiere, nada más, nada menos.</p><h2>Para desarrolladores que crean aplicaciones de búsqueda e IA</h2><p>Si estás cargando datos en Elasticsearch, escribiendo búsquedas o migrando índices, las habilidades reducen el ciclo de generación de código, errores y búsqueda de documentos a partir de los errores.</p><p>Pídele a tu agente que cargue un archivo CSV, y este utilizará una herramienta de ingesta en tiempo real que gestiona la contrapresión y deduce el mapping a partir de los datos. No es un bucle _bulk a medida que se queda sin memoria con el primer archivo grande. Pídele que realice búsquedas con ES|QL, y detectará los nombres reales de tus índices y los esquemas de los campos; luego generará búsquedas con barras verticales válidas, con la sintaxis correcta, las agregaciones adecuadas y una selección de características que tiene en cuenta la versión, en lugar de una conjetura al estilo SQL que requiere tres rondas de depuración. Si le pides que vuelva a indexar los clusters, sigue todo el flujo de trabajo operativo: crea el destino con mapping explícitos, configura los ajustes para el rendimiento, ejecuta el trabajo de forma asincrónica y restaura la configuración de producción cuando termina, no una llamada de _reindex que omite la mitad de los pasos que seguiría un operador experimentado.</p><p>En lugar de un agente que te da un punto de partida razonable que tienes que arreglar, obtienes uno que codifica la disciplina operativa que hace que el resultado realmente funcione.</p><p><strong>Ejemplos de impactos del uso de Elastic Agent Skills</strong></p><p>Eval</p><p>Qué cambió la habilidad</p><p>es-audit-query-failed-logins</p><p>Usó los patrones de búsqueda del log de auditoría de la habilidad en lugar de una búsqueda genérica</p><p>es-authz-role-mapping-ldap</p><p>Generó la estructura correcta de la llamada de API para el mapping de roles</p><p>esql-basic-query</p><p>Escribió la sintaxis de barras verticales ES|QL en lugar de Query DSL</p><p>esql-error-handling</p><p>Primero el esquema, en lugar de adivinar los nombres de los campos</p><p>esql-schema-discovery</p><p>Nunca adivinó el nombre de un índice</p><p>es-ingest-csv-with-infer</p><p>Usó únicamente --infer-mappings, evitó combinarlo con --source-format csv, que genera un índice vacío.</p><p>es-ingest-json-file</p><p>Usó un método de ingesta sólido capaz de gestionar archivos de gran tamaño</p><p>es-reindex-local-async</p><p>Creó primero el índice de destino con réplicas: 0 y refresh_interval: "-1", luego la reindexación asíncrona. La línea base omitió cualquier preparación</p><p>es-seguridad-403-privilegios</p><p>Siguió el flujo de trabajo de diagnóstico de la herramienta para los errores de privilegios, en lugar de seguir consejos genéricos</p><h2>Para equipos de seguridad</h2><p>Los equipos de seguridad repiten los mismos flujos de trabajo operativos a diario: clasificación de alertas, ajuste de reglas de detección, gestión de casos. Las habilidades del agente codifican ese conocimiento de procesos para que tu agente de IA pueda ejecutar estos flujos de trabajo correctamente al llamar a las API adecuadas en el orden correcto con los nombres de campo correctos. Para una guía práctica que te lleva de cero a un entorno de Elastic Security completamente configurado sin salir de tu IDE, consulta <a href="https://www.elastic.co/security-labs/agent-skills-elastic-security">Empieza con Elastic Security desde tu agente de IA</a>.</p><h2>Para equipos de observabilidad y operaciones</h2><p>Las nuevas funciones de los agentes de Elastic Observability reducen la carga operativa que supone instrumentar sistemas complejos, gestionar los SLO, analizar datos complejos y evaluar el estado de los servicios. Incorporar la experiencia nativa de Elastic directamente en los agentes de IA permite a los equipos ejecutar flujos de trabajo complejos de observabilidad utilizando un lenguaje natural sencillo. Esto permite a los SRE y equipos de operaciones resolver incidentes más rápido y mantener sistemas fiables con mayor facilidad. Obtén más información en <a href="https://www.elastic.co/observability-labs/blog/elastic-agent-skills-observability-workflows">este blog</a>.</p><h2>Open source, especificación abierta, impulsado por la comunidad</h2><p>Estamos lanzando Agent Skills bajo la licencia Apache 2.0 porque creemos que el conocimiento de los agentes debe ser abierto. La especificación <a href="https://agentskills.io">agentskills.io</a> que siguen las habilidades es un estándar abierto, no un formato propietario de Elastic. Queremos que las habilidades sean un esfuerzo comunitario, no un entorno cerrado.</p><h2>Parte de un panorama más amplio</h2><p>Agent Skills es una parte de una iniciativa más amplia para hacer de Elasticsearch la plataforma de datos disponible más preparada para los agentes. Para los agentes que están en la plataforma Elasticsearch, <a href="https://www.elastic.co/search-labs/blog/agent-builder-elastic-ga">Agent Builder</a> va más allá al heredar los controles de acceso y permisos de tus datos, ofrecer herramientas integradas y personalizadas para búsqueda y análisis, y permitir que los usuarios interactúen con los agentes en contexto junto con sus dashboards, alertas e investigaciones. Finalmente, el soporte para habilidades llegará pronto a Agent Builder, lo que permitirá a los desarrolladores la flexibilidad de aprovechar las habilidades de Elastic Agent, así como habilidades de cualquier otra fuente para habilitar chats y automatizaciones seguras y mejoradas por contexto en la plataforma Elasticsearch.</p><p>Para los agentes que viven en cualquier otro lugar, estamos invirtiendo en el ecosistema abierto:</p><ul><li><p><strong>Expansión del servidor del Model Context Protocol (MCP):</strong> Ampliando el <a href="https://www.elastic.co/docs/explore-analyze/ai-features/agent-builder/mcp-server">endpoint MCP</a> en Agent Builder con más herramientas, más allá de la búsqueda actual, ES|QL y operaciones de índice.</p></li><li><p><strong>Mejoras en la autenticación:</strong> Facilitar a los agentes la conexión segura, con el objetivo de eliminar la necesidad de copiar y pegar manualmente las claves de API.</p></li><li><p><strong>Documentación legible en LLM:</strong> publicar archivos <code>llms.txt</code> y <code>AGENTS.md</code> para que los agentes puedan descubrir y entender las API de Elastic por sí mismos.</p></li><li><p><strong>Una interfaz de línea de comandos (CLI) para los flujos de trabajo de los agentes:</strong> Herramientas de línea de comandos que facilitan la gestión de conexiones y las operaciones comunes para los agentes.</p></li></ul><p>Las habilidades son la capa que puedes usar hoy. El resto llegará más adelante.</p><h2>Comenzar</h2><p><strong>Antes de que empieces: </strong>los agentes de programación con IA operan con credenciales reales, acceso real a la shell y, a menudo, con todos los permisos del usuario que los ejecuta. Cuando esos agentes se orientan a flujos de trabajo de seguridad, hay más en juego: le estás dando a un sistema automatizado acceso a la lógica de detección, las acciones de respuesta y la telemetría sensible. El perfil de riesgo de cada organización es diferente. Antes de habilitar los flujos de trabajo de seguridad impulsados por IA, <strong>evalúa a qué datos puede acceder el agente, qué acciones puede realizar y qué pasa si se comporta de forma inesperada</strong>.</p><p>Instala Elastic Agent Skills en el tiempo de ejecución de tu agente:</p><p><code>npx skills add elastic/agent-skills</code></p><p>Esto detecta automáticamente tus tiempos de ejecución del agente instalado y coloca las habilidades en el directorio de configuración correcto. Desde allí, tu agente los recoge automáticamente.</p><p>También puedes navegar directamente en el <a href="https://github.com/elastic/agent-skills">catálogo de habilidades</a> e instalar habilidades individuales manualmente si copias la carpeta de la habilidad en el directorio de configuración de tu agente.</p><p>¿Aún no tienes un clúster de Elasticsearch? Comienza una <a href="https://cloud.elastic.co/registration">prueba gratis de Elastic Cloud</a>. Lleva aproximadamente un minuto obtener un entorno completamente configurado.</p><p><strong>Explora el proyecto:</strong></p><ul><li><p><a href="https://github.com/elastic/agent-skills">Repositorio de habilidades de agentes</a></p></li><li><p><a href="https://agentskills.io">especificación de agentskills.io</a></p></li><li><p><a href="https://www.elastic.co/docs">Documentación de Elasticsearch</a></p></li><li><p><a href="https://cloud.elastic.co/registration">Prueba gratuita de Elastic Cloud</a></p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/agent-skills-elastic</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/agent-skills-elastic</guid>
    <category><![CDATA[AI agéntica]]></category>
    <category><![CDATA[Herramientas de IA ]]></category>
    <dc:creator><![CDATA[Graham Hudgins,Matt Ryan]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbd233e8cf5c66c88/6a17074dc1e8a59502f8822a/09e64953819083168a9ecef0888c7f8bde1a43bd-720x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Mon, 16 Mar 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Lenguaje de expresión común (CEL): cómo la entrada CEL mejora la recopilación de datos en las integraciones de Elastic Agent]]></title>
    <description><![CDATA[Aprende cómo se diferencia el lenguaje de expresión común de otros lenguajes de programación, cómo lo ampliamos para la entrada CEL de Filebeat y la flexibilidad que te ofrece para expresar la lógica de recopilación de datos en integraciones con Elastic Agent.]]></description>
    <content:encoded><![CDATA[<p>Las <a href="https://www.elastic.co/integrations">integraciones</a> de Elastic Agent permiten a los usuarios realizar la ingesta de datos en Elasticsearch desde una amplia variedad de fuentes. Combinan la lógica de recopilación, canalizaciones de ingesta, dashboards y otros artefactos en un paquete que se puede instalar y administrar desde la interfaz web de Kibana.</p><p>Las integraciones configuran <a href="https://www.elastic.co/docs/reference/beats/filebeat/configuration-filebeat-options">las entradas de Filebeat</a> para realizar la recopilación de datos. Para recopilar datos de APIs HTTP, a menudo utilizamos la <a href="https://www.elastic.co/docs/reference/beats/filebeat/filebeat-input-httpjson">entrada HTTP JSON</a>. Sin embargo, incluso las APIs básicas de listado pueden diferir mucho en los detalles, y el modelo de transformaciones configuradas en YAML de la entrada HTTP JSON puede hacer que sea engorroso y a veces imposible expresar la lógica de recopilación requerida.</p><p>Se introdujo la <a href="https://www.elastic.co/docs/reference/beats/filebeat/filebeat-input-cel">entrada del lenguaje de expresión común (CEL)</a> para permitir una interacción más flexible con las APIs HTTP. <a href="https://cel.dev/">CEL</a> es un lenguaje diseñado para integrarse en aplicaciones que requieren una forma rápida, segura y extensible de expresar condiciones y transformaciones de datos. La entrada CEL permite a un creador de integraciones escribir una expresión que puede leer configuraciones, realizar un seguimiento de su propio estado, realizar solicitudes, procesar respuestas y, en última instancia, devolver eventos listos para su ingesta.</p><p>En este artículo, veremos cómo se diferencia el lenguaje de expresión común de otros lenguajes de programación, cómo lo ampliamos para la entrada del CEL, y la flexibilidad y el poder que te da para expresar tu lógica de recopilación de datos.</p><h2>CEL y cómo funciona en la entrada</h2><p>CEL es un lenguaje de expresión. No tiene declaraciones. Cuando escribes CEL, no le dices qué hacer al escribir declaraciones, sino que le dices qué valor producir al escribir una expresión. Cada expresión CEL produce un valor, y las expresiones más pequeñas pueden combinarse en una expresión mayor para producir un resultado según reglas más complejas. Más adelante veremos cómo usar expresiones que se pueden escribir con declaraciones en otros idiomas.</p><p>CEL es intencionadamente un lenguaje completo no Turing. No permite bucles ilimitados. Más adelante, veremos cómo puedes procesar listas y mapas mediante macros, pero al evitar bucles sin límites, el lenguaje garantiza un tiempo de ejecución predecible y limitado para expresiones individuales.</p><p>La entrada CEL está configurada con un programa CEL (una expresión) y un estado inicial. El estado se proporcionará como entrada al programa. El programa se evalúa para producir un estado de salida. Si el estado de salida incluye una lista de eventos, estos se eliminarán y se publicarán. El resto del estado de salida se usará como entrada para la próxima evaluación. Si el estado de salida incluye uno o más eventos y la advertencia <code>want_more: true</code>, la siguiente evaluación se realizará inmediatamente. De lo contrario, permanecerá en suspensión durante el resto del intervalo configurado antes de continuar. Aquí tienes un diagrama simplificado del flujo de control de la entrada:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0ec4ea57bfc2a2ff/6a17059f2b835f7d58f4b115/42671541f97e2dba808fd53969fe12f517917f9a-1600x529.png" alt="Flujo de control de entrada del lenguaje de expresión común (CEL)" /><p>La salida de cada evaluación se pasará como entrada a la siguiente evaluación, mientras se ejecute la entrada. Los datos de salida bajo la clave "<code>cursor</code>" se mantendrán en el disco y se volverán a cargar después de que la entrada se reinicie, pero el resto del estado no se conservará entre los reinicios.</p><p>El lenguaje CEL en sí tiene funcionalidades limitadas y evita efectos secundarios, pero es extensible. La implementación <a href="https://github.com/google/cel-go">cel-go</a> agrega algunas funcionalidades, como sintaxis y tipos opcionales. La biblioteca <a href="https://github.com/elastic/mito">Mito</a> se basa en cel-go y agrega más funcionalidades, como la capacidad de hacer solicitudes HTTP. La entrada CEL usa la versión de CEL de Mito.</p><h2>Trabajando con Mito</h2><p>Para construir o depurar una integración mediante una entrada CEL, lo más importante que debes entender es qué estado de salida producirá tu programa CEL para un estado de entrada dado. Durante el desarrollo, puede ser engorroso que tu programa CEL la ejecute la entrada, rodeada por toda la pila de Elastic Stack. Una forma de lograr un ciclo de retroalimentación más rápido es usar la herramienta de línea de comando de Mito, que te permitirá ejecutar un programa CEL directamente y ver la salida que produce para una entrada dada.</p><p>Mito está escrito en Go y se puede instalar de la siguiente manera:</p>go install github.com/elastic/mito/cmd/mito@latest<p>Cuando ejecutas un programa CEL con Mito, normalmente le asignas dos archivos: un archivo JSON con el estado inicial de entrada y otro archivo con el código fuente de tu programa CEL:</p>mito -data state.json src.cel<p>Para facilitar la copia y el pegado, los ejemplos en este artículo están escritos como comandos únicos que hacen que el shell cree archivos temporales al instante al envolver el contenido de cada archivo en <code>&lt;(echo '...content...')</code>. En tu propio desarrollo, trabajar con archivos reales será más fácil.</p><h2>Obtención de datos de incidencias desde GitHub</h2><p>El siguiente ejemplo incluye un programa CEL completo que obtendrá datos sobre los problemas de la <a href="https://docs.github.com/en/rest/issues/issues?apiVersion=2022-11-28#list-repository-issues">API de GitHub</a>. Su estado de entrada inicial tiene una URL para el endpoint de la API y cierta información sobre cómo debe manejar la paginación. El programa CEL utiliza los datos del estado de entrada para generar una solicitud. Decodificará la respuesta, generará eventos a partir de ella y los devolverá como parte de su estado de salida.</p>mito -data &lt;(echo '
  {
    "url": "https://api.github.com/repos/elastic/integrations/issues",
    "per_page": 3,
    "max_pages": 3
  }
') &lt;(echo '
  int(state.?cursor.page.orValue(1)).as(page,
    (
      state.url + "?" + {
        "state": ["all"],
        "sort": ["created"],
        "direction": ["asc"],
        "per_page": [string(state.per_page)],
        "page": [string(page)],
      }.format_query()
    ).as(full_url,
      request("GET", full_url).with({
        "Header": {
          "Accept": ["application/vnd.github+json"],
          "X-GitHub-Api-Version": ["2022-11-28"],
        }
      }).do_request().as(resp,
        resp.Body.decode_json().as(data,
          state.with({
            "events": data.map(i, {
              "html_url": i.html_url,
              "title": i.title,
              "created_at": i.created_at,
            }),
            "cursor": { "page": page + 1 },
            "want_more": size(data) == state.per_page &amp;&amp; page &lt; state.max_pages,
          })
        )
      )
    )
  )
')<p>Su primera evaluación produce la siguiente salida:</p>{
  "cursor": {
    "page": 2
  },
  "events": [
    {
      "created_at": "2018-09-14T09:47:35Z",
      "html_url": "https://github.com/elastic/integrations/issues/3250",
      "title": "Increase support of log formats in haproxy filebeat module"
    },
    {
      "created_at": "2019-02-06T12:37:37Z",
      "html_url": "https://github.com/elastic/integrations/issues/487",
      "title": "ETCD Metricbeat module needs polishing and grooming"
    },
    {
      "created_at": "2019-08-13T11:33:11Z",
      "html_url": "https://github.com/elastic/integrations/pull/1",
      "title": "Initial structure"
    }
  ],
  "max_pages": 3,
  "per_page": 3,
  "url": "https://api.github.com/repos/elastic/integrations/issues",
  "want_more": true
}<p>Los eventos se eliminarán y, cuando se ejecuten en la entrada CEL, se publicarán para su ingesta. El resto de la salida se proporcionará a la siguiente evaluación del programa CEL como estado de entrada.</p><p></p><p>Para entender cómo funciona ese programa CEL, veremos algunos ejemplos más pequeños de CEL y hablaremos con más detalle sobre cómo funciona la entrada de CEL.</p><h2>Conceptos básicos de CEL</h2><p>En el lenguaje CEL, no hay declaraciones, solo hay expresiones. Cada expresión CEL exitosa se evalúa hasta un valor final. Esta es una de las expresiones CEL más pequeñas que puedes escribir, junto con su salida:</p>mito &lt;(echo '
  "hello" + " " + "world"
')"hello world"<p>Muchas expresiones simples son intuitivas. Las operaciones matemáticas solo se admiten en valores del mismo tipo (por ejemplo, <code>int</code> con <code>int</code>), así que convierte los tipos según necesites (aquí de <code>int</code> a <code>double</code>):</p>mito &lt;(echo '
  double((1 + 2) * (3 + 4)) / 2.0
')10.5<p>No existen variables en el lenguaje CEL, pero una expresión puede recibir un nombre y usarse en una expresión más amplia con la ayuda de la macro <a href="https://pkg.go.dev/github.com/elastic/mito/lib#hdr-As__Macro_-Collections"><code>as</code></a> de Mito. En este ejemplo, la expresión <code>(1 + 1)</code> evalúa al valor <code>2</code> y <code>.as(n, ...)</code> da a ese valor el nombre <code>n</code> para su uso en la expresión <code>"one plus one is "+string(n)</code>:</p>mito &lt;(echo '
  (1 + 1).as(n, "one plus one is "+string(n))
')"one plus one is 2"<p>También es posible acumular información en un mapa y usarla más tarde en la expresión, como se demuestra aquí al usar <a href="https://pkg.go.dev/github.com/elastic/mito/lib#hdr-With-Collections"><code>with</code></a>:</p>mito &lt;(echo '
  { "key": "value" }.with({ "key2": "value2" }).as(data,
    {
      "data": data,
      "size": size(data),
    }
  )
'){
  "data": {
    "key": "value",
    "key2": "value2"
  },
  "size": 2
}<p>Mira ese ejemplo otra vez. Observa que la parte anidada, <code>({ "data": data, "size": size(data), })</code>, nos da la forma del valor final. Es un mapa con las claves <code>"data"</code> y <code>"size"</code>. Los valores de esas claves dependen de <code>data</code>, que la define la parte exterior de la expresión. Leer las expresiones de CEL desde adentro hacia afuera puede ayudar a ver rápidamente qué devolverán.</p><p>CEL no tiene declaraciones de flujo de control, como <code>if</code>, pero la ramificación condicional se puede hacer con el operador ternario:</p>mito &lt;(echo '
  1 + 1 &lt; 12 ? "few" : "many"
')"few"<p>Los bucles ilimitados y la recursión no son compatibles, ya que CEL no es un lenguaje de Turing completo. Esto hace que el tiempo de ejecución sea predecible y proporcional al tamaño de los datos de entrada y a la complejidad de la expresión.</p><p>Aunque los bucles sin límites no son posibles en expresiones CEL individuales, puedes procesar listas y mapas con macros como <a href="https://github.com/google/cel-spec/blob/master/doc/langdef.md#macros"><code>map</code></a>:</p>mito &lt;(echo '
  [1, 2, 3].map(x, x * 2)
')[2, 4, 6]<p>En esta sección, abordamos los siguientes temas:</p><ul><li><p>Textos, números, listas y mapas.</p></li><li><p>Concatenación de texto.</p></li><li><p>Operaciones matemáticas.</p></li><li><p>Conversión de tipos.</p></li><li><p>Condicionales.</p></li><li><p>Nombrando subexpresiones.</p></li><li><p>Procesando colecciones.</p></li></ul><p>A continuación, veremos cómo hacer solicitudes HTTP.</p><h2>Solicitudes</h2><p>Mito extiende el CEL con la capacidad de realizar <a href="https://pkg.go.dev/github.com/elastic/mito/lib#HTTP">solicitudes HTTP</a>:</p>mito &lt;(echo '
  get("https://example.com").as(resp, string(resp.Body))
')"&lt;!doctype html&gt;&lt;html lang=\"en\"&gt;&lt;head&gt;&lt;title&gt;Example Domain&lt;/title&gt;..."<p>Las solicitudes se pueden construir explícitamente antes de que se ejecuten. Esto permite utilizar diferentes métodos HTTP y agregar encabezados y un cuerpo.</p><p>En este ejemplo, construimos una URL con la ayuda de <a href="https://pkg.go.dev/github.com/elastic/mito/lib#hdr-Format_Query-HTTP"><code>format_query</code></a>, agregamos un encabezado a la solicitud y parseamos el cuerpo de la respuesta con <a href="https://pkg.go.dev/github.com/elastic/mito/lib#hdr-Decode_JSON-JSON"><code>decode_json</code></a>. Cuando se te da la opción <code>-log_requests</code>, Mito registra información detallada en formato JSON sobre cada solicitud y respuesta.</p>mito -log_requests &lt;(echo '
  request("GET",
    "https://postman-echo.com/get?" + {
        "q": ["query value"]
     }.format_query()
  ).with({
    "Header": { "Accept": ["application/json"] }
  }).do_request().as(resp, {
    "status": resp.StatusCode,
    "data": resp.Body.decode_json(),
  })
'){"time":"...","level":"INFO","msg":"HTTP request",...}
{"time":"...","level":"INFO","msg":"HTTP response",...}
{
  "data": {
    "args": {
      "q": "query value"
    },
    "headers": {
      "accept": "application/json",
      "accept-encoding": "gzip, br",
      "host": "postman-echo.com",
      "user-agent": "Go-http-client/2.0",
      "x-forwarded-proto": "https"
    },
    "url": "https://postman-echo.com/get?q=query+value"
  },
  "status": 200
}<h2>Gestión del estado y evaluaciones</h2><p>Ahora que ya vimos cómo realizar solicitudes y los conceptos básicos de CEL necesarios para producir el estado de salida deseado, veamos más de cerca qué debemos incluir en el estado de salida y cómo eso nos permite dirigir el procesamiento posterior.</p><p>El programa CEL de una integración debe asegurarse de que su estado de salida sea adecuado para su uso como entrada de la siguiente evaluación. La configuración establece el estado inicial, y eso debe repetirse en la salida con los cambios apropiados. Una forma sencilla de hacerlo es usar <code>state.with({ ... })</code>, para repetir el mapa de estado con algunas sobrescrituras. Un patrón común para programas pequeños es envolver todo el programa en <code>state.with()</code>, para que la propagación de estados no tenga que repetirse en cada rama que genere datos de salida (por ejemplo: éxito, errores).</p><p>Cuando hay valores de estado que se inicializan mediante una evaluación en lugar de estar codificados de forma fija en el estado de entrada inicial, el programa tendrá que comprobar si existe un valor antes de establecer el inicial. Eso es algo en lo que el soporte para <a href="https://pkg.go.dev/github.com/google/cel-go/cel#OptionalTypes">sintaxis y tipos opcionales</a> puede ayudar. Al usar un signo de interrogación antes del nombre del campo en una clave de mapa, el acceso se vuelve opcional: puede o no resolver a un valor, pero son posibles accesos opcionales adicionales y es fácil proporcionar un valor por defecto si no hay un valor presente:
</p>mito -data &lt;(echo '{}') &lt;(echo '
  int(state.?counter.orValue(0)).as(counter,
    state.with({
      "counter": counter + 1,
      "want_more": counter + 1 &lt; 3,
    })
  )
'){ "counter": 1, "want_more": true }
{ "counter": 2, "want_more": true }
{ "counter": 3, "want_more": false }<p>En ese ejemplo, el valor del contador leído desde el estado se convierte en <code>int</code> porque todos los números se serializan en el estado como números de coma flotante, de acuerdo con las convenciones establecidas por el tipo <code>Number</code> de JSON y JavaScript. También debe tenerse en cuenta que Mito honra <code>"want_more": true</code>, pero cuando se ejecuta en la entrada CEL, la evaluación solo se repetiría si la salida también contiene eventos.</p><p>Es un requisito de los programas CEL ejecutados por la entrada CEL que devuelvan una clave <code>"events"</code> en su mapa de salida. Su valor puede ser una lista de mapas de eventos, una lista vacía o un solo mapa de eventos. El caso de evento único generalmente se utiliza para errores. El evento se publicará mediante la entrada, pero su valor también se registrará y, si estableces un valor de <code>error.message</code>, se usará para actualizar el estado de salud de la Fleet de la integración. Si tu programa produce un solo evento sin error, es mejor envolverlo en una lista.</p><p>Echa otro vistazo a la salida de nuestro programa de problemas de GitHub de antes:</p>{
  "url": "https://api.github.com/repos/elastic/integrations/issues",
  "per_page": 3,
  "max_pages": 3,
  "cursor": {
    "page": 2
  },
  "events": [
    { ... },
    { ... },
    { ... }
  ],
  "want_more": true
}<p>El programa gestionó eficazmente su estado, mediante:</p><ul><li><p>Valores de estado iniciales repetidos en <code>url</code>, <code>per_page</code>y <code>max_pages</code>.</p></li><li><p>Agregar un estado que debería persistir durante los reinicios en <code>cursor.page</code>.</p></li><li><p>Devolver eventos listos para publicar en la lista <code>events</code>.</p></li><li><p>Solicito una reevaluación inmediata con <code>want_more: true</code>.</p></li></ul><p>Ahora que entiendes el acceso opcional y la gestión de estados, así como los conceptos básicos de CEL y las solicitudes HTTP, deberías poder leer el programa completo de incidencias de GitHub. Intenta ejecutarlo con Mito y experimentar con algunos cambios.</p><h2>Revisión y recursos</h2><p>En este artículo, analizamos qué es el lenguaje CEL y cómo se ha extendido en la biblioteca Mito para su uso en la entrada CEL. Vimos la flexibilidad de CEL en un programa de ejemplo que obtiene información de incidencias de la API de GitHub, y repasamos todos los detalles necesarios para comprender ese programa, incluidos el acceso a la configuración en el estado inicial, la interacción con las API HTTP, el retorno de eventos para su ingesta y la gestión del estado para ejecuciones posteriores del programa.</p><p>Para aprender más y construir integraciones mediante la entrada CEL, hay varios recursos que valen la pena explorar:</p><ul><li><p><a href="https://www.elastic.co/docs/reference/beats/filebeat/filebeat-input-cel">Entrada CEL - documentación de Filebeat</a></p></li><li><p><a href="https://pkg.go.dev/github.com/elastic/mito">Documentación de Mito</a></p></li><li><p><a href="https://cel.dev/">Lenguaje de expresión común - sitio web cel.dev</a></p></li><li><p><a href="https://www.elastic.co/docs/extend/integrations">Crear una integración - Documentación de Elastic</a></p></li></ul><p>Y quizás el recurso más valioso para crear integraciones con la entrada CEL sea el código CEL de las integraciones existentes de Elastic, que se puede encontrar en GitHub:</p><p><a href="https://github.com/search?q=repo%3Aelastic%2Fintegrations+path%3A**%2Fcel.yml.hbs&amp;type=code"><code>cel.yml.hbs</code></a><a href="https://github.com/search?q=repo%3Aelastic%2Fintegrations+path%3A**%2Fcel.yml.hbs&amp;type=code"> archivos en el repositorio de integraciones de Elastic - GitHub</a></p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/common-expression-language-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/common-expression-language-elasticsearch</guid>
    <category><![CDATA[AI agéntica]]></category>
    <dc:creator><![CDATA[Chris Berkhout]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt330db607ffb818f9/6a1705a08b73cb8502189f4c/985c50bfabee3348494eb4307f0b3375a97a0644-1280x720.png" length="0" type="image/png"/>
    <pubDate>Fri, 27 Feb 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Agent Builder, más allá del chat: presentamos la infraestructura aumentada]]></title>
    <description><![CDATA[Consigue información sobre Elastic Agent Builder con infraestructura aumentada, un agente de IA que ofrece operaciones aumentadas, desarrollo aumentado y Synthetics aumentados.]]></description>
    <content:encoded><![CDATA[<p><strong>Esto no es una especulación. Es un hecho.</strong></p><p>Todos somos testigos del auge de los agentes de IA. Son fantásticos para resumir texto, escribir fragmentos de código y responder preguntas basadas en la documentación. Pero para quienes trabajamos en DevOps y en ingeniería de confiabilidad de sitios (SRE), existe una limitación frustrante. La mayoría de los agentes están atrapados en el paradigma del centro de llamadas, lo que significa que pueden leer, pensar y chatear, pero no pueden interactuar con la infraestructura que se supone que deben administrar.</p><p>Para nuestro último proyecto de hackathon, decidimos superar esa limitación.</p><p>Desarrollamos <strong>infraestructura aumentada</strong>: un copiloto de infraestructura que no solo te da consejos, sino que también crea, despliega, monitorea y corrige el entorno en vivo.</p><h2><strong>El problema: copiar, reformatear, pegar</strong></h2><p>Los agentes estándar operan en el vacío. Si una app se cae y le cuesta a la compañía $5 millones, un agente estándar puede leerte el libro de apuntes sobre cómo arreglarlo. Pero sigue siendo <em>tu responsabilidad</em> hacer el trabajo. Ahora solo tienes que copiar el código, adaptarlo al entorno y pegarlo en tu terminal.</p><p>Queríamos crear un agente que entendiera la diferencia entre <em>hablar</em> de Kubernetes y <em>configurar</em> Kubernetes.</p><h2><strong>El motor: ¿Qué es Elastic Agent Builder?</strong></h2><p>Para desarrollarlo no empezamos desde cero. Partimos de <a href="https://www.elastic.co/es/elasticsearch/agent-builder"><strong>Elastic Agent Builder</strong></a>. Para quienes no lo conozcan, Elastic Agent Builder es un marco de trabajo diseñado para desarrollar agentes de forma rápida, y actúa como puente entre un gran modelo de lenguaje (LLM) (en nuestra demo usamos Google Gemini) y los datos privados almacenados en Elasticsearch.</p><p>Agent Builder se puede usar para la IA conversacional si se lo basa en datos internos, como documentos o registros. Pero su característica más poderosa es la capacidad de asignar <strong>herramientas</strong>. Estas herramientas permiten al LLM salir de la interfaz de chat para realizar tareas específicas. Nos dimos cuenta de que si llevábamos esta característica al límite, podíamos transformar Agent Builder en una potencia de automatización.</p><h2><strong>Cómo hacerlo funcionar: desarrollo de la primera versión</strong></h2><p>Cuando empezamos el proyecto, sabíamos que queríamos que los agentes pudieran cambiar el mundo exterior. Pensamos lo siguiente: ¿y si construimos algún software “runner” (para ejecutar cualquier comando que el agente pueda pensar en el host)? Y luego: ¿qué pasaría si los runners, Elastic Agent Builder y el usuario estuvieran en una llamada los tres?</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltec9d20da8c41a898/6a170704dc55debd4ce00d43/8dc8317c1301b8eb7b89438529e8d8d17411c95a-1024x559.png" alt="Agent Builder with Augmented Infrastructure architecture" /><p>Empezamos desarrollando un proyecto en Python, Augmented Infrastructure Runners, que era esencialmente un bucle while(true) que consultaba la API de conversaciones de Elastic Agent Builder cada segundo y verificaba una sintaxis especial que habíamos creado:</p>{
"tool_name": "my_tool",
       "tool_arguments": "\{stringified json arguments\}"
}<p>Luego actualizamos la indicación para enseñarle sobre nuestra nueva sintaxis de llamada de herramienta. Bill es uno de los mantenedores de <a href="https://gofastmcp.com/getting-started/welcome">FastMCP</a>, el marco de trabajo más popular para crear servidores del Protocolo de Contexto de Modelo (MCP) en Python. Se propuso trabajar usando el cliente FastMCP con este nuevo software runner para montar servidores MCP y poner sus herramientas a disposición del runner. Cuando el agente veía esto, ejecutaba la llamada a la herramienta y publicaba los resultados de vuelta a la conversación como si el usuario hubiera enviado los resultados. Esto hizo que el LLM respondiera al resultado, ¡y seguimos adelante!</p><p>Fue genial, pero tuvo dos problemas principales:</p><ol><li><p>El agente soltaba todo este JSON directamente a la conversación con el usuario.</p></li><li><p>El primer momento en el que los mensajes se podían ver a través de la API de conversaciones era cuando se completaba una ronda de conversación (es decir, cuando el LLM respondía).</p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0596217e962f8464/6a17070647d49c3fef2d890c/7b3755aeae17722ff1bb9677712293e9195f96a0-1058x1034.png" alt="Issue when building agent with augment infrastructure" /><p>Así que nos propusimos descubrir cómo llevarlo a segundo plano.</p><p>Luego cambiamos a darle al agente una herramienta llamada call_external_tool con dos argumentos: el tool_name y los argumentos de la herramienta en JSON como cadena de texto. Esta llamada a la herramienta externa no devolvía nada, pero lo importante es que era visible en la solicitud GET a la API de conversaciones. Luego dimos permiso a los runners para escribir documentos directamente en Elasticsearch, los cuales el agente de Elastic Agent Builder podía recuperar según fuera necesario. El agente siempre está operando en respuesta a un mensaje del usuario, por lo que necesitamos iniciar el agente con un mensaje del usuario para que busque resultados y continúe con el procesamiento. Así que hicimos que los agentes insertaran un pequeño mensaje en el chat para reanudar la conversación:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta22be3c67ad2ff1f/6a170708cdacbf0ae87d295b/61ff59a57c68ed5fad492d19c0580644113a507d-1600x1321.png" alt="Agent Builder with Augmented Infrastructure demostration" /><p>Así que ya teníamos llamadas a herramientas externas. Sin embargo, debido al segundo problema mencionado anteriormente, tuvimos que deshacernos de esa parte final de arranque. De lo contrario, ¡cada llamada a una herramienta externa requería una ronda completa de conversación para recuperar los resultados!</p><h2><strong>Llevarlo a otro nivel: introducción a los flujos de trabajo</strong></h2><p>Además de las llamadas al lenguaje de búsqueda de Elasticsearch (ES|QL) y a las herramientas de búsqueda de índices, los agentes de Agent Builder pueden llamar a herramientas basadas en flujo de trabajo de Elastic. Los flujos de trabajo de Elastic ofrecen una forma flexible y fácil de gestionar para ejecutar una secuencia y una lógica de acciones arbitrarias. Para nuestros objetivos, todo lo que necesitamos que haga el flujo de trabajo es almacenar una solicitud externa de herramienta en Elasticsearch y devolver un ID para consultar los resultados. Esto da lugar a la siguiente definición sencilla de flujo de trabajo:</p>nombre: llamada a la herramienta de IA
 habilitado: verdadero
 desencadenantes:
 - tipo: manual
 entradas:
 - nombre: runner_id
 tipo: texto
 - nombre: llamadas_de_herramientas
 tipo: texto

 pasos:
 - nombre: store_request
 tipo: Elasticsearch.create
 con:
 índice: solicitudes de herramientas distribuidas
 id: "{{inputs.runner_id}}_{{ execution.id }}"
 documento:
 request_id: "{{ execution.id }}"
 runner_id: "{{inputs.runner_id}}"
 llamada_herramienta: "{{inputs.tool_calls}}"
 estado: "sin gestionar"

 - nombre: resultado_de_salida
 tipo: consola
 con:
 mensaje: "Herramienta llamada, con ID de ejecución: {{ execution.id }}. Utiliza este ID para consultar los resultados".<p>Con eso, en vez de depender de que la solicitud de llamada a la herramienta se escriba en la conversación, los runners pueden simplemente consultar el índice distributed-tool-requests de Elasticsearch para nuevas solicitudes externas de herramienta y reportar los resultados en otro índice de Elasticsearch con el execution.id proporcionado.</p><p>Esto elimina los dos problemas principales mencionados anteriormente:</p><ol><li><p>El historial de conversaciones ya no está lleno de datos de las llamadas a herramientas externas.</p></li><li><p>Como los runners consultan el índice de Elasticsearch en lugar del historial de conversaciones, no están bloqueados por la ronda de conversación que debe completarse para que las solicitudes de herramientas externas se hagan visibles.</p></li></ol><p>El segundo punto tiene la gran ventaja de que el procesamiento de las llamadas a herramientas externas comienza dentro de la fase de pensamiento del agente (y no cuando se ha completado la ronda de conversación). Esto nos permite indicarle al modelo de lenguaje grande (LLM) en la indicación del sistema que consulte los resultados de la herramienta externa hasta que estén disponibles, lo que elimina la necesidad del mensaje de inicio. En general, esto tiene el efecto positivo de que la conversación se siente más natural: el LLM puede procesar varias solicitudes de herramienta externa dentro de una sola ronda de conversación (en vez de requerir una ronda de conversación por cada solicitud de herramienta) y, por tanto, puede realizar solicitudes de usuario más complejas de una sola vez.</p><h2><strong>Todo integrado</strong></h2><p>Para cerrar la brecha entre el LLM y el rack de servidores, desarrollamos una arquitectura específica empleando las capacidades de la herramienta de Agent Builder:</p><ol><li><p><strong>Runners de infraestructura aumentada:</strong> desplegamos runners ligeros dentro de los entornos de destino (servidores, clústeres de Kubernetes, cuentas en la cloud). Estos runners se conectan directamente a Elastic, utilizando endpoints seguros y secretos solo disponibles para cada uno de los runners.</p></li><li><p><strong>Recuperación de ES|QL:</strong> el copiloto utiliza <strong>ES|QL</strong> de Elastic para realizar búsquedas híbridas. No solo busca conocimientos, sino que busca <em>capacidades</em>. Consulta a los runners conectados para ver qué herramientas están disponibles (por ejemplo, list_ec2_instances, install_helm_chart).</p></li><li><p><strong>Ejecución del flujo de trabajo:</strong> una vez que el agente decide un curso de acción, crea un flujo de trabajo estructurado.</p></li><li><p><strong>Ciclo de retroalimentación:</strong> los runners ejecutan el comando de forma local y envían los resultados de vuelta a Elasticsearch. El copiloto lee el resultado del índice y decide el siguiente paso.</p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9726199693a10c5c/6a17070ae8fbced43a39fb9a/76be256da722c1965971fc506502768bd890f0c4-1290x1076.png" alt="Architecture using Agent Builder’s tool capabilities with Augmented Infrastructure" /><h2><strong>La demo: de interrupción a observabilidad</strong></h2><p>En el video, mostramos dos casos distintos que demuestran la potencia de esta arquitectura.</p><h3><strong>Escenario 1: Rescate de DevOps</strong></h3><p>Empezamos con un usuario que estaba preocupado por una interrupción de 5 millones de dólares causada por un punto ciego en su clúster de Kubernetes.</p><ul><li><p><strong>La solicitud:</strong> "¿Cómo me aseguro de que esto no vuelva a suceder?"</p></li><li><p><strong>La acción:</strong> el agente no solo proporcionó un tutorial. Identificó el clúster, creó los espacios de nombres necesarios, generó secretos de Kubernetes, instaló el operador OpenTelemetry e instantáneamente proporcionó un enlace a un dashboard APM en vivo.</p></li><li><p><strong>El resultado:</strong> observabilidad completa de Kubernetes e información de aplicación sin que el usuario escriba ni una sola línea de YAML.</p></li></ul><h3><strong>Caso 2: entrega de Security</strong></h3><p>Una regla fundamental de la seguridad de infraestructuras es que no puedes proteger lo que no puedes ver. Mientras llevamos a cabo nuestra intervención de DevOps, el agente ve una oportunidad para mejorar la seguridad del entorno.</p><p>Con una alerta iniciada tras una investigación previa relacionada con Elastic Observability, demostramos cómo un profesional de seguridad puede comunicarse directamente con su infraestructura: primero, para enumerar los activos y recursos en su entorno cloud; y segundo, para desplegar las herramientas necesarias para asegurarse de que el entorno esté protegido.</p><ul><li><p><strong>Descubrimiento:</strong> el copiloto enumeró los recursos de AWS para el profesional de la seguridad e identificó una brecha crítica: una instancia de Amazon Elastic Compute Cloud (EC2) y un clúster de Amazon Elastic Kubernetes Service (EKS) con terminales públicos que no tienen protección para endpoints.</p></li><li><p><strong>Remediación:</strong> con una simple aprobación, el copiloto desplegó <strong>Elastic Security</strong> <strong>detección y respuesta extendida (XDR) y detección y respuesta en el cloud (CDR)</strong> a los activos vulnerables, asegurando el entorno en tiempo real.</p></li><li><p><strong>El resultado:</strong> protección de los activos y recursos de AWS desplegados con seguridad en tiempo de ejecución completa.</p></li></ul><h2><strong>El futuro: todo aumentado</strong></h2><p>Este proyecto demuestra que Elastic Agent Builder puede ser el cerebro central de las operaciones distribuidas. No nos limitamos solo a infraestructura. Nuestra tecnología runner puede impulsar:</p><ul><li><p><strong>Synthetics aumentados:</strong> diagnosticar errores de TLS en runners globales.</p></li><li><p><strong>Desarrollo aumentado:</strong> crear pull requests e implementar CAPTCHAs en servicios frontend.</p></li><li><p><strong>Operaciones aumentadas:</strong> reconfiguración automática de los servidores DNS durante una interrupción.</p></li></ul><h2><strong>Pruébalo tú mismo</strong></h2><p>Creemos que el futuro de la IA no se trata solo de soporte por chat; se trata de <strong>infraestructura aumentada</strong>. Se trata de tener un socio que pueda desplegar, arreglar, observar y proteger a la par tuya.</p><p>¡Consulta el código y pruébalo tú mismo con runners distribuidos (<a href="https://github.com/strawgate/augmented-infrastructure">GitHub</a>) además de Elastic Agent Builder en <a href="https://cloud.elastic.co/">Elastic Cloud Serverless</a> hoy mismo!</p><ul><li><p>Crea un proyecto serverless en Elastic Cloud.</p></li><li><p>Despliega el código en un ejecutor.</p></li><li><p>Configura el runner.</p></li><li><p>Configura tu archivo mcp.json.</p></li><li><p>Inicia el runner, el cual creará de forma automática tu agente y sus herramientas.</p></li><li><p>¡Chatea con un agente que puede razonar, planificar y ejecutar acciones en tus runners distribuidos!</p></li></ul><p><strong>El equipo: </strong><em>Alex, Bill, Gil, Graham, &amp; Norrie</em></p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/agent-builder-augmented-infrastructure</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/agent-builder-augmented-infrastructure</guid>
    <category><![CDATA[AI agéntica]]></category>
    <category><![CDATA[Herramientas de IA ]]></category>
    <dc:creator><![CDATA[Alexander Wert,Bill Easton,Gil Raphaelli,Graham Hudgins,Norrie Taylor]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6de9245ad57ccc00/6a17070cdc55deaa39e00d48/e08daf78f328e826f39d06329f6a5487f75d178d-1272x700.png" length="0" type="image/png"/>
    <pubDate>Thu, 22 Jan 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Agent Builder ya está disponible para el público en general: envía agentes según el contexto en cuestión de minutos]]></title>
    <description><![CDATA[Agent Builder ahora está disponible para el público en general. Aprende cómo te permite desarrollar rápidamente agentes de IA basados en el contexto.]]></description>
    <content:encoded><![CDATA[<p>Nos complace anunciar la disponibilidad general de Agent Builder en Elastic Cloud Serverless y en la próxima versión 9.3. Agent Builder aprovecha el poder de Elasticsearch como plataforma de ingeniería del contexto para desarrollar rápidamente agentes de IA contextuales y centrados en los datos.</p><p>Los agentes están ganando terreno impulsados por su potencial para ofrecer aumentos en la eficiencia y mejores experiencias del cliente. Pero en la práctica, ofrecer a los agentes el contexto adecuado es difícil, especialmente cuando se opera sobre datos empresariales desordenados y no estructurados. Los desarrolladores deben gestionar herramientas, indicaciones, estados, lógica de razonamiento, modelos y, lo que es más importante, recuperar el contexto relevante de las fuentes empresariales para ofrecer resultados y acciones precisos. Elastic Agent Builder ofrece estos componentes centrales para desarrollar agentes seguros, confiables y basados en el contexto.</p><h2>Capacidades centrales de Agent Builder</h2><p>Agent Builder aprovecha las inversiones a largo plazo de Elastic en relevancia de búsqueda y retrieval-augmented generation, y trabaja para convertir a Elasticsearch en la mejor base de datos vectorial que simplifique el desarrollo de agentes de IA contextuales y centrados en datos.</p><p>Agent Builder te permite:</p><ul><li><p>Comienza inmediatamente con un agente conversacional integrado que pueda responder preguntas, realizar análisis e impulsar investigaciones sobre cualquier dato en Elasticsearch.</p></li><li><p>Pasa rápidamente de datos complejos y datos no estructurados a un agente personalizado con una experiencia de desarrollo basada en la configuración.</p></li><li><p>Aprovecha la mejor relevancia de búsqueda híbrida de su clase mediante ES|QL integrado o herramientas personalizadas para mejorar la calidad del contexto y la confiabilidad del agente.</p></li><li><p>Ejecuta flujos de trabajo complejos (vista previa) como herramientas reutilizables para enriquecer datos, actualizar registros, enviar mensajes y mucho más para la automatización basada en reglas.</p></li><li><p>Conéctate a fuentes de datos fuera de Elasticsearch usando flujos de trabajo y MCP para correlacionar y combinar el contexto para los agentes.</p></li><li><p>Integra con cualquier marco de trabajo de aplicación o agente utilizando herramientas integradas y personalizadas expuestas a través de MCP, y la capacidad de conectarte a MCP externo (vista previa), soporte para A2A y soporte completo de API.</p></li><li><p>Amplía las capacidades de Agent Builder con la integración de soluciones de terceros, como LlamaIndex para el procesamiento de documentos complejos, o Arcade.dev para un acceso seguro y estructurado a las herramientas.</p></li></ul><p>Para ampliar aún más la funcionalidad de Agent Builder, presentamos Elastic Workflows, nuestras nuevas capacidades de automatización basadas en reglas, ahora en versión preliminar técnica. Para las tareas organizacionales, los agentes a veces necesitan certeza y confiabilidad en las acciones basadas en reglas, que a menudo son necesarias para implementar una lógica de negocio específica. Elastic Workflows ofrece a los agentes una forma sencilla y declarativa de orquestar sistemas internos y externos para realizar acciones, recopilar y transformar datos y contexto. Los flujos de trabajo son totalmente componibles, orientados a eventos y flexibles, y pueden exponerse como herramientas a un agente mediante MCP.</p><h2>De los datos al agente en minutos</h2><p>El desarrollo de agentes puede llevar semanas de trabajo previo para consolidar almacenes de datos separados, construir pipelines manuales, ajustar búsquedas y gestionar una orquestación compleja. Agent Builder reduce el tiempo de desarrollo de agentes al eliminar la necesidad de almacenes de datos separados, bases de datos vectoriales, pipelines RAG, capas de búsqueda, traductores de búsquedas y orquestadores de herramientas, lo que te permite centrarte en la lógica de agentes y la entrega de aplicaciones.</p><p>Agent Builder integra de forma nativa las primitivas de la plataforma Elasticsearch para que el desarrollo de agentes sea rápido.</p><ul><li><p>Comienza con un agente conversacional integrado que pueda chatear inmediatamente y razonar con tus datos indexados.</p></li><li><p>Integra agentes en aplicaciones, dashboards o sistemas CI/CD con acceso interactivo a través de Kibana, API o MCP y A2A.</p></li><li><p>Construye con herramientas predeterminadas para entender tu estructura de datos, selecciona el índice adecuado, genera búsquedas híbridas, semánticas y estructuradas optimizadas, y crea visualizaciones configurables usando ES|QL basado en prompts de lenguaje natural.</p></li></ul><p>Para profundizar más, prueba un <a href="https://www.elastic.co/search-labs/blog/ai-agent-builder-elasticsearch">recorrido práctico</a> completo.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd8def92028138672/6a17e086af47b60cd8cdde96/b55b63eae40f72952967cc8f3ea4df4cd62d7d70-1080x608.gif" alt="Guía paso a paso de Elastic Agent Builder" /><h2>Desarrolla sobre Elasticsearch, una plataforma de datos completa para la ingeniería de contexto</h2><p>Para los agentes de IA, la calidad del contexto es esencial para brindar un razonamiento eficaz y reducir los riesgos de alucinación. Para muchos agentes de IA empresarial, los datos comerciales necesarios para realizar una tarea son el contexto más decisivo. Como almacén de datos masivamente escalable, base de datos vectorial y líder en relevancia, Elasticsearch ya ofrece muchas primitivas sólidas de ingeniería de contexto. La ingeniería de contexto va más allá de la simple retrieval-augmented generation, ya que te permite adaptar y escalar la forma en que los datos se recuperan, se clasifican, se filtran y se presentan a los agentes, lo que ayuda a reducir el ruido y la ambigüedad.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc4c10c1d09e9f81e/6a17e087577262feb31bcb4b/419b9b6f13739e0a8983249d8ac31478e73dac89-1600x901.png" alt="Diagrama de Agent Builder" /><p>Elasticsearch ofrece un motor de contexto que combina búsqueda léxica, búsqueda vectorial y filtrado estructurado para la recuperación, <a href="https://www.elastic.co/search-labs/blog/context-engineering-relevance-ai-agents-elasticsearch">lo cual mejora sustancialmente el rendimiento de los LLM</a> al garantizar que el modelo opere en un contexto relevante y preciso. Esta capacidad está respaldada por recuperación agente, junto con herramientas integradas y lógica de búsqueda que seleccionan automáticamente los índices correctos y transforman el lenguaje natural en búsquedas optimizadas para el contexto.</p><p>Con Agent Builder, puedes asegurarte de que los agentes reciban primero el contexto más útil con controles de relevancia y clasificación, lo que te permite ajustar la lógica de calificación, clasificación y filtrado. Elasticsearch te permite controlar qué importa, por qué importa y cómo se prioriza, en lugar de depender de un comportamiento de recuperación opaco. Todo esto está respaldado por Elasticsearch como una plataforma de datos escalable para almacenar y escalar todos tus datos, desde texto, vectores, metadatos, logs y más en una plataforma, lo que facilita la gestión del contexto para los agentes.</p><h2>Ejecutar flujos de trabajo complejos como herramientas reutilizables</h2><p>Aunque los agentes de IA permiten razonar tareas complejas, gran parte de la automatización depende de ejecutar de forma confiable acciones basadas en reglas que impongan una lógica de negocio específica. Elastic Workflows ofrece una forma sencilla y declarativa de orquestar sistemas internos y externos para realizar acciones, recopilar contexto o datos e integrarlos como parte de los agentes. Definidos en YAML, los flujos de trabajo son totalmente componibles, lo que permite que sean tan simples o complejos como requiera el trabajo. Esto ofrece a los agentes una forma eficiente de actuar en toda la plataforma y soluciones de Elasticsearch, así como con aplicaciones de terceros.</p><p>La integración de un flujo de trabajo con Agent Builder se puede realizar en tres pasos (requisito previo: habilitar los flujos de trabajo con los detalles que aparecen <a href="https://github.com/elastic/workflows">aquí</a>).</p><p>1. Crea y guarda un nuevo flujo de trabajo usando el sencillo editor basado en YAML con función de autocompletado y pruebas integradas.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt00585158429a3395/6a17e089e317916b122d5740/308888bf3d2fa013f9391a55be6a6fbd458b6dac-1600x998.png" alt="Flujo de trabajo de Agent Builder" /><p>2. Crea una nueva herramienta en Agent Builder con el tipo “flujo de trabajo” e ingresa una descripción para ayudar al agente a determinar cuándo usar la herramienta de flujo de trabajo.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt874b6a1ce3a2ac34/6a17e08be9ea87b1dea9c4d9/c04810d30d226112c3610bd58e208607b213fc3d-1600x945.png" alt="Crea una nueva herramienta en Agent Builder" /><p>3. Agrega la herramienta de flujo de trabajo a tu agente personalizado.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt94a31cb60ef11ce6/6a17e08daf47b61f0dcdde9a/724cd4ac93c46efb0d339fd140e5caf138f8150f-1600x948.png" alt="Agrega la herramienta de flujo de trabajo a tu agente personalizado." /><p>4. ¡Eso es todo! Ahora el agente puede llamar al flujo de trabajo desde una conversación.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5143f401a06e8ba2/6a17e08fdbb4ffcfc6fb55de/8dfdd726ab89e31c48b79372650ce33946713dca-1600x929.png" alt="Se ha creado un agente de IA con Elastic Agent Builder" /><h2>Tu agente, tus reglas</h2><p>Agent Builder no te limita a un solo paradigma de desarrollo. En cambio, está diseñado para permitir enfoques de desarrollo abiertos y flexibles para los agentes, con control total sobre los datos, la relevancia, los modelos, la interoperabilidad, la seguridad y el diseño de los agentes.</p><p>Las definiciones personalizadas de agentes te permiten elegir exactamente a qué herramientas puede acceder un agente, incorporar avisos personalizados del sistema, adaptar las instrucciones del agente y definir los límites de seguridad. Los agentes siguen siendo independientes del modelo, lo que te permite configurar de manera flexible un LLM de preferencia, tanto nativo como en todo el ecosistema más amplio, sin estar limitado a un solo proveedor.</p><p>Desarrolla herramientas extensibles que encapsulen lógica específica de dominio (por ejemplo, filtros de índice específicos, ES|QL joins, pipelines analíticas), y aplica limitaciones para un uso seguro en producción. El soporte completo de API permite la interoperabilidad con otros marcos de trabajo agentes, con soporte nativo para Model Context Protocol (MCP). La integración A2A significa que puedes exponer tus Elastic Agent a otros marcos de trabajo, servicios y apps de clientes, reutilizando los mismos datos y la lógica de ingeniería de contexto a través de las integraciones.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt309a0b3dd4cc367b/6a17e090ec0f8932045a6550/5e903ba24ffb3f40231e901f63bd494c89cb7757-1600x1004.png" alt="Configuración del agente de IA con Elastic Agent Builder" /><p>Agent Builder permite un desarrollo flexible y abierto y está diseñado para integrarse fácilmente con marcos de trabajo y plataformas populares de agentes. Estas integraciones pueden ser esenciales para ofrecer agentes efectivos. Como describe <strong>Sam Partee, cofundador de Arcade.dev</strong>,</p><p><em>“Los sistemas agénticos fallan hoy porque conectar la IA a herramientas y datos es complejo. Elastic Agent Builder con Arcade.dev ofrece a los desarrolladores una forma estructurada y segura de manejar cómo los agentes recuperan el contexto, la razón y la acción, lo que lleva a los agentes de la demostración al grado de producción".</em></p><p>Agent Builder también aprovecha la extensibilidad de Elasticsearch para manejar datos complejos. Como describe <strong>Jerry Liu, CEO de LlamaIndex</strong>,</p><p><em>“Desbloquear el contexto empresarial de las fuentes de datos no estructurados es clave para construir agentes efectivos. El procesamiento de documentos complejos de Elastic Agent Builder combinado de LlamaIndex fortalece la capa de contexto crítico, lo que ayuda a los equipos a recuperar, procesar y preparar datos para que los agentes puedan razonar con mayor precisión y ofrecer mejores resultados”.</em></p><h2>¿Qué puedes construir?</h2><p>Agent Builder ya se está utilizando para una variedad de casos de uso. A continuación, se muestran algunos ejemplos y arquitecturas de referencia para empezar a utilizar los agentes:</p><ul><li><p><strong>Automatizar la infraestructura: </strong>En escenarios de soporte, los agentes se han utilizado para leer, pensar y chatear, pero hasta la fecha, no pueden comunicarse y contactarse con la infraestructura que puede que sea necesario administrar. El equipo de ingeniería de Elastic creó un agente para la <a href="https://www.elastic.co/search-labs/blog/agent-builder-augmented-infrastructure">gestión automatizada de la infraestructura</a> como parte de un hackatón. El agente investiga activamente los problemas relacionados con la infraestructura de la aplicación y toma medidas automatizadas. Utiliza flujos de trabajo para optimizar configuraciones, responder a problemas y escalar recursos, todo ello basado en una comprensión inteligente de los logs de infraestructura.</p></li><li><p><strong>Análisis de amenazas a la seguridad: </strong>Se desarrolló un agente de vulnerabilidad de seguridad con Elastic Agent Builder, MCP y Elasticsearch. Automatiza el análisis de amenazas correlacionando datos de seguridad interna con inteligencia de amenazas externas. El agente realiza búsquedas semánticas sobre incidentes y configuraciones históricas, incrementa los resultados con datos en tiempo real de Internet y aplica razonamientos LLM para evaluar la relevancia ambiental, priorizar riesgos y producir remediaciones accionables. Consulta la <a href="https://www.elastic.co/search-labs/blog/agent-builder-mcp-reference-architecture-elasticsearch">arquitectura de referencia</a><strong>.</strong></p></li><li><p><strong>Soporte técnico al cliente: </strong>Los agentes pueden realizar múltiples tareas de soporte, incluyendo resumen de casos, desduplicación y creación de problemas, e investigación técnica profunda. Agent Builder permite esto mediante una búsqueda híbrida de varios pasos para encontrar solo los problemas, soluciones y procedimientos relacionados más relevantes, y formular hipótesis de causa raíz y planes de remediación. Agent Builder puede simplificar la arquitectura de <a href="https://www.elastic.co/blog/generative-ai-customer-support-elastic-support-assistant">sistemas de soporte</a> complejos y acelerar el tiempo de entrega.</p></li><li><p><strong>Descubrimiento de productos y contenido:</strong> Agent Builder simplifica el proceso de <a href="https://www.elastic.co/search-labs/blog/build-voice-agents-elastic-agent-builder">exponer catálogos de productos complejos para experiencias conversacionales</a>, al tiempo que permite a las organizaciones mantener la flexibilidad para incluir sus propios requisitos y lógica de negocio.</p></li><li><p><strong>Haz tu propio desarrollo:</strong> Únete al <a href="https://elasticsearch.devpost.com/">Agent Builder Hackathon, que</a> se celebrará del 22 de enero al 27 de febrero de 2026. Trabaja con la comunidad para crear agentes de IA basados en el contexto y en varios pasos que combinen búsqueda, flujos de trabajo, herramientas y razonamiento para automatizar tareas del mundo real*</p></li></ul><h2>Comienza a construir agentes personalizados ahora</h2><p>Comienza con una <a href="https://cloud.elastic.co/registration?onboarding_token=search&amp;pg=en-enterprise-search-page">prueba de Elastic Cloud</a> y revisa la documentación <a href="https://www.elastic.co/docs/solutions/search/elastic-agent-builder">aquí</a>. Para los clientes existentes, Agent Builder está disponible en Cloud Serverless y en el nivel Empresarial en Elastic Cloud Hosted y es autoadministrado.</p><p>* <a href="https://elasticsearch.devpost.com/rules">Haz clic aquí</a> para conocer los términos, condiciones y requisitos de elegibilidad completos para la hackatón</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/agent-builder-elastic-ga</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/agent-builder-elastic-ga</guid>
    <category><![CDATA[AI agéntica]]></category>
    <category><![CDATA[Elastic Cloud Serverless]]></category>
    <dc:creator><![CDATA[Anish Mathur,Evan Castle]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta5ffa581514d8b8c/6a17e092dbb4fff61afb55e2/6840eb7dbb884055ab0e965dcfd614fec54936af-2210x1440.png" length="0" type="image/png"/>
    <pubDate>Thu, 22 Jan 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Crear agentes de voz con Elastic Agent Builder]]></title>
    <description><![CDATA[Explorar cómo funcionan los agentes de voz y cómo crear uno con Elastic Agent Builder y LiveKit.]]></description>
    <content:encoded><![CDATA[<p>La AI ha estado atrapada en una caja de cristal. Escribes comandos, te responde con texto y eso es todo. Es útil, pero distante, como ver a alguien moverse detrás de una pantalla. Este año, 2026, será el año en que las empresas romperán ese cristal y llevarán a los agentes de AI a los productos, donde realmente aportan valor.</p><p>Una de las formas en que se romperá el “cristal” es mediante la adopción de <em>agentes de voz</em>, que son agentes de AI que reconocen el habla humana y sintetizan audio generado por computadora. Esto se ha vuelto posible gracias al auge de las transcripciones de baja latencia, los modelos de lenguaje grandes (LLM) rápidos y los modelos de texto a voz que suenan humanos.</p><p>Los agentes de voz también necesitan acceso a los datos empresariales para ser realmente valiosos. En este blog, aprenderemos cómo funcionan los agentes de voz y diseñaremos uno para ElasticSport, una tienda ficticia de equipamiento deportivo al aire libre, con <a href="https://livekit.io/">LiveKit</a> y <a href="https://www.elastic.co/elasticsearch/agent-builder">Elastic Agent Builder</a>. Nuestro agente de voz será consciente del contexto y trabajará con nuestros datos.</p><h2>Cómo funciona</h2><p>Existen dos paradigmas en el mundo de los agentes de voz: el primero usa modelos de voz a voz, y el segundo usa un pipeline de voz compuesto por voz a texto, LLM y texto a voz. Los modelos de voz a voz tienen sus propios beneficios, pero los pipelines de voz ofrecen mucha más personalización sobre las tecnologías utilizadas y cómo se gestiona el contexto, además de un mayor control sobre el comportamiento del agente. Nos enfocaremos en el modelo de pipeline de voz.</p><h3>Componentes clave</h3><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7dbeb09a3743f38d/6a17de9caf47b67330cdde7d/b237501903f9c3a71fe1b7755c3990e40c5495c8-1600x653.png" alt="Arquitectura para construir un agente de voz con AI​ con Elastic Agent Builder" /><h4>Transcripción (voz a texto)</h4><p>La transcripción es el punto de entrada del pipeline de voz. El componente de transcripción toma como entrada fragmentos de audio sin procesar, transcribe el habla en texto y entrega ese texto como salida. El texto transcrito se almacena en un búfer hasta que el sistema detecta que el habla del usuario ha terminado; en ese momento, se inicia la generación del LLM. Varios proveedores externos ofrecen transcripciones de baja latencia. Al seleccionar uno, considera la latencia y la precisión de la transcripción, y asegúrate de que soporten transcripciones en streaming.</p><p></p><p>Ejemplos de API de terceros: <a href="https://www.assemblyai.com/">AssemblyAI</a>, <a href="https://deepgram.com/product/speech-to-text">Deepgram</a>, <a href="https://platform.openai.com/docs/guides/realtime-transcription">OpenAI</a>, <a href="https://elevenlabs.io/speech-to-text">ElevenLabs</a></p><h4>Detección de turnos</h4><p>La detección de turnos es el componente del pipeline que detecta cuándo el hablante ha terminado de hablar y la generación debería comenzar. Una forma común de hacer esto es mediante un modelo de detección de actividad vocal (VAD), como <a href="https://github.com/snakers4/silero-vad">Silero VAD</a>. El VAD utiliza los niveles de energía del audio para detectar cuándo contiene habla y cuándo ha terminado. Sin embargo, el VAD por sí solo no puede identificar la diferencia entre una pausa y el final del discurso. Por eso, a menudo se combina con un modelo de fin de enunciado que predice si el hablante ha terminado de hablar, basándose en la transcripción provisional o el audio sin procesar.</p><p>Ejemplos (Hugging Face): <a href="https://huggingface.co/livekit/turn-detector">livekit/turn-detector</a>, <a href="https://huggingface.co/pipecat-ai/smart-turn-v3">pipecat-ai/smart-turn-v3</a></p><h4>Agente</h4><p>El agente es el núcleo de un pipeline de voz. Es responsable de entender la intención, reunir el contexto adecuado y formular una respuesta en formato de texto. <a href="https://www.elastic.co/elasticsearch/agent-builder">Elastic Agent Builder</a>, con sus capacidades de razonamiento integradas, su biblioteca de herramientas y la integración de flujos de trabajo, permite crear un agente que puede trabajar sobre tus datos e interactuar con servicios externos.</p><h4>LLM (texto a texto)</h4><p>Al seleccionar un LLM para Elastic Agent Builder, hay dos características principales a considerar: las evaluaciones de razonamiento del LLM y el tiempo hasta el primer token (TTFT).</p><p>Las evaluaciones de razonamiento indican qué tan bien el LLM es capaz de generar respuestas correctas. Las evaluaciones que considerar son aquellas que evalúan la adherencia a las conversaciones de varios turnos y las de inteligencia, como MT-Bench y el set de datos Humanity's Last Exam, respectivamente.</p><p>Las evaluaciones de TTFT evalúan qué tan rápido produce el modelo su primer token de salida. Existen otros tipos de evaluaciones de latencia, pero el TTFT es particularmente importante para los agentes de voz, ya que la síntesis de audio puede comenzar tan pronto como se recibe el primer token, lo que resulta en una menor latencia entre turnos y una conversación que se siente natural.</p><p>Por lo general, hay que elegir un equilibrio entre estas dos características, ya que los modelos más rápidos suelen tener un peor desempeño en las evaluaciones de razonamiento.</p><p>Ejemplos (Hugging Face): <a href="https://huggingface.co/openai/gpt-oss-20b">openai/gpt-oss-20b</a>, <a href="https://huggingface.co/openai/gpt-oss-120b">openai/gpt-oss-120b</a></p><h4>Síntesis (texto a voz)</h4><p>La parte final del pipeline es el modelo de texto a voz. Este componente es responsable de convertir la salida de texto del LLM en audio audible. Al igual que con el LLM, la latencia es una característica para tener en cuenta al momento de seleccionar un proveedor de texto a voz. La latencia de texto a voz se mide por el tiempo hasta el primer byte (TTFB). Es el tiempo que tarda en recibirse el primer byte de audio. Un TTFB más bajo también reduce la latencia entre turnos.</p><p>Ejemplos: <a href="https://elevenlabs.io/text-to-speech-api">ElevenLabs</a>, <a href="https://cartesia.ai/sonic">Cartesia</a>, <a href="https://www.rime.ai/">Rime</a></p><h4>Desarrollar el pipeline de voz</h4><p>Elastic Agent Builder puede integrarse en un pipeline de voz en varios niveles diferentes:</p><ol><li><p>Solo herramientas de Agent Builder: voz a texto → LLM (con herramientas de Agent Builder) → texto a voz</p></li><li><p>Agent Builder como MCP: conversión de voz a texto → LLM (con acceso a Agent Builder a través de MCP) → conversión de texto a voz</p></li><li><p>Agent Builder como núcleo: voz a texto → Agent Builder → texto a voz</p></li></ol><p>Para este proyecto, elegí Agent Builder como el enfoque núcleo. Con este enfoque, se puede usar toda la funcionalidad de Agent Builder y los flujos de trabajo. El proyecto usa LiveKit para orquestar voz a texto, detección de turnos y texto a voz, e implementa un nodo LLM personalizado que se integra directamente con Agent Builder.</p><h2>Agente de voz de soporte de Elastic</h2><p>Vamos a construir un agente de voz de soporte personalizado para una tienda de deportes ficticia llamada ElasticSport. Los clientes podrán llamar a la línea de ayuda, pedir recomendaciones de productos, buscar detalles de artículos, consultar el estado de sus pedidos y pedir que se les envíe la información del pedido por mensaje de texto. Para lograr esto, primero necesitamos configurar un agente personalizado y crear herramientas para ejecutar consultas y flujos de trabajo en el lenguaje de búsqueda de Elasticsearch (ES|QL).</p><h3>Configurar el agente</h3><h4>Indicación</h4><p>La indicación le señala al agente qué personalidad debe adoptar y cómo responder. Es importante destacar que hay algunas indicaciones específicas para voz que garantizan que las respuestas se sinteticen correctamente en audio y que los malentendidos se resuelvan con elegancia.</p>You are a Sales Assistant at ElasticSport, an outdoor sport shop specialized in hiking and winter equipment. 

[Profile]
- name: Iva
- company: ElasticSport
- role: Sales Assistant
- language: en-GB
- description: ElasticSport virtual sales assistant

[Context]
- Ask clarifying questions to understand the context.
- Use available tools to answer the user's question.
- Use the knowledge base to retrieve general information

[Style]
- Be informative and comprehensive.
- Maintain a professional, friendly and polite tone.
- Mimic human behavior and speech patterns.
- Be concise. Do not over explain initially

[Response Guideline]
- Present dates in spelled-out month date format (e.g., January fifteenth, two thousand and twenty-four).
- Avoid the use of unpronounceable punctuation such as bullet points, tables, emojis.
- Respond in plain text, avoid any formatting.
- Spell out numbers as words for more natural-sounding speech.
- Respond in short and concise sentences. Responses should be 1 or 2 sentences long.

[ERROR RECOVERY]
### Misunderstanding Protocol
1. Acknowledge potential misunderstanding
2. Request specific clarification<h4>Flujos de trabajo</h4><p>Agregaremos un pequeño flujo de trabajo para enviar un SMS a través de la API de mensajería de Twilio. El flujo de trabajo se expondrá al agente personalizado como una herramienta, lo que dará como resultado una experiencia de usuario donde el agente puede enviar al usuario un SMS mientras está en la llamada. Esto permite que quien llama pueda decir, por ejemplo: “¿Puedes enviarme más detalles sobre <em>X</em> por mensaje de texto?”.</p>name: send sms
enabled: true
triggers:
  - type: manual
inputs:
  - name: message
    type: string
    description: The message to send to the phone number.

  - name: phone_number
    type: string
    description: The phone number to send the message to.

consts:
  TWILIO_ACCOUNT: "****"
  BASIC_AUTH: "****"
  FROM_PHONE_NNUMBER: "****"
steps:
  - name: http_step
    type: http
    with:
      url: https://api.twilio.com/2010-04-01/Accounts/{{consts.TWILIO_ACCOUNT}}/Messages.json
      method: POST
      headers:
        Content-Type: application/x-www-form-urlencoded
        Authorization: Basic {{consts.BASIC_AUTH | base64_encode}}
      body: From={{consts.FROM_PHONE_NNUMBER}}&amp;To={{inputs.phone_number}}&amp;Body={{inputs.message}}
      timeout: 30s<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt960a9395fb0985bf/6a17de9e4b055d1dff4320f0/b057e71b0a7c50eb3da47cd4f95e77ec7b4c6126-1600x1245.png" alt="Crear una nueva herramienta para el agente de voz de IA con Elastic Agent Builder" /><h4>Herramientas ES|QL</h4><p>Las siguientes herramientas permiten que el agente proporcione respuestas relevantes basadas en datos reales. El repositorio de ejemplo contiene un script de configuración para inicializar Kibana con sets de datos de productos, pedidos y base de conocimientos.</p><ul><li><p><strong>Product.search</strong></p></li></ul><p>El set de datos de productos contiene 65 productos ficticios. Este es un documento de ejemplo:</p>{
      "sku": "ort3M7k",
      "name": "Ortovox Free Rider 26 Backpack",
      "price": 189,
      "currency": "USD",
      "image": "https://via.placeholder.com/150",
      "description": "The Ortovox Free Rider 26 is a technical freeride backpack with a dedicated safety compartment and diagonal ski carry system. Perfect for backcountry missions.\n\nKey Features:\n- 26L capacity\n- Diagonal ski carry system\n- Safety equipment compartment\n- Helmet holder\n- Hydration system compatible",
      "category": "Accessories",
      "subCategory": "Backpacks",
      "brand": "Ortovox",
      "sizes": ["One Size"],
      "colors": ["Black", "Blue", "Orange"],
      "materials": ["Nylon", "Polyester"]
    }<p>Los campos de nombre y descripción se mapean como <code>semantic_text</code>, lo que permite al LLM usar la búsqueda semántica a través de ES|QL para recuperar productos relevantes. La consulta de búsqueda híbrida realiza una coincidencia semántica en ambos campos, aplicando un peso ligeramente mayor a las coincidencias en el campo de nombre mediante un refuerzo.</p><p>La búsqueda primero recupera los 20 mejores resultados clasificados por su puntuación de relevancia inicial. Luego, estos resultados se reclasifican basándose en su campo de descripción utilizando el modelo de inferencia <code>.rerank-v1-elasticsearch</code> y, finalmente, se reducen a los cinco productos más relevantes.</p>type: ES|QL
toolId: products.search
description: Use this tool to search through the product catalogue by keywords.
query: |
    FROM products
        METADATA _score
      | WHERE
          MATCH(name, ?query, {"boost": 0.6}) OR
            MATCH(description, ?query, {"boost": 0.4})
      | SORT _score DESC
      | LIMIT 20
      | RERANK ?query
            ON description
            WITH {"inference_id": ".rerank-v1-elasticsearch"}
      | LIMIT 5

parameters:
    query: space separated keywords to search for in catalogue<ul><li><p><strong>Knowledgebase.search</strong></p></li></ul><p>Los sets de datos de la base de conocimientos contienen documentos con la siguiente estructura, en los que los campos de título y contenido se almacenan como texto semántico:</p>{
        id: "8273645",
        createdAt: "2025-11-14",
        title: "International Orders",
        content: `International orders are processed through our international shipping partner. Below are the countries we ship to and average delivery times.
        Germany: 3-5 working days
        France: 3-5 working days
        Italy: 3-5 working days
        Spain: 3-5 working days
        United Kingdom: 3-5 working days
        United States: 3-5 working days
        Canada: 3-5 working days
        Australia: 3-5 working days
        New Zealand: 3-5 working days
        `
}<p>Y la herramienta usa una búsqueda similar a la herramienta <code>product.search</code>:</p>type: "ES|QL"
toolId: knowledgebase.search
description: Use this tool to search the knowledgebase.
query: |
  FROM knowledge_base
    METADATA _score
  | WHERE
      MATCH(title, ?query, {"boost": 0.6}) OR
      MATCH(content, ?query, {"boost": 0.4})
  | SORT _score DESC
  | LIMIT 20
  | RERANK ?query
      ON content
      WITH {"inference_id": ".rerank-v1-elasticsearch"}
  | LIMIT 5

parameters:
  query: space separated keywords or natural language phrase to semantically search for in the knowledge base<ul><li><p><strong>Orders.search</strong></p></li></ul><p>La herramienta final que agregaremos es la que se usa para recuperar pedidos por <code>order_id</code>:</p>type: "ES|QL"
toolId: order.search
description: Use this tool to retrieve an order by its ID.
query: |
  FROM orders
    METADATA _score
  | WHERE order_id == ?order_id
  | SORT _score DESC
  | LIMIT 1

parameters:
  order_id: "the ID of the order"<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfaaf9634f27f70d7/6a17dea07f6f15b8d2c09a3d/d22bdd540a95b5a9c2bd5f308620835e8e6f7ecb-1600x1361.png" alt="Configuración del agente de voz" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8f4c704ab96c294a/6a17dea23e03d74af14f2b7e/d91709a50fb5391876b714885242d998b2b21027-1600x1443.png" alt="Herramientas de agentes de voz" /><p>Después de configurar el agente y vincular estos flujos de trabajo y herramientas ES|QL, puedes probar al agente dentro de Kibana.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdbfd2934a9582c04/6a17dea463baff1532741b5e/8691f41624247a6b1352d158c970031e1426ce5e-1600x1056.png" alt="Probar el agente de voz" /><p>Además de desarrollar un agente de soporte para ElasticSport, el agente, los flujos de trabajo y las herramientas pueden adaptarse a otros casos de uso, como un agente de ventas que califica clientes potenciales, un agente de servicio para reparaciones del hogar, reservas para un restaurante o un agente para agendar citas.</p><p></p><p>La parte final es conectar el agente que acabamos de crear con LiveKit, los modelos de texto a voz y de voz a texto. El repositorio enlazado al final de este blog contiene un nodo de LLM personalizado de Elastic Agent Builder que se puede usar con LiveKit. Solo hay que sustituir el <code>AGENT_ID</code> por el tuyo propio y enlazarlo con tu instancia de Kibana.</p><h2>Primeros pasos</h2><p>Echa un vistazo al código, y pruébalo tú mismo <a href="https://github.com/KDKHD/elastic_agent_builder_livekit">aquí</a>. </p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/build-voice-agents-elastic-agent-builder</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/build-voice-agents-elastic-agent-builder</guid>
    <category><![CDATA[AI agéntica]]></category>
    <dc:creator><![CDATA[Kenneth Kreindler]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2732d87a324baa78/6a17dea6e9ea873632a9c4cc/43ceabb9e2c0966261c188bd40e03178d5a91e5c-1280x720.png" length="0" type="image/png"/>
    <pubDate>Thu, 22 Jan 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Gestión de la memoria agentic con Elasticsearch]]></title>
    <description><![CDATA[Crear agentes más eficientes y sensibles al contexto mediante la gestión de memorias usando Elasticsearch.]]></description>
    <content:encoded><![CDATA[<p>En la disciplina emergente de <strong>ingeniería de contexto</strong>, proporcionar a los agentes de IA la información correcta en el momento adecuado es crucial. Uno de los aspectos más importantes de la ingeniería de contexto es gestionar la <strong>memoria</strong> de una IA. Al igual que los seres humanos, los sistemas de IA dependen tanto de la memoria a corto plazo como de la memoria a largo plazo para recordar información. Si queremos que los agentes de grandes modelos de lenguaje (LLM) mantengan conversaciones lógicas, recuerden las preferencias del usuario o construyan sobre resultados o respuestas previas, necesitamos equiparlos con mecanismos de memoria efectivos.</p><p>Después de todo, todo en el contexto influye en las respuestas de la IA. Es cierto lo que dicen: "<em>Lo que das es lo que recibes"</em>.</p><p>En este artículo, presentaremos lo que significan la memoria a corto y a largo plazo para los agentes de IA, específicamente:</p><ul><li><p>La diferencia entre la memoria a corto y a largo plazo.</p></li><li><p>Cómo se relacionan con las técnicas de RAG con bases de datos vectoriales, como Elasticsearch, y por qué es necesaria una gestión cuidadosa de la memoria.</p></li><li><p>Los riesgos de descuidar la memoria, como el desbordamiento de contexto y el envenenamiento por contexto.</p></li><li><p>Las mejores prácticas, como podar el contexto, resumir y recuperar solo lo relevante, para mantener la memoria de un agente útil y segura.</p></li><li><p>Finalmente, hablaremos sobre cómo compartir y propagar la memoria en sistemas multiagente para que los agentes colaboren sin confusión mediante Elasticsearch.</p></li></ul><h2>Memoria a corto plazo frente a memoria a largo plazo en los agentes de IA</h2><p><em><strong>La memoria a corto plazo</strong></em> en un agente de IA suele referirse al contexto o estado conversacional inmediato, es decir, al historial de chat actual o a los mensajes recientes de la sesión activa. Esto incluye la última consulta del usuario y los intercambios recientes. Es muy similar a la información que una persona tiene en mente durante una conversación.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blteb714ce810d1c472/6a170f321949f782cbe7aaf6/4fbcc6f68055b2bccefc4176297a4ca50056dc0d-764x498.png" alt="Memoria de agente a corto y largo plazo" /><p>Los marcos de trabajo de IA suelen mantener esta memoria transitoria como parte del estado del agente (por ejemplo, al utilizar un checkpointer para almacenar el estado de la conversación, como se muestra en <a href="https://docs.langchain.com/oss/python/langgraph/persistence#checkpoints">este ejemplo de LangGraph</a>). La memoria a corto plazo es <em><strong>de sesión</strong></em>; es decir, existe dentro de una sola conversación o tarea y se restablece o borra cuando esa sesión termina, a menos que se guarde explícitamente en otro lugar. Un ejemplo de memoria a corto plazo limitada a sesiones sería el <a href="https://help.openai.com/en/articles/8914046-temporary-chat-faq"><strong>chat temporal</strong></a>disponible en ChatGPT.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4b8680e22d4e1185/6a170f341949f78bbae7aafa/150bdf209cda5ed20b59cddf34e624ad1a8016aa-1100x577.png" alt="Memoria de los marcos de trabajo de IA" /><p><em><strong>Memoria a largo plazo</strong></em>, por otro lado, se refiere a la información que persiste <strong>a través de conversaciones o sesiones</strong>. Este es el conocimiento que un agente conserva a lo largo del tiempo, los datos que aprendió antes, las preferencias del usuario o cualquier dato que le hayamos dicho que recuerde permanentemente.</p><p>La memoria a largo plazo generalmente se implementa almacenándola y recuperándola de una fuente externa, como un archivo o una base de datos vectorial que está fuera de la ventana de contexto inmediata. A diferencia de la memoria de chat a corto plazo, la memoria a largo plazo no se incluye automáticamente en cada solicitud. En cambio, basado en un escenario dado, el agente debe <strong>recuperarla</strong> u obtenerla cuando se invocan las herramientas relevantes. En la práctica, la memoria a largo plazo puede incluir la información del perfil del usuario, respuestas o análisis previos realizados por el agente, o una base de conocimientos que el agente puede consultar.</p><p>Por ejemplo, si tienes un agente planificador de viajes, la <em>memoria a corto plazo</em> contendría detalles de la consulta actual del viaje (fechas, destino, presupuesto) y cualquier pregunta de seguimiento en esa conversación; mientras que la <em>memoria a largo plazo</em> podría almacenar las preferencias generales de viaje del usuario, itinerarios pasados y otros datos compartidos en sesiones anteriores. Cuando el usuario regresa más tarde, el agente puede extraer de este almacenamiento a largo plazo (por ejemplo, al usuario le encantan las playas y las montañas, tiene un presupuesto promedio de 100 000 INR, tiene una lista de deseos de lugares para visitar y prefiere experimentar la historia y la cultura en lugar de atracciones para niños) de modo que no trate al usuario como una pizarra en blanco todo el tiempo.</p><p>La memoria a corto plazo (historial de chat) proporciona un contexto inmediato y continuidad, mientras que la memoria a largo plazo proporciona un contexto más amplio del que el agente puede extraer cuando sea necesario. La mayoría de los marcos de trabajo de agentes de IA avanzados permiten ambas posibilidades: realizan un seguimiento de los diálogos recientes para mantener el contexto <em>y</em> ofrecen mecanismos para buscar o almacenar información en un repositorio a más largo plazo. La gestión de la memoria a corto plazo garantiza que se mantenga dentro de la ventana de contexto, mientras que la gestión de la memoria a largo plazo ayuda al agente a fundamentar las respuestas basadas en interacciones previas y personalidades.</p><h2>Memoria y RAG en ingeniería de contexto</h2><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt98c1514741bea460/6a170f36509168083ce1bbae/46635aa11ceff89b8d6a26ac3e22da52407d82f3-1600x900.png" alt="Memoria y RAG en ingeniería de contexto" /><p><em><strong>¿Cómo le damos a un agente de IA una memoria útil a largo plazo en la práctica?</strong></em></p><p>Un enfoque destacado para la memoria a largo plazo es <em><strong>la memoria semántica</strong></em>, que a menudo se implementa mediante <strong>generación aumentada de recuperación (RAG)</strong>. Esto implica acoplar el LLM con una tienda de conocimiento externa o un almacén de datos habilitado para vectores, como Elasticsearch. Cuando el LLM necesita información más allá de lo que aparece en el prompt o en su entrenamiento integrado, realiza una recuperación semántica contra Elasticsearch e inyecta los resultados más relevantes en el prompt como contexto. De esta manera, el contexto efectivo del modelo incluye no solo la conversación reciente (memoria a corto plazo), sino también datos pertinentes a largo plazo que se obtienen sobre la marcha. A continuación, el LLM basa su respuesta tanto en su propio razonamiento como en la información recuperada, combinando eficazmente la memoria a corto plazo y la memoria a largo plazo para producir una respuesta más precisa y consciente del contexto.</p><p><strong>Elasticsearch </strong>puede emplearse para implementar memoria a largo plazo para agentes de IA. Aquí hay un ejemplo de alto nivel de cómo se puede recuperar el contexto de Elasticsearch para la memoria a largo plazo.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt44f5a6887b0bca32/6a170f37a6c2b9c735e797be/41ccbc7b5171e8170ac300139a963c0708816ba6-1600x900.png" alt="RAG en acción" /><p>De esta manera, el agente "recuerda" al buscar datos relevantes en lugar de almacenar todo en su limitado prompt, <strong>lo que conduce a diferentes riesgos.</strong></p><p><strong>Usar RAG con Elasticsearch o cualquier almacén vectorial ofrece múltiples beneficios:</strong></p><p>Primero, <strong>amplía el conocimiento</strong> del modelo más allá de su límite de entrenamiento. El agente puede recuperar información actualizada o datos específicos del dominio que el LLM podría desconocer. Esto es crucial para preguntas sobre eventos recientes o temas especializados.</p><p>Segundo, obtener contexto bajo demanda ayuda a reducir las alucinaciones, especialmente porque los LLM no están capacitados con datos propietarios o altamente especializados en relación con tu caso de uso específico, lo que probablemente los exponga a alucinaciones. En lugar de que el LLM adivine o invente nueva información como se incentivó mediante la evaluación, como se destaca en un reciente artículo de OpenAI (<a href="https://arxiv.org/pdf/2509.04664">Por qué alucinan los modelos de lenguaje</a>), el modelo puede basarse en referencias factuales de Elasticsearch. Naturalmente, el LLM depende de la fiabilidad de los datos almacenados en el vector para realmente prevenir la desinformación, y los datos relevantes se recuperan según las medidas de relevancia de núcleo.</p><p>Tercero, la RAG permite que un agente trabaje con bases de conocimientos mucho más grandes de lo que podrías incluir en un prompt. En lugar de enviar documentos completos, como largos trabajos de investigación o documentos de políticas, a la ventana de contexto y correr el riesgo de que la sobrecarga o el contexto de información irrelevante <a href="https://www.elastic.co/search-labs/blog/agentic-memory-management-elasticsearch#context-poisoning">envenene</a> el razonamiento del modelo, la RAG se basa en <a href="https://www.elastic.co/search-labs/blog/chunking-strategies-elasticsearch">la fragmentación</a>. Los documentos grandes se dividen en piezas más pequeñas y semánticamente significativas, y el sistema recupera solo los fragmentos más relevantes para la consulta. De esta manera, el modelo no necesita un contexto de un millón de tokens para parecer conocedor; solo necesita acceso a los fragmentos correctos de un corpus mucho más grande.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4c90f81a56db0a33/6a170f3960084be7ba3c462e/e6897356c9f0940e35a63d005e9cd20bc33e5dd7-1600x931.png" alt="Evolución de la ingeniería de contexto de LLM" /><p>Vale la pena señalar que, a medida que las ventanas de contexto de LLM crecieron (<a href="https://www.anthropic.com/news/1m-context">algunos modelos ahora admiten cientos de miles o incluso millones de tokens</a><em>)</em>, surgió un debate sobre si la RAG está "muerta". ¿Por qué no enviar todos los datos al prompt? Si te preguntas lo mismo, consulta este maravilloso artículo de mis colegas, Jeffrey Rengifo y Eduard Martin, <a href="https://www.elastic.co/search-labs/blog/rag-vs-long-context-model-llm">Contexto más largo ≠ mejor: Por qué la RAG sigue siendo importante</a>. Esto evita el problema de la "Lo que das es lo que recibes": El LLM se mantiene enfocado en los pocos fragmentos que importan, en lugar de ejecutarse a través del ruido.</p><p>Dicho esto, integrar Elasticsearch o cualquier almacén vectorial en una arquitectura de agente de IA proporciona <strong>memoria a largo plazo</strong>. El agente almacena el conocimiento externamente y lo recupera como contexto de memoria cuando es necesario. Esto se podría implementar como una <em>arquitectura</em>, en la que, tras cada consulta de usuario, el agente realiza una búsqueda en Elasticsearch para obtener información relevante y luego agrega los primeros resultados al prompt antes de llamar al LLM. La respuesta también podría guardarse en el almacén a largo plazo si contiene nueva información útil (lo que crea un bucle de retroalimentación de aprendizaje). Al usar una memoria basada en recuperación, el agente se mantiene informado y actualizado, sin tener que abarrotar todo lo que sabe en cada solicitud, a pesar de que la ventana de contexto admite <em>un millón de tokens</em>. Esta técnica es una piedra angular de la ingeniería de contexto, ya que combina las ventajas de la recuperación de información y la IA generativa. </p><p>Aquí hay un ejemplo de un estado de conversación gestionado en memoria usando el sistema de puntos de control de LangGraph para la memoria a corto plazo durante la sesión. (Consulta nuestra <a href="https://github.com/someshwaranM/elastic-context-engineering-short-term-long-term-memory">app de ingeniería de contexto de apoyo</a>).</p># Initialize chat memory (Note: This is in-memory only, not persistent)
memory = MemorySaver()

# Create a LangGraph agent
langgraph_agent = create_react_agent(model=llm, tools=tools, checkpointer=memory)

...
...
# Only process and display checkpoints if verbose mode is enabled
if args.verbose:
    # List all checkpoints that match a given configuration
    checkpoints = memory.list({"configurable": {"thread_id": "1"}})
    # Process the checkpoints
    process_checkpoints(checkpoints)<p>Así es como almacena los <strong>puntos de control</strong>:</p>Checkpoint:
Timestamp: 2025-12-30T09:19:41.691087+00:00
Checkpoint ID: 1f0e560a-c2fa-69ec-8001-14ee5373f9cf
User: Hi I'm Som, how are you? (Message ID: ad0a8415-5392-4a58-85ad-84154875bbf2)
Agent: Hi Som! I'm doing well, thank you! How about you? (Message ID: 
56d31efb-14e3-4148-806e-24a839799ece)
Agent:  (Message ID: lc_run--019b6e8e-553f-7b52-8796-a8b1fbb206a4-0)

Checkpoint:
Timestamp: 2025-12-30T09:19:40.350507+00:00
Checkpoint ID: 1f0e560a-b631-6a08-8000-7796d108109a
User: Hi I'm Som, how are you? (Message ID: ad0a8415-5392-4a58-85ad-84154875bbf2)
Agent: Hi Som! I'm doing well, thank you! How about you? (Message ID: 
56d31efb-14e3-4148-806e-24a839799ece)

Checkpoint:
Timestamp: 2025-12-30T09:19:40.349027+00:00
Checkpoint ID: 1f0e560a-b62e-6010-bfff-cbebe1d865f6<p>Para la memoria a largo plazo, así es como realizamos la búsqueda semántica en Elasticsearch para recuperar conversaciones previas relevantes usando embeddings vectoriales tras resumir e indexar los puntos de control en Elasticsearch.</p>Functions: 
retrieve_from_elasticsearch() 

# Enhanced Elasticsearch retrieval with rank_window and verbose display
def retrieve_from_elasticsearch(query: str, k: int = 5, rank_window: int = None) -&gt; tuple[List[Dict[str, Any]], str]:
    """
    Retrieve context from Elasticsearch with score-based ranking
    
    Args:
        query: Search query
        k: Number of results to return
        rank_window: Number of candidates to retrieve before ranking (default: args.rank_window)
        
    Returns:
        Tuple of (retrieved_documents, formatted_context_string)
    """
    if not es_client or not es_index_name:
        return [], "Elasticsearch is not available. Cannot search long-term memory."
    
    if rank_window is None:
        rank_window = args.rank_window
    
    try:
        # Check if index exists and has documents
        if not es_client.indices.exists(index=es_index_name):
            return [], "No previous conversations stored in long-term memory yet."
        
        # Get document count
        try:
            doc_count = es_client.count(index=es_index_name)["count"]
            if doc_count == 0:
                return [], "Long-term memory is empty. No previous conversations to search."
        except Exception as e:
            return [], f"Error checking memory: {str(e)}"
        
        # Generate embedding for the query
        try:
            query_embedding = embeddings.embed_query(query)
        except Exception as e:
            return [], f"Error generating embedding: {str(e)}"
        
        # Perform semantic search using kNN with rank_window
        try:
            search_body = {
                "knn": {
                    "field": "vector",
                    "query_vector": query_embedding,
                    "k": k,
                    "num_candidates": rank_window  # Retrieve more candidates, then rank top k
                },
                "_source": ["text", "content", "message_type", "timestamp", "thread_id"],
                "size": k
            }
            
            response = es_client.search(index=es_index_name, body=search_body)
            
            if not response.get("hits") or len(response["hits"]["hits"]) == 0:
                return [], "No relevant previous conversations found in long-term memory."
            
            # Extract documents with scores
            retrieved_docs = []
            for hit in response["hits"]["hits"]:
                source = hit["_source"]
                score = hit["_score"]
                retrieved_docs.append({
                    "content": source.get("content", source.get("text", "")),
                    "message_type": source.get("message_type", "unknown"),
                    "timestamp": source.get("timestamp", "unknown"),
                    "thread_id": source.get("thread_id", "unknown"),
                    "score": score
                })
            
            # Format context string
            context_parts = []
            for i, doc in enumerate(retrieved_docs, 1):
                context_parts.append(doc["content"])
            
            context_string = "\n\n".join(context_parts)
            
            # Verbose display
            if args.verbose:
                rich.print(f"\n[bold yellow]🔍 RETRIEVAL ANALYSIS[/bold yellow]")
                rich.print("="*80)
                rich.print(f"[blue]Query:[/blue] {query}")
                rich.print(f"[blue]Retrieved:[/blue] {len(retrieved_docs)} documents (from {rank_window} candidates)")
                rich.print(f"[blue]Total context length:[/blue] {len(context_string)} characters\n")
                
                for i, doc in enumerate(retrieved_docs, 1):
                    rich.print(f"[cyan]📄 Document {i} | Score: {doc['score']:.4f} | Type: {doc['message_type']}[/cyan]")
                    rich.print(f"[cyan]   Timestamp: {doc['timestamp']} | Thread: {doc['thread_id']}[/cyan]")
                    content_preview = doc['content'][:200] + "..." if len(doc['content']) &gt; 200 else doc['content']
                    rich.print(f"[cyan]   Content: {content_preview}[/cyan]")
                    rich.print("-" * 80)
            
            return retrieved_docs, context_string
            
        except Exception as e:
            return [], f"Error searching memory: {str(e)}"
            
    except Exception as e:
        return [], f"Error accessing long-term memory: {str(e)}"<p>Ahora que hemos explorado cómo se indexan y recuperan la memoria a corto y largo plazo usando los puntos de control de LangGraph en Elasticsearch, tomemos un momento para entender por qué indexar y eliminar las conversaciones completas puede ser riesgoso.</p><h2>Riesgos de no gestionar la memoria de contexto</h2><p>Como hablamos mucho sobre ingeniería de contexto, junto con la memoria a corto y largo plazo, entendamos qué sucede si no gestionamos bien la memoria y el contexto de un agente.</p><p>Desafortunadamente, muchas cosas pueden salir mal cuando el contexto de una IA se vuelve extremadamente largo o contiene información errónea. A medida que las ventanas de contexto se agrandan, surgen <strong>nuevos tipos de falla</strong>, como:</p><ul><li><p><strong>Envenenamiento por contexto</strong></p></li><li><p><strong>Distracción del contexto</strong></p></li><li><p><strong>Confusión de contexto</strong></p></li><li><p><strong>Choque de contexto</strong></p></li><li><p><strong>Fuga de contexto y conflictos de conocimiento</strong></p></li><li><p><strong>Alucinaciones e información errónea</strong></p></li></ul><p>Hagamos un desglose de estos problemas y otros riesgos que surgen de una mala gestión del contexto:</p><h3>Envenenamiento por contexto</h3><p>El <em>envenenamiento por contexto</em> se refiere a cuando la información incorrecta o dañina termina en el contexto y "envenena" las salidas posteriores del modelo. Un ejemplo común es una alucinación del modelo que se trata como un hecho y se inserta en el historial de conversaciones. El modelo podría entonces aprovechar ese error en respuestas posteriores, lo que agravaría el error. En los bucles iterativos de agentes, una vez que una información falsa se introduce en el contexto compartido (por ejemplo, en un resumen de las notas de trabajo del agente), puede reforzarse una y otra vez. </p><p><a href="https://storage.googleapis.com/deepmind-media/gemini/gemini_v2_5_report.pdf">Los investigadores de DeepMind, en la publicación del reporte Gemini 2.5</a> (TL;DR, consulta <a href="https://www.dbreunig.com/2025/06/17/an-agentic-case-study-playing-pok%C3%A9mon-with-gemini.html">aquí</a>), observaron esto en un agente <em>que jugaba a Pokémon</em>desde hacía mucho tiempo: si el agente alucinaba un estado de juego erróneo y eso quedaba registrado en su <em>contexto (</em>su memoria de objetivos), el agente formaba <strong>estrategias sin sentido</strong> en torno a un objetivo imposible y se quedaba atascado. En otras palabras, un recuerdo contaminado puede llevar al agente por el camino equivocado de forma indefinida.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd56e9e0681f32239/6a170f3b4a531bd79536aa21/3f2facf5aad67613ad557422e09ec23a66adc0ed-1600x1388.png" alt="Envenenamiento por contexto" /><p>El envenenamiento del contexto puede ocurrir de forma inocente (por error) o incluso maliciosa, por ejemplo, mediante ataques de inyección de prompt donde un usuario o un tercero introduce una instrucción oculta o un hecho falso que el agente luego recuerda y sigue.</p><p><strong>Contramedidas recomendadas:</strong></p><p>Basándose en la información de <a href="https://www.wiz.io/academy/data-poisoning">Wiz</a>, <a href="https://zerlo.net/en/blog/what-is-llm-data-poisoning">Zerlo</a> y <a href="https://www.anthropic.com/research/small-samples-poison">Anthropic</a>, las contramedidas para el envenenamiento del contexto se centran en prevenir que la información errónea o engañosa entre en la ventana de contexto, el pipeline de recuperación o la ventana de contexto de un LLM. Los pasos clave incluyen:</p><ul><li><p>Revisa el contexto constantemente: monitoriza la conversación o el texto recuperado para detectar cualquier cosa sospechosa o dañina, no solo el prompt inicial.</p></li><li><p>Utiliza fuentes confiables: Puntúa o etiqueta los documentos según su credibilidad para que el sistema prefiera la información confiable e ignore los datos con baja puntuación.</p></li><li><p>Detecta datos inusuales: usa herramientas que detecten contenido extraño, fuera de lugar o manipulado, y elimínalo antes de que el modelo lo use.</p></li><li><p>Filtra entradas y salidas: Añade salvaguardas para que el texto dañino o engañoso no pueda entrar fácilmente en el sistema ni ser repetido por el modelo.</p></li><li><p>Mantén el modelo actualizado con datos limpios: actualiza regularmente el sistema con información verificada para contrarrestar cualquier dato incorrecto que haya pasado desapercibido.</p></li><li><p>Intervención humana: Haz que las personas revisen las salidas importantes o las comparen con fuentes conocidas y confiables.</p></li></ul><p>Los hábitos sencillos de los usuarios también ayudan, como restablecer los chats largos, compartir solo información relevante, dividir las tareas complejas en pasos más pequeños y mantener notas claras fuera del modelo.</p><p>En conjunto, estas medidas crean una defensa en capas que protege a los LLMs del envenenamiento del contexto y mantiene las salidas precisas y fiables.</p><p>Sin contramedidas como las mencionadas aquí, un agente podría recordar instrucciones, como ignorar directrices previaso datos triviales que un atacante introdujo, lo que podría provocar salidas dañinas.</p><h3>Distracción del contexto</h3><p><em>Distracción por contexto</em> es cuando un contexto crece tanto que el modelo se sobreenfoca en el contexto, y descuida lo que aprendió durante el entrenamiento. En casos extremos, esto se asemeja al <a href="https://en.wikipedia.org/wiki/Catastrophic_interference"><em>olvido catastrófico</em></a>; es decir, el modelo efectivamente "olvida" su conocimiento subyacente y se apega demasiado a la información colocada frente a él. Estudios previos han demostrado que los LLM a menudo pierden el enfoque cuando la solicitud es extremadamente larga.</p><p>El agente Gemini 2.5, por ejemplo, admitía una ventana de un millón de tokens, pero una vez que su contexto creció más allá de cierto punto (del orden de 100 000 tokens en un experimento), comenzó a <strong>fijarse en repetir sus acciones pasadas</strong> en lugar de encontrar nuevas soluciones. En cierto sentido, el agente se convirtió en prisionero de su extensa historia. Siguió mirando su largo log de movimientos anteriores (el contexto) e imitándolos, en lugar de usar su conocimiento de entrenamiento subyacente para diseñar estrategias nuevas y novedosas.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt91ea0056bbda6e2d/6a170f3d2b835fdd2bf4b2db/e08e5b6d2e8ec7e3511d455985eed3d7fa6241e0-1352x636.png" alt="Distracción del contexto " /><p>Esto es contraproducente. Queremos que el modelo emplee el contexto relevante para ayudar al razonamiento, no para anular su capacidad de pensamiento. Cabe destacar que incluso los modelos con ventanas enormes presentan esta <a href="https://research.trychroma.com/context-rot"><em>podredumbre contextual</em></a>: su rendimiento se degrada de forma no uniforme a medida que se agregan más tokens. Parece haber un <em>presupuesto de atención</em>. Al igual que los humanos con memoria de trabajo limitada, un LLM tiene una capacidad finita para atender a los tokens, y a medida que ese presupuesto se estira, su precisión y enfoque disminuyen.</p><p>Como medida de mitigación, puedes prevenir la distracción del contexto usando fragmentación, ingeniería de la información correcta, resumen regular del contexto y técnicas de evaluación y seguimiento para medir la precisión de la respuesta mediante puntaje.</p><p>Estos métodos mantienen el modelo basado tanto en el contexto relevante como en su entrenamiento subyacente, lo que reduce el riesgo de distracción y mejora la calidad general del razonamiento.</p><h3>Confusión de contexto</h3><p><em>La confusión de contexto</em> ocurre cuando el modelo emplea contenido superfluo en el contexto para generar una respuesta de baja calidad. Un ejemplo claro es dar a un agente un gran conjunto de herramientas o definiciones de API que podría emplear. Si muchas de esas herramientas no están relacionadas con la tarea actual, el modelo puede intentar usarlas de forma inapropiada, simplemente porque están presentes en contexto. Los experimentos demostraron que proporcionar <em>más</em> herramientas o documentos puede <em>perjudicar</em> el rendimiento si no se necesitan todos. El agente empieza a cometer errores, como llamar a la función equivocada o referenciar texto irrelevante. </p><p>En un caso, un pequeño <strong>modelo Llama 3.1 8B</strong> falló en una tarea cuando se le dieron 46 herramientas para considerar, pero tuvo éxito cuando se le dieron solo 19 herramientas. Las herramientas adicionales crearon confusión, a pesar de que el contexto se ajustaba a los límites de longitud. El problema subyacente es que cualquier información en el mensaje será <em>atendida</em> por el modelo. Si no sabe ignorar algo, ese algo podría influir en su salida de maneras no deseadas. Los elementos irrelevantes pueden "robar" parte de la atención del modelo y llevarlo por el camino equivocado (por ejemplo, un documento irrelevante podría hacer que el agente responda a una pregunta diferente a la que se le hizo). La confusión contextual a menudo se manifiesta cuando el modelo produce una respuesta de baja calidad que integra contextos no relacionados. Consulta el artículo de investigación: <a href="https://arxiv.org/pdf/2411.15399">Menos es más: optimización de la llamada de funciones para la ejecución de LLM en dispositivos periféricos.</a></p><p>Nos recuerda que más contexto no siempre es mejor, especialmente si no está <strong>curado</strong> para que sea relevante.</p><h3>Choque de contexto</h3><p><em>Choque de contexto</em> ocurre cuando <strong>partes del contexto se contradicen entre sí</strong>, lo que causa inconsistencias internas que desvían el razonamiento del modelo. Puede producir un choque si el agente acumula múltiples piezas de información que están en conflicto. </p><p>Por ejemplo, imagina un agente que obtuvo datos de dos fuentes: una dice que <em>el vuelo A sale a las 5 p. m.</em> y la otra dice que <em>el vuelo A sale a las 6 p. m</em>. Si ambos hechos terminan en el contexto, el pobre modelo no tiene forma de saber cuál es el correcto; puede confundirse o producir una respuesta incorrecta o no similar.</p><p>El choque de contexto también ocurre frecuentemente en conversaciones de múltiples turnos donde los <strong>intentos anteriores del modelo</strong> de responder todavía persisten en el contexto junto con información refinada posterior.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd86976266867c0ed/6a170f3e66c4f9c785f8c105/500d7a80dc8db1923f9b5ca84728eed64fa296f7-1316x580.png" alt="Choque de contexto" /><p>Un <a href="https://arxiv.org/pdf/2505.06120">estudio de investigación realizado</a> por Microsoft y Salesforce muestra que si divides una consulta compleja en múltiples turnos de chatbot (agregando detalles gradualmente), la precisión final disminuye significativamente, en comparación con dar todos los detalles en un solo mensaje. ¿Por qué? Porque las primeras vueltas contienen respuestas intermedias parciales o incorrectas del modelo, y estas permanecen en el contexto. Cuando el modelo luego intenta responder con toda la información, su <em>memoria</em> aún incluye esos intentos incorrectos, que entran en conflicto con la información corregida y lo desvían del camino. Básicamente, el contexto de la conversación entra en conflicto consigo mismo. El modelo puede usar inadvertidamente una pieza de contexto desactualizada (de un turno anterior) que no se aplica después de que se agrega nueva información.</p><p>En los sistemas de agentes, el choque de contexto es especialmente peligroso porque un agente puede combinar las salidas de diferentes herramientas o subagentes. Si esas salidas no coinciden, el contexto agregado es inconsistente. El agente podría entonces quedarse atascado o producir resultados absurdos al tratar de conciliar las contradicciones. La prevención del choque de contexto implica asegurarse de que el contexto sea <strong>fresco y consistente</strong>,por ejemplo, borrar o actualizar cualquier información obsoleta y no mezclar fuentes que no hayan sido objeto de un estudio de consistencia.</p><h3>Fuga de contexto y conflictos de conocimiento</h3><p>En los sistemas en los que varios agentes o usuarios comparten un almacén de memoria, existe el riesgo de que la información se filtre entre contextos.</p><p>Por ejemplo, si las incrustaciones de datos de dos usuarios distintos residen en la misma base de datos vectorial sin un control de acceso adecuado, un agente que responda a la consulta del usuario A podría recuperar accidentalmente parte de la memoria del usuario B. Esta <em><strong>fuga entre contextos</strong></em> puede exponer información privada o simplemente crear confusión en las respuestas.</p><p>Según el <a href="https://wtit.com/blog/2025/04/17/owasp-top-10-for-llm-applications-2025/">Top 10 de OWASP para aplicaciones LLM</a>, las bases de datos vectoriales de usuarios múltiples deben protegerse contra este tipo de fugas:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte433216805a66d29/6a170f404a531b2c4e36aa25/8f0ccf0b2f7bd6715c14aceee2deffb213d50bd9-1600x936.png" alt="Fuga de contexto" /><p>Según <a href="https://wtit.com/blog/2025/04/17/owasp-top-10-for-llm-applications-2025/">LLM08:2025 Debilidades de Vectores y Embedding</a><em>,</em> uno de los riesgos comunes es la fuga de contexto:</p><em>En entornos de múltiples usuarios donde varias clases de usuarios o aplicaciones comparten la misma base de datos vectorial, existe el riesgo de pérdida de contexto entre usuarios o consultas. Los errores de conflicto de conocimiento en la federación de datos pueden ocurrir cuando los datos de múltiples fuentes se contradicen entre sí. Esto también puede suceder cuando un LLM no puede reemplazar el conocimiento antiguo que aprendió durante el entrenamiento con los nuevos datos de generación aumentada.</em><p>Otro aspecto es que un LLM podría tener problemas para anular su <strong>conocimiento integrado</strong> con nueva información de memoria. Si el modelo fue entrenado con algún hecho y el contexto recuperado dice lo contrario, el modelo puede confundirse sobre cuál confiar. Sin un diseño adecuado, el agente podría confundir contextos o no actualizar el conocimiento antiguo con nueva evidencia, lo que llevaría a respuestas obsoletas o incorrectas.</p><h3><strong>Alucinaciones e información errónea</strong></h3><p>Mientras que una <em>alucinación </em>(el LLM inventa información plausible pero falsa) es un problema conocido incluso sin contextos largos, una mala gestión de la memoria puede amplificarlo. </p><p>Si la memoria del agente carece de un hecho crucial, el modelo puede <strong>llenar el vacío con una suposición</strong>, y si esa suposición entra en el contexto (envenenándolo), el error persiste. </p><p>El informe de seguridad de los LLM <a href="https://wtit.com/blog/2025/04/17/owasp-top-10-for-llm-applications-2025/"><strong>OWASP (LLM09:2025 Desinformación)</strong></a> destaca la desinformación como una vulnerabilidad de núcleo: los LLM pueden ofrecer respuestas seguras pero fabricadas, y los usuarios pueden confiar demasiado en ellos. Un agente con una memoria a largo plazo deficiente o desactualizada podría citar con confianza algo que era cierto el año pasado pero que ahora es falso, a menos que su memoria se mantenga actualizada. </p><p>La dependencia excesiva en la salida de la IA (ya sea por parte de los usuarios o del propio agente en un bucle) puede empeorar esta situación. Si nadie revisa nunca la información almacenada en la memoria, el agente puede acumular falsedades. Esta es la razón por la que la RAG se usa a menudo para reducir las alucinaciones: al recuperar una fuente autorizada, el modelo no tiene que inventar hechos. Pero si tu recuperación trae el documento incorrecto (digamos, uno que contiene información errónea) o si una alucinación temprana no se poda, el sistema puede propagar esa información errónea a través de sus acciones. </p><p>La conclusión: no administrar la memoria puede conducir a <strong>salidas incorrectas y engañosas</strong>, lo que puede ser perjudicial, especialmente si hay mucho en juego (por ejemplo, malos consejos en un dominio financiero o médico). Un agente necesita mecanismos para verificar o corregir su contenido de memoria, no solo confiar incondicionalmente en lo que esté en el contexto.</p><p>En resumen, darle a un agente de IA una memoria infinitamente larga o volcar cada cosa posible en su contexto <em>no</em> es una receta para el éxito.</p><h2>Mejores prácticas para la gestión de memoria en aplicaciones LLM</h2><p>Para evitar las trampas anteriores, los desarrolladores e investigadores idearon una serie de <strong>mejores prácticas para administrar el contexto y la memoria en sistemas</strong> de IA. Estas prácticas tienen como objetivo mantener el contexto de trabajo de la IA ágil, relevante y actualizado. Aquí hay algunas de las estrategias clave, junto con ejemplos de cómo ayudan.</p><h3>RAG: Utiliza un contexto específico.</h3><p>Gran parte de la RAG ya se ha cubierto en la sección anterior, así que esto sirve como un conjunto conciso de recordatorios prácticos:</p><ul><li><p>Usa la recuperación dirigida, no la carga masiva: Recupera solo los fragmentos más relevantes en lugar de insertar documentos enteros o historiales de conversación completos en el prompt.</p></li><li><p>Trata la RAG como una recuperación de memoria justo a tiempo: Obtén el contexto solo cuando sea necesario, en lugar de traer todo en cada turno.</p></li><li><p>Prefiere estrategias de recuperación conscientes de la relevancia: enfoques como la búsqueda semántica top-k, la fusión de rango recíproco o el filtrado de carga de herramientas ayudan a reducir el ruido y mejorar la conexión a tierra.</p></li><li><p>Las ventanas de contexto más grandes no eliminan la necesidad de RAG: dos párrafos muy relevantes son casi siempre más efectivos que 20 páginas poco relacionadas.</p></li></ul><p>Dicho esto, la RAG no se trata de agregar más contexto; se trata de agregar el contexto adecuado.</p><h3>Carga de herramientas</h3><p><em>Configuración de herramientas</em> se trata de darle a un modelo solo las herramientas que realmente necesita para una tarea. El término proviene de los juegos: Eliges un equipo que se ajuste a la situación. Demasiadas herramientas te ralentizan; las incorrectas causan fallas. Los LLM se comportan de la misma manera, según el documento de investigación <a href="https://arxiv.org/abs/2411.15399">Menos es más</a>. Una vez que pasas de unas 30 herramientas, las descripciones empiezan a solaparse y el modelo se confunde. Después de ~100 herramientas, el fracaso está casi garantizado. Esto no es un problema de ventana de contexto, es confusión de contexto.</p><p>Una solución simple y efectiva es <a href="https://arxiv.org/abs/2505.03275"><strong>RAG-MCP</strong></a>. En lugar de poner todas las herramientas en el mensaje, las descripciones de las herramientas se almacenan en una base de datos vectorial y solo se recuperan las más relevantes por solicitud. En la práctica, esto permite mantener un equipamiento reducido y específico, acorta considerablemente los tiempos de respuesta y puede mejorar hasta tres veces la precisión en la selección de herramientas.</p><p>Los modelos más pequeños alcanzan este límite incluso antes. La investigación muestra que un modelo 8B falla con docenas de herramientas, pero tiene éxito una vez que se reduce la carga. La selección dinámica de herramientas, a veces con un LLM primero, razonando sobre lo que cree que necesita, puede aumentar el rendimiento en un 44%, al tiempo que reduce el uso de poder y la latencia. La clave es que la mayoría de los agentes solo necesitan unas pocas herramientas, pero a medida que tu sistema crece, la carga de herramientas y el RAG-MCP se convierten en decisiones de diseño de primer orden.</p><h3>Poda de contexto: limita la duración del historial de chat</h3><p>Si una conversación se prolonga durante muchos turnos, el historial de chat acumulado puede llegar a ser demasiado grande para caber, lo que provoca un desbordamiento del contexto o distrae demasiado al modelo. </p><p><em>Recortar</em> significa eliminar o acortar programáticamente las partes menos importantes del diálogo a medida que crece. Una forma simple es descartar los turnos más antiguos de la conversación cuando alcanzas un cierto límite, manteniendo solo los últimos <em>N</em> mensajes. Una poda más sofisticada podría eliminar digresiones irrelevantes o instrucciones previas que ya no son necesarias. El objetivo es <strong>mantener la ventana de contexto despejada</strong> de noticias antiguas. </p><p>Por ejemplo, si el agente resolvió un subproblema hace 10 turnos y hemos seguido adelante desde entonces, podríamos eliminar esa parte del historial del contexto (asumiendo que ya no será necesaria). Muchas implementaciones basadas en chat hacen esto: mantienen una ventana móvil con los mensajes recientes. </p><p>Recortar puede ser tan simple como "olvidar" las primeras partes de una conversación una vez que se han resumido o se consideran irrelevantes. De esta manera, reducimos el riesgo de errores por exceso de contexto y también reducimos <a href="https://www.elastic.co/search-labs/blog/agentic-memory-management-elasticsearch#context-distraction"><strong>la distracción del contexto</strong></a>, por lo que el modelo no verá contenido antiguo o fuera de tema ni se distraerá con él. Este enfoque es muy similar a cómo los humanos podrían no recordar cada palabra de una charla de una hora, pero retendrán los puntos destacados. </p><p>Si tienes dudas acerca de la poda de contexto, como lo destaca el autor Drew Breunig <a href="https://www.dbreunig.com/2025/06/26/how-to-fix-your-context.html#tool-loadout:~:text=Provence%20is%20fast%2C%20accurate%2C%20simple%20to%20use%2C%20and%20relatively%20small%20%E2%80%93%20only%201.75%20GB.%20You%20can%20call%20it%20in%20a%20few%20lines%2C%20like%20so%3A">aquí</a>, el uso del modelo Provence (`<a href="https://huggingface.co/naver/provence-reranker-debertav3-v1">naver/provence-reranker-debertav3-v1</a>`), un podador de contexto ligero (1.75 GB), eficiente y preciso para la respuesta a preguntas, puede marcar la diferencia. Puedes reducir documentos grandes a solo el texto más relevante para una búsqueda determinada. Puedes llamarlo en intervalos específicos.</p><p>Así es como invocamos el modelo `provence-reranker` en nuestro código para podar el contexto:</p># Context pruning with Provence
def prune_with_provence(query: str, context: str, threshold: Optional[float] = None) -&gt; str:
    """
    Prune context using Provence reranker model
    
    Args:
        query: User's query/question
        context: Original context to prune
        threshold: Relevance threshold (0-1) for Provence reranker.
                   If None, uses args.pruning_threshold.
                   0.1 = conservative (recommended, no performance drop)
                   0.3-0.5 = moderate to aggressive pruning
    
    Returns:
        Pruned context with only relevant sentences
    """
    if provence_model is None:
        return context
    
    if threshold is None:
        threshold = args.pruning_threshold
    
    try:
        # Use Provence's process method
        provence_output = provence_model.process(
            question=query,
            context=context,
            threshold=threshold,
            always_select_title=False,
            enable_warnings=False
        )
        
        # Extract pruned context from output
        pruned_context = provence_output.get('pruned_context', context)
        reranking_score = provence_output.get('reranking_score', 0.0)
        
        # Log statistics
        original_length = len(context)
        pruned_length = len(pruned_context)
        reduction_pct = ((original_length - pruned_length) / original_length * 100) if original_length &gt; 0 else 0
        
        if args.verbose:
            rich.print(f"[cyan]📊 Pruning stats: {pruned_length}/{original_length} chars ({reduction_pct:.1f}% reduction, threshold={threshold:.2f}, rerank_score={reranking_score:.3f})[/cyan]")
        
        return pruned_context if pruned_context else context
        
    except Exception as e:
        rich.print(f"[yellow]⚠️ Error in Provence pruning: {str(e)}[/yellow]")
        rich.print(f"[yellow]⚠️ Falling back to original context[/yellow]")
        return context<p>Empleamos el modelo de reranking de Provenza (`naver/provence-reranker-debertav3-v1`) para calificar la relevancia de las oraciones. El filtrado basado en umbrales mantiene las oraciones por encima del umbral de relevancia. Además, introducimos un mecanismo de respaldo, donde volvemos al contexto original si la poda falla. Finalmente, el logging de estadísticas rastrea el porcentaje de reducción en el modo detallado.</p><h3>Resumen de contexto: Condensa la información antigua en lugar de eliminarla por completo</h3><p><em>El resumen</em> es un complemento al recorte. Cuando la historia o la base de conocimientos se vuelve demasiado grande, puedes emplear el LLM para generar un breve resumen de los puntos importantes y usar ese resumen en lugar del contenido completo en el futuro, como realizamos en nuestro código anterior.</p><p>Por ejemplo, si un asistente de IA tuvo una conversación de 50 turnos, en lugar de enviar los 50 turnos al modelo en el turno 51 (que probablemente no encaje), el sistema podría tomar los turnos 1 a 40, hacer que el modelo los resuma en un párrafo y luego solo proporcionar ese resumen más los últimos 10 turnos en el siguiente mensaje. De esta manera, el modelo aún sabe lo que se discutió sin necesidad de conocer todos los detalles. Los primeros usuarios del chatbot lo hacían manualmente preguntando: "¿Puedes resumir lo que hablamos hasta ahora?" y luego continuaban en una nueva sesión con el resumen. Ahora se puede automatizar. El resumen no solo ahorra espacio en la ventana de contexto, sino que también puede reducir <strong>la confusión y distracción del contexto</strong> al eliminar detalles adicionales y conservar solo los hechos más importantes.</p><p>Aquí es cómo usamos los modelos de OpenAI (puedes usar cualquier LLM) para condensar el contexto a la vez que preservamos toda la información relevante, lo que elimina la redundancia y la duplicación.
</p># Context summarization
def summarize_context(query: str, context: str) -&gt; str:
    """
    Summarize context using LLM to reduce duplication and focus on relevant information
    
    Args:
        query: User's query/question
        context: Context to summarize
        
    Returns:
        Summarized context
    """
    try:
        summary_prompt = f"""You are an expert at summarizing conversation context.

Your task: Analyze the provided conversation context and produce a condensed summary that fully answers or supports the user's specific question.

The summary must:
1. Preserve every fact, detail, and information that directly relates to the question
2. Eliminate redundancy and duplicate information
3. Maintain chronological flow when relevant
4. Focus on information that helps answer: "{query}"

Context to summarize:
{context}

Provide a concise summary that preserves all relevant information:"""

        summary = llm.invoke(summary_prompt).content
        
        if args.verbose:
            original_length = len(context)
            summary_length = len(summary)
            reduction_pct = ((original_length - summary_length) / original_length * 100) if original_length &gt; 0 else 0
            rich.print(f"[cyan]📝 Summarization stats: {summary_length}/{original_length} chars ({reduction_pct:.1f}% reduction)[/cyan]")
        
        return summary
        
    except Exception as e:
        rich.print(f"[yellow]⚠️ Error in context summarization: {str(e)}[/yellow]")
        rich.print(f"[yellow]⚠️ Falling back to original context[/yellow]")
        return context<p>Es importante destacar que cuando se resume el contexto, es menos probable que el modelo se vea abrumado por detalles triviales o errores pasados (suponiendo que el resumen sea exacto). </p><p>Sin embargo, el resumen debe hacerse con cuidado. Un mal resumen puede omitir un detalle crucial o incluso provocar un error. Es esencialmente otro mensaje para el modelo ("resume esto"), por lo que puede alucinar o perder matices. La mejor práctica es resumir de forma incremental y quizás mantener algunos hechos canónicos sin resumir.</p><p>No obstante, ha demostrado ser muy útil. <a href="https://storage.googleapis.com/deepmind-media/gemini/gemini_v2_5_report.pdf">En el escenario del agente Gemini, </a>resumir el contexto cada ~100k tokens era una forma de contrarrestar la tendencia del modelo a repetir. El resumen actúa como una memoria comprimida de la conversación o los datos. Como desarrolladores, podemos implementar esto haciendo que un agente llame periódicamente a una función de resumen (tal vez un LLM más pequeño o una rutina dedicada) en el historial de la conversación o en un documento largo. El resumen resultante reemplaza el contenido original en el prompt. Esta táctica se utiliza ampliamente para mantener los contextos dentro de unos límites y sintetizar la información.</p><h3>Cuarentena de contexto: aislar los contextos cuando sea posible</h3><p>Esto es más relevante en sistemas de agentes complejos o flujos de trabajo de varios pasos. La idea de la segmentación del contexto es dividir una tarea grande en tareas más pequeñas e independientes, cada una con su propio contexto, para que nunca acumules un contexto enorme que lo contenga todo. Cada subagente o subtarea trabaja en una parte del problema con un contexto enfocado, y luego un agente de nivel superior, o supervisor o coordinador integra los resultados.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt09d1eac7442aea2b/6a170f42dc55deb10de00ea7/f2de68c3339883d7658e633af3948f29f427e6cf-1600x900.png" alt="Cuarentena de contexto" /><p><a href="https://www.anthropic.com/engineering/multi-agent-research-system">La estrategia de investigación de Anthropic emplea múltiples subagentes</a>, cada uno investigando un aspecto diferente de una pregunta, con sus propias ventanas de contexto, y un agente principal que lee los resultados destilados de esos subagentes. Este enfoque paralelo y modular significa que ninguna ventana de contexto individual se vuelve demasiado voluminosa. También reduce la posibilidad de que se mezcle información irrelevante, cada hilo se mantiene en el tema (sin confusión de contexto) y no lleva equipaje innecesario al responder su subpregunta específica. En cierto sentido, es como ejecutar hilos separados de pensamiento que solo comparten sus resultados, no todo su proceso de pensamiento.</p><p>En sistemas multiagente, este enfoque es esencial. Si el agente A se encarga de la tarea A y el agente B se encarga de la tarea B, no hay razón para que ninguno de los dos consuma todo el contexto del otro, a menos que sea realmente necesario. En cambio, los agentes pueden intercambiar solo la información necesaria. Por ejemplo, el agente A puede pasar un resumen consolidado de sus hallazgos al agente B a través de un agente supervisor, mientras que cada subagente mantiene su propio hilo de contexto dedicado. Esta configuración no requiere intervención humana; se basa en un agente supervisor con herramientas habilitadas y con un intercambio de contexto mínimo y controlado.</p><p>No obstante, diseñar tu sistema de manera que los agentes o herramientas operen con la mínima superposición de contexto necesaria puede mejorar considerablemente la claridad y el rendimiento. Piensa en ello como <strong>microservicios para IA</strong>, cada componente se ocupa de su contexto y pasa mensajes entre ellos de una manera controlada, en lugar de un contexto monolítico. Estas mejores prácticas a menudo se usan en combinación. Además, esto te da la flexibilidad de recortar historiales triviales, resumir mensajes o conversaciones antiguas importantes, transferir los registros detallados a Elasticsearch para contexto a largo plazo y usar la recuperación para recuperar cualquier cosa relevante cuando sea necesario.</p><p>Como se mencionó <a href="https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents#:~:text=While%20some%20models,to%20the%20LLM">aquí</a>, el principio rector es que el contexto es un recurso limitado y valioso. Quieres que cada token del prompt se gane su conservación, lo que significa que debería contribuir a la calidad de la salida. Si algo en la memoria no está cumpliendo con su función (o peor aún, está causando confusión), entonces debe ser eliminado, resumido o descartado.</p><p>Como desarrolladores, ahora podemos programar el contexto igual que programamos el código, decidiendo qué información incluir, cómo formatearla y cuándo omitirla o actualizarla. Siguiendo estas prácticas, podemos proporcionar a los agentes LLM el contexto necesario para realizar tareas sin caer en los modos de fallo descritos anteriormente. El resultado son agentes que recuerdan lo que deben, olvidan lo que no necesitan y recuperan lo que requieren justo a tiempo.</p><h2>Conclusión</h2><p>La memoria no es algo que añades a un agente; es algo que diseñas. La memoria a corto plazo es el bloc de notas de trabajo del agente, y la memoria a largo plazo es su almacén duradero de conocimiento. La RAG es el puente entre los dos, ya que convierte un almacén de datos pasivo, como Elasticsearch, en un mecanismo de recuperación activo que puede conectar a tierra las salidas y mantener el agente actualizado.</p><p>Pero la memoria es un arma de doble filo. En el momento en que dejas que el contexto crezca sin control, invitas al envenenamiento, la distracción, la confusión y los choques, y en los sistemas compartidos, incluso la fuga de datos. Por eso, el trabajo de memoria más importante no es "almacenar más", sino "seleccionar mejor": recuperar selectivamente, podar agresivamente, resumir cuidadosamente y evitar mezclar contextos no relacionados a menos que la tarea realmente lo demande.</p><p>En la práctica, una buena ingeniería de contexto parece un buen diseño de sistemas: contextos más pequeños y suficientes, interfaces controladas entre componentes y una clara separación entre el estado crudo y el estado destilado que realmente quieres que vea el modelo. Si se hace correctamente, no terminas con un agente que lo recuerda todo, sino con un agente que recuerda las cosas adecuadas, en el momento adecuado, por la razón correcta.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/agentic-memory-management-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/agentic-memory-management-elasticsearch</guid>
    <category><![CDATA[AI agéntica]]></category>
    <dc:creator><![CDATA[Someshwaran Mohankumar]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3bad6b045392e641/6a170f43a29299c189d010cc/80907fd072e72d6ec902470b449c9f337957a0d7-1280x720.png" length="0" type="image/png"/>
    <pubDate>Fri, 16 Jan 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Primeros pasos con Elastic Agent Builder y Strands Agents SDK]]></title>
    <description><![CDATA[Aprende a crear un agente con Elastic Agent Builder y, a continuación, descubre cómo utilizar el agente a través del protocolo A2A orquestado con el SDK de Strands Agents.]]></description>
    <content:encoded><![CDATA[<p>¿Tienes una idea para un agente de IA? Probablemente implica hacer algo con datos porque, si un agente va a iniciar una acción útil, necesita tomar una decisión y necesita los datos correctos para tomar la decisión correcta.</p><p>Elastic Agent Builder facilita la creación de agentes de IA conectados a datos. Te mostraremos cómo hacerlo en esta publicación de blog. Veamos todos los pasos necesarios para crear un agente con una herramienta MCP que acceda a los datos almacenados en Elastic. Luego usaremos el SDK de Strands Agents y sus capacidades Agent2Agent (A2A) para operar el agente. El <a href="https://strandsagents.com/">SDK de Strands Agents</a> es una plataforma de desarrollo de IA multiagente que puedes usar para crear apps de agentes con el código justo para asegurarte de obtener el resultado que quieres.</p><p>Construyamos un agente de IA que juegue el juego de RPS+, que es una versión del clásico juego de "Piedra, papel o tijeras" con un toque adicional; les da a los jugadores del juego un par de opciones adicionales.</p><h2>Requisitos previos</h2><p>Aquí está lo que se requiere para seguir los pasos en esta publicación de blog:</p><ul><li><p>Un editor de texto ejecutándose en tu computadora local</p><ul><li><p><a href="https://code.visualstudio.com/download">Visual Studio Code</a> es lo que usaremos para las instrucciones de ejemplo en esta publicación de blog</p></li></ul></li><li><p><a href="https://www.python.org/downloads/">Python 3.10 o superior</a> funcionando en tu computadora local</p></li></ul><h2>Crea un proyecto Serverless</h2><p>Lo primero que necesitamos es un proyecto Elasticsearch Serverless, que incluye Elastic Agent Builder.</p><p>Ve a <a href="http://cloud.elastic.co/">cloud.elastic.co</a> y crea un nuevo proyecto de Elasticsearch Serverless.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3472edce39ec0b81/6a17060e66c4f93c17f8bf57/31b6a5c1c30dacbb4d5e58d1c566071e7143a0c8-1600x879.gif" alt="" /><h2>Crea un índice y agrega datos</h2><p>A continuación, vamos a agregar algunos datos a nuestro proyecto de Elasticsearch. Abre Developer Tools, donde podemos ejecutar comandos para crear un nuevo índice e insertar algunos datos en él. Selecciona Herramientas para desarrolladores en el menú de navegación de nivel superior.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltaedaa94068c07a17/6a17060f961e697558c4ce5f/f97d5af077504463155655a9e27c171a7f974f71-1600x879.jpg" alt="" /><p>Copia y pega el siguiente comando PUT en el área de entrada de solicitud de la consola de Developer Tools. Esta declaración crea un índice de Elasticsearch llamado “game-docs”.</p>PUT /game-docs
{
  "mappings": {
    "properties": {
      "title": { "type": "text" },
      "content": { 
        "type": "text"
      },
      "filename": { "type": "keyword" },
      "last_modified": { "type": "date" }
    }
  }
}<p>Haz clic en el botón <strong>Enviar solicitud</strong> que aparece en el lado derecho de la declaración en Herramientas para desarrolladores. Deberías ver una notificación que confirme que el índice <em>game-docs</em> se creó en el área de respuesta de Herramientas para desarrolladores.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt430c357b479d93af/6a170611a6c2b98191e79624/be0555a1930e4d4f58b7ed8b669c9b702532ed17-1600x880.jpg" alt="" /><p>Un índice llamado <em>game-docs</em> es el lugar ideal para almacenar los datos del juego que estamos creando. Pongamos un documento llamado <em>rps+-md</em> en este índice que contiene todos los datos que requiere nuestro juego. Copia y pega el siguiente comando PUT en la consola de Herramientas para desarrolladores.</p>PUT /game-docs/_doc/rps+-md
{
  "title": "Rock Paper Scissors +",
  "content": "
# Game Name
RPS+

# Starting Prompt
Let's play RPS+ !
---
What do you choose?

# Game Objects
1. Rock 🪨 👊
2. Paper 📜 🖐
3. Scissors ✄ ✌️
4. Light ☼ 👍
5. Dark Energy ☄ 🫱

# Judgement of Victory
* Rock beats Scissors
  * because rocks break scissors
* Paper beats Rock
  * because paper covers rock
* Scissors beat Paper
  * because scissors cut paper
* Rock beats Light
  * because you can build a rock structure to block out light
* Paper beats Light
  * because knowledge stored in files and paper books helps us understand light
* Light beats Dark Energy
  * because light enables humans to lighten up and laugh in the face of dark energy as it causes the eventual heat death of the universe
* Light beats Scissors
  * because light is needed to use scissors safely
* Dark Energy beats Rock
  * because dark energy rocks more than rocks. It rocks rocks and everything else in its expansion of the universe
* Dark Energy beats Paper
  * because humans, with their knowledge stored in files and paper books, can't explain dark energy 
* Scissors beat Dark Energy
  * because a human running with scissors is darker than dark energy

# Invalid Input
I was hoping for an worthy opponent
  - but alas it appears that time has past
  - but alas there's little time for your todo list when [todo:fix this] is so vast

# Cancel Game
The future belongs to the bold. Goodbye..
",
  "filename": "RPS+.md",
  "last_modified": "2025-11-25T12:00:00Z"
}<p>Haz clic en el <strong>botón Enviar solicitud</strong> junto a la instrucción para ejecutarlo y agregar el documento <em>rps+-md</em> al índice de documentación del juego.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt64d49e13754d5b25/6a17061214b270524be3c55d/3c01d8a4602de5c33337457591a388a4a4e3fad3-1600x879.jpg" alt="" /><p>Ahora deberíamos tener algunos datos para consultar, y con Agent Builder, eso es más simple que nunca.</p><p>Selecciona <strong>Agentes</strong> en el menú de navegación superior.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb4d374bf2ba9135c/6a1706147d8d67468570e63e/82dbd2e9a439cabd5a5eea3d0ce005b87df0c3ea-1600x879.jpg" alt="" /><p>Luego, todo lo que tienes que hacer es preguntarle al agente de Elastic AI predeterminado: "¿Qué datos tengo?".</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc0f879cf28772718/6a1706161949f7f25ee7a92d/f7a2f39c9d1486bdf02d9e88a732b540ac2e2cd1-1600x872.gif" alt="" /><p>El agente de Elastic AI evalúa los datos y devuelve una explicación concisa de los datos que tenemos.</p><h2>Crear una herramienta</h2><p>Listo, ahora tenemos algunos datos en Elastic; vamos a ponerlos en práctica. Agent Builder incluye soporte integrado para crear herramientas <a href="https://modelcontextprotocol.io/">MCP</a> que ayudan a los agentes a acceder a los datos que necesitan para tener el contexto adecuado para su tarea. Vamos a crear una herramienta sencilla que recupere los datos de nuestro juego.</p><p>Haz clic en el menú de acciones de Agent Builder.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7802a6b94e81440c/6a170618ab7f085287db9db4/0e327c202674dda33bcc0e494d2b588fa8b32e4f-1600x879.png" alt="" /><p>Selecciona <strong>Ver todas las herramientas </strong>en las opciones del menú.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9f52ffe114fb6ea7/6a17061a4a531b801b36a884/1ebf58650e9fb56750d3f0b1700fab50b44f9bdf-1600x879.png" alt="" /><p>Haz clic en <strong>+ Nueva herramienta.</strong></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8090769f6c4d1899/6a17061c286714294093e219/6c03a7f28b99ac2d805f34f39948979893316a00-1600x879.png" alt="" /><p>En el formulario <strong>Crear herramienta</strong>, selecciona <a href="https://www.elastic.co/docs/explore-analyze/query-filter/languages/esql"><strong>ES|QL</strong></a>como <strong>Tipo</strong> de herramienta e ingresa los siguientes valores.</p><p><strong>Para el ID de la herramienta</strong>:</p>example.get_game_docs<p>Para la <strong>Descripción</strong>:</p>Get RPS+ doc from Elasticsearch game-docs index.<p>Para <strong>la configuración, </strong>ingresa la siguiente consulta en el área de texto <strong>Consulta ES|QL </strong> :</p>FROM game-docs | WHERE filename == "RPS+.md"<p>Tu formulario completado de <strong>Crear herramienta</strong> debería verse así. Haz clic en <strong>Guardar</strong> para crear la herramienta.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt77034c305198217a/6a17061e66c4f9e54ef8bf5e/b6c93e344600f319b9d2c3030020cf2d171ac1c4-1600x1312.png" alt="" /><p>Tenemos una nueva herramienta disponible en el estante de herramientas. Las herramientas no deberían estar colgadas perpetuamente en un estante; hay que darles un uso digno. Creemos un agente que pueda emplear nuestra nueva herramienta personalizada.</p><h2>Crea un agente y asígnale una herramienta</h2><p>Crear un agente es sorprendentemente sencillo con Agent Builder. Solo tienes que ingresar las instrucciones del agente con algunos detalles y eso es todo lo que necesitas. Vamos a crear un agente ahora.</p><p>Haz <strong>clic en Gestionar agentes.</strong></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltaa8a83fc2f3758a9/6a1706201949f71a10e7a931/53934b93db07187e251d4b321cb9ca647e2fd51b-1600x858.png" alt="" /><p>Haz clic en<strong> + Nuevo agente.</strong></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3778403c5101a000/6a17062160084be12f3c449e/fae3ad8f31e71a6dfd044e1daa025a4e280b4e68-1600x490.png" alt="" /><p>Introduce la siguiente información en el formulario <strong>Nuevo agente</strong>.</p><p>Para <strong>ID de agente, </strong>ingresa el texto a continuación:</p>rps_plus_agent<p>En el área de texto de <strong>Instrucciones personalizadas</strong> , ingresa las siguientes instrucciones:</p>When prompted, if the prompt contains an integer, then select the corresponding numbered item in the list of "Game Objects" from your documents. Otherwise select a random game object. This is your chosen game object for a single round of the game.

# General Game Rules
* 2 players
    - the user: the person playing the game
    - you: the agent playing the game and serving as the game master
* Each player chooses a game object which will be compared and cause them to tie, win or lose.

# Start the game
1. This is the way each new game always starts. You make the first line of your response only the name of your chosen game object. 

2. The remainder of your response should be the "Starting Prompt" text from your documents and generate a list of "Game Objects" for the person playing the game to choose a game object from.  

# End of Game: The game ends in one of the following three outcomes:
1. Invalid Input: If the player responds with an invalid game object choice, respond with variations of the "Invalid Input" text from your documents and then end the game.

2. Tie: The game ends in a tie if the user chooses the same game object as your game object choice.

3. Win or Lose: The game winner is decided based on the "Judgement of Victory" conditions from your documents. Compare the user's game object choice and your game object choice and determine who chose the winning game object.

# Game conclusion
Respond with a declaration of the winner of the game by outputting the corresponding text in the "Judgement of Victory" section of your documents.<p>Para el <strong>Nombre de visualización, </strong>ingresa el texto a continuación:</p>RPS+ Agent<p>Para la <strong>Descripción de la pantalla, </strong>ingresa el texto a continuación:</p>An agent that plays the game RPS+<p>Brinda al agente la herramienta personalizada que creamos previamente al hacer clic en la pestaña <strong>Herramientas</strong>.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte5b0fe00abdde07c/6a17062314b2704bc4e3c563/1778f64bc3a1b4004998dc3668ef7f666788e193-1600x1390.png" alt="" /><p>Selecciona solo la herramienta <em>example.get_game_docs</em> que creamos anteriormente.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2210212e07e06104/6a170625a929cf3277ae08d1/7d734cd80161bcc058817482eb330ffcf1cb567b-1600x1363.png" alt="" /><p>Haz clic en <strong>Guardar</strong> para crear el nuevo agente.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6e3afc1918e26f14/6a170627ab7f084746db9db8/c0014faf605ce50c03679ed0d073bd9f3ae7234d-1600x468.png" alt="" /><p>Probemos nuestro nuevo agente. Hay un enlace útil para iniciar un chat con cualquier agente de la lista de agentes.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blteb4b69dc5971d3a0/6a1706286f7f046840914743/b7d6943ad90a4f68691207caf66b81742e712145-1600x560.png" alt="" /><p>Simplemente ingresa “iniciar juego” y el juego comenzará. ¡Funciona!</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte5b621d602223dff/6a17062ab339d568a1769ef8/984d008e4cc3f08cc1f101720673b0f7347c066c-1600x874.gif" alt="" /><p>Puedes ver que el agente muestra su elección de objeto de juego en la parte superior de su respuesta. Esto es útil porque podemos ver la elección del agente y confirmar que el juego funciona como se espera. Sin embargo, conocer la elección de tu oponente antes de elegir no lo convierte en un juego muy divertido de "Piedra, papel o tijeras". Para pulir y perfeccionar el juego hasta su forma final, podemos usar una plataforma de orquestación de agentes que pueda controlar a los agentes con código.</p><p>El SDK de Strands Agents entra al chat.</p><h2>SDK de agentes de Strands</h2><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt73901ec745a97fbf/6a17062c964cea23c808bab3/c195bba6ff2754f5d8fda174a0c1d247bc283710-456x156.png" alt="" /><p>Si tienes curiosidad por probar nuevos marcos de trabajo de desarrollo de agentes, entonces vale la pena probar el <a href="https://strandsagents.com/latest/">SDK de Strands Agents</a>. El <a href="https://aws.amazon.com/blogs/opensource/introducing-strands-agents-an-open-source-ai-agents-sdk/">SDK de Strands Agents fue lanzado por AWS (mayo de 2025)</a> como una implementación de open source <a href="https://github.com/strands-agents/sdk-python">Python</a>, y ahora también hay una versión en <a href="https://dev.to/aws/strands-agents-now-speaks-typescript-a-side-by-side-guide-12b3">Typescript</a>.</p><h2>Introducción al SDK de Strands Agents en Python</h2><p>Enciende tus motores de programación, ahora vamos a pasar rápidamente por el proceso de clonación y ejecución de una app de ejemplo que usa Strands Agents para controlar el agente de <em>RPS+</em> mediante el protocolo A2A. Vamos a crear una versión ajustada del juego RPS+ para que la elección del agente se revele después de que hagas tu elección, porque, después de todo, es la adivinación y el resultado sorpresa lo que hace que juegos como "Piedra, papel o tijeras" sean divertidos.</p><p>En tu computadora local, abre <a href="https://code.visualstudio.com/download">Visual Studio Code</a> y abre una nueva terminal.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3de752025d62993f/6a17062d0c4857f16501a997/2339cc37c89a3524f2b2a21684bc61dae958e1cf-915x460.jpg" alt="" /><p>En la terminal recién abierta, ejecuta el siguiente comando para clonar el repositorio de Elasticsearch Labs:</p>git clone https://github.com/elastic/elasticsearch-labs<p>Ejecuta el siguiente comando <em>cd </em>para cambiar el directorio al directorio elasticsearch-labs:</p>cd elasticsearch-labs<p>A continuación, ejecuta el siguiente comando para abrir el repositorio en Visual Studio Code:</p>code .<p>En el Explorador de archivos de Visual Studio, expande las carpetas <em>supporting-blog-content</em> y <em>agent-builder-a2a-strands-agents</em> y luego abre el archivo <em>elastic_agent_builder_a2a_rps+.py.</em> Así es como se ve el archivo abierto en Visual Studio Code:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt65ef8036a70bcaf1/6a17062f1949f7af36e7a935/d153b19e0e016c701576edb99ccab5af7c554f34-1484x1530.jpg" alt="" /><p>Aquí tienes el contenido de <em>elastic_agent_builder_a2a_rps+.py </em>que deberías ver en tu editor de texto:</p>import asyncio
from dotenv import load_dotenv
from uuid import uuid4
import httpx
import os
import random
from a2a.client import A2ACardResolver, ClientConfig, ClientFactory
from a2a.types import Message, Part, Role, TextPart

DEFAULT_TIMEOUT = 60  # set request timeout to 1 minute


def create_message(*, role: Role = Role.user, text: str, context_id=None) -&gt; Message:
    return Message(
        kind="message",
        role="user",
        parts=[Part(TextPart(kind="text", text=text))],
        message_id=uuid4().hex,
        context_id=context_id,
    )


async def main():
    load_dotenv()
    a2a_agent_host = os.getenv("ES_AGENT_URL")
    a2a_agent_key = os.getenv("ES_API_KEY")
    custom_headers = {"Authorization": f"ApiKey {a2a_agent_key}"}

    async with httpx.AsyncClient(
        timeout=DEFAULT_TIMEOUT, headers=custom_headers
    ) as httpx_client:
        # Get agent card
        resolver = A2ACardResolver(httpx_client=httpx_client, base_url=a2a_agent_host)
        agent_card = await resolver.get_agent_card(
            relative_card_path="/rps_plus_agent.json"
        )
        # Create client using factory
        config = ClientConfig(
            httpx_client=httpx_client,
            streaming=True,
        )
        factory = ClientFactory(config)
        client = factory.create(agent_card)
        # Use the client to communicate with the agent
        print("\nSending 'start game' message to Elastic A2A agent...")
        random_game_object = random.randint(1, 5)
        msg = create_message(text=f"start with game object {random_game_object}")
        async for event in client.send_message(msg):
            if isinstance(event, Message):
                context_id = event.context_id
                response_complete = event.parts[0].root.text
                # Get agent choice from the first line of the response
                parsed_response = response_complete.split("\n", 1)
                agent_choice = parsed_response[0]
                print(parsed_response[1])
        # User choice sent for game results from the agent
        prompt = input("Your Choice  : ")
        msg = create_message(text=prompt, context_id=context_id)
        async for event in client.send_message(msg):
            if isinstance(event, Message):
                print(f"Agent Choice : {agent_choice}")
                print(event.parts[0].root.text)


if __name__ == "__main__":
    asyncio.run(main())<p>Repasemos qué está pasando en este código. A partir del método <em><code>main()</code></em> , el código comienza accediendo a las variables de entorno para la URL del agente y la clave de API. Luego, usamos esos valores para crear un <em><code>httpx</code></em><code> client</code> que podamos usar para obtener la tarjeta de agente para el agente. El cliente luego usa los detalles de la tarjeta del agente para enviar una solicitud de "iniciar juego" al agente. Un aspecto interesante a tener en cuenta aquí es que incluimos un valor <code>random_game_object</code> como parte de la solicitud <code>"start game"</code>. Este valor es un número aleatorio generado con el módulo <em>aleatorio</em> de la biblioteca estándar de Python. La razón para hacerlo es que resulta que los poderosos LLM (que hacen posibles a los agentes de IA) no son muy buenos en la aleatoriedad. No hay problema, Python viene al rescate.</p><p>Continuando con la programación, una vez que el agente responde a la solicitud de "iniciar juego", el código elimina la selección del objeto de juego del agente y lo guarda en la variable <em>agent_choice</em>. El resto de la respuesta se muestra como texto para el usuario final. Luego, se le solicita al usuario su entrada de elección de objeto de juego, la cual se envía al agente. El código muestra la elección del objeto del juego del agente junto con la determinación final del agente sobre el resultado del juego.</p><h2>Establecer la URL de tu agente y la clave de API como variables de entorno</h2><p>Dado que la app de ejemplo se ejecutará en tu computadora local, para comunicarla con nuestro agente Agent Builder, debemos proporcionar al SDK de Strands Agents una URL A2A y una clave API para el agente. La app de ejemplo emplea un archivo llamado <em>.env</em> para almacenar estos valores.</p><p>Haz una copia del <em>archivo env.example</em> y nombra el nuevo <em>archivo .env</em></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta17961cbcb42985c/6a170631b0367dc5a072bc55/25ead5f15a17dedb777132a082097cffb06cae4d-1600x843.jpg" alt="" /><p>Vuelve a Elastic Agent Builder, donde podrás obtener los dos valores que necesitas.</p><p>Selecciona <strong>Ver todas las herramientas</strong> en el menú de acciones de Agent Builder en la parte superior derecha de la página.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt140885d7ebfcb969/6a1706327d8d67b17670e646/9c4f4e4a3bd76e11e0a182fa007a2f6aec7777b4-1600x880.jpg" alt="" /><p>Haz clic en el menú desplegable <strong>servidor MCP</strong> en la parte superior de la página Herramientas y selecciona <strong>Copiar URL del servidor MCP.</strong></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc153c2caa27e949b/6a170634a292997793d00f6d/6cde0de678bb6f81bef8a59deffb110ad6c6ce26-1600x882.jpg" alt="" /><p>Pega la <strong>URL del servidor MCP</strong> en el <em>archivo .env</em> como reemplazo para el valor de marcador de posición <strong>&lt;YOUR-ELASTIC-AGENT-BUILDER-URL&gt; </strong>. Ahora necesitamos hacer una actualización a la URL, es decir, reemplazar el texto final “mcp” con “a2a” porque el <a href="https://a2a-protocol.org/">protocolo A2A</a> es lo que usará el Agent Strands SDK para comunicarse con el agente que se ejecuta en Elastic Agent Builder.</p><p>La URL editada debería verse así:</p>https://rps-game-project-12345a.kb.us-east-1.aws.elastic.cloud/api/agent_builder/a2a<p>El otro valor que necesitamos obtener mientras estamos aquí en Elastic Cloud es una clave API. Haz clic en <strong>Elasticsearch en </strong>el menú de navegación superior.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltada5de819f31d8ff/6a170635b339d55ae9769efc/651676b9be65178cdad50b5d24f26441c0bf3f97-1600x549.jpg" alt="" /><p>Haz clic en el <strong>botón Copiar clave API </strong>para copiar la clave API.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta18f85790df00706/6a170637cf4f257145b2d0bd/17f1e2ed5c7682630c71e75b0b09ffb1d9036210-1600x879.jpg" alt="" /><p>Ahora, de vuelta en Visual Studio Code, pega la clave API en el archivo <em>.env</em> para reemplazar el texto del marcador de posición <strong>&lt;YOUR-ELASTIC-API-KEY&gt; </strong>. Tu archivo <em>.env</em> debería verse algo así:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt92ab4b37cdcca85e/6a1706386f7f0472ed914747/a357947e07f29c8c03382e00c7baedf04a399297-1600x286.jpg" alt="" /><h2>Ejecuta la app de ejemplo</h2><p>Abre una nueva terminal en Visual Studio Code.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8702d826849755d0/6a17063a60084b45ca3c44a2/33e1174c68ea1ed47c7fe62ab6a6da657c606f56-1413x711.jpg" alt="" /><p>Empieza ejecutando el siguiente comando <em>cd</em> en la terminal:</p>cd elasticsearch-labs/supporting-blog-content/agent-builder-a2a-strands-agents<p>Ejecuta el siguiente comando para crear un entorno virtual en Python.</p>python -m venv .venv<p>Dependiendo del sistema operativo de tu computadora local, ejecuta el siguiente comando para activar el entorno virtual.</p><ul><li><p>MacOS/Linux</p></li></ul>source .venv/bin/activate<ul><li><p>Windows</p></li></ul>.venv\Scripts\activate<p>La app de ejemplo utiliza Strands Agents SDK, y ahora nos encontramos en el punto de este tutorial en el que debes instalarlo. Ejecuta el siguiente comando para instalar el SDK de Strands Agents junto con todas tus dependencias requeridas de la biblioteca de Python.</p>pip install -r requirements.txt<p>Es hora de limpiar la plataforma de lanzamiento y comenzar la cuenta regresiva. Estamos listos para lanzar esta app. Retírate. Vamos a ejecutarla usando el siguiente comando:</p>python elastic_agent_builder_a2a_rps+.py<p>Deberías desafiarte con un juego de RPS+. ¡Bien hecho y mucha suerte!</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbb3715672995fcfa/6a17063c6234e07b76db195f/041df81fbf1776f09e1243af0a435c4c0af6aca1-1600x948.gif" alt="" /><h2>Crea tus apps de IA con contexto relevante</h2><p>Crear un agente con IA es ahora una habilidad en tu caja de herramientas. Y has visto lo fácil que es usar agentes de Elastic Agent Builder a través de A2A en marcos de trabajo de agentes como Strands Agents SDK. <a href="https://cloud.elastic.co/registration?utm_source=agentic-ai-category&amp;utm_medium=search-labs&amp;utm_campaign=agent-builder">Prueba Elastic</a> para crear agentes de IA conectados al contexto relevante en tus datos personalizados.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/agent-builder-a2a-strands-agents-guide</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/agent-builder-a2a-strands-agents-guide</guid>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[AI agéntica]]></category>
    <dc:creator><![CDATA[Jonathan Simon]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3472edce39ec0b81/6a17060e66c4f93c17f8bf57/31b6a5c1c30dacbb4d5e58d1c566071e7143a0c8-1600x879.gif" length="0" type="image/gif"/>
    <pubDate>Mon, 15 Dec 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Introducción del soporte de Elasticsearch en Google MCP Toolbox for Databases]]></title>
    <description><![CDATA[Descubre cómo el soporte de Elasticsearch ya está disponible en Google MCP Toolbox for Databases y aprovecha las herramientas ES|QL para integrar de forma segura tu índice con cualquier cliente MCP.]]></description>
    <content:encoded><![CDATA[<p>En este artículo, explicaremos cómo usar Google MCP Toolbox con <a href="https://github.com/elastic/elasticsearch">Elasticsearch</a> para crear una herramienta sencilla que permita extraer información de un índice de Elasticsearch.</p><p>Recientemente contribuimos al proyecto open source <a href="https://github.com/googleapis/genai-toolbox">Google MCP Toolbox for Databases</a> agregando soporte para Elasticsearch como base de datos.</p><p>Con esta nueva característica, ahora puedes usar Google MCP Toolbox para conectarte a Elasticsearch y “conversar” directamente con tus datos.</p><h2>Elasticsearch</h2><p>Es necesario tener una instancia de Elasticsearch en funcionamiento. Puedes activar una prueba gratuita en <a href="https://www.elastic.co/cloud">Elastic Cloud</a> o instalarla localmente utilizando el script <a href="https://github.com/elastic/start-local">start-local</a>:</p>curl -fsSL https://elastic.co/start-local | sh<p>Esto instalará Elasticsearch y Kibana en tu computadora y generará una clave API que se utilizará para configurar Google MCP Toolbox.</p><p>La clave de API se mostrará como salida del comando anterior y se almacenará en un archivo .env. en la carpeta elastic-start-local.</p><h2>Instala el set de datos de ejemplo</h2><p>Tras la instalación, puedes iniciar sesión en Kibana con el nombre de usuario <em>elastic</em> y la contraseña generada por el script start-local (almacenada en un archivo .env).</p><p>Puedes instalar el conjunto de datos de <strong>pedidos de comercio electrónico </strong>disponible desde Kibana. Incluye un único índice llamado <strong>kibana_sample_data_ecommerce</strong> que contiene información sobre 4675 pedidos de un sitio web de comercio electrónico. Para cada pedido, tenemos la siguiente información:</p><ul><li><p>Información del cliente (nombre, identificación, fecha de nacimiento, correo electrónico, etc.)</p></li><li><p>Fecha del pedido</p></li><li><p>ID de pedido</p></li><li><p>Productos (lista de todos los productos con precio, cantidad, identificación, categoría, descuento, etc.)</p></li><li><p>SKU</p></li><li><p>Precio total (sin impuestos, con impuestos)</p></li><li><p>Cantidad total</p></li><li><p>Información geográfica (ciudad, país, continente, ubicación, región)</p></li></ul><p>Para instalar los datos de muestra, abre la página <strong>Integraciones</strong> en Kibana (busca “Integración” en la barra superior de búsqueda) e instala los “Datos de muestra”. Para obtener más detalles, consulta la documentación aquí: <a href="https://www.elastic.co/docs/explore-analyze/#gs-get-data-into-kibana">https://www.elastic.co/docs/explore-analyze/#gs-get-data-into-kibana</a>.</p><p>El objetivo de este artículo es mostrar lo fácil que es configurar Google MCP Toolbox para conectarse a Elasticsearch e interactuar con el <strong>índice de kibana_sample_data_ecommerce</strong> usando lenguaje natural.</p><h2>Google MCP Toolbox</h2><p>Google MCP Toolbox es un servidor MCP open source diseñado para facilitar la interacción segura y eficiente de aplicaciones y agentes de IA con bases de datos. El proyecto, anteriormente conocido como el “GenAI Toolbox for Databases”, cambió su denominación después de adoptar la compatibilidad total con el <a href="https://www.anthropic.com/news/model-context-protocol">Protocolo de contexto de modelo</a> (MCP). Su propósito es eliminar el trabajo pesado que tradicionalmente se requiere al conectar agentes con bases de datos, gestionando la agrupación de conexiones, autenticación, observabilidad y otras preocupaciones operativas en segundo plano.</p><p>Esencialmente, Toolbox permite a los desarrolladores definir herramientas reutilizables de alto nivel que encapsulan las interacciones con la base de datos. Estas herramientas pueden ser invocadas por cualquier cliente compatible con MCP (como un agente de IA) sin requerir que el cliente implemente consultas SQL de bajo nivel o administre conexiones de base de datos. Este enfoque reduce drásticamente la cantidad de código repetitivo necesario para crear agentes compatibles con bases de datos, lo que permite integrar operaciones de datos avanzadas en solo unas pocas líneas de lógica de aplicación. Una vez definida una herramienta, se puede compartir entre varios agentes, marcos de trabajo o lenguajes (Figura 1).</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte90070297ee83546/6a16fa29964cea694b08b972/137cea290bb70ad5da21853f9a6358cef4cf7451-1248x1056.png" alt="" /><p>Una de las ventajas principales de usar la Toolbox es el modelo de seguridad integrado. Los flujos de autenticación como OAuth2 y OIDC son compatibles de forma nativa, lo que permite a los desarrolladores evitar manejar o almacenar credenciales confidenciales de bases de datos en agentes. La plataforma también ofrece características de observabilidad (como métricas y rastreo) a través de OpenTelemetry, que es esencial para la depuración, la supervisión y los despliegues de producción. En conjunto, MCP Toolbox sirve como una interfaz unificada, segura y extensible para interactuar con tus datos desde cualquier sistema compatible con MCP.</p><h2>Cómo instalar MCP Toolbox</h2><p>Puedes instalar el servidor MCP Toolbox en Linux usando el siguiente comando:</p>export VERSION=0.21.0
curl -L -o toolbox https://storage.googleapis.com/genai-toolbox/v$VERSION/linux/amd64/toolbox
chmod +x toolbox<p>Si quieres instalarlo en macOS o Windows, puedes seguir las instrucciones <a href="https://googleapis.github.io/genai-toolbox/getting-started/introduction/#installing-the-server">detalladas aquí</a>.</p><h2>Configura la Toolbox para Elasticsearch</h2><p>Para configurar el MCP Toolbox para Elasticsearch, necesitamos crear un archivo <strong>tools.yaml</strong>, de la siguiente manera:</p>sources:
  my-cluster:
    kind: elasticsearch
    addresses:
      - http://localhost:9200
    apikey: &lt;insert-here-api-key&gt;

tools:
  customer-orders:
    kind: elasticsearch-esql
    source: my-cluster
    description: Get the orders made by a customer identified by name.
    query: |
    	FROM kibana_sample_data_ecommerce | WHERE MATCH(customer_full_name, ?name, {"operator": "AND"})
    parameters:
      - name: name
        type: string
        description: The customer name.

toolsets:
  elasticsearch-tools:
    - customer-orders<p>Debes reemplazar el valor <strong>&lt;insert-here-api-key&gt;</strong> por una clave API válida de Elasticsearch. Si estás ejecutando Elasticsearch localmente usando start-local, puedes encontrar la clave de API en el archivo.env generado por start-local, bajo la variable <strong>ES_LOCAL_API_KEY</strong> . Si usas Elastic Cloud, puedes generar una clave API siguiendo el procedimiento <a href="https://www.elastic.co/docs/deploy-manage/api-keys/elastic-cloud-api-keys">descrito aquí</a>.</p><p>Las herramientas anteriores contienen la siguiente consulta ES|QL para Elasticsearch:</p><p>Si no estás familiarizado con ES|QL, es un lenguaje de búsqueda desarrollado por Elastic, similar a SQL, que puedes usar para buscar en uno o más índices. Puedes leer más sobre ES|QL en la documentación oficial <a href="https://www.elastic.co/docs/reference/query-languages/esql">aquí</a>.</p><p>La búsqueda anterior busca todos los pedidos almacenados en el <strong>índice kibana_sample_data_ecommerce</strong> que contienen el nombre del cliente especificado, usando el parámetro <strong>?name</strong> (el signo de interrogación indica un parámetro).</p><p>El nombre del cliente se define en la configuración YAML anterior empleando el texto de tipo y la descripción "El nombre del cliente".</p><p>Esta herramienta se puede usar para responder preguntas sobre los pedidos de un cliente, por ejemplo: <em>¿Cuántos pedidos realizó el cliente Foo en octubre de 2025?</em></p><p>Las descripciones de las herramientas y sus parámetros son esenciales para extraer la información relevante de la solicitud en lenguaje natural del usuario. Esta extracción se realiza utilizando la capacidad de <strong>llamada de función</strong> de un modelo de lenguaje grande (LLM). En la práctica, un LLM puede determinar qué función (herramienta) debe ejecutar para obtener la información necesaria, junto con los parámetros apropiados para esa función.</p><p>Para más información sobre las llamadas a funciones, sugerimos leer el artículo de <a href="https://www.elastic.co/search-labs/blog/function-calling-with-elastic">OpenAI sobre llamadas a funciones con Elasticsearch</a> de Ashish Tiwari.</p><h2>Ejecuta el servidor de Toolbox</h2><p>Puedes ejecutar la MCP Toolbox usando el archivo tools.yaml anterior con el siguiente comando:</p>./toolbox --tools-file tools.yaml --ui<p>El parámetro<strong> –ui</strong> ejecuta una aplicación sitio web en <a href="http://127.0.0.1:5000/ui">http://127.0.0.1:5000/ui</a> (Figura 2).</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0fb3953fae603572/6a16fa2aa6c2b92763e794fb/3caf2339b632bafd5847af1ed8b33b518a25b8a2-1600x314.png" alt="" /><p>Puedes seleccionar la <strong>Herramientas</strong> &gt; <strong>pedidos-clientes</strong> e insertar un nombre de cliente en el parámetro <strong>nombre</strong> (por ejemplo, Gwen Sanders) y haz clic en el botón <strong>Ejecutar herramienta</strong>. Deberías ver una respuesta JSON como se indica en la Figura 3.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltca02df8c78cb39ff/6a16fa2c961e6909b5c4cd22/b167e0142afb8919d9cedf6d0fa431d33d0e55f8-1600x933.png" alt="" /><p>La configuración se ha completado y MCP Toolbox puede ejecutar la herramienta de <strong>pedidos de clientes</strong> para comunicarse con Elasticsearch, ejecutando la consulta ES|QL.</p><h2>Usar la herramienta MCP Toolbox con Gemini CLI</h2><p>Podemos usar cualquier cliente del MCP para comunicarnos con MCP Toolbox for Database. Por ejemplo, podemos usar <a href="https://github.com/google-gemini/gemini-cli">Gemini CLI</a>, una herramienta de línea de comandos, para usar Gemini. Puedes instalar Gemini CLI siguiendo las instrucciones indicadas <a href="https://geminicli.com/docs/get-started/installation/">aquí</a>.</p><p>Gemini CLI ofrece una extensión preconfigurada para MCP Toolbox, disponible en <a href="https://github.com/gemini-cli-extensions/mcp-toolbox">gemini-cli-extensions/mcp-toolbox</a>. Puedes instalar esta extensión ejecutando el comando siguiente:</p>gemini extensions install https://github.com/gemini-cli-extensions/mcp-toolbox<p>Tras la instalación, debes ir al directorio donde almacenaste el archivo de configuración tools.yaml para MCP Toolbox y ejecutar la CLI Gemini de la siguiente manera (este paso es necesario para que la CLI Gemini se configure automáticamente con MCP Toolbox):</p>gemini<p>Deberías ver un anuncio de salida como el que se muestra en la Figura 4.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf7245c10b9e32cd6/6a16fa2d964cea073208b976/0f22df6d3da13c1dc50dcb560414fa7c630eb9a7-1434x341.png" alt="" /><p>Puedes comprobar si MCP Toolbox está conectada usando el siguiente comando:</p>/mcp list<p>Deberías ver el <strong>mcp_toolbox</strong> con las herramientas de<strong> customer-orders</strong> listadas (Figura 5).</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte857b42dbe604203/6a16fa2f8b73cb531b189e33/97edbc40de9e44f469f6f3a09427532be167de0e-493x155.png" alt="" /><p>Si el MCP Toolbox está conectado a la CLI de Gemini, ahora podemos intentar hacer algunas preguntas, como: “<em>Dame los pedidos del cliente Gwen Sanders</em>”. La CLI de Gemini solicitará entonces permiso para ejecutar la herramienta de pedidos de clientes desde el servidor mcp_toolbox (ver Figura 6).</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfb7ea752c1a39da7/6a16fa30cdacbfdf937d27ff/c052f3b5e49436903b804280c0065f67ee02444b-1432x284.png" alt="" /><p>Tras la confirmación, Gemini CLI ejecutará la solicitud a MCP Toolbox, obteniendo una respuesta JSON como resultado y utilizándola para dar formato a la respuesta (Figura 7).</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb9f6e04987137a9f/6a16fa320811ae2297e9fef7/7ea5128f1705951c2757af6da4b456d394d4a080-1432x734.png" alt="" /><p>La respuesta de Gemini CLI será un reporte que indica que Gwen Sanders hizo solo un pedido de 2 productos, por un precio total de 132 euros.</p><h2>SDK de MCP Toolbox</h2><p>Google MCP Toolbox también ofrece un SDK para acceder a todas las funcionalidades desde un programa escrito en Go, Python y Javascript.</p><p>Por ejemplo, el SDK de Python está disponible en Github en la siguiente página: <a href="https://github.com/googleapis/mcp-toolbox-sdk-python">https://github.com/googleapis/mcp-toolbox-sdk-python</a>.</p><p>Es necesario crear un agente simple para conectarnos a MCP Toolbox. Debemos instalar los siguientes paquetes:</p>pip install toolbox-core
pip install google-adk<p>Además, crear un nuevo proyecto de agente usando los siguientes comandos:</p>adk create my_agent<p>Esto creará un nuevo directorio llamado <strong>my_agent</strong> con un <strong>archivo agent.py</strong>.</p><p>Actualiza <strong>my_agent/agent.py</strong> con el siguiente contenido para conectar con Toolbox:</p>from google.adk import Agent
from google.adk.apps import App
from toolbox_core import ToolboxSyncClient

client = ToolboxSyncClient("http://127.0.0.1:5000")

root_agent = Agent(
    name='root_agent',
    model='gemini-2.5-flash',
    instruction="You are a helpful AI assistant designed to search information about a dataset of ecommerce orders.",
    tools=client.load_toolset(),
)

app = App(root_agent=root_agent, name="my_agent")<p>Crea un archivo <strong>.env</strong> con tu clave de API de Google:</p>echo 'GOOGLE_API_KEY="YOUR_API_KEY"' &gt; my_agent/.env<p>Finalmente, podemos ejecutar el agente y observar los resultados. Para ejecutar el agente, puedes ejecutar el siguiente comando:</p>adk run my_agent<p>O bien, puedes servirlo a través de una interfaz web:</p>adk web --port 8000<p>En ambos casos, puedes interactuar con MCP Toolbox usando una interfaz de preguntas frecuentes. Por ejemplo, puedes hacer la pregunta anterior: <em>Dame las órdenes de la cliente Gwen Sanders</em>.</p><p>Para más información sobre los diferentes SDK, puedes consultar <a href="https://googleapis.github.io/genai-toolbox/sdks/">esta página de documentación</a>.</p><h2>Conclusión</h2><p>En este artículo, hemos mostrado la integración de Elasticsearch con Google MCP Toolbox for Databases. Mediante un sencillo archivo de configuración YAML, podemos definir un conjunto de herramientas que traducen preguntas en lenguaje natural a consultas de Elasticsearch utilizando el lenguaje ES|QL.</p><p>Mostramos cómo interactuar con el set de datos kibana_sample_data_ecommerce, que contiene pedidos de un sitio web de comercio electrónico. Con este archivo de configuración, podemos simplemente ejecutar el servidor MCP Toolbox y conectarnos a él desde cualquier cliente MCP.</p><p>Por último, mostramos cómo utilizar la CLI de Gemini como cliente para conectarse a MCP Toolbox for Databases y consultar los datos de comercio electrónico almacenados en Elasticsearch. Ejecutamos una consulta en lenguaje natural para recuperar información sobre pedidos para un cliente específico identificado por su nombre.</p><p>A medida que el ecosistema MCP sigue creciendo, este patrón (definiciones ligeras de herramientas respaldadas por infraestructuras seguras y listas para producción) crea nuevas oportunidades para construir agentes cada vez más capaces y conscientes de los datos con un esfuerzo mínimo. Ya sea que experimentes localmente con los sets de datos de muestra de Elastic o integres capacidades de búsqueda en una aplicación más amplia, MCP Toolbox ofrece una base fiable y extensible para interactuar con tus datos de Elasticsearch usando lenguaje natural.</p><p>Para obtener más información sobre el desarrollo de aplicaciones de IA agentic, puedes leer el artículo <a href="https://search-labs-redesign.vercel.app/search-labs/blog/ai-agentic-workflows-elastic-ai-agent-builder">Creación de flujos de trabajo de IA agentic con Elasticsearch</a> de Anish Mathur y Dana Juratoni.</p><p>Para obtener más información sobre Google MCP Toolbox, puedes visitar <a href="https://googleapis.github.io/genai-toolbox/getting-started/introduction/">https://googleapis.github.io/genai-toolbox/getting-started/introduction/</a>.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/google-mcp-toolbox-elasticsearch-support</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/google-mcp-toolbox-elasticsearch-support</guid>
    <category><![CDATA[ES|QL]]></category>
    <category><![CDATA[AI agéntica]]></category>
    <dc:creator><![CDATA[Enrico Zimuel,Laurent Saint-Félix]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt72d49893c51407cf/6a16fa33cf4f2502bab2cf7e/425a48691f436ed47c9bdfaf5d561ac122b2c472-1062x668.png" length="0" type="image/png"/>
    <pubDate>Fri, 12 Dec 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Construye un flujo de trabajo de búsqueda de IA financiera utilizando LangGraph.js y Elasticsearch]]></title>
    <description><![CDATA[Aprende a usar LangGraph.js con Elasticsearch para crear un flujo de trabajo de búsqueda financiera impulsado por IA que convierte consultas en lenguaje natural en filtros dinámicos y condicionales para análisis de inversión y mercado.]]></description>
    <content:encoded><![CDATA[<p>Construir aplicaciones de búsqueda con IA suele implicar la coordinación de múltiples tareas, la recuperación de datos y la extracción de datos en un flujo de trabajo sin problemas. LangGraph simplifica este proceso permitiendo a los desarrolladores orquestar agentes de IA mediante una estructura basada en nodes. En este artículo, explicaremos cómo construir una solución financiera usando <a href="https://langchain-ai.github.io/langgraphjs/">LangGraph.js</a></p><h2>¿Qué es LangGraph?</h2><p><a href="https://langchain-ai.github.io/langgraphjs/">LangGraph</a> es un marco de trabajo para construir agentes de IA y orquestarlos en un flujo de trabajo para crear aplicaciones asistidas por IA. LangGraph tiene una arquitectura de nodes donde podemos declarar funciones que representan tareas y asignarlas como nodes del flujo de trabajo. El resultado de la interacción de varios nodes será un grafo. LangGraph es parte del ecosistema más amplio <a href="https://js.langchain.com/docs/introduction/">LangChain</a>, que proporciona herramientas para construir sistemas de IA modulares y componibles.</p><p>Para explicar mejor por qué LangGraph es útil, vamos a usarlo para resolver una situación problemática.</p><h2>Visión general de la solución</h2><p>En una firma de capital de riesgo, los inversores tienen acceso a una gran base de datos con muchas opciones de filtrado, pero cuando uno quiere combinar criterios, se vuelve difícil y lento. Esto puede hacer que algunas iniciativas relevantes no se encuentren para la inversión. Además, implica pasar muchas horas intentando identificar a los mejores candidatos, o incluso perder oportunidades.</p><p>Con LangGraph y Elasticsearch, podemos realizar búsquedas filtradas utilizando lenguaje natural, eliminando la necesidad de que los usuarios construyan manualmente solicitudes complejas con docenas de filtros. Para hacerlo más flexible, el flujo de trabajo decide automáticamente (basándose en la entrada del usuario) entre dos tipos de consulta:</p><ul><li><p><strong>Consultas centradas en la inversión</strong>: estas se dirigen a aspectos financieros y de financiación de las startups, como <a href="https://www.investopedia.com/articles/personal-finance/102015/series-b-c-funding-what-it-all-means-and-how-it-works.asp">rondas de financiación</a>, valoración o <a href="https://www.investopedia.com/terms/r/revenue.asp">ingresos</a>. <em>Ejemplo:</em> “Encuentra startups con financiamiento Serie A o Serie B entre $8M y $25M e ingresos mensuales superiores a $500K”.</p></li><li><p><strong>Consultas centradas en el mercado</strong>: estas se concentran en <a href="https://en.wikipedia.org/wiki/Vertical_market">verticales de la industria</a>, <a href="https://en.wikipedia.org/wiki/Target_market">mercados geográficos</a> o <a href="https://www.investopedia.com/terms/b/businessmodel.asp">modelos de negocio</a>, ayudando a identificar oportunidades en sectores o regiones específicos. <em>Ejemplo:</em> “Encuentra startups de fintech y salud en San Francisco, Nueva York o Boston”.</p></li></ul><p>Para mantener la solidez de las consultas, haremos que el LLM cree <a href="https://www.elastic.co/docs/solutions/search/search-templates">plantillas de búsqueda</a> en lugar de <a href="https://www.elastic.co/docs/explore-analyze/query-filter/languages/querydsl">consultas DSL</a> completas. De esta manera, siempre obtienes la consulta que deseas, y el LLM solo tiene que completar los espacios en blanco y no cargar con la responsabilidad de construir la consulta que necesitas cada vez.</p><h2>Lo que necesitas para comenzar</h2><ul><li><p>Clave de API de Elasticsearch</p></li><li><p>Clave de API de OpenAPI</p></li><li><p>Node 18 o más reciente</p></li></ul><h2>Instrucciones paso a paso</h2><p>En esta sección, mostramos cómo se verá la app. Para ello, emplearemos <a href="https://www.typescriptlang.org/">TypeScript</a>, un superconjunto de JavaScript que agrega tipos estáticos para hacer el código más fiable, fácil de mantener y seguro, detectando errores pronto mientras se mantiene totalmente compatible con el JavaScript existente.</p><p>El flujo de los nodos será el siguiente:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt90db8f03f372608c/6a170986dc55de6e16e00d93/b47d7f238c4964a6febc0de7fe5e68b186f539c3-363x555.png" alt="" /><p>La imagen de arriba es generada por LangGraph y representa el flujo de trabajo que define el orden de ejecución y la lógica condicional entre nodos:</p><ul><li><p><strong>decideStrategy: </strong>utiliza un LLM para analizar la consulta del usuario y decidir entre dos estrategias de búsqueda especializadas, como centrada en la inversión u orientada al mercado.</p></li><li><p><strong>prepareInvestmentSearch: </strong>extrae valores de filtro de la consulta y construye una plantilla predefinida que destaca los parámetros financieros y de financiación.</p></li><li><p><strong>prepareMarketSearch</strong>: extrae también los valores del filtro, pero construye dinámicamente parámetros que enfatizan el mercado, la industria y el contexto geográfico.</p></li><li><p><strong>executeSearch: </strong>envía la consulta construida a Elasticsearch usando una plantilla de búsqueda y recupera los documentos de startups correspondientes.</p></li><li><p><strong>visualizarResultados: </strong>formatea los resultados finales en un resumen claro y legible que muestre atributos clave de la startup, como financiación, industria e ingresos.</p></li></ul><p>Este flujo incluye una <a href="https://langchain-ai.github.io/langgraphjs/how-tos/branching/?h=conditional#how-to-create-branches-for-parallel-node-execution">ramificación condicional</a>, que funciona como una declaración “si” que determina si se debe usar la ruta de búsqueda de inversión o de mercado según la entrada del usuario. Esta lógica de decisión, impulsada por el LLM, hace que el flujo de trabajo sea adaptativo y consciente del contexto, un mecanismo que exploraremos con más detalle en las siguientes secciones.</p><h3>Estado de LangGraph</h3><p>Antes de ver cada node individualmente, necesitamos entender cómo se comunican y comparten datos. Para ello, LangGraph nos permite definir el estado del flujo de trabajo. Esto define el estado compartido que se pasará entre los nodes.</p><p>El estado actúa como un contenedor compartido que almacena datos intermedios a lo largo del flujo de trabajo: comienza con la consulta en lenguaje natural del usuario, luego guarda la estrategia de búsqueda seleccionada, los parámetros preparados para Elasticsearch, los resultados de búsqueda recuperados y, finalmente, la salida formateada.</p><p>Esta estructura permite que cada node lea y actualice el estado, asegurando un flujo coherente de información desde la entrada del usuario hasta la visualización final.</p>const VCState = Annotation.Root({
  input: Annotation&lt;string&gt;(), // User's natural language query
  searchStrategy: Annotation&lt;string&gt;(), // Search strategy chosen by LLM
  searchParams: Annotation&lt;any&gt;(), // Prepared search parameters
  results: Annotation&lt;any[]&gt;(), // Search results
  final: Annotation&lt;string&gt;(), // Final formatted response
});<h3>Configure la aplicación</h3><p>Todo el código de esta sección se puede encontrar en el <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/langgraph-js-elasticsearch">repositorio elasticsearch-labs</a>.</p><p>Abra un terminal en la carpeta donde estará la app e inicialice una aplicación Node.js con el comando:</p>npm init -y<p>Ahora podemos instalar las dependencias necesarias para este proyecto:</p>npm install @elastic/elasticsearch @langchain/langgraph @langchain/openai @langchain/core dotenv zod &amp;&amp; npm install --save-dev @types/node tsx typescript<ul><li><p><strong><code>@elastic/elasticsearch</code></strong>: nos ayuda a gestionar las solicitudes de Elasticsearch, como la ingesta y la recuperación de datos.</p></li><li><p><strong><code>@langchain/langgraph</code></strong>: Dependencia de JS para proporcionar todas las herramientas de LangGraph.</p></li><li><p><strong><code>@langchain/openai</code></strong>: cliente de OpenAI LLM para LangChain.</p></li><li><p>@langchain/core: proporciona los bloques fundamentales del núcleo para las apps de LangChain, incluidas las plantillas de prompts.</p></li><li><p><strong><code>dotenv</code></strong>: dependencia necesaria para usar variables de entorno en JavaScript.</p></li><li><p><strong><code>zod</code></strong>: dependencia para escribir datos.</p></li></ul><p><code>@types/node</code> <code>tsx</code> <code>typescript</code> nos permite escribir y ejecutar código TypeScript.</p><p>Ahora crea los siguientes archivos:</p><ul><li><p><code>elasticsearchSetup</code><a href="http://ingest.ts/"><code>.ts</code></a>: creará los mapping de índice, cargará el conjunto de datos desde un archivo JSON e ingerirá los datos en Elasticsearch.</p></li><li><p><a href="http://main.ts/"><code>main.ts</code></a>: incluirá la aplicación LangGraph.</p></li><li><p><code>.env</code>: archivo para almacenar las variables de entorno</p></li></ul><p>En el archivo <code>.env</code>, agreguemos las siguientes variables de entorno:</p>ELASTICSEARCH_ENDPOINT="your-endpoint-here"
ELASTICSEARCH_API_KEY="your-key-here"
OPENAI_API_KEY="your-key-here"<p>La clave API de OpenAPI no se usará directamente en el código; en su lugar, se usará internamente por la biblioteca <code>@langchain/openai</code>.</p><p>Toda la lógica relacionada con la creación de mapping, la creación de plantillas de búsqueda y la ingesta de sets de datos se encuentra en el archivo <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/langgraph-js-elasticsearch/elasticsearchSetup.ts"><code>elasticsearchSetup.ts</code></a>. En los próximos pasos, nos centraremos en el archivo <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/langgraph-js-elasticsearch/main.ts"><code>main.ts</code></a> . Además, puedes consultar los sets de datos para entender mejor cómo se ven los datos en el <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/langgraph-js-elasticsearch/dataset.json"><code>dataset.json</code></a>.</p><h3>Aplicación LangGraph</h3><p>En el archivo <code>main.ts</code>, vamos a importar algunas dependencias necesarias para consolidar la aplicación LangGraph. En este archivo, también debes incluir las funciones del node y la declaración de estado. La declaración del grafo se realizará en un método <code>main</code> en los siguientes pasos. El archivo <code>elasticsearchSetup.ts</code> contendrá ayudantes de Elasticsearch que vamos a usar dentro de los nodes en los próximos pasos.</p>import { writeFileSync } from "node:fs";
import { StateGraph, Annotation, START, END } from "@langchain/langgraph";
import { ChatOpenAI } from "@langchain/openai";
import { z } from "zod";
import {
  esClient,
  ingestDocuments,
  createSearchTemplates,
  INDEX_NAME,
  INVESTMENT_FOCUSED_TEMPLATE,
  MARKET_FOCUSED_TEMPLATE,
  createIndex,
} from "./elasticsearchSetup.js";

const llm = new ChatOpenAI({ model: "gpt-4o-mini" });<p>Como se mencionó anteriormente, el cliente LLM se utilizará para generar los parámetros de la plantilla de búsqueda de Elasticsearch basados en la pregunta del usuario.</p>async function saveGraphImage(app: any): Promise&lt;void&gt; {
  try {
    const drawableGraph = app.getGraph();
    const image = await drawableGraph.drawMermaidPng();
    const arrayBuffer = await image.arrayBuffer();

    const filePath = "./workflow_graph.png";
    writeFileSync(filePath, new Uint8Array(arrayBuffer));
    console.log(`📊 Workflow graph saved as: ${filePath}`);
  } catch (error: any) {
    console.log("⚠️  Could not save graph image:", error.message);
  }
}<p>El método anterior genera la imagen del grafo en formato png y utiliza la <a href="https://mermaid.ink/">API de Mermaid.INK</a> en segundo plano. Esto es útil si deseas ver cómo interactúan los nodes de la app con una visualización estilizada.</p><h3>Nodes LangGraph</h3><p>Ahora veamos cada node en detalle:</p><h3>node decideSearchStrategy</h3><p>El <code>decideSearchStrategy</code> node analiza la entrada del usuario y determina si realizar una búsqueda centrada en la inversión o en el mercado. Utiliza un LLM con un esquema de salida estructurado (definido con Zod) para clasificar el tipo de consulta. Antes de tomar la decisión, recupera los filtros disponibles del índice mediante una agregación, lo que garantiza que el modelo cuente con información actualizada sobre sectores, ubicaciones y datos de financiación.</p><p>Para extraer los posibles valores de los filtros y enviarlos al LLM, usemos una consulta <a href="https://www.elastic.co/docs/explore-analyze/query-filter/aggregations">de agregación</a> para obtenerlos directamente del índice de Elasticsearch. Esta lógica se encuentra en un método llamado <code>getAvailableFilters</code>:</p>async function getAvailableFilters() {
  try {
    const response = await esClient.search({
      index: INDEX_NAME,
      size: 0,
      aggs: {
        industries: {
          terms: { field: "industry", size: 100 },
        },
        locations: {
          terms: { field: "location", size: 100 },
        },
        funding_stages: {
          terms: { field: "funding_stage", size: 20 },
        },
        business_models: {
          terms: { field: "business_model", size: 10 },
        },
        lead_investors: {
          terms: { field: "lead_investor", size: 100 },
        },
        funding_amount_stats: {
          stats: { field: "funding_amount" },
        },
      },
    });

    return response.aggregations;
  } catch (error) {
    console.error("❌ Error getting available filters:", error);
    return {};
  }
}<p>Con la consulta de agregación anterior, tenemos los siguientes resultados:</p>{
  "industries": {
    "doc_count_error_upper_bound": 0,
    "sum_other_doc_count": 0,
    "buckets": [
      {
        "key": "logistics",
        "doc_count": 5
      },
      ...
    ]
  },
  "locations": {
    "doc_count_error_upper_bound": 0,
    "sum_other_doc_count": 0,
    "buckets": [
      {
        "key": "San Francisco, CA",
        "doc_count": 4
      },
      {
        "key": "New York, NY",
        "doc_count": 3
      },
      ...
    ]
  },
  "funding_stages": {
    "doc_count_error_upper_bound": 0,
    "sum_other_doc_count": 0,
    "buckets": [
      {
        "key": "Series A",
        "doc_count": 8
      },
      ...
    ]
  },
  "business_models": {
    "doc_count_error_upper_bound": 0,
    "sum_other_doc_count": 0,
    "buckets": [
      {
        "key": "B2B",
        "doc_count": 13
      },
      ...
    ]
  },
  "lead_investors": {
    "doc_count_error_upper_bound": 0,
    "sum_other_doc_count": 0,
    "buckets": [
      {
        "key": "Battery Ventures",
        "doc_count": 1
      },
      {
        "key": "Benchmark Capital",
        "doc_count": 1
      },
      ...
    ]
  },
  "funding_amount_stats": {
    "count": 20,
    "min": 4500000,
    "max": 35000000,
    "avg": 14075000,
    "sum": 281500000
  }
}<p>Vea todos los resultados <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/langgraph-js-elasticsearch/responses/aggregationsResponse.json">aquí</a>.</p><p>Para ambas estrategias, utilizaremos la búsqueda híbrida para detectar tanto la parte estructurada de la pregunta (filtros) como las partes más subjetivas (semántica). A continuación se muestra un ejemplo de ambas consultas utilizando <a href="https://www.elastic.co/docs/solutions/search/search-templates">plantillas de búsqueda</a>:</p>await esClient.putScript({
      id: INVESTMENT_FOCUSED_TEMPLATE,
      script: {
        lang: "mustache",
        source: `{
          "size": 5,
          "retriever": {
            "rrf": {
              "retrievers": [
                {
                  "standard": {
                    "query": {
                      "semantic": {
                        "field": "semantic_field",
                        "query": "{{query_text}}"
                      }
                    }
                  }
                },
                {
                  "standard": {
                    "query": {
                      "bool": {
                        "filter": [
                          {"terms": {"funding_stage": {{#join}}{{#toJson}}funding_stage{{/toJson}}{{/join}}}},
                          {"range": {"funding_amount": {"gte": {{funding_amount_gte}}{{#funding_amount_lte}},"lte": {{funding_amount_lte}}{{/funding_amount_lte}}}}},
                          {"terms": {"lead_investor": {{#join}}{{#toJson}}lead_investor{{/toJson}}{{/join}}}},
                          {"range": {"monthly_revenue": {"gte": {{monthly_revenue_gte}}{{#monthly_revenue_lte}},"lte": {{monthly_revenue_lte}}{{/monthly_revenue_lte}}}}}
                        ]
                      }
                    }
                  }
                }
              ],
              "rank_window_size": 100,
              "rank_constant": 20
            }
          }
        }`,
      },
    });<p>Vea las consultas detalladas en el archivo <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/langgraph-js-elasticsearch/elasticsearchSetup.ts#L119"><code>elasticsearchSetup.ts</code></a> . En el siguiente node, se decidirá cuál de las dos consultas se empleará:</p>// Node 1: Decide search strategy using LLM
async function decideSearchStrategy(state: typeof VCState.State) {
  // Zod schema for specialized search strategy decision
  const SearchDecisionSchema = z.object({
    search_type: z
      .enum(["investment_focused", "market_focused"])
      .describe("Type of specialized search strategy to use"),
    reasoning: z
      .string()
      .describe("Brief explanation of why this search strategy was chosen"),
  });

  const decisionLLM = llm.withStructuredOutput(SearchDecisionSchema);

  // Get dynamic filters from Elasticsearch
  const availableFilters = await getAvailableFilters();

  const prompt = `Query: "${state.input}"
    Available filters: ${JSON.stringify(availableFilters, null, 2)}

    Choose between two specialized search strategies:
    
    - investment_focused: For queries about funding stages, funding amounts, monthly revenue, lead investors, financial performance
    
    - market_focused: For queries about industries, locations, business models, market segments, geographic markets
    
    Analyze the query intent and choose the most appropriate strategy.
  `;

  try {
    const result = await decisionLLM.invoke(prompt);
    console.log(
      `🤔 Search strategy: ${result.search_type} - ${result.reasoning}`
    );

    return {
      searchStrategy: result.search_type,
    };
  } catch (error: any) {
    console.error("❌ Error in decideSearchStrategy:", error.message);
    return {
      searchStrategy: "investment_focused",
    };
  }
}<h3>Nodes prepareInvestmentSearch y prepareMarketSearch</h3><p>Ambos nodos emplean una función auxiliar compartida, <code>extractFilterValues</code>, que aprovecha el LLM para identificar los filtros relevantes mencionados en la entrada del usuario, como la industria, la ubicación, la etapa de financiación, el modelo de negocio, etc. Estamos utilizando este esquema para crear nuestra <a href="https://www.elastic.co/docs/solutions/search/search-templates">plantilla de búsqueda</a>.</p>// Extract all possible filter values from user input
async function extractFilterValues(input: string) {
  const FilterValuesSchema = z.object({
    // Investment-focused filters
    funding_stage: z
      .array(z.string())
      .default([])
      .describe("Funding stage values mentioned in query"),
    funding_amount_gte: z
      .number()
      .default(0)
      .describe("Minimum funding amount in USD"),
    funding_amount_lte: z
      .number()
      .default(100000000)
      .describe("Maximum funding amount in USD"),
    lead_investor: z
      .array(z.string())
      .default([])
      .describe("Lead investor values mentioned in query"),
    monthly_revenue_gte: z
      .number()
      .default(0)
      .describe("Minimum monthly revenue in USD"),
    monthly_revenue_lte: z
      .number()
      .default(10000000)
      .describe("Maximum monthly revenue in USD"),
    industry: z
      .array(z.string())
      .default([])
      .describe("Industry values mentioned in query"),
    location: z
      .array(z.string())
      .default([])
      .describe("Location values mentioned in query"),
    business_model: z
      .array(z.string())
      .default([])
      .describe("Business model values mentioned in query"),
  });

  const extractorLLM = llm.withStructuredOutput(FilterValuesSchema);
  const availableFilters = await getAvailableFilters();

  const extractPrompt = `Extract ALL relevant filter values from: "${input}"
    Available options: ${JSON.stringify(availableFilters, null, 2)}
    Extract only values explicitly mentioned in the query. Leave fields empty if not mentioned.`;

  return await extractorLLM.invoke(extractPrompt);
}<p>Según de la intención detectada, el flujo de trabajo selecciona una de dos rutas:</p><p><strong>prepareInvestmentSearch:</strong> desarrolla parámetros de búsqueda orientados a la financiación, incluyendo la etapa de financiación, el importe de la inversión, el inversionista y la información de renovación. Puedes encontrar la plantilla completa de consulta en el archivo <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/langgraph-js-elasticsearch/elasticsearchSetup.ts"><code>elasticsearchSetup.ts</code></a> :</p>// Node 2A: Prepare Investment-Focused Search Parameters 
async function prepareInvestmentSearch(state: typeof VCState.State) {
  console.log(
    "💰 Preparing INVESTMENT-FOCUSED search parameters with financial emphasis..."
  );

  try {
    // Extract all filter values from input
    const values = await extractFilterValues(state.input);

    let searchParams: any = {
      template_id: INVESTMENT_FOCUSED_TEMPLATE,
      query_text: state.input,
      ...values,
    };

    return { searchParams };
  } catch (error) {
    console.error("❌ Error preparing investment-focused params:", error);
    return {
      searchParams: {},
    };
  }
}<p><strong>prepareMarketSearch:</strong> crea parámetros orientados al mercado centrados en industrias, geografías y modelos de negocio. Ver la consulta completa en el archivo <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/langgraph-js-elasticsearch/elasticsearchSetup.ts"><code>elasticsearchSetup.ts</code></a>:</p>// Node 2B: Prepare Market-Focused Search Parameters
async function prepareMarketSearch(state: typeof VCState.State) {
  console.log(
    "🔍 Preparing MARKET-FOCUSED search parameters with market emphasis..."
  );

  try {
    // Extract all filter values from input
    const values = await extractFilterValues(state.input);

    let searchParams: any = {
      template_id: MARKET_FOCUSED_TEMPLATE,
      query_text: state.input,
      ...values,
    };

    return { searchParams };
  } catch (error) {
    console.error("❌ Error preparing market-focused params:", error);
    return {};
  }
}<h3>Node executeSearch</h3><p>Este node toma los parámetros de búsqueda generados del estado y los envía primero a Elasticsearch, usando la <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-render-search-template">_render API</a> para visualizar la consulta con fines de depuración, y luego envía una petición para recuperar los resultados.</p>// Node 3: Execute Search
async function executeSearch(state: typeof VCState.State) {
  const { searchParams } = state;

  try {
    // getting formed query from template for debugging
    const renderedTemplate = await esClient.renderSearchTemplate({
      id: searchParams.template_id,
      params: searchParams,
    });

    console.log(
      "📋 Complete query:",
      JSON.stringify(renderedTemplate.template_output, null, 2)
    );

    const results = await esClient.searchTemplate({
      index: INDEX_NAME,
      id: searchParams.template_id,
      params: searchParams,
    });

    return {
      results: results.hits.hits.map((hit: any) =&gt; hit._source),
    };
  } catch (error: any) {
    console.error(`❌ ${state.searchParams.search_type} search error:`, error);
    return { results: [] };
  }
}<h3>node visualizarResultados</h3><p>Finalmente, este nodo muestra los resultados de Elasticsearch.</p>// Node 4: Visualize results
async function visualizeResults(state: typeof VCState.State) {
  const results = state.results || [];

  let formattedResults = `🎯 Found ${results.length} startups matching your criteria:\n\n`;

  results.forEach((startup: any, index: number) =&gt; {
    formattedResults += `${index + 1}. **${startup.company_name}**\n`;
    formattedResults += `   📍 ${startup.location} | 🏢 ${startup.industry} | 💼 ${startup.business_model}\n`;
    formattedResults += `   💰 ${startup.funding_stage} - $${(
      startup.funding_amount / 1000000
    ).toFixed(1)}M\n`;
    formattedResults += `   👥 ${startup.employee_count} employees | 📈 $${(
      startup.monthly_revenue / 1000
    ).toFixed(0)}K MRR\n`;
    formattedResults += `   🏦 Lead: ${startup.lead_investor}\n`;
    formattedResults += `   📝 ${startup.description}\n\n`;
  });

  return {
    final: formattedResults,
  };
}<p>Programáticamente, todo el grafo se ve así:</p>  const workflow = new StateGraph(VCState)
    // Register nodes - these are the processing functions
    .addNode("decideStrategy", decideSearchStrategy)
    .addNode("prepareInvestment", prepareInvestmentSearch)
    .addNode("prepareMarket", prepareMarketSearch)
    .addNode("executeSearch", executeSearch)
    .addNode("visualizeResults", visualizeResults)
    // Define execution flow with conditional branching
    .addEdge(START, "decideStrategy") // Start with strategy decision
    .addConditionalEdges(
      "decideStrategy",
      (state: typeof VCState.State) =&gt; state.searchStrategy, // Conditional function
      {
        investment_focused: "prepareInvestment", // If investment focused -&gt; RRF template preparation
        market_focused: "prepareMarket", // If market focused -&gt; dynamic query preparation
      }
    )
    .addEdge("prepareInvestment", "executeSearch") // Investment prep -&gt; execute
    .addEdge("prepareMarket", "executeSearch") // Market prep -&gt; execute
    .addEdge("executeSearch", "visualizeResults") // Execute -&gt; visualize
    .addEdge("visualizeResults", END); // End workflow<p>Como puede ver, tenemos una aplicación condicional donde la aplicación decide qué “ruta” o node ejecutar a continuación. Esta característica es útil cuando los flujos de trabajo necesitan lógica de ramificación, como elegir entre múltiples herramientas o incluir un paso con intervención de una persona.</p><p>Con las características básicas del núcleo de LangGraph entendidas, podemos configurar la aplicación donde se ejecutará el código:</p><p>Juntando todo en un flujo de trabajo <code>main</code>, aquí declaramos el grafo con todos los elementos bajo la variable flujo de trabajo:</p>async function main() {
  await createIndex();
  await createSearchTemplates();
  await ingestDocuments();

  // Create the workflow graph with shared state
  const workflow = new StateGraph(VCState)
    // Register nodes - these are the processing functions
    .addNode("decideStrategy", decideSearchStrategy)
    .addNode("prepareInvestment", prepareInvestmentSearch)
    .addNode("prepareMarket", prepareMarketSearch)
    .addNode("executeSearch", executeSearch)
    .addNode("visualizeResults", visualizeResults)
    // Define execution flow with conditional branching
    .addEdge(START, "decideStrategy") // Start with strategy decision
    .addConditionalEdges(
      "decideStrategy",
      (state: typeof VCState.State) =&gt; state.searchStrategy, // Conditional function
      {
        investment_focused: "prepareInvestment", // If investment focused -&gt; RRF template preparation
        market_focused: "prepareMarket", // If market focused -&gt; dynamic query preparation
      }
    )
    .addEdge("prepareInvestment", "executeSearch") // Investment prep -&gt; execute
    .addEdge("prepareMarket", "executeSearch") // Market prep -&gt; execute
    .addEdge("executeSearch", "visualizeResults") // Execute -&gt; visualize
    .addEdge("visualizeResults", END); // End workflow


  const app = workflow.compile();

  await saveGraphImage(app);

  const query =
    "Find startups with Series A or Series B funding between $8M-$25M and monthly revenue above $500K";

  const marketResult = await app.invoke({ input: query });
  console.log(marketResult.final);
}<p>La variable de consulta simula la entrada del usuario introducida en una barra de búsqueda hipotética:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltba7189d5f4e63403/6a1709880e2e49cc3041a076/e8d76909eb2bc1bb62f3ca9a8b3e4b85fcec2893-1600x164.png" alt="" /><p>De la frase en lenguaje natural “Encuentra startups con financiamiento de Serie A o Serie B entre $8M y $25M, e ingresos mensuales superiores a $500K” se extraerán todos los filtros.</p><p>Finalmente, invoca el método principal:</p>main().catch(console.error);<h3>Resultados</h3>🔍 Checking if index exists...
🏗️ Creating index...
✅ Index created successfully!
Ingesting documents...
✅ Documents ingested successfully!
✅ Investment-focused template created successfully!
✅ Market-focused template created successfully!

📊 Workflow graph saved as: ./workflow_graph.png

🔍 Query: "Find startups with Series A or Series B funding between $8M-$25M and monthly revenue above $500K"

🤔 Search strategy: investment_focused - The query specifically seeks profitable fintech startups with defined funding amounts and high monthly revenue, which aligns closely with financial performance metrics and investment-related criteria.

💰 Preparing INVESTMENT-FOCUSED search parameters with financial emphasis...

📋 Complete query: {
  "size": 5,
  "retriever": {
    "rrf": {
      "retrievers": [
        {
          "standard": {
            "query": {
              "semantic": {
                "field": "semantic_field",
                "query": "Find startups with Series A or Series B funding between $8M-$25M and monthly revenue above $500K"
              }
            }
          }
        },
        {
          "standard": {
            "query": {
              "bool": {
                "filter": [
                  {
                    "terms": {
                      "funding_stage": [
                        "Series A",
                        "Series B"
                      ]
                    }
                  },
                  {
                    "range": {
                      "funding_amount": {
                        "gte": 8000000,
                        "lte": 25000000
                      }
                    }
                  },
                  {
                    "terms": {
                      "lead_investor": []
                    }
                  },
                  {
                    "range": {
                      "monthly_revenue": {
                        "gte": 500000,
                        "lte": 0
                      }
                    }
                  }
                ]
              }
            }
          }
        }
      ],
      "rank_window_size": 100,
      "rank_constant": 20
    }
  }
}
🎯 Found 5 startups matching your criteria:

1. **TechFlow**
   📍 San Francisco, CA | 🏢 logistics | 💼 B2B
   💰 Series A - $8.0M
   👥 45 employees | 📈 $500K MRR
   🏦 Lead: Sequoia Capital
   📝 TechFlow optimizes supply chain operations using AI-powered route optimization and real-time tracking. Founded in 2023, shows remarkable growth with $500K monthly revenue.

2. **DataViz**
   📍 New York, NY | 🏢 enterprise software | 💼 B2B
   💰 Series A - $10.0M
   👥 42 employees | 📈 $450K MRR
   🏦 Lead: Battery Ventures
   📝 DataViz creates intuitive data visualization tools for enterprise customers. No-code platform allows business users to create dashboards without technical expertise.

3. **FinanceAI**
   📍 San Francisco, CA | 🏢 fintech | 💼 B2C
   💰 Series C - $25.0M
   👥 120 employees | 📈 $1200K MRR
   🏦 Lead: Tiger Global Management
   📝 FinanceAI provides AI-powered investment advisory services to retail investors. Uses machine learning to analyze market trends with over 100,000 active users.

4. **UrbanMobility**
   📍 New York, NY | 🏢 logistics | 💼 B2B2C
   💰 Series B - $15.0M
   👥 78 employees | 📈 $750K MRR
   🏦 Lead: Kleiner Perkins
   📝 UrbanMobility revolutionizes urban transportation through autonomous delivery drones and smart logistics hubs. Partners with major retailers for same-day delivery across Manhattan and Brooklyn.

5. **HealthTech Solutions**
   📍 Boston, MA | 🏢 healthcare | 💼 B2B
   💰 Series B - $18.0M
   👥 95 employees | 📈 $900K MRR
   🏦 Lead: General Catalyst
   📝 HealthTech Solutions develops medical devices and software for remote patient monitoring. Comprehensive telehealth platform reducing hospital readmissions by 30%.

✨  Done in 18.80s.<p>Para la entrada enviada, la aplicación elige la ruta <strong>centrada en la inversión</strong> y, como resultado, podemos ver la consulta de Elasticsearch generada por el flujo de trabajo de LangGraph, que extrae los valores y los rangos de la entrada del usuario. También podemos ver la consulta enviada a Elasticsearch con los valores extraídos aplicados y, finalmente, los resultados formateados por el node <code>visualizeResults</code> con los resultados.</p><p>Ahora vamos a probar el node <strong>centrado en el mercado</strong> usando la consulta “Encuentre startups de fintech y salud en San Francisco, Nueva York o Boston”:</p>...

🔍 Query: Find fintech and healthcare startups in San Francisco, New York, or Boston

🤔 Search strategy: market_focused - The query is focused on finding fintech startups in San Francisco that are disrupting traditional banking and payment systems, which pertains to specific industries (fintech) and locations (San Francisco). Thus, a market-focused strategy is more appropriate.

🔍 Preparing MARKET-FOCUSED search parameters with market emphasis...

📋 Complete query: {
  "size": 5,
  "retriever": {
    "rrf": {
      "retrievers": [
        {
          "standard": {
            "query": {
              "semantic": {
                "field": "semantic_field",
                "query": "Find fintech and healthcare startups in San Francisco, New York, or Boston"
              }
            }
          }
        },
        {
          "standard": {
            "query": {
              "bool": {
                "filter": [
                  {
                    "terms": {
                      "industry": [
                        "fintech",
                        "healthcare"
                      ]
                    }
                  },
                  {
                    "terms": {
                      "location": [
                        "San Francisco, CA",
                        "New York, NY",
                        "Boston, MA"
                      ]
                    }
                  },
                  {
                    "terms": {
                      "business_model": []
                    }
                  }
                ]
              }
            }
          }
        }
      ],
      "rank_window_size": 50,
      "rank_constant": 10
    }
  }
}
🎯 Found 5 startups matching your criteria:

1. **FinanceAI**
   📍 San Francisco, CA | 🏢 fintech | 💼 B2C
   💰 Series C - $25.0M
   👥 120 employees | 📈 $1200K MRR
   🏦 Lead: Tiger Global Management
   📝 FinanceAI provides AI-powered investment advisory services to retail investors. Uses machine learning to analyze market trends with over 100,000 active users.

2. **CryptoWallet**
   📍 Miami, FL | 🏢 fintech | 💼 B2C
   💰 Series B - $16.0M
   👥 73 employees | 📈 $820K MRR
   🏦 Lead: Coinbase Ventures
   📝 CryptoWallet provides secure digital wallet solutions for cryptocurrency trading and storage. Multi-chain support with enterprise-grade security features.

...

✨  Done in 7.41s.<h2>Aprendizajes</h2><p>Durante el proceso de escritura aprendí:</p><ul><li><p>Debemos mostrar al LLM los valores exactos de los filtros; de lo contrario, dependemos de que el usuario escriba los valores exactos de las cosas. Para baja cardinalidad, este enfoque está bien, pero cuando la cardinalidad es alta, necesitamos algún mecanismo para filtrar los resultados.</p></li><li><p>El uso de plantillas de búsqueda hace que los resultados sean mucho más consistentes que dejar que el LLM escriba la consulta de Elasticsearch, y también es más rápido.</p></li><li><p>Los bordes condicionales son un mecanismo potente para crear aplicaciones con múltiples variantes y rutas de ramificación.</p></li><li><p>La salida estructurada es extremadamente útil cuando se genera información con LLM porque impone respuestas predecibles y de tipo seguro. Esto mejora la confiabilidad y reduce las interpretaciones incorrectas de los prompts.</p></li></ul><p>La combinación de búsqueda semántica y estructurada a través de la recuperación híbrida produce resultados mejores y más relevantes, equilibrando la precisión y la comprensión del contexto.</p><h2>Conclusión</h2><p>En este ejemplo, combinamos LangGraph.js con Elasticsearch para crear un flujo de trabajo dinámico capaz de interpretar consultas en lenguaje natural y decidir entre estrategias de búsqueda financieras u orientadas al mercado. Este enfoque reduce la complejidad de la creación de consultas manuales, al tiempo que mejora la flexibilidad y la precisión para los analistas de capital de riesgo.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/ai-agent-workflow-finance-langgraph-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/ai-agent-workflow-finance-langgraph-elasticsearch</guid>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[AI agéntica]]></category>
    <dc:creator><![CDATA[Jeffrey Rengifo]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt013eba5d152f11f3/6a1709892b835f6784f4b1a6/12b6057d84c6356267cd178a3c6c1a5c61123ece-2000x1256.png" length="0" type="image/png"/>
    <pubDate>Fri, 05 Dec 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Crear un conector de ChatGPT con Elasticsearch para buscar incidencias de GitHub.]]></title>
    <description><![CDATA[Aprende cómo crear un conector personalizado de ChatGPT y desplegar un servidor MCP de Elasticsearch que utiliza una búsqueda híbrida para buscar incidencias internas de GitHub.]]></description>
    <content:encoded><![CDATA[<p>Recientemente, OpenAI anunció la característica de <a href="https://help.openai.com/en/articles/11487775-connectors-in-chatgpt">conectores personalizados</a> para ChatGPT en los planes Pro/Business/Enterprise y Edu. Además de los conectores listos para usar que permiten acceder a datos en Gmail, GitHub, Dropbox, etc. Es posible crear conectores personalizados utilizando servidores MCP.</p><p>Los conectores personalizados te permiten combinar tus conectores de ChatGPT existentes con otras fuentes de datos como Elasticsearch para obtener respuestas integrales.</p><p>En este artículo, crearemos un servidor <a href="https://modelcontextprotocol.io/docs/getting-started/intro">MCP</a> que conecta ChatGPT a un índice de Elasticsearch que contiene información sobre incidencias internas de GitHub y solicitudes de extracción. Esto permite responder a búsquedas en lenguaje natural mediante los datos de Elasticsearch.</p><p>Desplegaremos el servidor MCP utilizando <a href="https://gofastmcp.com/getting-started/welcome">FastMCP</a> en Google Colab con ngrok para obtener una URL pública a la que ChatGPT pueda conectarse, lo que eliminará la necesidad de una infraestructura compleja.</p><p>Para una visión general del MCP y su ecosistema, consulta <a href="https://www.elastic.co/search-labs/blog/mcp-current-state">El estado actual de MCP</a>.</p><h2>Prerrequisitos</h2><p>Antes de comenzar, necesitarás:</p><ul><li><p>Clúster de Elasticsearch (8.X o superior).</p></li><li><p>Clave API de Elasticsearch con acceso de lectura a tu índice.</p></li><li><p>Cuenta de Google (para Google Colab)</p></li><li><p>Cuenta de Ngrok (el nivel gratuito funciona)</p></li><li><p>Cuenta de ChatGPT con plan Pro/Enterprise/Business o Edu.</p></li></ul><h2>Comprensión de los requisitos del conector MCP de ChatGPT.</h2><p>Los conectores MCP de ChatGPT requieren la implementación de dos herramientas: <code>search</code> y <code>fetch</code>. Para más detalles, consulta <a href="https://platform.openai.com/docs/mcp#create-an-mcp-server">OpenAI Docs</a>.</p><h3><a href="https://platform.openai.com/docs/mcp#search-tool">Herramienta de búsqueda</a></h3><p>Devuelve una lista de resultados relevantes de tu índice de Elasticsearch según la búsqueda del usuario.</p><h4>Lo que recibe:</h4><ul><li><p>Un solo texto con la búsqueda de lenguaje natural del usuario.</p></li><li><p>Ejemplo: “Encuentra incidencias relacionadas con la migración de Elasticsearch”.</p></li></ul><h4>Lo que devuelve: </h4><ul><li><p>Un objeto con una clave <code>result</code> que contiene un arreglo de objetos de resultado. Cada resultado incluye:</p><ul><li><p><code>id</code> - Identificador único de documentos.</p></li><li><p><code>title</code> - Título de la incidencia o PR.</p></li><li><p><code>url</code> - Enlace a la incidencia o PR.</p></li></ul></li></ul><h4>En nuestra implementación:</h4>return {
    "results": [
        {
            "id": "PR-612",
            "title": "Fix memory leak in WebSocket notification service",
            "url": "https://internal-git.techcorp.com/pulls/612"
        },
        # ... more results
    ]
}<h3><a href="https://platform.openai.com/docs/mcp#fetch-tool">Herramienta de extracción</a></h3><p>Recupera el contenido completo de un documento específico.</p><h4>Lo que recibe:</h4><ul><li><p>Una sola cadena de texto con el ID del documento de Elasticsearch del resultado de la búsqueda.</p></li><li><p>Ejemplo: “Consígueme los detalles de PR-578”.</p></li></ul><h4>Lo que devuelve:</h4><ul><li><p>Un objeto de documento completo con:</p><ul><li><p><code>id</code> - Identificador único de documentos.</p></li><li><p><code>title</code> - Título de la incidencia o PR.</p></li><li><p><code>text</code> - Descripción completa del problema/PR y sus detalles</p></li><li><p><code>url</code> - Enlace a la incidencia o PR.</p></li><li><p><code>type</code> - Tipo de documento (incidencia, pull_request).</p></li><li><p><code>status</code> - Estado actual (abierto, en progreso, resuelto)</p></li><li><p><code>priority</code> - Nivel de prioridad (bajo, medio, alto, crítico)</p></li><li><p><code>assignee</code> - Persona asignada al problema/PR</p></li><li><p><code>created_date</code> - Fecha de creación.</p></li><li><p><code>resolved_date</code> - Cuando se resolvió (si procede)</p></li><li><p><code>labels</code> - Etiquetas asociadas al documento</p></li><li><p><code>related_pr</code> - ID de solicitud de extracción relacionado</p></li></ul></li></ul>return {
    "id": "PR-578",
    "title": "Security hotfix: Patch SQL injection vulnerabilities",
    "text": "Description: CRITICAL SECURITY FIX for ISSUE-1889. Patches SQL...",
    "url": "https://internal-git.techcorp.com/pulls/578",
    "type": "pull_request",
    "status": "closed",
    "priority": "critical",
    "assignee": "sarah_dev",
    "created_date": "2025-09-19",
    "resolved_date": "2025-09-19",
    "labels": "security, hotfix, sql",
    "related_pr": null
}<p><strong>Nota</strong>: Este ejemplo usa una estructura plana donde todos los campos están en el nivel raíz. Los requisitos de OpenAI son flexibles y también admiten objetos de metadatos anidados.</p><h2>Sets de datos de incidencias y PR de GitHub</h2><p>Para este tutorial, vamos a usar un set de datos interno de GitHub que contenga incidencias y solicitudes de extracción. Esto representa un escenario en el que deseas buscar datos internos privados a través de ChatGPT.</p><p>Los sets de datos se pueden encontrar <a href="https://gist.github.com/TomasMurua/4e7bbdf7a7ebbdffaa663c43578d934a">aquí</a>. Y actualizaremos el índice de los datos mediante la <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-bulk">API de bulk</a>.</p><p>Este sets de datos incluye:</p><ul><li><p>Problemas con descripciones, estado, prioridad y responsables.</p></li><li><p>Solicitudes de extracción con cambios de código, revisiones e información de despliegue.</p></li><li><p>Relaciones entre incidencias y PR (p. ej., PR-578 soluciona ISSUE-1889).</p></li><li><p>Etiquetas, fechas y otros metadatos</p></li></ul><h3>Mappings de índices</h3><p>El índice utiliza los siguientes <a href="https://www.elastic.co/docs/manage-data/data-store/mapping">mappings</a> para brindar soporte a la búsqueda híbrida con <a href="https://www.elastic.co/docs/explore-analyze/machine-learning/nlp/ml-nlp-elser">ELSER</a>. El campo <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/semantic-text">text_semantic</a> se utiliza para la búsqueda semántica, mientras que los demás campos permiten la búsqueda por palabras clave.</p>{
  "mappings": {
    "properties": {
      "id": {
        "type": "keyword"
      },
      "title": {
        "type": "text"
      },
      "text": {
        "type": "text"
      },
      "text_semantic": {
        "type": "semantic_text",
        "inference_id": ".elser-2-elasticsearch"
      },
      "url": {
        "type": "keyword"
      },
      "type": {
        "type": "keyword"
      },
      "status": {
        "type": "keyword"
      },
      "priority": {
        "type": "keyword"
      },
      "assignee": {
        "type": "keyword"
      },
      "created_date": {
        "type": "date",
        "format": "iso8601"
      },
      "resolved_date": {
        "type": "date",
        "format": "iso8601"
      },
      "labels": {
        "type": "keyword"
      },
      "related_pr": {
        "type": "keyword"
      }
    }
  }
}<h2>Construye el servidor MCP</h2><p>Nuestro servidor MCP implementa dos herramientas que siguen las especificaciones de OpenAI, y utilizan búsquedas híbridas para combinar coincidencia semántica y textual para obtener mejores resultados.</p><h3>Herramienta de búsqueda</h3><p>Usa la búsqueda híbrida con <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/reciprocal-rank-fusion">RRF</a> (Fusión de Rango Recíproco), combinando la búsqueda semántica con la coincidencia de texto:</p>@mcp.tool()
    async def search(query: str) -&gt; Dict[str, List[Dict[str, Any]]]:
        """
        Search for internal issues and PRs using hybrid search (semantic + text with RRF).
        Returns list with id, title, and url per OpenAI spec.
        """
        if not query or not query.strip():
            return {"results": []}

        logger.info(f"Searching for: '{query}'")

        try:
            # Hybrid search with RRF (Reciprocal Rank Fusion)
            response = es_client.search(
                index=ELASTICSEARCH_INDEX,
                size=10,
                source=["id", "title", "url", "type", "priority"],
                retriever={
                    "rrf": {
                        "retrievers": [
                            {
                                # Semantic search with ELSER
                                "standard": {
                                    "query": {
                                        "semantic": {
                                            "field": "text_semantic",
                                            "query": query
                                        }
                                    }
                                }
                            },
                            {
                                # Text search (BM25) for keyword matching
                                "standard": {
                                    "query": {
                                        "multi_match": {
                                            "query": query,
                                            "fields": [
                                                "title^3",
                                                "text^2",
                                                "assignee^2",
                                                "type",
                                                "labels",
                                                "priority"
                                            ],
                                            "type": "best_fields",
                                            "fuzziness": "AUTO"
                                        }
                                    }
                                }
                            }
                        ],
                        "rank_window_size": 50,
                        "rank_constant": 60
                    }
                }
            )

            results = []
            if response and 'hits' in response:
                for hit in response['hits']['hits']:
                    source = hit['_source']
                    results.append({
                        "id": source.get('id', hit['_id']),
                        "title": source.get('title', 'Unknown'),
                        "url": source.get('url', '')
                    })

            logger.info(f"Found {len(results)} results")
            return {"results": results}

        except Exception as e:
            logger.error(f"Search error: {e}")
            raise ValueError(f"Search failed: {str(e)}")<h3>Puntos clave:</h3><ul><li><p><strong>Búsqueda híbrida con RRF:</strong> Combina búsqueda semántica (ELSER) y búsqueda por texto (BM25) para mejores resultados.</p></li><li><p><strong>Búsqueda de múltiples coincidencias:</strong> <a href="https://www.elastic.co/docs/reference/query-languages/query-dsl/query-dsl-multi-match-query">Busca en múltiples campos</a> con mejores ponderaciones (título^3, texto^2, responsable^2). El símbolo de intercalación (^) multiplica las puntuaciones de relevancia, y prioriza las coincidencias en los títulos sobre el contenido.</p></li><li><p><strong>Correspondencia aproximada:</strong> <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/common-options#fuzziness"><code>fuzziness: AUTO</code></a> maneja los errores tipográficos y ortográficos al permitir coincidencias aproximadas.</p></li><li><p><strong>Ajuste de parámetros de RRF:</strong></p><ul><li><p><code>rank_window_size: 50</code> - Especifica cuántos resultados principales de cada recuperador (semántico y textual) se consideran antes de combinarlos.</p></li><li><p><code>rank_constant: 60</code> - Este valor determina cuánta influencia tienen los documentos en los conjuntos de resultados individuales sobre el resultado final clasificado.</p></li></ul></li><li><p><strong>Solo devuelve los campos obligatorios:</strong> <code>id</code>, <code>title</code>, <code>url</code> según la especificación de OpenAI, y evita exponer otros campos innecesariamente.</p></li></ul><h3>Herramienta de extracción</h3><p>Recupera los detalles del documento por ID de documento, si existe:</p>@mcp.tool()
    async def fetch(id: str) -&gt; Dict[str, Any]:
        """
        Retrieve complete issue/PR details by ID.
        Returns id, title, text, url.
        """
        if not id:
            raise ValueError("ID is required")

        logger.info(f"Fetching: {id}")

        try:
            # Search by the 'id' field (not _id) since IDs are stored as a field
            response = es_client.search(
                index=ELASTICSEARCH_INDEX,
                body={
                    "query": {
                        "term": {
                            "id": id  # Search by your custom 'id' field
                        }
                    },
                    "size": 1
                }
            )

            if not response or not response['hits']['hits']:
                raise ValueError(f"Document with id '{id}' not found")

            hit = response['hits']['hits'][0]
            source = hit['_source']

            result = {
                "id": source.get('id', id),
                "title": source.get('title', 'Unknown'),
                "text": source.get('text', ''),
                "url": source.get('url', ''),
                "type": source.get('type', ''),
                "status": source.get('status', ''),
                "priority": source.get('priority', ''),
                "assignee": source.get('assignee', ''),
                "created_date": source.get('created_date', ''),
                "resolved_date": source.get('resolved_date', ''),
                "labels": source.get('labels', ''),
                "related_pr": source.get('related_pr', '')
            }

            logger.info(f"Fetched: {result['title']}")
            return result

        except Exception as e:
            logger.error(f"Fetch error: {e}")
            raise ValueError(f"Failed to fetch '{id}': {str(e)}")<h3>Puntos clave:</h3><ul><li><p><strong>Búsqueda por campo de ID de documento:</strong> usa la búsqueda de término en el campo personalizado <code>id</code>.</p></li><li><p><strong>Devuelve el documento completo:</strong> incluye el campo completo <code>text</code> con todo el contenido.</p></li><li><p><strong>Estructura plana:</strong> todos los campos en el nivel raíz, coincidiendo con la estructura de documentos de Elasticsearch.</p></li></ul><h2>Desplegar en Google Colab</h2><p>Usaremos Google Colab para ejecutar nuestro servidor MCP y ngrok para exponerlo públicamente de forma tal que ChatGPT pueda conectarse.</p><h3>Paso 1: Abre el cuaderno de Google Colab.</h3><p>Accede a nuestro cuaderno preconfigurado <a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/elasticsearch-chatgpt-connector">Elasticsearch MCP para ChatGPT</a>.</p><h3>Paso 2: Configura tus credenciales</h3><p>Necesitarás tres datos:</p><ul><li><p><strong>URL de Elasticsearch:</strong> Tu <a href="https://www.elastic.co/docs/deploy-manage/deploy/cloud-enterprise/connect-elasticsearch">URL del cluster de Elasticsearch</a>.</p></li><li><p><strong>Clave API de Elasticsearch:</strong> <a href="https://www.elastic.co/docs/deploy-manage/api-keys/elasticsearch-api-keys">Clave API</a> con acceso de lectura a tu índice.</p></li><li><p><strong>Token de autenticación ngrok:</strong> Token gratis de <a href="https://ngrok.com/">ngrok</a>. Usaremos ngrok para exponer la URL de MCP a internet y así ChatGPT pueda conectarse.</p></li></ul><h4>Obtener tu token ngrok</h4><ol><li><p>Regístrate para una cuenta gratis en <a href="https://ngrok.com/">ngrok</a></p></li><li><p>Ve a tu <a href="https://dashboard.ngrok.com/">dashboard de ngrok</a></p></li><li><p>Copia tu token de autenticación</p></li></ol><h4>Agregar secretos a Google Colab</h4><p>En el cuaderno de Google Colab:</p><ol><li><p>Haz clic en el <strong>icono de llave </strong>en la barra lateral izquierda para abrir <strong>Secretos</strong>.</p></li><li><p>Añade estos tres secretos:</p></li></ol>ELASTICSEARCH_URL=https://your-cluster.elastic.com:443
ELASTICSEARCH_API_KEY=your-api-key
NGROK_TOKEN=your-ngrok-token<p>3.   Habilitar el acceso al cuaderno para cada secreto</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5acae97b386277f8/6a17f08f5ea30f74c964b6c2/d5dd6ac19fe816a562c6351fdb0f11369da0e877-609x321.jpg" alt="Agregar secretos a Google Collab" /><h3>Paso 3: Ejecutar el cuaderno</h3><ol><li><p>Haz clic en <strong>Tiempo de ejecución</strong> y, a continuación, en <strong>Ejecutar todo</strong> para ejecutar todas las celdas.</p></li><li><p>Espera que el servidor se inicie (aproximadamente 30 segundos).</p></li><li><p>Busque la salida que muestre su URL pública de ngrok</p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdd11aacf2deab67c/6a17f091e8fbce81f13a1a41/f185100e8869624bc9e1c7b2b4eb32785e2d89e7-1189x283.png" alt="" /><p>4. La salida mostrará algo como:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8891d917fdbaaf48/6a17f092abe0f208c7dfeaf6/e02e625e91ed9136454e4401b184575fb03a336e-1052x465.jpg" alt="La salida de ejecutar un cuaderno en Google Collab" /><h2>Conéctate a ChatGPT.</h2><p>Ahora conectaremos el servidor MCP a tu cuenta de ChatGPT.</p><ol><li><p>Abre ChatGPT y ve a <strong>Configuración</strong>.</p></li><li><p>Navega a <strong>Conectores</strong>.Si estás usando una cuenta Pro, debes activar el <a href="https://platform.openai.com/docs/guides/developer-mode">modo de desarrollador</a> en los conectores.</p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt95efdcb2c39307e7/6a17f094abe0f24d8edfeafa/32c02192912fc0e7e5a52e9399077ba7ae3b4901-739x715.png" alt="Conectando el servidor MPC a una cuenta de ChatGPT" /><p><em>Si estás usando la versión Enterprise o Business de ChatGPT, debes publicar el conector en tu lugar de trabajo.</em></p><p>3. Haz clic en <strong>Crear.</strong></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd4c8fc8dd6033918/6a17f095631730de19585b7b/15c53e5ccc381108a9dc0052cca05bf0fc97679a-755x683.png" alt="Agregar un conector a ChatGPT" /><p><em><strong>Nota</strong></em><em>: En los espacios de trabajo Business, Enterprise y Edu, solo los propietarios, administradores y usuarios que tengan habilitada la correspondiente configuración (para Enterprise/Edu) pueden agregar conectores personalizados. Los usuarios con un rol de miembro común no tienen la capacidad de agregar conectores personalizados por su cuenta.</em></p><p><em>Una vez que un propietario o usuario administrador agrega un conector y lo habilita, estará disponible para que lo usen todos los miembros del espacio de trabajo.</em></p><p>4. Introduce la información requerida y la URL de tu ngrok que termina en <code>/sse/</code>. Recuerda la “/” después de “sse”. No funcionará si no la agregas:</p><ul><li><p><strong>Nombre:</strong> Elasticsearch MCP</p></li><li><p><strong>Descripción: </strong>MCP personalizado para buscar y extraer información interna de GitHub.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd716ad0beeeb1d35/6a17f09714d90c11cc79b6d7/162a85705cc8ac48a3f2f665551d513e0719f93d-479x684.png" alt="Crear un conector MCP de Elastic " /><p>5. Presione <strong>Crear</strong> para guardar el MCP personalizado.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt857794237d7d3b5a/6a17f0983e03d729b74f2d54/97eb5fb0a32b86bfadfb35561f698616f217c049-913x629.png" alt="Guardar el conector MCP personalizado haciendo clic en crear" /><p>La conexión es instantánea si tu servidor está en funcionamiento. No se necesita ninguna otra autenticación, ya que la clave API de Elasticsearch está configurada en tu servidor.</p><h2>Prueba el servidor MCP</h2><p>Antes de hacer preguntas, necesitas seleccionar qué conector ChatGPT debe usar.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd1602c48878dc7a9/6a17f09a6df731cca90a0fff/77a6fc1eb263a0eb16aac64f2ecaca5f4ac12ec2-966x568.gif" alt="Seleccionar qué conector debe usar ChatGPT" /><h3>Indicación 1: Buscar incidencias</h3><p>Pregunta: “<strong>Encuentre incidencias relacionadas con la migración de Elasticsearch” </strong>y confirma la llamada a la herramienta de acciones.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6c204ceacf897f61/6a17f09c9da390fb1de4657d/cfd781acbff8cd7c8095bbe29224f8b26d581f77-650x375.png" alt="Pide a ChatGPT “Encuentra incidencias relacionadas con la migración de Elasticsearch” y confirma la llamada a la herramienta de acciones." /><p>ChatGPT llamará a la herramienta <code>search</code> con tu búsqueda. Puedes ver que está buscando herramientas disponibles y preparándose para llamar a la herramienta Elasticsearch y confirma con el usuario antes de tomar cualquier acción en relación con la herramienta.</p><h4>Solicitud de llamada a la herramienta:</h4>{
  "query": "Elasticsearch migration issues"
}<h4>Respuesta de la herramienta:</h4>{
  "results": [
    {
      "id": "PR-598",
      "title": "Elasticsearch 8.x migration - Application code changes",
      "url": "https://internal-git.techcorp.com/pulls/598"
    },
    {
      "id": "ISSUE-1712",
      "title": "Migrate from Elasticsearch 7.x to 8.x",
      "url": "https://internal-git.techcorp.com/issues/1712"
    },
    {
      "id": "RFC-045",
      "title": "Design Proposal: Microservices Migration Architecture",
      "url": "https://internal-git.techcorp.com/rfcs/045"
    }
    // ... 7 more results
  ]
}<p>ChatGPT procesa los resultados y los presenta en un formato natural y conocido.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1b4378e7d26b4ad0/6a17f09ddbb4ff4de1fb57bf/9d5b6cff85c7e54ccc2584b8ae96d45495fae8c1-923x1352.png" alt="Cómo procesa ChatGPT los resultados de la solicitud de llamada a la herramienta y la respuesta de la herramienta" /><h3>Entre bastidores</h3><h4>Indicación: “Busca incidencias relacionadas con la migración de Elasticsearch”</h4><p>1. Llamadas de ChatGPT <code>search(“Elasticsearch migration”)</code></p><p>2. Elasticsearch realiza una búsqueda híbrida</p><ul><li><p>La <strong>búsqueda semántica</strong> entiende conceptos como “actualización” y “<em>compatibilidad de versiones”.</em></p></li><li><p>La <strong>búsqueda de texto</strong> encuentra coincidencias exactas de "<em>Elasticsearch</em>" y "migración".</p></li><li><p><strong>RRF</strong> combina y clasifica los resultados de ambos enfoques</p></li></ul><p>3. Devuelve los 10 mejores eventos que coinciden con <code>id</code>, <code>title</code>, <code>url</code></p><p>4. ChatGPT identifica “<em>ISSUE-1712: migrar de Elasticsearch 7.x a 8.x</em>” como el resultado más relevante.</p><h3>Indicación 2: Obtén los detalles completos</h3><p>Pregunta: <em><strong>“Obtén detalles de ISSUE-1889”</strong></em></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8d1a53db8bfe8326/6a17f09f445de966104d021a/5c0db5245535ce67a36056e61e135bddc97ce496-934x629.png" alt="ChatGPT reconoce que quieres información detallada sobre un problema específico y llama a la herramienta de búsqueda y confirma con el usuario antes de hacer cualquier acción con la herramienta." /><p>ChatGPT reconoce que deseas información detallada sobre una incidencia específica, llama a la herramienta <code>fetch</code> y confirma con el usuario antes de tomar cualquier acción con la herramienta.</p><h4>Solicitud de llamada a la herramienta:</h4>{
  "id": "ISSUE-1889"
}<h4>Respuesta de la herramienta:</h4>{
  "id": "ISSUE-1889",
  "title": "SQL injection vulnerability in search endpoint",
  "text": "Description: Security audit identified SQL injection vulnerability in /api/v1/search endpoint. User input from query parameter is not properly sanitized before being used in raw SQL query. Severity: HIGH - Immediate action required Affected Code: - File: services/search/query_builder.py - Line: 145-152 - Issue: String concatenation used instead of parameterized queries Investigation: - @security_team_alice: Confirmed exploitable with UNION-based injection - @sarah_dev: Checking all other endpoints for similar patterns - @john_backend: Found 3 more instances in legacy codebase Remediation: - Rewrite using SQLAlchemy ORM or parameterized queries - Add input validation and sanitization - Implement WAF rules as additional layer - Security regression tests Comments: - @tech_lead_mike: Stop all other work, this is P0 - @sarah_dev: PR-578 ready with fixes for all 4 vulnerable endpoints - @alex_devops: Deployed hotfix to production 2025-09-19 at 14:30 UTC - @security_team_alice: Verified fix, conducting full pentest next week Resolution: All vulnerable endpoints patched. Added pre-commit hooks to catch raw SQL queries. Security training scheduled for team.",
  "url": "https://internal-git.techcorp.com/issues/1889",
  "type": "issue",
  "status": "closed",
  "priority": "critical",
  "assignee": "sarah_dev",
  "created_date": "2025-09-18",
  "resolved_date": "2025-09-19",
  "labels": "security, vulnerability, bug, sql",
  "related_pr": "PR-578"
}<p>ChatGPT sintetiza la información y la presenta de manera clara.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt560958fa3bd212d0/6a17f0a0faa91355ba93c974/410f19f213e94fc4e3c47eeef6e04b69e0c86159-602x462.png" alt="Cómo ChatGPT sintetiza la información y la presenta " /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcccf35a584e8373b/6a17f0a2505ac3471cad8c2e/54d8ffa117628a1e3afc317c3ab75d4f7731d7ab-767x1600.png" alt="Cómo ChatGPT presenta la información" /><h3>Entre bastidores</h3><h4>Indicación: «Obtén los detalles de ISSUE-1889»</h4><ol><li><p>Llamadas de ChatGPT <code>fetch(“ISSUE-1889”)</code></p></li><li><p>Elasticsearch recupera el documento completo</p></li><li><p>Devuelve un documento completo con todos los campos a nivel raíz.</p></li><li><p>ChatGPT sintetiza la información y responde con citas adecuadas.</p></li></ol><h2>Conclusión</h2><p>En este artículo, creamos un servidor MCP personalizado que conecta ChatGPT a Elasticsearch con herramientas MCP dedicadas de <strong>búsqueda</strong> y <strong>extracción</strong>, lo cual permite realizar búsquedas en lenguaje natural sobre datos privados.</p><p>Este patrón MCP funciona para cualquier índice, documentación, productos, logs o cualquier otro dato de Elasticsearch que quieras buscar mediante lenguaje natural.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/chatgpt-connector-mcp-server-github-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/chatgpt-connector-mcp-server-github-elasticsearch</guid>
    <category><![CDATA[AI agéntica]]></category>
    <category><![CDATA[Búsqueda híbrida]]></category>
    <dc:creator><![CDATA[Tomás Murúa]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd1602c48878dc7a9/6a17f09a6df731cca90a0fff/77a6fc1eb263a0eb16aac64f2ecaca5f4ac12ec2-966x568.gif" length="0" type="image/gif"/>
    <pubDate>Mon, 01 Dec 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Desarrollo de un asistente RAG agente usando LangChain y Elasticsearch]]></title>
    <description><![CDATA[Aprende a construir un asistente de noticias de prensa de prensa agential usando LangChain y Elasticsearch que responda a consultas sobre artículos con enrutamiento adaptativo.]]></description>
    <content:encoded><![CDATA[<p>Esta entrada de blog profundiza en los flujos de trabajo de RAG agentes, explicando sus características clave y patrones de diseño habituales. Además, demuestra cómo implementar estos flujos de trabajo mediante un ejemplo práctico que emplea Elasticsearch como almacén vectorial y LangChain para construir el marco agente RAG. Finalmente, el artículo analiza brevemente las mejores prácticas y los desafíos asociados al diseño e implementación de dichas arquitecturas. Puedes seguir el proceso para crear una pipeline agente RAG sencilla con este <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/agentic-rag/agent_rag_news_assistant.ipynb">cuaderno Jupyter</a>.</p><h2>Introducción al RAG agente</h2><p>La Generación Aumentada por Recuperación (<a href="https://www.elastic.co/docs/solutions/search/rag">RAG</a>) se convirtió en una piedra angular en aplicaciones basadas en LLM, permitiendo a los modelos proporcionar respuestas óptimas al recuperar el contexto relevante basado en las consultas de los usuarios. Los sistemas RAG mejoran la precisión y el contexto de las respuestas de los LLM al aprovechar información externa de APIs o almacenes de datos, en lugar de limitar al conocimiento preentrenado de los LLM. Por otro lado, los agentes de IA operan de forma autónoma, tomando decisiones y tomando medidas para alcanzar sus objetivos designados.</p><p>El RAG agente es un marco que unifica las fortalezas tanto de la generación aumentada por recuperación como del razonamiento agentivo. Integra RAG en el proceso de toma de decisiones del agente, permitiendo al sistema elegir dinámicamente fuentes de datos, refinar consultas para una mejor recuperación de contexto, generar respuestas más precisas y aplicar un bucle de retroalimentación para mejorar continuamente la calidad de la salida.</p><h2>Características clave del RAG agente</h2><p>El marco RAG agente supone un avance importante respecto a los sistemas RAG tradicionales. En lugar de seguir un proceso de recuperación fijo, aprovecha agentes dinámicos capaces de planear, ejecutar y optimizar resultados en tiempo real.</p><p>Veamos algunas de las características clave que distinguen a las pipelines RAG agenticas:</p><ul><li><p><strong>Toma de decisiones dinámica</strong>: El RAG agente emplea un mecanismo de razonamiento para entender la intención del usuario y enrutar cada consulta a la fuente de datos más relevante, produciendo respuestas precisas y conscientes del contexto.</p></li><li><p><strong>Análisis exhaustivo de consultas:</strong> Agentic RAG analiza en profundidad las consultas de los usuarios, incluyendo subpreguntas y su intención general. Evalúa la complejidad de las consultas y selecciona dinámicamente las fuentes de datos más relevantes para obtener información, cerciorando respuestas precisas y completas.</p></li><li><p><strong>Colaboración en varias etapas</strong>: Este marco permite la colaboración en varias etapas a través de una red de agentes especializados. Cada agente gestiona una parte específica de un objetivo mayor, trabajando de forma secuencial o simultánea para lograr un resultado coherente.</p></li><li><p><strong>Mecanismos de autoevaluación</strong>: La cadena agente RAG emplea la autorreflexión para evaluar documentos recuperados y respuestas generadas. Puede comprobar si la información recuperada responde completamente a la consulta y luego revisar la salida para comprobar su exactitud, completitud y consistencia fáctica.</p></li><li><p><strong>Integración con herramientas externas</strong>: Este flujo de trabajo puede interactuar con APIs externas, bases de datos y fuentes de información en tiempo real, incorporando información actualizada y adaptar dinámicamente a los datos en evolución.</p></li></ul><h2>Patrones de flujo de trabajo del RAG agente</h2><p>Los patrones de flujo de trabajo definen cómo la IA agente estructura, gestiona y orquesta aplicaciones basadas en LLM de manera fiable y eficiente. Varios frameworks y plataformas, como <a href="https://www.langchain.com/">LangChain</a>, <a href="https://www.langchain.com/langgraph">LangGraph</a>, <a href="https://www.crewai.com/">CrewAI</a> y <a href="https://www.llamaindex.ai/">LlamaIndex</a>, pueden emplear para implementar estos flujos de trabajo agentes.</p><ol><li><p><strong>Cadena de recuperación secuencial</strong>: Los flujos de trabajo secuenciales dividen tareas complejas en pasos simples y ordenados. Cada paso mejora la entrada para el siguiente, lo que conduce a mejores resultados. Por ejemplo, al crear un perfil de cliente, un agente puede extraer datos básicos de un CRM, otro obtener el historial de compras de una base de datos de transacciones y un agente final combinar esta información para generar un perfil completo de recomendaciones o reportes.</p></li><li><p><strong>Cadena de recuperación de enrutamiento</strong>: En este patrón de flujo de trabajo, un agente router analiza la entrada y la dirige al proceso o fuente de datos más adecuada. Este enfoque es especialmente eficaz cuando existen múltiples fuentes de datos distintas con una superposición mínima. Por ejemplo, en un sistema de atención al cliente, el agente del router categoriza las solicitudes entrantes, como problemas técnicos, reembolsos o reclamaciones, y las encamina al departamento correspondiente para su gestión eficiente.</p></li><li><p><strong>Cadena de recuperación paralela</strong>: En este patrón de flujo de trabajo, se ejecutan simultáneamente múltiples subtareas independientes y sus salidas se agregan posteriormente para generar una respuesta final. Este enfoque reduce significativamente el tiempo de procesamiento y aumenta la eficiencia del flujo de trabajo. Por ejemplo, en un flujo de trabajo paralelo de atención al cliente, un agente recupera solicitudes pasadas similares y otro consulta artículos relevantes de la base de conocimiento. Un agregador combina entonces estas salidas para generar una resolución completa.</p></li><li><p><strong>Cadena de trabajadores Orchestrator</strong>: Este flujo de trabajo comparte similitudes con la paralelización debido a su utilización de subtareas independientes. Sin embargo, una distinción clave radica en la integración de un agente orquestador. Este agente es responsable de analizar las consultas de los usuarios, segmentarlas dinámicamente en subtareas durante la ejecución e identificar los procesos o herramientas adecuadas necesarias para formular una respuesta precisa.</p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1e2e634cf9c94e25/6a17ff81b1e113c9fc79f4e0/ece6fc2403f211556c93e99d5227bfb7053b0c31-1600x1047.png" alt="Patrón de flujo de trabajo en RAG agentic" /><h2>Construyendo una pipeline RAG agentica desde cero</h2><p>Para ilustrar los principios del RAG agente, diseñemos un flujo de trabajo usando LangChain y Elasticsearch. Este flujo de trabajo adopta una arquitectura basada en enrutamiento, donde varios agentes colaboran para analizar consultas, recuperar información relevante, evaluar resultados y generar respuestas coherentes. Podrías consultar este <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/agentic-rag/agent_rag_news_assistant.ipynb">cuaderno Jupyter</a> para seguir este ejemplo.</p><p>El flujo de trabajo comienza con el agente router, que analiza la consulta del usuario para seleccionar el método óptimo de recuperación, es decir, un enfoque <code>vectorstore</code>, <code>websearch</code>o <code>composite</code> . La vectorstore se encarga de la recuperación tradicional de documentos basada en RAG, la búsqueda sitio web obtiene la información más reciente que no está almacenada en la vectorstore, y el enfoque compuesto combina ambas cuando se necesita información de múltiples fuentes.</p><p>Si los documentos se consideran adecuados, el agente de resumen genera una respuesta clara y contextualmente adecuada. Sin embargo, si los documentos son insuficientes o irrelevantes, el agente de reescritura de la consulta reformula la consulta para mejorar la búsqueda. Esta consulta revisada resetear entonces el proceso de enrutamiento, permitiendo al sistema refinar su búsqueda y mejorar la salida final.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt16020333cf6dda91/6a17ff82e8fbceb4d83a1c00/ed8701a7f15558fbf2e967a884b3e770eccb826b-1256x1092.png" alt="Cómo un sistema agentico refina su salida con diferentes consultas" /><h3>Prerrequisitos</h3><p>Este flujo de trabajo se basa en los siguientes componentes clave para ejecutar el ejemplo de forma eficaz:</p><ul><li><p>Python 3.10</p></li><li><p><a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/agentic-rag/agent_rag_news_assistant.ipynb">Cuaderno Jupyter</a></p></li><li><p>Azure OpenAI</p></li><li><p>Elasticsearch</p></li><li><p>LangChain</p></li></ul><p>Antes de continuar, se te pedirá que configures el siguiente conjunto de variables de entorno requeridas para este ejemplo.</p>AZURE_OPENAI_ENDPOINT="Add your azure openai endpoint"
AZURE_OPENAI_KEY="Add your azure openai key"
AZURE_OPENAI_DEPLOYMENT="gpt-4.1"
AZURE_OPENAI_API_VERSION="Add your azure openai api version"

ES_ENDPOINT = "Add your Elasticsearch ENDPOINT"
ES_API_KEY = "Add your Elasticsearch API KEY"<h3>Fuentes de datos</h3><p>Este flujo de trabajo se ilustra empleando un subconjunto del conjunto de datos de AG News. El conjunto de datos comprende artículos de noticias en diversas categorías, como Internacional, Deportes, Negocios y Ciencia/Tecnología.</p>dataset = load_dataset("ag_news", split="train[:1000]")
docs = [
    Document(
        page_content=sample["text"],
        metadata={"category": sample["label"]}
    )
    for sample in dataset
]<p>El <a href="https://python.langchain.com/docs/integrations/vectorstores/elasticsearch/">módulo ElasticsearchStore</a> se emplea desde el <code>langchain_elasticsearch</code> como nuestro almacén vectorial. Para la recuperación, implementamos SparseVectorStrategy, empleando <a href="https://www.elastic.co/docs/explore-analyze/machine-learning/nlp/ml-nlp-elser">ELSER</a>, el modelo propietario de incrustación de Elastic. Es esencial confirmar que el modelo ELSER está correctamente instalado y desplegado en tu entorno Elasticsearch antes de iniciar el almacén vectorial.</p>elastic_vectorstore = ElasticsearchStore.from_documents(
    docs,
    es_url=ES_ENDPOINT,
    es_api_key=ES_API_KEY,
    index_name=index_name,
    strategy=SparseVectorStrategy(model_id=".elser_model_2"),
)

elastic_vectorstore.client.indices.refresh(index=index_name)<p>La funcionalidad de búsqueda sitio web se implementa usando <a href="https://python.langchain.com/api_reference/community/tools/langchain_community.tools.ddg_search.tool.DuckDuckGoSearchRun.html">DuckDuckGoSearchRun</a> de las herramientas comunitarias LangChain, lo que permite al sistema recuperar información en tiempo real de la web de forma eficiente. También puedes considerar usar otras APIs de búsqueda que puedan ofrecer resultados más relevantes. Esta herramienta fue elegida porque permite búsquedas sin necesidad de clave API.</p>duckduckgo = DuckDuckGoSearchRun(description= "A custom DuckDuckGo search tool for finding latest news stories.", verbose=True)
def websearch_retriever(query):
    results = duckduckgo.run(f"{query}")
    return results<p>El retriever compuesto está diseñado para consultas que requieren una combinación de fuentes. Se emplea para proporcionar una respuesta completa y contextualmente precisa al recuperar datos en tiempo real de la web y consultar noticias históricas del almacén vectorial.</p>def composite_retriever(query):
    related_docs = vectorstore_retriever(query)
    related_docs += websearch_retriever(query)
    return related_docs<h3>Preparando a los agentes</h3><p>En el siguiente paso, los agentes LLM se definen para proporcionar capacidades de razonamiento y toma de decisiones dentro de este flujo de trabajo. Las cadenas de LLM que crearemos incluyen: <code>router_chain</code>, <code>grade_docs_chain</code>, <code>rewrite_query_chain</code>y <code>summary_chain</code>.</p><p>El agente router emplea un asistente LLM para determinar la fuente de datos más adecuada para una consulta determinada en tiempo de ejecución. El agente evaluador evalúa la relevancia de los documentos recuperados. Si los documentos se consideran relevantes, se entregan al agente de resumen para que genere un resumen. De lo contrario, el agente de consulta de reescritura reformula la consulta y la envía de vuelta al proceso de enrutamiento para otro intento de recuperación. Puedes encontrar las instrucciones de todos los agentes en la sección de cadenas de LLM del <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/agentic-rag/agent_rag_news_assistant.ipynb">cuaderno</a>.</p>class RouteQuery(BaseModel):
    datasource: Literal["vectorstore", "websearch", "composite"] = Field(
        ...,
        description="Choose to route the query to web search, vectorstore or composite."
    )

router_prompt = ChatPromptTemplate.from_template("""You are an assistant that decides the best data source for questions based on news articles.
Choose one of the following options:
- 'vectorstore': for general, background, or historical news articles.
- 'websearch': for recent discoveries, 'latest', 'current', or '2025' type queries.
- 'composite': when the question needs both historical and current knowledge on news articles.

Question: {query}

Return one word: 'vectorstore', 'websearch', or 'composite'.
""")
router_structured = llm.with_structured_output(RouteQuery)
router_chain: RunnableSequence = router_prompt | router_structured<p>La <code>llm.with_structured_output</code> limita la salida del modelo para seguir un esquema predefinido definido por el BaseModel bajo la clase <code>RouteQuery</code> , cerciorando la consistencia de los resultados. La segunda línea compone una <code>RunnableSequence</code> conectando <code>router_prompt</code> con <code>router_structured</code>, formando una tubería en la que el modelo de lenguaje procesa el prompt de entrada para producir resultados estructurados y compatibles con el esquema.</p><h3>Definir nodos de grafo</h3><p>Esta parte implica definir los estados del grafo, que representan los datos que fluyen entre los diferentes componentes del sistema. Una especificación clara de estos estados cerciora que cada nodo del flujo de trabajo sepa qué información puede acceder y actualizar.</p>class RAGState(TypedDict):
    query: str
    docs: List[Document]
    router: str
    summary: str
    self_reflection: bool
    retry_count: int = 0<p>Una vez definidos los estados, el siguiente paso es definir los nodos del grafo. Los nodos son como las unidades funcionales del grafo que realizan operaciones específicas sobre los datos. Hay 7 nodos diferentes en nuestra pipeline.</p>def router(state: RAGState):
   router = router_chain.invoke({'query': state["query"]})
   logger.info(f"Router selected the datasource: {router.datasource}")
   logger.info(f"User query: {state['query']}")
   return {"router": router.datasource}

def vectorstore(state: RAGState):
   return {"docs": vectorstore_retriever(state["query"])}

def websearch(state: RAGState):
   return {"docs": websearch_retriever(state["query"])}

def composite(state: RAGState):
   return {"docs": composite_retriever(state["query"])}

def self_reflection(state: RAGState):
   evaluation = grade_docs_chain.invoke(
       {"query": state["query"], "docs": state["docs"]}
   )
   if evaluation.binary_score:
       logger.info(f"Self-reflection passed -- binary_score={evaluation.binary_score}")
   else:
       logger.info(f"Self-reflection failed -- binary_score={evaluation.binary_score}")

   return {
       "self_reflection": evaluation.binary_score,
   }

def query_rewriter(state: RAGState):
   retry_count = state.get("retry_count", 0) + 1
   new_query = rewrite_query_chain.invoke({"query": state["query"]})
   logger.info(f"Query rewritten: {new_query}, retry_count: {retry_count}")
   return {
       "query": new_query,
       "retry_count": retry_count,
   }

def summarize(state: RAGState):
   summary = summarize_chain.run(
       query=state["query"],
       docs=state["docs"],
   )
   return {"summary": summary}<p>El nodo <code>query_rewriter</code> cumple dos funciones en el flujo de trabajo. Primero, reescribe la consulta del usuario usando el <code>rewrite_query_chain</code> para mejorar la recuperación cuando los documentos evaluados por el agente autorreflexivo se consideran insuficientes o irrelevantes. Segundo, actúa como un contador que registra cuántas veces se reescribió la consulta.</p><p>Cada vez que se invoca el nodo, incrementa la <code>retry_count</code> almacenada en el estado del flujo de trabajo. Este mecanismo impide que el flujo de trabajo entre en un bucle infinito. Si el <code>retry_count</code> supera un umbral predefinido, el sistema puede recurrir a un estado de error, una respuesta por defecto o cualquier otra condición predefinida que elijas.</p><h3>Compilación del grafo</h3><p>El último paso es definir las aristas del grafo y agregar las condiciones necesarias antes de compilarlo. Cada grafo debe comenzar desde un nodo inicial designado, que sirve como punto de entrada para el flujo de trabajo. Las aristas en el gráfico representan el flujo de datos entre nodos y pueden ser de dos tipos:</p><ul><li><p>Aristas rectas: Estas definen un flujo directo e incondicional de un nodo a otro. Cada vez que el primer nodo completa su tarea, el flujo de trabajo avanza automáticamente al siguiente nodo a lo largo de la arista recta.</p></li><li><p>Aristas condicionales: Estas permiten que el flujo de trabajo se ramifice según el estado actual o los resultados del cálculo de un nodo. El siguiente nodo se selecciona dinámicamente en función de condiciones como resultados de evaluación, decisiones de enrutamiento o recuentos de intentos.</p></li></ul>graph.add_edge(START, "router")

def after_router(state: RAGState):
   route = state.get("router", None)
   if route == "vectorstore":
       return "vectorstore"
   elif route == "websearch":
       return "websearch"
   else:
       return "composite"

def after_self_reflection(state: RAGState):
   if state["self_reflection"]:
           return "summarize"
   return "query_rewriter"

def after_query_rewriter(state: RAGState):
   while state['retry_count'] &lt;= 3:
           return "router"
   raise RuntimeError("Maximum retries (3) reached -- evaluation failed.")

graph.add_conditional_edges(
   "router",
   after_router,
   {
       "vectorstore": "vectorstore",
       "websearch": "websearch",
       "composite": "composite"
   }
)

graph.add_edge("vectorstore", "self_reflection")
graph.add_edge("websearch", "self_reflection")
graph.add_edge("composite", "self_reflection")
graph.add_conditional_edges(
   "self_reflection",
   after_self_reflection,
   {
       "summarize": "summarize",
       "query_rewriter": "query_rewriter"
   }
)
graph.add_conditional_edges("query_rewriter", after_query_rewriter, {"router": "router"})
graph.add_edge("summarize", END)
agent=graph.compile()<p>Con eso, tu primera pipeline de RAG agente está lista y puede probar usando el agente compilado.</p>result = agent.invoke({"query": query1})
logger.info(f"\nFinal Summary:\n: {result['summary']}")<h3>Prueba de la tubería agente RAG</h3><p>Ahora probaremos esta canalización usando tres tipos distintos de consultas como se indica a continuación. Ten en cuenta que los resultados pueden variar, y los ejemplos que se muestran a continuación ilustran solo un posible resultado.</p>query1="What are the latest AI models released this month?"
query2="What technological innovations are discussed in Sci/Tech news?"
query3="Compare a Sci/Tech article from the dataset with a current web article about AI trends."<p>Para la primera consulta, el router selecciona <code>websearch</code> como fuente de datos. La consulta no supera la evaluación de autorreflexión y posteriormente se redirige a la etapa de reescritura de la consulta, como se muestra en el resultado.</p>INFO     | __main__:router:11 - Router selected the datasource: websearch
INFO     | __main__:router:12 - User query: What are the latest AI models released this month?
Latest Singapore news, including the city state's relationships with Malaysia and Mahathir, China and Xi Jinping, and the rest of Southeast Asia. 3 days ago · The latest military news, insights and analysis from China. All the latest news, opinions and analysis on Hong Kong, China, Asia and around the world Latest news, in-depth features and opinion on Malaysia, covering politics, economy, society and the Asean member-nation's relationships with China, Singapore, and other Southeast Asian ... Oct 12, 2025 · Brics (an acronym for Brazil, Russia, India, China and South Africa) refers to an association of 10 leading emerging markets. The other member states are Egypt, Ethiopia, ...
INFO     | __main__:self_reflection:31 - Self-reflection failed -- binary_score=False
INFO     | __main__:query_rewriter:40 - Query rewritten: query='Which AI models have been officially released in June 2024?', retry_count: 1
INFO     | __main__:router:11 - Router selected the datasource: websearch
INFO     | __main__:router:12 - User query: query='Which AI models have been officially released in June 2024?'
Dream Machine is a text-to-video model created by Luma Labs and launched in June 2024 . It generates video output based on user prompts or still images. Dream Machine has been noted for its ability to realistically capture motion... Released in June 2023. In June 2024 , Baidu announced Ernie 4.0 Turbo. In April 2025, Ernie 4.5 Turbo and X1 Turbo were released . These models are optimized for faster response times and lower operational costs.[28][29]. The meaning of QUERY is question, inquiry. How to use query in a sentence. Synonym Discussion of Query. QUERY definition: 1. a question, often expressing doubt about something or looking for an answer from an authority.... Learn more. Query definition: a question; an inquiry.. See examples of QUERY used in a sentence.
INFO     | __main__:self_reflection:29 - Self-reflection passed -- binary_score=True
INFO     | __main__:&lt;module&gt;:2 - 
Final Summary:
: In June 2024, two AI models were officially released: Dream Machine, a text-to-video model launched by Luma Labs, and Ernie 4.0 Turbo, announced by Baidu, which is optimized for faster response times and lower operational costs.<p>A continuación, examinamos un ejemplo en el que se emplea <code>vectorstore</code> recuperación, demostrado con la segunda consulta.</p>INFO     | __main__:router:11 - Router selected the datasource: vectorstore
INFO     | __main__:router:12 - User query: What technological innovations are discussed in Sci/Tech news?
INFO     | __main__:self_reflection:29 - Self-reflection passed -- binary_score=True
INFO     | __main__:&lt;module&gt;:2 - 
Final Summary:
: Recent Sci/Tech news highlights several technological innovations: NASA is collaborating with Silicon Valley firms to build a powerful Linux-based supercomputer to support theoretical research and shuttle engineering; new chromatin transfer techniques have enabled the cloning of cats; cybersecurity advancements are being discussed in relation to protecting personal technology; Princeton University scientists assert that existing technologies can be used immediately to stabilize global warming; and a set of GameBoy micro-games has been recognized for innovation in game design.<p>La consulta final se dirige a la recuperación compuesta, que emplea tanto la vectorstore como la búsqueda sitio web.</p>INFO     | __main__:router:11 - Router selected the datasource: composite
INFO     | __main__:router:12 - User query: Compare a Sci/Tech article from the dataset with a current web article about AI trends.
Atlas currently only available on macOS, built on Chromium with planned features like ad-blocking still in development. OpenAI's Atlas browser launched with bold promises of AI -powered web browsing, but early real-world testing reveals a different story. Career-long data are updated to end-of-2024 and single recent year data pertain to citations received during calendar year 2024. The selection is based on the top 100,000 scientists by c-score (with and without self-citations) or a percentile rank of 2% or above in the sub-field. In this article I list 45 AI tools across 21 different categories. After exploring all the available options in each category, I've carefully selected the best tools based on my personal experience. Reading a complex technical article ? Simply highlight confusing terminology and ask "what's this?" to receive instant explanations. compare browsers. Comparison showing traditional browser navigation versus OpenAI Atlas AI -powered workflows. After putting Gemini, ChatGPT, Grok, and DeepSeek through rigorous testing in October 2025, it's clear that there isn't one AI that reigns supreme across all categories.
INFO     | __main__:self_reflection:29 - Self-reflection passed -- binary_score=True
INFO     | __main__:&lt;module&gt;:2 - 
Final Summary:
: A Sci/Tech article from the dataset highlights NASA's development of robust artificial intelligence software for planetary rovers, aiming to make them more self-reliant and capable of decision-making during missions. In contrast, a current web article about AI trends focuses on the proliferation of AI-powered tools across various categories, including browsers like OpenAI Atlas, and compares leading models such as Gemini, ChatGPT, Grok, and DeepSeek, noting that no single AI currently excels in all areas. While the NASA article emphasizes specialized AI applications for autonomous robotics in space exploration, the current trends article showcases the broadening impact of AI across consumer and professional technologies, with ongoing competition and rapid innovation among major AI platforms.<p>En el flujo de trabajo anterior, el RAG agente determina de forma inteligente qué fuente de datos emplear al recuperar información para una consulta de usuario, mejorando así la precisión y relevancia de la respuesta. Puedes crear ejemplos adicionales para probar al agente y revisar los resultados para ver si dan resultados interesantes.</p><h2>Mejores prácticas para construir flujos de trabajo agenticos RAG</h2><p>Ahora que entendemos cómo funciona el RAG agente, veamos algunas buenas prácticas para construir estos flujos de trabajo. Seguir estas directrices ayudará a mantener el sistema eficiente y fácil de mantener.</p><ul><li><p><strong>Prepárate para los recursos de respaldo</strong>: Planea estrategias de respaldo con antelación para escenarios en los que algún paso del flujo de trabajo falle. Estos pueden incluir devolver respuestas por defecto, activar estados de error o emplear herramientas alternativas. Esto garantiza que el sistema gestione los fallos con elegancia sin romper el flujo de trabajo global.</p></li><li><p><strong>Implementa registros completos</strong>: Prueba a implementar registros en cada etapa del flujo de trabajo, como intentos, salidas generadas, elecciones de enrutamiento y reescrituras de consultas. Estos registros ayudan a mejorar la transparencia, facilitan la depuración y ayudan a refinar los prompts, el comportamiento de los agentes y las estrategias de recuperación con el tiempo.</p></li><li><p><strong>Selecciona el patrón de flujo de trabajo adecuado</strong>: Examina tu caso de uso y selecciona el patrón que mejor se adapte a tus necesidades. Emplea flujos de trabajo secuenciales para razonamiento paso a paso, flujos de trabajo paralelos para fuentes de datos independientes y patrones orquestador-trabajador para consultas multiherramienta o complejas.</p></li><li><p><strong>Incorpora estrategias de evaluación</strong>: Integra mecanismos de evaluación en diferentes etapas del flujo de trabajo. Estos pueden incluir agentes de autorreflexión, calificación de documentos recuperados o controles automáticos de calidad. La evaluación ayuda a verificar que los documentos recuperados son relevantes, que las respuestas son precisas y que todas las partes de una consulta compleja están abordadas.</p></li></ul><h2>Desafíos</h2><p>Aunque los sistemas RAG agenticos ofrecen beneficios significativos en términos de adaptabilidad, precisión y razonamiento dinámico, también presentan ciertos desafíos que deben abordar durante sus etapas de diseño e implementación. Algunos de los principales retos incluyen:</p><ul><li><p><strong>Flujos de trabajo complejos</strong>: A medida que se agregan más agentes y puntos de decisión, el flujo de trabajo global se vuelve cada vez más complejo. Esto puede llevar a mayores probabilidades de errores o fallos en tiempo de ejecución. Siempre que sea posible, prioriza flujos de trabajo optimizados eliminando agentes redundantes y puntos de decisión innecesarios.</p></li><li><p><strong>Escalabilidad</strong>: Puede ser complicado escalar sistemas RAG agentes para manejar grandes conjuntos de datos y grandes volúmenes de consultas. Incorpora estrategias eficientes de indexación, caché y procesamiento distribuido para mantener el rendimiento a gran escala.</p></li><li><p><strong>Orquestación y sobrecarga computacional</strong>: La ejecución de flujos de trabajo con múltiples agentes requiere orquestación avanzada. Esto incluye una planeación cuidadosa, gestión de dependencias y coordinación de agentes para evitar cuellos de botella y conflictos, todo lo cual contribuye a la complejidad general del sistema.</p></li><li><p><strong>Complejidad de la evaluación</strong>: La evaluación de estos flujos de trabajo presenta desafíos inherentes, ya que cada etapa requiere una estrategia de evaluación distinta. Por ejemplo, la etapa RAG debe evaluar para verificar la relevancia y completitud de los documentos recuperados, mientras que los resúmenes generados deben verificar para garantizar su calidad y precisión. Del mismo modo, la efectividad de la reformulación de consultas requiere una lógica de evaluación separada para determinar si la consulta reescrita mejora los resultados de recuperación.</p></li></ul><h2>Conclusión</h2><p>En esta entrada de blog, presentamos el concepto de RAG agente y destacamos cómo mejora el marco tradicional de RAG al incorporar capacidades autónomas de la IA agente. Exploramos las características principales de RAG agente y demostramos estas características mediante un ejemplo práctico, construyendo un asistente de noticias usando Elasticsearch como almacén vectorial y LangChain para crear el marco agente.</p><p>Además, discutimos las mejores prácticas y los principales retos a considerar al diseñar e implementar una pipeline agentica RAG. Estos conocimientos están destinados a guiar a los desarrolladores en la creación de sistemas agentivos robustos, escalables y eficientes que combinen eficazmente la recuperación, el razonamiento y la toma de decisiones.</p><h2>¿Qué sigue ahora?</h2><p>El flujo de trabajo que creamos es sencillo, dejando amplio margen para mejoras y experimentación. Podemos mejorar esto experimentando con varios modelos de incrustación y refinando estrategias de recuperación. Además, integrar a un agente de reclasificación para priorizar los documentos recuperados podría ser beneficioso. Otra área de exploración implica desarrollar estrategias de evaluación para marcos agentivos, identificando específicamente enfoques comunes y reutilizables aplicables a diferentes tipos de marcos. Por último, experimentar con estos marcos en conjuntos de datos grandes y más complejos.</p><p>Mientras tanto, si tienes experimentos similares que compartir, ¡nos encantaría saberlos! No dudes en dar tus opiniones o conectar con nosotros a través de nuestro <a href="https://ela.st/slack">canal comunitario de Slack</a> o <a href="https://discuss.elastic.co/c/security">foros de discusión</a>.</p><h2>Recursos</h2><ul><li><p><a href="https://arxiv.org/abs/2310.11511">Auto-RAG: Aprender a recuperar, generar y criticar a través de la autorreflexión</a></p></li><li><p><a href="https://arxiv.org/abs/2501.09136">Generación aumentada por recuperación agentica: una encuesta sobre el RAG agente</a></p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/agentic-rag-news-assistant-langchain-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/agentic-rag-news-assistant-langchain-elasticsearch</guid>
    <category><![CDATA[AI agéntica]]></category>
    <dc:creator><![CDATA[Kirti Sodhi]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8c7a9f3b0d141d5d/6a17ff83fbc5f86686491d15/59dc0077f5dab00561d9f1b1e7dbf8ec3456259e-1600x1047.heif" length="0" type="image/*"/>
    <pubDate>Fri, 28 Nov 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Construir un agente de IA para RRHH con Elastic Agent Builder y GPT-OSS]]></title>
    <description><![CDATA[Descubre cómo crear un agente de IA que pueda responder consultas en lenguaje natural sobre los datos de RRHH de tus empleados usando Elastic Agent Builder y GPT-OSS.]]></description>
    <content:encoded><![CDATA[<h2>Introducción</h2><p>Este artículo te mostrará cómo construir un agente de IA para RRHH usando <a href="https://openai.com/index/introducing-gpt-oss/">GPT-OSS</a> y Elastic Agent Builder. El agente puede responder a tus preguntas sin enviar datos a OpenAI, Anthropic ni ningún servicio externo.</p><p>Usaremos LM Studio para servir GPT-OSS localmente y lo conectaremos a Elastic Agent Builder.</p><p>Al final de este artículo, tendrás un agente de IA personalizado que podrá responder preguntas en lenguaje natural sobre los datos de tus empleados manteniendo el control total sobre tu información y modelo.</p><h2>Prerrequisitos</h2><p>Para este artículo, necesitas:</p><ul><li><p><a href="https://www.elastic.co/cloud">Elastic Cloud</a> alojaba la versión 9.2, despliegue serverless o <a href="https://www.elastic.co/docs/deploy-manage/deploy/self-managed/local-development-installation-quickstart">local</a></p></li><li><p>Máquina con 32GB de RAM recomendada (mínimo 16GB para GPT-OSS 20B)</p></li><li><p><a href="https://lmstudio.ai/">LM Studio</a> instalado</p></li><li><p><a href="https://www.docker.com/products/docker-desktop/">Escritorio Docker</a> Instalado</p></li></ul><h2>¿Por qué usar GPT-OSS?</h2><p>Con un LLM local tienes el control para desplegarlo en tu propia infraestructura y ajustarlo para adaptarlo a tus propias necesidades. Todo esto manteniendo el control sobre los datos que compartes con el modelo y, por supuesto, no tienes que pagar una tasa de licencia a un proveedor externo.</p><p>OpenAI <a href="https://openai.com/index/introducing-gpt-oss/">lanzó GPT-OSS</a> el 5 de agosto de 2025, como parte de su compromiso con el ecosistema del modelo abierto.</p><p>El modelo de parámetros 20B ofrece:</p><ul><li><p><strong>Capacidades de uso de herramientas</strong></p></li><li><p><strong>Inferencia eficiente</strong></p></li><li><p><strong>Compatible con SDK OpenAI</strong></p></li><li><p><strong>Compatible con flujos de trabajo agentes</strong></p></li></ul><p>Comparación de referencias:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt58fab956edb40412/6a170cfcb0367da43a72bd80/29160e3345352088e8213297630882f252b00c47-1600x680.png" alt="" /><h2>Arquitectura de soluciones</h2><p>La arquitectura funciona completamente en tu máquina local. Elastic (que funciona en Docker) se comunica directamente con tu LLM local a través de LM Studio, y Elastic Agent Builder emplea esta conexión para crear agentes de IA personalizados que pueden consultar los datos de tus empleados.</p><p>Para más detalles, consulte esta <a href="https://www.elastic.co/docs/solutions/observability/connect-to-own-local-llm">documentación</a>.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt80db5bb0a797f51b/6a170cfd0e2e492f2c41a16f/a4a886750ff25fa8bb7aefc7448161e52cf73ed3-1600x896.png" alt="" /><h2>Construir un agente de IA para RRHH: Pasos</h2><p>Dividiremos la implementación en 5 pasos:</p><ol><li><p>Configurar LM Studio con un modelo local</p></li><li><p>Despliega Elastic Local con Docker</p></li><li><p>Crea el conector OpenAI en Elastic</p></li><li><p>Sube los datos de los empleados a Elasticsearch</p></li><li><p>Construye y prueba tu agente de IA</p></li></ol><h2>Paso 1: Configurar LM Studio con GPT-OSS 20B</h2><p>LM Studio es una aplicación fácil de usar que te permite ejecutar grandes modelos de lenguaje localmente en tu computadora. Proporciona un servidor API compatible con OpenAI, lo que facilita su integración con herramientas como Elastic sin un proceso de configuración complejo. Para más detalles, consulta la <a href="https://lmstudio.ai/docs/app">documentación de LM Studio</a>.</p><p>Primero, descarga e instala LM Studio desde el sitio web oficial. Una vez instalado, abre la aplicación.</p><h3>En la interfaz de LM Studio:</h3><ol><li><p>Ve a la pestaña de búsqueda y busca "GPT-OSS"</p></li><li><p>Selecciona el <code>openai/gpt-oss-20b</code> de OpenAI</p></li><li><p>Haz clic en descargar</p></li></ol><p>El tamaño de este modelo debería ser aproximadamente <strong>12,10GB</strong>. La descarga puede tardar unos minutos, dependiendo de tu conexión a Internet.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2dc341a6625e34b7/6a170cff839dfa2eb4dcff44/5d01bc4dcb377b5259fc6b521fe2425a31b90ca4-1312x872.png" alt="" /><h4>Una vez descargado el modelo:</h4><ol><li><p>Ve a la pestaña del servidor local</p></li><li><p>Selecciona el openai/gpt-oss-20b</p></li><li><p>Usa el puerto predeterminado 1234</p></li><li><p>En el panel derecho, ve a <strong>Cargar </strong>y establece la longitud de contexto en <strong>40K</strong> o más</p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3704ca1b28465cc4/6a170d00d7c022ed8fde64ef/e546033f916381647b876815b2c1f1ae2a08365f-326x337.png" alt="" /><p>5. Haz clic en iniciar servidor</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7b9170a4945ff857/6a170d0266c4f9ffadf8c0a6/28ee78a3caa84d14e04db3d42f30acbe4d4d005a-1312x872.png" alt="" /><p>Deberías ver esto si el servidor está funcionando.</p>[LM STUDIO SERVER] Success! HTTP server listening on port 1234
[LM STUDIO SERVER] Supported endpoints:
[LM STUDIO SERVER] -&gt;	GET  http://localhost:1234/v1/models
[LM STUDIO SERVER] -&gt;	POST http://localhost:1234/v1/responses
[LM STUDIO SERVER] -&gt;	POST http://localhost:1234/v1/chat/completions
[LM STUDIO SERVER] -&gt;	POST http://localhost:1234/v1/completions
[LM STUDIO SERVER] -&gt;	POST http://localhost:1234/v1/embeddings
Server started.<h2>Paso 2: Desplegar Elastic Local con Docker</h2><p>Ahora configuraremos Elasticsearch y Kibana localmente usando Docker. Elastic proporciona un script conveniente que gestiona todo el proceso de configuración. Para más detalles, consulte la <a href="https://www.elastic.co/docs/deploy-manage/deploy/self-managed/local-development-installation-quickstart">documentación oficial</a>.</p><h3>Ejecuta el script de inicio local</h3><p>Ejecuta el siguiente comando en tu terminal:</p>curl -fsSL https://elastic.co/start-local | sh<p>Este guion será:</p><ul><li><p>Descargar y configurar Elasticsearch y Kibana</p></li><li><p>Inicia ambos servicios usando Docker Compose</p></li><li><p>Activa automáticamente una licencia de prueba Platinum de 30 días</p></li></ul><h3>Producción esperada</h3><p>Solo espera el siguiente mensaje y almacena la contraseña y la clave API que se muestra; los necesitarás para acceder a Kibana:</p>🎉 Congrats, Elasticsearch and Kibana are installed and running in Docker!
🌐 Open your browser at http://localhost:5601
   Username: elastic
   Password: KSUlOMNr
🔌 Elasticsearch API endpoint: http://localhost:9200
🔑 API key: cnJGX0pwb0JhOG00cmNJVklUNXg6cnNJdXZWMnM4bncwMllpQlFlUTlWdw==
Learn more at https://github.com/elastic/start-local<h3>Acceso a Kibana</h3><p>Abre tu navegador y navega a:</p>http://localhost:5601<p>Inicia sesión usando las credenciales obtenidas en la salida del terminal.</p><h3>Habilitar Agent Builder</h3><p>Una vez iniciado sesión en Kibana, navega a <strong>Management </strong>&gt;<strong> AI </strong>&gt;<strong> Agent Builder </strong>y activa el Agent Builder.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0a934bd99fa6a0ce/6a170d046234e019c3db1a5a/92e104cb846c20d875865ded8a3d37f5c7daae9b-1491x1528.png" alt="" /><h2>Paso 3: Crea el conector OpenAI en Elastic</h2><p>Ahora configuraremos Elastic para que use tu LLM local.</p><h3>Conectores de acceso</h3><ol><li><p>En Kibana</p></li><li><p>Ve a Configuración &gt; <strong>Gestión del Proyecto</strong> </p></li><li><p>En <strong>Alertas e Información</strong>, selecciona <strong>Conectores</strong></p></li><li><p>Haz clic en Crear conector</p></li></ol><h3>Configurar el conector</h3><p>Selecciona <strong>OpenAI</strong> de la lista de conectores. LM Studio emplea el SDK OpenAI, lo que lo hace compatible.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt762023c39781eb78/6a170d06a29299a59ed01087/5ac87042e086c7a2bd47a8039e646ec831f0dcc6-923x974.png" alt="" /><p>Diligencia el espacio con estos valores:</p><ul><li><p><strong>Nombre del conector: </strong>LM Studio - GPT-OSS 20B</p></li><li><p><strong>Selecciona un proveedor de OpenAI: </strong>Otros (Servicio compatible con OpenAI)</p></li><li><p><strong>URL: </strong><code>http://host.docker.internal:1234/v1/chat/completions</code></p></li><li><p><strong>Modelo por defecto: </strong>openai/gpt-oss-20b</p></li><li><p><strong>API Key:</strong> testkey-123 (cualquier texto funciona, porque LM Studio Server no requiere autenticación.)</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt980e595f80e2be2e/6a170d086f7f0468a19148cc/2084ac32fcf1fb810c8b54ecab1c85a1e3e8905b-672x1302.png" alt="" /><p>Para terminar la configuración, haz clic en <strong>Almacenar y probar</strong>.</p><p><strong>Importante:</strong> Activa la opción "<strong>Habilitar la llamada a funciones nativas</strong>"; esto es necesario para que el Constructor de Agentes funcione correctamente. Si no activas esto, te aparecerá un error de <strong><code>No tool calls found in the response</code></strong> .</p><h3>Prueba la conexión</h3><p>El elástico debería probar automáticamente la conexión. Si todo está configurado correctamente, verás un mensaje de éxito como este:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4d2e815dd558f881/6a170d090e2e49076541a177/f567d767f1969c4730c1daa92f651789dc3742ac-1042x812.png" alt="" /><p>Respuesta:</p>{
  "status": "ok",
  "data": {
    "id": "chatcmpl-flj9h0hy4wcx4bfson00an",
    "object": "chat.completion",
    "created": 1761189456,
    "model": "openai/gpt-oss-20b",
    "choices": [
      {
        "index": 0,
        "message": {
          "role": "assistant",
          "content": "Hello! 👋 How can I assist you today?",
          "reasoning": "Just greet.",
          "tool_calls": []
        },
        "logprobs": null,
        "finish_reason": "stop"
      }
    ],
    "usage": {
      "prompt_tokens": 69,
      "completion_tokens": 23,
      "total_tokens": 92
    },
    "stats": {},
    "system_fingerprint": "openai/gpt-oss-20b"
  },
  "actionId": "ee1c3aaf-bad0-4ada-8149-118f52dad757"
}<h2>Paso 4: Subir los datos de los empleados a Elasticsearch</h2><p>Ahora subiremos el <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/gpt-oss-with-elasticsearch/hr-employees-bulk.json">conjunto de datos de empleados de RRHH</a> para demostrar cómo el agente trabaja con datos sensibles. Generé un conjunto de datos ficticio con esta estructura.</p><h3>Estructura del conjunto de datos</h3>{
  "employee_id": "0f4dce68-2a09-4cb1-b2af-6bcb4821539b",
  "full_name": "Daffi Stiebler",
  "email": "lscutchings0@huffingtonpost.com",
  "date_of_birth": "1975-06-20T15:39:36Z",
  "hire_date": "2025-07-28T00:10:45Z",
  "job_title": "Physical Therapy Assistant",
  "department": "HR",
  "salary": "108455",
  "performance_rating": "Needs Improvement",
  "years_of_experience": 2,
  "skills": "Java",
  "education_level": "Master's Degree",
  "manager": "Carl MacGibbon",
  "emergency_contact": "Leigha Scutchings",
  "home_address": "5571 6th Park"
}<h3>Crea el índice con mapeos</h3><p>Primero, crea el índice con mapeos adecuados. Ten en cuenta que estamos usando <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/semantic-text">semantic_text</a> campos para algunos campos clave; Esto permite capacidades de búsqueda semántica para nuestro índice.</p>​​PUT hr-employees
{
  "mappings": {
    "properties": {
      "@timestamp": {
        "type": "date"
      },
      "employee_id": {
        "type": "keyword"
      },
      "full_name": {
        "type": "text",
        "copy_to": "employee_semantic"
      },
      "email": {
        "type": "keyword"
      },
      "date_of_birth": {
        "type": "date",
        "format": "iso8601"
      },
      "hire_date": {
        "type": "date",
        "format": "iso8601"
      },
      "job_title": {
        "type": "text",
        "copy_to": "employee_semantic"
      },
      "department": {
        "type": "text",
        "copy_to": "employee_semantic"
      },
      "salary": {
        "type": "double"
      },
      "performance_rating": {
        "type": "text",
        "copy_to": "employee_semantic"
      },
      "years_of_experience": {
        "type": "long"
      },
      "skills": {
        "type": "text",
        "copy_to": "employee_semantic"
      },
      "education_level": {
        "type": "text",
        "copy_to": "employee_semantic"
      },
      "manager": {
        "type": "text",
        "copy_to": "employee_semantic"
      },
      "emergency_contact": {
        "type": "keyword"
      },
      "home_address": {
        "type": "keyword"
      },
      "employee_semantic": {
        "type": "semantic_text"
      }
    }
  }
}<h3>Índice con API Bulk</h3><p>Copia y pega el <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/gpt-oss-with-elasticsearch/hr-employees-bulk.json">conjunto de datos</a> en tus Dev Tools en Kibana y ejecutalo:</p>POST hr-employees/_bulk
{"index": {}}
{"employee_id": "57728b91-e5d7-4fa8-954a-2384040d3886", "full_name": "Filide Gane", "email": "vhallahan1@booking.com", "job_title": "Business Systems Development Analyst", "department": "Marketing", "salary": "$52330.27", "performance_rating": "Meets Expectations", "years_of_experience": 12, "skills": "Java", "education_level": "Bachelor's Degree", "date_of_birth": "2000-02-07T16:49:32Z", "hire_date": "2023-11-07T13:03:16Z", "manager": "Freedman Kings", "emergency_contact": "Vilhelmina Hallahan", "home_address": "75 Dennis Junction"}
{"index": {}}
{"employee_id": "...", ...}<h3>Verifica los datos</h3><p>Realiza una consulta para verificar:</p>GET hr-employees/_search<h2>Paso 5: Construye y prueba tu agente de IA</h2><p>Con todo configurado, es hora de crear un agente de IA personalizado usando Elastic Agent Builder. Para más detalles, consulte la <a href="https://www.elastic.co/docs/solutions/search/agent-builder/get-started">documentación de Elastic</a>.</p><h3>Agregar el conector</h3><p>Antes de poder crear nuestro nuevo agente, tenemos que configurar nuestro constructor de agentes para usar nuestro conector personalizado llamado <code>LM Studio - GPT-OSS 20B</code> porque el predeterminado es el <a href="https://www.elastic.co/docs/reference/kibana/connectors-kibana/elastic-managed-llm">Elastic Managed LLM</a>. Para eso, necesitamos ir a <strong>Configuración</strong> &gt; <strong>Gestión de Proyectos</strong> &gt; <strong>Configuración</strong> de GenAI; ahora seleccionamos la que creamos y hacemos clic <strong>en</strong> Almacenar.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc42f079c5e756057/6a170d0acf4f2501d9b2d1c7/11e830c3e2fb4c298b020c928fa5422f3397ba08-1600x1152.png" alt="" /><h3>Constructor de Agentes de Acceso</h3><ol><li><p>Ir a <strong>Agentes</strong></p></li><li><p>Haz clic <strong>en Crear un nuevo agente</strong></p></li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb8e734817c5a7c6a/6a170d0ca929cf867cae0a34/c1e60541563650163f972ac9088dc1ed1de759a7-1600x1054.png" alt="" /><h3>Configurar el agente</h3><p>Para crear un nuevo agente, los campos requeridos son el <strong>ID del agente</strong>, <strong>el nombre de visualización</strong> y <strong>las instrucciones de visualización</strong>.</p><p>Pero hay más opciones de personalización, como las Instrucciones Personalizadas que guían cómo se comportará tu agente e interactuará con tus herramientas, similar a una indicación del sistema, pero para nuestro agente personalizado. Las etiquetas ayudan a organizar tus agentes, el color del avatar y el símbolo del avatar.</p><p>Los que elegí para nuestro agente basándome en el conjunto de datos son:

<strong>ID del agente:</strong> <code>hr_assistant</code></p><p><strong>Instrucciones personalizadas:</strong></p>You are an HR Analytics Assistant that helps answer questions about employee data.
When responding to queries:
- Provide clear, concise answers
- Include relevant employee details (name, department, salary, skills)
- Format monetary values with currency symbols
- Be professional and maintain data confidentiality<p>
Etiquetas: <code>Human Resources</code> y <code>GPT-OSS</code></p><p>Nombre de visualización: <code>HR Analytics Assistant</code></p><p>Descripción de la exposición:</p>A specialized AI assistant for Human Resources that helps analyze employee data, compensation, performance metrics, and talent management. Ask questions about employees, departments, salaries, or performance analytics.<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt23fb011e5b4f4d49/6a170d0e7d8d67f47a70e77f/f94bb2bf08497e5e756ca76b30a3a51f42927756-1424x1217.png" alt="" /><p>Con todos los datos ahí, podemos hacer clic en <strong>Almacenar</strong> a nuestro nuevo agente.</p><h3>Prueba al agente</h3><p>Ahora puedes hacer preguntas en lenguaje natural sobre los datos de tus empleados, y GPT-OSS 20B entenderá la intención y generará una respuesta adecuada.</p><h4>Pronto:</h4>Which employee is the one with the highest salary in the hr-employees index?<h4>Respuesta:</h4><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc0c52faacf63b583/6a170d0f0e2e497bfd41a17b/94ad19f80b96304028a59f60beca51dfc9aecc8a-899x631.png" alt="" /><p>El proceso de Agente fue:</p><p>1. Entiende tu pregunta usando el conector GPT-OSS</p><p>2. Generar la consulta Elasticsearch adecuada (usando las herramientas integradas o <a href="https://www.elastic.co/docs/reference/query-languages/esql">ES| personalizadoQL</a>)</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte32a8a7e6363c7f2/6a170d115091680077e1bb44/6f2961d0d1b97475f6dda300acee84da540938e6-844x466.png" alt="" /><p>3. Recuperar los registros de empleados coincidentes</p><p>4. Presentar resultados en lenguaje natural con un formato adecuado</p><p>A diferencia de la búsqueda léxica tradicional, el agente impulsado por GPT-OSS entiende la intención y el contexto, facilitando encontrar información sin conocer los nombres exactos de los campos o la sintaxis de la consulta. Para más detalles sobre el proceso de pensamiento del agente, consulta este <a href="https://www.elastic.co/search-labs/blog/ai-agent-builder-experiments-performance">artículo</a>.</p><h2>Conclusión</h2><p>En este artículo, creamos un agente de IA personalizado usando el Agent Builder de Elastic para conectarse al modelo OpenAI GPT-OSS que se ejecuta localmente. Al desplegar tanto Elastic como el LLM en tu máquina local, esta arquitectura te permite aprovechar las capacidades de IA generativa manteniendo el control total sobre tus datos, todo ello sin enviar información a servicios externos.</p><p>Usamos GPT-OSS 20B como experimento, pero <a href="https://www.elastic.co/docs/solutions/search/agent-builder/models#recommended-models">aquí</a> se hacen referencia a los modelos oficialmente recomendados para Elastic Agent Builder. Si necesitas capacidades de razonamiento más avanzadas, también está la <a href="https://huggingface.co/openai/gpt-oss-120b">variante de parámetros 120B</a> que rinde mejor para escenarios complejos, aunque requiere una máquina de especificaciones más altas para ejecutar localmente. Para más detalles, consulte la <a href="https://openai.com/open-models/">documentación oficial de OpenAI</a>.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/build-an-ai-agent-hr-elastic-agent-builder-gpt-oss</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/build-an-ai-agent-hr-elastic-agent-builder-gpt-oss</guid>
    <category><![CDATA[AI agéntica]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Tomás Murúa]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt664f490053e46e6b/6a170d13b0367d2d7e72bd84/05d2d0513fff67d975f9223d75108aa9f50646bc-1600x914.png" length="0" type="image/png"/>
    <pubDate>Wed, 26 Nov 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Los principales proyectos y aprendizajes de Elastic Agent Builder de Cal Hacks 12.0]]></title>
    <description><![CDATA[Explora los principales proyectos de Elastic Agent Builder de Cal Hacks 12.0 y sumérgete en nuestras conclusiones técnicas sobre Serverless, ES|QL y arquitecturas de agentes.]]></description>
    <content:encoded><![CDATA[<p>Hace unas semanas, tuvimos la asombrosa oportunidad de patrocinar <a href="https://cal-hacks-12-0.devpost.com/">Cal Hacks 12.0</a>, uno de los hackatones presenciales más grandes con más de 2000 participantes de todo el mundo. Ofrecimos una pista dedicada al mejor uso de Elastic Agent Builder en Serverless, y la respuesta fue fenomenal. En solo 36 horas, recibimos 29 envíos que usaban Agent Builder de formas creativas, desde la creación de herramientas de inteligencia contra incendios forestales hasta validadores StackOverflow.</p><p>Más allá de los impresionantes proyectos, la experiencia en Cal Hacks 12.0 también nos dio algo igualmente valioso: comentarios rápidos y sin filtros de desarrolladores que se encuentran con nuestra Stack por primera vez. Los hackathons son pruebas de presión únicas con plazos ajustados, cero familiaridad previa y obstáculos impredecibles (como los famosos cortes de WiFi). Exponen exactamente dónde brilla la experiencia del desarrollador y dónde aún necesita mejoras. Esto importa aún más ahora, ya que los desarrolladores interactúan con el Elastic Stack de nuevas maneras, cada vez más a través de flujos de trabajo impulsados por LLM. En esta entrada del blog, profundizaremos en lo que los participantes construyeron con Agent Builder y lo que aprendimos en el proceso.</p><h2>Los proyectos ganadores</h2><h3>Primer puesto: AgentOverflow</h3><p>Stack Overflow reconstruido para la era de los LLM y los agentes.</p><p>Lee más sobre <a href="https://devpost.com/software/agentoverflow">AgentOverflow aquí</a>.</p><p>AgentOverflow aborda un problema que la mayoría de los desarrolladores de IA enfrentan: los LLMs alucinan, los historiales de chat desaparecen y los desarrolladores pierden tiempo resolviendo los mismos problemas.</p><p>AgentOverflow captura, valida y reactiva pares reales problema-solución, para que los desarrolladores puedan romper la espiral de alucinaciones y enviar más rápido.</p><h4>Cómo funciona:</h4><p><strong>1. Compartir JSON - el "Esquema de Solución".</strong></p><p>Un clic desde un recurso compartido de Claude extraerá, extrae y ensamblará un JSON de Solución de Compartir, que es un formato estructurado que contiene:</p><ul><li><p>Problema</p></li><li><p>Contexto</p></li><li><p>Código</p></li><li><p>Etiquetas</p></li><li><p>Pasos verificados de la solución.</p></li></ul><p>Un validador (LAVA) comprueba y aplica la estructura, el usuario agrega una línea de contexto extra y luego se almacena e indexa dentro de Elasticsearch.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte7bc35b6d54921e8/6a17f0176df73162760a0fe6/45a3e96f4474050a855419628c2a7338bb12c706-1600x877.png" alt="Al hacer clic en &quot;Compartir solución&quot; se extraerá la sesión actual junto con los metadatos relevantes" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9967f52007fff99e/6a17f019ec0f8987c45a6701/2d65cb154d8ee32fc96ff17dfa5b0bf2636e3777-1600x1002.png" alt="Los usuarios proporcionan contexto adicional a través del frontend sitio web, luego el JSON se indexa en Elasticsearch" /><p><strong>2. Encontrar solución</strong></p><p>Cuando te quedes atascado, haz clic en <code>Find Solution</code> y AgentOverflow extraerá tu conversación actual, la usará para crear una consulta y ejecutará una búsqueda híbrida en Elasticsearch para que aparezca:</p><ul><li><p>Arreglos clasificados y validados por la comunidad</p></li><li><p>Los exactos prompts que originalmente resolvieron el problema</p></li></ul><p>Esto permite a los desarrolladores copiar, pegar y desbloquear rápidamente su sesión actual.</p><p><strong>3. MCP - inyección de contexto para LLMs</strong></p><p>Al conectarse a las soluciones estructuradas almacenadas dentro de Elasticsearch a través de MCP (Model Context Protocol), los LLM reciben un contexto de alta señal (código, registros, configuraciones, correcciones previas) en tiempo de ejecución sin ruido adicional.</p><p>AgentOverflow emplea Agent Builder con Elasticsearch como una capa de memoria estructurada que inyecta el contexto relevante en los LLMs. Esto los transforma de chatbots pasivos en solucionadores de problemas conscientes del contexto.</p><h3>Segundo puesto: MarketMind</h3><p>Una visión interpretable en tiempo real de la energía del mercado, impulsada por seis Agentes Elásticos.</p><p>Lee más sobre <a href="https://devpost.com/software/marketmind-b6cy2q">MarketMind aquí</a>.</p><p>MarketMind consiguió su spot al ofrecer a los traders novatos una plataforma que convierte datos de mercado fragmentados en señales claras y en tiempo real. En lugar de compaginar la acción del precio, los fundamentos, el sentimiento y la volatilidad entre diferentes herramientas, MarketMind consolida toda esta información en una sola plataforma, ayudando a los traders a obtener información útil. Este proyecto también empleó algunos complejos ES|QL consulta al construir sus agentes.</p><h4>Cómo funciona:</h4><p><strong>1. Recopilar datos de mercado en tiempo real</strong></p><p>MarketMind extrae métricas de precio-acción, fundamentos, sentimiento, volatilidad y riesgo de Yahoo Finance. Estos datos se ingieren y organizan en múltiples índices de Elasticsearch.</p><p><strong>2. Seis agentes especializados analizan el mercado</strong></p><p>Cada agente, construido con Agent Builder, se centra en una capa diferente del mercado. Leen desde un índice de Elasticsearch, calculan sus propias métricas específicas de dominio y generan una salida JSON estandarizada con puntajes y razonamientos.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd4ba582f9872b65b/6a17f01b7f6f15c2d8c09c1c/7d9716cca06a047a2b3584378b5c7e592a785ba1-1284x878.png" alt="6 agentes especializados de IA GOOGL que analizan el mercado" /><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd86ed3bfe4b8bd2b/6a17f01c5ea30f868164b6ba/5aac6a833347c0d2e596c02049ec4b4d3aae5cd7-794x764.png" alt="Anomalías de volumen y capacidades de análisis de detección de catástrofes de los agentes especializados GOOGL" /><p><strong>3. Agregar señales en un modelo unificado de "energía de mercado"</strong></p><p>Las salidas combinadas aparecen como pulsos brillantes alrededor de cada acción, ilustrando si el impulso está creciendo, el riesgo está aumentando o el sentimiento está cambiando.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5af7c7c838308275/6a17f01e42022917b629f6ca/46b3da8e3d528c5dd4e2829416c5446098acb3aa-744x718.png" alt="Modelo unificado de &quot;energía de mercado&quot; de agentes especializados GOOGL" /><p><strong>4. Visualizar percepciones</strong></p><p>El frontend se construyó con React y <a href="https://github.com/vercel/next.js">Next.js</a>, usando TypeScript, gráficos basados en física SVG y <a href="https://github.com/chartjs">Chart.js</a> para gráficos en tiempo real con candelabros. Esto convierte el análisis en bruto en feedback accionable en tiempo real.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt775e1880aa7afacc/6a17f01f1d1b83ce1f93e528/3f000c043117b77ed4127202be5a49c12e3682ba-1600x930.png" alt="Cómo visualizar los insights del análisis de agentes especializados de GOOGL" /><h2>Otros proyectos interesantes:</h2><p>Aquí tienes otros candidatos fuertes que usaron Elastic en diferentes partes de su stack:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltffe292009e446a70/6a17f0216df731068c0a0fea/76c49a853426844f475cd6b2a74999e60af20e8c-926x1080.png" alt="" /><p>Encuentra aquí la lista completa de proyectos que se presentaron a <a href="https://cal-hacks-12-0.devpost.com/submissions/search?utf8=%E2%9C%93&amp;prize_filter%5Bprizes%5D%5B%5D=91882">nuestra especialidad.</a></p><h2>Lo que aprendimos de los desarrolladores</h2><ul><li><p><strong>Agent Builder es fácil de usar:</strong></p></li></ul><p>La mayoría de los equipos nunca usaron Elastic antes y aún así podían crear agentes rápidamente con poco apoyo. Organizamos un taller para quienes necesitaban más orientación, pero la mayoría pudo absorber sus datos y crear un agente para realizar acciones sobre esos datos.</p><ul><li><p><strong>Los LLMs son excelentes en </strong><strong><code>kNN</code></strong><strong> consultas, pero aún necesitan orientación para generar ES|QL:</strong></p></li></ul><p>Pidiendo a ChatGPT-5 que genere ES|Las consultas QL devolvían información incorrecta, a menudo mezclando ES|QL y SQL. Alimentar al LLM la documentación en un archivo de markdown parecía una solución viable.</p><ul><li><p><strong>ES| solo para instantáneasFunciones QL filtradas en documentos:</strong></p></li></ul><p>Las próximas funciones de agregación <code>FIRST</code> y <code>LAST</code> pasaron sin querer a nuestro ES|Documentos de QL. Como enviamos esa documentación a ChatGPT, el modelo empleó fielmente estas funciones, aunque aún no estén disponibles en Serverless. Gracias a los comentarios del grupo, ingeniería abrió y fusionó rápidamente una corrección para eliminar las funciones de la documentación publicada (<a href="https://github.com/elastic/elasticsearch/pull/137341">PR #137341</a>).</p><ul><li><p><strong>Falta orientación específica para Serverless:</strong></p></li></ul><p>Un equipo intentó activar <code>LOOKUP JOIN</code> en un índice que no se creó en modo búsqueda. El mensaje de error les hizo perseguir comandos que no existen en Serverless. Transmitimos esto al equipo de producto, que inmediatamente abrió una corrección para un mensaje específico y accionable para Serverless. A largo plazo, la visión es ocultar por completo la complejidad del reindexado (<a href="https://github.com/elastic/elasticsearch-serverless/issues/4838">Número #4838</a>).</p><ul><li><p><strong>Valor de los eventos presenciales:</strong></p></li></ul><p>Los hackathons online son geniales, pero nada iguala el bucle de retroalimentación rápida que tienes cuando depuras codo a codo con los constructores. Vimos cómo los equipos integraban Agent Builder en diferentes casos de uso, y detectamos la experiencia de los desarrolladores con ES|QL podría mejorar y solucionar los problemas mucho más rápido que intentar hacerlo por canales asincrónicos.</p><h2>Conclusión</h2><p>Cal Hacks 12.0 nos ofreció más que un fin de semana de demos chulas; también nos dio una visión de cómo los nuevos desarrolladores interactúan con el Elastic Stack. En tan solo 36 horas, vimos a los equipos adquirir Agent Builder, incorporar datos en Elasticsearch, diseñar sistemas multiagente y probar nuestras funcionalidades de diversas maneras. El evento también nos recordó por qué son importantes los eventos presenciales. Los rápidos bucles de retroalimentación, las conversaciones reales y la depuración práctica nos ayudaron a entender las necesidades actuales de los desarrolladores. Estamos entusiasmados por devolver lo aprendido al equipo de ingeniería. Nos vemos en el próximo hackathon.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/agent-builder-projects-learnings-cal-hacks-12-0</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/agent-builder-projects-learnings-cal-hacks-12-0</guid>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[AI agéntica]]></category>
    <dc:creator><![CDATA[JD Armada]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0f079179be9832d4/6a17f023631730a69c585b6d/8ba034a6f19b50521f541b8131756a8acdb52975-1280x960.jpg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 25 Nov 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Creación de una redacción de agentes LLM con protocolo A2A y MCP en Elasticsearch: Parte II]]></title>
    <description><![CDATA[Descubre cómo construir una sala de redacción híbrida especializada para agentes LLM empleando el protocolo A2A para la colaboración de agentes y MCP para el acceso a herramientas en Elasticsearch.]]></description>
    <content:encoded><![CDATA[<h2>A2A y MCP: el código en acción</h2><p>Este es el artículo complementario al artículo "¡Creando una redacción para agentes LLM con protocolo A2A y MCP en Elasticsearch!", que explicaba los beneficios de implementar tanto las arquitecturas A2A como MCP dentro del mismo agente para aprovechar realmente los beneficios únicos de ambos frameworks. Hay un <a href="https://github.com/justincastilla/elastic-newsroom">repositorio</a> disponible si deseas ejecutar la demo por tu cuenta.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt232e466d2153c764/6a17f15f631730042d585b8d/7196f004089127f83547b2e5dc3f663205cfcdce-1162x1600.png" alt="Flujo de trabajo de agentes de protocolos A2A y MCP" /><p>Vamos a repasar cómo colaboran nuestros agentes de redacción empleando tanto A2A como MCP para producir un artículo de noticias. El repositorio adjunto para ver a los agentes en acción se puede <a href="https://github.com/justincastilla/elastic-newsroom">encontrar aquí</a>.</p><h3>Paso 1: Asignación de la historia</h3><p>El <strong>Jefe de Noticias</strong> (actuando como cliente) asigna una noticia:</p>{
  "message_type": "task_request",
  "sender": "news_chief",
  "receiver": "reporter_agent",
  "payload": {
    "task_id": "story_renewable_energy_2024",
    "assignment": {
      "topic": "Renewable Energy Adoption in Europe",
      "angle": "Policy changes driving solar and wind expansion",
      "target_length": 1200,
      "deadline": "2025-09-30T18:00:00Z"
    }
  }
}<h3>Paso 2: El reportero aplicar investigación</h3><p>El <strong>Agente Reportero</strong> reconoce que necesita información de fondo y delega al <strong>Agente Investigador</strong> mediante A2A:</p>{
  "message_type": "task_request",
  "sender": "reporter_agent",
  "receiver": "researcher_agent",
  "payload": {
    "task_id": "research_eu_renewable_2024",
    "parent_task_id": "story_renewable_energy_2024",
    "capability": "fact_gathering",
    "parameters": {
      "queries": [
        "EU renewable energy capacity 2024",
        "Solar installations growth Europe",
        "Wind energy policy changes 2024"
      ],
      "depth": "comprehensive"
    }
  }
}<h3>Paso 3: El reportero aplicar contexto histórico al Agente de Archivo</h3><p>El <strong>Reporter Agent</strong> reconoce que el contexto histórico fortalecería la historia. Delega al <strong>Agente de Archivo</strong> (impulsado <a href="https://www.elastic.co/docs/solutions/search/elastic-agent-builder">por el Agente A2A de Elastic</a>) a través de A2A para buscar en el archivo de artículos impulsado por Elasticsearch de la redacción:</p>{
  "message_type": "task_request",
  "sender": "reporter_agent",
  "receiver": "archive_agent",
  "payload": {
    "task_id": "archive_search_renewable_2024",
    "parent_task_id": "story_renewable_energy_2024",
    "capability": "search_archive",
    "parameters": {
      "query": "European renewable energy policy changes and adoption trends over past 5 years",
      "focus_areas": ["solar", "wind", "policy", "Germany", "France"],
      "time_range": "2019-2024",
      "result_count": 10
    }
  }
}<h3>Paso 4: El Agente de Archivo emplea el Agente Elastic A2A con MCP</h3><p><strong>El Archive Agent</strong> emplea el A2A Agent de Elastic, que a su vez emplea MCP para acceder a las herramientas de Elasticsearch. Esto demuestra la arquitectura híbrida donde A2A permite la colaboración entre agentes mientras que MCP proporciona acceso a herramientas:</p># Archive Agent using Elastic A2A Agent
async def search_historical_articles(self, query_params):
    # The Archive Agent sends a request to Elastic's A2A Agent
    elastic_response = await self.a2a_client.send_request(
        agent="elastic_agent",
        capability="search_and_analyze",
        parameters={
            "natural_language_query": query_params["query"],
            "index_pattern": "newsroom-articles-*",
            "filters": {
                "topics": query_params["focus_areas"],
                "date_range": query_params["time_range"]
            },
            "analysis_type": "trend_analysis"
        }
    )
    
    # Elastic's A2A Agent internally uses MCP tools:
    # - platform.core.search (to find relevant articles)
    # - platform.core.generate_esql (to analyze trends)
    # - platform.core.index_explorer (to identify relevant indices)
    
    return elastic_response<p>El <strong>Agente de Archivo</strong> recibe datos históricos completos del Agente A2A de Elastic y los devuelve al Reportero:</p>{
  "message_type": "task_response",
  "sender": "archive_agent",
  "receiver": "reporter_agent",
  "payload": {
    "task_id": "archive_search_renewable_2024",
    "status": "completed",
    "archive_data": {
      "historical_articles": [
        {
          "title": "Germany's Energiewende: Five Years of Solar Growth",
          "published": "2022-06-15",
          "key_points": [
            "Germany added 7 GW annually 2020-2022",
            "Policy subsidies drove 60% of growth"
          ],
          "relevance_score": 0.94
        },
        {
          "title": "France Balances Nuclear and Renewables",
          "published": "2023-03-20",
          "key_points": [
            "France increased renewable target to 40% by 2030",
            "Solar capacity doubled 2021-2023"
          ],
          "relevance_score": 0.89
        }
      ],
      "trend_analysis": {
        "coverage_frequency": "EU renewable stories increased 150% since 2019",
        "emerging_themes": ["policy incentives", "grid modernization", "battery storage"],
        "coverage_gaps": ["Small member states", "offshore wind permitting"]
      },
      "total_articles_found": 47,
      "search_confidence": 0.91
    }
  }
}<p>Este paso demuestra cómo el Agente A2A de Elastic se integra en el flujo de trabajo de la redacción. El Agente de Archivo (un agente específico de redacción) coordina con el Agente A2A de Elastic (un especialista externo) para aprovechar las poderosas capacidades de búsqueda y análisis de Elasticsearch. El agente de Elastic emplea MCP internamente para acceder a las herramientas de Elasticsearch, mostrando la separación limpia entre la coordinación del agente (A2A) y el acceso a la herramienta (MCP).</p><h3>Paso 5: El investigador emplea servidores MCP</h3><p>El <strong>Agente Investigador</strong> accede a múltiples servidores MCP para recopilar información:</p># Researcher Agent using MCP to access tools
async def gather_facts(self, queries):
    results = []
    
    # Use News API MCP Server
    news_data = await self.mcp_client.invoke_tool(
        server="news_api_mcp",
        tool="search_articles",
        parameters={
            "query": queries[0],
            "date_range": "2024-01-01 to 2024-09-30",
            "sources": ["reuters", "bloomberg", "euractiv"]
        }
    )
    
    # Use Statistics MCP Server
    stats_data = await self.mcp_client.invoke_tool(
        server="eurostat_mcp",
        tool="get_energy_statistics",
        parameters={
            "metrics": ["solar_capacity_mw", "wind_capacity_mw"],
            "countries": ["EU27"],
            "year": 2024
        }
    )
    
    # Use Fact-Checking MCP Server
    verified_facts = await self.mcp_client.invoke_tool(
        server="factcheck_mcp",
        tool="verify_claims",
        parameters={
            "claims": [
                "EU solar capacity increased 40% in 2024",
                "Germany leads in wind installations"
            ]
        }
    )
    
    return self._synthesize_research(news_data, stats_data, verified_facts)<h3>Paso 6: El investigador devuelve los datos al Reportero</h3><p>El <strong>Agente Investigador</strong> envía una investigación exhaustiva de vuelta a través de A2A:</p>{
  "message_type": "task_response",
  "sender": "researcher_agent",
  "receiver": "reporter_agent",
  "payload": {
    "task_id": "research_eu_renewable_2024",
    "status": "completed",
    "research_data": {
      "key_facts": [
        {
          "claim": "EU solar capacity reached 260 GW in 2024",
          "source": "Eurostat Q3 2024 Report",
          "confidence": 0.95,
          "verified": true
        },
        {
          "claim": "Germany installed 12 GW of wind capacity in 2024",
          "source": "German Federal Network Agency",
          "confidence": 0.92,
          "verified": true
        }
      ],
      "statistics": {
        "solar_growth_rate": "35%",
        "wind_growth_rate": "28%"
      },
      "sources_count": 15
    }
  }
}<h3>Paso 7: Reportero escribe artículo</h3><p>El <strong>Reporter Agent</strong> emplea los datos de investigación y sus propias capacidades de LLM para redactar el artículo. Durante la redacción, el Reportero emplea los servidores MCP para el estilo y las plantillas:</p># Reporter Agent writing with MCP assistance
async def write_article(self, research_data, assignment):
    # Get style guidelines via MCP
    style_guide = await self.mcp_client.get_resource(
        server="newsroom_mcp",
        resource="style://editorial/ap_style_guide"
    )
    
    # Get article template via MCP
    template = await self.mcp_client.get_resource(
        server="newsroom_mcp",
        resource="template://articles/news_story"
    )
    
    # Generate article using LLM + research + style
    draft = await self.llm.generate(
        prompt=f"""
        Write a news article following these guidelines:
        {style_guide}
        
        Using this template:
        {template}
        
        Based on this research:
        {research_data}
        
        Assignment: {assignment}
        """
    )
    
    # Self-evaluate confidence in claims
    confidence_check = await self._evaluate_confidence(draft)
    
    return draft, confidence_check<h3>Paso 8: la baja confianza desencadena la re-investigación</h3><p>El <strong>Agente Reportero</strong> evalúa su borrador y encuentra que una afirmación tiene baja confianza. Envía otra solicitud al <strong>Agente Investigador</strong>:</p>{
  "message_type": "collaboration_request",
  "sender": "reporter_agent",
  "receiver": "researcher_agent",
  "payload": {
    "request_type": "fact_verification",
    "claims": [
      {
        "text": "France's nuclear phase-down contributed to 15% increase in renewable capacity",
        "context": "Discussing policy drivers for renewable growth",
        "current_confidence": 0.45,
        "required_confidence": 0.80
      }
    ],
    "urgency": "high"
  }
}<p>El <strong>investigador</strong> verifica la afirmación empleando servidores MCP de verificación de hechos y devuelve información actualizada:</p>{
  "message_type": "collaboration_response",
  "sender": "researcher_agent",
  "receiver": "reporter_agent",
  "payload": {
    "verified_claims": [
      {
        "original_claim": "France's nuclear phase-down contributed to 15% increase...",
        "verified_claim": "France's renewable capacity increased 18% in 2024, partially offsetting reduced nuclear output",
        "confidence": 0.88,
        "corrections": "Percentage was 18%, not 15%; nuclear phase-down is gradual, not primary driver",
        "sources": ["RTE France", "French Energy Ministry Report 2024"]
      }
    ]
  }
}<h3>Paso 9: El reportero revisa y envía al editor</h3><p>El <strong>Reportero</strong> incorpora los hechos verificados y envía el borrador completo al <strong>Agente Editor</strong> a través de A2A:</p>{
  "message_type": "task_request",
  "sender": "reporter_agent",
  "receiver": "editor_agent",
  "payload": {
    "task_id": "edit_renewable_story",
    "parent_task_id": "story_renewable_energy_2024",
    "content": {
      "headline": "Europe's Renewable Revolution: Solar and Wind Surge 30% in 2024",
      "body": "[Full article text...]",
      "word_count": 1185,
      "sources": [/* array of sources */]
    },
    "editing_requirements": {
      "check_style": true,
      "check_facts": true,
      "check_seo": true
    }
  }
}<h3>Paso 10: Revisiones del editor usando herramientas MCP</h3><p>El <strong>Editor Agent</strong> emplea múltiples servidores MCP para revisar el artículo:</p># Editor Agent using MCP for quality checks
async def review_article(self, content):
    # Grammar and style check
    grammar_issues = await self.mcp_client.invoke_tool(
        server="grammarly_mcp",
        tool="check_document",
        parameters={"text": content["body"]}
    )
    
    # SEO optimization check
    seo_analysis = await self.mcp_client.invoke_tool(
        server="seo_mcp",
        tool="analyze_content",
        parameters={
            "headline": content["headline"],
            "body": content["body"],
            "target_keywords": ["renewable energy", "Europe", "solar", "wind"]
        }
    )
    
    # Plagiarism check
    originality = await self.mcp_client.invoke_tool(
        server="plagiarism_mcp",
        tool="check_originality",
        parameters={"text": content["body"]}
    )
    
    # Generate editorial feedback
    feedback = await self._generate_feedback(
        grammar_issues, 
        seo_analysis, 
        originality
    )
    
    return feedback<p>El <strong>editor</strong> aprueba el artículo y lo envía hacia adelante:</p>{
  "message_type": "task_response",
  "sender": "editor_agent",
  "receiver": "reporter_agent",
  "payload": {
    "status": "approved",
    "quality_score": 9.2,
    "minor_edits": [
      "Changed 'surge' to 'increased' in paragraph 3 for AP style consistency",
      "Added Oxford comma in list of countries"
    ],
    "approved_content": "[Final edited article]"
  }
}<h3>Paso 11: El editor publica vía CI/CD</h3><p>Finalmente, el <strong>Agente de Impresora</strong> publica el artículo aprobado empleando los servidores MCP para la tubería CMS y CI/CD:</p># Publisher Agent publishing via MCP
async def publish_article(self, content, metadata):
    # Upload to CMS via MCP
    cms_result = await self.mcp_client.invoke_tool(
        server="wordpress_mcp",
        tool="create_post",
        parameters={
            "title": content["headline"],
            "body": content["body"],
            "status": "draft",
            "categories": metadata["categories"],
            "tags": metadata["tags"],
            "featured_image_url": metadata["image_url"]
        }
    )
    
    post_id = cms_result["post_id"]
    
    # Trigger CI/CD deployment via MCP
    deploy_result = await self.mcp_client.invoke_tool(
        server="cicd_mcp",
        tool="trigger_deployment",
        parameters={
            "pipeline": "publish_article",
            "environment": "production",
            "post_id": post_id,
            "schedule": "immediate"
        }
    )
    
    # Track analytics
    await self.mcp_client.invoke_tool(
        server="analytics_mcp",
        tool="register_publication",
        parameters={
            "post_id": post_id,
            "publish_time": datetime.now().isoformat(),
            "story_id": metadata["story_id"]
        }
    )
    
    return {
        "status": "published",
        "post_id": post_id,
        "url": f"https://newsroom.example.com/articles/{post_id}",
        "deployment_id": deploy_result["deployment_id"]
    }<p>El <strong>editor</strong> confirma la publicación a través de A2A:</p>{
  "message_type": "task_complete",
  "sender": "printer_agent",
  "receiver": "news_chief",
  "payload": {
    "task_id": "story_renewable_energy_2024",
    "status": "published",
    "publication": {
      "url": "https://newsroom.example.com/articles/renewable-europe-2024",
      "published_at": "2025-09-30T17:45:00Z",
      "post_id": "12345"
    },
    "workflow_metrics": {
      "total_time_minutes": 45,
      "agents_involved": ["reporter", "researcher", "archive", "editor", "printer"],
      "iterations": 2,
      "mcp_calls": 12
    }
  }
}<p>Aquí está la secuencia completa del flujo de trabajo A2A en el repositorio adjunto usando los mismos Agentes descritos anteriormente.</p><p>#</p><p>De</p><p>Para</p><p>Acción</p><p>Protocolo</p><p>Descripción</p><p>1</p><p>Usuario</p><p>Jefe de Noticias</p><p>Asignar historia</p><p>HTTP POST</p><p>El usuario presenta la historia, tema y ángulo</p><p>2</p><p>Jefe de Noticias</p><p>Interno</p><p>Crear historia</p><p>-</p><p>Crea un registro de historia con un ID único</p><p>3</p><p>Jefe de Noticias</p><p>Reportero</p><p>Asignación de Delegados</p><p>A2A</p><p>Envía asignación de historia mediante el protocolo A2A</p><p>4</p><p>Reportero</p><p>Interno</p><p>Aceptar Asignación</p><p>-</p><p>Asignación interna de almacenes</p><p>5</p><p>Reportero</p><p>Servidor MCP</p><p>Generar esquema</p><p>MCP/HTTP</p><p>Crea un esquema de artículos y preguntas de investigación</p><p>6A</p><p>Reportero</p><p>Investigador</p><p>Solicitud de investigación</p><p>A2A</p><p>Envía preguntas (paralelo con 6b)</p><p>6b</p><p>Reportero</p><p>Archivero</p><p>Archivo de búsqueda</p><p>A2A JSONRPC</p><p>Busca en artículos históricos (paralelo con 6a)</p><p>7</p><p>Investigador</p><p>Servidor MCP</p><p>Preguntas de investigación</p><p>MCP/HTTP</p><p>Emplea Anthropic vía MCP para responder preguntas</p><p>8</p><p>Investigador</p><p>Reportero</p><p>Investigación de Retorno</p><p>A2A</p><p>Respuestas de investigación de retornos</p><p>9</p><p>Archivero</p><p>Elasticsearch</p><p>Índice de búsqueda</p><p>ES REST API</p><p>Consultas news_archive índice</p><p>10</p><p>Archivero</p><p>Reportero</p><p>Archivo de retorno</p><p>A2A JSONRPC</p><p>Devuelve resultados históricos de búsqueda</p><p>11</p><p>Reportero</p><p>Servidor MCP</p><p>Generar artículo</p><p>MCP/HTTP</p><p>Crea un artículo con contexto de investigación/archivo</p><p>12</p><p>Reportero</p><p>Interno</p><p>Tiro de la tienda</p><p>-</p><p>Salvamentos en el draft interno</p><p>13</p><p>Reportero</p><p>Jefe de Noticias</p><p>Enviar Borrador</p><p>A2A</p><p>Entrega borrador completado</p><p>14</p><p>Jefe de Noticias</p><p>Interno</p><p>Actualización de la historia</p><p>-</p><p>Borrador de la tienda, actualiza el estado a "draft_submitted"</p><p>15</p><p>Jefe de Noticias</p><p>Editor</p><p>Borrador de revisión</p><p>A2A</p><p>Rutas automáticas al Editor para su revisión</p><p>16</p><p>Editor</p><p>Servidor MCP</p><p>Artículo de revisión</p><p>MCP/HTTP</p><p>Analiza contenido usando Anthropic a través de MCP</p><p>17</p><p>Editor</p><p>Jefe de Noticias</p><p>Reseña de Retorno</p><p>A2A</p><p>Envía comentarios y sugerencias editoriales</p><p>18</p><p>Jefe de Noticias</p><p>Interno</p><p>Reseña de la tienda</p><p>-</p><p>Comentarios sobre el editor de tiendas</p><p>19</p><p>Jefe de Noticias</p><p>Reportero</p><p>Aplicar ediciones</p><p>A2A</p><p>Comentarios de la revisión de rutas al Reportero</p><p>20</p><p>Reportero</p><p>Servidor MCP</p><p>Aplicar ediciones</p><p>MCP/HTTP</p><p>Revisa el artículo basar en los comentarios</p><p>21</p><p>Reportero</p><p>Interno</p><p>Actualización del borrador</p><p>-</p><p>Borrador de actualizaciones con revisiones</p><p>22</p><p>Reportero</p><p>Jefe de Noticias</p><p>Devolución revisada</p><p>A2A</p><p>Artículo revisado de declaraciones</p><p>23</p><p>Jefe de Noticias</p><p>Interno</p><p>Actualización de la historia</p><p>-</p><p>Borrador revisado de las tiendas, estado a "revisado"</p><p>24</p><p>Jefe de Noticias</p><p>Editor</p><p>Publicar artículo</p><p>A2A</p><p>Rutas automáticas hacia el editor</p><p>25</p><p>Editor</p><p>Servidor MCP</p><p>Generar etiquetas</p><p>MCP/HTTP</p><p>Crea etiquetas y categorías</p><p>26</p><p>Editor</p><p>Elasticsearch</p><p>Artículo índice</p><p>ES REST API</p><p>Índice de artículos a índice news_archive</p><p>27</p><p>Editor</p><p>Sistema de archivos</p><p>Almacenar Markdown</p><p>E/S de archivos</p><p>Almacena el artículo como .md archivo en /articles</p><p>28</p><p>Editor</p><p>Jefe de Noticias</p><p>Confirmar publicación</p><p>A2A</p><p>Resultados de éxito</p><p>29</p><p>Jefe de Noticias</p><p>Interno</p><p>Actualización de la historia</p><p>-</p><p>Actualiza el estado de la noticia a "publicado"</p><h2>Conclusión</h2><p>Tanto A2A como MCP tienen papeles importantes en el paradigma moderno de infraestructura de LLM aumentado. A2A ofrece flexibilidad para sistemas complejos multiagente, pero potencialmente menos portabilidad y mayor complejidad operativa. MCP ofrece un enfoque estandarizado para la integración de herramientas que es más sencillo de implementar y mantener, aunque no está diseñado para manejar orquestación multiagente.</p><p>La elección no es binaria. Como demuestra nuestro ejemplo de redacción, los sistemas respaldados por LLM más sofisticados y efectivos suelen combinar ambos enfoques: los agentes coordinan y se especializan mediante protocolos A2A mientras acceden a sus herramientas y recursos a través de servidores MCP. Esta arquitectura híbrida ofrece los beneficios organizacionales de los sistemas multiagente junto con los beneficios de estandarización y ecosistema del MCP. Esto sugiere que puede que no sea necesario elegir en absoluto: simplemente usar ambos como un enfoque estándar</p><p>Depende de ti, como desarrollador o arquitecto, probar y determinar la mejor combinación de ambas soluciones para crear el resultado adecuado para tu caso de uso específico. Comprender las fortalezas, limitaciones y aplicaciones adecuadas de cada enfoque te permitirá construir sistemas de IA más eficaces, mantenibles y escalables.</p><p>Ya sea que estés construyendo una redacción digital, una plataforma de atención al cliente, un asistente de investigación o cualquier otra aplicación impulsada por LLM, considerar cuidadosamente tus necesidades de coordinación (A2A) y requisitos de acceso a herramientas (MCP) te pondrá en el camino hacia el éxito.</p><h2>Recursos adicionales</h2><ul><li><p><strong>Constructor de agentes Elasticsearch: </strong><a href="https://www.elastic.co/docs/solutions/search/elastic-agent-builder">https://www.elastic.co/docs/solutions/search/elastic-agent-builder</a></p></li><li><p><strong>Especificación A2A</strong>: <a href="https://a2a-protocol.org/latest/specification/">https://a2a-protocol.org/latest/specification/</a></p></li><li><p><strong>Integración de A2A y MCP</strong>: <a href="https://a2a-protocol.org/latest/topics/a2a-and-mcp/">https://a2a-protocol.org/latest/topics/a2a-and-mcp/</a></p></li><li><p><strong>Protocolo de Contexto del Modelo</strong>: <a href="https://modelcontextprotocol.io/">https://modelcontextprotocol.io</a></p></li></ul>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/a2a-protocol-mcp-llm-agent-workflow-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/a2a-protocol-mcp-llm-agent-workflow-elasticsearch</guid>
    <category><![CDATA[AI agéntica]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Justin Castilla]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1b1f22cdc2130333/6a17f161ec0f8917fa5a6712/f87330e5d4ca961593b3cfb861ca850a4cc34186-1519x1173.png" length="0" type="image/png"/>
    <pubDate>Mon, 24 Nov 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Ya sabes, para contexto - Parte III: El poder de la búsqueda híbrida en ingeniería de contexto]]></title>
    <description><![CDATA[Descubre cómo usar la ingeniería de contexto y la búsqueda híbrida para mejorar la precisión de la salida de la IA mediante agregaciones, RBAC y señales no relacionadas con contenido.]]></description>
    <content:encoded><![CDATA[<p>Hablamos tanto de búsqueda híbrida (<a href="https://www.elastic.co/search-labs/blog/context-engineering-hybrid-search-evolution-agentic-ai">Parte I</a>) como de ingeniería del contexto (<a href="https://www.elastic.co/search-labs/blog/context-engineering-llm-evolution-agentic-ai">Parte II</a>); Ahora, vamos a profundizar en cómo trabajan juntos para lograr el mayor efecto en proporcionar contexto dirigido a las operaciones de IA RAG y agente.</p><h2>La búsqueda no está muerta, solo se movió</h2><p>Así que tuvimos este cambio de buscar principalmente contexto a través de un cuadro de texto y usar la información (el contexto) que devuelven para construir las respuestas nosotros mismos, a ahora usar lenguaje natural para decirle a un agente lo que queremos y dejar que él investigue y compile automáticamente la respuesta por nosotros. Muchos en el mundo tecnológico señalan este cambio y proclaman que "la búsqueda está muerta" (bueno, el mundo del SEO y las palabras publicitarias <a href="https://www.pewresearch.org/short-reads/2025/07/22/google-users-are-less-likely-to-click-on-links-when-an-ai-summary-appears-in-the-results/">definitivamente está cambiando</a>: ¿ <a href="https://www.wired.com/story/goodbye-seo-hello-geo-brandlight-openai/">alguien quiere GEO</a> ?), pero la búsqueda sigue siendo absolutamente crítica para las operaciones agenticas — solo que ahora se realiza en gran medida fuera de la vista a través de las herramientas.</p><p>Anteriormente, los humanos eran los principales árbitros de relevancia subjetiva: cada usuario tiene sus propios motivos para realizar la búsqueda, y su experiencia personal influye en la precisión relativa de los resultados. Si queremos confiar en que los agentes pueden llegar a la misma conclusión (o mejor) que nosotros, debemos cerciorarnos de que la información contextual a la que tienen acceso esté lo más cerca posible de nuestra intención subjetiva. ¡Tenemos que diseñar el contexto que proporcionamos a los LLMs para ese objetivo!</p><h2>Generación de contexto con recuperación de búsqueda híbrida</h2><p>Solo un recordatorio de la Parte I de que la búsqueda híbrida de Elastic combina las fortalezas de la búsqueda tradicional basada en palabras clave (flexibilidad sintaxis, precisión de palabras clave y puntaje de relevancia) con la comprensión semántica de la búsqueda por similitud vectorial, y ofrece múltiples técnicas de reclasificación. Esta sinergia (¡nunca se encontró un uso más verdadero de esa palabra!) Permite resultados muy relevantes, con consultas que pueden ser mucho más matizadas en cómo dirigen el contenido. No es solo que puedas aplicar la relevancia subjetiva como <em>una</em> de tus etapas de recuperación; En realidad, la recuperación de la primera etapa puede incluir puntaje de relevancia junto con todos esos otros modos a la vez.</p><h3>Precisión y eficiencia superiores</h3><p>Emplear una plataforma de datos que pueda ofrecer búsqueda, recuperación y reclasificación distribuidas como tu principal motor de recuperación de contexto tiene mucho sentido. Puedes usar sintaxis avanzada de consulta para agregar el componente que falta de la intención subjetiva y filtrar contenido que pueda distraer o enturbiar el valor de la información contextual devuelta. Puedes seleccionar cualquiera de las opciones sintácticas individuales disponibles, o combinar modalidades en una única búsqueda que se dirija a cada tipo de datos de la manera que mejor entienda, y luego combinarlas o reordenarlas con el reclasificamiento. Puedes filtrar la respuesta para incluir solo los campos/valores que quieres, manteniendo a distancia los datos superfluos. En servicio de los agentes, esa flexibilidad de segmentación te permite construir herramientas extremadamente precisas en cómo recuperan el contexto.</p><h3>Refinamiento del contexto (agregaciones y señales no de contenido)</h3><p>Las agregaciones pueden ser especialmente útiles para moldear el contenido que una herramienta entrega a la ventana de contexto. Las agregaciones proporcionan naturalmente datos numéricos sobre la forma de los datos contextuales devueltos, lo que facilita y hace más preciso que los LLMs razonen. Como las agregaciones pueden anidar jerárquicamente, es una forma sencilla de agregar detalles multinivel para que el LLM genere una comprensión más matizada. Las agregaciones también pueden ayudar a gestionar el tamaño de la ventana de contexto — puedes reducir fácilmente un resultado de consulta de 100k documentos a unos pocos cientos de tokens de insights agregados.</p><p>Las señales no relacionadas con el contenido son los indicadores inherentes a tus datos que te muestran una visión general de lo que estás viendo; Son las características adicionales de los resultados, como popularidad, frescura, geolocalización, categorías, diversidad de anfitriones o bandas de precios. Estos datos pueden ser útiles para informar al agente sobre cómo valora la importancia del contexto que recibió. Algunos ejemplos sencillos podrían ayudar a ilustrar esto mejor:</p><ul><li><p><strong>Potenciar contenido publicado recientemente y popular</strong> - Imagina que tienes una base de conocimientos de artículos. Quieres encontrar artículos relevantes para la consulta de un usuario, pero también potenciar artículos que sean recientes y que fueron útiles por otros usuarios (por ejemplo, que tengan un alto número de "me gusta"). En este escenario, podemos usar una búsqueda híbrida para encontrar artículos relevantes y luego reclasificarlos en función de una combinación de su fecha de publicación y popularidad.</p></li><li><p><strong>Búsqueda de comercio electrónico con ajustes de ventas y stock</strong> - En un entorno de comercio electrónico, quieres mostrar a los clientes productos que coincidan con su término de búsqueda, pero también quieres promocionar productos que se venden bien y estén en stock. También podrías bajar el rango de productos con poco stock para evitar frustraciones del cliente.</p></li><li><p><strong>Priorizar los problemas de alta gravedad en un rastreador de errores</strong> : para un equipo de desarrollo de software, al buscar problemas, es fundamental destacar primero los problemas de alta gravedad, alta prioridad y actualizados recientemente. Puedes usar no señales como 'criticidad' y 'más debatido' para sopesar diferentes factores de forma independiente, cerciorando que los temas más críticos y debatidos salgan a la superficie</p></li></ul><p>Estas consultas de ejemplo y más se pueden encontrar en la <a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/you-know-for-context/">página de contenido</a> de Elasticsearch Labs que la acompaña.</p><h3>Aplicación de la seguridad</h3><p>Un beneficio crítico de aprovechar una capa de velocidad basada en búsqueda como Elastic para la ingeniería de contexto es su marco de seguridad integrado. La plataforma de Elastic garantiza que el contexto entregado a las operaciones de IA agente y generativa respete y proteja la información privada sensible mediante un control de acceso basado en roles (RBAC) y un control de acceso basado en atributos (ABAC). Esto significa que no solo las consultas se gestionan con eficiencia, sino que los resultados se filtran según las licencias específicas del agente o del usuario que inicia la solicitud.</p><p>Los agentes se ejecutan como el usuario autenticado, por lo que la seguridad se aplica implícitamente a través de las características de seguridad integradas en la plataforma:</p><ul><li><p><strong>Licencias detalladas:</strong> Define el acceso a nivel de documento, campo o incluso término, cerciorando que los agentes de IA solo reciban los datos que están autorizados a ver.</p></li><li><p><strong>Control de acceso basado en roles (RBAC):</strong> Asignar roles a agentes o usuarios, otorgando acceso a conjuntos de datos o funcionalidades específicas según sus responsabilidades definidas.</p></li><li><p><strong>Control de acceso basado en atributos (ABAC):</strong> Implementar políticas de acceso dinámicas basadas en los atributos de los datos, del usuario o del entorno, permitiendo una seguridad altamente adaptable y consciente del contexto.</p></li><li><p><strong>Seguridad a nivel de documento (DLS) y seguridad a nivel de campo (FLS):</strong> Estas capacidades cercioran que, incluso dentro de un documento recuperado, solo sean visibles las partes autorizadas, evitando que se exponga información sensible.</p></li><li><p><strong>Integración con la seguridad empresarial:</strong> Integra sin problemas con los sistemas de gestión de identidades existentes (como LDAP, SAML, OIDC) para hacer cumplir políticas de seguridad coherentes en toda la organización.</p></li></ul><p>Al integrar estas medidas de seguridad directamente en el mecanismo de recuperación de contexto, Elastic actúa como un guardián seguro, cerciorando que los agentes de IA operen dentro de límites de datos definidos, evitando exposiciones no autorizadas y manteniendo el cumplimiento de las normativas de privacidad de datos. Esto es fundamental para generar confianza en sistemas de IA agente que manejan información confidencial o propietaria.</p><p>Como beneficio adicional, al usar una capa unificada de velocidad de datos sobre las fuentes de datos de tu compañía, alivias las cargas inesperadas de consultas ad hoc en esos repositorios que crearían las herramientas agentes. Tienes un único lugar para buscar todo casi en tiempo real, y un lugar para aplicar controles de seguridad y gobernanza.</p><h2>Herramientas híbridas basadas en búsqueda</h2><p>Hay algunas características fundamentales (y <a href="https://www.elastic.co/blog/whats-new-elastic-9-2-0">cada vez van más</a> y más) de la plataforma Elastic que impulsan mucho la búsqueda de la ingeniería de contexto. Lo principal aquí es que la plataforma ofrece multitud de formas de lograr cosas, con la flexibilidad de adaptar, cambiar y ampliar métodos a medida que avanza el ecosistema de IA.</p><h3>Presentando Agent Builder</h3><p>Elastic <a href="https://www.elastic.co/elasticsearch/agent-builder">Agent Builder</a> es nuestra primera incursión en el ámbito de herramientas de IA agente diseñadas para comunicar con los datos que ya almacenas en Elastic. Agent Builder ofrece una interfaz de chat que permite a los usuarios crear y gestionar sus propios agentes y herramientas dentro de Kibana. Incluye servidores MCP y A2A integrados, APIs programáticas y un conjunto de herramientas de sistema prediseñadas para consultar y explorar índices de Elasticsearch, así como para generar ES|Consultas QL desde lenguaje natural. Agent Builder te permite crear herramientas personalizadas que dirigen y esculpen los datos contextuales devueltos al agente a través de <a href="https://www.elastic.co/docs/reference/query-languages/esql">ES| expresivoSintaxis de consultas QL</a> .</p><p>¿Cómo funciona ES|¿Quieres que QL realice búsqueda híbrida, preguntas? La capacidad principal se logra mediante la combinación del tipo de campo <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/semantic-text">semantic_text</a> y los comandos <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/fork">FORK</a>/<a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/fuse">FUSE</a> (FUSE usa <a href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/reciprocal-rank-fusion">RRF</a> por defecto para fusionar los resultados de cada bifurcación). Aquí tienes un ejemplo sencillo de una búsqueda ficticia de producto:</p>FROM products
| FORK
  (MATCH description "high performance gaming laptop" | EVAL search_type = "bm25"),
  (MATCH description_semantic "high performance gaming laptop" | EVAL search_type = "semantic")
| FUSE 
| LIMIT 20
| KEEP product_name, description, _score, search_type<p>La cláusula <a href="https://www.elastic.co/docs/reference/query-languages/esql/commands/eval">EVAL</a> incluida con cada una de las ramas FORK en el ejemplo anterior no es estrictamente necesaria; Solo se incluye para demostrar cómo se puede rastrear de qué modalidad de búsqueda se devuelve un resultado determinado.</p><h3>Plantillas de búsqueda</h3><p>Supongamos que quieres apuntar tus propias herramientas de agencia externa a tu despliegue de Elastic. Y en lugar de ES|QL, quieres usar recuperadores multietapa o reutilizar la sintaxis DSL existente que desarrollaste, y también quieres poder controlar las entradas que acepta la consulta, la sintaxis usada para ejecutar la búsqueda y los campos devueltos en la salida. Las <a href="https://www.elastic.co/docs/solutions/search/search-templates">plantillas de búsqueda</a> permiten a los usuarios definir estructuras predefinidas para patrones de búsqueda comunes, mejorando la eficiencia y la consistencia en la obtención de datos. Esto es especialmente beneficioso para herramientas agentes que interactúan con APIs de búsqueda, ya que ayudan a estandarizar el código estándar y permiten una iteración más rápida de la lógica de búsqueda. Y si alguna vez necesitas ajustar alguno de esos factores, solo actualizas la plantilla de búsqueda y voilà que los cambios se implementan. Si buscas un ejemplo de plantillas de búsqueda en acción con herramientas agentes, echa un vistazo al blog de Elasticsearch Labs '<a href="https://www.elastic.co/search-labs/blog/mcp-intelligent-search">MCP for intelligent search</a>', que emplea una plantilla de búsqueda detrás de una llamada a herramienta desde un servidor MCP externo.</p><h3>Flujos de trabajo integrados (¡por la primera vez!)</h3><p>Una de las cosas más difíciles de navegar en nuestro nuevo mundo de IA agente es la naturaleza no determinista de agentes "razonamientos" semi-autónomos y autodirigidos. La ingeniería de contexto es una disciplina crítica para la IA agentica: son las técnicas que ayudan a reducir las posibles conclusiones que puede generar nuestro agente a lo que sabemos de la verdad fundamental. Incluso con una ventana de contexto altamente precisa y relevante (cuando salimos del ámbito de los hechos numéricos) seguimos faltando esa pequeña garantía de que la respuesta del agente es totalmente repetible y fiable.</p><p>Cuando envías la misma solicitud a un agente varias veces, las respuestas pueden ser <em>esencialmente</em> las mismas con <em>solo una pequeña</em> diferencia en la respuesta. Eso suele estar bien para consultas simples, quizá apenas perceptibles, y podemos intentar moldear el resultado con técnicas de ingeniería de contexto. Pero a medida que las tareas que pedimos a nuestros agentes se vuelven más complejas, existe más probabilidad de que una o más de las subtareas introduzcan una variación que cambie ligeramente el resultado final. Probablemente empeorará a medida que empecemos a depender más de las comunicaciones agente a agente, y esas variaciones se acumularán. Esto vuelve a la idea de que las herramientas con las que interactúan nuestros agentes deben ser muy flexibles y ajustables para dirigir con precisión los datos contextuales, y que deben responder en un formato de salida esperado. También indica que, en muchos casos de uso, necesitamos dirigir las interacciones entre agentes y herramientas — ¡aquí es donde entran en juego los flujos de trabajo!</p><p>Elastic pronto tendrá flujos de trabajo completamente personalizables integrados en el núcleo de la plataforma. Estos flujos de trabajo podrán operar con agentes y herramientas de forma bidireccional, por lo que los flujos de trabajo podrán llamar a agentes y herramientas, y agentes y herramientas podrán llamar a flujos de trabajo. Tener estas capacidades totalmente integradas en la misma plataforma de IA de búsqueda, donde todos tus datos viven siendo transformadores, ¡el potencial de los flujos de trabajo es extremadamente emocionante! ¡Pronto, muy pronto!</p><h3>Elastic como banco de memoria unificado</h3><p>Al ser una plataforma de datos distribuida diseñada para búsquedas casi en tiempo real, Elastic realiza naturalmente las funciones de memoria a largo plazo para sistemas de IA agente. Con la experiencia de chat integrada en Agent Builder, también tenemos seguimiento y gestión de la memoria a corto plazo y el historial de chat. Y dado que toda la plataforma es API-first, es extremadamente fácil emplear Elastic como plataforma para mantener la salida contextual de una herramienta (y poder consultar ella después) que podría saturar la ventana de contexto del agente; Esta técnica a veces se denomina "<a href="https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents#:~:text=Agents%20can%20assemble%20understanding%20layer%20by%20layer%2C%20maintaining%20only%20what%27s%20necessary%20in%20working%20memory%20and%20leveraging%20note%2Dtaking%20strategies%20for%20additional%20persistence">toma de notas</a>" en círculos de ingeniería contextual.</p><p>Tener memoria a corto y largo plazo en la misma plataforma de búsqueda aporta muchos beneficios intrínsecos: imagina poder usar historiales de chat y respuestas contextuales persistentes como parte de los influencers semánticos para futuras interacciones en chat, o para realizar análisis de amenazas, o para crear productos de datos persistentes que se generan automáticamente a partir de llamadas a herramientas repetidas con frecuencia... ¡Las posibilidades son infinitas!</p><h2>Conclusión</h2><p>La aparición de grandes modelos de lenguaje cambió la forma en que podemos comparar contenido y los métodos que empleamos para analizar nuestros datos. Nos estamos alejando rápidamente de nuestro mundo actual, donde los humanos realizan la investigación, la consideración contextual y el razonamiento lógico para responder a sus propias preguntas, a uno donde esos pasos están en gran medida automatizados mediante IA agente. Para confiar en las respuestas generadas que recibimos, necesitamos la seguridad de que el agente consideró <em>toda</em> la información <em>más relevante</em> (incluido el factor de relevancia subjetiva) al generar su respuesta. Nuestro método principal para hacer que la IA agente sea fiable es fundamentar las herramientas que recuperan contexto adicional mediante técnicas de RAG e ingeniería contextual, pero cómo esas herramientas realizan la <em>recuperación inicial</em> puede ser crucial para la precisión de la respuesta.</p><p>La plataforma Elastic Search AI ofrece la flexibilidad y beneficio de la búsqueda híbrida, junto con varias funciones integradas que ayudan a la IA agente en términos de precisión, rendimiento y escalabilidad; en otras palabras, Elastic es una plataforma fantástica para varios aspectos de la ingeniería de contexto. Al estandarizar la recuperación de contexto a través de una plataforma de búsqueda, simplificamos las operaciones de las herramientas agenticas en varios frentes — y, similar al oxímoron de "ralentizar para ir más rápido", la simplicidad en la capa de generación de contexto significa una IA agente más rápida y fiable.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/context-engineering-hybrid-search-agentic-ai-accuracy</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/context-engineering-hybrid-search-agentic-ai-accuracy</guid>
    <category><![CDATA[Búsqueda híbrida]]></category>
    <category><![CDATA[AI agéntica]]></category>
    <dc:creator><![CDATA[Woody Walton]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt42a203a316f0e22e/6a170932b339d58ebc769f5f/b82ff25242e4229cc20b218d9cc91c60cfd680bc-720x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Thu, 20 Nov 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Ya sabes, para contextualizar - Parte II: IA agente y la necesidad de la ingeniería del contexto]]></title>
    <description><![CDATA[Aprende cómo la evolución de los LLMs hacia la IA agente aumenta la necesidad de ingeniería de contexto para resolver los límites de contexto de RAG y la gestión de la memoria.]]></description>
    <content:encoded><![CDATA[<p>Con ese conocimiento (bastante <a href="https://www.elastic.co/search-labs/blog/context-engineering-hybrid-search-evolution-agentic-ai">extenso)</a> sobre cómo los LLMs cambiaron los procesos subyacentes de recuperación de información, veamos cómo también cambiaron la forma en que consultamos datos.</p><h2>Una nueva forma de interactuar con los datos</h2><p>La IA generativa (genIA) y la IA agente hacen las cosas de forma diferente a la búsqueda tradicional. Mientras que la forma en que empezábamos a investigar la información era buscando ("déjame buscar en Google..."), la acción inicial tanto para la IA de generación como para los Agentes suele ser mediante lenguaje natural introducido en una interfaz de chat. La interfaz de chat es una discusión con un LLM que emplea su comprensión semántica para convertir nuestra pregunta en una respuesta destilada, una respuesta resumida que aparentemente proviene de un oráculo que tiene un amplio conocimiento de todo tipo de información. Lo que realmente lo vende es la capacidad del LLM para generar frases coherentes y reflexivas que enlazan los fragmentos de conocimiento que saca a la luz — incluso cuando son inexactas o totalmente alucinadas, tienen <a href="https://en.wikipedia.org/wiki/Truthiness">cierta veracidad</a> .</p><p>Esa vieja barra de búsqueda con la que estábamos tan acostumbrados a interactuar puede considerar el motor RAG que usábamos cuando <em><strong>nosotros mismos</strong></em> éramos el agente de razonamiento. Ahora, incluso los motores de búsqueda de Internet están convirtiendo nuestra experiencia léxica de búsqueda "caza y picotea" en una visión general impulsada por IA que responde a la consulta con un resumen de los resultados, ayudando a los usuarios a evitar la necesidad de hacer clic y evaluar los resultados individuales por sí mismos.</p><h2>IA generativa y RAG</h2><p>La IA generativa intenta usar su comprensión semántica del mundo para analizar la intención subjetiva expresada a través de una solicitud de chat, y luego emplea sus habilidades de inferencia para crear una respuesta experta sobre la marcha. Hay varias partes en una interacción generativa con IA: comienza con la entrada/consulta del usuario, conversaciones previas en la sesión de chat pueden usar como contexto adicional, y el prompt instructivo que indica al LLM cómo razonar y qué procedimientos seguir para construir la respuesta. Los prompts evolucionaron desde simples "explícame esto como si tuviera cinco años" a desglosar completos sobre cómo procesar solicitudes. Estos desgloses suelen incluir secciones distintas que describen detalles de la persona/rol de la IA, razonamiento pregeneración/proceso de pensamiento interno, criterios objetivos, restricciones, formato de salida, audiencia, así como ejemplos para ayudar a demostrar los resultados esperados.</p><p>Además de la consulta del usuario y el prompt del sistema, la generación aumentada por recuperación (RAG) proporciona información contextual adicional en lo que se denomina una "ventana de contexto". RAG fue una adición fundamental a la arquitectura; es lo que usamos para informar al LLM sobre las piezas que faltan en su comprensión semántica del mundo.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbfa000ccfdd9d184/6a17ddb57b54f955f38b37da/5b9671d5d07d4caefde372bb3188000754a91eed-1470x746.png" alt="Cómo los LLM procesan consultas de usuario y crean contexto" /><p>Las ventanas de contexto pueden ser un <a href="https://www.dbreunig.com/2025/06/22/how-contexts-fail-and-how-to-fix-them.html">poco quisquillosas</a> en cuanto a qué, dónde y cuánto les das. Qué contexto se selecciona es muy importante, por supuesto, pero también importa la relación señal-ruido del contexto proporcionado, así como la duración de la ventana.</p><h3>Muy poca información</h3><p>Proporcionar muy poca información en una consulta, una indicación o una ventana de contexto puede provocar alucinaciones porque el LLM no puede determinar con precisión el contexto semántico correcto desde el que generar una respuesta. También existen problemas con la similitud vectorial de los tamaños de fragmentos de documentos: una pregunta corta y sencilla puede no coincidir semánticamente con los documentos completos y detallados que encontramos en nuestras bases de conocimiento vectorizadas. Se desarrollaron técnicas de expansión de consultas como <a href="https://medium.com/data-science/how-to-use-hyde-for-better-llm-rag-retrieval-a0aa5d0e23e8">los Embeddings de Documentos Hipotéticos (HyDE)</a> que emplean LLMs para generar una respuesta hipotética más rica y expresiva que la consulta corta. El peligro aquí, por supuesto, es que el documento hipotético es en sí mismo una alucinación que aleja aún más al LLM del contexto correcto.</p><h3>Demasiada información</h3><p>Al igual que nos pasa a los humanos, demasiada información en una ventana de contexto puede abrumar y confundir a un LLM sobre cuáles deberían ser las partes importantes. El desbordamiento de contexto (o "<a href="https://research.trychroma.com/context-rot">podredumbre del contexto</a>") afecta a la calidad y el rendimiento de las operaciones de IA generativa; afecta enormemente a la "cotización de atención" del LLM (su memoria de trabajo) y diluye la relevancia entre muchos tokens competidores. El concepto de "podredumbre del contexto" también incluye la observación de que los LLMs tienden a tener un <a href="https://alexandrabarr.beehiiv.com/p/context-windows">sesgo posicional</a> : prefieren el contenido al principio o al final de una ventana de contexto sobre el contenido de la sección central.</p><h3>Información que distrae o contradice</h3><p>Cuanto más grande es una ventana de contexto, más posibilidades hay de que incluya información superflua o contradictoria que pueda distraer al LLM de seleccionar y procesar el contexto correcto. En cierto modo, se convierte en un problema de basura entrando y saliendo basura: simplemente volcar un conjunto de documentos resulta en una ventana de contexto le da al LLM mucha información para analizar (potencialmente demasiado), pero dependiendo de cómo se seleccionó el contexto hay una mayor posibilidad de que se filtre información contradictoria o irrelevante.</p><h2>Agentic AI</h2><p>Te dije que había mucho por cubrir, pero lo conseguimos — ¡por fin estamos hablando de temas de IA agente! La IA Agente es un uso muy emocionante de las interfaces de chat LLM que amplía la capacidad de la IA generativa (¿podemos llamarla ya "legado"?) para sintetizar respuestas basar en su propio conocimiento y la información contextual que proporcionas. A medida que la IA generativa maduraba, nos dimos cuenta de que había un cierto nivel de tareas y automatización que podíamos hacer con los LLMs, inicialmente relegados a actividades tediosas y de bajo riesgo que un humano podía comprobar o validar fácilmente. En un corto periodo de tiempo, ese alcance inicial creció: una ventana de chat de un LLM puede ahora ser la chispa que envíe a un agente de IA para planear, ejecutar y evaluar iterativamente su plan para lograr su objetivo especificado. Los agentes tienen acceso al razonamiento propio de sus LLMs, al historial de chat y a la memoria de pensamiento (tal como es), y también disponen de herramientas específicas que pueden emplear para ese objetivo. También estamos viendo arquitecturas que permiten a un agente de alto nivel actuar como orquestador de múltiples <a href="https://www.philschmid.de/the-rise-of-subagents">subagentes</a>, cada uno con sus propias cadenas lógicas, conjuntos de instrucciones, contexto y herramientas.</p><p>Los agentes son el punto de entrada a un flujo de trabajo mayormente automatizado: son autodirigidos en el sentido de que pueden chatear con un usuario y luego usar la 'lógica' para determinar qué herramientas tienen disponibles para ayudar a responder a la pregunta del usuario. Las herramientas suelen considerar pasivas en comparación con los agentes y están diseñadas para realizar un solo tipo de tarea. Los <em>tipos</em> de tareas que una herramienta puede realizar son bastante ilimitados (¡lo cual es realmente emocionante!), pero una tarea principal que realizan las herramientas es recopilar información contextual para que un agente la tenga en cuenta al ejecutar su flujo de trabajo.</p><p>Como tecnología, la IA agente aún está en pañezas y propensa al equivalente LLM del trastorno por déficit de atención: olvida fácilmente lo que se le pide hacer y a menudo se escapa a hacer otras cosas que no formaban parte del encargo. Bajo la aparente magia, las habilidades de "razonamiento" de los LLM siguen basar en predecir el siguiente token más probable en una secuencia. Para que el razonamiento (o algún día, la inteligencia artificial general (AGI)) sea fiable y digno de confianza, necesitamos poder verificar que, cuando se nos da la información correcta y más actualizada, razonarán como esperamos (y quizás nos darán ese poco más que quizá no pensamos). Para que eso ocurra, las arquitecturas agenticas necesitarán la capacidad de comunicar claramente (protocolos), adherir a los flujos de trabajo y restricciones que les damos (barreras de seguridad), recordar en qué punto de una tarea (estado) se sienten, gestionar su espacio de memoria disponible y validar que sus respuestas son precisas y cumplen los criterios de la tarea.</p><h2>Háblame en un idioma que pueda entender</h2><p>Como es habitual en nuevas áreas de desarrollo (especialmente en el mundo de los LLM), inicialmente existían bastantes enfoques para la comunicación agente-herramienta, pero rápidamente convergieron hacia el <a href="https://modelcontextprotocol.io/docs/getting-started/intro">Protocolo de Contexto del Modelo (MCP)</a> como estándar de facto. La definición de Protocolo de Contexto de Modelo está realmente en el nombre: es el <strong>protocolo</strong> que emplea un <strong>modelo</strong> para aplicar y recibir información <strong>contextual</strong> . MCP actúa como un adaptador universal para que los agentes LLM se conecten a herramientas externas y fuentes de datos; simplifica y estandariza las APIs para que diferentes frameworks y herramientas de LLM puedan interoperar fácilmente. Eso convierte a MCP en una especie de punto de pivote entre la lógica de orquestación y los indicios del sistema dados a un agente para actuar de forma autónoma al servicio de sus objetivos, y las operaciones enviadas a las herramientas para que se ejecuten de forma más aislada (aislada al menos respecto al agente iniciador).</p><p>Este ecosistema es tan nuevo que cada dirección de expansión se siente como una nueva frontera. Tenemos protocolos similares para interacciones agente a agente (<a href="https://developers.googleblog.com/en/a2a-a-new-era-of-agent-interoperability/">Agent2Agent (A2A</a> , por supuesto!) así como otros proyectos para mejorar la memoria de razonamiento de agentes (<a href="https://venturebeat.com/ai/new-memory-framework-builds-ai-agents-that-can-handle-the-real-worlds">ReasoningBank</a>), para seleccionar el mejor servidor MCP para el trabajo en cuestión (<a href="https://arxiv.org/abs/2505.03275">RAG-MCP</a>), y usar análisis semántico como la clasificación zero-shot y la detección de patrones en entrada y salida como <a href="https://openai.github.io/openai-guardrails-python/">Guardrails</a> para controlar sobre qué puede operar un agente.</p><p>Quizá notaste que la intención subyacente de cada uno de estos proyectos es mejorar la calidad y el control de la información que se devuelve en una ventana de contexto agente/genAI. Aunque el ecosistema de IA agente continúa desarrollando la capacidad de manejar mejor esa información contextual (para controlarla, gestionar y operar sobre ella), siempre habrá necesidad de recuperar la información <em>contextual más relevante</em> como materia para que el agente siga adelante.</p><h2>¡Bienvenido a la ingeniería de contexto!</h2><p>Si conoces los términos de IA generativa, probablemente oíste hablar de la 'ingeniería de prompts'; a estas alturas, es casi una pseudociencia en sí misma. La ingeniería de prompts se emplea para encontrar las mejores y más eficientes formas de describir proactivamente los comportamientos que quieres que el LLM emplee para generar su respuesta. La '<a href="https://www.elastic.co/search-labs/blog/context-engineering-overview">ingeniería de contexto</a>' extiende las técnicas de 'ingeniería de prompts' más allá del lado del agente para cubrir también las fuentes y sistemas de contexto disponibles en el lado de herramientas del protocolo MCP, e incluye los temas generales de gestión, procesamiento y generación de contexto:</p><ul><li><p><strong>Gestión del contexto </strong>- Relacionada con mantener el estado y la eficiencia del contexto en flujos de trabajo agentivos de larga duración y/o más complejos. Planeación iterativa, seguimiento y orquestación de tareas y llamada a herramientas para lograr los objetivos del agente. Debido a la limitada "cotización de atención" que los agentes deben trabajar, la gestión del contexto se centra principalmente en técnicas que ayudan a refinar la ventana de contexto para capturar tanto el alcance más completo como los aspectos más importantes del contexto (¡su precisión frente a la memoria!). Las técnicas incluyen compresión, resumen y persistencia de contexto de pasos previos o llamadas a herramientas para dejar espacio en la memoria de trabajo para contexto adicional en los pasos posteriores.</p></li><li><p><strong>Procesamiento de contexto </strong>: los pasos lógicos y, con suerte, mayormente programáticos para integrar, normalizar o refinar el contexto adquirido de fuentes dispares, de modo que el agente pueda razonar a través de todo el contexto de manera más o menos uniforme. El trabajo subyacente consiste en hacer que el contexto de todas las fuentes (prompts, RAG, memoria, etc.), todo sea consumible por el agente de la forma más eficiente posible. </p></li><li><p><strong>Generación de contexto </strong>- Si el procesamiento de contexto consiste en hacer que el contexto recuperado sea utilizable para el agente, entonces la generación de contexto le da al agente el alcance para aplicar y recibir esa información contextual adicional a voluntad, pero también con restricciones.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5e1e68c08fe050bc/6a17ddb7414c645035945073/4a8240e1eb078b2294b8d981b9caa8593589cac4-1600x900.png" alt="Ingeniería del contexto en los LLMs" /><p>Los distintos efímeros de las aplicaciones de chat LLM se corresponden directamente (y a veces de formas superpuestas) a esas funciones de alto nivel de la ingeniería del contexto:</p><ul><li><p><strong>Instrucciones / prompt del sistema</strong> - Los prompts son el marco de cómo la actividad generativa (o agente) de IA dirigirá su pensamiento hacia el logro del objetivo del usuario. Los prompts son contexto en sí mismos; No son solo instrucciones tonales: también suelen incluir lógica de ejecución de tareas y reglas para cosas como "pensar paso a paso" o "respirar hondo" antes de responder para validar que la respuesta responde completamente a la petición del usuario. Pruebas recientes demostraron que los lenguajes de marcado son muy eficaces para enmarcar las diferentes partes de un prompt, pero también hay que tener cuidado de calibrar las instrucciones para que quede en un punto óptimo entre demasiado vago y demasiado específico; queremos dar suficiente instrucción para que el LLM encuentre el contexto adecuado, pero sin ser tan prescriptivo que pierda ideas inesperadas.</p></li><li><p><strong>Memoria a corto plazo</strong> (estado/historial) - La memoria a corto plazo es esencialmente la interacción de la sesión de chat entre el usuario y el LLM. Estos son útiles para refinar el contexto en sesiones en tiempo real y pueden almacenar para su recuperación y continuación futuras. </p></li><li><p><strong>Memoria a largo</strong> plazo - La memoria a largo plazo debe consistir en información útil a lo largo de varias sesiones. Y no solo se accede a bases de conocimiento específicas de dominio a través de RAG; investigaciones recientes emplean los resultados de solicitudes previas de IA agente/generativa para aprender y referenciar dentro de las interacciones agentices actuales. Algunas de las innovaciones más interesantes en el ámbito de la memoria a largo plazo están relacionadas con ajustar cómo <a href="https://steve-yegge.medium.com/introducing-beads-a-coding-agent-memory-system-637d7d92514a">se almacena y</a> enlaza el estado para que los agentes puedan retomar donde lo dejaron. </p></li><li><p><strong>Salida estructurada</strong> - La cognición requiere esfuerzo, así que probablemente no sea de extrañar que, incluso con capacidades de razonamiento, los LLMs (igual que los humanos) quieran gastar menos esfuerzo al pensar, y en ausencia de una API o protocolo definido, tener un mapa (un esquema) para leer los datos devueltos de una llamada a una herramienta es de gran ayuda. La inclusión de <a href="https://platform.openai.com/docs/guides/structured-outputs?lang=javascript">Salidas Estructuradas</a> como parte del marco agential ayuda a hacer que estas interacciones máquina a máquina sean más rápidas y fiables, con menos necesidad de análisis sintáctico impulsado por el pensamiento.</p></li><li><p><strong>Herramientas disponibles</strong> - Las herramientas pueden hacer todo tipo de cosas, desde recopilar información adicional (por ejemplo, enviar consultas RAG a repositorios de datos empresariales o a través de APIs en línea) hasta realizar acciones automatizadas en nombre del agente (como reservar una habitación de hotel según los criterios de la solicitud del agente). Las herramientas también podrían ser subagentes con sus propias cadenas de procesamiento agenticos. </p></li><li><p><strong>Generación Aumentada por Recuperación (RAG)</strong> - Me gusta mucho la descripción de RAG como "integración dinámica del conocimiento". Como se describió antes, RAG es la técnica para proporcionar la información adicional a la que el LLM no tenía acceso cuando fue capacitado, o es una reiteración de las ideas que consideramos más importantes para obtener la respuesta correcta — la que es más relevante para nuestra consulta subjetiva.</p></li></ul><h2>¡Un poder cósmico fenomenal, un espacio vital diminuto!</h2><p>¡La IA Agente tiene tantos reinos nuevos fascinantes y emocionantes por explorar! Todavía quedan muchos de los problemas tradicionales de recuperación y procesamiento de datos por resolver, pero también nuevas clases de desafíos que solo ahora se están exponiendo a la luz en la nueva era de los LLM. Muchos de los problemas inmediatos con los que lidiamos hoy están relacionados con la ingeniería de contexto, es decir, conseguir que los LLMs reciban la información contextual adicional que necesitan sin saturar su limitado espacio de memoria de trabajo.</p><p>La flexibilidad de los agentes semiautónomos que tienen acceso a una variedad de herramientas (y otros agentes) da lugar a tantas ideas nuevas para implementar IA que es difícil imaginar las diferentes formas en que podríamos unir las piezas. La mayor parte de la investigación actual se centra en el campo de la ingeniería del contexto y se centra en construir estructuras de gestión de memoria capaces de manejar y rastrear mayores cantidades de contexto — esto se debe a que los problemas de pensamiento profundo que realmente queremos que resuelvan los LLMs presentan una mayor complejidad y pasos de pensamiento multifásicos y de larga duración, donde la memoria es extremadamente importante.</p><p>Gran parte de la experimentación continua en el campo consiste en intentar encontrar la gestión óptima de tareas y configuraciones de herramientas para alimentar la boca agente. Cada llamada a una herramienta en la cadena de razonamiento de un agente genera un costo acumulado, tanto en términos de cálculo para realizar la función de esa herramienta como del impacto en la ventana de contexto limitada. Algunas de las técnicas más recientes para gestionar el contexto de agentes LLM provocaron efectos de cadena no intencionados como el "<a href="https://venturebeat.com/ai/ace-prevents-context-collapse-with-evolving-playbooks-for-self-improving-ai">colapso del contexto</a>", donde comprimir/resumir el contexto acumulado para tareas de larga duración se vuelve <em>demasiado</em> perdiente. El resultado deseado son herramientas que devuelvan un contexto conciso y preciso, sin que información extraña se filtre en el valioso espacio de memoria de la ventana de contexto.</p><h3>Demasiadas posibilidades</h3><p>Queremos separación de tareas con flexibilidad para reutilizar herramientas/componentes, así que tiene todo el sentido crear herramientas agentes dedicadas para conectar a fuentes de datos específicas: cada herramienta puede especializar en consultar un tipo de repositorio, un tipo de flujo de datos o incluso un caso de uso. Pero cuidado: en la lucha por ahorrar tiempo/dinero/demostrar que algo es posible, va a haber una fuerte tentación de usar los LLMs como herramienta de federación... Intenta no hacerlo, ¡ya pasamos <a href="https://www.elastic.co/pdf/elastic-distributed-not-federated-search.pdf">por eso</a> antes! La consulta federada actúa como un "traductor universal" que convierte una consulta entrante en la sintaxis que el repositorio remoto entiende, y luego tiene que racionalizar de alguna manera los resultados de múltiples fuentes para obtener una respuesta coherente. La federación como técnica <em>funciona</em> <em>bien</em> a pequeña escala, pero a gran escala y especialmente cuando los datos son multimodales, la federación intenta salvar brechas que son demasiado amplias.</p><p>En el mundo agente, el agente sería el federador y las herramientas (a través de MCP) serían las conexiones definidas manualmente con recursos dispares. Emplear herramientas dedicadas para llegar a fuentes de datos no conectadas puede parecer una forma poderosa de unir dinámicamente diferentes flujos de datos por consulta, pero usar herramientas para hacer la misma pregunta a múltiples fuentes probablemente acabará causando más problemas de los que resuelve. Cada una de esas fuentes de datos probablemente sean diferentes tipos de repositorios debajo, cada uno con sus propias capacidades para recuperar, clasificar y cerciorar los datos que contienen. Esas variaciones o "desajustes de impedancia" entre repositorios agregan carga de procesamiento, por supuesto. También pueden introducir información o señales contradictorias, donde algo aparentemente inocuo como un desalineamiento de puntaje podría desajustar radicalmente la importancia dada a un poco de contexto devuelto y afectar la relevancia de la respuesta generada al final.</p><h3>El cambio de contexto también es difícil para las computadoras</h3><p>Cuando envías a un agente en una misión, a menudo su primera tarea es encontrar todos los datos relevantes a los que tiene acceso. Al igual que ocurre con los humanos, si cada fuente de datos que el agente conecta a respuestas con respuestas disímiles y desagregadas, habrá carga cognitiva (aunque no exactamente del mismo tipo) asociada a extraer los fragmentos contextuales salientes del contenido recuperado. Eso lleva tiempo/cálculo, y cada pequeño detalle se acumula en la cadena lógica agentica. Esto lleva a la conclusión de que, al igual que se discute sobre <a href="https://blog.cloudflare.com/code-mode/">MCP</a>, la mayoría de las herramientas agenticas deberían comportar más como APIs — funciones aisladas con entradas y salidas conocidas, ajustadas para soportar las necesidades de diferentes tipos de agentes. Incluso nos estamos dando cuenta de que <a href="https://arxiv.org/html/2501.12372v5">los LLM necesitan contexto para contexto</a> — son mucho mejores conectando los puntos semánticos, especialmente cuando es una tarea como traducir lenguaje natural a sintaxis estructurada, cuando tienen un esquema al que referir (¡RTFM, sin duda!).</p><h2>¡Séptima entrada!</h2><p>Ahora cubrimos el <a href="https://www.elastic.co/search-labs/blog/context-engineering-hybrid-search-evolution-agentic-ai">impacto que los LLMs tuvieron en la recuperación y consulta de datos</a>, así como cómo la ventana de chat está madurando hacia la experiencia de IA agente. Pongamos los dos temas juntos y veamos cómo podemos emplear nuestras nuevas capacidades de búsqueda y recuperación para mejorar nuestros resultados en ingeniería de contexto. ¡Pasando a <a href="https://www.elastic.co/search-labs/blog/context-engineering-hybrid-search-agentic-ai-accuracy">la Parte III: ¡El poder de la búsqueda híbrida en la ingeniería de contexto</a>!</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/context-engineering-llm-evolution-agentic-ai</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/context-engineering-llm-evolution-agentic-ai</guid>
    <category><![CDATA[AI agéntica]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Woody Walton]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5f98889141fba45b/6a17ddb80b0bed0822dd34a2/79c0378b68d74d9e018c35ee2c1fd17daeee9f2c-1080x608.webp" length="0" type="image/webp"/>
    <pubDate>Tue, 18 Nov 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Creando una sala de redacción de un agente LLM con protocolo A2A y MCP en Elasticsearch: Parte I]]></title>
    <description><![CDATA[Explora los conceptos del protocolo A2A y MCP en un ejemplo práctico de redacción donde agentes especializados en LLM colaboran para investigar, escribir, editar y publicar artículos de noticias.]]></description>
    <content:encoded><![CDATA[<h2>Introducción</h2><p>Los sistemas actuales respaldados por LLM están evolucionando rápidamente más allá de aplicaciones de modelo único hacia redes complejas donde agentes especializados trabajan juntos para realizar tareas que la informática moderna nunca antes consideró posibles. A medida que estos sistemas se vuelven más complejos, la infraestructura que permite la comunicación de agentes y el acceso a herramientas se convierte en el principal foco de desarrollo. Surgieron dos enfoques complementarios para abordar estas necesidades: los protocolos <strong>Agent2Agent (A2A)</strong> para la coordinación multiagente y el <strong>Protocolo de Contexto del Modelo (MCP)</strong> para el acceso estandarizado a herramientas y recursos.</p><p>Entender cuándo usar cada uno en armonía con y sin el otro puede afectar significativamente la escalabilidad, mantenibilidad y efectividad de tus aplicaciones. Este artículo explora los conceptos e implementaciones de <strong>A2A</strong> dentro del ejemplo práctico de una redacción digital, donde agentes especializados en LLM colaboran para investigar, escribir, editar y publicar artículos de noticias.</p><p>Aquí <a href="https://github.com/justincastilla/elastic-newsroom/tree/main">se puede encontrar</a> un repositorio adjunto, y examinaremos ejemplos concretos de A2A en acción cerca del final del artículo en la Sección 5.</p><h3>Prerrequisitos</h3><p>El <a href="https://github.com/justincastilla/elastic-newsroom/tree/main">repositorio</a> consiste en implementaciones basadas en Python de los agentes A2A. Flask proporciona un servidor API, así como un servicio de mensajería personalizado en Python llamado Event Hub, que enruta mensajes para registro y actualizaciones de la interfaz. Por último, se dispone de una interfaz React para el uso independiente de las funciones de la redacción. Todo está contenido dentro de una imagen Docker para facilitar su implementación. Si quieres ejecutar los servicios directamente con tu máquina, querrás cerciorarte de tener estas tecnologías instaladas:</p><p>Lenguajes y tiempos de ejecución</p><ul><li><p>Python 13.12 - Lenguaje central de backend</p></li><li><p>Node.js 18+ - Interfaz de React opcional</p></li></ul><p>Frameworks y SDKS básicos:</p><ul><li><p>A2A SDK 0.3.8 - Coordinación y comunicación de agentes</p></li><li><p>SDK Anthropic - Integración de Claude para generación de IA</p></li><li><p>Uvicorn - servidor ASGI para ejecutar agentes</p></li><li><p>FastMCP 2.12.5+ - Implementación del servidor MCP</p></li><li><p>React 18.2 - Marco de interfaz de usuario frontend</p></li></ul><p>Datos y búsqueda</p><ul><li><p>Elasticsearch 9.1.1+ - Indexación y búsqueda de artículos</p></li></ul><p>Despliegue en Docker (opcional, pero recomendado)</p><ul><li><p>Docker 28.5.1+</p></li></ul><h2>Sección 1: ¿Qué es Agent2Agent (A2A)?</h2><h3>Definición y conceptos fundamentales</h3><p>Agent2Agent (A2A) es un protocolo estandarizado para la interacción entre agentes independientes de LLM. En lugar de un único sistema monolítico que gestione todas las tareas, A2A permite que múltiples agentes especializados se comuniquen, coordinen y colaboren para lograr flujos de trabajo complejos que serían difíciles, lentos o directamente imposibles de manejar eficientemente para un solo agente.</p><p><strong>Especificaciones oficiales</strong>: <a href="https://a2a-protocol.org/latest/specification/">https://a2a-protocol.org/latest/specification/</a></p><h3>Orígenes y evolución</h3><p>El concepto de comunicación Agent2Agent, o sistemas multiagente, tiene raíces en sistemas distribuidos, microservicios e investigación multiagente que se remontan <a href="https://en.wikipedia.org/wiki/Multi-agent_system">a décadas</a> atrás. Los primeros trabajos en inteligencia artificial distribuida sentaron las bases para agentes capaces de negociar, coordinar y colaborar. Estos primeros sistemas estaban dedicados a <a href="https://www.jasss.org/5/1/7.html">simulaciones sociales</a> a gran escala, <a href="https://arxiv.org/html/2410.09403v1">investigación académica</a> y <a href="https://www.researchgate.net/publication/334765661_Generation_Expansion_Planning_Considering_Investment_Dynamic_of_Market_Participants_Using_Multi-agent_System">gestión de redes eléctricas</a>.</p><p>Con la llegada de la disponibilidad de LLMs y la reducción del costo operativo, los sistemas multiagente estuvieron disponibles para los mercados "prosumers", con el respaldo de Google y la comunidad de investigación en IA en general. Ahora conocido como sistemas Agent2Agent, la incorporación del protocolo A2A evolucionó hasta convertir en un estándar moderno diseñado específicamente para la era de múltiples grandes modelos de lenguaje coordinando esfuerzos y tareas.</p><p>El protocolo A2A garantiza una comunicación y coordinación fluidas entre agentes aplicando estándares y principios consistentes a los puntos de interacción donde los LLMs se conectan y comunican. Esta estandarización permite que agentes de diferentes desarrolladores —empleando distintos modelos subyacentes— trabajen juntos de forma eficaz.</p><p>Los protocolos de comunicación no son nuevos y tienen raíces ampliamente arraigadas en casi todas las transacciones digitales realizadas en Internet. Si <a href="https://www.elastic.co/search-labs">escribiste https://www.elastic.co/search-labs</a> en un navegador para acceder a este artículo, las probabilidades son altas de que se ejecutaron protocolos TCP/IP, transporte HTTP y consulta DNS, cerciorando una experiencia de navegación coherente para nosotros.</p><h3>Características clave</h3><p>Los sistemas A2A se basan en varios principios fundamentales para garantizar una comunicación fluida. Construir sobre estos principios garantiza que diferentes agentes, basados en distintos LLMs, frameworks y lenguajes de programación, interactúen de forma fluida.</p><p>Estos son los cuatro principios principales:</p><ul><li><p><strong>Paso de mensajes</strong>: Los agentes se comunican mediante mensajes estructurados con propiedades y formatos bien definidos</p></li><li><p><strong>Coordinación</strong>: Los agentes orquestan flujos de trabajo complejos delegando tareas entre sí y gestionando dependencias sin bloquear a otros agentes</p></li><li><p><strong>Especialización</strong>: Cada agente se centra en un dominio o capacidad específica, convertir en un experto en su área y ofreciendo la realización de tareas basar en ese conjunto de habilidades</p></li><li><p><strong>Estado distribuido</strong>: El estado y el conocimiento se distribuyen entre agentes en lugar de centralizar, con los agentes con la capacidad de actualizar mutuamente sobre el progreso con el estado de la tarea y retornos parciales (artefactos)</p></li></ul><h3>La redacción: Un ejemplo recurrente</h3><p>Imagina una redacción digital impulsada por agentes de IA, cada uno especializado en un aspecto diferente del periodismo:</p><ul><li><p><strong>Jefe de Noticias</strong> (coordinador/cliente): Asigna historias y monitorear el flujo de trabajo</p></li><li><p><strong>Agente reportero</strong>: Escribe artículos basados en investigaciones y entrevistas</p></li><li><p><strong>Agente Investigador</strong>: recopila datos, estadísticas e información de fondo</p></li><li><p><strong>Archive Agent</strong>: Busca en artículos históricos e identifica tendencias usando Elasticsearch</p></li><li><p><strong>Editor Agent</strong>: Reseña artículos para calidad, estilo y optimización SEO</p></li><li><p><strong>Agente editorial</strong>: Publica artículos aprobados en la plataforma de blogs vía CI/CD</p></li></ul><p>Estos agentes no actúan de forma aislada; cuando el Jefe de Noticias asigna una noticia sobre <em>la adopción de energías renovables</em>, el Reportero necesita que el Investigador recopile estadísticas, que el Editor revise el borrador y que el Editor publique el texto final. Esta coordinación se realiza a través de protocolos A2A.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb6c7215a96326481/6a17f2dd445de953024d0243/cc0760dbd74c49b92fa00dafbb8c2e8740eb70b6-963x693.png" alt="" /><h2>Sección 2: comprensión de la arquitectura A2A</h2><h3>Roles de Agente de Cliente y Agente Remoto</h3><p>En la arquitectura A2A, los agentes asumen dos roles principales. El <strong>Agente Cliente</strong> es responsable de formular y comunicar tareas a otros agentes del sistema. Identifica a los agentes remotos y sus capacidades, empleando esta información para tomar decisiones informadas sobre la delegación de tareas. El agente cliente coordina el flujo de trabajo general, cerciorando que las tareas se distribuyan correctamente y que el sistema avance hacia sus objetivos.</p><p>El <strong>Agente Remoto</strong>, en cambio, actúa en tareas delegadas por los clientes. Proporciona información o toma acciones específicas en respuesta a solicitudes, pero no inicia acciones de forma independiente. Los agentes remotos también pueden comunicar con otros agentes remotos según sea necesario para cumplir con sus responsabilidades asignadas, creando una red colaborativa de capacidades especializadas.</p><p>En nuestra redacción, el Jefe de Noticias actúa como agente cliente, mientras que el Reportero, el Investigador, el Editor y el Editor son agentes remotos que responden a las solicitudes y coordinan entre sí.</p><h3>Capacidades básicas de A2A</h3><p>Los protocolos A2A definen varias capacidades que permiten la colaboración multiagente:</p><h4>1. Descubrimiento</h4><p>Los servidores A2A deben anunciar sus capacidades para que los clientes sepan cuándo y cómo emplearlas para tareas específicas. Esto se logra mediante Tarjetas de Agente—documentos JSON que describen las habilidades, entradas y salidas de un agente. Las Tarjetas de Agente están disponibles en puntos finales consistentes y conocidos (como el recomendado <code>/.well-known/agent-card.json</code> endpoint), permitiendo a los clientes descubrir y consultar las capacidades de un agente antes de iniciar la colaboración.</p><p>A continuación hay un ejemplo de Tarjeta de Agente para el Agente de Archivo personalizado de Elastic, "Archie Archivist". Ten en cuenta que proveedores de software como Elastic alojan sus agentes A2A y proporcionan una URL para el acceso:</p>{
  "name": "Archie Archivist",
  "description": "Helps find historical news documents in the Elasticsearch Index of archived news articles and content.",
  "url": "https://xxxxxxxxxxxxx-abc123.kb.us-central1.gcp.elastic.cloud/api/agent_builder/a2a/archive-agent",
  "provider": {
    "organization": "Elastic",
    "url": "https://elastic.co"
  },
  "version": "0.1.0",
  "protocolVersion": "0.3.0",
  "preferred_transport": "JSONRPC",
  "documentationURL": "https://www.elastic.co/docs/solutions/search/agent-builder/a2a-server"
  "capabilities": {
    "streaming": false,
    "pushNotifications": false,
    "stateTransitionHistory": false
  },
  "skills": [
    {
      "id": "platform.core.search",
      "name": "platform.core.search",
      "description": "A powerful tool for searching and analyzing data within your Elasticsearch cluster.",
      "inputModes": ["text/plain", "application/json"],
      "outputModes": ["text/plain", "application/json"]
    },
    {
      "id": "platform.core.index_explorer",
      "name": "platform.core.index_explorer",
      "description": "List relevant indices, aliases and datastreams based on a natural language query.",
      "inputModes": ["text/plain", "application/json"],
      "outputModes": ["text/plain", "application/json"]
    }
  ],
  "defaultInputModes": ["text/plain"],
  "defaultOutputModes": ["text/plain"]
}<p>Esta Tarjeta de Agente revela varios aspectos importantes del Agente de Archivo de Elastic. El agente se identifica como "Archie Archivist" y deja claro su propósito: ayudar a encontrar documentos históricos de noticias en un índice de Elasticsearch. La tarjeta especifica el proveedor (Elastic) y la versión del protocolo (0.3.0), cerciorando la compatibilidad con otros agentes compatibles con A2A. Lo más importante es que el <code>skills</code> array enumera las capacidades específicas que ofrece este agente, incluyendo una poderosa funcionalidad de búsqueda y una exploración inteligente de índices. Cada habilidad define qué modos de entrada y salida soporta, permitiendo a los clientes entender exactamente cómo comunicar con este agente. Este agente deriva del servicio Agent Builder de Elastic, que ofrece un conjunto de herramientas nativas respaldadas por LLM y endpoints API para mantener una conversación con tu almacén de datos, no solo para recuperarlo de él. El acceso a los agentes A2A en Elasticsearch puede <a href="https://www.elastic.co/docs/solutions/search/agent-builder/a2a-server">encontrar aquí</a>.</p><h4>2. Negociación</h4><p>Clientes y agentes deben acordar los métodos de comunicación —ya sea que las interacciones se realicen mediante texto, formularios, iframes o incluso audio/video— para garantizar una interacción adecuada del usuario y el intercambio de datos. Esta negociación ocurre al inicio de la colaboración entre agentes y establece los protocolos que gobernarán su interacción a lo largo del flujo de trabajo. Por ejemplo, un agente de atención al cliente basado en voz podría negociar para comunicar mediante flujos de audio, mientras que un agente de análisis de datos podría preferir JSON estructurado. El proceso de negociación garantiza que ambas partes puedan intercambiar información de forma eficaz en un formato que se adapte a sus capacidades y a los requisitos de la tarea en cuestión.</p><p>Las capacidades listadas en el fragmento JSON anterior tienen todas esquemas de entrada y salida; Estos establecen una expectativa de cómo interactuar con este agente por parte de otros agentes.</p><h4>3. Gestión de tareas y estado</h4><p>Los clientes y agentes necesitan mecanismos para comunicar el estado de la tarea, cambios y dependencias a lo largo de la ejecución. Esto incluye gestionar todo el ciclo de vida de una tarea, desde su creación y asignación hasta las actualizaciones de progreso y los cambios de estado. Los estados típicos incluyen estados pendientes, en curso, completados o fallidos. El sistema también debe hacer un seguimiento de las dependencias entre tareas para cerciorar que el trabajo previo se complete antes de que comiencen las tareas dependientes. El manejo de errores y la lógica de reintentos también son componentes esenciales, que permiten al sistema recuperar de forma elegante tras fallos y seguir avanzando hacia el objetivo principal.</p><p>Ejemplo de mensaje de tarea:</p>{
  "message_id": "msg_789xyz",
  "message_type": "task_request",
  "sender": "news_chief",
  "receiver": "researcher_agent",
  "timestamp": "2025-09-30T10:15:00Z",
  "payload": {
    "task_id": "task_456abc",
    "capability": "fact_gathering",
    "parameters": {
      "query": "renewable energy adoption rates in Europe 2024",
      "sources": ["eurostat", "iea", "ember"],
      "depth": "comprehensive"
    },
    "context": {
      "story_id": "story_123",
      "deadline": "2025-09-30T18:00:00Z",
      "priority": "high"
    }
  }
}<p>Este mensaje de tarea de ejemplo demuestra varios aspectos clave de la comunicación A2A.</p><ul><li><p>La estructura <strong>del mensaje</strong> incluye metadatos como un identificador único del mensaje, el tipo de mensaje enviado, la identificación del remitente y receptor, y una marca de tiempo para el seguimiento y la depuración.</p></li><li><p>La <strong>carga útil</strong> contiene la información real de la tarea, especificando qué capacidad se está invocando en el agente remoto y proporcionando los parámetros necesarios para ejecutar dicha capacidad.</p></li><li><p>La sección <strong>de contexto</strong> proporciona información adicional que ayuda al agente receptor a comprender el flujo de trabajo más amplio, incluyendo plazos y niveles de prioridad que informan sobre cómo debe asignar sus recursos y programar su trabajo.</p></li></ul><h4>4. Colaboración</h4><p>Clientes y agentes <strong>deben</strong> apoyar una interacción dinámica pero estructurada, permitiendo a los agentes aplicar aclaraciones, información o subacciones al cliente, otros agentes o usuarios. Esto crea un entorno colaborativo donde los agentes pueden hacer preguntas de seguimiento cuando las instrucciones iniciales son ambiguas, aplicar contexto adicional para tomar mejores decisiones, delegar subtareas a otros agentes con experiencia más adecuada y proporcionar resultados intermedios para recibir retroalimentación antes de proceder con la tarea completa. Esta comunicación multidireccional cerciora que los agentes no actúen de forma aislada, sino que participen en un diálogo continuo que conduce a mejores resultados.</p><h3>Comunicación distribuida entre pares</h3><p>A2A permite la comunicación distribuida donde los agentes pueden ser alojados por diferentes organizaciones, con algunos agentes mantenidos internamente mientras que otros son proporcionados por servicios externos. Estos agentes pueden funcionar en diferentes infraestructuras, potencialmente abarcando múltiples proveedores de nube o centros de datos locales. Pueden usar diferentes LLMs subyacentes, con algunos agentes impulsados por modelos GPT, otros por Claude y otros más por alternativas de código abierto. Los agentes incluso podrían operar en diferentes regiones geográficas para cumplir con los requisitos de soberanía de datos o reducir la latencia. A pesar de esta diversidad, todos los agentes acuerdan un protocolo de comunicación común para el intercambio de información, garantizando la interoperabilidad independientemente de los detalles de la implementación. Esta arquitectura distribuida ofrece flexibilidad en la construcción y despliegue de los sistemas, permitiendo a las organizaciones combinar los mejores agentes e infraestructuras según sus necesidades específicas.</p><p>Esta es la arquitectura final de la aplicación de redacción:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt74d59cd9267f54d8/6a17f2de505ac31129ad8c71/82e01a0d9746038eafd69d11177042b5390507ae-1600x838.png" alt="" /><h2>Sección 3: Protocolo de Contexto de Modelo (MCP)</h2><h3>Definición y propósito</h3><p>El Protocolo de Contexto del Modelo (MCP) es un protocolo estandarizado desarrollado por Anthropic para mejorar y potenciar un LLM individual con herramientas, recursos y prompts definidos por el usuario, entre otras adiciones suplementarias a la base de código. MCP proporciona una interfaz universal entre los modelos de lenguaje y los recursos externos que necesitan para completar tareas de manera eficaz. Este <a href="https://www.elastic.co/search-labs/blog/mcp-current-state">artículo</a> describe el estado actual de MCP con ejemplos de casos de uso, tendencias emergentes y la propia implementación de Elastic.</p><h3>Conceptos centrales de MCP</h3><p>MCP opera sobre una arquitectura cliente-servidor con tres componentes principales:</p><ul><li><p><strong>Clientes:</strong> aplicaciones (como Claude Desktop o aplicaciones de IA personalizadas) que se conectan a servidores MCP para acceder a sus capacidades.</p></li><li><p><strong>Servidores</strong>: aplicaciones que exponen recursos, herramientas y prompts a modelos de lenguaje. Cada servidor se especializa en proporcionar acceso a capacidades o fuentes de datos específicas.</p><ul><li><p><strong>Herramientas</strong>: funciones definidas por el usuario que los modelos pueden invocar para realizar acciones, como bases de datos de búsqueda, llamar a APIs externas o transformaciones de ejecución sobre datos</p></li><li><p><strong>Recursos:</strong> fuentes de datos de las que los modelos pueden leer, que se sirven con datos dinámicos o estáticos y se acceden mediante patrones URI (similares a rutas REST)</p></li><li><p><strong>Prompts: </strong>plantillas reutilizables de prompts con variables que guían el modelo para realizar tareas específicas.</p></li></ul></li></ul><h3>Patrón de petición-respuesta</h3><p>MCP sigue un patrón de interacción petición-respuesta familiar similar a las APIs REST. El cliente (LLM) aplicar un recurso o invoca una herramienta, luego el servidor MCP procesa la solicitud y devuelve el resultado, que el LLM emplea para continuar su tarea. Este modelo centralizado con servidores periféricos proporciona un patrón de integración más sencillo en comparación con la comunicación entre agentes entre iguales.</p><h3>MCP en la redacción</h3><p>En nuestro ejemplo de redacción, los agentes individuales emplean servidores MCP para acceder a las herramientas y datos que necesitan:</p><ul><li><p><strong>Utilizaciones de Researcher Agent</strong> :</p><ul><li><p>Servidor MCP de la API de Noticias (acceso a bases de datos de noticias)</p></li><li><p>Verificación de hechos del servidor MCP (verificar afirmaciones frente a fuentes confiables)</p></li><li><p>Servidor MCP de la base de datos académica (artículos académicos e investigaciones)</p></li></ul></li><li><p><strong>Reporter Agent</strong> emplea:</p><ul><li><p>Guía de estilo MCP Server (estándares de redacción para redacción)</p></li><li><p>Servidor MCP de plantillas (plantillas y formatos de artículos)</p></li><li><p>Servidor MCP de Biblioteca de Imágenes (fotos de archivo y gráficos)</p></li></ul></li><li><p><strong>Editor Agent</strong> emplea:</p><ul><li><p>Grammar Checker MCP Server (herramientas de calidad del lenguaje)</p></li><li><p>Servidor MCP de detección de plagio (verificación de originalidad)</p></li><li><p>Análisis SEO Servidor MCP (optimización de titulares y palabras clave)</p></li></ul></li><li><p><strong>Utilizaciones del Agente de la Editorial</strong> :</p><ul><li><p>CMS MCP Server (API del sistema de gestión de contenidos)</p></li><li><p>Servidor CI/CD MCP (Pipeline de despliegue)</p></li><li><p>Servidor MCP de Análisis (seguimiento y monitorización)</p></li></ul></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt195fe0bd36d36a48/6a17f2e0b1e113afe479f36c/b67311e3b58b27f9eb1b42a7b1dbad47ef3be4ad-808x535.png" alt="" /><h2>
Sección 4: comparación de arquitectura</h2><h3>Cuándo usar A2A</h3><p>La arquitectura A2A destaca en <strong>escenarios que requieren colaboración genuina entre múltiples agentes</strong>. Los flujos de trabajo de varios pasos que requieren coordinación se benefician enormemente de A2A, especialmente cuando las tareas implican múltiples pasos secuenciales o paralelos, flujos de trabajo que requieren iteración y refinamiento, y procesos con puntos de control y necesidades de validación. En nuestro ejemplo de redacción, el flujo de trabajo de la historia requiere que el Reportero escriba, pero puede que tenga que iterar de nuevo al Investigador si la confianza en ciertos hechos es baja, luego proceder al Editor y finalmente al Editor.</p><p><strong>La especialización específica de dominio en múltiples áreas</strong> es otro caso de uso estable para A2A. Cuando se necesitan múltiples expertos en distintos campos para realizar una tarea mayor, con cada agente aportando un profundo conocimiento del dominio y capacidades de razonamiento especializadas para distintos aspectos, A2A proporciona el marco de coordinación necesario para establecer esas conexiones. La redacción lo ejemplifica perfectamente: el investigador se especializa en la recopilación de información, el reportero en la escritura y el editor en el control de calidad, cada uno con una experiencia distinta.</p><p>La necesidad de un comportamiento autónomo de agentes hace que A2A sea especialmente valioso. Los agentes que pueden<strong> tomar decisiones independientes, mostrar un comportamiento proactivo basado en condiciones cambiantes y adaptar dinámicamente a los requisitos del flujo de trabajo</strong> prosperan en una arquitectura A2A. La escalada horizontal de funciones especializadas es otro beneficio clave: en lugar de tener un único maestro de todos los oficios, múltiples agentes especializados trabajan coordinados, y múltiples instancias del mismo agente pueden manejar subtareas de forma asíncrona. Durante noticias de última hora en nuestra redacción, por ejemplo, varios agentes de Reporter pueden trabajar simultáneamente en diferentes ángulos de la misma historia.</p><p>Por último, las tareas que requieren colaboración genuina entre múltiples agentes son ideales para A2A. Esto incluye mecanismos <a href="https://arxiv.org/abs/2404.18796">de evaluación de los LLM como jurado</a> , sistemas de construcción de consenso y votación, y <strong>la resolución colaborativa de problemas donde se necesitan múltiples perspectivas</strong> para alcanzar el mejor resultado.</p><h3>Cuándo usar MCP</h3><p>El Protocolo de Contexto del Modelo es ideal para ampliar las capacidades de un solo modelo de IA. Cuando un único modelo de IA necesita acceso a múltiples herramientas y fuentes de datos, MCP ofrece la solución perfecta con razonamiento centralizado combinado con herramientas distribuidas e integración sencilla de herramientas. En nuestro ejemplo de redacción, el Agente de Investigación (un modelo) necesita acceso a múltiples fuentes de datos, incluyendo News API, servicios de verificación de hechos y bases de datos académicas, todo ello accedido a través de servidores MCP estandarizados.</p><p>La integración estandarizada de herramientas se convierte en una prioridad cuando la ampliación de la compartición y la reutilización de las integraciones de herramientas importan. MCP destaca aquí con su ecosistema de servidores MCP preconstruidos que reducen significativamente el tiempo de desarrollo para integraciones comunes. Cuando se requiere simplicidad y mantenibilidad, los patrones de solicitud-respuesta de MCP son familiares para los desarrolladores, más fáciles de entender y depurar que los sistemas distribuidos, y tienen una menor complejidad operativa.</p><p>Por último, MCP suele ser ofrecido por proveedores de software para facilitar la comunicación remota con sus sistemas. Estos servidores MCP ofrecidos por el proveedor reducen significativamente el tiempo de incorporación y desarrollo, al tiempo que ofrecen una interfaz estandarizada con sistemas propietarios, haciendo que la integración sea mucho más sencilla que el desarrollo de APIs personalizadas.</p><h3>Cuándo usar ambos (MCP de A2A ❤️)</h3><p>Muchos sistemas sofisticados se benefician de combinar A2A y MCP, como se indica en la <a href="https://a2a-protocol.org/latest/topics/a2a-and-mcp/">documentación de A2A sobre integración MCP</a>. Los sistemas que requieren tanto coordinación como estandarización son candidatos ideales para un enfoque híbrido. A2A se encarga de la coordinación de agentes y la orquestación de flujos de trabajo, mientras que MCP proporciona acceso a herramientas individuales a los agentes. En nuestro ejemplo de redacción, los agentes coordinan mediante A2A; con el flujo de trabajo pasando de reportero a investigador, luego editor y luego editor. Sin embargo, cada agente emplea servidores MCP para sus herramientas especializadas, creando una separación arquitectónica limpia.</p><p>Múltiples agentes especializados, cada uno usando MCP para acceder a herramientas, representan un patrón común donde hay una capa de coordinación de agentes gestionada por A2A y una capa de acceso a herramientas gestionada por MCP. Esta clara separación de preocupaciones facilita la comprensión y el mantenimiento de los sistemas.</p><p>Los beneficios de combinar ambos enfoques son sustanciales. Obtienes los beneficios organizacionales de los sistemas multiagente, incluyendo especialización, autonomía y procesamiento en paralelo, además de disfrutar de los beneficios de estandarización y ecosistema del MCP, como la integración de herramientas y el acceso a recursos. Hay una clara separación entre la coordinación de agentes (A2A) y el acceso a recursos (MCP), y lo importante es que A2A no es necesario solo para tareas pequeñas como el acceso a API—MCP las gestiona de forma eficiente sin la sobrecarga de la orquestación multiagente.</p><p><strong>Preguntas frecuentes: A2A vs. MCP - Casos de uso</strong></p><p>Característica</p><p>Agent2Agent (A2A)</p><p>Protocolo de Contexto de Modelo (MCP)</p><p>Híbrido (A2A + MCP)</p><p>Objetivo principal</p><p>Coordinación Multiagente: Permite que un equipo de agentes especializados trabaje juntos en flujos de trabajo complejos y de varios pasos.</p><p>Mejora de un solo agente: Extiende la capacidad de un único LLM/Agente con herramientas, recursos y datos externos.</p><p>Fuerza combinada: A2A gestiona el flujo de trabajo del equipo, mientras que MCP proporciona herramientas a cada miembro del equipo.</p><p>Ejemplo de equipo de redacción</p><p>La cadena de flujo de trabajo: Jefe de Noticias → Reportero → Investigador → Editor → Editor. Esta es la capa de coordinación.</p><p>Herramientas individuales del agente: El Agente Reportero accede al servidor de la guía de estilo y al servidor de plantillas (a través de MCP). Esta es la capa de acceso a herramientas.</p><p>El sistema completo: el reportero coordina con el editor (A2A), y el reportero emplea el servidor MCP de la Biblioteca de Imágenes para encontrar un gráfico para la historia.</p><p>Cuándo usar cuál</p><p>Cuando necesitas colaboración genuina, iteración y refinamiento, o experiencia especializada repartida entre varios agentes.</p><p>Cuando un solo agente necesita acceso a múltiples herramientas y fuentes de datos o requiere integración estandarizada con sistemas propietarios.</p><p>Cuando necesitas los beneficios organizacionales de los sistemas multiagente y los beneficios de estandarización y ecosistema del MCP.</p><p>Beneficio fundamental</p><p>Autonomía y escalado: Los agentes pueden tomar decisiones independientes, y el sistema permite la escalado horizontal de funciones especializadas.</p><p>Simplicidad y estandarización: Más fácil de depurar y mantener gracias al razonamiento centralizado, y proporciona una interfaz universal para los recursos.</p><p>Separación clara de preocupaciones: Facilita la comprensión del sistema: A2A = trabajo en equipo, MCP = acceso a la herramienta.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1735ea5de41e10fd/6a17f2e26864a4125cb688c4/ddf6a29b1107ac6a63e94ecef703abc561a29e1e-986x656.png" alt="" /><h2>Conclusión</h2><p>Esta es la primera sección de dos partes que cubren la implementación de agentes basados en A2A reforzados con servidores MCP para proporcionar soporte y acceso externo a datos y herramientas. El siguiente artículo explorará el código real para demostrar que trabajan juntos para emular las actividades en una redacción online. Aunque ambos sistemas son extremadamente capaces y flexibles por sí mismos, verás cuánto se complementan cuando trabajan en conjunto.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/a2a-protocol-mcp-llm-agent-newsroom-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/a2a-protocol-mcp-llm-agent-newsroom-elasticsearch</guid>
    <category><![CDATA[AI agéntica]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Justin Castilla]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2716d804698ec878/6a17f2e41480095fd7b48888/9f938d8e2f0fdf7509edf028816c48bdbc8b3fc7-1600x900.png" length="0" type="image/png"/>
    <pubDate>Thu, 13 Nov 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Construir un agente de conocimiento con recordación semántica usando Mastra y Elasticsearch]]></title>
    <description><![CDATA[Aprende a construir un agente de conocimiento con recordación semántica usando Mastra y Elasticsearch como almacén vectorial para la recuperación de memoria e información.]]></description>
    <content:encoded><![CDATA[<p><a href="https://www.elastic.co/search-labs/blog/context-engineering-overview">La Ingeniería del Contexto</a> está ganando cada vez más importancia para construir agentes y arquitecturas de IA fiables. A medida que los modelos mejoran, su eficacia y fiabilidad dependen menos de sus datos capacitados y más de lo bien que estén fundamentados en el contexto adecuado. Los agentes que pueden recuperar y aplicar la información más relevante en el momento adecuado tienen muchas más probabilidades de producir resultados precisos y fiables.</p><p>En este blog, emplearemos <a href="https://mastra.ai/">Mastra</a> para construir un agente de conocimiento que recuerda lo que dicen los usuarios y puede recuperar información relevante más adelante, empleando Elasticsearch como backend de memoria y recuperación. Puedes extender fácilmente este mismo concepto a casos de uso reales, piensa en agentes de soporte que puedan recordar conversaciones y resoluciones pasadas, permitiéndoles adaptar las respuestas a usuarios específicos o a soluciones superficiales más rápido basar en contextos previos.</p><p>Sigue aquí para ver cómo construirlo paso a paso. Si te pierdes o simplemente quieres ejecutar un ejemplo terminado, echa un vistazo al <a href="https://github.com/jdarmada/getting-started-mastra-elastic/tree/main">repositorio aquí</a>.</p><h2>¿Qué es Mastra?</h2><p>Mastra es un framework TypeScript de código abierto para construir agentes de IA con partes intercambiables para razonamiento, memoria y herramientas. Su función <a href="https://mastra.ai/docs/memory/semantic-recall">de recuperación semántica</a> permite a los agentes recordar y recuperar interacciones pasadas almacenando mensajes como incrustaciones en una base de datos vectorial. Esto permite a los agentes mantener el contexto y la continuidad de la conversación a largo plazo. Elasticsearch es un excelente almacén vectorial para habilitar esta función, ya que soporta una búsqueda vectorial densa eficiente. Cuando se activa la recuperación semántica, el agente extrae mensajes pasados relevantes en la ventana de contexto del modelo, permitiendo que el modelo emplee ese contexto recuperado como base para su razonamiento y respuestas.</p><h2>Lo que necesitas para empezar</h2><ul><li><p>Nodo v18+</p></li><li><p>Elasticsearch (versión 8.15 o posterior)</p></li><li><p>Clave API de Elasticsearch</p></li><li><p><a href="https://help.openai.com/en/articles/4936850-where-do-i-find-my-openai-api-key">Clave API de OpenAI</a></p></li></ul><p>Nota: Necesitarás esto porque la demo usa el proveedor OpenAI, pero Mastra soporta otros SDKs de IA y proveedores de modelos comunitarios, así que puedes cambiarlo fácilmente según tu configuración.</p><h2>Construyendo un proyecto de Mastra</h2><p>Emplearemos la CLI integrada de Mamra para proporcionar el andamiaje de nuestro proyecto. Ejecuta el comando:</p>npm create mastra@latest<p>Recibirás un conjunto de indicaciones, que empiezan por:</p><p>1. Pon un nombre a tu proyecto.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt87f941f654d03827/6a16f7af67045b214d45bfa1/2b9fe559e0276140dd539e24f916a73c60870405-620x84.png" alt="Nombrar un prompt en la app Mastra" /><p>2. Podemos mantener este valor predeterminado; No dudes en dejar esto en blanco.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7dbb3d4f27435cac/6a16f7b0cdacbf29497d27de/e04729eb03bce8499e973e18c28642402340d0e5-852x68.png" alt="Indicándole a Mastra dónde almacenar los archivos de prompt" /><p>3. Para este proyecto, emplearemos un modelo proporcionado por OpenAI.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1f654f6cb9397e94/6a16f7b2964cea899a08b942/a86596a469a71bdf8bd99cbaf528d0f0cf7272c0-436x222.png" alt="Selección de un modelo proporcionado por OpenAI en Mastra" /><p>4. Selecciona la opción "Saltar por ahora" porque almacenaremos todas nuestras variables de entorno en un archivo '.env' que configuraremos en un paso posterior.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltff106117521a3519/6a16f7b3c1e8a5031af880d8/02b19ccc34af0bdacf52fd94b519d036540ca2e6-426x114.png" alt="Seleccionando saltar por ahora para la clave OpenAI" /><p>5. También podemos saltar esta opción.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcda7d9c51c3878d7/6a16f7b450916809dbe1b892/b3fe63d19d270bc2e0de1dd92033bf8b26750819-990x208.png" alt="" /><p>Una vez que termines de inicializar, podemos pasar al siguiente paso.</p><h3>Instalación de dependencias</h3><p>A continuación, necesitamos instalar algunas dependencias:</p>npm install ai @ai-sdk/openai @elastic/elasticsearch dotenv<ul><li><p><code>ai</code> - Paquete básico de SDK de IA que proporciona herramientas para gestionar modelos de IA, prompts y flujos de trabajo en JavaScript/TypeScript. Mastra está construido sobre el <a href="https://ai-sdk.dev/">SDK de IA</a> por Vercel, así que necesitamos esta dependencia para permitir la interacción del modelo con tu agente.</p></li><li><p><code>@ai-sdk/openai</code> - Plugin que conecta el SDK de IA con modelos OpenAI (como GPT-4, GPT-4o, etc.), habilitando llamadas API usando tu clave API OpenAI.</p></li><li><p><code>@elastic/elasticsearch</code> - <a href="https://www.elastic.co/docs/reference/elasticsearch/clients/javascript">Cliente oficial de Elasticsearch para Node.js</a>, se emplea para conectarse a tu Elastic Cloud o a un clúster local para operaciones de indexación, búsqueda y vectores.</p></li><li><p><code>dotenv</code> - Carga variables de entorno desde un .env archivar en process.env, permitiendo inyectar de forma segura credenciales como claves API y endpoints Elasticsearch.</p></li></ul><h3>Configuración de variables de entorno</h3><p>Crea un archivo <code>.env</code> en el directorio raíz de tu proyecto si aún no ves uno. Alternativamente, puedes copiar y renombrar el ejemplo <code>.env</code> que proporcioné en el <a href="https://github.com/jdarmada/getting-started-mastra-elastic/blob/main/.env.example">repositorio</a>. En este archivo, podemos agregar las siguientes variables:</p>ELASTICSEARCH_ENDPOINT="your-endpoint-here"
ELASTICSEARCH_API_KEY="your-key-here"
OPENAI_API_KEY="your-key-here"<p>Eso concluye la configuración básica. Desde aquí, ya puedes empezar a construir y orquestar agentes. Vamos un paso más allá y agregaremos Elasticsearch como la capa de almacenamiento y búsqueda vectorial.</p><h2>Agregar Elasticsearch como almacenamiento vectorial</h2><p>Crea una nueva carpeta llamada <code>stores</code> y dentro, agrega este <a href="https://github.com/jdarmada/getting-started-mastra-elastic/blob/main/src/mastra/stores/elastic-store.ts">archivo</a>. Antes de que Mastra y Elastic lanzaran una integración oficial de almacenamiento vectorial de Elasticsearch, <a href="https://github.com/abhiaiyer91">Abhi Aiyer</a>(CTO de Mestra) compartió esta clase prototipo temprana llamada <code>ElasticVector</code>. Simplemente, conecta la abstracción de memoria de Mamra con las densas capacidades vectoriales de Elasticsearch, para que los desarrolladores puedan incluir Elasticsearch como base de datos vectorial para sus agentes.</p><p>Echemos un vistazo más profundo a las partes importantes de la integración:</p><h3>Ingestión del cliente Elasticsearch</h3><p>Esta sección define la clase <code>ElasticVector</code> y configura la conexión cliente de Elasticsearch con soporte tanto para despliegues estándar como serverless.</p>export interface ElasticVectorConfig extends ClientOptions {
    /**
     * Explicitly specify if connecting to Elasticsearch Serverless.
     * If not provided, will be auto-detected on first use.
     */
    isServerless?: boolean;
    
    /**
     * Maximum documents to count accurately when describing indices.
     * Higher values provide accurate counts but may impact performance on large indices.
     * 
     * @default 10000
     */
    maxCountAccuracy?: number;
}

export class ElasticVector extends MastraVector {
    private client: Client;
    private isServerless: boolean | undefined;
    private deploymentChecked: boolean = false;
    private readonly maxCountAccuracy: number;

    constructor(config: ElasticVectorConfig) {
        super();
        this.client = new Client(config);
        this.isServerless = config.isServerless;
        this.maxCountAccuracy = config.maxCountAccuracy ?? 10000;
    }
}<ul><li><p><code>ElasticVectorConfig extends ClientOptions</code>: Esto crea una nueva interfaz de configuración que hereda todas las opciones del cliente de Elasticsearch (como <code>node</code>, <code>auth</code>, <code>requestTimeout</code>) y agrega nuestras propiedades personalizadas. Esto significa que los usuarios pueden pasar cualquier configuración válida de Elasticsearch junto con nuestras opciones específicas para serverless.</p></li><li><p><code>extends MastraVector</code>: Esto permite <code>ElasticVector</code> heredar de la clase base de <code>MastraVector</code> de Mastra, que es una interfaz común a la que se ajustan todas las integraciones de almacenamiento vectorial. Esto garantiza que Elasticsearch se comporte como cualquier otro backend de vectores Mastra desde la perspectiva del agente.</p></li><li><p><code>private client: Client</code>: Esta es una propiedad privada que contiene una instancia del cliente JavaScript Elasticsearch. Esto permite que la clase hable directamente con tu grupo.</p></li><li><p><code>isServerless</code> y <code>deploymentChecked</code>: Estas propiedades trabajan juntas para detectar y almacenar en caché si estamos conectados a un despliegue serverless o estándar de Elasticsearch. Esta detección ocurre automáticamente en el primer uso, o puede configurar explícitamente.</p></li><li><p><code>constructor(config: ClientOptions)</code>: Este constructor toma un objeto de configuración (que contiene tus credenciales de Elasticsearch y configuraciones opcionales de serverless) y lo emplea para inicializar el cliente en la línea <code>this.client = new Client(config)</code>.</p></li><li><p><code>super()</code>: Esto llama constructor base de Mastra, por lo que hereda el registro, los asistentes de validación y otros ganchos internos.</p></li></ul><p>En este punto, Mastra sabe que hay un nuevo almacén vectorial llamado <code>ElasticVector</code></p><h3>Detección del tipo de despliegue</h3><p>Antes de crear índices, el adaptador detecta automáticamente si estás usando Elasticsearch estándar o Elasticsearch Serverless. Esto es importante porque los despliegues serverless no permiten la configuración manual de shards.</p>private async detectServerless(): Promise&lt;boolean&gt; {
    // Return cached result if already detected
    if (this.deploymentChecked) {
        return this.isServerless ?? false;
    }

    // Use explicit configuration if provided
    if (this.isServerless !== undefined) {
        this.deploymentChecked = true;
        this.logger?.info(
            `Using explicit deployment type: ${this.isServerless ? 'Serverless' : 'Standard'}`
        );
        return this.isServerless;
    }

    try {
        const info = await this.client.info();
        
        // Primary detection: build flavor (most reliable)
        const isBuildFlavorServerless = info.version?.build_flavor === 'serverless';
        
        // Secondary detection: tagline (fallback)
        const isTaglineServerless = info.tagline?.toLowerCase().includes('serverless') ?? false;
        
        this.isServerless = isBuildFlavorServerless || isTaglineServerless;
        this.deploymentChecked = true;
        
        this.logger?.info(
            `Auto-detected ${this.isServerless ? 'Serverless' : 'Standard'} Elasticsearch deployment`,
            { 
                buildFlavor: info.version?.build_flavor, 
                version: info.version?.number,
                detectionMethod: isBuildFlavorServerless ? 'build_flavor' : 'tagline'
            }
        );
        
        return this.isServerless;
    } catch (error) {
        this.logger?.warn(
            'Could not auto-detect deployment type, assuming Standard Elasticsearch. ' +
            'Set isServerless: true explicitly in config if using Serverless.',
            { error: error instanceof Error ? error.message : String(error) }
        );
        this.isServerless = false;
        this.deploymentChecked = true;
        return false;
    }
}<p>Qué pasa:</p><ul><li><p>Primero comprueba si pusiste explícitamente <code>isServerless</code> en la configuración (se salta la auto-detección).</p></li><li><p>Llama a la API <code>info()</code> de Elasticsearch para obtener información del clúster</p></li><li><p>Comprueba el <code>build_flavor field</code> (los despliegues serverless devuelven <code>serverless</code>)</p></li><li><p>Vuelve a revisar el lema si no hay variedad de build disponible</p></li><li><p>Almacena en caché el resultado para evitar llamadas repetidas a la API</p></li><li><p>Por defecto se aplica al despliegue estándar si falla la detección</p></li></ul><p> Ejemplo de uso:</p>// Option 1: Auto-detect (recommended)
const vector = new ElasticVector({
    node: 'https://your-cluster.es.cloud',
    auth: { apiKey: 'your-api-key' }
});
// Detection happens automatically on first index operation

// Option 2: Explicit configuration (faster startup)
const vector = new ElasticVector({
    node: 'https://your-serverless.es.cloud',
    auth: { apiKey: 'your-api-key' },
    isServerless: true  // Skips auto-detection
});<h3>Creación del almacén de "memoria" en Elasticsearch</h3><p>La función siguiente establece un índice Elasticsearch para almacenar incrustaciones. Comprueba si el índice ya existe. Si no, crea uno con el mapeo que aparece abajo y contiene un campo <code>dense_vector</code> para almacenar incrustaciones y métricas de similitud personalizadas.</p><p>Algunas cosas a tener en cuenta:</p><ul><li><p>El parámetro <code>dimension</code> es la longitud de cada vector de incrustación, que depende del modelo de incrustación que estés usando. En nuestro caso, generaremos incrustaciones usando el modelo <code>text-embedding-3-small</code> de OpenAI, que genera vectores de tamaño <code>1536</code>. Usaremos esto como nuestro valor por defecto.</p></li><li><p>La variable <code>similarity</code> empleada en el mapeo a continuación se define a partir de la función auxiliar c<code>onst similarity = this.mapMetricToSimilarity(metric)</code>, que toma el valor del parámetro <code>metric</code> y lo convierte en una palabra clave compatible con Elasticsearch para la métrica de distancia elegida.</p><ul><li><p>Por ejemplo: Mastra emplea términos generales para similitud vectorial como <code>cosine</code>, <code>euclidean</code>, y <code>dotproduct</code>. Si pasáramos la métrica <code>euclidean</code> directamente al mapeo de Elasticsearch, generaría un error porque Elasticsearch espera que la palabra clave <code>l2_norm</code> represente la distancia euclidiana.</p></li></ul></li><li><p>Compatibilidad sin servidor: El código omite automáticamente los ajustes de shard y réplica para despliegues sin servidor, ya que estos son gestionados automáticamente por Elasticsearch Serverless.</p></li></ul>async createIndex(params: CreateIndexParams): Promise&lt;void&gt; {
    const { indexName, dimension = 1536, metric = 'cosine' } = params;

    try {
        const exists = await this.client.indices.exists({ index: indexName });

        if (exists) {
            try {
                await this.validateExistingIndex(indexName, dimension, metric);
                this.logger?.info(`Index "${indexName}" already exists and is valid`);
                return;
            } catch (validationError) {
                throw new Error(
                    `Index "${indexName}" exists but does not match the required configuration: ${
                        validationError instanceof Error ? validationError.message : String(validationError)
                    }`
                );
            }
        }

        const isServerless = await this.detectServerless();
        const similarity = this.mapMetricToSimilarity(metric);

        const indexConfig: any = {
            index: indexName,
            mappings: {
                properties: {
                    vector: {
                        type: 'dense_vector',
                        dims: dimension,
                        index: true,
                        similarity: similarity,
                    },
                    metadata: {
                        type: 'object',
                        enabled: true,
                        dynamic: true, // Allows flexible metadata structures
                    },
                },
            },
        };

        // Only configure shards/replicas for non-serverless deployments
        // Serverless manages infrastructure automatically
        if (!isServerless) {
            indexConfig.settings = {
                number_of_shards: 1,
                number_of_replicas: 0, // Increase for production HA deployments
            };
        }

        await this.client.indices.create(indexConfig);

        this.logger?.info(
            `Created ${isServerless ? 'Serverless' : 'Standard'} Elasticsearch index "${indexName}"`,
            { dimension, metric, similarity }
        );
    } catch (error) {
        const errorMessage = error instanceof Error ? error.message : String(error);
        this.logger?.error(`Failed to create index "${indexName}": ${errorMessage}`);
        throw new Error(`Failed to create index "${indexName}": ${errorMessage}`);
    }
}<h3>Almacenar una nueva recordación o nota tras una interacción</h3><p>Esta función toma nuevas incrustaciones generadas tras cada interacción, junto con los metadatos, y luego las inserta o actualiza en el índice usando la API <code>bulk</code> de Elastic. La API <code>bulk</code> agrupa múltiples operaciones de escritura en una sola solicitud; Esta mejora en nuestro rendimiento de indexación garantiza que las actualizaciones se mantengan eficientes a medida que la memoria de nuestro agente sigue creciendo.</p>async upsert(params: UpsertVectorParams): Promise&lt;string[]&gt; {
    const { indexName, vectors, metadata = [], ids } = params;

    try {
        // Generate unique IDs if not provided
        const vectorIds = ids || vectors.map((_, i) =&gt; 
            `vec_${Date.now()}_${i}_${Math.random().toString(36).substr(2, 9)}`
        );

        const operations = vectors.flatMap((vec, index) =&gt; [
            { index: { _index: indexName, _id: vectorIds[index] } },
            {
                vector: vec,
                metadata: metadata[index] || {},
            },
        ]);

        const response = await this.client.bulk({
            refresh: true,
            operations,
        });

        if (response.errors) {
            const erroredItems = response.items.filter((item: any) =&gt; item.index?.error);
            const erroredIds = erroredItems.map((item: any) =&gt; item.index?._id);
            const errorDetails = erroredItems.slice(0, 3).map((item: any) =&gt; ({
                id: item.index?._id,
                error: item.index?.error?.reason || item.index?.error,
                type: item.index?.error?.type
            }));
            
            const errorMessage = `Failed to upsert ${erroredIds.length}/${vectors.length} vectors`;
            console.error(`${errorMessage}. Sample errors:`, JSON.stringify(errorDetails, null, 2));
            this.logger?.error(errorMessage, { 
                failedCount: erroredIds.length, 
                totalCount: vectors.length,
                sampleErrors: errorDetails 
            });
            
            // Still return successfully inserted IDs
            const successfulIds = vectorIds.filter((id, idx) =&gt; 
                !erroredIds.includes(id)
            );
            
            if (successfulIds.length === 0) {
                throw new Error(`${errorMessage}. All operations failed. See logs for details.`);
            }
            
            return successfulIds;
        }

        this.logger?.info(`Successfully upserted ${vectors.length} vectors to "${indexName}"`);
        return vectorIds;
    } catch (error) {
        const errorMessage = error instanceof Error ? error.message : String(error);
        this.logger?.error(`Failed to upsert vectors to "${indexName}": ${errorMessage}`);
        throw new Error(`Failed to upsert vectors to "${indexName}": ${errorMessage}`);
    }
}<h3>Consulta de vectores similares para la recuperación semántica</h3><p>Esta función es el núcleo de la característica de recuperación semántica. El agente emplea búsqueda vectorial para encontrar incrustaciones almacenadas similares dentro de nuestro índice.</p>async query(params: QueryVectorParams&lt;any&gt;): Promise&lt;QueryResult[]&gt; {
    const { indexName, queryVector, topK = 10, filter, includeVector = false } = params;

    try {
        const knnQuery: any = {
            field: 'vector',
            query_vector: queryVector,
            k: topK,
            num_candidates: Math.max(topK * 10, 100), // Search more candidates for better recall
        };

        // Apply metadata filters if provided
        if (filter) {
            knnQuery.filter = this.buildElasticFilter(filter);
        }

        const sourceFields = ['metadata'];
        if (includeVector) {
            sourceFields.push('vector');
        }

        const response = await this.client.search({
            index: indexName,
            knn: knnQuery,
            size: topK,
            _source: sourceFields,
        });

        const results = response.hits.hits.map((hit: any) =&gt; ({
            id: hit._id,
            score: hit._score || 0,
            metadata: hit._source?.metadata || {},
            vector: includeVector ? hit._source?.vector : undefined,
        }));

        this.logger?.debug(`Query returned ${results.length} results from "${indexName}"`);
        return results;
    } catch (error) {
        const errorMessage = error instanceof Error ? error.message : String(error);
        this.logger?.error(`Failed to query vectors from "${indexName}": ${errorMessage}`);
        throw new Error(`Failed to query vectors from "${indexName}": ${errorMessage}`);
    }
}<p>Bajo el capó:</p><ul><li><p>Ejecuta una consulta <a href="https://www.elastic.co/docs/solutions/search/vector/knn">kNN</a> (k-vecinos más cercanos) usando la API <code>knn</code> en Elasticsearch.</p></li><li><p>Recupera los vectores top-K similares al vector de consulta de entrada.</p></li><li><p>Opcionalmente, aplica filtros de metadatos para reducir resultados (por ejemplo, buscar solo dentro de una categoría o rango de tiempo específico)</p></li><li><p>Devuelve resultados estructurados que incluyen el ID del documento, el puntaje de similitud y los metadatos almacenados.</p></li></ul><h2>Creación del agente del conocimiento</h2><p>Ahora que vimos la conexión entre Mastra y Elasticsearch a través de la integración <code>ElasticVector</code> , creemos el propio Knowledge Agent.</p><p>Dentro de la carpeta <code>agents</code>, crea un archivo llamado <code>knowledge-agent.ts</code>. Podemos empezar conectando nuestras variables de entorno e inicializando el cliente Elasticsearch.</p>import { Agent } from '@mastra/core/agent';
import { Memory } from '@mastra/memory';
import { openai } from '@ai-sdk/openai';
import { Client } from '@elastic/elasticsearch';
import { ElasticVector } from '../stores/elastic-store';
import dotenv from "dotenv";

dotenv.config();

const ELASTICSEARCH_ENDPOINT = process.env.ELASTICSEARCH_ENDPOINT;
const ELASTICSEARCH_API_KEY = process.env.ELASTICSEARCH_API_KEY;

//Error check for undefined credentials
if (!ELASTICSEARCH_ENDPOINT || !ELASTICSEARCH_API_KEY) {
  throw new Error('Missing Elasticsearch credentials');
}

//Check to see if a connection can be established
const testClient = new Client({
  node: ELASTICSEARCH_ENDPOINT,
  auth: { 
    apiKey: ELASTICSEARCH_API_KEY 
  },
});

try {
  await testClient.ping();
  console.log('Connected to Elasticsearch successfully');
} catch (error: unknown) {
  if (error instanceof Error) {
    console.error('Failed to connect to Elasticsearch:', error.message);
  } else {
    console.error('Failed to connect to Elasticsearch:', error);
  }
  process.exit(1);
}
//Initialize the Elasticsearch vector store
const vectorStore = new ElasticVector({
  node: ELASTICSEARCH_ENDPOINT,
  auth: {
    apiKey: ELASTICSEARCH_API_KEY,
  },
//Optional: Explicitly set to true if using Elasticsearch Serverless to skip auto-detection and improve startup time
//isServerless: true,
});<p>Aquí, nosotros:</p><ul><li><p>Usa <code>dotenv</code> para cargar nuestras variables desde nuestro archivo <code>.env</code> .</p></li><li><p>Comprueba si las credenciales de Elasticsearch se están inyectando correctamente y podemos establecer una conexión exitosa con el cliente.</p></li><li><p>Pasa el endpoint de Elasticsearch y la clave API al constructor <code>ElasticVector</code> para crear una instancia de nuestro almacén vectorial que definimos antes.</p></li><li><p>Opcionalmente, especifica <code>isServerless: true</code> si usas Elasticsearch Serverless. Esto omite el paso de detección automática y mejora el tiempo de arranque. Si se omite, el adaptador detectará automáticamente el tipo de despliegue en el primer uso.</p></li></ul><p>A continuación, podemos definir el agente usando la clase <code>Agent</code> de Mastra.</p>export const knowledgeAgent = new Agent({
    name: 'KnowledgeAgent',
    instructions: 'You are a helpful knowledge assistant.',
    model: openai('gpt-4o'),
    memory: new Memory({

        vector: vectorStore,

        //embedder used to create embeddings for each message
        embedder: 'openai/text-embedding-3-small',

        //set semantic recall options
        options: {
            semanticRecall: {
                topK: 3, // retrieve 3 similar messages
                messageRange: 2, // include 2 messages before/after each match
                scope: 'resource',
            },
        },
    }),
});<p>Los campos que podemos definir son:</p><ul><li><p><code>name</code> y <code>instructions</code>: Darle una identidad y función primaria.</p></li><li><p><code>model</code>: Estamos usando la <code>gpt-4o</code> de OpenAI a través del paquete <code>@ai-sdk/openai</code> .</p></li><li><p><code>memory</code>:</p><ul><li><p><code>vector</code>: Apunta a nuestra tienda Elasticsearch, así que los embeddings se almacenan y recuperan desde allí.</p></li><li><p><code>embedder</code>: Qué modelo usar para generar incrustaciones</p></li><li><p><code>semanticRecall</code> Las opciones deciden cómo funciona la retirada:</p><ul><li><p><code>topK</code>: Cuántos mensajes semánticamente similares recuperar.</p></li><li><p><code>messageRange</code>: Cuánto de la conversación incluir en cada partido.</p></li><li><p><code>scope</code>: Define el límite de la memoria.</p></li></ul></li></ul></li></ul><p>Casi termino. Solo tenemos que agregar este agente recién creado a nuestra configuración de Mestra. En el archivo llamado <a href="http://index.ts/"><code>index.ts</code></a>, importa el agente de conocimiento e insértalo en el campo <code>agents</code> .</p>export const mastra = new Mastra({
  agents: { knowledgeAgent },
  storage: new LibSQLStore({
    // stores observability, scores, ... into memory storage, if it needs to persist, change to file:../mastra.db
    url: ":memory:",
  }),
  logger: new PinoLogger({
    name: 'Mastra',
    level: 'info',
  }),
  telemetry: {
    // Telemetry is deprecated and will be removed in the Nov 4th release
    enabled: false, 
  },
  observability: {
    // Enables DefaultExporter and CloudExporter for AI tracing
    default: { enabled: true }, 
  },
});<p>Los otros campos incluyen:</p><ul><li><p><code>storage</code>: Este es el almacén interno de datos de Mamra para historial de ejecuciones, métricas de observabilidad, puntajes y cachés. Para más información sobre el almacenamiento de mastras, visita <a href="https://mastra.ai/docs/server-db/storage">aquí</a>.</p></li><li><p><code>logger</code>: Mastra emplea <a href="https://github.com/pinojs/pino">Pino</a>, que es un registrador JSON estructurado y ligero. Captura eventos como inicios y atajada de agentes, llamadas y resultados de herramientas, errores y tiempos de respuesta de los LLM.</p></li><li><p><code>observability</code>: Controla el rastreo de IA y la visibilidad de ejecución de los agentes. Sigue lo siguiente:</p><ul><li><p>Inicio/final de cada paso de razonamiento.</p></li><li><p>Qué modelo o herramienta se empleó.</p></li><li><p>Entradas y salidas.</p></li><li><p>Puntajes y evaluaciones</p></li></ul></li></ul><h3>Probando al agente con Mastra Studio</h3><p>¡Felicidades! Si llegaste hasta aquí, estás listo para ejecutar este agente y probar sus capacidades semánticas de recuperación. Por suerte, Mastra ofrece una interfaz de chat integrada para que no tengamos que crear la nuestra.</p><p>Para iniciar el servidor de desarrollo de Mestra, abre un terminal y ejecuta el siguiente comando:</p>npm run dev<p>Tras el empaquetado y el arranque inicial del servidor, debería proporcionarte una dirección del Playground.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte5f857fddc74ffc9/6a16f7b6a6c2b995d5e794c0/8b045f70008d26aec4d2e6b59d61085555b9c5b2-686x116.png" alt="Dirección del servidor para Playground" /><p>Pega esta dirección en tu navegador y te recibirás con Mastra Studio.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc7fdda6ce46ce068/6a16f7b7b0367d4f7672bacf/69bc80fe8486edd9e0cf91d87b39f465aeb23111-1600x438.png" alt="Pegar la dirección de Playground para acceder a Mastra Studio" /><p>Selecciona la opción de <code>knowledgeAgent</code> y charla sin parar.</p><p>Para una prueba rápida y ver si todo está correctamente cableado, dale información como: "El equipo anunció que el rendimiento de ventas en octubre subió un 12%, impulsado principalmente por renovaciones empresariales. El siguiente paso es ampliar su alcance a clientes de gama media." Después, inicia un nuevo chat y haz una pregunta como: "¿En qué segmento de clientes dijimos que debemos centrarnos a continuación?" El agente de conocimiento debería ser capaz de recordar la información que le diste en el primer chat. Deberías ver una respuesta como:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfec3266e81a7213b/6a16f7b92b835f6f70f4afe2/da8ebddad89874023ed440a8f1ad2cb04ed043f4-1070x288.png" alt="Al charlar con un agente de conocimiento en Mastra Studio: el agente puede recordar información" /><p>Ver una respuesta así significa que el agente almacenó con éxito nuestro mensaje anterior como incrustaciones en Elasticsearch y lo recuperó después usando búsqueda vectorial.</p><h3>Inspección del almacenamiento de memoria a largo plazo del agente</h3><p>Ve a la pestaña <code>memory</code> en la configuración de tu agente en Mastra Studio. Esto te permite ver lo que tu agente aprendió con el tiempo. Cada mensaje, respuesta e interacción que se incrusta y almacena en Elasticsearch pasa a formar parte de esta memoria a largo plazo. Puedes buscar semánticamente en interacciones pasadas para encontrar rápidamente información o contexto recordado que el agente aprendió antes. Este es esencialmente el mismo mecanismo que emplea el agente durante la recuperación semántica, pero aquí puedes inspeccionarlo directamente. En nuestro ejemplo a continuación, buscamos el término "ventas" y recibimos cada interacción que incluyera algo relacionado con las ventas.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte428134d7bf2a43a/6a16f7bbb0367d185872bad3/3decaa0c332d288c5ae0b11c25f592c7d50c2f0f-1104x1320.png" alt="Cómo inspeccionar el almacenamiento de memoria a largo plazo de los agentes de conocimiento" /><h2>Conclusión</h2><p>Al conectar Mastra y Elasticsearch, podemos dar memoria a nuestros agentes, que es una capa clave en la ingeniería de contexto. Con la memoria semántica, los agentes pueden construir contexto con el tiempo, basando sus respuestas en lo que aprendieron. Eso significa interacciones más precisas, fiables y naturales.</p><p>Esta integración temprana es solo el punto de partida. El mismo patrón aquí puede permitir que los agentes de soporte recuerden tiquetes anteriores, bots internos que recuperen la documentación relevante o asistentes de IA que puedan recuperar detalles de los clientes en medio de una conversación. También estamos trabajando en una integración oficial de Mestra, haciendo que esta pareja sea aún más fluida en un futuro próximo.</p><p>Estamos deseando ver qué construyes a continuación. Pruébalo, explora <a href="https://mastra.ai/">Mastra</a> y sus funciones de memoria, y siéntete libre de compartir lo que descubras con la comunidad.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/knowledge-agent-semantic-recall-mastra-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/knowledge-agent-semantic-recall-mastra-elasticsearch</guid>
    <category><![CDATA[AI agéntica]]></category>
    <category><![CDATA[Experiencia del desarrollador]]></category>
    <category><![CDATA[Integraciones]]></category>
    <dc:creator><![CDATA[JD Armada]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt09afdbff05603865/6a16f7bd839dfabbf2dcfcb5/b8d51c2726d5573385c9246a7821d12ade4f1b0e-720x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Thu, 06 Nov 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Servidor Elastic MCP: Expone las herramientas de Agent Builder a cualquier agente de IA]]></title>
    <description><![CDATA[Descubre cómo emplear el servidor Elastic MCP integrado en Agent Builder para ampliar de forma segura cualquier agente de IA con acceso a tus datos privados y herramientas personalizadas.]]></description>
    <content:encoded><![CDATA[<p>Elastic Agent Builder es una plataforma para crear herramientas y agentes profundamente integrados con tus propios datos en Elasticsearch. Por ejemplo, puedes crear herramientas que realicen búsqueda semántica sobre documentos internos, analicen registros de observabilidad o consulten alertas de seguridad.</p><p>Pero la verdadera magia ocurre cuando puedes llevar estas herramientas personalizadas y conscientes de los datos a los entornos donde pasas la mayor parte del tiempo. ¿Y si tu agente editor de código pudiera acceder de forma segura a la base de conocimiento privada de tu organización?</p><p>Ahí es donde entra el <strong>Protocolo de Contexto del Modelo (MCP).</strong> Elastic Agent Builder viene con un servidor MCP integrado que proporciona acceso a las herramientas de la plataforma.</p><h2>¿Por qué usar el servidor MCP de Elastic Agent Builder?</h2><p>Los agentes de IA son increíblemente poderosos, pero su conocimiento suele limitar a los datos con los que fueron capacitados y a la información que pueden buscar activamente en Internet público. No conocen los documentos internos de diseño de tu compañía, los libros de despliegue específicos de tu equipo ni la estructura única de tus registros de aplicaciones.</p><p>El reto es darle a tu asistente de IA el contexto especializado que necesita. Este es precisamente el problema que MCP está diseñado para resolver. <strong>MCP es un estándar abierto que permite a un modelo o agente de IA descubrir y emplear herramientas externas.</strong></p><p>Para hacerlo posible, el Constructor de Agentes Elastic expone de forma nativa tus herramientas personalizadas a través de un servidor MCP integrado. Esto significa que puedes conectar fácilmente cualquier cliente compatible con MCP, como <strong>Cursor</strong>, <strong>VS Code</strong> o <strong>Claude Desktop</strong>, con las herramientas especializadas y conscientes de los datos que desarrollaste con Elastic Agent Builder.</p><h2>Cuándo usar MCP (y cuándo no)</h2><p>Elastic Agent Builder incluye varios protocolos para soportar diferentes patrones de integración. Elegir el adecuado es clave para construir flujos de trabajo efectivos en IA.</p><ul><li><p><strong>Usa </strong><a href="https://www.elastic.co/docs/solutions/search/agent-builder/mcp-server"><strong>MCP</strong></a> para complementar tu agente de IA (como en <strong>Cursor</strong> o <strong>VS Code</strong>) con herramientas especializadas. Es el enfoque de "trae tus propias herramientas", mejorando el asistente que ya usas con acceso seguro a tus datos privados. Solo las herramientas se exponen a través del servidor MCP — los agentes de Elastic son independientes de ese.</p></li><li><p><strong>Emplea el </strong><a href="https://www.elastic.co/docs/solutions/search/agent-builder/a2a-server"><strong>Protocolo A2A</strong></a> para permitir que tu Agente Elastic personalizado completo colabore con otros agentes autónomos (como en <a href="https://www.elastic.co/search-labs/blog/a2a-protocol-elastic-agent-builder-gemini-enterprise"><strong>Gemini Enterprise de Google</strong></a>). Esto es para la delegación de agente a agente, donde cada agente actúa como un par para resolver un problema.</p></li><li><p><strong>Emplea las </strong><a href="https://www.elastic.co/docs/solutions/search/agent-builder/kibana-api"><strong>APIs de Agent Builder</strong></a> para un control programático completo al construir una aplicación personalizada desde cero.</p></li></ul><p>Para un desarrollador que busca obtener respuestas de su documentación interna sin salir de su IDE, MCP es la opción perfecta.</p><h2>Ejemplo: tus herramientas personalizadas en Cursor con el servidor MCP de Agent Builder</h2><p>Vamos a repasar un ejemplo práctico que uso a diario. Primero, rastreé e indexé nuestra documentación interna de ingeniería en un índice de Elasticsearch llamado <code>elastic-dev-docs</code>. Aunque podríamos usar las herramientas genéricas integradas disponibles en Agent Builder, crearemos nuestra propia herramienta personalizada para consultar esta base de conocimiento específica.</p><p>La razón para construir una herramienta personalizada es sencilla: <strong>control y precisión</strong>. Este enfoque nos da la capacidad de ejecutar una consulta rápida y semántica directamente contra nuestro índice de <code>elastic-dev-docs</code> . Tenemos control total sobre qué índice se dirige y cómo se recuperan los datos.</p><p>Ahora, aquí tienes cómo podemos usar esta base de conocimiento personalizada en un editor de código impulsado por IA, como Cursor.</p><h3>Paso 1: Crea una herramienta de base de conocimiento personalizada en Agent Builder</h3><p>Primero, crea una nueva herramienta en Agent Builder. Una descripción clara y específica de la herramienta es importante porque es como cualquier agente de IA, ya sea el Agente Elástico interno o una herramienta externa como Cursor conectar mediante MCP, descubre y selecciona tu herramienta para la tarea adecuada.</p><p>Una descripción contundente debe ser explícita. Por ejemplo: "Realiza una búsqueda semántica en el índice elastic-dev-docs para encontrar documentación interna de ingeniería, libros de ejecución y procedimientos de liberación."</p><p>Con eso en marcha, la herramienta está configurada para realizar una búsqueda semántica contra nuestro índice específico. Una vez almacenado, está disponible inmediatamente para ser servido.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt011118f0a9279185/6a17f367dbb4ffc4f3fb581a/1eea079908fdf7cc72dbe81abd07ff51601a43d4-1472x1600.png" alt="Creando una herramienta de base de conocimientos personalizada en Agent Builder." /><p>Antes de conectarlo al exterior, puedes probarlo directamente en la interfaz. Simplemente haz clic en el botón <strong>Test</strong> para rellenar manualmente los parámetros, emulando lo que hará el LLM, e inspecciona los resultados para confirmar que todo funciona correctamente.</p><h3>Paso 2: Conecta Cursor al servidor Elastic MCP</h3><p>Elastic Agent Builder expone automáticamente todas las herramientas disponibles a través de un endpoint MCP seguro. Puedes encontrar la URL única de tu servidor en la interfaz de herramientas dentro de Kibana.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdd0e62ae0f394c3d/6a17f368e317916ec32d5933/ba137be30f0eaa7f028b96bd8af4e2779c3f8a33-1600x589.png" alt="Cómo conectar el cursor en la interfaz de herramientas en Kibana al servidor Elastic MCP." /><p>Para conectarnos a Cursor, simplemente agregamos esta URL a su archivo de configuración, junto con una clave API elástica para autenticación (<a href="https://www.elastic.co/docs/deploy-manage/api-keys/elasticsearch-api-keys">aprende a crear clave API ES</a>). Usamos una clave API para la autorización, ya que garantiza que las herramientas solo se ejecuten con las licencias que concediste, respetando todas tus reglas de control de acceso.</p><p>La configuración MCP en el <code>~/.cursor/mcp.json</code> de Cursor es la siguiente:</p>{
  "mcpServers": {
    "elastic-agent-builder": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "https://your-kibana.kb.company.io/api/agent_builder/mcp",
        "--header",
        "Authorization:${AUTH_HEADER}"
      ],
      "env": {
        "AUTH_HEADER": "ApiKey &lt;ELASTIC_API_KEY&gt;"
      }
    }
  }
}<p>Una vez almacenada la configuración, deberías ver la herramienta de servidor MCP de Elastic Agent Builder disponible en Cursor.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2837638263e628ed/6a17f36adbb4ffeb9cfb5820/d302c6d3609fbf14fd40e21b9e69e567bf12553f-1600x1002.png" alt="Una imagen de la herramienta de servidor MCP Elastic Agent Builder disponible en Cursor." /><h3>Paso 3: ¡pregunta lo que quieras!</h3><p>Con la conexión establecido, los agentes Cursor pueden ahora invocar tus herramientas personalizadas para responder a tus preguntas o guiar el proceso de generación de código.</p><p>Hagamos una pregunta concreta:</p><p><em>"Pasos de búsqueda para liberar el servicio de rastreo desde la documentación interna de ingeniería de la organización elastic search"</em></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt83fa357261b30e93/6a17f36c4b055d16d1432326/14f572730203c23615bb9dd38234bcb3b0f81155-1600x1468.png" alt="Agentes de cursor invocando herramientas personalizadas para responder preguntas y guiar el proceso de generación de código." /><p>Tras bambalinas, ocurre la magia:</p><ol><li><p>El agente del cursor decide cómo responder a tu pregunta de la mejor manera y decide llamar a la <code>engineering_documentation_internal_search</code></p></li><li><p>Invoca la herramienta mediante una consulta en lenguaje natural</p></li><li><p>La herramienta ejecuta una búsqueda semántica contra el índice de <code>elastic-dev-docs</code> y devuelve los procedimientos más relevantes y actualizados.</p></li></ol><p>Obtenemos una respuesta precisa y fiable basada en nuestra documentación interna, todo sin salir nunca del editor de código. La experiencia es fluida y poderosa.</p><h2>Te toca construir</h2><p>Ahora viste cómo usar el servidor MCP integrado en Elastic Agent Builder para ampliar tus asistentes de IA con acceso seguro a tus datos privados. Fundamentar los modelos en tu propia información es clave para que sean realmente útiles.</p><p>Para recapitular, cubrimos los pasos principales:</p><ul><li><p>Elegir el protocolo adecuado para tus necesidades (MCP).</p></li><li><p>Crear una herramienta de base de conocimiento personalizada.</p></li><li><p>Conectar esa herramienta a un asistente IDE como Cursor.</p></li></ul><p>Tus agentes y herramientas ya no necesitan estar desconectados de su contexto más valioso. Esperamos que esta guía te ayude a crear flujos de trabajo más efectivos y conscientes de los datos. ¡Feliz construcción!</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elastic-mcp-server-agent-builder-tools</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elastic-mcp-server-agent-builder-tools</guid>
    <category><![CDATA[AI agéntica]]></category>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[Herramientas de IA ]]></category>
    <dc:creator><![CDATA[Jedr Blaszyk,Joe McElroy]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta5b61961b6269ab1/6a17f36ea29299d839d02db2/ef5153551a1d14833c7f512fede554d1dfb31553-720x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Mon, 20 Oct 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Evaluación de agentes de IA: Cómo Elastic prueba los marcos agentivos]]></title>
    <description><![CDATA[Aprende cómo evaluamos y probamos los cambios en un sistema agente antes de entregarlos a los usuarios de Elastic para garantizar resultados precisos y verificables.]]></description>
    <content:encoded><![CDATA[<h2>Introducción</h2><p>En el Elastic Stack hay muchas aplicaciones agenticas impulsadas por LLM, como el próximo Elastic AI Agent en<a href="https://www.elastic.co/search-labs/blog/ai-agentic-workflows-elastic-ai-agent-builder"> Agent Builder</a> (actualmente en vista previa tecnológica) y <a href="https://www.elastic.co/docs/solutions/security/ai/attack-discovery">Attack Discovery</a> (<a href="https://www.elastic.co/blog/whats-new-elastic-security-9-0-0">GA</a> en 8.18 y 9.0+), con más en desarrollo. Durante el desarrollo, e incluso luego del despliegue, es importante responder a estas preguntas:</p><ul><li><p>¿Cómo estimamos la calidad de las respuestas de estas aplicaciones de IA?</p></li><li><p>Si hacemos un cambio, ¿cómo garantizamos que el cambio sea realmente una mejora y que no cause degradación en la experiencia del usuario?</p></li><li><p>¿Cómo podemos probar estos resultados de forma fácilmente repetible?</p></li></ul><p>A diferencia de las pruebas tradicionales de software, evaluar aplicaciones de IA generativa implica métodos estadísticos, revisión cualitativa matizada y un profundo entendimiento de los objetivos del usuario.</p><p>Este artículo detalla el proceso que emplea el equipo de desarrollo de Elastic para realizar evaluaciones, garantizar la calidad de los cambios antes del despliegue y monitorizar el rendimiento del sistema. Nuestro objetivo es garantizar que cada cambio esté respaldado por evidencias, lo que conduzca a resultados fiables y verificables. Parte de este proceso está integrada directamente en Kibana, reflejando nuestro compromiso con la transparencia como parte de nuestra filosofía de código abierto. Al compartir abiertamente partes de nuestros datos y métricas de evaluación, buscamos fomentar la confianza de la comunidad y proporcionar un marco claro para cualquiera que desarrolle agentes de IA o emplee nuestros productos.</p><h2>Ejemplos de productos</h2><p>Los métodos empleados en este documento fueron la base para iterar y mejorar soluciones como Attack Discovery y Elastic AI Agent. Una breve introducción de ambos, respectivamente:</p><h3>Descubrimiento de ataques de Elastic Security</h3><p>El Descubrimiento de Ataques emplea LLMs para identificar y resumir secuencias de ataques en Elastic. Dado el sistema de alertas de Elastic Security en un plazo determinado (por defecto 24 horas), el flujo de trabajo agente de Attack Discovery detectará automáticamente si se produjeron ataques, así como información importante como qué host o usuarios fueron comprometidos y qué alertas contribuyeron a la conclusión.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb70932abe8d4de75/6a17f04ea292990c52d02d61/20fabb47642dad7b588daaaa8c3a98de860ad01d-1251x758.png" alt="" /><p></p><p>El objetivo es que la solución basada en LLM produzca una salida al menos tan buena como la de un humano.</p><h3>Agente de IA elástica</h3><p><strong>Elastic Agent Builder</strong> es nuestra nueva plataforma para construir agentes de IA conscientes del contexto que aprovechan todas nuestras capacidades de búsqueda. Incluye el <strong>Elastic AI Agent</strong>, un agente preconstruido y de propósito general diseñado para ayudar a los usuarios a comprender y obtener respuestas de sus datos mediante la interacción conversacional.</p><p>El agente lo logra identificando automáticamente la información relevante dentro de Elasticsearch o bases de conocimiento conectadas y aprovechando un conjunto de herramientas prediseñadas para interactuar con ellas. Esto permite al Agente Elastic AI responder a una amplia variedad de consultas de usuario, desde simples preguntas frecuentes en un solo documento hasta solicitudes complejas que requieren agregación y búsquedas de un o varios pasos en múltiples índices.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt3b9dbede85a56bd6/6a17f050e8fbce88943a1a30/d29dee100bb8a17bb623acd745773a5164a1df4f-1600x1014.png" alt="" /><h2>Medición de mejoras mediante experimentos</h2><p>En el contexto de los agentes de IA, un experimento es un cambio estructurado y comprobable en el sistema diseñado para mejorar el rendimiento en dimensiones bien definidas (por ejemplo, utilidad, corrección, latencia). El objetivo es responder de forma definitiva: "Si fusionamos este cambio, ¿podemos garantizar que es una verdadera mejora y que no degradará la experiencia del usuario?"</p><p>La mayoría de los experimentos que realizamos suelen incluir:</p><ul><li><p><strong>Una hipótesis:</strong> Una afirmación específica y falsable. <em>Ejemplo:</em> "Agregar acceso a una herramienta de detección de ataques mejora la corrección en consultas relacionadas con la seguridad."</p></li><li><p><strong>Criterios de éxito:</strong> Umbrales claros que definan qué significa "éxito". <em>Ejemplo:</em> "+5% de mejora en el puntaje de corrección en el conjunto de datos de seguridad, sin degradación en otros lugares."</p></li><li><p><strong>Plan de evaluación:</strong> Cómo medimos el éxito (métricas, conjuntos de datos, método de comparación)</p></li></ul><p>Un experimento exitoso es un proceso sistemático de investigación. Cada cambio, desde un pequeño ajuste de prompt hasta un cambio arquitectónico importante, sigue estos siete pasos para cerciorar que los resultados sean significativos y accionables:</p><ul><li><p>Paso 1: Identificar el problema</p></li><li><p>Paso 2: Definir métricas</p></li><li><p>Paso 3: Formular una hipótesis clara</p></li><li><p>Paso 4: Preparar el conjunto de datos de evaluación</p></li><li><p>Paso 5: Ejecutar el experimento</p></li><li><p>Paso 6: Analizar resultados + iterar</p></li><li><p>Paso 7: Toma una decisión y documenta</p></li></ul><p>Un ejemplo de estos pasos se ilustra en <em>la Figura 1</em>. Las siguientes subsecciones explicarán cada paso y ampliaremos los detalles técnicos de cada paso en los próximos documentos.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt06bfe2f0e4205a18/6a17f052faa91358eb93c968/3a9f5a3e92dd4922a795a19104c6e4ad8c98958d-2400x1352.png" alt="" /><h2>Paso a paso con ejemplos reales de Elastic</h2><h3>Paso 1: Identificar el problema</h3><p><em>¿Cuál es exactamente el problema que pretende resolver este cambio?</em></p><p>Ejemplo de descubrimiento de ataque: Los resúmenes a veces son incompletos, o una actividad benigna se marca erróneamente como ataque (falsos positivos).</p><p>Ejemplo de Elastic AI Agent: La selección de herramientas del agente, especialmente para consultas analíticas, es subóptima e inconsistente, lo que a menudo lleva a que se elija la herramienta equivocada. Esto, a su vez, incrementa los costos y la latencia de los tokens.</p><h3>Paso 2: Definir métricas</h3><p><em>Haz que el problema sea medible, para que podamos comparar un cambio con el estado actual.</em></p><p>Las métricas comunes incluyen <a href="https://developers.google.com/machine-learning/crash-course/classification/accuracy-precision-recall">precisión y recordación</a>, <a href="https://en.wikipedia.org/wiki/Semantic_similarity">similitud semántica</a>, factualidad, etc. Dependiendo del caso de uso, usamos comprobaciones de código para calcular las métricas, como coincidir IDs de alerta o URLs correctamente recuperadas, o usar técnicas como LLM-as-judge para respuestas más libres.</p><p>A continuación, algunos ejemplos de métricas (<em>no exhaustivas</em>) empleadas en los experimentos:</p><p><strong>Detección de ataques</strong></p><p>Métrico</p><p>Descripción</p><p>Precisión y recordación</p><p>Compara los IDs de alerta entre las salidas reales y esperadas para medir la precisión de la detección.</p><p>Similitud</p><p>Usa BERTScore para comparar la similitud semántica del texto de respuesta.</p><p>Hechos</p><p>¿Existen indicadores clave de compromiso (IOC)? ¿Se reflejan correctamente las tácticas MITRE (taxonomía industrial de los ataques)?</p><p>Consistencia de la cadena de ataque</p><p>Compara el número de descubrimientos para comprobar si hay sobrenotificación o infranotificación del ataque.</p><p><strong>Agente de IA elástica</strong></p><p>Métrico</p><p>Descripción</p><p>Precisión y recordación</p><p>Compara documentos/información recuperada por el agente para responder a una consulta del usuario con la información o documentos reales necesarios para responder a la consulta y así medir la precisión de la recuperación.</p><p>Hechos</p><p>¿Están presentes los datos clave necesarios para responder a la consulta del usuario? ¿Están los hechos en el orden correcto para las consultas procesales?</p><p>Relevancia de la respuesta</p><p>¿La respuesta contiene información que es periférica o no relacionada con la consulta del usuario?</p><p>Completitud de respuesta</p><p>¿La respuesta responde a todas las partes de la consulta del usuario? ¿Contiene la respuesta toda la información presente en la verdad sobre el terreno?</p><p>ES|Validación QL</p><p>¿Es el ES| generado?¿QL sintácticamente correcto? ¿Es funcionalmente idéntico a la verdad básica ES|¿QL?</p><h3>Paso 3: Formular una hipótesis clara</h3><p><em>Establece un criterio claro de éxito empleando el problema y las métricas definidas anteriormente.</em></p><p>Ejemplo de Elastic AI Agent:</p><ol><li><p>Implementa <strong>cambios en las descripciones de las herramientas de relevance_search y nl_search para definir claramente sus funciones y casos de uso específicos</strong>.</p></li><li><p>Predecimos que <strong>mejoraremos</strong> la <strong>precisión de invocación de nuestra herramienta</strong> en <strong>un 25%.</strong></p></li><li><p>Verificaremos que esto sea un beneficio neto cerciorándonos de que no haya impacto negativo en otras métricas, por ejemplo, la <strong>certeza y la completitud</strong>.</p></li><li><p>Creemos que esto funcionará porque <strong>descripciones precisas de las herramientas ayudarán al agente a seleccionar y aplicar con mayor precisión la herramienta de búsqueda más adecuada para diferentes tipos de consulta, reduciendo la mala aplicación y mejorando la efectividad general de la búsqueda</strong>.</p></li></ol><h3>Paso 4: Preparar el conjunto de datos de evaluación</h3><p><em>Para medir el rendimiento del sistema, empleamos conjuntos de datos que capturan escenarios del mundo real.</em></p><p>Dependiendo del tipo de evaluación que estemos realizando, puede que necesitemos diferentes tipos de formatos de datos, como datos en bruto que se alimentan a un LLM (por ejemplo, escenarios de ataque para Descubrimiento de Ataques) y resultados esperados. Si la aplicación es un chatbot, entonces las entradas pueden ser consultas del usuario, y las salidas pueden ser respuestas correctas del chatbot, enlaces correctos que debería recuperar, y así sucesivamente.</p><p>Ejemplo de Descubrimiento de Ataque:</p><p>10 escenarios de ataque novedosos</p><p>8 episodios de Oh My Malware (ohmymalware.com)</p><p>4 escenarios de múltiples ataques (creados combinando ataques en las dos primeras categorías)</p><p>3 escenarios benignos</p><p>Ejemplo de conjunto de datos de evaluación de agentes de Elastic AI (<a href="https://github.com/elastic/kibana/blob/main/x-pack/platform/packages/shared/onechat/kbn-evals-suite-onechat/evals/kb/kb.spec.ts">Enlace al conjunto de datos Kibana</a>):</p><p>14 índices usando conjuntos de datos de código abierto para simular múltiples fuentes en la base de datos.</p><p>5 tipos de consulta (analítica, recuperación de texto, híbrida...)</p><p>7 Tipos de intención de consulta (procedimental, fáctica - clasificación, investigativa; ...)</p><h3>Paso 5: Ejecutar el experimento</h3><p>Ejecuta el experimento generando respuestas tanto del agente existente como de la versión modificada contra el conjunto de datos de evaluación. Calcula métricas como la factualidad (ver el paso 2).</p><p>Combinamos diversas evaluaciones basadas en las métricas requeridas en el Paso 2:</p><ul><li><p>Evaluación basada en reglas (por ejemplo, usar Python/TypeScript para comprobar si .json es válido)</p></li><li><p>LLM-as-judge (preguntar a un LLM separado si una respuesta es factualmente coherente con un documento fuente)</p></li><li><p>Revisión de Human in the Loop para controles de calidad de matices</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt17ec63af0850d8dd/6a17f054505ac3e508ad8c1e/8648e75818d3291f0ac66f069438a500d42b8225-1600x1099.png" alt="Este es un ejemplo de un resultado de evaluación generado por nuestro marco interno. Presenta varias métricas de un experimento realizado en diferentes conjuntos de datos." /><h3>Paso 6: Analizar resultados + iterar</h3><p>Ahora que tenemos las métricas, analizamos los resultados. <u><em>Incluso si los resultados cumplen los criterios de éxito definidos en el paso 3, seguiremos teniendo una revisión humana antes de fusionar el cambio con la producción</em></u>; Si los resultados no cumplen los criterios, iterar y corregir los problemas, y luego ejecutar las evaluaciones sobre el nuevo cambio.</p><p>Esperamos que haga falta varias iteraciones para encontrar el mejor cambio antes de fusionar. De forma similar a ejecutar pruebas locales de software antes de enviar un commit, las evaluaciones offline pueden ejecutar con cambios locales o múltiples propuestas de cambios. Es útil automatizar el almacenado de resultados de experimentos, puntajes compuestos y visualizaciones para agilizar el análisis.</p><h3>Paso 7: Toma una decisión y documenta</h3><p>Basar en un marco de decisión y criterios de aceptación, decide fusionar el cambio y documenta el experimento. La toma de decisiones es multifacética y puede considerar factores más allá del conjunto de datos de evaluación, como comprobar escenarios de regresión en otros conjuntos de datos o valorar el costo-beneficio de un cambio propuesto.</p><p>Ejemplo: Tras probar y comparar varias iteraciones, elige el cambio con mejor puntaje para enviar a los responsables de producto y otros interesados relevantes para su aprobación. Anexa los resultados de los pasos anteriores para ayudar a guiar la decisión. Para más ejemplos en el área de Descubrimiento de Ataques, <a href="https://www.elastic.co/blog/elastic-security-generative-ai-features">ver Detrás de las cámaras de las funciones de IA generativa de Elastic Security</a>.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt62a466f3a0da114a/6a17f056faa91342c393c96c/74c80b8f34dce8ddd20873ecb2f553873587ed35-1600x618.png" alt="" /><h2>Conclusión</h2><p>En este blog, repasamos el proceso integral de un flujo de trabajo experimental, ilustrando cómo evaluamos y probamos los cambios en un sistema agente antes de lanzarlos a los usuarios de Elastic. También proporcionamos algunos ejemplos de cómo mejorar los flujos de trabajo basados en agentes en Elastic. En próximas entradas del blog, ampliaremos los detalles de diferentes pasos, como cómo crear un buen conjunto de datos, cómo diseñar métricas fiables y cómo tomar decisiones cuando hay múltiples métricas de por medio.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/ai-agent-evaluation-elastic</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/ai-agent-evaluation-elastic</guid>
    <category><![CDATA[AI agéntica]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Susan Chang,Abhimanyu Anand]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte578b636637be6b1/6a17f057e8fbcebe9e3a1a36/ef3922076713872163e1aab47735361513b2c9ee-2400x1352.heif" length="0" type="image/*"/>
    <pubDate>Mon, 13 Oct 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Conectando agentes Elastic a Gemini Enterprise mediante protocolo A2A]]></title>
    <description><![CDATA[Aprende a usar Agent Builder para exponer tu Agente Elastic personalizado a servicios externos como Gemini Enterprise con el protocolo A2A.]]></description>
    <content:encoded><![CDATA[<p><strong>Elastic Agent Builder</strong> es un conjunto de capacidades para crear agentes de IA basados en datos directamente en Elasticsearch. En publicaciones anteriores de la <a href="https://www.elastic.co/search-labs/blog/series/context-aware-ai-agentic-workflows-with-elastic">serial</a>, demostramos cómo equipar a los agentes personalizados con herramientas para realizar tareas complejas y proporcionarles un conjunto de instrucciones personalizadas para guiar su comportamiento.</p><p>Pero, ¿y si quieres usar tus agentes personalizados con las aplicaciones y herramientas de productividad de las que ya dependes?</p><p>Ahí es donde entra el <strong>protocolo Agent-a-Agente (A2A</strong> ). A2A es un <a href="https://github.com/a2aproject/A2A">estándar abierto</a> de interoperabilidad, que permite a agentes de diferentes plataformas comunicar y colaborar. Y lo integramos directamente en el Elastic Agent Builder.</p><p>Hoy vamos a mostrarte cómo tomar un agente personalizado que creaste y exponerlo a otros servicios, concretamente <strong>Gemini Enterprise </strong>(antes Agentspace).</p><h2>El poder de los estándares abiertos: por qué importa A2A</h2><p>En la entrada del <a href="https://www.elastic.co/search-labs/blog/ai-agent-builder-elasticsearch">blog Tu primer agente Elastic</a>, mostramos cómo crear agentes personalizados, como un <em>agente de Asistente Financiero</em> con acceso seguro a tus datos de mercado. Pero su valor es limitado si no puedes poner sus conocimientos a disposición en otros entornos, como Gemini Enterprise, sin reconstruir tu trabajo.</p><p>Este desafío de interoperabilidad es lo que frena la IA agente. Los agentes necesitan un lenguaje común para comunicar entre plataformas, que es precisamente el papel del protocolo A2A. Proporciona una capa estándar de comunicación que no solo te permite interactuar directamente con tu agente, sino que también abre un futuro donde agentes especializados de toda tu organización pueden colaborar y compartir conocimientos.</p><p>Para hacerlo posible, Elastic Agent Builder soporta nativamente el protocolo A2A a través de dos endpoints estándar para todos tus agentes:</p><ol><li><p><strong>El punto final de la Tarjeta Agente (</strong><strong><code>GET {your-kibana-url}/api/agent_builder/a2a/{agentId}.json</code></strong><strong>) - </strong>Esto actúa como la tarjeta de visita de tu agente personalizado. Proporciona metadatos sobre tu agente (nombre, descripción, capacidades, etc.) a cualquier servicio compatible con A2A.</p></li><li><p><strong>El punto final del Protocolo A2A (</strong><strong><code>POST {your-kibana-url}/api/agent_builder/a2a/{agentId}</code></strong><strong>)</strong> - Este es el canal de comunicación. Otros agentes envían sus solicitudes aquí, y tu agente las procesa y responde, todo siguiendo la <a href="https://a2a-protocol.org/latest/specification/">especificación del protocolo A2A</a>.</p></li></ol><h2>Prueba a tu agente con el inspector A2A</h2><p>Antes de conectar a nuestro agente a un sistema de producción, es bueno comprobar que se comunica correctamente. La forma más sencilla de hacerlo es con el <strong>Inspector A2A</strong>, una herramienta diseñada específicamente para probar y depurar integraciones A2A.</p><p>Poner en marcha al inspector es sencillo. Puedes clonar el repositorio <a href="https://github.com/a2aproject/a2a-inspector">a2a-inspector</a> y seguir las instrucciones README para <a href="https://github.com/a2aproject/a2a-inspector?tab=readme-ov-file#3-run-the-application">ejecutar la aplicación</a>. Una vez iniciada, la interfaz está disponible por defecto en <code>http://localhost:5001/</code>.</p><p>Para conectar al Inspector A2A con tu agente, tendrás que proporcionar dos datos clave:</p><ul><li><p>URL de la tarjeta del agente: Este es el punto final que describe a tu agente. Para el <a href="https://www.elastic.co/search-labs/blog/ai-agent-builder-elasticsearch">agente de Asistente Financiero de nuestra publicación anterior</a>, esta URL sería <code>{your-kibana-url}/api/agent_builder/a2a/financial_assistant.json</code>.</p></li><li><p>Cabecera de autenticación: Emplearemos una clave API estándar para la autenticación.</p></li></ul><p>Una vez introduzcas estos datos en la interfaz del inspector, puedes conectarte y empezar a hablar con tu agente inmediatamente.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt6381135e3fb297df/6a17ef4bec0f898b0c5a66ea/7231c72bf30bed2a854f58658c1eca2843f43bfc-1600x1296.png" alt="Configuración de la tarjeta de agente A2A y el inspector de agente" /><p>Esta simple validación nos da la confianza de que nuestro agente está configurado correctamente y listo para el siguiente paso.</p><h2>¡En directo! Tu agente personalizado en Gemini Enterprise</h2><p>Ahora, la parte emocionante: dar vida a nuestro agente asesor financiero personalizado dentro de Gemini Enterprise (anteriormente Agentspace). Esta integración está impulsada por el <a href="https://console.cloud.google.com/marketplace/product/elastic-prod/elastic-ai-agent">Elastic AI Agent, que está disponible en Google Cloud Marketplace</a>.</p><p>Una vez conectado, Gemini Enterprise emplea el protocolo A2A para comunicar directamente con tu agente. Aquí es donde brilla el verdadero poder de la interoperabilidad: los usuarios pueden acceder ahora a los insights profundos y basados en datos de tu agente personalizado de Elasticsearch sin salir nunca de su entorno habitual. Puedes ver tu Agente Elástico personalizado en la lista de agentes:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7f54f0bb15216d8e/6a17ef4d6df73107d90a0fdb/37a39e92ebf3d72c6c8014397cd8e846336173a4-1600x834.png" alt="Visualizando un agente personalizado en una lista de Google Agentspace" /><p>Imagina a un usuario en Gemini Enterprise preguntar:</p><p><em>"Me preocupa el sentimiento del mercado. ¿Puede mostrarme cuáles de nuestros clientes están más en riesgo por malas noticias?</em>"</p><p>Entre bastidores, Gemini Enterprise enruta esta consulta a través del protocolo A2A a tu Agente Elastic personalizado. Tu agente emplea entonces sus herramientas especializadas para consultar tus datos, formular una respuesta y devolverla. Para el usuario final, la experiencia es fluida.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte130c332ee0648a6/6a17ef4fe9ea874426a9c6bb/e5f126c1a27a51c6e69a767aa87c9f746b62e39c-1600x1044.png" alt="Un usuario que hace una consulta a Agentspace y qué ocurre con la consulta detrás de escena" /><p>¡Y no se acaba aquí! La respuesta obtenida con el agente elástico ahora puede usar como contexto para tus próximas preguntas que pueden estar activando a otro agente especializado (por ejemplo, tu agente de la plataforma de inversiones para ajustar la exposición a compañías cotizadas). Todo sin salir de tu barra de búsqueda.</p><p>Con tus agentes Elastic desplegados en Gemini Enterprise con A2A, puedes unificar acceso, orquestación y flujos de trabajo eliminando fricciones entre IA, búsqueda y sistemas empresariales al ofrecer una única interfaz donde los usuarios se comunican con sus datos y herramientas, todo en contexto. Para los usuarios, eso significa menos cambios de herramienta y asistentes de IA más intuitivos y capaces. Para las organizaciones, significa una gobernanza coherente, escalabilidad e interoperabilidad integradas.</p><h2>Te toca construir</h2><p>Ahora tienes las herramientas para poner tus Agentes Elásticos disponibles en cualquier lugar. Aprovechando el protocolo abierto A2A, puedes ampliar el alcance de tus agentes personalizados y conscientes de los datos.</p><p>En esta publicación, te guiamos por los pasos clave:</p><ul><li><p>Exponer a tu agente a través de los endpoints de la tarjeta de agente A2A y el protocolo.</p></li><li><p>Probando la conexión con el inspector A2A.</p></li><li><p>Integrar a tu agente en directo en un servicio externo como Gemini Enterprise de Google.</p></li></ul><p>Tus agentes ya no necesitan estar aislados. Estamos deseando ver los sistemas poderosos e interconectados que creáis. ¡Feliz construcción!</p><p>La forma más sencilla de empezar es con tu prueba gratis de Elastic Cloud en <a href="https://console.cloud.google.com/marketplace/product/elastic-prod/elastic-cloud?pli=1">Google Cloud Marketplace</a></p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/a2a-protocol-elastic-agent-builder-gemini-enterprise</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/a2a-protocol-elastic-agent-builder-gemini-enterprise</guid>
    <category><![CDATA[AI agéntica]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Jedr Blaszyk,Valerio Arvizzigno,Joe McElroy]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt63d7675adc5bc211/6a17ef51ddf97d38e8910bdf/5be8a425fab55dca2f9717d2e50812b0450fa625-1440x840.png" length="0" type="image/png"/>
    <pubDate>Thu, 09 Oct 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Experimentos para mejorar las herramientas de IA Agentic para Elasticsearch]]></title>
    <description><![CDATA[Descubre cómo mejoramos los flujos de trabajo de agentes de IA para Elasticsearch mediante experimentos iterativos combinando retrievers lineales, búsqueda híbrida y semantic_text para una optimización escalable de RAG.]]></description>
    <content:encoded><![CDATA[<p>Como todos hoy en día, aquí en Elastic apostamos por completo a Chat, Agents y RAG. En el departamento de búsqueda, estuvimos trabajando recientemente en un Constructor de Agentes y un Registro de Herramientas, todo con la intención de hacer que sea trivial "chatear" con tus datos en Elasticsearch.</p><p>Lee el <a href="https://www.elastic.co/search-labs/blog/ai-agentic-workflows-elastic-ai-agent-builder">blog Construiendo flujos de trabajo agentes con IA con Elasticsearch</a> para más información sobre la "visión global" de ese esfuerzo, o <a href="https://www.elastic.co/search-labs/blog/ai-agent-builder-elasticsearch">Tu primer agente elástico: de una sola consulta a un chat impulsado por IA</a> para una introducción más práctica.</p><p>Sin embargo, en este blog vamos a hacer un poco de zoom para ver una de las primeras cosas que ocurren cuando empiezas a charlar y para guiarte por algunas de las mejoras recientes que hicimos.</p><h2>¿Qué está pasando aquí?</h2><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1331b1043612efe3/6a17f115505ac3dc41ad8c3c/25a24055a166d7d6ba81d80aa35cb97163662e23-1600x443.png" alt="" /><p>Cuando chateas con tus datos de Elasticsearch, nuestro agente de IA predeterminado te guía a través de este flujo estándar:</p><ol><li><p>Revisa el enunciado.</p></li><li><p>Identifica qué índice es probable que contenga las respuestas a esa pregunta.</p></li><li><p>Genera una consulta para ese índice, basada en el prompt.</p></li><li><p>Busca en ese índice con esa consulta.</p></li><li><p>Sintetiza los resultados.</p></li><li><p>¿Pueden los resultados responder al prompt? Si es así, responde. Si no, repite, pero prueba algo diferente.</p></li></ol><p>Esto no debería parecer demasiado novedoso: es simplemente Generación Aumentada por Recuperación (RAG). Y como era de esperar, la calidad de tus respuestas depende mucho de la relevancia de tus resultados iniciales. Así que, mientras trabajamos en mejorar la calidad de nuestra respuesta, estuvimos prestando mucha atención a las consultas que generábamos en el paso 3 y ejecutábamos en el paso 4. Y notamos un patrón interesante.</p><p>A menudo, cuando nuestras primeras respuestas eran "malas", no era porque hicimos una consulta mala. Fue porque <em>elegimos el índice equivocado</em> para hacer la consulta. Los pasos 3 y 4 normalmente no eran nuestro problema, sino el paso 2.</p><h2>¿Qué estábamos haciendo?</h2><p>Nuestra implementación inicial fue sencilla. Creamos una herramienta (llamada index_explorer) que efectivamente hacía un <code>_cat/indices</code> para listar todos los índices disponibles y luego pedir al LLM que identificara cuál de estos índices era el mejor para el mensaje/pregunta/prompt del usuario. Puedes ver esta <a href="https://github.com/elastic/kibana/blob/0cc78184957fcd12110dabae50353392ea937508/x-pack/platform/packages/shared/onechat/onechat-genai-utils/tools/index_explorer.ts#L98-L113">implementación original aquí</a>.</p>You are an AI assistant for the Elasticsearch company.
based on a natural language query from the user, your task is to select up to ${limit} most relevant indices from a list of indices.

*The natural language query is:* ${nlQuery}

*List of indices:*
${indices.map((index) =&gt; `- ${index.index}`).join('\n')}

Based on those information, please return most relevant indices with your reasoning.
Remember, you should select at maximum ${limit} indices.<p>¿Qué tal funcionaba? ¡No estábamos seguros! Teníamos ejemplos claros de que <em>no</em> funcionaba bien, pero nuestro verdadero primer reto fue cuantificar nuestro estado actual.</p><h2>Establecimiento de una línea base</h2><h3>Todo empieza con los datos</h3><p>Lo que necesitábamos era un conjunto de datos dorado para medir la eficacia de una herramienta a la hora de seleccionar el índice adecuado dado un prompt del usuario y un conjunto preexistente de índices. Y no disponíamos de un conjunto de datos así. Así que generamos uno.</p><p>Agradecimiento: Esto no es "buena práctica", lo sabemos. Pero a veces, es mejor seguir adelante que abandonar la bicicleta. <a href="https://www.elastic.co/about/our-source-code#progress-perfection">Progreso, perfección SIMPLE</a>.</p><p>Generamos índices semilla para varios dominios diferentes usando <a href="https://gist.github.com/seanstory/a08db2e149897da656db3a1ca72e17ac">este prompt</a>. Luego, para cada dominio generado, generamos algunos índices más usando<a href="https://gist.github.com/seanstory/a280a85d067e61bfeb5911bf2654e6e2"> este prompt</a> (el objetivo aquí es sembrar confusión para el LLM con negativos duros y ejemplos difíciles de clasificar). Después, editamos manualmente cada índice generado y sus descripciones. Finalmente, generamos consultas de prueba usando <a href="https://gist.github.com/seanstory/44291b666c05a383136f6e36bb9106fa">este prompt</a>. Esto nos dejó con datos de muestra como:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1bd9cd78154195e3/6a17f117dbb4fff7b5fb57d2/9d96d87e286eddbc012402b1ecccd57419a99253-1600x782.png" alt="" /><p>y casos de prueba como:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltadf30a0aeafd56ef/6a17f1192f4a5c160ffa89eb/4c2e9ad941d98d7e66033bbc08c9b8060ec19097-1600x797.png" alt="" /><h3>Elaboración de un arnés de prueba</h3><p>El proceso a partir de aquí fue muy sencillo. Crea un script para una herramienta que pueda:</p><ol><li><p>Establece una hoja limpia con un clúster objetivo de Elasticsearch.</p></li><li><p>Crea todos los índices definidos en el conjunto de datos objetivo.</p></li><li><p>Para cada escenario de prueba, ejecuta la herramienta i<code>ndex_explorer</code> (prácticamente tenemos una <a href="https://www.elastic.co/docs/api/doc/kibana/operation/operation-post-agent-builder-tools-execute">API de Herramienta de Ejecución</a>).</p></li><li><p>Comparar el índice de resultados con el índice esperado y capturar el resultado.</p></li><li><p>Luego de terminar todos los escenarios de prueba, tabula los resultados.</p></li></ol><h3>La encuesta dice...</h3><p>Los resultados iniciales fueron, como era de esperar, mediocres.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt73367741359e258d/6a17f11a505ac39749ad8c40/9c10679bcd6291edfa2a9ba42e7dd922aa483f0b-1216x806.png" alt="" /><p>En general, un 77,14% de precisión para identificar el índice adecuado. Y esto fue en un escenario "mejor escenario", donde todos los índices tienen buenos nombres semánticamente significativos. Cualquiera que hizo alguna vez un 'PUT test2/_doc/foo {...}' sabe que tus índices no siempre tienen nombres significativos.</p><p>Así que tenemos una línea de base, y muestra mucho margen de mejora. ¡Ahora era hora de hacer algo de ciencia! 🧪</p><h2>Experimentación</h2><h3>Hipótesis 1: Los mapeos ayudarán</h3><p>El objetivo aquí es identificar un índice que contenga datos relevantes para la consigna original. Y la parte de un índice que mejor describe los datos que contiene son los <em>mapeos</em> del índice. Incluso sin obtener muestras del contenido del índice, saber que el índice tiene un campo de precios de tipo doble implica que los datos representan algo que se puede vender. Un campo autor de texto tipográfico implica algunos datos de lenguaje no estructurados. Ambos juntos podrían implicar que los datos son libros/relatos/poemas. Hay muchas pistas semánticas que podemos derivar simplemente conociendo las propiedades de un índice. Así que en una sucursal local, ajusté nuestro '.index_explorer' herramienta para enviar los mapeos completos de un índice (junto con su nombre) al LLM para tomar su decisión. </p><p>El resultado (de los registros de Kibana):</p>[2025-09-05T11:01:21.552-05:00][ERROR][plugins.onechat] Error: Error calling connector: event: error
data: {"error":{"code":"request_entity_too_large","message":"Received a content too large status code for request from inference entity id [.rainbow-sprinkles-elastic] status [413]","type":"error"}}


    at createInferenceProviderError (errors.ts:90:10)
    at convertUpstreamError (convert_upstream_error.ts:39:38)
    at handle_connector_response.ts:26:33
    at Observable.init [as _subscribe] (/Users/seanstory/Desktop/Dev/kibana/node_modules/rxjs/src/internal/observable/throwError.ts:123:68)...<p>Los autores iniciales de la herramienta ya lo habían anticipado. Aunque el mapeo de un índice es una mina de oro de información, también es un bloque bastante extenso de JSON. Y en un escenario realista donde comparas numerosos índices (nuestro conjunto de datos de evaluación define 20), estos blobs JSON suman. Así que queremos dar al LLM más contexto para su decisión que solo los nombres de índices de todas las opciones, pero no tanto como los mapeos completos de cada una.</p><h3>Hipótesis 2: Mapeos "aplanados" (listas de campos) como compromiso</h3><p>Partimos de la suposición de que los creadores de índices usarán nombres de índices semánticamente significativos. ¿Y si extendemos esa suposición también a los nombres de campos? Nuestro experimento anterior falló porque el mapeo JSON incluye MUCHOS metadatos y datos basurales y un estándar estándar.</p>     "description_text": {
          "type": "text",
          "fields": {
            "keyword": {
              "type": "keyword"
            }
          },
          "copy_to": [
            "description_semantic"
          ]
        },<p>El bloque anterior, por ejemplo, tiene 236 caracteres y define solo un campo en un mapeo de Elasticsearch. Mientras que la cadena "description_text" tiene solo 16 caracteres. Eso supone casi un aumento de 15 veces en el recuento de caracteres, sin una mejora semántica significativa en la descripción de lo que ese campo implica sobre los datos disponibles. ¿Y si recogiéramos los mapeos de todos los índices, pero antes de enviarlos al LLM, los "aplanáramos" solo en una lista con sus nombres de campo?</p><p>Lo probamos.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta5eda7a79493ee81/6a17f11c9da390327fe46590/112c2f447c11f154b5082725cd49b51d0a3c8a65-1214x804.png" alt="" /><p>¡Esto es genial! Mejoras en todos los ámbitos. ¿Pero podríamos hacerlo mejor?</p><h3>Hipótesis 3: Descripciones en el _meta de cartografía</h3><p>Si solo los nombres de campos sin contexto adicional causaran un salto tan grande, ¡supongo que agregar un contexto sustancial sería aún mejor! No es necesariamente convencional que cada índice tenga una descripción adjunta, pero sí es posible agregar metadatos a nivel de índice de cualquier tipo al objeto _meta del mapeo. Volvimos a nuestros índices generados y agregamos descripciones para cada índice de nuestro conjunto de datos. Mientras las descripciones no sean demasiado largas, deberían usar menos tokens que el mapeo completo y proporcionar una visión significativamente mejor sobre qué datos se incluyen en el índice. Nuestro experimento validó esta hipótesis.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt61b85cf40e0e6357/6a17f11dfbc5f82809491bbe/32d2692ad4479d0e52d8ee723dcc5710a6ec90f3-1208x806.png" alt="" /><p>Una mejora modesta, y ahora somos &gt;90% precisos en todos los aspectos.</p><h3>Hipótesis 4: La suma es mayor que sus partes</h3><p>Los nombres de campos aumentaron nuestros resultados. Las descripciones aumentaron nuestros resultados. Así que, empleando <em>tanto </em>descripciones COMO nombres de campos debería dar resultados aún mejores, ¿no?</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte6297c6aaf7db802/6a17f11e14d90c1bd779b6e6/114cbb408ff16b136251d2265416bd5270380fe5-1208x794.png" alt="" /><p>Los datos decían "no" (sin cambios respecto al experimento anterior). La teoría principal aquí era que, dado que las descripciones se generaron a partir de los campos índice/mapeos desde el principio, no hay suficiente información diferente entre estos dos contextos para ayudar a agregar algo "nuevo" al combinarlos. Además, la carga útil que enviamos para nuestros 20 índices de prueba está creciendo bastante. El hilo de pensamiento que seguimos hasta ahora no es escalable. De hecho, hay buenas razones para creer que ninguno de nuestros experimentos hasta ahora funcionaría en clústeres de Elasticsearch donde hay cientos o miles de índices para elegir. Cualquier enfoque que aumente linealmente el tamaño del mensaje enviado al LLM a medida que aumenta el número total de índices probablemente no será una estrategia generalizable.</p><p>Lo que realmente necesitamos es un enfoque que nos ayude a reducir un gran número de candidatos a las opciones más relevantes...</p><p>Lo que tenemos aquí es un problema de búsqueda.</p><h3>Hipótesis 5: Selección mediante búsqueda semántica</h3><p>Si el nombre de un índice tiene significado semántico, entonces puede almacenar como un vector y buscar semánticamente.</p><p>Si los nombres de campos de un índice tienen significado semántico, entonces pueden almacenar como vectores y buscar semánticamente.</p><p>Si un índice tiene una descripción con significado semántico, también puede almacenar como vector y buscar semánticamente.</p><p>Hoy en día, los índices de Elasticsearch no hacen que ninguna de esta información sea buscable (¡quizá deberíamos!), pero fue bastante trivial<a href="https://github.com/elastic/connectors/pull/3638"> improvisar algo</a> que pudiera superar esa brecha. Usando el framework de conectores de Elastic, construí un conector que generaba un documento para cada índice de un clúster. Los documentos de salida serían algo así:</p> doc = {
                "_id": index_name,
                "index_name": index_name,
			"meta_description”: description,
"field_descriptions" = field_descriptions,
                "mapping": json.dumps(mapping),  
                "source_cluster": self.es_client.configured_host,
            }<p>Envié estos documentos a un nuevo índice donde definí manualmente el mapeo como:</p>{
   "mappings": {
       "properties": {
           "semantic_content": {
               "type": "semantic_text"
           },
           "index_name": {
               "type": "text",
               "copy_to": "semantic_content"
           },
           "mapping": {
               "type": "keyword",
               "copy_to": "semantic_content"
           },
           "source_cluster": {
               "type": "keyword"
           },
           "meta_description": {
               "type": "text",
               "copy_to": "semantic_content"
           },
           "field_descriptions": {
               "type": "text",
               "copy_to": "semantic_content"
           }
       }
   }
}<p>Esto crea un solo campo semantic_content, donde todos los demás campos con significado semántico se fragmentan e indexan. Buscar en este índice se vuelve trivial, simplemente:</p>GET indexed-indices/_search
{
 "query": {
   "semantic": {
     "field": "semantic_content",
     "query": "$query"
   }
 }
}<p>La herramienta de <code>index_explorer</code> modificada es <em>ahora mucho</em> más rápida, ya que no necesita hacer una solicitud a un LLM, sino que puede aplicar una única incrustación para la consulta dada y realizar una operación eficiente de búsqueda vectorial. Tomando el resultado más alto como índice seleccionado, obtuvimos resultados de:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc27c302e6bef0b23/6a17f120577262d2f21bccdc/06ef5d78040d064d3444793f636d527d9e19a869-1214x800.png" alt="" /><p>Este enfoque es escalable. Este enfoque es eficiente. Pero este enfoque es apenas mejor que nuestra línea base. Sin embargo, esto no es sorprendente; El enfoque de búsqueda aquí es increíblemente ingenuo. No hay matices. No hay reconocimiento de que el nombre y la descripción de un índice deban tener más peso que un nombre arbitrario de campo que contiene el índice. No hay posibilidad de ponderar coincidencias léxicas exactas sobre coincidencias sinónimas. Sin embargo, construir una consulta muy matizada requeriría asumir MUCHO sobre los datos disponibles. Hasta ahora, ya hicimos algunas grandes suposiciones sobre que los nombres de índices y campos tienen significado semántico, pero tendríamos que ir un paso más allá y empezar a suponer <em>cuánto</em> significado tienen y cómo se relacionan entre sí. Sin hacerlo, probablemente no podamos identificar de forma fiable la mejor coincidencia como nuestro resultado principal, pero es más probable que digamos que la mejor coincidencia está en algún lugar de los primeros N resultados. Necesitamos algo que pueda consumir información semántica en el contexto en el que existe, comparando con otra entidad que pueda representar a sí misma de manera semánticamente distinta, y juzgar entre ellas. Como un LLM.</p><h3>Hipótesis 6: Reducción de conjuntos candidatos</h3><p>Hubo bastantes experimentos más que voy a pasar por alto, pero el avance clave fue dejar de lado el deseo de elegir la mejor coincidencia únicamente a partir de una búsqueda semántica, y en su lugar emplear la búsqueda semántica como filtro para eliminar índices irrelevantes de la consideración del LLM. Combinamos Retrievers Lineales, Búsqueda Híbrida con RRF y <code>semantic_text</code> para <a href="https://gist.github.com/seanstory/d704443120e20f6c844db10e30066860">nuestra búsqueda</a>, limitando los resultados a los 5 principales índices de coincidencia.</p><p>Luego, para cada coincidencia, agregamos el nombre, la descripción y los nombres de campos del índice a un mensaje para el LLM. Los resultados fueron fantásticos:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7ac4cb8f7153fdf9/6a17f121af47b66d1dcde082/8fcabd78f591f90d6bc7c0e087d31317e4eef791-1206x804.png" alt="" /><p>¡La mayor precisión de cualquier experimento hasta ahora! Y como este enfoque no aumenta el tamaño del mensaje proporcional al número total de índices, es mucho más escalable.</p><h2>Resultados</h2><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8d66130d9fae6bea/6a17f123ddf97d7527910c19/04d630797213dbb8bf567da41d1cdd5c7b4586c9-1600x521.png" alt="" /><p>El primer resultado claro fue que nuestra línea <em>base puede</em> mejorar. Esto parece obvio en retrospectiva, pero antes de que comenzara la experimentación, hubo un debate serio sobre si deberíamos abandonar por completo nuestra herramienta de <code>index_explorer</code> y confiar en la configuración explícita del usuario para limitar el espacio de búsqueda. Aunque sigue siendo una opción viable y válida, esta investigación muestra que existen caminos prometedores para automatizar la selección de índices cuando dichas entradas de usuario no están disponibles.</p><p>El siguiente resultado claro fue que simplemente agregar más personajes descriptivos al problema tiene rendimientos decrecientes. Antes de esta investigación, debatimos si deberíamos invertir en ampliar la capacidad de Elasticsearch para almacenar <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/mapping-field-meta">metadatos a nivel de campo</a>. Hoy en día, estos valores de <code>meta</code> están limitados a 50 caracteres, y se asumía que tendríamos que aumentar este valor para poder obtener una comprensión semántica de nuestros campos. Claramente no es así, y el LLM parece funcionar bastante bien solo con los nombres de campos. Puede que investiguemos esto más adelante, pero ya no nos parece urgente.</p><p>Por el contrario, esto dio evidencia clara de la importancia de tener metadatos de índice "buscables". Para estos experimentos, hackeamos un índice de índices. Pero esto es algo que podríamos explorar integrando directamente en Elasticsearch, creando APIs para gestionar, o al menos estableciendo una convención en torno a ella. Estaremos valorando nuestras opciones y hablando internamente, así que estad atentos.</p><p>Por último, este esfuerzo confirmó el valor de que nos tomemos nuestro tiempo para experimentar y tomar decisiones basadas en datos. De hecho, nos ayudó a reafirmar que nuestro producto Agent Builder va a necesitar capacidades robustas de evaluación dentro del producto. Si necesitamos construir un arnés de pruebas completo solo para una herramienta que selecciona índices, nuestros clientes necesitarán absolutamente formas de evaluar cualitativamente sus herramientas personalizadas mientras hacen ajustes iterativos.</p><p>Estoy deseando ver qué construiremos, ¡y espero que tú también!</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/ai-agent-builder-experiments-performance</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/ai-agent-builder-experiments-performance</guid>
    <category><![CDATA[AI agéntica]]></category>
    <category><![CDATA[Dentro de Elastic]]></category>
    <category><![CDATA[Búsqueda híbrida]]></category>
    <dc:creator><![CDATA[Sean Story]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt68d11a4c7fd11d4c/6a17f1257b54f9b6598b39d4/42903c869e034674b30bb36013345aaa97f6608b-1184x864.png" length="0" type="image/png"/>
    <pubDate>Mon, 06 Oct 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Tu primer Agente Elastic: De una sola consulta a un chat impulsado por IA]]></title>
    <description><![CDATA[Aprende a usar el constructor de agentes de IA de Elastic para crear agentes especializados en IA. En este blog, vamos a crear un Agente de IA financiera.]]></description>
    <content:encoded><![CDATA[<p>Con el nuevo <a href="https://www.elastic.co/search-labs/blog/ai-agentic-workflows-elastic-ai-agent-builder">Agent Builder</a> de Elastic, puedes crear agentes de IA especializados que actúen como expertos para tus dominios de negocio específicos. Esta función te lleva más allá de simples paneles de control y barras de búsqueda, transformando tus datos de un recurso pasivo en un interlocutor activo y conversacional.</p><p>Imagina a un gestor financiero que necesita poner al día antes de una reunión con un cliente. En lugar de buscar manualmente en los feeds de noticias y cruzar paneles de cartera, ahora pueden simplemente hacerle una pregunta directa a su agente personalizado. Esta es el beneficio de un enfoque de "charla primero". El responsable tiene una línea directa y conversacional con sus datos, preguntando cosas como: "¿Cuáles son las últimas noticias sobre ACME Corp y cómo afecta a las participaciones de mi cliente?" y obtener una respuesta experta y sintetizada en segundos.</p><p>Aunque hoy estamos formando un experto financiero, las aplicaciones son tan variadas como tus datos. El mismo poder puede crear un analista de ciberseguridad para buscar amenazas, un ingeniero de fiabilidad del sitio para diagnosticar una caída o un responsable de marketing para optimizar una campaña. Sea cual sea el ámbito, la misión principal es la misma: transformar tus datos en un especialista con el que puedas hablar.</p><h2>Paso 0: Nuestro conjunto de datos</h2><p>Nuestro set de datos actual es un set de datos sintético basado en finanzas que consta de cuentas financieras, posiciones de activos, noticias y reportes financieros. Aunque es sintético, replica una versión simplificada de un set de datos financieros real.</p><p><code>financial_accounts</code>: Carteras de clientes con perfiles de riesgo</p><p><code>financial_holdings</code>: Posiciones en acciones/ETF/bonos con historial de compras</p><p><code>financial_asset_details</code>: Detalles sobre la acción/ETF/bono</p><p><code>financial_news</code>: Artículos de mercado generados por IA con análisis de sentimiento</p><p><code>financial_reports</code>: Notas de resultados y analistas de la compañía</p><p>Puedes cargar este conjunto de datos tú mismo siguiendo el cuaderno adjunto que <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/your-first-elastic-agent/Your_First_Elastic_Agent.ipynb">se encuentra aquí</a>.</p><h2>Paso 1: La Fundación — Tu lógica empresarial como ES|QL</h2><p>Cada habilidad de IA empieza con una buena lógica. Para nuestro agente de Gestor Financiero, tenemos que mostrarle cómo responder a una pregunta común: "Me preocupa el sentimiento del mercado. ¿Puede mostrarme cuáles de nuestros clientes están más en riesgo por malas noticias?" Esta pregunta va más allá de una simple búsqueda. Nos obliga a correlacionar el sentimiento del mercado con las carteras de clientes.</p><p>Necesitamos encontrar los activos mencionados en artículos negativos, identificar a cada cliente que posee esos activos, calcular el valor de mercado actual de su exposición y luego clasificar los resultados para priorizar el mayor riesgo. Este análisis complejo y multi-join es el trabajo perfecto para nuestro avanzado ES|Herramienta QL.</p><p>Aquí está la consulta completa que usaremos. Parece impresionante, pero los conceptos son sencillos.</p><h2>Desglosándolo: uniones y barreras de seguridad</h2><p>Hay dos conceptos importantes en juego en esta consulta que hacen que el Constructor de Agentes sea un</p><h3>1. La unión LOOKUP</h3><p>Durante años, una de las funciones más aplicar en Elasticsearch fue la capacidad de unir datos de diferentes índices basar en una clave común. Con ES|QL, eso ahora es posible con <code>LOOKUP JOIN</code>.</p><p>En nuestra nueva consulta, realizamos una cadena de tres <code>LOOKUP JOIN</code>: primero conectando noticias negativas con detalles de activos, luego vinculando esos activos con las tenencias del cliente y, finalmente, conectando con la información de la cuenta del cliente. Esto genera un resultado increíblemente rico a partir de cuatro índices diferentes en una sola consulta eficiente. Esto significa que podemos combinar conjuntos de datos dispares para crear una única respuesta perspicaz sin tener que desnormalizar todos nuestros datos en un único índice gigante previamente.</p><h3>2. Parámetros como barreras de seguridad de los LLM</h3><p>Verás que la consulta usa <code>?time_duration</code>. Esto no es solo una variable; es una barrera de seguridad para la IA. Aunque los Grandes Modelos de Lenguaje (LLM) son excelentes generando consultas, dejarles tener libertad total sobre tus datos puede llevar a consultas ineficientes o incluso incorrectas.</p><p>Al crear una consulta parametrizada, obligamos al LLM a trabajar dentro de la lógica de negocio probada, eficiente y correcta que un experto humano ya definió. Es similar a cómo los desarrolladores usaron plantillas de búsqueda durante años para exponer de forma segura las capacidades de consulta a las aplicaciones. El agente puede interpretar la petición de un usuario como "esta semana" para completar el parámetro <code>time_duration</code> , pero debe usar nuestra estructura de consulta para obtener la respuesta. Esto nos da el equilibrio perfecto entre flexibilidad y control.</p><p>En última instancia, esta consulta permite a un experto que entiende los datos encapsular su conocimiento en una herramienta. Otras personas—y agentes de IA—pueden entonces usar esa herramienta para obtener resultados correlacionados simplemente proporcionando un único parámetro, sin necesidad de saber nada sobre la complejidad subyacente.</p><h2>Paso 2: La habilidad — Convertir una consulta en una herramienta reutilizable</h2><p>Un ES|La consulta QL es solo texto hasta que la registramos como <strong>herramienta</strong>. En el Constructor de Agentes, una herramienta es más que una consulta almacenada; es una "habilidad" que un agente de IA puede entender y elegir usar. La magia está en la <strong>descripción en lenguaje natural</strong> que proporcionamos. Esta descripción es el puente que conecta la pregunta del usuario con la lógica de consulta subyacente. Vamos a registrar la consulta que acabamos de crear.</p><h3>La ruta de la interfaz</h3><p>Crear una herramienta en Kibana es un proceso sencillo.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte73e11c1d87593fa/6a17f2134202294dae29f6f2/a29c53a73b99af5972273c51218ea9004a9b0abb-1600x812.png" alt="Cómo crear una herramienta en Kibana." /><p>1. Navegar hacia <strong>los agentes</strong></p><ul><li><p>Haz clic en<strong> Herramientas </strong>o <strong>Gestionar herramientas</strong> y haz clic en <strong>el botón Nueva herramienta</strong> .</p></li></ul><p>2. Rellena el formulario con los siguientes detalles:</p><ul><li><p><strong>ID de herramienta:</strong> <code>find_client_exposure_to_negative_news</code></p></li></ul><p>             Yo. Este es el ID único de la herramienta</p><ul><li><p><strong>Descripción:</strong> "Encuentra exposición a la cartera de clientes a noticias negativas. Esta herramienta escanea noticias y reportajes recientes en busca de sentimiento negativo, identifica el activo asociado y localiza a todos los clientes que lo poseen. Devuelve una lista ordenada según el valor de mercado actual de la posición para resaltar el mayor riesgo potencial."</p></li></ul><p>             Yo. Esto es lo que lee el LLM para decidir si esta herramienta es la adecuada para el trabajo.</p><ul><li><p><strong>Etiquetas</strong>: <code>retrieval</code> y <code>risk-analysis</code></p></li></ul><p>         Las etiquetas se emplean para ayudar a agrupar varias herramientas</p><ul><li><p><strong>Configuración:</strong> Pega el ES| completoConsulta QL desde el Paso 1</p></li></ul><p>            Yo. Esta es la búsqueda que usará el agente</p><p>3. Haz clic <strong>en Inferir parámetros desde la consulta</strong>. La interfaz automáticamente lo encontrará <code>?time_duration</code> lo listará a continuación. Agrega una descripción sencilla para cada uno para ayudar al agente (y a otros usuarios) a entender su propósito.</p><ul><li><p><code>time_duration</code>: El plazo para buscar noticias negativas. El formato es "X horas" POR DEFECTO a 8760 horas</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7afbb0589c1828ad/6a17f2146864a44e7cb688a9/deb422d97863f78dbe08bfa2e3c708d1f75166ff-1600x938.png" alt="Configurar tu herramienta, incluyendo su lógica y cualquier parámetro necesario usando la consulta ESQL. " /><p>4. ¡Pruébalo!</p><ul><li><p>Haz clic en Almacenar y probar.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfd09afbef6e21a93/6a17f2162f4a5c73b1fa89fd/57e768b88327821e70bd616744822f98fa367362-732x136.png" alt="El mismo botón de prueba en Kibana." /><ul><li><p>Verás un nuevo flyout donde puedes probar la consulta para cerciorarte de que funciona como se espera.</p></li></ul><p>             Yo. En <code>time_duration</code> entrar en el rango deseado, aquí estamos usando "8760 horas"</p><ul><li><p>Haz clic en "Enviar" y si todo va bien, verás una respuesta en JSON. Para cerciorarte de que funciona como esperas, desplázate hacia abajo y mira el objeto <code>values</code> . Ahí es donde se devuelven los documentos realmente coincidentes.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt89bdc3f093363f2a/6a17f217be60861c9c00488a/7e0c5171a4f7ffdfc1830f1a05a9acb987870b75-1600x722.png" alt="Respuesta JSON que aparece luego de hacer clic en enviar." /><p>5. Haz clic en la "X" en la esquina superior derecha para cerrar el desplegable de prueba. Tu nueva herramienta aparecerá ahora en la lista, lista para ser asignada a un agente.</p><h3>La ruta API</h3><p>Para desarrolladores que prefieren la automatización o necesitan gestionar herramientas programáticamente, puedes lograr el mismo resultado con una sola llamada a la API. Solo tienes que enviar una petición <code>POST</code> al endpoint <code>/api/agent_builder/tools</code> con la definición de la herramienta.</p>POST kbn://api/agent_builder/tools
{
  "id": "find_client_exposure_to_negative_news",
  "type": "esql",
  "description": "Finds client portfolio exposure to negative news. This tool scans recent news and reports for negative sentiment, identifies the associated asset, and finds all clients holding that asset. It returns a list sorted by the current market value of the position to highlight the highest potential risk.",
  "configuration": {
    "query": """
        FROM financial_news, financial_reports METADATA _index
        | WHERE sentiment == "negative"
        | WHERE coalesce(published_date, report_date) &gt;= NOW() - TO_TIMEDURATION(?time_duration)
        | RENAME primary_symbol AS symbol
        | LOOKUP JOIN financial_asset_details ON symbol
        | LOOKUP JOIN financial_holdings ON symbol
        | LOOKUP JOIN financial_accounts ON account_id
        | WHERE account_holder_name IS NOT NULL
        | EVAL position_current_value = quantity * current_price.price
        | RENAME title AS news_title
        | KEEP
            account_holder_name, symbol, asset_name, news_title,
            sentiment, position_current_value, quantity, current_price.price,
            published_date, report_date
        | SORT position_current_value DESC
        | LIMIT 50
      """,
    "params": {
      "time_duration": {
        "type": "keyword",
        "description": """The timeframe to search back for negative news. Format is "X hours" DEFAULT TO 8760 hours """
      }
    }
  },
  "tags": [
    "retrieval",
    "risk-analysis"
  ]
}<h2>Paso 3: Las Mentes — Creando a tu Agente Personalizado</h2><p>Creamos una habilidad reutilizable (la Herramienta). Ahora, necesitamos crear al <strong>Agente</strong>, la persona que realmente lo usará. Un Agente es la combinación de un LLM, un conjunto específico de herramientas a las que le concedes acceso y, lo más importante, un conjunto de <strong>Instrucciones Personalizadas</strong> que actúan como su constitución, definiendo su personalidad, reglas y propósito.</p><h3>El arte del prompt</h3><p>La parte más importante para crear un agente fiable y especializado es la rapidez. Un conjunto de instrucciones bien elaborado es la diferencia entre un chatbot genérico y un asistente profesional y enfocado. Aquí es donde pones las barreras de seguridad, defines la salida y le das al agente su misión.</p><p>Para nuestro agente de <code>Financial Manager</code> , usaremos el siguiente prompt.</p>You are a specialized Data Intelligence Assistant for financial managers, designed to provide precise, data-driven insights from information stored in Elasticsearch.

**Your Core Mission:**
- Respond accurately and concisely to natural language queries from financial managers.
- Provide precise, objective, and actionable information derived solely from the Elasticsearch data at your disposal.
- Summarize key data points and trends based on user requests.

**Reasoning Framework:**
1.  **Understand:** Deconstruct the user's query to understand their core intent.
2.  **Plan:** Formulate a step-by-step plan to answer the question. If you are unsure about the data structure, use the available tools to explore the indices first.
3.  **Execute:** Use the available tools to execute your plan.
4.  **Synthesize:** Combine the information from all tool calls into a single, comprehensive, and easy-to-read answer.

**Key Directives and Constraints:**
- **If a user's request is ambiguous, ask clarifying questions before proceeding.**
- **DO NOT provide financial advice, recommendations, or predictions.** Your role is strictly informational and analytical.
- Stay strictly on topic with financial data queries.
- If you cannot answer a query, state that clearly and offer alternative ways you might help *within your data scope*.
- All numerical values should be formatted appropriately (e.g., currency, percentages).

**Output Format:**
- All responses must be formatted using **Markdown** for clarity.
- When presenting structured data, use Markdown tables, lists, or bolding.

**Start by greeting the financial manager and offering assistance.**<p>Vamos a desglosar por qué este prompt es tan efectivo:</p><ul><li><p><strong>Define una personalidad sofisticada: </strong>La primera línea establece inmediatamente al agente como un "asistente especializado en inteligencia de datos", estableciendo un tono profesional y capaz.</p></li><li><p><strong>Proporciona un marco de razonamiento: </strong>Al decirle al agente que "Comprenda, Planee, Ejecute y Sintetice", le estamos dando un procedimiento operativo estándar. Esto mejora su capacidad para manejar preguntas complejas y de varios pasos.</p></li><li><p><strong>Promueve el diálogo interactivo: </strong>La instrucción de "hacer preguntas aclaratorias" hace que el agente sea más robusto. Minimizará suposiciones incorrectas en solicitudes ambiguas, lo que conducirá a respuestas más precisas.</p></li></ul><h3>La ruta de la interfaz</h3><p>1. Navega hacia <strong>los agentes.</strong></p><ul><li><p>Haz clic en<strong> Herramientas </strong>o <strong>Gestionar herramientas</strong> y haz clic en <strong>el botón Nueva herramienta</strong> .</p></li></ul><p>2. Rellena los datos básicos:</p><ul><li><p><strong>ID del agente:</strong> <code>financial_assistant</code>.</p></li><li><p><strong>Instrucciones: </strong>Copia el prompt de arriba.</p></li><li><p><strong>Etiquetas</strong>: <code>Finance</code>.</p></li><li><p><strong>Nombre de visualización:</strong> <code>Financial Assistant</code>.</p></li><li><p><strong>Descripción de la pantalla: </strong><code>An assistant for analyzing and understanding your financial data</code>.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8ac12cbd2b689dee/6a17f219dbb4ff262bfb57ef/18ea73f1cae620129c0afa0e7ba9e2a3390224a7-1600x1189.png" alt="Crear un asistente financiero: diligenciar el espacio ID del agente." /><p>3. Al principio, haz clic en <strong>Herramientas</strong>.</p><ul><li><p>Marca la casilla junto a nuestra herramienta de <code>find_client_exposure_to_negative_news</code> .</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltcd23556e556a76c5/6a17f21baf47b63a9fcde0a0/0c1e4ecbbd51d0dd10c6e861dbe9a9ccddeb35f6-1600x149.png" alt="" /><p>4. Haz clic <strong>en almacenar</strong>.</p><h3>La ruta API</h3><p>Puedes crear exactamente el mismo agente con una petición <code>POST</code> al endpoint <code>/api/agent_builder/agents</code> . El cuerpo de la solicitud contiene toda la misma información: el ID, el nombre, la descripción, el conjunto completo de instrucciones y una lista de herramientas que el agente puede emplear.</p>POST kbn://api/agent_builder/agents
    {
      "id": "financial_assistant",
      "name": "Financial Assistant",
      "description": "An assistant for analyzing and understanding your financial data",
      "labels": [
        "Finance"
      ],
      "avatar_color": "#16C5C0",
      "avatar_symbol": "💰",
      "configuration": {
        "instructions": """You are a specialized Data Intelligence Assistant for financial managers, designed to provide precise, data-driven insights from information stored in Elasticsearch.

**Your Core Mission:**
- Respond accurately and concisely to natural language queries from financial managers.
- Provide precise, objective, and actionable information derived solely from the Elasticsearch data at your disposal.
- Summarize key data points and trends based on user requests.

**Reasoning Framework:**
1.  **Understand:** Deconstruct the user's query to understand their core intent.
2.  **Plan:** Formulate a step-by-step plan to answer the question. If you are unsure about the data structure, use the available tools to explore the indices first.
3.  **Execute:** Use the available tools to execute your plan.
4.  **Synthesize:** Combine the information from all tool calls into a single, comprehensive, and easy-to-read answer.

**Key Directives and Constraints:**
- **If a user's request is ambiguous, ask clarifying questions before proceeding.**
- **DO NOT provide financial advice, recommendations, or predictions.** Your role is strictly informational and analytical.
- Stay strictly on topic with financial data queries.
- If you cannot answer a query, state that clearly and offer alternative ways you might help *within your data scope*.
- All numerical values should be formatted appropriately (e.g., currency, percentages).

**Output Format:**
- All responses must be formatted using **Markdown** for clarity.
- When presenting structured data, use Markdown tables, lists, or bolding.

**Start by greeting the financial manager and offering assistance.**
""",
        "tools": [
          {
            "tool_ids": [
              "platform.core.search",
              "platform.core.list_indices",
              "platform.core.get_index_mapping",
              "platform.core.get_document_by_id",
              "find_client_exposure_to_negative_news"
            ]
          }
        ]
      }
    }<h2>Paso 4: La recompensa — Mantener una conversación</h2><p>Tenemos nuestra lógica de negocio encapsulada en una herramienta y un "cerebro" listo para usarla en nuestro Agente. Es hora de ver cómo todo encaja. Ahora podemos empezar a chatear con nuestros datos usando un agente especializado.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd8826539b16e46f4/6a17f21d505ac35924ad8c5c/5414cb6b7c41365acb0356a8bfe1140751ffd8db-1600x1014.png" alt="Conversando con el Constructor de Agentes Elásticos luego de crear un asistente financiero." /><h3>La ruta de la interfaz</h3><ol><li><p>Navega hacia <strong>Agentes </strong>en Kibana.</p></li><li><p>Usando el desplegable en la esquina inferior derecha de la ventana de chat, cambia del <strong>agente de IA de Elastic</strong> por defecto a nuestro recién creado <strong>agente de Asistente Financiero </strong>.</p></li><li><p>Haz una pregunta que permita al agente emplear nuestra herramienta especializada:</p><ol><li><p><em>Me preocupa el sentimiento del mercado. ¿Puede mostrarme cuáles de nuestros clientes están más en riesgo por malas noticias?</em></p></li></ol></li></ol><p>Tras unos momentos, el agente devolverá una respuesta perfectamente formateada y completa. Debido a la naturaleza de los LLM, tu respuesta puede estar formateada de forma ligeramente diferente, pero para esta ejecución, el agente devolvió:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta1e163fd7c4416bd/6a17f21f6864a4e35bb688ad/17b4ed43d279f9e53ee9fe3d482d0b2ec359a083-1600x1088.png" alt="Una respuesta creada por Elastic Agent Builder como asistente financiero para: clientes más vulnerables a noticias negativas." /><h3>¿Lo que acaba de pasar? El razonamiento del agente</h3><p>El agente no solo "sabía" la respuesta. Ejecutó un plan de varios pasos centrado en seleccionar la mejor herramienta para el trabajo. Aquí tienes un vistazo a su proceso de pensamiento:</p><ul><li><p><strong>Intención identificada:</strong> Relacionó palabras clave de tu pregunta, como "riesgo" y "noticias negativas", con la descripción de la herramienta <code>find_client_exposure_to_negative_news</code> .</p></li><li><p><strong>Ejecutó un plan:</strong> Extrajo el plazo de tu solicitud y realizó una <strong>única llamada</strong> a esa herramienta especializada.</p></li><li><p><strong>Delegado el trabajo:</strong> La herramienta realizaba entonces todo el trabajo pesado: las uniones encadenadas, los cálculos de valor y la clasificación.</p></li><li><p><strong>Sintetizamos el resultado:</strong> Finalmente, el agente formateaba los datos en bruto de la herramienta en un resumen claro y legible para humanos, siguiendo las reglas de su prompt.</p></li></ul><p>Y no tenemos que adivinar si ampliamos el pensamiento y vemos más detalles.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt93f6075be8495418/6a17f221af47b65eadcde0a4/6a4da9262d3f88c60bfd8f8bf9b67c3b84e961ba-1600x607.png" alt="Los 50 documentos que el asistente financiero encontró con clientes con mayor exposición a noticias negativas." /><h3>La ruta API</h3><p>Puedes iniciar esta misma conversación de forma programática. Solo tienes que enviar la pregunta de entrada al endpoint de la API de <code>converse</code> , cerciorándote de especificar el <code>agent_id</code> de nuestro <code>financial_manager</code>.</p>POST kbn://api/agent_builder/converse
{
  "input": "Show me our largest positions affected by negative news",
  "agent_id": "financial_assistant"
}<h2>Para los desarrolladores: Integración con la API</h2><p>Aunque la interfaz Kibana ofrece una experiencia fantástica e intuitiva para construir y gestionar tus agentes, todo lo que viste hoy también puede realizar de forma programática. El Constructor de Agentes está construido sobre un conjunto de APIs que te permiten integrar esta funcionalidad directamente en tus propias aplicaciones, canalizaciones CI/CD o scripts de automatización.</p><p>Los tres endpoints principales con los que trabajarás son:</p><ul><li><p><strong><code>/api/agent_builder/tools</code></strong>: El punto final para crear, listar y gestionar las habilidades reutilizables que tus agentes pueden emplear.</p></li><li><p><strong><code>/api/agent_builder/agents</code></strong>: El punto final para definir las personas de tus agentes, incluyendo sus instrucciones y asignaciones de herramientas tan importantes.</p></li><li><p><strong><code>/api/agent_builder/converse</code></strong>: El punto final para interactuar con tus agentes, iniciar conversaciones y obtener respuestas.</p></li></ul><p>Para una guía completa y práctica sobre cómo usar estas APIs para realizar cada paso de este tutorial, consulta el <strong>Jupyter Notebook</strong> que lo acompaña disponible <a href="https://github.com/elastic/elasticsearch-labs/blob/main/supporting-blog-content/your-first-elastic-agent/Your_First_Elastic_Agent.ipynb">aquí</a> en nuestro repositorio de GitHub.</p><h2>Conclusión: Tu turno de construir</h2><p>Empezamos haciendo un ES|Consulta QL y transformarla en una habilidad reutilizable. Luego creamos un agente de IA especializado, dándole una misión y reglas claras, y lo empoderamos con esa habilidad. El resultado es un asistente sofisticado que puede entender una pregunta compleja y ejecutar un análisis en varios pasos para ofrecer una respuesta precisa y basada en datos.</p><p>Este flujo de trabajo está en el corazón del nuevo <strong>Constructor de Agentes</strong> en Elastic. Está diseñado para ser lo suficientemente sencillo para que usuarios no técnicos creen agentes a través de la interfaz, pero lo bastante matizado para que los desarrolladores puedan crear aplicaciones personalizadas impulsadas por IA sobre nuestras APIs. Lo más importante es que te permite conectar de forma segura y protegida los LLMs con tus propios datos, regidos por la lógica experta que defines, y chatear con tus datos.</p><h2>¿Listo para usar Agentes para chatear con tus datos?</h2><p>La mejor manera de consolidar lo que aprendiste es ensuciarte las manos. Prueba todo lo que comentamos hoy en nuestro <a href="https://www.elastic.co/training/elastic-ai-agents-mcp"><strong>taller práctico gratis e</strong></a> interactivo. Vas a pasar por todo este flujo y más en un entorno sandbox dedicado.</p><p>En un futuro blog, te mostraremos cómo usar una aplicación independiente que interactúa con nuestro agente <code>Financial Assistant</code> y nos adentraremos en el <strong>Protocolo de Contexto del Modelo (MCP)</strong> que lo hace todo posible. Y en un blog aparte hablaremos del soporte de Agent Builder para el protocolo en desarrollo Agent2Agent, o A2A.</p><p>¡Estad atentos y feliz construcción!</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/ai-agent-builder-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/ai-agent-builder-elasticsearch</guid>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[AI agéntica]]></category>
    <category><![CDATA[Dentro de Elastic]]></category>
    <dc:creator><![CDATA[Jeff Vestal]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbe5e78eeb775d715/6a17f2230b0bed719ddd369a/ca853555eaa213f10f1db8c0ab0a2bbacee97b88-1456x816.png" length="0" type="image/png"/>
    <pubDate>Thu, 25 Sep 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Construcción de flujos de trabajo agentes con IA con Elasticsearch]]></title>
    <description><![CDATA[Descubre Agent Builder, una nueva capa de IA en Elasticsearch que proporciona un marco para construir flujos de trabajo agentivos con IA, empleando la búsqueda híbrida para proporcionar a los agentes el contexto que necesitan para razonar y actuar.]]></description>
    <content:encoded><![CDATA[<p>Aquí en Elastic, estuvimos aportando contexto a los LLMs e interfaces conversacionales con asistentes de IA, mejoras avanzadas en RAG y bases de datos vectoriales. Recientemente, con el auge de los agentes de IA, vimos crecer la necesidad de contexto relevante y aprendimos que<strong> los agentes de IA de alto impacto necesitan una búsqueda excelente</strong>. Así que creamos nuevas capacidades nativas en Elastic Stack diseñadas para ayudar a desarrollar agentes de IA que aprovechen tus datos en Elasticsearch. Nos gustaría compartir nuestro progreso en este camino y hacia dónde vemos que irá a continuación.</p><h2>Constructor de Agentes: Una base para construir agentes de IA basados en datos</h2><p>La promesa de un agente de IA es sencilla: dale un objetivo y cumple su función. Pero para los promotores, la realidad es un serial de desafíos complejos. Primero, un agente solo es tan bueno como su percepción de su entorno y las herramientas que se le proporcionan para alcanzar los objetivos del usuario. Entonces, ofrecer el contexto adecuado a partir de un mar de datos empresariales diversos es un reto enorme. Por último, todo esto debe estar orquestado por un bucle de razonamiento fiable que pueda planear, ejecutar y aprender.</p><p>Para solucionar esto, los desarrolladores necesitan construir una pila compleja y frágil desde cero. La arquitectura actual de agentes requiere que unas múltiples piezas dispares: un LLM, una base de datos vectorial, un almacén de metadatos, sistemas separados para registro y trazado, y alguna forma de evaluar si todo funciona siquiera. Esto no es solo complejo; es costosa, propensa a errores y dificulta la construcción de los sistemas de IA de alta calidad y fiables que tus usuarios exigen.</p><p>Así que queremos hacerlo más sencillo. Para ello, nuestro enfoque es tomar las piezas esenciales de un agente eficaz basado en el contexto e integrarlas directamente en el núcleo de Elasticsearch con un nuevo conjunto de capacidades llamado <strong>Elastic AI Agent Builder</strong>. Esta nueva capa proporciona un marco con todos los bloques esenciales para crear Agentes de IA impulsados por Elasticsearch: un conjunto abierto de primitivas, protocolos basados en estándares y acceso seguro a los datos, para que puedas construir sistemas agenticos adaptados a datos y requisitos del mundo real:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2779dae5df010328/6a17e15eabe0f24f18dfe931/1ee1e73dd3f485ce86294d39490c98ce2a3d9925-1238x1072.png" alt="" /><p><strong>Ofrecer experiencias de IA</strong>: este es el objetivo final. Con nuestra Plataforma de IA de Búsqueda y tus datos como base, puedes construir cualquier tipo de aplicación de IA generativa: desde interfaces de chat personalizadas hasta integraciones con frameworks de agentes como LangChain o aplicaciones empresariales como Salesforce.</p><p><strong>Impulsado por Agentes y Herramientas</strong>: sobre la plataforma, exponemos una capa limpia y sencilla de abstracciones. Interactúas directamente con los Agentes y Herramientas, que puedes personalizar para adaptarlos a tus necesidades específicas. También puedes acceder a las capacidades de la plataforma a través de APIs robustas y estándares abiertos como MCP y A2A.</p><p><strong>Habilitado por la Plataforma de IA de Búsqueda</strong>: este es el motor central donde integramos los componentes. La avanzada base de datos vectorial, la lógica del agente, la construcción de consultas, las características de seguridad, el rastreo para evaluación, todo está aquí, gestionado y optimizado por Elastic.</p><p><strong>Desbloquear el poder de tus datos</strong>: la base de cualquier gran agente son los grandes datos. Nuestra plataforma comienza con la capacidad de ingerir o federar el acceso a todos los datos de tu compañía</p><h2>Edificio de agentes en la plataforma</h2><p>Agent Builder, integrado en la plataforma Search AI, proporciona un marco completo para el desarrollo de agentes. Se basa en cinco pilares clave, cada uno diseñado para abordar un aspecto crítico de la construcción y despliegue de sistemas de IA de grado de producción. Desglosemos cómo los agentes definen el objetivo, las herramientas proporcionan las capacidades, los estándares abiertos garantizan la interoperabilidad, la evaluación aporta transparencia y la seguridad proporciona la confianza.</p><h3>Agentes</h3><p>Los agentes son el bloque de construcción de más alto nivel en esta nueva capa de Elasticsearch. Un agente define el objetivo a alcanzar, el conjunto de herramientas disponibles para su ejecución y las fuentes de datos sobre las que puede operar. Los agentes no se limitan a interacciones conversacionales; Pueden impulsar flujos de trabajo completos, automatización de tareas o experiencias orientadas al usuario.</p><p>Cuando una consulta se dirige a un agente, sigue un ciclo estructurado:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt774ffd7df65bd01d/6a17e15f25daabd5cc08a17f/627ad1744b629bbe27359325702f40d97e40d1f4-704x852.png" alt="" /><ol><li><p>Interpreta tu aportación y objetivo</p></li><li><p>Seleccionar la herramienta y los argumentos adecuados para la ejecución</p></li><li><p>Razón sobre la respuesta de la herramienta</p></li><li><p>Decide si devolver un resultado o continuar con más invocaciones de herramientas</p></li></ol><p>Elastic se encarga de la orquestación, el contexto y la ejecución de este ciclo. Los desarrolladores se centran en definir <em>qué</em> debe hacer el agente: objetivos, herramientas y datos, mientras que el sistema gestiona <em>cómo</em> se llevan a cabo el razonamiento y los flujos de trabajo.</p><p><em>El agente por defecto</em></p><p>Nuestro primer agente construido sobre esta plataforma es un agente conversacional nativo en Kibana, que te da la capacidad de interactuar inmediatamente con tus datos. Proporciona una experiencia lista para usar, sin perder la extensión completa, y permite empezar a interactuar con tus datos de inmediato, sin configuraciones adicionales.</p><p>Puedes interactuar con esta experiencia directamente en Kibana a través de una nueva experiencia de chat o a través de API.</p><p>Consultar el agente por defecto a través de la API requiere solo una llamada:</p>POST kbn://api/agent_builder/converse
{
    "input": "what is our top portfolio account?"
}<p>Como las conversaciones son con estado, puedes seguir interactuando con un agente usando un conversation_id , o recuperar el historial completo de la conversación:</p>POST kbn://api/agent_builder/converse
{
    "input": "What about the second top?",
    "conversation_id": "ec757c6c-c3ed-4a83-8e2c-756238f008bb"
}

## get the full conversation
GET kbn://api/agent_builder/conversations/ec757c6c-c3ed-4a83-8e2c-756238f008bb<p><em>Agentes de Aduanas</em></p><p>Los desarrolladores también pueden crear sus propios agentes personalizados mediante APIs sencillas. Los agentes encapsulan instrucciones, herramientas y acceso a datos, creando motores de razonamiento personalizados.</p><p>Crear un agente personalizado es tan sencillo como hacer una sola llamada a la API. El ejemplo siguiente muestra un ejemplo: el campo "configuración" contiene todos los detalles clave, como instrucciones o herramientas disponibles:</p>POST kbn://api/agent_builder/agents
{
  "id": "custom_agent",
  "name": "My Custom Agent",
  "description": "Description of the custom agent",
  "configuration": {
      "instructions": "You are a log expert specialising in ...",
      "tools": 
...
   }
}<p>Una vez creado, el agente puede ser consultado directamente:</p>POST kbn://api/agent_builder/converse
{
    "input": "What news about DIA?",
    "agent_id": "custom_agent"
}<p>Este enfoque transforma al agente de un sistema complejo para construir desde cero en una unidad simple y declarativa de lógica de negocio, permitiéndote ofrecer automatización inteligente más rápido.</p><p>Para una visión profunda de cómo construir un agente especializado desde cero, consulta nuestra guía detallada paso a paso: <a href="https://www.elastic.co/search-labs/blog/ai-agent-builder-elasticsearch">Tu primer agente elástico: de una sola consulta a un chat impulsado por IA</a>.</p><h3>Herramientas</h3><p>Si los agentes definen <em>qué</em> lograr, las herramientas <em>definen cómo</em>.</p><p>Las herramientas exponen capacidades específicas del núcleo Elastic para que los agentes ejecuten y recuperen información o realicen una acción. Las herramientas pueden incluir capacidades clave como obtener índices, obtener mapeos, o capacidades más avanzadas como lenguaje natural a ES|QL.</p><p>Elasticsearch viene con un conjunto de herramientas predeterminadas optimizadas para necesidades comunes. Pero la verdadera flexibilidad viene de crear el tuyo propio. Al definir herramientas, decides exactamente qué consultas, índices y campos están expuestos a un agente con ES|QL, que te da un control preciso sobre la velocidad, la precisión y la seguridad.</p><p>Registrar una nueva herramienta también es tan sencillo como una sola llamada a la API. Podrías crear una herramienta que aproveche nuestro <a href="https://www.elastic.co/search-labs/blog/esql-timeline-of-improvements">ES|QL (Elasticsearch Query Language)</a> para encontrar noticias sobre un activo financiero específico:</p>POST kbn://api/agent_builder/tools
{
  "id": "news_on_asset",
  "type": "esql",
  "description": "Find news and reports about a particular asset where ...",
  "configuration": {
    "query": "FROM financial_news, financial_reports | where MATCH(company_symbol, ?symbol) OR MATCH(entities, ?symbol) | limit 5",
    "params": {
      "symbol": {
        "type": "keyword",
        "description": "The asset symbol"
      }
    }
  ...
  }
...
}<p>Una vez registrado, puedes asignar la nueva herramienta a tus agentes personalizados, dándoles un conjunto seleccionado de habilidades para razonar e invocar cuando sea lo adecuado.</p><p>Ofrecemos una plataforma para crear herramientas personalizadas según tus necesidades específicas, por ejemplo, con ES|QL que transforma al agente de un agente de propósito general a un experto específico de un dominio, basado en tus datos únicos y en el dominio de negocio.</p><h3>Estándares abiertos e interoperabilidad</h3><p>Los Agentes y Herramientas de Elasticsearch se exponen mediante APIs estándar abiertos, lo que facilita su integración como bloques fundamentales dentro del ecosistema más amplio de marcos agenticos. Nuestro enfoque es sencillo: nada de cajas negras. Queremos que seas capaz de tomar la fuerza central de Elastic en busca y combinarla con capacidades complementarias y otros sistemas agentes.</p><p>Para hacerlo posible, estamos exponiendo nuestras capacidades a través de APIs, protocolos emergentes y estándares abiertos.</p><p><em>Protocolo de Contexto de Modelo (MCP)</em></p><p><a href="https://www.elastic.co/search-labs/blog/model-context-protocol-elasticsearch">El Protocolo de Contexto de Modelo (MCP)</a> se está convirtiendo rápidamente en el estándar abierto para conectar herramientas entre sistemas. Al soportar MCP, Elasticsearch puede conectar IA conversacional con tus bases de datos, índices y APIs externas. Con un servidor MCP remoto integrado en Elastic Stack, cualquier cliente compatible con MCP puede acceder a las herramientas de Elastic y usarlas como bloques de construcción en tus flujos de trabajo agenticos más amplios.</p><p>Esto no es una calle de sentido único. También podrás importar herramientas desde servidores MCP externos y ponerlas a disposición dentro de Elasticsearch. Pronto, los servidores MCP probablemente estarán disponibles para casi todo y serán mucho más completos que cualquier cosa que crearíamos nosotros mismos. Elastic ofrece búsqueda y recuperación a gran escala, y puedes combinarlo con capacidades especializadas de otras plataformas para crear agentes efectivos.</p><p><em>Agente a Agente (A2A)</em></p><p>También estamos trabajando en el soporte Agent-to-Agent (A2A). Mientras que MCP trata de conectar herramientas, A2A trata de conectar agentes. Con un servidor A2A, los agentes Elastic que construyas podrán comunicar directamente con agentes de otros sistemas: compartiendo contexto, delegando tareas y coordinando flujos de trabajo.</p><p>Piénsalo como interoperabilidad en la capa de razonamiento. Tu agente de Elastic podría encargar de la búsqueda y recuperación, luego delegar una tarea a un soporte especializado o a un agente de TI, y obtener el resultado sin problemas. El resultado es un ecosistema de agentes cooperantes, cada uno haciendo lo que mejor sabe hacer.</p><p>En última instancia, la adopción de MCP y A2A refuerza nuestro compromiso con el papel de Elasticsearch como ciudadano de primera clase, cerciorando una integración abierta en todo el ecosistema agente más amplio.</p><h3>Rastreo y evaluación</h3><p>A medida que la búsqueda se integra con los agentes, el desafío de una evaluación efectiva se vuelve crítico. Para desplegar agentes con confianza en entornos empresariales reales, necesitas la seguridad de que no solo son precisos, sino también eficientes y fiables. ¿Cómo mides el rendimiento, diagnosticas una mala respuesta o mejoras la línea base? Todo empieza por la visibilidad.</p><p>Por eso diseñamos nuestras APIs de agentes para que sean transparentes desde cero. Considera esta simple interacción con el agente:</p>POST kbn://api/agent_builder/converse
{
    "input": "what is our top portfolio account?"
}<p>La respuesta incluye no solo la respuesta final, sino la traza completa de ejecución, detallando qué herramientas seleccionó el agente, los parámetros que empleó y los resultados de cada paso.</p>{
  "conversation_id": "db5c0c8b-12bf-4928-a57e-d99129ad2fea",
  "steps": [
    {
      "type": "tool_call",
      "tool_call_id": "tooluse_Nfqr3mwtR92HTRIsTcGXZQ",
      "tool_id": ".index_explorer",
      "params": {
        "query": "indices containing portfolio data"
      },
      "results": [...]
    }
    // ... more steps ...
  ],
  "response": {
    "message": "Based on the information I've gathered...."
  }
}<p>Un rastreo y registro exhaustivos son esenciales para un bucle de mejora continua, y pronto podrás almacenar y ver estos rastros de agentes directamente en Elasticsearch. Mejor aún, estas trazas están construidas sobre el protocolo OpenTelemetry, lo que garantiza que estén estandarizadas y sean portátiles para su integración con la plataforma de observabilidad que elija.</p><p>Este nivel de detalle es la base de un verdadero bucle de mejora continua. Te permite construir un conjunto completo de pruebas, depurar fallos, identificar modos de fallo para evitar regresiones y capturar patrones exitosos para afinar el rendimiento. En última instancia, este enfoque basado en datos es la clave para transformar un prototipo prometedor en un sistema de IA fiable y de calidad de producción.</p><h3>Seguridad</h3><p>A medida que los agentes y herramientas se vuelven más capaces, la seguridad no es opcional, es fundamental. Exponer APIs, automatizar tareas y flujos de trabajo requiere que los sistemas empresariales sean de confianza. Especialmente a medida que los Agentes empiezan a automatizar más flujos de trabajo, la capacidad de protegerlos y cerciorar de que cumplen con los requisitos empresariales es esencial.</p><p>Las capacidades, por encima de todo, heredan los controles ya disponibles en Elastic hoy en día, incluyendo <a href="https://www.elastic.co/search-labs/blog/rag-and-rbac-integration">el control de acceso basado en roles (RBAC)</a> para llamadas API y gestión de claves API. También estamos extendiendo los mismos controles a nuevos protocolos como MCP. Eso significa soporte para estándares como OAuth, así como la capacidad de conectar mecanismos de autenticación personalizados.</p><p>Nuestro objetivo es ofrecerte la flexibilidad para experimentar con agentes y herramientas, manteniendo al mismo tiempo el nivel de seguridad, cumplimiento y gobernanza que exige tu organización.</p><h2>Lo que viene después</h2><p>No solo estamos agregando funcionalidades; estamos ampliando Elasticsearch para la ingeniería de contexto agentica. Planeamos desarrollar en el futuro basándonos en estos principios:</p><p>1. Compromiso con el código abierto y los estándares</p><p>Nuestro compromiso con el código abierto y los estándares abiertos garantiza que estas capacidades sigan siendo interoperables con los marcos agentes externos. Siempre podrás conectar, ampliar y componer agentes en todo tu ecosistema, manteniendo tus datos y flujos de trabajo bajo tu control.</p><p>2. Valor del contexto</p><p>El contexto de un agente de IA es su mayor activo. Gestionar el contexto mientras los agentes realizan búsquedas y operaciones de flujo de trabajo puede ser una tarea desafiante. Estamos aprovechando las fortalezas principales de Elastic para resolver la ingeniería de contexto, cerciorando que la información más relevante esté siempre disponible para tu agente.</p><p>3. Enfoque en flujos de datos agentes</p><p>De ahora en adelante, los agentes serán una fuente cada vez mayor de datos, incluyendo la salida de los agentes (documentos generados, reportes, visualizaciones) y el rastro de ejecución de los agentes (su razonamiento, llamadas a herramientas, memoria/contexto). Elastic es muy adecuado para manejar este tipo de datos, y estamos trabajando en investigaciones relacionadas con la realización de análisis, evaluación y mejora automatizada empleando estos datos.</p><p>4. Seguridad y Protección desde el Diseño</p><p>Los agentes de IA introducen un conjunto completamente nuevo de desafíos de seguridad y protección. Elastic siempre fue líder en soluciones seguras, y seguimos incorporando barreras de seguridad de nivel empresarial, controles de acceso y principios de "confianza cero".</p><p>5. Integrado en la plataforma</p><p>Las capacidades para construir agentes de IA están integradas en la plataforma Elasticsearch. Esto significa que capacidades a nivel de plataforma como el trazo, la evaluación, la visualización y el análisis son todas aplicables a los agentes. Quiero desarrollar paneles basados en la ejecución de agentes, eso está incorporado. Quiero evaluar el rendimiento del agente de IA usando análisis de sentimiento? La plataforma lo permite. Esto te permite construir un ciclo de vida completo alrededor de tus experiencias con IA.</p><p>El objetivo de Elastic es proporcionarte las interfaces para construir IA conversacional y flujos de trabajo automatizados que estén totalmente integrados, sean extensibles y estén basados en tus datos. Pronto se compartirán más detalles técnicos y avances.</p><p>Agent Builder ya está disponible en vista previa privada. <a href="https://www.elastic.co/contact?pg=global&amp;plcmt=nav&amp;cta=205352">Contáctate con nosotros</a> para aplicar acceso. ¿Tienes preguntas u opiniones? Conéctate con nuestra comunidad de desarrolladores en nuestro <a href="https://elasticstack.slack.com/archives/C09GRHEQ4AG"><strong>espacio de trabajo de Slack</strong></a> o en nuestro <a href="https://discuss.elastic.co/c/search/84"><strong>foro de discusión</strong></a>.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/ai-agentic-workflows-elastic-ai-agent-builder</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/ai-agentic-workflows-elastic-ai-agent-builder</guid>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[AI agéntica]]></category>
    <category><![CDATA[Dentro de Elastic]]></category>
    <dc:creator><![CDATA[Anish Mathur,Dana Juratoni]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt16a3d8736bf086e0/6a17e1616864a45410b686c7/71876470119e02a45bcbfcbf27a3e110328bbd14-1020x654.png" length="0" type="image/png"/>
    <pubDate>Tue, 23 Sep 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Construcción de un asistente RAG agente con JavaScript, Mastra y Elasticsearch]]></title>
    <description><![CDATA[Aprende a construir agentes de IA en el ecosistema JavaScript]]></description>
    <content:encoded><![CDATA[<p>Esta idea se me ocurrió en medio de una liga de baloncesto de fantasía muy intensa y de alto riesgo. Me preguntaba: <em>¿Podría crear un agente de IA que me ayudara a dominar mis enfrentamientos semanales? ¡Absolutamente!</em></p><p>En esta entrada, exploraremos cómo construir un asistente RAG agente usando <a href="https://mastra.ai/en/docs">Mastra</a> y una aplicación sitio web ligero en JavaScript para interactuar con él. Al conectar este agente con Elasticsearch, le damos acceso a datos estructurados de jugadores y la capacidad de ejecutar agregaciones estadísticas en tiempo real, para ofrecerte recomendaciones basadas en estadísticas de jugadores. Visita el <a href="https://github.com/jdarmada/nba-ai-assistant-js.git">repositorio</a> de GitHub para seguirle; el <a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/README.md">README</a> proporciona instrucciones sobre cómo clonar y ejecutar la aplicación por tu cuenta. </p><p>Así debería ver cuando todo esté montado:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt63ea3e7a09306fbf/6a17f1d97f6f150e22c09c50/1c73bd1dc1b5fe54f025c7a2b7c322acc9122f3a-1999x1393.png" alt="" /><p>Nota: Esta entrada del blog se basa en "<a href="https://www.elastic.co/search-labs/blog/ai-agents-ai-sdk-elasticsearch">Construcción de agentes de IA con SDK de IA y Elastic</a>". Si eres nuevo en los agentes de IA en general y en qué podrían servir, empieza por ahí.
</p><h2><strong>Visión general de la arquitectura</strong></h2><p>En el núcleo del sistema se encuentra un gran modelo de lenguaje (LLM), que actúa como motor de razonamiento del agente (el cerebro). Interpreta la entrada del usuario, decide qué herramientas llamar y orquesta los pasos necesarios para generar una respuesta relevante.</p><p>El propio agente está estructurado por Mastra, un framework de agentes dentro del ecosistema JavaScript. Mastra envuelve el LLM con infraestructura backend, lo expone como un endpoint API y proporciona una interfaz para definir herramientas, indicaciones del sistema y comportamiento de agentes.</p><p>En el frontend, usamos <a href="https://vite.dev/guide/">Vite</a> para andamiar rápidamente una aplicación sitio web React que proporciona una interfaz de chat para enviar consultas al agente y recibir sus respuestas.</p><p>Por último, tenemos Elasticsearch, que almacena estadísticas de jugadores y datos de emparejamientos que el agente puede consultar y agregar.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte13f09493f217047/6a17f1db1d1b83178d93e546/443bdc00d84ed1dd49e9f9e431e86ca4b0892563-1999x977.png" alt="" /><h2><strong>Fondo</strong></h2><p>Vamos a repasar algunos conceptos fundamentales:</p><h3><strong>¿Qué es el RAG agente?</strong></h3><p>Los agentes de IA pueden interactuar con otros sistemas, operar de forma independiente y realizar acciones basadas en sus parámetros definidos. El RAG agente combina la autonomía de un agente de IA con los principios de generación aumentada por recuperación, permitiendo a un LLM elegir qué herramientas llamar y qué datos usar como contexto para generar una respuesta. Lee más sobre <a href="https://www.elastic.co/search-labs/blog/retrieval-augmented-generation-rag">RAG aquí</a>.</p><h3><strong>Elegir un framework, ¿por qué ir más allá del SDK de IA?</strong></h3><p>Hay muchos frameworks de agentes de IA disponibles y probablemente oíste hablar de los más populares como <a href="https://www.elastic.co/search-labs/blog/using-crewai-with-elasticsearch">CrewAI</a>, <a href="https://www.elastic.co/search-labs/blog/using-autogen-with-elasticsearch">AutoGen</a> y <a href="https://www.elastic.co/search-labs/blog/build-rag-workflow-langgraph-elasticsearch">LangGraph</a>. La mayoría de estos frameworks comparten un conjunto común de funcionalidades, incluyendo soporte para diferentes modelos, uso de herramientas y gestión de memoria.</p><p>Aquí tienes una <a href="https://docs.google.com/spreadsheets/d/1B37VxTBuGLeTSPVWtz7UMsCdtXrqV5hCjWkbHN8tfAo/edit?gid=0#gid=0">hoja comparativa</a> de marcos de Harrison Chase (CEO de LangChain).</p><p>Lo que despertó mi interés con Mastra es que es un framework JavaScript-first diseñado para que desarrolladores full-stack integren fácilmente agentes en su ecosistema. El AI-SDK de Vercel también hace la mayoría de esto, pero donde Mastra destaca es cuando tus proyectos incluyen flujos de trabajo de agentes más complejos. Mastra mejora los patrones base establecido por el AI-SDK y en este proyecto los usaremos conjuntamente.</p><h3><strong>Marcos y consideraciones sobre la elección del modelo</strong></h3><p>Aunque estos frameworks pueden ayudarte a construir agentes de IA rápidamente, existen algunos inconvenientes a tener en cuenta. Por ejemplo, al usar cualquier otro framework fuera de agentes de IA o cualquier capa de abstracción en general, pierdes un poco de control. Si el LLM no emplea correctamente las herramientas o hace algo que no quieres, la abstracción dificulta la depuración. Aun así, en mi opinión, este equilibrio merece la facilidad y rapidez que obtienes al construir, especialmente porque estos frameworks están ganando impulso y se iteran constantemente.</p><p>De nuevo, estos frameworks son independientes de los modelos, lo que significa que puedes conectar y jugar diferentes modelos; recuerda que los modelos varían en los conjuntos de datos con los que fueron capacitados y, a su vez, varían en las respuestas que dan. Algunos modelos ni siquiera soportan la llamada de herramientas. Así que es posible cambiar y probar diferentes modelos para ver cuál te da las mejores respuestas, pero ten en cuenta que probablemente tendrás que reescribir el prompt del sistema para cada uno. Por ejemplo, usando Llama3.3 en comparación con GPT-4o, implica mucho más indicación e instrucciones específicas para obtener la respuesta que buscas.</p><h3><strong>Baloncesto de fantasía de la NBA</strong></h3><p>El baloncesto de fantasía consiste en empezar una liga con un grupo de colegas (advertencia, dependiendo de lo competitivo que sea tu grupo, esto podría afectar al estado de tus amistades), normalmente con algo de dinero en juego. Cada uno de vosotros selecciona un equipo de 10 jugadores para competir contra los 10 jugadores de otro amigo alternando semanalmente. Los puntos que contribuyen a tu puntaje global son cómo se desempeña cada uno de tus jugadores contra sus oponentes en una semana determinada.</p><p>Si un jugador de tu equipo se lesiona, es suspendido, etc., hay una lista de jugadores agentes libres disponibles para agregar a tu equipo. Aquí es donde ocurre gran parte de la dificultad en los deportes de fantasía, porque solo tienes un número limitado de fichas y todos están constantemente a la caza del mejor jugador.</p><p>Aquí es donde nuestro asistente de IA de la NBA brillará, especialmente en situaciones en las que tienes que decidir rápidamente qué jugador elegir. En lugar de tener que buscar manualmente cómo rinde un jugador contra un oponente específico, el asistente puede encontrar esos datos rápidamente y comparar promedios para darte una recomendación informada.</p><p>Ahora que ya sabes lo básico sobre el RAG agente y el baloncesto fantasy de la NBA, vamos a verlo en la práctica.</p><h2><strong>Construcción del proyecto</strong></h2><p>Si te quedas atascado en algún momento o no quieres montarlo desde cero, por favor consulta el <a href="https://github.com/jdarmada/nba-ai-assistant-js.git">repositorio</a>.</p><h3><strong>Lo que vamos a cubrir</strong></h3><ol><li><p><strong>Andamiaje del proyecto:</strong></p><ol><li><p><strong>Backend (Mastra):</strong> Usa NPX Create mastra@latest para andamiar el backend y definir la lógica del agente.</p></li><li><p><strong>Frontend (Vite + React):</strong> Usa npm create vite@latest para construir la interfaz de chat del frontend y así interactuar con el agente.</p></li></ol></li><li><p><strong>Configuración de variables de entorno</strong></p><ol><li><p>Instala dotenv para gestionar las variables del entorno.</p></li><li><p>Crea un .env archiva y proporciona las variables requeridas.</p></li></ol></li><li><p><strong>Configuración de Elasticsearch</strong></p><ol><li><p>Crea un clúster de Elasticsearch (ya sea localmente o en la nube).</p></li><li><p>Instala el cliente oficial de Elasticsearch.</p></li><li><p>Cerciórate de que las variables del entorno sean accesibles.</p></li><li><p>Establece conexión con el cliente.</p></li></ol></li><li><p><strong>Ingiriendo en masa datos de la NBA en Elasticsearch</strong></p><ol><li><p>Crea un índice con los mapeos apropiados para permitir agregaciones.</p></li><li><p>Ingirir en masa las estadísticas de los jugadores de un archivo CSV en un índice de Elasticsearch.</p></li></ol></li><li><p><strong>Definir agregaciones de Elasticsearch</strong></p><ol><li><p>Consulta para calcular promedios históricos contra un oponente específico.</p></li><li><p>Consulta para calcular los promedios de temporada contra un rival específico.</p></li></ol></li><li><p><strong>Archivo utilitario de comparación de jugadores</strong></p><ol><li><p>Consolida funciones auxiliares y agregaciones de Elasticsearch.</p></li></ol></li><li><p><strong>Construcción del agente</strong></p><ol><li><p>Agrega la definición del agente y el prompt del sistema.</p></li><li><p>Instala Zod y define herramientas.</p></li><li><p>Agregar una configuración de middleware para manejar CORS.</p></li></ol></li><li><p><strong>Integración del frontend</strong></p><ol><li><p>Usando el useChat de AI-SDK para interactuar con el agente.</p></li><li><p>Crea la interfaz para mantener conversaciones bien formateadas.</p></li></ol></li><li><p><strong>Ejecutando la aplicación</strong></p><ol><li><p>Inicia tanto el backend (servidor Mestra) como el frontend (app React).</p></li><li><p>Ejemplos de consultas y uso.</p></li></ol></li><li><p><strong>Qué sigue: Hacer que el agente sea más inteligente</strong></p><ol><li><p>Agregar capacidades de búsqueda semántica para permitir recomendaciones más profundas.</p></li><li><p>Habilitar la consulta dinámica moviendo la lógica de búsqueda al servidor Elasticsearch MCP (Model Context Protocol).</p></li></ol></li></ol><h3><strong>Prerrequisitos</strong></h3><ul><li><p><strong>Node.js y NPM</strong>: Tanto el backend como el frontend funcionan en Node. Cerciórate de tener instalados Node 18+ y npm v9+ (que vienen incluidos con Node 18+).</p></li><li><p><strong>Clúster de Elasticsearch:</strong> Un clúster activo de Elasticsearch, ya sea localmente o en la nube.</p></li><li><p><strong>Clave API de OpenAI</strong>: Genera una en la página de claves API <a href="https://platform.openai.com/api-keys">del portal para desarrolladores de OpenAI</a>.</p></li></ul><p></p><h3><strong>Estructura del proyecto</strong></h3><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt749baa120552e4ab/6a17f1dd1d1b83bfe993e54a/1c0bde11ad0eead523a95e03b9b905aa776e3fd1-1420x934.png" alt="" /><h4><strong>Paso 1: Estructurar el proyecto</strong></h4><ol><li><p>Primero, crea el directorio nba-ai-assistant-js y navega dentro usando: </p></li></ol>mkdir nba-ai-assistant-js &amp;&amp; cd nba-ai-assistant-js<p><strong>Backend:</strong></p><ol><li><p>Usa la herramienta de creación de Mastra con el comando: </p></li></ol>npx create-mastra@latest<p>2. Deberías recibir algunos prompts en tu terminal; para el primero, llamaremos al backend del proyecto:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt65abf68fe588e968/6a17f1de63baff2814741d5b/de2725031ed6837db99a979efcdd0ece1e197dbb-608x84.png" alt="" /><p>3. A continuación, mantendremos la estructura predeterminada para almacenar los archivos Mastra, así que introduzca <code>src/</code>.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt89bd829fcf0ae6b9/6a17f1e04b055dd30e432302/88919d9ff1852126395e1fcd700ecb1b59aac63c-866x116.png" alt="" /><p>4. Luego, elegiremos OpenAI como nuestro proveedor de LLM por defecto.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfd167cc77a40b9a8/6a17f1e11480099e29b48863/2328761e769f3ded134e5a21e8a0bf8f41e88f68-404x210.png" alt="" /><p>5. Finalmente, te pedirá la clave de la API de OpenAI. Por ahora, elegiremos la opción de saltarlo y proporcionarlo más adelante en un archivo<code> .env</code> .</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt12654151ed495370/6a17f1e22f4a5c0f84fa89f9/0662de9bd28758e377e4c63df8d08b479068ce63-444x120.png" alt="" /><p><strong>Frontend:</strong></p><ol><li><p>Vuelve al directorio raíz y ejecuta la <a href="https://vite.dev/guide/">herramienta de creación de Vite</a> usando este comando: <code>npm create vite@latest frontend -- --template react</code></p></li></ol><p>Esto debería crear una aplicación ligera de React llamada <code>frontend</code> con una plantilla específica para React.</p><p>Si todo va bien, dentro de tu directorio de proyecto, deberías estar mirando un directorio backend que contenga el código Mastra y un directorio <code>frontend</code> con tu app React.</p><p></p><h4><strong>Paso 2: Configuración de variables de entorno</strong></h4><ol><li><p>Para gestionar claves sensibles, usaremos el paquete <code>dotenv</code> para cargar nuestras variables de entorno desde el .env archivo. Navega al directorio del backend e instala <code>dotenv</code>:</p></li></ol>cd backend
npm install dotenv --save<p>2. Mientras está en el directorio backend, se proporciona un archivo example.env con las variables adecuadas para rellenar. Si creas el tuyo propio, cerciórate de incluir las siguientes variables:</p># OpenAI Configuration
OPENAI_API_KEY=your_openai_api_key_here

# Elasticsearch Configuration
ELASTIC_ENDPOINT=your_elasticsearch_endpoint_here
ELASTIC_API_KEY=your_elasticsearch_api_key_here
<p></p><p>Nota: Cerciórate de que este archivo esté excluido de tu control de versiones agregando <code>.env</code> a <code>.gitignore</code>.</p><h4><strong>Paso 3: Configuración de Elasticsearch</strong></h4><p>Primero, necesitas un clúster activo de Elasticsearch. Hay dos opciones:</p><ul><li><p><strong>Opción A: Usar Elasticsearch Cloud</strong></p><ul><li><p>Apúntate a <a href="https://cloud.elastic.co/registration">Elastic Cloud</a></p></li><li><p>Crear un nuevo despliegue</p></li><li><p>Obtén la URL de tu endpoint y la clave API (codificada)</p></li></ul></li><li><p><strong>Opción B: Ejecutar Elasticsearch localmente</strong></p><ul><li><p>Instala y ejecuta Elasticsearch localmente</p></li><li><p>Usa http://localhost:9200 como punto final</p></li><li><p>Generar una clave API</p></li></ul></li></ul><p></p><p><strong>Instalación del cliente Elasticsearch en el backend:</strong></p><ol><li><p>Primero, instala el cliente oficial de Elasticsearch en tu directorio backend:</p></li></ol>npm install @elastic/elasticsearch<p>2. Luego crea una lib de directorios para almacenar funciones reutilizables y navega hacia ella:</p>mkdir lib &amp;&amp; cd lib<p>3. Dentro, crea un nuevo archivo llamado <a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/lib/elasticClient.js">elasticClient.js</a>. Este archivo inicializará el cliente Elasticsearch y lo expondrá para su uso en todo tu proyecto.</p><p>4. Como usamos módulos ECMAScript (ESM), __dirname and __nombre de archivo no están disponibles. Para cerciorarte de que tus variables de entorno se carguen correctamente desde el archivo .env Archivo en la carpeta de backend, agrega esta configuración en la parte superior de tu archivo:</p>import { config } from 'dotenv';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
import { Client } from '@elastic/elasticsearch';

// Grab current directory and load .env from backend folder
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const envPath = join(__dirname, '../.env');

// Load environment variables from the correct path
config({ path: envPath });<p>5. Ahora, inicializa el cliente Elasticsearch usando tus variables de entorno y comprueba la conexión:</p>//Elastic client Initialization, make sure environment variables are being loaded in correctly
const config= {
    node: `${process.env.ELASTIC_ENDPOINT}`,
    auth: {
        apiKey: `${process.env.ELASTIC_API_KEY}`,
    },
};

export const elasticClient = new Client(config);

//Check if the client is connected
async function checkConnection() { 
    try {
        const info = await elasticClient.info();
        console.log('Elasticsearch is connected:', info);
    } catch (error) {
        console.error('Elasticsearch connection error:', error);
    }
}

checkConnection();
<p>Ahora, podemos importar esta instancia cliente a cualquier archivo que necesite interactuar con tu clúster de Elasticsearch.</p><p></p><h4><strong>Paso 4: Ingirir en masa datos de la NBA en Elasticsearch</strong></h4><p><strong>Conjunto de datos:</strong></p><p>Para este proyecto, consultaremos los conjuntos de datos disponibles en el <a href="https://github.com/jdarmada/nba-ai-assistant-js/tree/main/backend">directorio de backend/datos</a> del repositorio. Nuestro asistente de la NBA empleará estos datos como base de conocimiento para realizar comparaciones estadísticas y generar recomendaciones.</p><ul><li><p><a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/data/sample_nba_data.csv">sample_player_game_stats.csv</a> - Estadísticas de juego de un jugador (por ejemplo, puntos, rebotes, robos, etc., por partido por jugador a lo largo de toda su carrera en la NBA). Emplearemos este conjunto de datos para realizar agregaciones. (Nota: Estos son datos simulados, pregenerados para fines demo y no provenientes de fuentes oficiales de la NBA.)</p></li><li><p><a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/data/playerAndTeamInfo.js">playerAndTeamInfo.js</a> - Sustituye los metadatos de jugadores y equipos que normalmente proporcionaría una llamada a la API para que el agente pueda relacionar los nombres de jugadores y equipos con IDs. Como usamos datos de muestra, no queremos la sobrecarga de buscar desde una API externa, así que codificamos de forma fija algunos valores que el agente puede referenciar.</p></li></ul><p></p><p><strong>Implementación:</strong></p><ol><li><p>Mientras estés en el directorio <code>backend/lib</code> , crea un archivo llamado <a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/lib/playerDataIngestion.js">playerDataIngestion.js</a>.</p></li><li><p>Configura las importaciones, resuelve la ruta del archivo CSV y configura el análisis sintáctico. De nuevo, como estamos usando ESM, necesitamos reconstruir <code>__dirname</code> para resolver el camino hacia el CSV de muestra. Además, importaremos <a href="http://node.js/">Node.js</a> módulos incorporados, <code>fs</code> y <code>readline</code>, para analizar el archivo CSV dado línea por línea.</p></li></ol>import fs from 'fs';
import readline from 'readline';
import path from 'path';
import { fileURLToPath } from 'url';
import { elasticClient } from './elasticClient.js';

const indexName = 'sample-nba-player-data'; //Replace with your preferred index name

//Since we are using ES modules __dirname and __filename don't exist, so this is a workaround that allows us to use the absolute file path for our sample data.
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const filePath = path.resolve(__dirname, '../data/sample_nba_data.csv');<p>Esto te prepara para leer y analizar eficientemente el CSV cuando lleguemos al paso de la ingestión masiva.</p><p>3. Crear un índice con el mapeo adecuado. Aunque Elasticsearch puede inferir automáticamente los tipos de campos con <a href="https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/dynamic">mapeo dinámico</a>, queremos ser explícitos aquí para que cada estadística se trate como un campo numérico. Esto es importante porque usaremos estos campos para agregaciones más adelante. También queremos usar el tipo <code>float </code>para estadísticas como puntos, rebotes, etc., para cerciorarnos de incluir valores decimales. Finalmente, queremos agregar la propiedad de mapeo <code>dynamic: 'strict'</code> para que Elasticsearch no mapee dinámicamente campos no reconocidos. 
</p>// Function to create an index with mappings
async function createIndex() {
    try {
        // Check if the index already exists
        const exists = await elasticClient.indices.exists({ index: indexName });

        if (exists) {
            console.log(`Index "${indexName}" already exists, deleting it now.`);
            await elasticClient.indices.delete({ index: indexName });
            console.log(`Deleted index "${indexName}".`);
        }
        // Create the index with mappings
        const response = await elasticClient.indices.create({
            index: indexName,
            body: {
                mappings: {
                    dynamic: 'strict', // Prevent dynamic mapping
                    properties: {
                        game_id: { type: 'integer' },
                        game_date: { type: 'date' },
                        player_id: { type: 'integer' },
                        player_full_name: { type: 'text' },
                        player_team_id: { type: 'integer' },
                        player_team_name: { type: 'text' },
                        home_team: { type: 'boolean' },
                        opponent_team_id: { type: 'integer' },
                        opponent_team_name: { type: 'text' },
                        points: { type: 'float' },
                        rebounds: { type: 'float' },
                        assists: { type: 'float' },
                        steals: { type: 'float' },
                        blocks: { type: 'float' },
                        fg_percentage: { type: 'float' },
                        minutes_played: { type: 'float' },
                    },
                },
            },
        });

        console.log('Index created:', response);
        return true;
    } catch (error) {
        console.error('Error creating index:', error);
        return false;
    }
}
<p>4. Agregar la función para ingirte en masa los datos CSV en tu índice de Elasticsearch. Dentro del bloque de código, saltamos la línea de cabecera. Luego, divide cada elemento de línea por una coma y empújalos en el objeto documento. Este paso también los limpia y cerciora que sean del tipo adecuado. A continuación, enviamos los documentos al array bulkBody junto con la información del índice, que servirá como carga útil para la ingestión masiva en Elasticsearch.</p>async function bulkIngestCsv(filePath) {
    const readStream = fs.createReadStream(filePath);
    const rl = readline.createInterface({
        input: readStream,
        crlfDelay: Infinity,
    });

    const bulkBody = [];
    let lineNum = 0;

    //Skip the header line
    let headerLine = true;
    for await (const line of rl) {
        if (headerLine) {
            headerLine = false;
            continue;
        }
        lineNum++;

        // Split the line by comma and remove whitespace
        const [
            game_id,
            game_date,
            player_id,
            player_full_name,
            player_team_id,
            player_team_name,
            home_team,
            opponent_team_id,
            opponent_team_name,
            points,
            rebounds,
            assists,
            steals,
            blocks,
            fg_percentage,
            minutes_played,
        ] = line.split(',');

        // Create a document object
        const document = {
            game_id: parseInt(game_id),
            game_date: game_date.trim(),
            player_id: parseInt(player_id),
            player_full_name: player_full_name.trim(),
            player_team_id: parseInt(player_team_id),
            player_team_name: player_team_name.trim(),
            home_team: home_team.trim() === 'True', // Converts True/False into a boolean
            opponent_team_id: parseInt(opponent_team_id),
            opponent_team_name: opponent_team_name.trim(),
            points: parseFloat(points),
            rebounds: parseFloat(rebounds),
            assists: parseFloat(assists),
            steals: parseFloat(steals),
            blocks: parseFloat(blocks),
            fg_percentage: parseFloat(fg_percentage),
            minutes_played: parseFloat(minutes_played),
        };

        // Prepare the bulk operation format
        bulkBody.push({ index: { _index: indexName } });
        bulkBody.push(document);
    }

    console.log(`Parsed ${lineNum} lines from CSV`);
<p>5. Luego, podemos usar la <a href="https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-bulk">API Bulk</a> de Elasticsearch con <code>elasticClient.bulk()</code> para ingirse varios documentos en una sola solicitud. La gestión de errores a continuación está estructurada para darte un recuento de cuántos documentos no se ingirieron y cuántos fueron exitosos.</p>try {
        // Perform the bulk request
        const response = await elasticClient.bulk({ body: bulkBody });

        if (response.errors) {
            console.log('Bulk Ingestion had some hiccups:');

            // Count successful vs failed operations
            let successCount = 0;
            let errorCount = 0;
            const errorDetails = [];

            response.items.forEach((item, index) =&gt; {
                const operation = item.index || item.create || item.update || item.delete;
                if (operation.error) {
                    errorCount++;
                    errorDetails.push({
                        document: index + 1,
                        error: operation.error,
                    });
                } else {
                    successCount++;
                }
            });

            console.log(`Successfully indexed: ${successCount} documents`);
            console.log(`Failed to index: ${errorCount} documents, here are the details`, errorDetails);

        } else {
            console.log(`Bulk Ingestion fully successful!`);
        }

    } catch (error) {
        console.error('Error performing bulk ingestion:', error);
    }
}
<p>6. Ejecuta la función <code>main()</code> a continuación para ejecutar secuencialmente las funciones <code>createIndex()</code> y <code>bulkIngestCsv()</code> .</p>// Run this function
async function main() {
    const result = await createIndex();
    if (!result) {
        console.error('Index setup failed. Aborting.');
        return;
    }

    await bulkIngestCsv(filePath);
    console.log('Bulk ingestion completed!');
}

main();
<p>Si ves un registro de consola que indique que la ingesta masiva fue exitosa, realiza una comprobación rápida en tu índice de Elasticsearch para ver si los documentos realmente se ingirieron con éxito.</p><h4><strong>Paso 5: Definición de agregaciones de Elasticsearch y consolidación</strong></h4><p>Estas serán las funciones principales que se emplearán cuando definamos las herramientas para el Agente de IA para comparar las estadísticas de los jugadores entre sí.</p><p>1. Navega al directorio <code>backend/lib</code> y crea un archivo llamado <a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/lib/elasticAggs.js">elasticAggs.js</a>.</p><p>2. Agregar la consulta siguiente para calcular los promedios históricos de un jugador frente a un oponente específico. Esta consulta emplea un <a href="https://www.elastic.co/search-labs/tutorials/search-tutorial/full-text-search/filters">filtro</a> <code>bool</code> con 2 condiciones: una que coincide con <code>player_id</code> y otra que coincide con la <code>opponent_team_id</code>, para recuperar solo los juegos relevantes. No necesitamos devolver ningún documento, solo nos importan las agregaciones, así que establecemos <code>size:0</code>. Bajo el bloque <code>aggs</code> , ejecutamos múltiples <a href="https://www.elastic.co/docs/explore-analyze/query-filter/aggregations">agregaciones métricas</a> en paralelo en campos como <code>points, rebounds, assists, steals, blocks</code> y <code>fg_percentage</code> para calcular sus valores medios. Los LLMs pueden ser impredecibles con los cálculos y esto transfiere ese proceso a Elasticsearch, cerciorando que nuestro asistente de IA de la NBA tenga acceso a datos precisos.</p>export async function getHistoricalAveragesAgainstOpponent(player_id, opponent_team_id) {
    try {
        //Query for Historical Averages
        const historicalQuery = await elasticClient.search({
            index: 'sample-nba-player-data', 
            size: 0,
            query: {
                bool: {
                    must: [
                        {
                            term: {
                                player_id: {
                                    value: player_id,
                                },
                            },
                        },
                        {
                            term: {
                                opponent_team_id: {
                                    value: opponent_team_id,
                                },
                            },
                        },
                    ],
                },
            },
            aggs: {
                avg_points: { avg: { field: 'points' } },
                avg_rebounds: { avg: { field: 'rebounds' } },
                avg_assists: { avg: { field: 'assists' } },
                avg_steals: { avg: { field: 'steals' } },
                avg_blocks: { avg: { field: 'blocks' } },
             avg_fg_percentage: { avg: { field: 'fg_percentage' } },
            },
        });

        return {
            points: historicalQuery.aggregations.avg_points.value || 0,
            rebounds: historicalQuery.aggregations.avg_rebounds.value || 0,
            assists: historicalQuery.aggregations.avg_assists.value || 0,
            steals: historicalQuery.aggregations.avg_steals.value || 0,
            blocks: historicalQuery.aggregations.avg_blocks.value || 0,
            fgPercentage: historicalQuery.aggregations.avg_fg_percentage.value || 0,
        };
    } catch (error) {
        console.error('Query error from getHistoricalAveragesAgainstOpponent function:', error);
        return { error: 'Queries failed in getting historical averages against opponent.' };
    }
}
<p>3. Para calcular los promedios de temporada de un jugador frente a un oponente específico, usaremos prácticamente la misma consulta que la histórica. La única diferencia en esta consulta es que el filtro <code>bool</code> tiene una condición adicional para <code>game_date</code>. El <code>game_date</code> del campo tiene que estar dentro del rango de la temporada actual de la NBA. En este caso, el rango está entre <code>2024-10-01</code> y <code>2025-06-30</code>. Esta condición adicional a continuación garantiza que las agregaciones que sigan aislarán solo los partidos de esta temporada.
</p>        {
                            range: {
                    //Range for this season, change to match current season
                                game_date: {
                                    gte: '2024-10-01',
                                    lte: '2025-06-30',
                                },
                            },
<h4><strong>Paso 6: Utilidad de comparación de jugadores</strong></h4><p>Para mantener nuestro código modular y mantenible, crearemos un archivo de utilidad que consolide las funciones auxiliares de metadatos y las agregaciones de Elasticsearch. Esto alimentará la herramienta principal que emplea el agente. Más sobre eso más adelante:</p><p>1. Crear un nuevo <a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/lib/comparePlayers.js">archivo comparePlayers.js</a> en el directorio <code>backend/lib</code> .</p><p>2. Agregar la función siguiente para consolidar los asistentes de metadatos y la lógica de agregación de Elasticsearch en una única función que alimente la herramienta principal empleada por el agente.
</p>import { playersByName } from '../data/playerAndTeamInfo.js';
import { teamsByName } from '../data/playerAndTeamInfo.js';
import { upcomingMatchups } from '../data/playerAndTeamInfo.js';
import { getHistoricalAveragesAgainstOpponent } from './elasticAggs.js';
import { getSeasonAveragesAgainstOpponent } from './elasticAggs.js';

//Simple helper functions to simulate API calls for player and team metadata. These reference the hardcoded values from playerAndTeamInfo.js in the data directory
export function getPlayerInfo(playerFullName) {
    return playersByName[playerFullName];
}

export function getTeamID(teamFullName) {
    return teamsByName[teamFullName];
}

export function getUpcomingMatchups(teamId) {
    return upcomingMatchups[teamId];
}

//Main function used by the 'playerComparisonTool' agent tool
export async function comparePlayersForNextMatchup(player1Name, player2Name) {
    //Get Player Info
    const player1Info = getPlayerInfo(player1Name);
    const player2Info = getPlayerInfo(player2Name);

    //Get upcoming matchups
    const player1NextGame = getUpcomingMatchups(player1Info.team_id)[0];
    const player2NextGame = getUpcomingMatchups(player2Info.team_id)[0];

    //Get season and historical averages against next opponent for player 1
    const player1SeasonAverages = await getSeasonAveragesAgainstOpponent(
        player1Info.player_id,
        player1NextGame.opponent_team_id
    );
    const player1HistoricalAverages = await getHistoricalAveragesAgainstOpponent(
        player1Info.player_id,
        player1NextGame.opponent_team_id
    );

    //Get season and historical averages against next opponent for player 2
    const player2SeasonAverages = await getSeasonAveragesAgainstOpponent(
        player2Info.player_id,
        player2NextGame.opponent_team_id
    );
    const player2HistoricalAverages = await getHistoricalAveragesAgainstOpponent(
        player2Info.player_id,
        player2NextGame.opponent_team_id
    );

    const player1 = {
        name: player1Name,
        playerId: player1Info.player_id,
        teamId: player1Info.team_id,
        nextOpponent: {
            teamId: player1NextGame.opponent_team_id,
            teamName: player1NextGame.opponent_team_name,
            home: player1NextGame.home,
        },
        stats: {
            seasonAverages: player1SeasonAverages,
            historicalAverages: player1HistoricalAverages,
        },
    };

    const player2 = {
        name: player2Name,
        playerId: player2Info.player_id,
        teamId: player2Info.team_id,
        nextOpponent: {
            teamId: player2NextGame.opponent_team_id,
            teamName: player2NextGame.opponent_team_name,
            home: player2NextGame.home,
        },
        stats: {
            seasonAverages: player2SeasonAverages,
            historicalAverages: player2HistoricalAverages,
        },
    };

    return [player1, player2];
}
<h4><strong>Paso 7: Construir el agente</strong></h4><p>Ahora que creaste el frontend y el backend de estructura, absorbiste datos de los partidos de la NBA y establecido una conexión con Elasticsearch, podemos empezar a juntar todas las piezas para construir el agente.</p><p><strong>Definición del agente</strong></p><p>1. Navega al archivo <a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/src/mastra/agents/index.ts">index.ts</a> dentro del directorio <code>backend/src/mastra/agents</code> y agrega la definición del agente. Puedes especificar campos como:</p><ul><li><p><strong>Nombre:</strong> Dale a tu agente un nombre que se use como referencia cuando te llamen en el frontend.</p></li><li><p><strong>Instrucciones/prompt del sistema: </strong>Un prompt del sistema proporciona al LLM el contexto inicial y las reglas a seguir durante la interacción. Es similar al prompt que los usuarios envían a través del cuadro de chat, pero este se da antes de cualquier entrada del usuario. De nuevo, esto cambiará según el modelo que elijas.</p></li><li><p><strong>Modelo:</strong> ¿Qué LLM usar (Mastra soporta OpenAI, Anthropic, modelos locales, etc.).</p></li><li><p><strong>Herramientas:</strong> Una lista de funciones de la herramienta que el agente puede llamar.</p></li><li><p><strong>Memoria:</strong> (Opcional) si queremos que el agente recuerde el historial de conversaciones, etc. Para simplificar, podemos empezar sin memoria persistente, aunque Mastra la admite.</p></li></ul><p></p>import { openai } from '@ai-sdk/openai';
import { Agent } from '@mastra/core/agent';
import { playerComparisonTool } from '../tools';

export const basketballAgent = new Agent({
    name: 'Basketball Agent',
    instructions: `
      You are a NBA Basketball expert.
      Your primary function is to compare two NBA players and recommend which one is the better fantasy pickup.

      Only compare players from the following list:
      - LeBron James
      - Stephen Curry
      - Jayson Tatum
      - Jaylen Brown
      - Nikola Jokic
      - Luka Doncic
      - Kyrie Irving
      - Anthony Davis
      - Kawhi Leonard
      - Russell Westbrook

      Input Handling Rules:
      - If the user asks about a player that is not on this list, respond with the list of available players for comparison.
      - If the user only inputs one player, ask the user to add another player from the list provided.
      - If the user inputs a player with the wrong spelling or capitalizations, infer from the list of available players provided.
      - IMPORTANT: If the user asks a question or asks you to generate a response about anything outside of basketball or the scope of this project, DO NOT answer and affirm you can only talk about basketball.

      Tool Usage:
      - Extract and standardize player names to match the list exactly.
      - Use the playerComparisonTool, passing both names as strings.
      - The tool will return an object with game information, stats, and analysis.

      Format your response using Markdown syntax. Use:

        Example output format:

       
        #### Next Game Info
        - ***LeBron James** vs Warriors, May 24 (Home)  
        - ***Stephen Curry** vs Lakers, May 24 (Away)


        #### Stats Comparison  
        \`\`\`  
        Stat                  LeBron James (vs Warriors)    Stephen Curry (vs Lakers)  
        --------------------  -----------------------------  ----------------------------  
        Historical Points     28.3                          30.3  
        Historical Assists    6.7                           8.7  
        Season Points         28.8                          23.3  
        Season Assists        6.2                           4.7  
        \`\`\`

        #### Fantasy Recommendation  
        Explain which player is the better fantasy pickup and why.
      
    `,
    model: openai('gpt-4o'),
    tools: { playerComparisonTool },
});
<p><strong>
Definición de herramientas</strong></p><ol><li><p>Navega al archivo <a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/src/mastra/tools/index.ts">index.ts</a> dentro del directorio <code>backend/src/mastra/tools</code> .</p></li><li><p>Instala Zod usando el comando:</p></li></ol>npm install zod<p>3. Agregar definiciones de herramientas. Ten en cuenta que importamos la función dentro del archivo <code>comparePlayers.js</code> como la función principal que el agente usará al llamar a esta herramienta. Usando la función <code>createTool()</code> de Mastra, registraremos nuestro <code>playerComparisonTool</code>. Los campos incluyen:</p><ul><li><p><code>id</code>: Esta es una descripción en lenguaje natural para ayudar al agente a entender qué hace la herramienta.</p></li><li><p><code>input schema</code>: Para definir la forma de la entrada de la herramienta, Mastra emplea el esquema <a href="https://zod.dev/">Zod</a> , que es una biblioteca de validación de esquema TypeScript. Zod ayuda cerciorar de que el agente reciba entradas estructuradas correctamente y evita que la herramienta se ejecute si la estructura de entrada no coincide.</p></li><li><p><code>description</code>: Esta es una descripción en lenguaje natural para ayudar al agente a entender cuándo llamar y usar la herramienta.</p></li><li><p><code>execute</code>: La lógica que se ejecuta cuando se llama a la herramienta. En nuestro caso, estamos usando una función auxiliar importada para devolver estadísticas de rendimiento.</p></li></ul>import { comparePlayersForNextMatchup } from '../../../lib/comparePlayers.js'
import { createTool } from "@mastra/core/tools";
import { z } from "zod";

export const playerComparisonTool = createTool({
    id: "Compare two NBA players",
    inputSchema: z.object({
        player1:z.string(),
        player2:z.string()
    }),
    description: "Use this tool to compare two players given in the user prompt.",
    execute: async ({ context: { player1, player2 } }) =&gt; {
        return await comparePlayersForNextMatchup(player1, player2);
      },
})<p><strong>Adición de middleware para manejar CORS</strong></p><p>Agregar middleware en el servidor Mastra para manejar <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS">CORS</a>. Dicen que hay tres cosas en la vida que no puedes evitar: la muerte, los impuestos, y para los desarrolladores web es el CORS. En resumen, el Intercambio de Recursos entre Orígenes es una función de seguridad en el navegador que bloquea al frontend para que no haga peticiones a un backend que se ejecuta en otro dominio o puerto. Aunque ejecutamos tanto el backend como el frontend en localhost, usan puertos diferentes, lo que activa la política CORS. Necesitamos agregar el middleware especificado en la <a href="https://mastra.ai/en/docs/server-db/middleware">documentación de Mastra</a> para que nuestro backend permita esas peticiones desde el frontend.</p><p>1. Navega al archivo <a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/backend/src/mastra/index.ts">index.ts</a> dentro del directorio <code>backend/src/mastra</code> y agrega la configuración para CORS:</p><ul><li><p><code>origin: ['http://localhost:5173']</code></p><ul><li><p>Permite solicitudes solo desde esta dirección (dirección predeterminada de Vite)</p></li></ul></li><li><p><code>allowMethods: ["GET", "POST"]</code></p><ul><li><p>Métodos HTTP que están permitidos. La mayoría de las veces, será usando POST.</p></li></ul></li><li><p><code>allowHeaders: ["Content-Type", "Authorization", "x-mastra-client-type, "x-highlight-request", "traceparent"],</code></p><ul><li><p>Estos deciden qué encabezados personalizados pueden usar en las solicitudes</p></li></ul></li></ul><p></p>import { Mastra } from '@mastra/core/mastra';
import { basketballAgent } from './agents';

console.log('Starting Mastra server...');

export const mastra = new Mastra({
  agents: { basketballAgent },
  server:{
    timeout: 10 * 60 * 1000, // 10 minutes
    cors: {
      origin: ['http://localhost:5173'],
      allowMethods: ["GET", "POST"],
      allowHeaders: [
        "Content-Type",
        "Authorization",
        "x-mastra-client-type",
        "x-highlight-request",
        "traceparent",
      ],
      exposeHeaders: ["Content-Length", "X-Requested-With"],
      credentials: false,
    },
  },

});

console.log('Mastra server configured.'); // Log after server configuration
<h4><strong>Paso 8: Integración del frontend</strong></h4><p>Este componente React proporciona una interfaz de chat sencilla que se conecta al agente Mastra AI usando el gancho <a href="https://mastra.ai/en/docs/frameworks/agentic-uis/ai-sdk#using-the-usechat-hook">useChat()</a> de <code>@ai-sdk/react</code>. También vamos a usar este gancho para mostrar el uso de tokens, llamadas a herramientas y para renderizar la conversación. En el prompt del sistema anterior, también pedimos al agente que genere la respuesta en markdown, así que usaremos <code>react-markdown</code> para formatear correctamente la respuesta.</p><p></p><p>1. Mientras estés en el directorio frontend, instala el paquete @ai-sdk/react para usar el hook useChat().</p>npm install @ai-sdk/react<p>2. Mientras estés en el mismo directorio, instala React Markdown para que podamos formatear correctamente la respuesta que genera el agente.</p>npm install react-markdown<p>3. Implementar <code>useChat()</code>. Este gancho gestionará la interacción entre tu frontend y el backend de tu agente de IA. Gestiona el estado del mensaje, la entrada del usuario, el estado y te proporciona ganchos de ciclo de vida para fines de observabilidad. Las opciones que descartamos incluyen:</p><ul><li><p><code>api:</code> Esto define el punto final de tu Mastra AI Agent. Por defecto se pone en el puerto 4111 y también queremos agregar la ruta que soporta respuestas en streaming.</p></li><li><p><code>onToolCall</code>: Esto se ejecuta cada vez que el agente llama a una herramienta; Lo estamos usando para rastrear qué herramientas está llamando nuestro agente.</p></li><li><p><code>onFinish</code>: Esto se ejecuta después de que el agente complete una respuesta completa. Aunque activamos el streaming, <code>onFinish</code> seguirá ejecutar luego de recibir el mensaje completo y no luego de cada chunk. Aquí, lo estamos usando para rastrear nuestro uso de tokens. Esto puede ser útil para monitorizar los costos de los LLM y optimizarlos.</p></li></ul><p>4. Por último, ve al componente <a href="https://github.com/jdarmada/nba-ai-assistant-js/blob/main/frontend/components/ChatUI.jsx">ChatUI.jsx</a> en el directorio <code>frontend/components</code> para crear la interfaz que mantenga nuestra conversación. A continuación, envuelve la respuesta en un componente <code>ReactMarkdown</code> para formatear correctamente la respuesta del agente.</p>import React, { useState } from 'react';
import { useChat } from '@ai-sdk/react';
import ReactMarkdown from 'react-markdown';

export default function ChatUI() {
    const [totalTokenUsage, setTotalTokenUsage] = useState(0);
    const [promptTokenUsage, setPromptTokenUsage] = useState(0);
    const [completionTokenUsage, setCompletionTokenUsage] = useState(0);
    const [toolsCalled, setToolsCalled] = useState([]);

    const { messages, input, handleInputChange, handleSubmit, status } = useChat({
        api: 'http://localhost:4111/api/agents/basketballAgent/stream', //Replace with your own endpoint for your agent
        id: 'my-chat-session',

        //Optional parameter to check agent tool calls
        onToolCall: ({ toolCall }) =&gt; {
            setToolsCalled((prev) =&gt; [...prev, toolCall.toolName]);
        },

        //Optional parameter to check token usages
        onFinish: (message, { usage }) =&gt; {
            setTotalTokenUsage((prev) =&gt; prev + usage.totalTokens);
            setPromptTokenUsage((prev) =&gt; prev + usage.promptTokens);
            setCompletionTokenUsage((prev) =&gt; prev + usage.completionTokens);
        },

        //Optional parameter for error handling
        onError: (error) =&gt; {
            console.error('Agent error:', error);
        },
    });

    return (
        &lt;div&gt;
            &lt;div className="agent-info"&gt;
                &lt;h4 className="stats-title"&gt;What's My Agent Doing?&lt;/h4&gt;

                &lt;div className="stats-box"&gt;
                    &lt;strong className="stats-sub-title"&gt;Tools Called:&lt;/strong&gt;
                    &lt;ul className="tool-list"&gt;
                        {toolsCalled.map((tool, idx) =&gt; (
                            &lt;li key={idx}&gt;{tool}&lt;/li&gt;
                        ))}
                        {toolsCalled.length === 0 &amp;&amp; &lt;li&gt;No tools called yet.&lt;/li&gt;}
                    &lt;/ul&gt;

                    &lt;div className="usage-stats"&gt;
                        &lt;p&gt;Prompt Token Usage: {promptTokenUsage}&lt;/p&gt;
                        &lt;p&gt;Completion Token Usage: {completionTokenUsage}&lt;/p&gt;
                        &lt;p&gt;Total Token Usage: {totalTokenUsage}&lt;/p&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
            &lt;/div&gt;

            &lt;strong&gt;Conversation:&lt;/strong&gt;
            &lt;div className="convo-box"&gt;
                {messages.map((msg) =&gt; (
                    &lt;div key={msg.id} className="message-item"&gt;
                        &lt;strong className="message-role"&gt;{msg.role === 'assistant' ? 'Basketbot' : 'You'}:&lt;/strong&gt;
                        &lt;ReactMarkdown&gt;{msg.content}&lt;/ReactMarkdown&gt;
                    &lt;/div&gt;
                ))}
            &lt;/div&gt;

            &lt;form onSubmit={handleSubmit}&gt;
                &lt;input
                    type="text"
                    value={input}
                    onChange={handleInputChange}
                    placeholder="Input two players you want to compare."
                    className="input-box"
                /&gt;
                &lt;button type="submit" disabled={status === 'streaming'}&gt;
                    {status === 'streaming' ? 'Thinking...' : 'Send'}
                &lt;/button&gt;
            &lt;/form&gt;
        &lt;/div&gt;
    );
}<h4><strong>Paso 9: Ejecutar la aplicación</strong></h4><p>¡Felicidades! Ahora estás listo para ejecutar la aplicación. Sigue estos pasos para empezar tanto el backend como el frontend.</p><ol><li><p>En una ventana de terminal, empezando desde el directorio raíz, navega hasta el directorio backend e inicia el servidor Mestra:</p></li></ol>cd backend

npm run dev<p>2. En otra ventana de terminal, empezando desde el directorio raíz, navega hasta el directorio frontend e inicia la aplicación React:</p><p></p>cd frontend

npm run dev<p></p><p>3. Ve a tu navegador y navega a:</p><p></p><p><a href="http://localhost:5173/">http://localhost:5173</a></p><p></p><p>Deberías poder ver la interfaz del chat. Prueba estos ejemplos de prompts:</p><ul><li><p>"Compara a LeBron James y Stephen Curry"</p></li><li><p>"¿A quién debería elegir entre Jayson Tatum y Luka Doncic?"</p></li></ul><p></p><h3><strong>Qué sigue: Hacer que el agente sea más inteligente</strong></h3><p>Para que el asistente sea más agenciado y las recomendaciones más perspicaces, agregaré algunas mejoras clave en la próxima iteración.</p><p></p><p><strong>Búsqueda semántica de noticias de la NBA</strong></p><p>Hay un montón de factores que pueden afectar al rendimiento de los jugadores, muchos de los cuales no aparecen en las estadísticas brutas. Cosas como reportes de lesiones, cambios en la alineación o incluso análisis postpartido, solo se pueden encontrar en artículos de prensa. Para captar este contexto adicional, agregaré capacidades de búsqueda semántica para que el agente pueda recuperar artículos relevantes de la NBA e incluir esa narrativa en sus recomendaciones.</p><p></p><p><strong>Búsqueda dinámica con el servidor MCP de Elasticsearch</strong></p><p>MCP (Model Context Protocol) se está convirtiendo rápidamente en el estándar para cómo los agentes se conectan a fuentes de datos. Migraré la lógica de búsqueda al servidor Elasticsearch MCP, que permite al agente construir consultas dinámicamente en lugar de depender de funciones de búsqueda predefinidas que proporcionamos. Esto nos permite emplear más flujos de trabajo en lenguaje natural y reduce la necesidad de escribir manualmente cada consulta de búsqueda. Descubre más sobre el servidor MCP de Elasticsearch y el estado actual del <a href="https://www.elastic.co/search-labs/blog/mcp-current-state">ecosistema aquí</a>.</p><p></p><p>Estos cambios ya están en marcha, ¡estad atentos!</p><h3><strong>Conclusión</strong></h3><p></p><p>En este blog, creamos un asistente RAG agente que ofrece recomendaciones personalizadas para tu equipo de baloncesto de fantasía usando JavaScript, Mastra y Elasticsearch. Cubrimos lo siguiente:</p><ul><li><p><strong>Fundamentos del RAG agente</strong> y cómo combinar la autonomía de un agente de IA con las herramientas para usar RAG de forma eficaz puede conducir a agentes más matizados y dinámicos.</p></li><li><p><strong>Elasticsearch </strong>y cómo sus capacidades de almacenamiento de datos y poderosas agregaciones nativas lo convierten en un gran socio como base de conocimiento para un LLM.</p></li><li><p><strong>El framework Mastra </strong>y cómo simplifica la construcción de estos agentes para desarrolladores en el ecosistema javaScript.</p></li></ul><p>Tanto si eres un fanático del baloncesto, explorando cómo crear agentes de IA, o ambos como yo, espero que este blog te dio algunos cimientos para empezar. El repositorio completo está disponible en <a href="https://github.com/jdarmada/nba-ai-assistant-js">GitHub</a>, siéntete libre de clonar y trastear. ¡Ahora, gana esa liga de fantasía!</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/agentic-rag</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/agentic-rag</guid>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[AI agéntica]]></category>
    <category><![CDATA[Javascript]]></category>
    <dc:creator><![CDATA[JD Armada]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8ffd561836a4cb20/6a17f1e47b54f978588b39e4/8132ed781c1ea5d46ca244182f421ed5c721f23b-1200x628.png" length="0" type="image/png"/>
    <pubDate>Tue, 01 Jul 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Conecta agentes a Elasticsearch con Model Context Protocol]]></title>
    <description><![CDATA[Usemos el servidor Model Context Protocol para chatear con tus datos en Elasticsearch.]]></description>
    <content:encoded><![CDATA[<p>¿Qué pasaría si interactuar con sus datos fuera tan fácil como chatear con un colega? Imaginar simplemente preguntar: "Muéstreme todos los pedidos superiores a $ 500 del mes pasado" o "¿Qué productos recibieron la mayor cantidad de reseñas de 5 estrellas?" y obtener respuestas instantáneas y precisas, sin necesidad de consultar.</p><p>Model Context Protocol (MCP) lo hace posible. Conecta a la perfección la IA conversacional con sus bases de datos y API externas, transformando solicitudes complejas en conversaciones naturales. Si bien los LLM modernos son excelentes para comprender el lenguaje, su verdadero potencial se desbloquea cuando se integran con sistemas del mundo real. MCP cierra la brecha entre ellos, haciendo que la interacción de datos sea más intuitiva y eficiente.</p><p>En esta publicación, exploraremos:</p><ul><li><p>Arquitectura MCP: cómo funciona bajo el capó</p></li><li><p>Beneficios de un servidor MCP conectado a Elasticsearch</p></li><li><p>Creación <a href="https://github.com/elastic/mcp-server-elasticsearch">de un servidor MCP con tecnología de Elasticsearch</a></p></li></ul><p>¡Se avecinan tiempos emocionantes! La integración de MCP con tu Elastic stack transforma la forma en que interactúas con la información, haciendo que las consultas complejas sean tan intuitivas como la conversación diaria.</p><h2>Protocolo de contexto del modelo</h2><p><a href="https://modelcontextprotocol.io/introduction">Model Context Protocol</a> (MCP), desarrollado por Anthropic, es un estándar abierto que conecta modelos de IA con fuentes de datos externas a través de canales seguros y bidireccionales. Resuelve una limitación importante de la IA: el acceso en tiempo real a sistemas externos mientras se preserva el contexto de la conversación.</p><h3>Arquitectura del MCP</h3><p>La arquitectura del protocolo de contexto del modelo consta de dos componentes clave:</p><ul><li><p><strong>Clientes MCP</strong> : asistentes de IA y chatbots que aplicar información o ejecutan tareas en nombre de los usuarios.</p></li><li><p><strong>Servidores MCP</strong> : repositorios de datos, motores de búsqueda y API que recuperan información relevante o realizan acciones aplicar (por ejemplo, llamar a API externas).</p></li></ul><p>Los servidores MCP exponen cuatro capacidades principales a los clientes:</p><ul><li><p><strong>Recursos</strong> : datos estructurados, documentos y contenido que se pueden recuperar y emplear como contexto para las interacciones de LLM. Esto permite a los asistentes de IA acceder a información relevante de bases de datos, índices de búsqueda u otras fuentes.</p></li><li><p><strong>Herramientas</strong> : funciones ejecutables que permiten a los LLM interactuar con sistemas externos, realizar cálculos o realizar acciones en el mundo real. Estas herramientas amplían las capacidades de IA más allá de la generación de texto, lo que permite a los asistentes activar flujos de trabajo, llamar a API o manipular datos dinámicamente.</p></li><li><p><strong>Solicitudes</strong> : plantillas de solicitudes y flujos de trabajo reutilizables para estandarizar y compartir interacciones comunes de LLM.</p></li><li><p><strong>Ejemplificación</strong> : aplicar finalizaciones de LLM a través del cliente para habilitar comportamientos sofisticados de agentes mientras mantiene la seguridad y la privacidad.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfe82754551bb187a/6a17f7ec6864a43e71b6895d/bef5178133391e96e3d66ae634e41a85712a33a9-2345x1620.png" alt="Arquitectura del protocolo de contexto del modelo (MCP)" /><h2>Servidor MCP + Elasticsearch</h2><p></p><p>Los sistemas tradicionales de generación aumentada de recuperación (RAG) recuperan documentos en función de las consultas de los usuarios, pero MCP va un paso más allá: permite a los agentes de IA construir y ejecutar tareas dinámicamente en tiempo real. Esto permite a los usuarios hacer preguntas en lenguaje natural como:</p><p></p><ul><li><p>"Muéstrame todos los pedidos superiores a $ 500 del mes pasado".</p></li><li><p>"¿Qué productos recibieron la mayor cantidad de reseñas de 5 estrellas?"</p></li></ul><p></p><p>Y obtén respuestas instantáneas y precisas, sin escribir una sola consulta.</p><p></p><p>MCP logra esto a través de:</p><ul><li><p>Selección dinámica de herramientas: los agentes eligen de manera inteligente las herramientas correctas expuestas a través de los servidores MCP en función de la intención del usuario. Los LLM "más inteligentes" son generalmente mejores para seleccionar las herramientas adecuadas con los argumentos apropiados basados en el contexto.</p></li><li><p>Comunicación bidireccional: los agentes y las fuentes de datos intercambian información de manera fluida, refinando las consultas según sea necesario (p. ej. mapeo de índice de búsqueda primero, solo luego construya la consulta ES).</p></li><li><p>Orquestación de múltiples herramientas: los flujos de trabajo pueden aprovechar las herramientas de varios servidores MCP simultáneamente.</p></li><li><p>Contexto persistente: los agentes recuerdan interacciones anteriores, manteniendo la continuidad en todas las conversaciones.</p></li></ul><p>Un servidor MCP conectado a Elasticsearch desbloquea una poderosa arquitectura de recuperación en tiempo real. Los agentes de IA pueden explorar, consultar y analizar datos de Elasticsearch bajo demanda. Sus datos se pueden buscar a través de una interfaz de chat simple.</p><p>Más allá de la simple recuperación de datos, MCP permite la acción. Se integra con otras herramientas para activar flujos de trabajo, automatizar procesos y aportar información a los sistemas de análisis. Al separar la búsqueda de la ejecución, MCP mantiene las aplicaciones impulsadas por IA flexibles, actualizadas y perfectamente integradas en los flujos de trabajo de los agentes.</p><h2>Manos a la obra: servidor MCP para chatear con tus datos de Elasticsearch</h2><p>Para interactuar con Elasticsearch a través de un servidor MCP, necesitamos al menos funciones para:</p><ul><li><p>Recuperar índices</p></li><li><p>Obtener asignaciones</p></li><li><p>Realiza búsquedas con Query DSL de Elasticsearch</p></li></ul><p>Nuestro servidor está escrito en TypeScript y usaremos el <a href="https://github.com/modelcontextprotocol/typescript-sdk">SDK oficial de MCP TypeScript</a>. Para la configuración, recomendamos instalar la aplicación de escritorio Claude (la versión gratis es suficiente) ya que incluye un cliente MCP incorporado. Nuestro servidor MCP esencialmente expone el cliente oficial de <a href="https://www.elastic.co/es/guide/en/elasticsearch/client/javascript-api/current/index.html">JavaScript Elasticsearch</a> a través de herramientas MCP.</p><p>Comencemos definiendo el cliente de Elasticsearch y el servidor MCP:</p> const esClient = new Client({
    node: url,
    auth: {
      apiKey: apiKey,
    },
  });

  const server = new McpServer({
    name: "elasticsearch-mcp-server",
    version: "0.1.0",
  });<p>Usaremos las siguientes herramientas de servidor MCP que pueden interactuar con Elasticsearch:</p><ul><li><p><strong>List Indices</strong> (<a href="https://github.com/elastic/mcp-server-elasticsearch/blob/main/index.ts#L46">list_indices</a>): Esta herramienta recupera todos los índices de Elasticsearch disponibles, proporcionando detalles como el nombre del índice, el estado de salud y el recuento de documentos.</p></li><li><p><strong>Obtener asignaciones</strong> (<a href="https://github.com/elastic/mcp-server-elasticsearch/blob/main/index.ts#L94">get_mappings</a>): esta herramienta obtiene las asignaciones de campos para un índice de Elasticsearch específico, lo que ayuda a los usuarios a comprender la estructura y los tipos de datos de los documentos almacenados.</p></li><li><p><strong>Búsqueda</strong> (<a href="https://github.com/elastic/mcp-server-elasticsearch/blob/main/index.ts#L147">search</a>): esta herramienta ejecuta una búsqueda de Elasticsearch mediante un DSL de consulta proporcionado. Habilita automáticamente los resaltados para los campos de texto, lo que facilita la identificación de resultados de búsqueda relevantes.</p></li></ul><p>La implementación completa del servidor MCP de Elasticsearch está disponible en el repositorio <a href="https://github.com/elastic/mcp-server-elasticsearch">elastic/mcp-server-elasticsearch</a> .</p><h4>Chatea con tu índice</h4><p>Exploremos cómo configurar el servidor MCP de Elasticsearch para que puedas hacer preguntas en lenguaje natural sobre tus datos, como "Encuentra todos los pedidos superiores a $500 del mes pasado".</p><p><strong>Configure su aplicación de escritorio Claude</strong></p><ul><li><p>Abra la aplicación de escritorio de Claude</p></li><li><p>Vaya a Configuración &gt; Servidores MCP para desarrolladores &gt;</p></li><li><p>Haga clic en "Editar configuración" y agregue esta configuración a su <code>claude_desktop_config.json</code>:</p></li></ul>{
  "mcpServers": {
    "Elasticsearch MCP Server": {
      "command": "npx",
      "args": [
        "-y",
        "@elastic/mcp-server-elasticsearch"
      ],
      "env": {
        "ES_URL": "",
        "ES_API_KEY": ""
      }
    }
  }
}<p>Nota: Esta configuración emplea el paquete npm <a href="https://www.npmjs.com/package/@elastic/mcp-server-elasticsearch">@elastic/mcp-server-elasticsearch</a> publicado por Elastic. Si deseas desarrollar localmente, puedes encontrar más detalles sobre cómo activar el servidor MCP de Elasticsearch <a href="https://github.com/elastic/mcp-server-elasticsearch/blob/main/README.md">aquí</a>.</p><p><strong>Rellenar el índice de Elasticseach</strong></p><ul><li><p>Puede usar nuestros <a href="https://gist.github.com/jedrazb/60e9400cbe40addfd9e4337749c28431">datos de ejemplo</a> para completar el índice de "pedidos" para esta demostración</p></li><li><p>Esto le permitirá probar consultas como "Buscar todos los pedidos superiores a $ 500 del mes pasado"</p></li></ul><p><strong>Empieza a usarlo</strong></p><ul><li><p>Abrir una nueva conversación en la aplicación de escritorio de Claude</p></li><li><p>El servidor MCP se conectará automáticamente</p></li><li><p>¡Comienza a hacer preguntas sobre tus datos de Elasticsearch!</p></li></ul><p>Mira esta demostración para ver lo fácil que es consultar tus datos de Elasticsearch usando lenguaje natural:</p><h4>¿Cómo funciona?</h4><p>Cuando se le pregunta "Buscar todos los pedidos superiores a $ 500 del mes pasado", el LLM reconoce la intención de buscar en el índice de Elasticsearch con restricciones específicas. Para realizar una búsqueda efectiva, el agente calcula:</p><ul><li><p>Averigüe el nombre del índice: <code>orders</code></p></li><li><p>Comprender las asignaciones de <code>orders</code> índice</p></li><li><p>Construir el DSL de consulta compatible con asignaciones de índice y finalmente ejecutar la solicitud de búsqueda</p></li></ul><p>Esta interacción se puede representar como:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt152f41bc8c3e9752/6a17f7ee6df73152df0a10cc/8875bc75745124be87deac0be666509446887de2-2345x1620.png" alt="¿Cómo funciona el servidor MCP + Elasticsearch?" /><h2>Conclusión</h2><p>Model Context Protocol mejora la forma en que interactúas con los datos de Elasticsearch, lo que permite conversaciones en lenguaje natural en lugar de consultas complejas. Al unir las capacidades de IA con sus datos, MCP crea un flujo de trabajo más intuitivo y eficiente que mantiene el contexto a lo largo de sus interacciones.</p><p>El servidor MCP de Elasticsearch está disponible como un paquete npm público (<a href="https://www.npmjs.com/package/@elastic/mcp-server-elasticsearch">@elastic/mcp-server-elasticsearch</a>), lo que facilita la integración para los desarrolladores. Con una configuración mínima, su equipo puede comenzar a explorar datos, activar flujos de trabajo y obtener información a través de conversaciones simples.</p><p>¿Listo para experimentarlo por ti mismo? Prueba el <a href="https://github.com/elastic/mcp-server-elasticsearch">servidor MCP de Elasticsearch</a> hoy mismo y comienza a chatear con tus datos.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/model-context-protocol-elasticsearch</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/model-context-protocol-elasticsearch</guid>
    <category><![CDATA[AI agéntica]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Jedr Blaszyk,Joe McElroy]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltce68a95c633809ae/6a17f7f0148009fa28b48915/65b378f644bd13e3edf2f108d48186f1889f546c-1200x628.png" length="0" type="image/png"/>
    <pubDate>Fri, 28 Mar 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[El agente de IA para gestionar proyectos serverless de Elasticsearch]]></title>
    <description><![CDATA[Un agente de IA impulsado por lenguaje natural que gestiona sin esfuerzo proyectos Serverless de Elasticsearch, permitiendo la creación, eliminación y comprobación de estado de proyectos.]]></description>
    <content:encoded><![CDATA[<h2>Cómo usar un agente de IA para gestionar proyectos de Serverless Elasticsearch</h2><ol><li><p><strong>Clona el repositorio:</strong> Descarga el código de la herramienta desde GitHub usando <code>git clone https://github.com/elastic/elasticsearch-labs/supporting-blog-content/serverless-ai-agent</code> <code>a</code>y navega por el directorio con <code>cd serverless-ai-agent</code>.</p></li><li><p><strong>Configurar el entorno: </strong>Crea un entorno virtual (opcional) con <code>python -m venv venv</code> y actívalo (<code>source venv/bin/activate</code> o <code>venv\Scripts\activate</code> en Windows). Luego, instala los paquetes Python necesarios usando <code>pip install -r requirements.txt</code>.</p></li><li><p><strong>Configurar credenciales: </strong>Crea un archivo <code>.env</code> en la raíz del proyecto y replétalo con la URL de la API de Elasticsearch (<code>ES_URL</code>), clave API (<code>API_KEY</code>), región (<code>REGION</code>) y clave API de OpenAI (<code>OPENAI_API_KEY</code>).</p></li><li><p><strong>Ejecuta la herramienta: </strong>Ejecuta la herramienta ejecutando <code>python main.py</code> en tu terminal. Esto iniciará al agente de IA y mostrará un prompt para tus comandos.</p></li><li><p><strong>Gestionar proyectos con lenguaje natural:</strong> Interactúa con la herramienta usando comandos en inglés sencillo como "Crea un proyecto serverless llamado my\_project", "Get the status of the serverless project named my\_project" o "Delete the serverless project named my\_project". La IA interpretará tus comandos y ejecutará las funciones correspondientes.</p></li></ol><h2>Fondo</h2><p>Esta pequeña herramienta de línea de comandos te permite gestionar tus <a href="https://www.elastic.co/guide/en/serverless/current/intro.html">proyectos de Serverless Elasticsearch</a> en un inglés sencillo. Habla con una IA (en este caso, OpenAI) para averiguar a qué te refieres y llamar a las funciones correctas usando LlamaIndex.</p><h3>¿Qué puede hacer el agente de IA serverless de Elasticsearch?</h3><ul><li><p><strong>Crea un proyecto</strong>: Pone en marcha un nuevo proyecto Serverless Elasticsearch.</p></li><li><p><strong>Eliminar un proyecto</strong>: Elimina un proyecto existente (sí, limpia luego de ti).</p></li><li><p><strong>Consulta el estado del proyecto</strong>: Comprueba cómo va tu proyecto.</p></li><li><p><strong>Obtén detalles del proyecto</strong>: Recoge todos los detalles jugosos sobre tu proyecto.</p></li></ul><p>Consulta el código en <a href="https://github.com/elastic/elasticsearch-labs/tree/a65f7bc1e4a041765d1c0a45ac44b9cd9fc1589f/supporting-blog-content/serverless-ai-agent">GitHub.</a></p><h3>Cómo funciona el agente de IA Serverless de Elasticsearch</h3><p>Cuando escribes algo como:</p><p><em>"Crea un proyecto serverless llamado my_project"</em></p><p>… Esto es lo que ocurre entre bastidores:</p><ul><li><p><strong>Entrada y contexto del usuario:</strong> Tu comando en lenguaje natural se envía al agente de IA.</p></li><li><p><strong>Descripciones de funciones:</strong> El agente de IA ya conoce algunas funciones —como create_ess_project, delete_ess_project, get_ess_project_status y get_ess_project_details— porque le dimos descripciones detalladas. Estas descripciones indican a la IA qué hace cada función y qué parámetros necesitan.</p></li><li><p><strong>Procesamiento de LLM:</strong> Tu consulta junto con la información de la función se envía al LLM. Esto significa que la IA ve:</p><ul><li><p><strong>La consulta del usuario</strong>: Tu instrucción en inglés sencillo.</p></li><li><p><strong>Funciones y descripciones disponibles</strong>: Detalles sobre lo que hace cada herramienta para poder elegir la adecuada.</p></li><li><p><strong>Contexto/información histórica del chat</strong>: Como es una conversación, recuerda lo que se dijo antes.</p></li></ul></li><li><p><strong>Llamada y respuesta de función:</strong> La IA decide qué función llamar, pasa los parámetros correctos (como el nombre de tu proyecto) y luego la función se ejecuta. La respuesta se te envía de vuelta en un formato amigable.</p></li></ul><p>En resumen, enviamos tanto tu consulta en lenguaje natural como una lista de descripciones detalladas de herramientas al LLM para que pueda "entender" y elegir la acción adecuada para tu solicitud.</p><h3>Configurar el agente de IA</h3><h4>Prerrequisitos:</h4><p>Antes de ejecutar el agente de IA, cerciórate de tener configurado lo siguiente:</p><ol><li><p><strong>Python (v3.7 o posterior)</strong> instalado.</p></li><li><p><strong>Cuenta serverless de Elasticsearch</strong> configurada en Elastic Cloud.</p></li><li><p><strong>Cuenta OpenAI</strong> para interactuar con el modelo de lenguaje.</p></li></ol><h4>Pasos:</h4><p><strong>1. Clonar el repositorio:</strong></p>git clone https://github.com/elastic/elasticsearch-labs/supporting-blog-content/serverless-ai-agent
cd serverless-ai-agent<p><strong>2. Crear un entorno virtual (opcional pero recomendado):</strong> Si tienes problemas relacionados con el entorno, puedes configurar un entorno virtual para aislamiento:</p>python -m venv venv
source venv/bin/activate  # On Windows, use venv\Scripts\activate<p><strong>3. Instalar las dependencias:</strong> Cerciorar de que todas las dependencias necesarias se instalen ejecutando:</p>pip install -r requirements.txt<p><strong>4. Configura tu entorno:</strong> Crea un .env archiva en la raíz del proyecto con las siguientes variables. Aquí tienes un ejemplo <code>.env.example</code> archivo para ayudarte:</p>ES_URL=your_elasticsearch_api_url  # The base URL for your Elasticsearch service (e.g., https://your-cluster-id.es.region.aws.elastic-cloud.com)
API_KEY=your_elasticsearch_api_key  # Your API key for Elasticsearch
REGION=your_region  # Example: aws-eu-west-1
OPENAI_API_KEY=your_openai_api_key  # Your OpenAI API key<p>Cerciórate de tener los valores correctos para <code>ES_URL</code>, <code>API_KEY</code>y <code>OPENAI_API_KEY</code>. Puedes encontrar tus claves API en los paneles de servicio correspondientes.</p><p><strong>5. Archivo de proyectos:</strong> La herramienta emplea un archivo <code>projects.json</code> para almacenar los mapeos de tus proyectos (nombres de proyectos con sus detalles). Este archivo se creará automáticamente si no existe ya.</p><h3>Ejecutando el agente de IA</h3>python main.py<p>Verás un prompt como este:</p>Welcome to the Serverless Project AI Agent Tool!
You can ask things like:
 - 'Create a serverless project named my_project'
 - 'Delete the serverless project named my_project'
 - 'Get the status of the serverless project named my_project'
 - 'Get the details of the serverless project named my_project'<p>Escribe tu comando y el agente de IA hará su magia. Cuando termines, escribe <code>exit</code> o <code>quit</code> para irte.</p><h3>Unos pocos detalles más</h3><ul><li><p><strong>Integración con LLM</strong>: El LLM recibe tanto tu consulta como descripciones detalladas de cada función disponible. Esto lo ayuda a entender el contexto y decidir, por ejemplo, si llamar a <code>create_ess_project</code> o <code>delete_ess_project</code>.</p></li><li><p><strong>Descripciones de herramientas</strong>: Cada herramienta funcional (creada usando FunctionTool.from_defaults) tiene una descripción amistosa. Esta descripción se incluye en el prompt enviado al LLM para que "sepa" qué acciones están disponibles y qué espera cada acción.</p></li><li><p><strong>Persistencia</strong>: Tus proyectos y sus detalles se almacenan en projects.json, Así que no tienes que volver a introducir información cada vez.</p></li><li><p><strong>Registro verboso</strong>: El agente está configurado en modo verboso, lo cual es genial para depurar y ver cómo se traducen tus instrucciones en llamadas a funciones.</p></li></ul><h3>Ejemplo de utilización de agentes de Elasticsearch AI</h3>python main.py 

Welcome to the Serverless Project AI Agent Tool!

You can ask things like:
 - 'Create a serverless project named my_project'
 - 'Delete the serverless project named my_project'
 - 'Get the status of the serverless project named my_project'
 - 'Get the details of the serverless project named my_project'

User: Create a serverless project named my-february-test
Added user message to memory: Create a serverless project named my-february-test

=== Calling Function ===
Calling function: create_ess_project with args: {"project_name":"my-february-test"}
========================


Agent: The serverless Elasticsearch project named "my-february-test" has been created successfully. Here are the details:

- **Project ID:** &lt;&gt;&gt;
- **Elasticsearch Endpoint:** [https://my-february-test-ccc399.es.eu-west-1.aws.qa.elastic.cloud](https://my-february-test-ccc399.es.eu-west-1.aws.qa.elastic.cloud)
- **Kibana Endpoint:** [https://my-february-test-ccc399.kb.eu-west-1.aws.qa.elastic.cloud](https://my-february-test-ccc399.kb.eu-west-1.aws.qa.elastic.cloud)

User: get my-february-test status
Added user message to memory: get my-february-test status

=== Calling Function ===
Calling function: get_ess_project_status with args: {"project_name":"my-february-test"}
========================

Agent: The status of the "my-february-test" project is currently **initializing**.

User: get my-february-test info
Added user message to memory: get my-february-test info

=== Calling Function ===
Calling function: get_ess_project_details with args: {"project_name":"my-february-test"}
========================

Agent: Here are the details for the "my-february-test" project:

- **Project Name:** my-february-test
- **Alias:** my-february-test-ccc399
- **Cloud ID:** ""
- **Project ID:** ""
- **Region ID:** aws-eu-west-1
- **Created At:** 2025-02-12T15:42:00.530177619Z
- **Created By:** 571213384
- **Organization ID:** ""
- **Type:** Elasticsearch
- **Optimized For:** General Purpose
- **Search Lake:**
  - **Boost Window:** 7
  - **Search Power:** 100
- **Endpoints:**
  - **Elasticsearch:** https://my-february-test-ccc399.es.eu-west-1.aws.qa.elastic.cloud
  - **Kibana:** https://my-february-test-ccc399.kb.eu-west-1.aws.qa.elastic.cloud
- **Credentials:**
  - **Username:** ""
  - **Password:** ""

Please ensure to keep the credentials secure.

User: please delete the my-february-test project
Added user message to memory: please delete the my-february-test project

=== Calling Function ===
Calling function: delete_ess_project with args: {"project_name":"my-february-test"}
========================

Agent: The "my-february-test" project has been deleted successfully.<p></p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/serverless-elasticsearch-ai-agent</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/serverless-elasticsearch-ai-agent</guid>
    <category><![CDATA[Elastic Cloud Serverless]]></category>
    <category><![CDATA[AI agéntica]]></category>
    <dc:creator><![CDATA[Fram Souza]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt88526af16bafdb7c/6a17d7807f6f15825dc0998d/d11e1ba058784ec92b8953fb8db62e1bad21c210-720x420.jpg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 04 Mar 2025 00:00:00 GMT</pubDate>
  </item>
  </channel>
</rss>