<?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[Integraciones - 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[Integraciones - 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/integrations</link>
    </image>
    <link>https://www.elastic.co/es/search-labs/blog/category/integrations</link>
    <atom:link href="https://www.elastic.co/es/search-labs/rss/category/integrations.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[es]]></language>
    <lastBuildDate>Tue, 29 Sep 2026 10:27:36 GMT</lastBuildDate>
  <item>
    <title><![CDATA[API de paneles de Kibana: un contrato estable para cada tipo de panel, probado por más de 50 equipos antes de GA]]></title>
    <description><![CDATA[Administra los dashboards de Kibana como código: haz commit con Git, promueve en todos los entornos y automatiza los despliegues con la API de Kibana y Terraform.]]></description>
    <content:encoded><![CDATA[<p>Las<a href="https://dashboardsapispec.kibana.dev/dashboards#tag/Dashboards"> API de dashboards y visualizaciones de Kibana</a> están listas para producción en Elastic 9.5, disponibles en todos los niveles de suscripción, con total compatibilidad con versiones anteriores. Define tus paneles como JSON, haz commit en Git y luego despliega en diferentes entornos usando pipelines de integración continua y despliegue continuo (CI/CD),<a href="https://registry.terraform.io/providers/elastic/elasticstack/latest/docs/resources/kibana_dashboard"> Terraform</a> o cualquier herramienta que ya tengas. Más de 50 equipos probaron la API durante<a href="https://www.elastic.co/search-labs/blog/kibana-dashboards-as-code-terraform-api"> la vista previa técnica en la versión 9.4</a>, algunos ya ejecutándola en producción. La versión 9.5 también agrega nuevos endpoints (en vista previa técnica) para<a href="https://dashboardsapispec.kibana.dev/tags.html"> las etiquetas</a>, con endpoints <a href="https://dashboardsapispec.kibana.dev/markdowns.html"> de paneles Markdown</a> y<a href="https://dashboardsapispec.kibana.dev/links.html#tag/Links"> Enlaces</a> disponibles ahora en Elastic Cloud Serverless y que aterrizan en la 9.6.</p><h2>Qué significa la compatibilidad con versiones anteriores para la API de Dashboards de Kibana</h2><p>Durante la vista previa técnica, la forma de la API podría cambiar entre versiones.[1] Eso ya no es así. Disponibilidad general (GA) significa:</p><ul><li><p><strong>Compatibilidad total con versiones anteriores.</strong> Con el tiempo, se agregarán nuevos campos y tipos de panel, pero los campos y el comportamiento actuales se mantendrán sin cambios. Cualquier cambio futuro que rompa la compatibilidad será considerado con mucho cuidado y solo se introducirá en una nueva versión principal del stack.</p></li><li><p><strong>Listo para producción con soporte completo.</strong> La API ofrece las garantías completas de soporte de Elastic. Puedes usarla de manera segura en entornos de producción para despliegues automatizados, promoción del entorno y administración programática del dashboard.</p></li></ul><h2>Nuevos endpoints de la API Kibana para los paneles de etiquetas, markdown y enlaces</h2><p>Elastic 9.5 también introduce un nuevo endpoint independiente para <a href="https://dashboardsapispec.kibana.dev/tags.html"><strong>etiquetas</strong></a>, que permite categorizar y filtrar paneles. Ahora puedes administrarlos mediante programación a través de endpoints CRUD dedicados, lo que facilita organizar paneles a gran escala en todos los entornos.	</p><p>Los nuevos endpoints de paneles <a href="https://dashboardsapispec.kibana.dev/markdowns.html"><strong>Markdown</strong></a> y <a href="https://dashboardsapispec.kibana.dev/links.html#tag/Links"><strong>Enlaces</strong></a> ya están disponibles en Serverless y llegarán en la próxima versión de la pila (9.6).</p><h2>¿Qué tipos de paneles soporta la API de Kibana Dashboards?</h2><p>La API de dashboards admite todos los <em>paneles por valor</em> en la versión 9.5 (los definidos directamente en un dashboard, a diferencia de los paneles de biblioteca guardados para su reutilización). Cada tipo de panel admitido tiene un esquema tipificado y validado.</p><p><strong>Tipo de panel</strong></p><p><strong>Estado</strong></p><p>Gráficos XY</p><p>Con soporte</p><p>Métricas</p><p>Con soporte</p><p>Circular</p><p>Con soporte</p><p>Calibre</p><p>Con soporte</p><p>Mapa de calor</p><p>Con soporte</p><p>Tablas de datos</p><p>Con soporte</p><p>Mapa de árbol</p><p>Con soporte</p><p>Sesiones de Discover</p><p>Con soporte</p><p>Controles</p><p>Con soporte</p><p>Markdown</p><p>Con soporte</p><p>Enlaces</p><p>Con soporte</p><p>Paneles de ML</p><p>Con soporte</p><p>Paneles de Observability</p><p>Con soporte</p><p>Mapas</p><p>Próximamente</p><p>Vega</p><p>Próximamente</p><h2>Cómo gestionar los dashboards de Kibana como código</h2><p>La API de dashboards permite un flujo de trabajo completo de dashboards como código: exportar un dashboard como JSON limpio y diferenciable, hacer commit en Git como fuente de verdad, revisar los cambios en pull requests, y desplegar la misma definición en desarrollo, staging y producción. Una vez que un dashboard se administra como código, trata a Git como la única fuente de verdad: los cambios efectuados directamente en la UI se sobrescriben la próxima vez que despliegues.</p><p>El principal desafío al mover un dashboard entre espacios, clústeres o etapas es que los dashboards hacen referencia a objetos como Data view y visualizaciones de biblioteca mediante un ID. Debido a que estos ID se generan automáticamente y difieren de un entorno a otro, un dashboard exportado desde un entorno puede apuntar a objetos que no existen en otro. Hay tres formas de manejarlo, enumeradas aquí de la más a la menos automatizada:</p><ul><li><p><strong>Usa Terraform.</strong> El <a href="https://registry.terraform.io/providers/elastic/elasticstack/latest/docs/resources/kibana_dashboard">proveedor Elastic Stack Terraform</a> rastrea cada recurso y mapea automáticamente los ID por entorno, por lo que las referencias se mantienen estables mientras promocionas un dashboard desde el desarrollo hasta la producción.</p></li><li><p><strong>Definir por valor </strong><a href="https://www.elastic.co/docs/explore-analyze/visualize/esorql"><strong>Paneles de lenguaje de búsqueda de Elasticsearch (ES|QL)</strong></a><strong>.</strong> La forma más portátil de construir un panel es definir su visualización con ES|QL directamente en el dashboard. Una consulta <a href="https://www.elastic.co/docs/explore-analyze/query-filter/languages/esql-kibana">ES|QL</a> lee los índices que se especifican en ella, por lo que el panel no contiene referencias externas a vistas de datos ni a objetos de biblioteca. El resultado es un dashboard totalmente autónomo y portátil.</p></li><li><p><strong>Asigna ID coincidentes.</strong> Si haces referencia a objetos guardados, como Data view o visualizaciones de biblioteca, créalos con un ID elegido usando PUT (upsert) en lugar de POST (que genera automáticamente un ID). Emplea ID legibles por humanos, como logs-prod, para que sean fáciles de reutilizar y reconocer en diferentes entornos.</p></li></ul><p>Para un recorrido detallado de estos patrones de portabilidad y del flujo de trabajo completo de dashboards como código, consulta la <a href="https://www.elastic.co/docs/explore-analyze/dashboards/manage-dashboards-as-code#dashboards-as-code-portability">documentación de Gestionar dashboards como código</a>.</p><h3>Crea un dashboard de Kibana con la API de dashboards usando PUT</h3><p>Aquí hay un ejemplo rápido de crear un dashboard con un panel métrico usando PUT en lugar de POST para asignar un ID personalizado usando el nombre del dashboard (service-health-overview). La misma lógica funciona para crear visualizaciones independientes guardadas en la biblioteca.</p>PUT kbn:/api/dashboards/service-health-overview
{
  "title": "Service health overview",
  "description": "Key service metrics — managed via API",
  "tags": [
    "production",
    "sre-team"
  ],
  "panels": [
    {
      "type": "vis",
      "grid": {
        "x": 0,
        "y": 0,
        "w": 12,
        "h": 8
      },
      "config": {
        "title": "Error rate (5xx)",
        "type": "metric",
        "data_source": {
          "type": "esql",
          "query": "FROM logs-* | WHERE http.response.status_code &gt;= 500 | STATS error_rate=count(*) BY host.name"
        },
        "metrics": [
          {
            "type": "primary",
            "column": "count"
          }
        ]
      }
    }
  ]
}<h2>Roadmap de la API de paneles de Kibana: Maps, Vega y endpoints independientes</h2><p>Estamos ampliando activamente el alcance de la API. El siguiente paso es agregar compatibilidad con mapas y paneles Vega, incluyendo esquemas tipados para ellos. También estamos creando endpoints CRUD independientes para las sesiones de Discover (más allá de su compatibilidad actual como paneles del dashboard), Vega, Maps y Anotaciones, desacoplados del ciclo de vida del dashboard.</p><p>Para las definiciones completas de esquemas, visita la <a href="https://dashboardsapispec.kibana.dev/dashboards#tag/Dashboards">documentación de la API de Dashboards</a>. Para los usuarios de Terraform, el <a href="https://registry.terraform.io/providers/elastic/elasticstack/latest/docs/resources/kibana_dashboard">proveedor Terraform de Elastic Stack</a> es compatible con la API GA Dashboards.</p><h2>Nota</h2><ol><li><p>Los endpoints de núcleo no han cambiado desde la vista previa técnica. Si construiste integraciones contra 9.4, funcionan en 9.5. Los únicos cambios incompatibles son dos menores que afectan al listado del dashboard y a los formatos de unidades de duración, documentados <a href="https://www.elastic.co/docs/release-notes/kibana/breaking-changes">aquí</a>.</p></li></ol>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/dashboards-as-code-kibana-api</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/dashboards-as-code-kibana-api</guid>
    <category><![CDATA[Kibana]]></category>
    <category><![CDATA[Experiencia del desarrollador]]></category>
    <category><![CDATA[Integraciones]]></category>
    <dc:creator><![CDATA[Teresa Alvarez Soler]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8ed7e33de291f255/6a730619c8b7ac02b251f9d3/image1.png" length="0" type="image/png"/>
    <pubDate>Wed, 05 Aug 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Local en menos de 5 minutos: modelos de incrustación de Jina ya disponibles para el despliegue local]]></title>
    <description><![CDATA[Los 28 modelos de Jina AI, incluidos los reclasificadores, como contenedores docker listos para desplegar, con cero telemetría y sin servidor de licencias. Compatible de forma directa con las API de OpenAI, Cohere, Voyage AI y Elastic Inference Service.]]></description>
    <content:encoded><![CDATA[<p>Los 28 modelos de incrustación y reclasificación de Jina AI ahora se envían como contenedores docker completamente fuera de línea para despliegues locales, incluidos <a href="https://www.elastic.co/es/search-labs/blog/jina-embeddings-v5-omni-all-media-one-index">jina-embeddings-v5-omni</a><a href="https://www.elastic.co/es/search-labs/blog/jina-embeddings-v5-omni-all-media-one-index"> </a>y <a href="https://www.elastic.co/es/search-labs/tutorials/jina-tutorial/jina-reranker-v3">jina-reranker-v3</a>. Descarga uno, transfiérelo a un sistema local aislado o protegido por firewall, y la inferencia local estará funcionando en menos de cinco minutos. Los contenedores son completamente independientes y no hacen conexiones externas. No hay llamadas a Hugging Face ni a ningún registro de modelos. Tampoco hay un servidor de licencias ni endpoints de telemetría o logging. Para las industrias reguladas, los requisitos de soberanía de datos o los entornos donde el acceso a internet no es confiable o simplemente no está disponible, esto elimina la dependencia de los servicios de IA de terceros. Jina local brinda soporte para los esquemas de API de Elastic Inference Service (EIS), OpenAI, Cohere, Voyage AI y Gemini, por lo que las aplicaciones existentes funcionan sin cambios de código.</p><p>Los modelos de IA más potentes se ejecutan en instalaciones cloud remotas con acceso a través de una API web, lo que significa que tienes que confiar en tu proveedor de servicios de IA para la seguridad, la disponibilidad del servicio y los precios estables. No puedes alinear fácilmente las exigencias razonables de confiabilidad, privacidad, costos manejables y una buena gobernanza de datos con un uso de la IA cada vez más potente, sofisticado e intensivo en recursos.</p><p>Las regulaciones gubernamentales, los fallos judiciales y las consideraciones comerciales efectuadas en interés de terceros han dado como recientemente derivaron en la restricción del acceso a servicios específicos. Y, aunque puedas cambiar a otros servicios, los modelos de IA no son componentes que se puedan intercambiar simplemente cuando quieras. Las aplicaciones que usan incrustaciones semánticas dependen de tener acceso a los mismos modelos al momento de la búsqueda que al momento de la ingesta de datos. Perder el acceso a tu modelo de incrustación significa que tu sistema de búsqueda se detiene.</p><p>Los modelos de precios de AI agravan ese riesgo. Las declaraciones financieras recientes de los principales proveedores de IA dan a los clientes buenas razones para preocuparse por los posibles aumentos de precios. La dependencia de productos con costos impredecibles suma más riesgo a las inversiones en IA intensivas en capital que tal vez no generen retornos claros.</p><p>Jina On-Prem es la respuesta de Elastic a estos desafíos.</p><h2>¿Quién necesita IA local?</h2><p>El alojamiento local y el control directo sobre tus modelos de IA brindan soporte a una variedad de demandas técnicas, requisitos de la industria e intereses comerciales.</p><p>La instalación local reduce lo que pagas a tus proveedores de servicios de IA, pero traslada el costo del hardware y del acceso confiable a tu organización. Dependiendo de tu volumen de uso, simplemente puede ser más económico. Pero hay razones adicionales y apremiantes para considerar ejecutar tu propia IA. Si alguno de los problemas descritos a continuación afecta a tu empresa, considera una solución de IA local como Jina On-Prem. Esta lista no es exhaustiva.</p><p>Caso de uso</p><p>¿Por qué local?</p><p>Ejemplo</p><p>Aislado/alta seguridad</p><p>Sin transmisión de datos salientes; aislamiento completo de la red</p><p>Defensa, inteligencia, investigación clasificada</p><p>Cumplimiento normativo</p><p>Soberanía de datos; sin transmisión transfronteriza ni exposición a terceros</p><p>Atención médica (Ley de Portabilidad y Responsabilidad de los Seguros Médicos [HIPAA]), finanzas, empresas de la UE (Reglamento General de Protección de Datos [RGPD])</p><p>Crítico para la latencia</p><p>Cero dependencia de red; ninguna tolerancia a las fallas de conexión</p><p>Robótica, computación perimetral, vehículos, buques</p><p>Previsibilidad de costos</p><p>Costo fijo de infraestructura frente a precios por token con tarifas futuras inciertas</p><p>Cargas de trabajo de inferencia continua de alto volumen</p><p>Reducción de responsabilidad</p><p>Sin exposición de datos a terceros; mantiene el privilegio legal y el deber de cuidado</p><p>Firmas de abogados, agencias gubernamentales</p><h3>Por qué los sistemas aislados y protegidos por firewall necesitan IA local</h3><p>Los sistemas aislados de la red y protegidos por firewall no pueden usar API de IA externas. Jina On-Prem se ejecuta por completo dentro de tu infraestructura sin conexiones salientes.</p><p>Para las organizaciones que gestionan datos especialmente confidenciales, las consideraciones de seguridad y privacidad son primordiales. De poco sirve invertir en proteger tus datos confidenciales si inmediatamente se los entregas a un tercero remoto que puede tener una seguridad insuficiente o que podría estar sujeto a las exigencias de un gobierno extranjero.</p><p>Los empleados de organizaciones que manejan datos confidenciales suelen recibir cierta capacitación sobre el manejo seguro de datos, pero esto no es muy efectivo cuando todos tienen navegadores web que pueden estar abiertos en cualquier página de Internet mientras manejan esos datos. El aislamiento es la medida de seguridad más efectiva disponible, ya sea mediante aislamiento de red o firewalls muy restrictivos, pero eso dificulta el uso de servicios externos de cualquier tipo.</p><h3>IA local para sistemas sensibles a la latencia y de alta disponibilidad</h3><p>El software como servicio y la computación en la nube representan una solución intermedia entre el costo de ofrecer servicios muy accesibles y confiables en tus propias computadoras y delegar el problema a un tercero. Pero vienen con latencia variable, interrupciones y una pérdida total de control cuando las cosas salen mal. Los servicios de IA no son la excepción. Si tu sistema de búsqueda queda fuera de línea cuando no puedes acceder a tu modelo de embedding, es posible que ya no parezca una buena solución intermedia.</p><p>Además, confiar en la IA externa siempre implica riesgos que no puedes prever ni gestionar fácilmente. El acceso a internet y la latencia de red pueden degradarse sin previo aviso como resultado de acontecimientos políticos, mal tiempo o barcos que arrastran sus anclas sobre cables de fibra óptica submarinos. Los gobiernos pueden usar prohibiciones de exportación para bloquear repentinamente el acceso a los modelos de IA, y recientemente lo han hecho. Los proveedores de servicios de IA a veces retiran modelos para inducirte a cambiar a otros más nuevos. La flexibilidad y los costos gestionados de los servicios externos deben equilibrarse con los riesgos de la dependencia.</p><h3>IA local para el cumplimiento del RGPD, la HIPAA y la soberanía de datos</h3><p>Las organizaciones que recopilan datos están sujetas a normativas cada vez más estrictas que a menudo difieren entre jurisdicciones y pueden imponer requisitos contradictorios. En particular, <a href="https://www.hhs.gov/hipaa/for-professionals/privacy/laws-regulations/index.html">las reglas de la HIPAA</a> imponen protecciones de datos muy estrictas a los proveedores de salud estadounidenses, y las sólidas leyes generales de protección de datos en <a href="https://laws-lois.justice.gc.ca/eng/acts/p-8.6/">Canadá</a>, la <a href="https://gdpr-info.eu/">Unión Europea</a> y <a href="https://www.japaneselawtranslation.go.jp/en/laws/view/4241">muchas jurisdicciones asiáticas</a> exigen que todas las empresas que manejan información personal lo hagan de manera segura y limiten la transmisión de dichos datos a otras partes u otras jurisdicciones. Estas reglas pueden incluso imponer obligaciones a entidades extranjeras si tienen algún cliente en esas jurisdicciones. Las instituciones financieras suelen estar sujetas a reglas aún más estrictas y asumen la misma responsabilidad directa en cuanto a la seguridad de la información que las que aplican para protegerse contra otras formas de actividad criminal.</p><p>El cumplimiento normativo puede ser incompatible con los servicios de IA externos, especialmente si usarlos implica la transmisión transfronteriza de datos.</p><p>Además, los eventos recientes muestran que las reglas que restringen la ubicación física de los almacenes de datos pueden no ser una fuente confiable de protección cuando los proveedores de servicios de nube internacionales están sujetos a la presión de gobiernos extranjeros. Las leyes locales pueden entrar en conflicto entre jurisdicciones, lo que exige el almacenamiento y procesamiento local de datos e imposibilita el uso de servicios de terceros. En algunos casos, la única solución es llevar todas las partes de tus procesos de forma interna, incluidos tus sistemas de IA.</p><h3>Riesgos de responsabilidad de la IA por la transmisión de datos de terceros</h3><p>Las leyes de protección de datos y los deberes de cuidado reconocidos respecto de los datos confidenciales suelen tener implicaciones de responsabilidad, a veces muy graves. Puedes ser responsable del manejo de tus datos por parte de terceros proveedores de servicios. Si bien los tribunales y los procedimientos legales pueden brindar algunas protecciones retrospectivas frente a proveedores de servicios que no cumplen los requisitos de seguridad, esos recursos no están disponibles ni son generalmente efectivos contra actores de seguridad nacional, las fuerzas de orden público o hackers criminales.</p><p>Para los gobiernos, ya ha habido casos de proveedores de servicios de nube transfronterizos que revelan información estatal confidencial a actores extranjeros.</p><p>Pero, aunque no te preocupen los gobiernos extranjeros o los hackers, y aunque tus proveedores de servicios de IA externos sean seguros en sí mismos, el solo hecho de que sean externos puede generar responsabilidades.</p><p>Por ejemplo, en la mayoría de las jurisdicciones, las comunicaciones de los abogados con sus clientes gozan de protecciones legales especiales, y los despachos de abogados tienen responsabilidades estrictas al grabar o almacenar esta información. En Estados Unidos, este “privilegio abogado-cliente” es tan famoso que es un elemento central en las tramas de películas y series de TV. Pero una de las formas en que ese privilegio se puede perder es comunicando información a alguien que no tiene este privilegio, y los avances recientes sugieren que los proveedores externos de servicios de IA podrían calificar.</p><p>Es posible, al menos en Estados Unidos, que el solo uso de servicios de IA de terceros a través de una API de internet, como modelos de incrustaciones que ofrecen servicios para indexar, infrinja reglas críticas de confidencialidad. Un bufete de abogados podría ser demandado, sancionado o inhabilitado tan solo por usar software hospedado externamente, incluso si no ocurre ninguna brecha de seguridad.</p><h3>IA local para sistemas offline, de borde y aislados físicamente</h3><p>Los sistemas informáticos no solo se aíslan por razones de seguridad. Por ejemplo, los vehículos en movimiento no pueden depender del acceso a Internet para ninguna función esencial. Los barcos y las aeronaves cuentan con sistemas informáticos a bordo muy amplios que deben funcionar sin conexiones a Internet y, por lo tanto, no pueden usar servicios de IA externos. Las plataformas marítimas, las instalaciones remotas en zonas silvestres y los servicios informáticos en el Ártico, la Antártida y pequeñas islas sin conexiones físicas adecuadas a las redes globales son ejemplos de instalaciones que se benefician de hospedar localmente todos los servicios que necesitan. A medida que aumenta el rol de la IA en la informática empresarial, resulta más importante abordar estas limitaciones.</p><p>Las aplicaciones emergentes de la IA a sistemas físicos (robótica y otros casos de uso espacialmente delimitados u orientados al mundo exterior, como sistemas de gestión logística o incluso cajas de supermercado) pueden estar conectadas a la internet global, pero no tienen tolerancia a las fallas de conexión o los picos de latencia. Si dependen de un sistema de IA para funcionar, ese sistema de IA debe ser lo más local y confiable posible.</p><h2>¿Quién no necesita IA local?</h2><p>Los servicios de software remotos y la IA fuera de las instalaciones sí tienen beneficios. Ejecutar modelos de IA puede requerir procesadores costosos y de alto consumo de energía con una vida útil notoriamente corta. El acceso a hardware de alta calidad es particularmente difícil en este momento debido a factores del mercado e impactos económicos externos. Dadas las circunstancias, puede tener sentido pagar por token para usar una API externa en lugar de asumir los elevados costos de capital de la IA local.</p><p>Las API externas son más útiles para los usuarios intermitentes. Si usas modelos de IA principalmente para procesar datos en batch para análisis, en lugar de ejecutar un sistema de búsqueda que tenga que estar en línea todo el tiempo, tiene poco sentido invertir en hardware con uso intensivo de capital e instalaciones locales.</p><p>Además, cuando el procesamiento de tus datos ya en la nube por razones de confiabilidad y accesibilidad, usar servicios de IA ubicados en la misma infraestructura de nube puede ofrecer una mejor relación precio-calidad que introducir tu propio despliegue de modelo de IA con licencia. Ya dependes de tu proveedor de servicios en la nube, por lo que depender de sus servicios de IA no añade mucho riesgo.</p><p>Si tu caso de uso se ajusta a esa descripción, los modelos de Jina AI están disponibles en <a href="https://www.elastic.co/docs/explore-analyze/elastic-inference/eis">EIS</a>, <a href="https://aws.amazon.com/marketplace/seller-profile?id=seller-stch2ludm6vgy">AWS Marketplace</a> y <a href="https://console.cloud.google.com/marketplace/browse?q=jina">Google Cloud Platform</a> específicamente para satisfacer tus necesidades.</p><p>La tabla a continuación resume los factores clave. La respuesta que obtengas depende de tus datos, infraestructura y patrón de uso.</p><p>Factor</p><p>Preferentemente local</p><p>API de nube preferida</p><p>Patrón de uso</p><p>Inferencia continua o de alto volumen</p><p>Procesamiento intermitente o por batch</p><p>Sensibilidad de los datos</p><p>Regulado, soberano o clasificado</p><p>Sin restricciones transfronterizas ni de terceros</p><p>Entorno de red</p><p>Aislado, protegido por firewall o poco confiable</p><p>Internet estable y siempre activa</p><p>Infraestructura actual</p><p>Tienes o puedes adquirir hardware de GPU</p><p>Ya hospedada en la nube con IA coubicada</p><p>Modelo de costos</p><p>Hardware fijo + licencia; predecible a escala</p><p>Por token; menor costo inicial, variable a largo plazo</p><p>Tolerancia a la latencia</p><p>Ninguna (robótica, borde, tiempo real)</p><p>La variabilidad de la red es aceptable</p><p>Responsabilidad operativa</p><p>Tu equipo administra el hardware y la disponibilidad</p><p>El proveedor gestiona el hardware y las actualizaciones; tú gestionas la integración</p><p>Tienes que considerar los costos y beneficios a la luz de tus circunstancias particulares y de tus casos de uso, teniendo en cuenta los problemas destacados en la sección anterior que se apliquen a ti. El análisis costo-beneficio sin duda cambiará con el tiempo. No podemos predecir el futuro de la industria de la IA o los precios del hardware incluso a corto plazo.</p><h2>Presentamos Jina On-Prem</h2><p>Para los usuarios que pueden beneficiarse de los servicios de IA locales, presentamos <a href="https://github.com/jina-ai/jina-on-prem/wiki/">Jina On-Prem</a>, una suite de instalación totalmente independiente para los modelos de alto rendimiento de Jina AI.</p><p>Los modelos de Jina AI igualan la precisión de los modelos de incrustación <a href="https://mteb-leaderboard.hf.space/benchmark/MTEB(Multilingual%2C%20v2)">varias veces más grandes</a>, lo que reduce los costos informáticos, la huella de memoria y los requisitos de hardware. Esto los convierte en una opción ideal para los usuarios que quieren o necesitan mantener su IA de forma local. Las licencias comerciales están disponibles con soluciones escalables y de precio proporcional para casos de uso de todas las envergaduras.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt190865fb3ebde472/6a6a33d0065b162508701ff9/02559ceca556a26c53eb703ae87d421452b27251-1374x1400.png" alt="MMTEB Multilingual v2 leaderboard showing Jina AI embedding model rankings: jina-embeddings-v5-omni-small and jina-embeddings-v5-text-small ranked 13th, jina-embeddings-v5-omni-nano and jina-embeddings-v5-text-nano ranked 19th, competing against models from Microsoft, Google, Tencent, NVIDIA and Qwen" /><h3>¿Qué esquemas de API soporta Jina On-Prem?</h3><ul><li><p>Disponible como una colección completa de dependencias para instalación local o como un <a href="https://www.docker.com/">contenedor docker</a> que puedes instalar y ejecutar en minutos.</p></li><li><p>Las instalaciones de Jina On-Prem <em>no</em> hacen llamadas a sistemas externos.</p><ul><li><p>Sin llamadas a Hugging Face Hub ni a ningún registro de modelos (HF_HUB_OFFLINE=1 y TRANSFORMERS_OFFLINE=1 están integrados).</p></li><li><p>No hay un servidor de licencias.</p></li><li><p>No hay endpoints de telemetría o de logging.</p></li></ul></li><li><p>Soporta hardware de CPU y GPU, con autodetección de GPU.</p></li><li><p>Todos los 28 modelos de Jina AI disponibles, incluidos los últimos modelos de incrustación multimodal <a href="https://www.elastic.co/es/search-labs/blog/jina-embeddings-v5-omni-all-media-one-index">jina-embeddings-v5-omni</a> y <a href="https://www.elastic.co/es/search-labs/tutorials/jina-tutorial/jina-reranker-v3">jina-reranker-v3</a>.</p></li><li><p>Acceso mediante esquemas estándar de API de IA: <a href="https://jina.ai/api-dashboard">API de Jina</a>, OpenAI, Cohere, Voyage AI y Gemini. Jina On-Prem es una solución directa para las aplicaciones creadas sobre esos esquemas.</p></li><li><p>Reemplazo directo para modelos servidos por el <a href="https://www.elastic.co/docs/explore-analyze/elastic-inference/eis">EIS</a>. Jina On-Prem se integra directamente con <a href="https://www.elastic.co/es/blog/deploy-elastic-air-gapped-disconnected-environments">despliegues de Elastic aislados de la red</a>.</p></li></ul><h2>Requisitos de hardware para los modelos locales de Jina AI</h2><p>Los requisitos de hardware varían para los diferentes modelos de Jina. La tabla de abajo muestra las recomendaciones para los modelos más recientes usando la configuración de GPU. No necesitas nada más potente que una GPU NVIDIA L4, aunque se recomienda una A100 para los modelos de incrustación v5. Nuestro modelo de incrustación más reciente requiere actualmente un mínimo de 8 GB de VRAM.</p><p>Modelo</p><p>VRAM mínima</p><p>GPU recomendada</p><p>jina-embeddings-v5-text-nano</p><p>2 GB</p><p>T4 / L4</p><p>jina-embeddings-v5-text-small</p><p>3 GB</p><p>L4 / A10G</p><p>jina-embeddings-v5-omni-small</p><p>8 GB</p><p>L4 / A10G / A100</p><p>jina-reranker-v3</p><p>3 GB</p><p>L4</p><p>jina-clip-v2</p><p>4 GB</p><p>L4</p><p>jina-code-embeddings-1.5b</p><p>4 GB</p><p>L4</p><p>ReaderLM-v2</p><p>4 GB</p><p>L4</p><p>Si usas más de un modelo a la vez, los requisitos de VRAM aumentarán. Consulta la <a href="https://github.com/jina-ai/jina-on-prem/wiki/Sizing-And-Hardware">página de Dimensionamiento y hardware</a> para obtener más información.</p><h2>Cómo instalar Jina On-Prem con docker</h2><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt20265d09e2d8d0f4/6a6a33d1065b162105701ffd/ada9881af407168298b1940f8537ad71a5411c89-1999x1200.png" alt="" /><p>La forma más rápida de dar los primeros pasos es <a href="https://www.docker.com/get-started/">instalar docker</a> (si aún no lo has hecho) y seguir las instrucciones de la página <a href="https://github.com/jina-ai/jina-on-prem/wiki/QuickStart">Inicio rápido de Jina On-Prem</a>.</p><p>Hay contenedores docker preconfigurados para los 28 modelos de Jina. Descarga uno y transfiérelo a tu destino de instalación, y podrás tener modelos de Jina AI ejecutándose en menos de cinco minutos.</p><p>Para compilaciones multimodales o personalizadas, o para descargar el conjunto completo de dependencias para la instalación fuera de un contenedor, sigue los pasos descritos en la <a href="https://github.com/jina-ai/jina-on-prem/wiki/Bundling-Guide">guía de empaquetado</a>.</p><p>Tu instalación de Jina On-Prem brinda soporte para toda la funcionalidad de la API de Jina y de EIS, y para la generación de incrustaciones mediante las API de OpenAI, Cohere, Voyage AI y Gemini, por lo que puede integrarse en aplicaciones preexistentes que usen interfaces estándar. Consulta la <a href="https://github.com/jina-ai/jina-on-prem/wiki/API-Reference">documentación de la API</a> para obtener más información.</p><p>Los modelos de Jina, incluidos los modelos instalados con Jina On-Prem, están disponibles bajo diversos términos de licencia, y los últimos modelos son gratuitos para uso no comercial bajo una licencia <a href="https://creativecommons.org/licenses/by-nc/4.0/deed.en">CC BY-NC 4.0</a>. Para adquirir la licencia de Jina On-Prem para uso comercial, ponte en contacto con <a href="https://www.elastic.co/es/contact">Ventas de Elastic</a>.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/on-prem-ai-jina-embedding-models</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/on-prem-ai-jina-embedding-models</guid>
    <category><![CDATA[Jina AI]]></category>
    <category><![CDATA[Integraciones]]></category>
    <dc:creator><![CDATA[Scott Martens]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt17731ab0c6ec66f6/6a6a33d140a4941014ca5c9a/09bc6dac4e6a86c7877f8ed78d68f5d581aeffa9-1999x1200.png" length="0" type="image/png"/>
    <pubDate>Thu, 23 Jul 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Potenciando Elasticsearch: agregamos soporte nativo de la API de Prometheus]]></title>
    <description><![CDATA[Realiza una búsqueda en Elasticsearch directamente desde clientes compatibles con Prometheus a través de endpoints nativos de PromQL, descubrimiento y metadatos. Envía datos a Elasticsearch con Prometheus Remote Write.]]></description>
    <content:encoded><![CDATA[<p>Conecta cualquier cliente compatible con Prometheus a Elasticsearch y ejecuta PromQL directamente sobre tus métricas existentes. Elasticsearch está agregando endpoints de búsqueda, descubrimiento y metadatos nativos de Prometheus como una vista previa tecnológica que funciona sobre métricas ingeridas a través de Prometheus Remote Write, OpenTelemetry o la API de bulk. La API se ejecuta sobre los flujos de datos temporales (TSDS) de Elasticsearch, por lo que no hay una capa de almacenamiento específica de Prometheus para operar.</p><p>Esta publicación explica cómo los endpoints de búsqueda, descubrimiento y metadatos se basan en el trabajo anterior de ingesta y búsqueda para formar esa superficie de API. Las publicaciones complementarias profundizan en las piezas individuales:</p><ul><li><p><a href="https://www.elastic.co/observability-labs/blog/elasticsearch-supports-promql">La compatibilidad nativa con PromQL en ES|QL</a> explica cómo se traducen las consultas PromQL en planes de ejecución de ES|QL.</p></li><li><p><a href="https://www.elastic.co/observability-labs/blog/prometheus-remote-write-elasticsearch">Envía métricas de Prometheus a Elasticsearch con Remote Write</a> cubre la configuración de ingesta.</p></li><li><p><a href="https://www.elastic.co/observability-labs/blog/prometheus-remote-write-elasticsearch-architecture">El artículo “Cómo funciona la ingesta de escritura remota de Prometheus en Elasticsearch”</a> explica los detalles internos de la escritura remota.</p></li></ul><p>Esto aún está en desarrollo. En las secciones siguientes se indica qué es lo que ya está disponible y qué partes aún están en desarrollo.</p><h2>La superficie de la API</h2><p>Hoy en día, la interfaz de programación de aplicaciones (API) compatible con Prometheus se divide en tres grupos.</p><h3>Endpoints de consulta</h3><p>Los endpoints de consulta permiten a los clientes compatibles con Prometheus evaluar expresiones PromQL:</p><ul><li><p><code>GET /_prometheus/api/v1/query_range</code> evalúa una expresión PromQL en un intervalo de tiempo (resultados en forma de matriz).</p></li><li><p><code>GET /_prometheus/api/v1/query</code> evalúa en un solo punto en el tiempo (resultados vectoriales). Actualmente implementado como una búsqueda de alcance corto que devuelve la última muestra.</p></li></ul><p>Hoy en día, solo GET es compatible con los puntos de consulta. Algunos clientes usan POST de forma predeterminada, así que es posible que tengas que configurarlos para que usen GET. La convención POST de Prometheus emplea <code>application/x-www-form-urlencoded</code> cuerpos, que la capa HTTP de Elasticsearch rechaza como salvaguarda CSRF antes de que la solicitud llegue al controlador.</p><p>Para ver el estado completo de la cobertura de PromQL, consulta la <a href="https://www.elastic.co/observability-labs/blog/elasticsearch-supports-promql">publicación complementaria sobre PromQL en ES|QL</a>.</p><h3>Endpoints de metadatos</h3><p>Los endpoints de metadatos proporcionan la información de descubrimiento que los clientes necesitan para el autocompletado, los desplegables de variables y la navegación de métricas.</p><p>La serie, las etiquetas y los puntos finales de valores de etiqueta aceptan selectores <code>match[]</code> y un rango de tiempo (<code>start</code>/<code>end</code>). El parámetro <code>match[]</code> toma un selector de serie de Prometheus como <code>http_requests_total{job="api"}</code> y restringe la respuesta a series temporales que coinciden. Esto garantiza que las respuestas sean rápidas y pertinentes en clústeres con una gran cantidad de métricas. Por ejemplo:</p>GET /_prometheus/api/v1/series?match[]=http_requests_total{job="api"}GET /_prometheus/api/v1/labels?match[]=http_requests_totalGET /_prometheus/api/v1/label/instance/values?match[]=http_requests_total{job="api"}<p>La primera devuelve todas las series para <code>http_requests_total</code> donde <code>job="api"</code>, con sus conjuntos de etiquetas completos. La segunda devuelve solo los nombres de las etiquetas que existen en las series de <code>http_requests_total</code>. El tercero solo devuelve los valores de <code>instance</code> que aparecen en las series coincidentes.</p><p><code>GET /_prometheus/api/v1/metadata</code> es diferente: devuelve el tipo y la unidad para cada métrica, opcionalmente filtrados por nombre mediante un parámetro <code>metric</code>.</p>GET /_prometheus/api/v1/metadata?metric=http_requests_total<p>No acepta <code>match[]</code> selectores ni un intervalo de tiempo. En Prometheus, los metadatos se recogen de objetivos activos de extracción (las líneas <code>HELP</code>, <code>TYPE</code> y <code>UNIT</code> que exponen), por lo que la respuesta no implica un escaneo de datos. Elasticsearch no tiene un almacén de metadatos dedicado como ese, por lo que la implementación actual descubre metadatos de métricas visitando datos temporales de las últimas 24 horas. Esto mantiene la consulta rápida sin requerir un escaneo completo del índice. Ese retroceso de 24 horas está corregido hoy: la API de metadatos de Prometheus no expone los parámetros <code>start</code> o <code>end</code> que Elasticsearch podría usar para que sean ajustables por el usuario.</p><p>Cómo funcionan los endpoints de metadatos de forma interna, incluidos los comandos <code>TS_INFO</code> y <code>METRICS_INFO</code> que los potencian, se explica <a href="https://www.elastic.co/search-labs/blog//elasticsearch-native-prometheus-api#ts-info-and-metrics-info">a continuación</a>.</p><h3>Pre-filtrado de índices</h3><p>Todos los endpoints de consulta y metadato aceptan un segmento de ruta <code>{index}</code> opcional después de <code>/_prometheus/</code>:</p>GET /_prometheus/metrics-prod-*/api/v1/query_range?query=up&amp;start=...&amp;end=...<p>Esto limita los índices de Elasticsearch contra los que se ejecuta la consulta antes de que comience la evaluación de cualquier expresión. En clústeres con muchos flujos de datos entre equipos o entornos, esto evita escanear índices no relacionados y puede reducir en gran medida la latencia de las consultas. Puedes configurar fuentes de datos independientes por patrón de índice para dar a los equipos acceso limitado a sus propias métricas.</p><h3>Una nota sobre Remote Write</h3><p>Para la ingesta, Elasticsearch también expone el endpoint estándar de escritura remota de Prometheus:</p><ul><li><p><code>POST /_prometheus/api/v1/write</code> ingiere series temporales a través del protocolo Prometheus Remote Write v1. v2 aún no es compatible.</p></li></ul><p>La escritura remota escribe en los flujos de datos temporales existentes (TSDS) de Elasticsearch, no en una capa de almacenamiento específica de Prometheus separada. Las etiquetas de Prometheus se convierten en dimensiones de TSDS, y los nombres de las métricas se convierten en campos en el mapeo del índice. La <a href="https://www.elastic.co/observability-labs/blog/prometheus-remote-write-elasticsearch-architecture">publicación de arquitectura de escritura remota</a> abarca todo el mapeo en detalle, incluso explica cómo se infieren los tipos de métricas y cómo se almacenan las etiquetas con un prefijo <code>labels.</code>.</p><h3>Cómo funciona</h3><p>Detrás de escena, todos los endpoints funcionan de la misma manera: parsean los parámetros HTTP entrantes, construyen un plan de consulta ES|QL, lo ejecutan contra los flujos de datos de series temporales y convierten el resultado columnar de vuelta al formato JSON que esperan los clientes de Prometheus.</p><h2>TS_INFO y METRICS_INFO</h2><p>Los endpoints de metadatos deben responder preguntas como "¿qué etiquetas existen?" o "¿qué tipos de métricas están definidos?" a través de lo que podrían ser millones de series temporales, sin tener que analizar cada punto de datos.</p><p>A nivel interno, los endpoints de metadatos de Prometheus responden a esas preguntas construyendo planes de ES|QL basados en dos nuevos comandos de procesamiento: <code>METRICS_INFO</code> y <code>TS_INFO</code>. No es necesario usar estos comandos directamente para usar la API de Prometheus, pero son las primitivas de ejecución del núcleo detrás de las respuestas de metadatos. Ambos funcionan visitando solo un documento por serie temporal para extraer su metadato, en vez de escanear todas las muestras. Esto significa que su costo varía en función de la cantidad de series temporales distintas, no de la cantidad de puntos de datos.</p><p><code>METRICS_INFO</code> devuelve una fila por cada métrica distinta, con su nombre, tipo, unidad y los campos de dimensión asociados. <code>TS_INFO</code> es más granular: una fila por combinación (métrica, serie temporal), incluyendo los valores de dimensión reales como un objeto JSON.</p><p>Muy pronto llegará una publicación de blog especial sobre <code>TS_INFO</code> y <code>METRICS_INFO</code> que cubrirá el modelo de ejecución en dos fases, cómo escalan y cómo usarlos directamente en consultas de ES|QL por fuera de la API de Prometheus.</p><h3>Cómo los endpoints de metadatos los emplean</h3><p>Cada endpoint de metadatos construye un ES|QL plan con uno de estos comandos como núcleo.</p><p><code>/api/v1/labels</code> y <code>/api/v1/series</code> usan <code>TS_INFO</code>, ya que necesitan detalles por serie temporal (qué etiquetas existen, qué valores de dimensión identifican cada serie). <code>/api/v1/metadata</code> y <code>/api/v1/label/__name__/values</code> usan <code>METRICS_INFO</code>, ya que solo necesitan información por métrica (nombres de métricas, tipos, unidades).</p><p><code>/api/v1/label/{name}/values</code> para etiquetas normales (cualquier otra cosa que no sea <code>__name__</code>), no se usa ninguno de los dos comandos. Algunas etiquetas regulares como <code>job</code> o <code>instance</code> son campos dimensionales reales en el índice, por lo que el endpoint puede consultarlos directamente mediante una agregación según grupo. Cuando se proporcionan selectores <code>match[]</code>, se traducen en una cláusula <code>WHERE</code> que filtra la serie temporal antes de que se ejecute la agregación.</p><p>La etiqueta <code>__name__</code> necesita una estrategia diferente porque no siempre está presente como un campo dimensional. Prometheus Remote Write sí almacena <code>labels.__name__</code>, pero las métricas ingeridas por otros caminos (OpenTelemetry, la API de bulk) no la tienen. El nombre de la métrica está codificado en el propio nombre del campo (por ejemplo, <code>metrics.http_requests_total</code>). Podrías consultar las asignaciones de índices para enumerar los nombres de los campos, pero las asignaciones por sí solas no te dicen qué métrica tiene qué dimensiones, y no se pueden filtrar por valores de etiquetas de un selector <code>match[]</code>. <code>METRICS_INFO</code> puede hacer ambas cosas: enumera los nombres de las métricas en los índices mientras respeta los filtros <code>WHERE</code> upstream.</p><p>En todos los casos, la capa API gestiona la traducción de vuelta a las convenciones de Prometheus: eliminando los prefijos de almacenamiento <code>labels.</code> y <code>metrics.</code> y sintetizando <code>__name__</code> para métricas no Prometheus que carecen de ellas.</p><h2>En conclusión</h2><p>El resultado: cualquier cliente compatible con Prometheus puede buscar y explorar métricas de Elasticsearch a través de endpoints que ya entiende. Las métricas de Remote Write así como las de OpenTelemetry y las métricas indexadas a través de otras rutas aparecen todas a través de la misma API, respaldadas por los mismos índices TSDS.</p><p>Todas las API de Prometheus que se mencionan aquí ya están disponibles como versión preliminar técnica en Elasticsearch Serverless. Para clústeres autogestionados y despliegues alojados de Elastic Cloud Hosted, disponibles como vista previa técnica en Elasticsearch 9.4, con la excepción de <code>GET /_prometheus/api/v1/metadata</code>. Para probarlo a nivel local, usa <a href="https://www.elastic.co/docs/deploy-manage/deploy/self-managed/local-development-installation-quickstart">start-local</a>.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/elasticsearch-native-prometheus-api</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/elasticsearch-native-prometheus-api</guid>
    <category><![CDATA[Integraciones]]></category>
    <dc:creator><![CDATA[Felix Barnsteiner]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt12b4e100d5bbb7f0/6a16f7a22b835ff747f4afdd/c7b333bd73e8a1f4e18486b2d692ba742788dcfd-1376x768.jpg" length="0" type="image/jpeg"/>
    <pubDate>Mon, 11 May 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[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[Introducción a los modelos de Jina, su funcionalidad y usos en Elasticsearch]]></title>
    <description><![CDATA[Explora las incrustaciones multimodales de Jina, Reranker v3 y los modelos de incrustación semántica, y aprende cómo usarlos de forma nativa en Elasticsearch.]]></description>
    <content:encoded><![CDATA[<p>Jina de Elastic proporciona modelos fundamentales de búsqueda para aplicaciones y una automatización de procesos empresariales. Estos modelos proporcionan funcionalidad del núcleo para llevar la IA a las aplicaciones de Elasticsearch y a los proyectos innovadores de IA.</p><p>Los modelos de Jina se dividen en tres amplias categorías diseñadas para apoyar el procesamiento, la organización y la recuperación de información:</p><ul><li><p>Modelos de incrustación semántica</p></li><li><p>Modelos de reordenamiento</p></li><li><p>Pequeños modelos de lenguaje generativo</p></li></ul><h2>Modelos de incrustación semántica</h2><p>La idea detrás de las incrustaciones semánticas es que un modelo de IA puede aprender a representar aspectos del significado de sus entradas en términos de la geometría de espacios de alta dimensión.</p><p>Puedes pensar en una incrustación semántica como un punto (técnicamente un <em>vector</em>) en un espacio de alta dimensión. Un modelo de incrustación es una red neuronal que toma algunos datos digitales como entrada (potencialmente cualquier cosa, pero con mayor frecuencia un texto o una imagen) y genera la ubicación de un punto de alta dimensión correspondiente como un conjunto de coordenadas numéricas. Si el modelo funciona bien, la distancia entre dos incrustaciones semánticas es proporcional a la medida en que sus objetos digitales correspondientes significan lo mismo.</p><p>Para entender cómo esto es importante para las aplicaciones de búsqueda, imagina una incrustación para la palabra “perro” y una para la palabra “gato” como puntos en el espacio:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbad74e5d8292a60e/6a17db73abe0f2114edfe8d2/802cf9bbcb82180d3fc91009f9f62027eee8f031-615x615.png" alt="" /><p>Un buen modelo de incrustación debe generar una para la palabra “felino” que esté mucho más cerca de “gato” que de “perro”, y “canino” debe tener una incrustación mucho más cercana a “perro” que de “gato”, porque esas palabras significan casi lo mismo:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltdb2b25691801a881/6a17db747b54f946d28b37a5/bce49daf9a31b8fb7ce1c6ef7ae4e8117a4e8b33-615x615.png" alt="" /><p>Si un modelo es multilingüe, esperaríamos lo mismo para las traducciones de “gato” y “perro”:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt976ba40be7776449/6a17db75be6086c2bd0045f2/ce4d030385324526cbd7539140e0e634d939371c-615x615.png" alt="" /><p>Los modelos de incrustación traducen la similitud o diferencia en el significado entre las cosas en relaciones espaciales entre incrustaciones. Las imágenes anteriores solo tienen dos dimensiones para que puedas verlas en una pantalla, pero los modelos de incrustación producen vectores con docenas o miles de dimensiones. Esto les permite codificar sutilezas de significado para textos completos y asignar un punto en un espacio que tenga cientos o miles de dimensiones para documentos de miles de palabras o más.</p><h2>Incrustaciones multimodales</h2><p>Los modelos multimodales amplían el concepto de incrustaciones semánticas a otros elementos, además de los textos, especialmente a las imágenes. Esperaríamos que una incrustación para una imagen esté cerca de una incrustación de una descripción fiel de la imagen:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt66dc8895485734ec/6a17db77b1e11318d279f155/1ac6aef5b1423e5fe4853e8a547a74e66b0885c2-615x615.png" alt="" /><p>Las incrustaciones semánticas tienen muchos usos. Entre otras cosas, puedes utilizarlos para crear clasificadores eficientes, agrupar datos y llevar a cabo una variedad de tareas, como la deduplicación de datos y la investigación de la diversidad de datos, ambas importantes para aplicaciones de big data que implican trabajar con demasiados datos para gestionarlos manualmente.</p><p>El mayor uso directo de las incrustaciones es en la recuperación de información. Elasticsearch puede almacenar objetos de recuperación con incrustaciones como claves. Las consultas se convierten en vectores de incrustación y una búsqueda devuelve los objetos almacenados cuyas claves son las más cercanas a la incrustación de la consulta.</p><p>Donde la recuperación tradicional <em>basada en vectores</em> (a veces llamada <em>recuperación de vectores dispersos</em>) utiliza vectores basados en palabras o <em>metadatos</em> en documentos y búsquedas, la <em>recuperación basada en incrustaciones</em> (también conocida como recuperación de vectores densos) utiliza significados evaluados por la IA en lugar de palabras. Esto los hace, en general, mucho más flexibles y precisos que los métodos de búsqueda tradicionales.</p><h2>Aprendizaje de representación de Matryoshka</h2><p>El número de dimensiones que tiene una incrustación y la precisión de los números que contiene tienen un impacto significativo en el rendimiento. Los espacios de muy alta dimensión y los números extremadamente de alta precisión pueden representar información muy detallada y compleja, pero exigen modelos de IA más grandes que sean más caros de entrenar y de ejecutar. Los vectores que generan requieren más espacio de almacenamiento y se necesitan más ciclos de computación para calcular las distancias entre ellos. El uso de modelos de incrustación semántica implica hacer compensaciones importantes entre la precisión y el consumo de recursos.</p><p>Para maximizar la flexibilidad para los usuarios, los modelos de Jina se entrenan con una técnica llamada <a href="https://arxiv.org/abs/2205.13147">Aprendizaje de representación de Matryoshka</a>. Esto hace que los modelos carguen las distinciones semánticas más importantes en las primeras dimensiones del vector de incrustación para que simplemente puedas cortar las dimensiones superiores y aún obtener un buen rendimiento.</p><p>En la práctica, esto significa que los usuarios de los modelos de Jina pueden elegir cuántas dimensiones desean que tengan sus incrustaciones. Elegir menos dimensiones reduce la precisión, pero la degradación en el rendimiento es menor. En la mayoría de las tareas, las métricas de rendimiento de los modelos de Jina disminuyen entre un 1 % y un 2 % cada vez que reduces el tamaño de la incrustación en un 50 %, hasta aproximadamente una reducción del 95 % en el tamaño.</p><h2>Recuperación asimétrica</h2><p>La similitud semántica usualmente se mide de manera simétrica. El valor que obtienes al comparar “gato” con “perro” es el mismo que el valor que obtendrías al comparar “perro” con “gato”. Pero cuando usas incrustaciones para la recuperación de información, funcionan mejor si rompes la simetría y codificas las búsquedas de manera diferente a como codificas los objetos de recuperación.</p><p>Esto se debe a la forma en que entrenamos los modelos de incrustación. Los datos de entrenamiento contienen ejemplos de los mismos elementos, como palabras, en muchos contextos diferentes, y los modelos aprenden semántica al comparar las similitudes y diferencias contextuales entre los elementos.</p><p>Entonces, por ejemplo, podríamos encontrar que la palabra “animal” no aparece en muchos de los mismos contextos que “gato” o “perro”, y por lo tanto, la incrustación para “animal” podría no estar particularmente cerca de “gato” o “perro”:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltf219074e18a6290a/6a17db78be6086cf060045f6/9a33163405af6c71ee7f4ba8ebc86af39e295a69-615x615.png" alt="" /><p>Esto hace que sea menos probable que una búsqueda de "animal" recupere documentos sobre gatos y perros, lo cual es lo opuesto a nuestro objetivo. Así que, en su lugar, codificamos "animal" de forma diferente cuando es una consulta que cuando es un objetivo para la recuperación:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt33438b4964001467/6a17db79b1e113101c79f159/363992d4f0affba7937c0c8a9f82c9a531fcd3ba-615x615.png" alt="" /><p>La <em>recuperación asimétrica</em> significa usar un modelo diferente para las búsquedas o entrenar especialmente un modelo de incrustación para codificar cosas de una manera cuando se almacenan para su recuperación y para codificar consultas de otra manera.</p><h2>Incrustaciones multivectoriales</h2><p>Las incrustaciones únicas son buenas para la recuperación de información porque se ajustan al marco de trabajo básico de una base de datos indexada: almacenamos objetos para su recuperación con un único vector de incrustación como clave de recuperación. Cuando los usuarios consultan el almacén de documentos, sus búsquedas se traducen en vectores de incrustación y los documentos cuyas claves están más cercanas a la incrustación de búsquedas (en el espacio de incrustación de alta dimensión) se recuperan como posibles coincidencias.</p><p>Las incrustaciones multivectoriales funcionan un poco diferente. En lugar de generar un vector de longitud fija para representar una búsqueda y un objeto almacenado completo, producen una secuencia de incrustaciones que representan partes más pequeñas de ellos. Las partes suelen ser tokens o palabras para textos y son mosaicos de imagen para datos visuales. Estas incrustaciones reflejan el significado de la parte en su contexto.</p><p>Por ejemplo, considera estas oraciones:</p><ul><li><p>Ella tenía un corazón de oro.</p></li><li><p>Ella cambió de opinión con el corazón.</p></li><li><p>Ella tuvo un ataque al corazón.</p></li></ul><p>Superficialmente, se ven muy similares, pero un modelo multivectorial probablemente generaría incrustaciones muy diferentes para cada instancia de "corazón", representando cómo cada una significa algo diferente en el contexto de la oración completa:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt5c81f089771e6029/6a17db7b7f6f157601c099ec/a33e60c8d8ee3d312bca8375ca2a8b0a0cd40ba9-615x615.png" alt="" /><p>La comparación de dos objetos a través de sus incrustaciones multivectoriales a menudo implica medir su distancia de chaflán: comparar cada parte de una incrustación multivectorial con cada parte de otra y sumar las distancias mínimas entre ellas. Otros sistemas, incluidos los Jina Rerankers que se describen a continuación, los incluyen en un modelo de IA entrenado específicamente para evaluar su similitud. Ambos enfoques suelen tener mayor precisión que la simple comparación de incrustaciones de un solo vector, ya que las incrustaciones multivectoriales contienen información mucho más detallada que las de un solo vector.</p><p>Sin embargo, las incrustaciones multivectoriales no son adecuadas para indexar. A menudo se utilizan en tareas de reclasificación, como se describe para el modelo de <code>jina-colbert-v2</code> en la siguiente sección.</p><h2>Modelos de incrustación de Jina</h2><h3>Jina embeddings v4</h3><p><a href="https://jina.ai/news/jina-embeddings-v4-universal-embeddings-for-multimodal-multilingual-retrieval/"><strong>jina-embeddings-v4</strong></a> es un modelo de incrustación multilingüe y multimodal de 3.8 mil millones (3.8×10⁹) de parámetros que admite imágenes y textos en una variedad de idiomas ampliamente utilizados. Utiliza una arquitectura novedosa para aprovechar el conocimiento visual y el conocimiento del lenguaje para mejorar el rendimiento en ambas tareas, lo que le permite sobresalir en la recuperación de imágenes y especialmente en la recuperación <a href="https://huggingface.co/tasks/visual-document-retrieval">visual de documentos</a>. Esto significa que maneja imágenes como gráficos, diapositivas, mapas, capturas de pantalla, escaneos de páginas y diagramas, tipos comunes de imágenes que a menudo contienen texto importante incrustado y que quedan fuera del alcance de los modelos de visión artificial entrenados con imágenes de escenas del mundo real.</p><p>Hemos optimizado este modelo para varias tareas diferentes a través de <a href="https://huggingface.co/docs/peft/en/package_reference/lora">adaptadores de Low-Rank Adaptation (LoRA)</a> compactos. Esto nos permite entrenar un único modelo para que se especialice en varias tareas, sin comprometer el rendimiento en ninguna de ellas, con un costo adicional mínimo en memoria o procesamiento.</p><p>Las características principales incluyen las siguientes:</p><ul><li><p>Rendimiento de vanguardia en la recuperación visual de documentos, junto con texto multilingüe e imágenes regulares que superan significativamente a modelos mucho más grandes.</p></li><li><p>El soporte para un gran tamaño de contexto de entrada: 32.768 tokens equivale aproximadamente a 80 páginas de texto en inglés a doble espacio, y 20 megapíxeles equivalen a una imagen de 4.500 x 4.500 píxeles.</p></li><li><p>Tamaños de incrustación seleccionados por el usuario, desde un máximo de 2048 dimensiones hasta 128 dimensiones. Descubrimos empíricamente que el rendimiento se degrada significativamente por debajo de ese umbral.</p></li><li><p>Soporte para ambas incrustaciones simples y multivector. En el caso de los textos, la salida multivectorial consiste en una incrustación de 128 dimensiones para cada token de entrada. Para las imágenes, produce una incrustación de 128 dimensiones para cada mosaico de 28x28 píxeles necesario para cubrir la imagen.</p></li><li><p>Optimización para la recuperación asimétrica mediante un par de adaptadores LoRA entrenados específicamente para tal fin.</p></li><li><p>Un adaptador LoRA optimizado para el cálculo de similitud semántica.</p></li><li><p>Soporte especial para lenguajes de programación informática y marcos de trabajo de TI, también a través de un adaptador LoRA.</p></li></ul><p>Desarrollamos <code>jina-embeddings-v4</code> para servir como una herramienta general y multipropósito para una amplia variedad de tareas de búsqueda común, comprensión del lenguaje natural y análisis de IA. Es un modelo relativamente pequeño teniendo en cuenta sus capacidades, pero su despliegue requiere recursos considerables y es más adecuado para su uso a través de una API en la nube o en un entorno de gran volumen.</p><h3>Jina embeddings v3</h3><p><a href="https://jina.ai/news/jina-embeddings-v3-a-frontier-multilingual-embedding-model/"><strong>jina-embeddings-v3</strong></a> es un modelo de incrustación compacto, de alto rendimiento, multilingüe y solo de texto con menos de 600 millones de parámetros. Admite hasta 8192 tokens de entrada de texto y genera incrustaciones de vector único con tamaños elegidos por el usuario, desde un valor predeterminado de 1024 dimensiones hasta 64.</p><p>Capacitamos a <code>jina-embeddings-v3</code> para una variedad de tareas de texto. No solo para la recuperación de información y la similitud semántica, sino también para tareas de clasificación, como análisis de sentimiento y moderación de contenido, así como tareas de agrupar, como agregación de noticias y recomendaciones. Al igual que <code>jina-embeddings-v4</code>, este modelo proporciona adaptadores LoRA especializados para las siguientes categorías de uso:</p><ul><li><p>Recuperación asimétrica</p></li><li><p>Similitud semántica</p></li><li><p>Clasificación</p></li><li><p>Agrupación</p></li></ul><p><code>jina-embeddings-v3</code> es un modelo mucho más pequeño que <code>jina-embeddings-v4</code> con un tamaño de contexto de entrada significativamente reducido, pero su funcionamiento cuesta menos. No obstante, tiene un rendimiento muy competitivo, aunque solo para textos, y es una mejor opción para muchos casos de uso.</p><h3>Inmercaciones del código Jina</h3><p>Los modelos especializados de incrustación de código de Jina, <a href="https://jina.ai/models/jina-code-embeddings-1.5b"><strong>jina-code-embeddings (0.5b y 1.5b)</strong></a>, admiten 15 esquemas y marcos de trabajo de programación, así como textos en inglés relacionados con la informática y la tecnología de la información. Son modelos compactos con quinientos millones (0.5x10⁹) y mil quinientos millones (1.5x10⁹) de parámetros, respectivamente. Ambos modelos admiten tamaños de contexto de entrada de hasta 32.768 tokens y permiten a los usuarios seleccionar los tamaños de su incrustación de salida, desde 896 hasta 64 dimensiones para el modelo más pequeño y 1536 hasta 128 para el modelo más grande.</p><p>Estos modelos admiten la recuperación asimétrica para cinco especializaciones específicas de tareas, mediante el <a href="https://arxiv.org/abs/2101.00190">ajuste de prefijos</a> en lugar de adaptadores LoRA:</p><ul><li><p><strong>Código a código.</strong> Recupera un código similar en todos los lenguajes de programación. Esto se utiliza para la alineación de códigos, deduplicación de códigos y soporte para la traslación y refactorización.</p></li><li><p><strong>Lenguaje natural para programar.</strong> Recupera códigos para hacer coincidir consultas, comentarios, descripciones y documentación en lenguaje natural.</p></li><li><p><strong>Código a lenguaje natural. </strong>Haz coincidir el código con la documentación u otros textos en lenguaje natural.</p></li><li><p><strong>Finalización de código a código.</strong> Sugiere un código relevante para completar o mejorar el código existente.</p></li><li><p><strong>Preguntas y respuestas técnicas.</strong> Identifica las respuestas en lenguaje natural a las preguntas sobre tecnologías de la información, que son ideales para casos de uso de soporte técnico.</p></li></ul><p>Estos modelos ofrecen un rendimiento superior para tareas relacionadas con documentación informática y materiales de programación con un costo computacional relativamente bajo. Son muy adecuados para integrarse en entornos de desarrollo y asistentes de código.</p><h3>Jina ColBERT v2</h3><p><a href="https://jina.ai/models/jina-colbert-v2"><strong>jina-colbert-v2</strong></a> es un modelo de incrustación de texto multivectorial de 560 millones de parámetros. Es multilingüe, entrenado con materiales en 89 idiomas, y soporta tamaños de incrustación variables y recuperación asimétrica.</p><p>Como se ha señalado anteriormente, las incrustaciones multivectoriales no son adecuadas para la indexación, pero resultan muy útiles para aumentar la precisión de los resultados de otras estrategias de búsqueda. Mediante <code>jina-colbert-v2</code><strong>,</strong> puedes calcular incrustaciones multivector de antemano y luego usarlas para reclasificar candidatos de recuperación al momento de la búsqueda. Este enfoque es menos preciso que usar uno de los modelos de reclasificación en la siguiente sección, pero es mucho más eficiente porque simplemente implica comparar incrustaciones multivectoriales almacenadas en lugar de invocar todo el modelo de IA para cada búsqueda y coincidencia posible. Es ideal para casos de uso en los que la latencia y la sobrecarga computacional que supone el uso de modelos de reclasificación son demasiado grandes, o cuando el número de candidatos que comparar es demasiado elevado para los modelos de reclasificación.</p><p>Este modelo genera una secuencia de incrustaciones, una por token de entrada, y los usuarios pueden seleccionar incrustaciones de token de 128, 96 o 64 dimensiones. Las coincidencias de texto de candidatos están limitadas a 8,192 tokens. Las búsquedas se codifican de manera asimétrica, por lo que los usuarios deben especificar si un texto es una búsqueda o una coincidencia candidata y deben limitar las búsquedas a 32 tokens.</p><h3>Jina CLIP v2</h3><p><a href="https://jina.ai/news/jina-clip-v2-multilingual-multimodal-embeddings-for-text-and-images/"><strong>Jina-Clip-V2</strong></a> es un modelo de incrustación multimodal de 900 millones de parámetros, y está capacitado para que los textos e imágenes produzcan incrustaciones muy cercanas si el texto describe el contenido de la imagen. Su uso principal es para recuperar imágenes basadas en consultas de textura, pero también es un modelo de solo texto de alto rendimiento, lo que reduce los costos de usuario porque no necesitas modelos separados para la recuperación de texto a texto y de texto a imagen.</p><p>Este modelo admite un contexto de entrada de texto de 8,192 tokens, y las imágenes se escalan a 512x512 píxeles antes de generar incrustaciones.</p><p>Las arquitecturas de preentrenamiento de lenguaje-imagen contrastivo (CLIP) son fáciles de entrenar y operar y pueden producir modelos muy compactos, pero tienen algunas limitaciones fundamentales. No pueden utilizar los conocimientos adquiridos en un medio para mejorar su rendimiento en otro. No pueden usar un medio para mejorar su rendimiento en otro. Así que, aunque pueda saber que las palabras "perro" y "gato" están más cercanas en significado que a "auto", no necesariamente sabrá que una foto de un perro y una de un gato están más relacionadas que cualquiera de las dos con una foto de un auto.</p><p>También sufren de lo que se llama la <em>brecha de modalidad</em>: es probable que una incrustación de un texto sobre perros esté más cerca de una incrustación de un texto sobre gatos que de una incrustación de una imagen de perros. Debido a esta limitación, te recomendamos utilizar CLIP como modelo de recuperación de texto a imagen o como modelo de solo texto, pero sin mezclar los dos en una sola búsqueda.</p><h2>Modelos de reordenamiento</h2><p>Los modelos de reclasificación toman una o más coincidencias candidatas, junto con una consulta como entrada al modelo, y las comparan directamente, produciendo coincidencias de mucha mayor precisión.</p><p>En principio, podrías usar un reranker directamente para la recuperación de información al comparar cada búsqueda con cada documento almacenado, pero esto sería muy costoso desde el punto de vista computacional y no es práctico para cualquier colección excepto para las más pequeñas. Como resultado, los rerankers tienden a usarse para evaluar listas relativamente cortas de coincidencias de candidatos encontradas por otros medios, como la búsqueda basada en incrustaciones u otros algoritmos de recuperación. Los modelos de reclasificación son ideales para esquemas de búsqueda híbridos y federados, donde realizar una búsqueda puede significar que las consultas se envían a sistemas de búsqueda separadas con conjuntos de datos distintos, cada una devolviendo resultados distintos. Funcionan muy bien al combinar resultados diversos en un único resultado de alta calidad.</p><p>La búsqueda basada en incrustaciones puede ser un gran compromiso, ya que implica reindexar todos tus datos almacenados y cambiar las expectativas del usuario sobre los resultados. Agregar un reranker a un esquema de búsqueda existente puede sumar muchos de los beneficios de la IA sin necesidad de rediseñar toda tu solución de búsqueda.</p><h2>Modelos de reordenación de Jina</h2><h3>Jina Reranker m0</h3><p><a href="https://jina.ai/models/jina-reranker-m0/"><strong>jina-reranker-m0</strong></a> es un reranker multimodal de 2.4 mil millones (2.4x10⁹) de parámetros que admite consultas textuales y coincidencias candidatas que consisten en textos y/o imágenes. Es el modelo líder en recuperación visual de documentos, lo que lo convierte en una solución ideal para almacenar archivos PDF, escaneos de texto, capturas de pantalla y otras imágenes generadas o modificadas por computadora que contengan texto u otra información semiestructurada, así como datos mixtos que consistan en documentos de texto e imágenes.</p><p>Este modelo toma una búsqueda única y una coincidencia candidata y devuelve una puntuación. Cuando la misma consulta se usa con diferentes candidatos, las puntuaciones son comparables y pueden usarse para clasificarlas. Soporta un tamaño total de entrada de hasta 10 240 tokens, incluido el texto de la consulta y el texto o imagen candidata. Cada mosaico de 28x28 píxeles necesario para cubrir una imagen cuenta como un token para calcular el tamaño de entrada.</p><h3>Jina Reranker v3</h3><p><a href="https://jina.ai/models/jina-reranker-v3/"><strong>jina-reranker-v3</strong></a> es un reranker de texto de 600 millones de parámetros con rendimiento de vanguardia para modelos de tamaño comparable. A diferencia de <code>jina-reranker-m0</code>, toma una sola búsqueda y una lista de hasta 64 candidatos coincidentes y devuelve el orden de clasificación. Tiene un contexto de entrada de 131 000 tokens, incluida la consulta y todos los candidatos de texto.</p><h3>Jina Reranker v2</h3><p><a href="https://jina.ai/models/jina-reranker-v2"><strong>jina-reranker-v2-base-multilingual</strong></a> es un reranker muy compacto y de uso general con características adicionales diseñadas para admitir llamadas a funciones y consultas SQL. Con un peso inferior a 300 millones de parámetros, proporciona una reclasificación de texto multilingüe rápido, eficiente y preciso, con soporte adicional para seleccionar tablas SQL y funciones externas que coincidan con consultas de texto, lo que lo hace adecuado para casos de uso de agentes.</p><h2>Pequeños modelos de lenguaje generativo</h2><p>Los modelos de lenguaje generativo son modelos como ChatGPT de OpenAI, Google Gemini y Claude de Anthropic que toman entradas de texto o multimedia y responden con salidas de texto. No existe una línea bien definida que separe los modelos de lenguaje <em>grandes</em> (LLM) de los modelos de lenguaje <em>pequeños</em> (SLM), pero los problemas prácticos de desarrollar, operar y utilizar LLM de primera línea son bien conocidos. Los más conocidos no se distribuyen públicamente, por lo que solo podemos estimar su tamaño, pero se espera que ChatGPT, Gemini y Claude estén en el rango de parámetros de 1 a 3 billones (1–3x10¹²).</p><p>Ejecutar estos modelos, incluso si están disponibles públicamente, está muy lejos del alcance del hardware convencional, que requiere los chips más avanzados dispuestos en grandes matrices paralelas. Puedes acceder a LLMs a través de API pagas, pero esto conlleva costos significativos, tiene una gran latencia y es difícil de alinear con las demandas de protección de datos, la soberanía digital y la repatriación en la nube. Además, los costos relacionados con la capacitación y la personalización de modelos de ese tamaño pueden ser considerables.</p><p>En consecuencia, se han realizado numerosas investigaciones para desarrollar modelos más pequeños que, aunque carecen de todas las capacidades de los LLM más grandes, pueden realizar determinados tipos de tareas con la misma eficacia y a un menor costo. Las empresas generalmente despliegan software para abordar problemas específicos, y el software de IA no es diferente, por lo que las soluciones basadas en SLM se suelen preferir a las de LLM. Por lo general, pueden ejecutarse en hardware básico, son más rápidos y consumen menos energía para ejecutarse, y son mucho más fáciles de personalizar.</p><p>Las ofertas de SLM de Jina están creciendo a medida que nos enfocamos en la mejor manera de llevar la IA a soluciones de búsqueda prácticas.</p><h2>Jina SLM</h2><h3>ReaderLM v2</h3><p><a href="https://jina.ai/models/ReaderLM-v2"><strong>ReaderLM-v2</strong></a> es un modelo de lenguaje generativo que convierte HTML en Markdown o JSON, según los esquemas JSON proporcionados por el usuario y las instrucciones en lenguaje natural.</p><p>El preprocesamiento y la normalización de datos son una parte esencial del desarrollo de buenas soluciones de búsqueda para datos digitales, pero los datos del mundo real, especialmente la información derivada de la web, suelen ser caóticos, y las estrategias de conversión simples a menudo resultan ser muy frágiles. En cambio, <code>ReaderLM-v2</code> ofrece una solución de modelo de IA inteligente que puede entender el caos de un volcado de árbol DOM de una página web e identificar de manera segura elementos útiles.</p><p>Con 1500 millones (1,5 x 10⁹) de parámetros, es tres órdenes de magnitud más compacto que los LLM de última generación, pero su rendimiento es similar al de estos en esta tarea específica.</p><h3>Jina VLM</h3><p><a href="https://jina.ai/models/jina-vlm"><strong>jina-vlm</strong></a> es un modelo de lenguaje generativo con 2400 millones (2,4 x 10⁹) de parámetros que está entrenado para responder preguntas en lenguaje natural sobre imágenes. Tiene un fuerte soporte para el análisis visual de documentos, es decir, responder preguntas sobre escaneos, capturas de pantalla, diapositivas, diagramas y datos similares de imágenes no naturales.</p><p>Por ejemplo:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt124b9932e01dcd40/6a17db7d4202291eca29f4bc/adfa1420d079ca4fd5582eef4349b1265b378e76-950x500.png" alt="" /><p>También es muy bueno para leer texto en imágenes:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1a862a9c9a0e42ce/6a17db7fb1e1133a2979f15d/ea3956e7ad86f8e171841cab2c28c8b3498da1d4-1002x500.png" alt="" /><p>Pero donde <code>jina-vlm</code> realmente se destaca es en entender el contenido de las imágenes informativas y creadas por el hombre:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt761cf621ea32e4ae/6a17db8163baff7730741b26/f68606f9d2d99e2cd616d4ff81db3574dc4e26a5-1020x700.png" alt="" /><p>O:</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4df4d31e574df7e3/6a17db82e3179134e02d56e9/297e85e7e78f296388a02301e1e08fed70827423-1000x500.png" alt="" /><p><code>jina-vlm</code> es adecuado para la generación automática de subtítulos, descripciones de productos, texto alternativo de imágenes y aplicaciones de accesibilidad para personas con discapacidad visual. También crea posibilidades para que los sistemas de generación aumentada por recuperación (RAG) utilicen información visual y para que los agentes de IA procesen imágenes sin ayuda humana.</p>]]></content:encoded>
    <link>https://www.elastic.co/search-labs/blog/jina-models-elasticsearch-guide</link>
    <guid isPermaLink="true">https://www.elastic.co/search-labs/blog/jina-models-elasticsearch-guide</guid>
    <category><![CDATA[Integraciones]]></category>
    <category><![CDATA[Jina AI]]></category>
    <dc:creator><![CDATA[Scott Martens]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta03919124faf767a/6a17db84ec0f89b8fe5a64d8/407b4c862b51ebdfc7f26db4e25950a65caf1673-656x442.png" length="0" type="image/png"/>
    <pubDate>Thu, 01 Jan 2026 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>
  </channel>
</rss>