Ir para o conteúdo

Entreposto e sessão autenticada

Qual a necessidade de um entreposto?

O Serprobots é uma plataforma genérica para o gerenciamento de chatbots e pode não possuir determinadas características necessárias para o seu projeto. Isto ocorre, pois, conforme informamos, o Serprobots foi criado para atender ao maior número possível de projetos e não faria sentido incluir funcionalidades específicas e que atenderiam a apenas um projeto.

Então, como posso resolver isso?

Caso exista algum requisito muito específico ao seu projeto como, por exemplo, realizar um registro de auditoria detalhado, relacionando dados do Serprobots com dados específicos do seu sistema, sugerimos a criação de um serviço intermediário, que chamaremos aqui de Entreposto. Este entreposto servirá como uma ponte entre o Serprobots e o seu sistema. Este intermediário interceptará todas as mensagens enviadas pela UI padrão do Serprobots, fará as tarefas necessárias e específicas ao seu sistema e, por fim, encaminhará a mensagem interceptada para o Gateway do Serprobots.

Funcionamento da UI

A UI padrão do Serprobots tem um comportamento padronizado. Assim que o usuário do Chatbot abre a janela de conversação, a UI inicia uma conversação e, portanto, precisa gerar um identificador único para esta conversação. A geração deste ID não é responsabilidade da UI, assim, a UI deve requisitar que um serviço externo realize esta tarefa.

Qual serviço fará isso, então? Por padrão, a UI encaminha esta solicitação para o Gateway do Serprobots. Este Gateway gera um ID no formato UUID e retorna para que a UI o guarde e possa enviá-lo nas próximas requisições. Uma vez que a UI tem o ID da conversação em mãos, ela está preparada para que o usuário envie suas mensagens.

Quando isto ocorre, a UI encaminha não apenas a mensagem digitada pelo usuário, como também o identificador da conversação. Mas envia para onde? Por padrão, será também para o Gateway do Serprobots.

Modificando a URL do Serviço

Conforme discutido na seção anterior, a UI precisa repassar as requisições para um determinado serviço e, por padrão, este serviço é o Gateway do Serprobots. Entretanto, é possível informar um outro serviço. Para informar este serviço, acesse o Serprobots e, no passo de Interface, altere o valor do atributo serviceURL para o endereço de um entreposto criado para o seu projeto.

A UI passará a chamar este novo endereço tanto para criar o ID da conversação como para enviar as perguntas dos usuários. Para entender melhor e de forma prática, considere que o valor do atributo serviceURL foi definido para http://meusistema.gov.br/api.

Neste caso, toda vez que a UI necessitar criar um ID para uma determinada conversação, ela fará uma requisição POST para o endereço http://meusistema.gov.br/api/conversation/mnemonico, onde mnemonico é o mnemônico do seu Chatbot dentro do Serprobots.

Já quando a UI receber uma pergunta do usuário, ele realizará uma requisição POST para o endereço http://meusistema.gov.br/api/answer/mnemonico

Modificando a URL do Serviço

Agora que você já alterou o serviceURL, é necessário criar um entreposto. Este entreposto deve ser uma API publicada externamente, com acesso para a Internet. Não importa qual tecnologia será adotada, desde que o entreposto esteja disponibilizado publicamente e seguindo os padrões de uma API Rest. Além disto, ela deve possuir dois endpoints: /conversation e /answer.

@Path("/conversation")
class ConversationController {}

O trecho de código acima exemplifica um Controller criado em Kotlin/Java usando a JAX-RS. Este Controller será responsável por tratar todas as chamadas para a criação de um ID para a conversação. Note o uso da anotação @Path com o parâmetro "/conversation", que seu projeto deve seguir também, conforme discutido na seção anterior a esta.

Agora, é necessário criar um endpoint, neste Controller, que responda às requisições da UI para criar um ID para a conversação. Alertamos que esta requisição deve ser redirecionada para o Gateway do Serprobots, que será responsável pela geração de um ID no formato UUID. Assim que o Gateway responder, repasse este identificador para a UI.

