Cómo usar Elasticsearch para enviar indicaciones a ChatGPT con lenguaje natural

blog-thumb-elasticsearch-gears-light-blue.png

Hoy en día todo el mundo está hablando de ChatGPT. Una de las características interesantes de este gran modelo de lenguaje (Large Language Model, LLM) es la capacidad de generar código. Lo usamos para generar búsquedas del idioma específico del dominio (Domain Specific Language, DSL) de Elasticsearch. El objetivo es buscar en Elasticsearch® con frases como "Dame los primeros 10 documentos de 2017 del índice bursátil". Este experimento demostró que es posible, con algunas limitaciones. En esta publicación, describimos este experimento y la biblioteca de open sourceque publicamos para este caso de uso.

¿Puede ChatGPT generar el DSL de Elasticsearch?

Comenzamos el experimento con algunas pruebas centradas en la capacidad de ChatGPT para generar búsquedas del DSL de Elasticsearch. Para este alcance, debes proporcionar algún contexto a ChatGPT sobre la estructura de los datos que deseas buscar.

En Elasticsearch, los datos se almacenan en un índice, que es similar a una "tabla" en una base de datos relacional. Tiene un mapeo que define múltiples campos y sus tipos. Esto significa que debemos proporcionar la información de mapeo del índice que queremos buscar. Al hacerlo, ChatGPT tiene el contexto necesario para traducir la búsqueda al DSL de Elasticsearch.

Elasticsearch ofrece una API de obtención de mapeo para recuperar el mapeo de un índice. En nuestro experimento, usamos un conjunto de datos de índices bursátiles disponible aquí. Este conjunto de datos contiene cinco años de precios de acciones de 500 empresas de Fortune, que abarcan desde febrero de 2013 hasta febrero de 2018.

Aquí informamos las primeras cinco líneas del archivo CSV que contiene el conjunto de datos:

date,open,high,low,close,volume,name
2013-02-08,15.07,15.12,14.63,14.75,8407500,AAL
2013-02-11,14.89,15.01,14.26,14.46,8882000,AAL
2013-02-12,14.45,14.51,14.1,14.27,8126000,AAL
2013-02-13,14.3,14.94,14.25,14.66,10259500,AAL

Cada línea contiene la fecha de la acción, el valor de la apertura del día, los valores alto y bajo, el valor de cierre, el volumen de las acciones intercambiadas y, finalmente, el nombre de la acción: por ejemplo, American Airlines Group Inc. (AAL).

El mapeo asociado al índice bursátil es el siguiente:

{
  "stocks": {
    "mappings": {
      "properties": {
        "close": {"type":"float"},
        "date" : {"type":"date"},
        "high" : {"type":"float"},
        "low"  : {"type":"float"},
        "name" : {
          "type": "text",
          "fields": {
            "keyword":{"type":"keyword", "ignore_above":256}
          }
        },
        "open"  : {"type":"float"},
        "volume": {"type":"long"}
      }
    }
  }
}

Podemos usar la APIGET /stocks/_mappingpara recuperar el mapeo de Elasticsearch.

[Artículo relacionado: ChatGPT and Elasticsearch: OpenAI meets private data (ChatGPT y Elasticsearch: OpenAI en combinación con datos privados)]

Creemos una indicación para descubrirlo.

Para traducir una búsqueda expresada en lenguaje humano al DSL de Elasticsearch, debemos encontrar la indicación correcta para enviarla a ChatGPT. Esta es la parte más difícil del proceso: programar ChatGPT usando el formato de pregunta correcto (en otras palabras, la indicación correcta).

Después de algunas iteraciones, terminamos con la siguiente indicación que parece funcionar bastante bien:

Given the mapping delimited by triple backticks ```{mapping}``` translate the text delimited by triple quotes in a valid Elasticsearch DSL query """{query}""". Give me only the json code part of the answer. Compress the json output removing spaces.

