Los chatbots son una de las mejores maneras para ofrecer una manera fácil de acceder al gran número de datos abiertos que los gobiernos e instituciones publican en internet. Es lo que llamamos “open data chatbots“.

Hoy os enseñamos como de fácil puede llegar a ser alguno de estos bots cuando los datos abiertos se ofrecen vía una API REST. Más concretamente, veremos como crear un chatbot (hecho con Xatkit) que consulta OpenWeather para darte el tiempo de cualquier ciudad del mundo.

Todo en menos de 100 líneas de código Java. El código completo lo tenéis en nuestro repositorio de ejemplos pero os explicamos aquí todos los detalles importantes.

Defininiedo la intención Dame el tiemo en

El primer paso es definir el intent que permite al usuario pedir la previsión del tiempo para una ciudad dada. En Xatkit lo hacemos así:

        val howIsTheWeather = intent("HowIsTheWeather")
                .trainingSentence("How is the weather today in CITY?")
                .trainingSentence("What is the forecast for today in CITY?")
                .parameter("cityName").fromFragment("CITY").entity(city());

Fíjate como recogemos información de la ciudad sobre la que se pide la previsión como parte del análisis de la intención. Además, el valor del parámetro ciudad no puede ser cualquier cadena de carácteres sinó que esta restringido a uno de los valores de la entity city. La mayoría de los motores de NLP (natural language processing) con los que trabaja Xatkit ofrecen tipos de datos predeterminados más allá de los primitivos (entero, string, real,…) como por ejemplo ciudad, país, teléfono,… Esto permite mejorar la calidad del matching del texto que entra el usuario. Aseguraros de configurar el bot para usar DialogFlow o Nlp.js (y recordad que cambiar de motor de NLP no implica ningún cambio en el código del bot, sólo de su fichero de configuración).

Respondiendo a la petición del tiempo

Una vez el chatbot reconoce que el usuario le pide la meteorología de una ciudad X, el bot tiene que llamar a la REST API de OpenWeather pasando X como parámetro.

Empezamos con un estado inicial donde esperamos a que el usuario nos pida el tiempo,

      awaitingInput
                .next()
                .when(intentIs(howIsTheWeather)).moveTo(printWeather);

Una vez hacemos match, nos movemos al estado printWeather donde se llama a la API y se procesa la respuesta para imprimir el mensaje de la previsión de vuelta al usuario.

Recuerda que Xatkit viene con una plataforma REST que simplifica la interacción con APIs REST. En el código, getJsonRequest te ayuda a construir la URL exacta para realizar tu petición y recoge la respuesta en un objeto response que puedes usar para chequear el éxito de la petición y extraer fácilmente la información que te haga falta para construir la respuesta.

Una vez impresa la respuesta, el bot vuelve al estado inicial esperando la nueva petición.

     printWeather
                .body(context -> {
                    String cityName = (String) context.getIntent().getValue("cityName");
                    Map<String, Object> queryParameters = new HashMap<>();
                    queryParameters.put("q", cityName);
                    ApiResponse<JsonElement> response = restPlatform.getJsonRequest(context, "http://api" +
                                    ".openweathermap.org/data/2.5/weather", queryParameters, Collections.emptyMap(),
                            Collections.emptyMap());
                    if (response.getStatus() == 200) {
                        long temp = Math.round(response.getBody().getAsJsonObject().get("main").getAsJsonObject().get(
                                "temp").getAsDouble());
                        long tempMin =
                                Math.round(response.getBody().getAsJsonObject().get("main").getAsJsonObject().get(
                                        "temp_min").getAsDouble());
                        long tempMax =
                                Math.round(response.getBody().getAsJsonObject().get("main").getAsJsonObject().get(
                                        "temp_max").getAsDouble());
                        String weather =
                                response.getBody().getAsJsonObject().get("weather").getAsJsonArray().get(0).getAsJsonObject().get("description").getAsString();
                        String weatherIcon =
                                "http://openweathermap.org/img/wn/" + response.getBody().getAsJsonObject().get(
                                        "weather").getAsJsonArray().get(0).getAsJsonObject().get("icon").getAsString() + ".png";
                        reactPlatform.reply(context, MessageFormat.format("The current weather is {0} &deg;C with " +
                                        "{1} ![{1}]({2}) with a high of {3} &deg;C and a low of {4} &deg;C", temp,
                                weather,
                                weatherIcon, tempMax, tempMin));
                    } else if (response.getStatus() == 400) {
                        reactPlatform.reply(context, "Oops, I couldn't find this city");
                    } else {
                        reactPlatform.reply(context, "Sorry, an error " +  response.getStatus() + " " + response.getStatusText() + " occurred when accessing the openweathermap service");
                    }
 
                })
                .next()
                .moveTo(awaitingInput);

El chatbot meteorólogo

Puedes ver el bot en acción aquí.

weather bot in action

Fíjate que el bot es capaz de entender la pregunta aunque no sea exactamente igual que las frases de entrenamiento (¡la magia de las redes neuronales!). Evidentemente, éste es un ejemplo minimalista, tienes todo el poder de nuestra Fluent API para ampliarlo como quieras.

Imagen de cabecera por Wim van ‘t Einde en Unsplash