O trecho de código abaixo exemplifica um método criado no ConversationController acima. Note que usamos a anotação @POST para informar que este endpoint responde apenas a requisições do tipo POST. Logo em seguida, usamos a anotação @Produces com o parâmetro MediaType.TEXT_PLAIN. Isto também é importante, pois a UI espera que a resposta seja um texto puro, contendo apenas o identificador gerado.

@POST
@Produces(MediaType.TEXT_PLAIN)
@Path(value = "/{mnemonico}")
fun createConversation(@PathParam("mnemonico") mnemonico: String): Response {
    return try {

        // Chamada ao Gateway do Serpro, que retornará o ID.
        val conversationId = gatewaySerprobots.createConversation(mnemonico)

        // Realizar aqui as operações específicas ao seu sistema.

        // Retornar o ID criado.
        Response.ok(conversationId).build()
    } catch (e: Exception) {
        Response
            .status(Response.Status.INTERNAL_SERVER_ERROR.statusCode)
            .build()
    }
}

Seguindo, usamos a anotação @Path com o parâmetro /{mnemonico}, que conterá o mnemônico do Chatbot e que é enviado pela UI. Por último, precisamos de um endpoint que responda às perguntas dos usuários.

Respondendo Perguntas do Chatbot

Para que seu entreposto possa responder as perguntas dos usuários, será necessário criar um novo Controller, conforme o trecho de código abaixo. Observe que esta classe está anotada com a anotação @Path, que tem como parâmetro o valor /answer. Lembre-se, a UI chamará exatamente este endpoint, por isto é importante que este controller responda neste path.

@Path("/answer")
class AnswerController {}

Uma vez que a classe do Controller está criada, podemos criar um método que será responsável pelo tratamento das mensagens oriundas da UI. O trecho de código abaixo exemplifica como deve ser a assinatura deste método. Observe que usamos quatro anotações. A primeira é a anotação @POST, que define que este endpoint responde apenas a requisições POST.

Em seguida, informamos que o endpoint aguarda que o corpo da mensagem seja um JSON através da anotação @Consumes(MediaType.APPLICATION_JSON). Também informamos que o método responde à requisição com um JSON, através da anotação @Produces(MediaType.APPLICATION_JSON). Por último, igual a como fizemos com o método que cria o ID da conversação, temos a anotação @Path com o valor /{mnemonico}, que conterá o mnemônico do Chatbot, que é enviado pela UI.

Note que também temos três parâmetros neste método, o primeiro é anotado com a anotação @Context e refere-se ao HttpRequest que originou esta requisição. Em seguida, temos o parâmetro mnemonico e, por último, a mensagem que será recebida.

@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Path(value = "/{mnemonico}")
fun answer(
    @Context request: HttpRequest, 
    @PathParam("mnemonico") mnemonico: String, 
    message: Message): Response {}
)

A classe Message, recebida como parâmetro no método acima, deve conter três atributos, conforme ilustrado no trecho de código abaixo. O atributo conversationId contém o ID da conversação, enquanto o atributo text contém o texto enviado pelo usuário do Chatbot.

data class Message(
    var conversationId: UUID? = null,
    var text: String? = null,
    var parameters: Map<String, Any?>? = emptyMap()
)

Mais uma vez, lembramos que esta mensagem deve ser repassada para o Gateway do Serprobots para que se obtenha uma resposta que deve ser repassada para a UI.

Na prática

O endereço do Gateway do Serpro que responde pelo seu Chatbot segue o padrão https://serprobots.estaleiro.serpro.gov.br/web/(mnemonico-ambiente)/api/(acao)/, onde temos duas partes variáveis: mnemonico-ambiente e acao. O mnemonico deve ser substituído pelo mnemônico do seu Chatbot dentro do Serprobots. O ambiente deve ser substituído pela letra inicial do ambiente em que o Chatbot foi publicado no Serprobots.

