Construindo um Agente SOAR Simples usando Regras
Executando o Hello World do SOAR
O SOAR trabalha com Regras, não consegue interpretar comandos em linguagem natural. O conhecimento é armazenado no SOAR por meio de regras (rules) do tipo "if - then". Essas regras também são chamadas de Produções (productions). As regras são usadas para selecionar Operadores (operators) que serão aplicados, usados, na solução de problemas.
Neste primeiro exemplo, será criado um Agente SOAR muito simples que usa somente regras. Ainda não será abordado o uso de operadores, ou seja, regras que definem qual operador será selecionado e aplicado. Para criar este agente, será usado o ambiente SOAR Debugger. Ao iniciar esse aplicativo, um agente default será criado, com o nome "soar1".
Criando a Regra Hello World
Este agente terá a função de imprimir (numa tela) o texto "Hello World". Para tanto, será preciso gerar uma regra que permita ao agente realizar essa função. O SOAR opera testando as partes "if" (condições) das regras. Se todas as condições, daquela regra, forem verdadeiras (satisfeitas), então as partes "then" (ações) serão executadas.
A execução de uma ação normalmente causa alterações na Working Memory (Memória de Trabalho) do SOAR. (A memória de trabalho do SOAR será discutida abaixo). A memória de trabalho define a situação atual do agente, ou seja, ela consiste da percepção atual do agente a respeito do seu mundo. A memória de trabalho possui uma estrutura de dados que também armazena resultados de cálculos intermediários, metas (goals) ativas, além dos próprios operadores. Para "disparar" uma regra, ou seja, executar suas ações, o SOAR compara as condições da regra com a estrutura de dados na memória de trabalho. Caso sejam atendidas as condições da regra, esta é disparada.
O programador tem a liberdade de criar novas regras para um agente. Essas regras são armazenadas na Rule Memory do SOAR, formando a memória de longa duração do agente. A Working Memory define a memória de curta duração do agente. A memória de trabalho (Working Memory) é uma estrutura em grafo formada por elementos individuais. A memória de trabalho é comparada com a Rule Memory para determinar quais regras serão disparadas. Quando uma regra dispara, suas ações podem acarretar mudanças na memória de trabalho (cujas mudanças poderão afetar o disparo de outras regras).
Após esta breve discussão sobre a estrutura do SOAR, vamos dar início ao primeiro exemplo (simples) de um agente SOAR.
Para executar a regra hello-world, essa regra deve ser carregada na Rule Memory (memória de regras) do SOAR, a partir de um arquivo. Isso será feito usando-se a ferramenta de depuração do SOAR, o aplicativo SOAR Debugger (cuja tela inicial é mostrada na figura abaixo).
Na tela inicial do SOAR Debugger, clica-se na opção File (do menu), e em seguida, seleciona-se a opção Load source file. Em seguida, na janela pop-up, seleciona-se o arquivo hello-world-rule (que encontra-se na pasta /Agents/). Após o arquivo ter sido carregado, basta clicar no botão Run (na parte inferior da tela do depurador) para executar a regra. O resultado está exibido na figura abaixo:
Pode-se ver, na janela de interação (parte esquerda da tela do depurador), que foram impressas algumas instruções, dentre elas, o texto "Hello World". Ao executar esta regra, o SOAR cria (primeiramente e automaticamente) o estado inicial S1. Este estado pode ser visualizado no lado direito da tela (na figura acima). Todos os elementos da memória de trabalho são organizados como estados. Dessa forma, antes que qualquer regra dispare, um estado é criado. Depois que o estado inicial é criado, as condições da regra são satisfeitas e ela dispara, executando ações. Neste exemplo, o texto "Hello World" é impresso e, em seguida, o agente é "suspenso" (Agent halted).
O aplicativo VisualSOAR permite visualizar a estrutura da regra hello-world, conforme figura abaixo:
Cada regra, no SOAR, começa com o símbolo "sp" (SOAR Production) seguido do nome da regra, suas condições e suas ações. O corpo da regra é delimitado por chaves ("{ }"), e o símbolo "-->" separa as partes if (condições) das partes then (ações). Neste exemplo, o nome da regra é hello-world, suas condições são apenas uma (que exista um estado inicial, na memória de trabalho), e as ações são imprimir o texto "Hello World" e em seguida "suspender" o agente (halt).
As condições, de uma regra, testam a existência (ou ausência) de dados na memória de trabalho do SOAR. A maioria das ações, de uma regra, criam novos elementos na memória de trabalho, ou, removem elementos existentes desta. Algumas ações podem criar preferências para a seleção de operadores, conforme será visto posteriormente.
Utilizando a Memória de Trabalho
A memória de trabalho do SOAR contém as informações do agente (SOAR) sobre seus estados internos e sobre o mundo exterior. Ela armazena os dados recebidos dos sensores do agente, além de cálculos intermediários e os operadores e metas atuais do agente. A memória de trabalho está organizada em estados formando uma estrutura em grafo (conforme a figura abaixo). Cada elemento da memória de trabalho está ligado, direta ou indiretamente, a um estado.
Conforme a figura acima, cada estado possui um nome (identifier), atributos (attribute) e valores (value). O grafo da figura acima representa a informação do agente sobre uma característica do seu ambiente, capturada pelos seus sensores. Neste caso, o conhecimento de que existem dois blocos empilhados sobre uma mesa. O estado S1 é o estado inicial do agente, criado automaticamente. O estado S1 possui outros três estados ligados a ele: os blocos (^block) B1 e B2 e a mesa (^table) T1. Esses outros estados também possuem atributos (^name, ^color, ^type) e valores relacionados a esses atributos (por exemplo, color = blue). Um atributo (^ontop) define a organização espacial dos três objetos, ou seja, o bloco A está sobre o bloco B que está sobre a mesa. Dessa forma, o SOAR consegue armazenar as informações dos seus agentes de forma bastante clara e organizada. Um detalhe: os grafos gerados, no SOAR, possuem dois tipos de nós. Nós do tipo identifiers (S1, B1) e nós do tipo constants (blue, block). Os nós identifiers levam a outros nós. Os nós constants são nós terminais.
Os objetos da memória de trabalho, normalmente, representam coisas que existem no mundo (exterior) do agente. Esses objetos possuem propriedades e estão relacionados entre si. Como no exemplo da figura acima: um bloco azul sobre outro bloco amarelo, em cima de uma mesa cinza. A figura abaixo mostra uma outra maneira de visualizar os elementos da memória de trabalho estruturados no grafo da figura anterior. Na figura (abaixo), estão representados os elementos da memória de trabalho (S1, B1, B2, T1) e seus pares de atributos e valores (por exemplo, ^color blue).
A memória de trabalho também contém elementos (objetos) que não representam coisas conceituais e nem tem uma ligação física com o mundo exterior. Por exemplo, o estado S1, que é usado para organizar outros objetos, suas relações e propriedades. A estrutura inicial desse estado, que é gerada pelo SOAR quando o estado S1 é criado, pode ser visualizada na próxima figura:
O estado inicial S1 possui três atributos: ^superstate, ^io, ^type. O estado S1 possui um nó, I1, que é responsável pelas informações que o agente (SOAR) recebe de seus sensores, e, pelas informações que o agente envia para seus atuadores. O nó I2 é responsável pela saída (^output-link) e o nó I3 é responsável pela entrada (^input-link) das informações. Conforme o agente atua no seu ambiente, novas estruturas de dados serão geradas e adicionadas a esses dois nós (I2, I3). A memória de trabalho está sempre construindo novos elementos que são uma tripla (compostos de) identifier, attribute, value. O value pode ser um nó identifier ou um nó constant.