Los valores {mapping}y {query} en la indicación son dos marcadores de posición que se reemplazarán con el texto json de mapeo (por ejemplo, devuelta por GET /stocks/_mapping en el ejemplo anterior) y la búsqueda expresada en lenguaje humano (por ejemplo: Devolver los primeros 10 documentos de 2017).

Por supuesto, ChatGPT es limitado y, en algunos casos, no podrá responder una pregunta. Nos hemos dado cuenta de que, la mayoría de las veces, esto sucede porque la frase usada en la indicación es demasiado general o ambigua. Para resolver esta situación, necesitamos mejorar la indicación con más detalles. Este proceso se denomina iteración y requiere varios pasos para definir la frase adecuada que se usará.

Si deseas probar cómo ChatGPT puede traducir una frase de búsqueda en una consulta del DSL de Elasticsearch (o incluso SQL), puedes usar dsltranslate.com.

La combinación de todo

Usando la API de ChatGPT ofrecida por OpenAI y la API de Elasticsearch para mapeo y búsqueda, lo reunimos todo en una biblioteca experimental para PHP.

Esta biblioteca expone una función de búsqueda() con la siguiente API:

search(string $index, string $prompt, bool $cache = true)

Donde $index es el nombre del índice que se usará,$prompt es la búsqueda expresada en lenguaje humano y $bool es un parámetro opcional para usar un caché (habilitado de forma predeterminada).

El proceso de esta función se describe en el siguiente diagrama:

elasticsearch openai diagram

Las entradas son el índice y el indicador (a la izquierda). El índice se usa para recuperar el mapeo de Elasticsearch (usando la API de obtención de mapeo). El resultado es un mapeo en JSON que se usa para construir el texto de búsqueda para enviar a ChatGPT usando el siguiente código API. Usamos el modelo gpt-3.5-turbo de OpenAI que es capaz de traducir en código.

El resultado de ChatGPT contiene una búsqueda del DSL de Elasticsearch que usamos para hacer búsquedas de Elasticsearch. Luego, el resultado se devuelve al usuario. Para hacer búsquedas de Elasticsearch, usamos el cliente oficial elastic/elasticsearch-php.

Para optimizar el tiempo de respuesta y reducir el costo de uso de la API de ChatGPT, usamos un sencillo sistema de almacenamiento en caché basado en archivos. Usamos un caché para:

  • Almacenar el JSON del mapeo devuelto por Elasticsearch: Almacenamos este JSON en un archivo que lleva el nombre del índice. Esto nos permite recuperar la información de mapeo sin realizar llamadas adicionales a Elasticsearch.
  • Almacenar el DSL de Elasticsearch generado por ChatGPT: Para almacenar en caché el DSL de Elasticsearch generado, nombramos el archivo de caché usando el hash (MD5) de la indicación usada. Este enfoque nos permite reutilizar el DSL de Elasticsearch generado previamente para la misma búsqueda, lo que elimina la necesidad de volver a llamar a la API de ChatGPT.

También agregamos la posibilidad de recuperar el DSL de Elasticsearch mediante programación usando la función getLastQuery().

Ejecución del experimento con datos financieros

Usamos Elastic Cloud para almacenar el valor de las acciones informado aquí. En particular, usamos un sencillo script masivo para leer el archivo de acciones en CSV y enviarlo a Elasticsearch usando la API de bulk.

Para obtener más detalles sobre cómo configurar el Elastic Cloud y recuperar la clave API, lee la documentación.

Una vez que almacenamos el índice bursátil, usamos un sencillo script PHP para probar algunas búsquedas expresadas en inglés. El script que usamos es examples/test.php.

Para ejecutar este examples/test.php script, necesitamos configurar tres variables de entorno:

  • OPENAI_API_KEY: la clave API de OpenAI
  • ELASTIC_CLOUD_ENDPOINT: la URL de la instancia de Elasticsearch
  • ELASTIC_CLOUD_API_KEY: la clave API de Elastic Cloud

Usando el mapeo de acciones, probamos las siguientes búsquedas registrando todas las respuestas del DSL de Elasticsearch:

