Middleware

APLICA-SE A: SDK v4

O middleware é simplesmente uma classe que fica entre o adaptador e sua lógica de bot, adicionada à coleção de middlewares do seu adaptador durante a inicialização. O SDK permite escrever seu próprio middleware ou adicionar o middleware criado por outras pessoas. Toda atividade que entra ou sai do seu bot flui pelo middleware.

O adaptador processa e direciona as atividades de entrada por meio do pipeline de middleware do bot para a lógica do bot e, em seguida, recua novamente. O adaptador processa e direciona atividades de entrada através do pipeline de middleware de bot para a lógica do seu bot e, em seguida, recua novamente.

Antes de entrar no middleware, é importante entender os bots em geral e como eles processam atividades.

Usos para middleware

A pergunta geralmente surge: "Quando devo implementar ações como middleware versus usar minha lógica de bot normal?" O Middleware oferece oportunidades extras para interagir com o fluxo de conversa dos usuários antes e depois de cada turno da conversa ser processada. O middleware também permite armazenar e recuperar as informações relacionadas à lógica de processamento adicional de conversas e chamadas quando necessário. Abaixo estão alguns cenários comuns que mostram onde o middleware pode ser útil.

Olhando ou agindo em todas as atividades

Existem muitas situações que exigem que seu bot faça algo em cada atividade, ou para cada atividade de um certo tipo. Por exemplo, talvez você queira registrar todas as atividades de mensagem recebidas pelo bot ou fornecer uma resposta de fallback se o bot não tiver gerado uma resposta dessa vez. O middleware é um ótimo lugar para esses processos, com sua capacidade de agir antes e depois que o restante da lógica do bot for executado.

Modificando ou aprimorando o contexto de turno

Certas conversas podem ser muito mais proveitosas se o bot tiver mais informações do que as fornecidas na atividade. O middleware neste caso poderia examinar as informações de estado da conversa até o momento, consultar uma fonte de dados externa e anexá-las ao objeto de contexto de turno antes de passar a execução para a lógica do bot.

O SDK define o middleware de registros de log que pode registrar as atividades de entrada e saída, mas você também pode definir seu próprio middleware.

O pipeline de middleware de bot

Para cada atividade, o adaptador chama o middleware na ordem na qual você o adicionou. O adaptador passa o objeto de contexto para o turno e uma próxima delegado e o middleware chama o delegado para transmitir o controle para o próximo middleware no pipeline. Middleware também tem a oportunidade de fazer coisas após o próxima delegado retorna antes de concluir o método. Você pode pensar nisso como cada objeto de middleware tem a primeira e última chance de agir em relação aos objetos de middleware que o seguem no pipeline.

Por exemplo:

  • O manipulador de turno do primeiro objeto middleware executa o código antes de chamar o próximo.
    • O manipulador de turno do segundo objeto middleware executa o código antes de chamar em seguida.
      • O manipulador de turnos do bot é executado e retornado.
    • O manipulador de turno do segundo objeto middleware executa qualquer código restante antes de retornar.
  • O manipulador de turno do primeiro objeto middleware executa qualquer código restante antes de retornar.

Se o middleware não chamar o próximo delegado, o adaptador não chamará nenhum dos manipuladores de middleware ou bot subsequentes e os curtos-circuitos do pipeline.

Depois que o pipeline de middleware do bot for concluído, o turno terminará e o contexto do turn ficará fora do escopo.

O middleware ou o bot podem gerar respostas e registrar manipuladores de eventos de resposta, mas lembre-se de que as respostas são tratadas em processos separados.

Ordem de middleware

Como a ordem na qual o middleware é adicionado determina a ordem na qual o middleware processa uma atividade, é importante decidir a sequência que o middleware deve ser adicionado.

Observação

Isto serve para dar a você um padrão comum que funciona para a maioria dos bots, mas lembre-se de considerar como cada parte do middleware irá interagir com os outros para a sua situação.

Middleware que cuida das tarefas de nível mais baixo que a cada bot deve ser adicionado primeiro ao pipeline do middleware. Exemplos incluem o registro em log, tratamento de exceções e a tradução. Solicite isso dependendo de suas necessidades, como se você deseja que a mensagem de entrada seja traduzida primeiro, antes que as mensagens sejam armazenadas ou se o armazenamento de mensagens deve ocorrer primeiro, o que pode significar que as mensagens armazenadas não serão traduzidas.