Por exemplo, caso seu Chatbot esteja publicado no ambiente de Desenvolvimento, então deve ser d. Já no caso de uma publicação em Produção, você deve subtituir por p. Por último, temos a ação a ser executada. Lembre-se que a UI solicita apenas a criação de um ID e a resposta para uma mensagem, então, as duas únicas opções são answer e conversation.

Vamos ver um exemplo real? Imagine que o mnemônico do seu Chatbot é meu-chatbot. Caso você o publique no ambiente de Desenvolvimento, então, você terá os dois endereços abaixo:

  1. https://serprobots.estaleiro.serpro.gov.br/web/meu-chatbot-d/api/conversation/
  2. https://serprobots.estaleiro.serpro.gov.br/web/meu-chatbot-d/api/answer/

Recebendo Parâmetros da UI O entreposto tem acesso aos parâmetros informados através da QueryString na UI do chatbot. Por exemplo, caso seu chatbot esteja publicado em https://serprobots.estaleiro.serpro.gov.br/meuchatbot, você pode anexar a QueryString ?nome=João, ficando com https://serprobots.estaleiro.serpro.gov.br/meuchatbot?nome=João. Neste caso, o parâmetro nome estará disponível para o entreposto através do atributo parameters da classe Message, que é um Map.

API de um chatbot no Serprobots

Endpoint de criação da conversa (conversation) É o endpoint que cria a conversa, gerando um identificador que será usado nas trocas de mensagens daquela sessão.

POST https://serprobots.estaleiro.serpro.gov.br/web/<IDENTIFICADOR CHATBOT>/api/conversation/

Exemplo de chamada:

curl -X POST 'https://serprobots.estaleiro.serpro.gov.br/web/bot-supai-d/api/conversation/'

Retorno será um texto informando o UUID da conversa criada (conversationId). Exemplo:

8d19ef24-7433-4262-b9a8-170780723ac7

Endpoint de conversação (message) É o endpoint que manda uma mensagem para o chatbot e recebe de volta as respostas que o chatbot gerou para aquela pergunta.

POST https://serprobots.estaleiro.serpro.gov.br/web/<IDENTIFICADOR CHATBOT>/api/answer/

Exemplo de chamada:

curl -X POST 'https://serprobots.estaleiro.serpro.gov.br/web/bot-supai-d/api/answer/'\
    -H 'content-type: application/json'\
    --data-raw '{"parameters":{},"conversationId":"de7331b7-9076-4d8f-b473-0e4ca1095b11","text":"olá"}'

O payload enviado é um JSON informando o valor do 'conversationId' e o texto desejado '(text)':

{
    "parameters": {},
    "conversationId": "de7331b7-9076-4d8f-b473-0e4ca1095b11",
    "text": "olá"
}

O retorno será um JSON com com um array de respostas (campo contents), já que a resposta pode se tratar de várias partes, ou balões:

{
    "contents": [
        {
            "text": "Olha 👀 que coisa mais linda mais cheia de graça ♫ ♩",
            "type": "TEXT"
        }
    ],
    "conversationId": "de7331b7-9076-4d8f-b473-0e4ca1095b11",
    "empty": false,
    "question": "olá",
    "ui": {
    }
}

Uma resposta no array contents pode ser do tipo TEXT

{
    "type": "TEXT",
    "text": "Infelizmente não sei responder..."    
}

Há ainda respostas do tipo IMAGE, e nesse caso vai conter a URL da imagem:

{
    "type": "IMAGE",
    "url": "http://url.da/figura.png"    
}

E também existem respostas do tipo OPTIONAL, que contém um título (title) seguido das opções (array options) que o usuário poderá escolher. Cada opção poderá ter um 'label' (valor visível para ser clicado) e um value (valor de fato enviado ao clicar na opção). Já o campo display vai sugerir uma forma de exibição dessas opções, podendo ser button, selection ou dropdown.

{
    "type": "OPTIONS"
    "title": "Você está bem?"
    "display": "button"
    "options": [
        {
            "label": "Sim",
            "showValue": true,
            "value": "yes"
        },
        {
            "label": "Não",
            "showValue": true,
            "value": "no"
        }        
    ]    
}