Query: Return the first 10 documents of 2017

Result:
{
  "query":{
    "range":{
      "date":{
        "gte":"2017-01-01",
        "lte":"2017-12-31"
      }
    }
  },
  "size":10
}
Query: Return the first 30 names of all the different stock names

Result:
{
  "size":0,
  "aggs":{
    "unique_names":{
      "terms":{
        "field":"name.keyword",
        "size":30
      }
    }
  }
}
Query: Return the max value of the field "high" for each stock in 2015

Result: 
{
  "aggs":{
    "stocks":{
      "terms":{"field":"name.keyword","size":100},
      "aggs":{
        "max_high":{"max":{"field":"high"}}
      }
    }
  },
  "query":{
    "bool":{
      "must":[{
        "range":{
          "date":{
            "gte":"2015-01-01",
            "lt":"2016-01-01"
          }
        }
      }]
    }
  }
}
Query: Return the average value of the field "high" for each stock in 2015

Result:
{
  "size":0,
  "aggs":{
    "stocks":{
      "terms":{
        "field":"name.keyword",
        "size":10000
      },
      "aggs":{
        "2015":{
          "filter":{
            "range":{
              "date":{
                "gte":"2015-01-01",
                "lt":"2016-01-01"
              }
            }
          },
          "aggs":{
            "avg_high":{
              "avg":{
                "field":"high"
              }
            }
          }
        }
      }
    }
  }
}
Query: Return the max value of the field "high" for all the documents with name MON in 2014

Result:

{
  "query":{
    "bool":{
      "must":[
        {
          "match":{
            "name.keyword":"MON"
          }
        },
        {
          "range":{
            "date":{
              "gte":"2014-01-01",
              "lt":"2015-01-01"
            }
          }
        }
      ]
    }
  },
  "size":0,
  "aggs":{
    "max_high":{
      "max":{
        "field":"high"
      }
    }
  }
}
Query: Return the documents that have the difference between close and open fields > 20

Response:
{
  "query":{
    "bool":{
      "must":[
        {
          "script":{
            "script":{
              "lang":"painless",
              "source":"doc['close'].value - doc['open'].value > 20"
            }
          }
        }
      ]
    }
  }
}

Como puedes ver, los resultados son bastante buenos. ¡El último sobre la diferencia entre campos cerrados y abiertos fue bastante impresionante!

Todas las solicitudes se han traducido en una búsqueda del DSL de Elasticsearch válida y correcta según la pregunta expresada en lenguaje natural.

¡Usa el idioma que hablas!

Una característica muy interesante de ChatGPT es la posibilidad de especificar preguntas en diferentes idiomas.

Eso significa que puedes usar esta biblioteca y especificar la búsqueda en diferentes idiomas naturales, como italiano, español, francés, alemán, etc.

Aquí hay un ejemplo:

# English
$result = $chatGPT->search('stocks', 'Return the first 10 documents of 2017');
# Italian
$result = $chatGPT->search('stocks', 'Restituisci i primi 10 documenti del 2017');
# Spanish
$result = $chatGPT->search('stocks', 'Devuelve los 10 primeros documentos de 2017');
# French
$result = $chatGPT->search('stocks', 'Retourner les 10 premiers documents de 2017');
# German
$result = $chatGPT->search('stocks', 'Senden Sie die ersten 10 Dokumente des Jahres 2017 zurück');

Todas las búsquedas anteriores tienen los mismos resultados produciendo la siguiente búsqueda de Elasticsearch (más o menos):

{"size":10,"query":{"range":{"date":{"gte":"2017-01-01","lt":"2018-01-01"}}}}

Importante: ChatGPT es un LLM que ha sido optimizado para inglés, lo que significa que los mejores resultados se obtienen al realizar consultas ingresadas en inglés.

Limitaciones de los LLM