O middleware específico do bot deve ser adicionado ao seu pipeline de middleware por último, middleware que você implementa para fazer algum processamento em cada mensagem enviada ao bot. Se seu middleware usa informações de estado ou outras informações definidas no contexto do bot, inclua-as no pipeline do middleware após o middleware que modifica o estado ou o contexto.

Curto-circuito

Uma ideia importante em torno do middleware e dos manipuladores de respostas é o curto-circuito. Se a execução deve continuar através das camadas que a seguem, o middleware (ou um manipulador de resposta) é necessário para passar a execução chamando seu próximo delegado. Se o próximo delegado não for chamado dentro desse middleware (ou manipulador de resposta), os curtos-circuitos de pipeline associados e as camadas subsequentes não serão executados. Isso significa que toda lógica de bot e qualquer middleware mais tarde no pipeline são ignorados. Há uma diferença sutil entre o middleware e o manipulador de resposta, causando um curto-circuito.

Quando o middleware fizer um curto-circuito, o manipulador de turnos de bot não será chamado, mas todo o código de middleware executado antes desse ponto no pipeline ainda será executado até a conclusão.

Para manipuladores de eventos, não chamar em seguida significa que o evento é cancelado, o que é muito diferente da lógica de ignorar middleware. Por não processar o resto do evento, o adaptador nunca o envia.

Dica

Se você fizer com que um evento de resposta entre em curto-circuito, como SendActivities, tenha certeza de esse seja o comportamento pretendido. Caso contrário, ele pode resultar em bugs difíceis de corrigir.

Manipuladores de eventos de resposta

Além da lógica de middleware aplicativo, os manipuladores de resposta (às vezes também chamados de manipuladores de eventos ou manipuladores de eventos de atividade) podem ser adicionados ao objeto de contexto. Esses manipuladores são chamados quando a resposta associada acontece no objeto de contexto atual, antes de executar a resposta real. Esses manipuladores são úteis quando você sabe que deseja fazer algo, antes ou depois do evento real, para cada atividade desse tipo para o restante da resposta atual.

Aviso

Tenha o cuidado para não chamar um método de resposta de atividade de dentro de seu respectivo manipulador de eventos de resposta, por exemplo, chamando o método de atividade de envio de dentro de um manipulador de atividades de envio. Isso pode gerar um loop infinito.

Lembre-se: cada nova atividade obtém um novo thread para executar. Quando o encadeamento para processar a atividade é criado, a lista de manipuladores dessa atividade é copiada para esse novo encadeamento. Nenhum manipulador adicionado após esse ponto será executado para esse evento de atividade específico. Os manipuladores registrados em um objeto de contexto são tratados da mesma forma como o adaptador gerencia o pipeline de middleware. Ou seja, os manipuladores são chamados na ordem em que são adicionados e chamar o próximo representante passa o controle para o próximo manipulador de eventos registrado. Se um manipulador não chamar o próximo delegado, nenhum dos manipuladores de eventos subsequentes será chamado, os curtos-circuitos do evento e o adaptador não enviará a resposta para o canal.

Tratando do estado em middleware

Um método comum para salvar o estado é chamar o método salvar alterações no final do manipulador de turno. Aqui está um diagrama com foco na chamada.

Diagrama de sequência de uma curva de bot, com o estado salvo do manipulador de turnos do bot.

O problema com essa abordagem é que todas as atualizações de estado feitas de algum middleware personalizado que acontece depois que o manipulador de turnos do bot tiver retornado não serão salvas no armazenamento durável. A solução é mover a chamada para o método salvar alterações após o middleware personalizado ser concluído, adicionando uma instância do middleware salvar alterações automaticamente no início da pilha de middleware ou pelo menos antes de qualquer middleware que possa atualizar o estado. A execução é mostrada abaixo.

Diagrama de sequência de uma curva de bot, com o estado salvo do middleware.

Adicione os objetos de gerenciamento de estado que devem ser atualizados para um objeto de conjunto de estado do bot e, em seguida, use-os ao criar seu middleware de salvamento automático de alterações.

Recursos adicionais

Você pode dar uma olhada no middleware do agente de transcrição, conforme implementado no SDK do Bot Framework [C# | JS].