Desafortunadamente, ChatGPT y los LLM en general no son capaces de verificar la exactitud de la respuesta desde un punto de vista semántico. Dan respuestas que parecen correctas desde un punto de vista estadístico. Esto significa que no podemos comprobar si la búsqueda del DSL de Elasticsearch generada por ChatGPT es la traducción correcta de la búsqueda en lenguaje natural.

Por supuesto, esta es una gran limitación en este momento. En algunos otros casos de uso, como las operaciones matemáticas, podemos resolver el problema de exactitud usando un plugin externo, como el Wolfram Plugin de ChatGPT. En este caso, el resultado de ChatGPT usa el motor Wolfram que verifica la exactitud de la respuesta, usando un modelo simbólico matemático.

Además de la limitación de exactitud, que implica que siempre debemos verificar las respuestas de ChatGPT, también existen limitaciones en la capacidad de traducir una frase humana en una búsqueda del DSL de Elasticsearch.

Por ejemplo, usando el conjunto de datos de acciones anterior si preguntamos algo como lo siguiente:

Query: Return the first 10 documents of 2017 and 2015

Result:
{
  "from":0,
  "size":10,
  "query":{
    "bool":{
      "must":[
        {
          "range":{
            "date":{
              "gte":"2015-01-01",
              "lte":"2015-12-31",
              "format":"yyyy"
            }
          }
        },
        {
          "range":{
            "date":{
              "gte":"2017-01-01",
              "lte":"2017-12-31",
              "format":"yyyy"
            }
          }
        }
      ]
    }
  }
}

La búsqueda del DSL generada por ChatGPT no es válida produciendo este error de Elasticsearch:

Error al parsear el campo de fecha [2015-01-01] con formato [aaaa].

Si reformulamos la frase usando información más específica, eliminando la aparente ambigüedad sobre el formato de la fecha, podemos obtener la respuesta correcta, de la siguiente manera:

Query: Return the first 10 documents with year 2017 and 2015 in "date" field

Result:
{
  "size":10,
  "query":{
    "bool":{
      "should":[
        {
          "term":{
            "date":"2017"
          }
        },
        {
          "term":{
            "date":"2015"
          }
        }
      ]
    }
  }
}

Básicamente, la frase debe expresarse usando una descripción de cómo debería ser el DSL de Elasticsearch en lugar de una frase humana real.

Resumen

En esta publicación, presentamos un caso de uso experimental de ChatGPT para traducir frases de búsqueda en lenguaje natural a búsquedas del DSL de Elasticsearch. Desarrollamos una sencilla biblioteca en PHP para usar la API de OpenAI para traducir la búsqueda internamente, proporcionando también un sistema de almacenamiento en caché.

Los resultados del experimento son prometedores, incluso con la limitación de la exactitud de la respuesta. Dicho esto, definitivamente investigaremos más a fondo la posibilidad de hacer búsquedas de Elasticsearch en lenguaje natural usando ChatGPT, así como otros modelos LLM que cada vez son más populares.

Obtén más información sobre las posibilidades de Elasticsearch y AI.



En este blog, es posible que hayamos usado herramientas de AI generativa de terceros, que son propiedad de sus respectivos propietarios y operadas por estos. Elastic no tiene ningún control sobre las herramientas de terceros, y no somos responsables de su contenido, funcionamiento o uso, ni de ninguna pérdida o daño que pueda resultar del uso de dichas herramientas. Ten cautela al usar herramientas de AI con información personal o confidencial. Cualquier dato que envíes puede ser utilizado para el entrenamiento de AI u otros fines. No hay garantías de que la información que proporciones se mantenga segura o confidencial. Deberías familiarizarte con las prácticas de privacidad y los términos de uso de cualquier herramienta de AI generativa previo a su uso.  

Elastic, Elasticsearch y las marcas asociadas son marcas comerciales, logotipos o marcas comerciales registradas de Elasticsearch N.V. en los Estados Unidos y otros países. Todos los demás nombres de empresas y productos son marcas comerciales, logotipos o marcas comerciales registradas de sus respectivos propietarios.