Memória episódica é um mecanismo do SOAR que AUTOMATICAMENTE captura, indexa e armazena os ESTADOS do agente, provendo uma interface que permite o acesso a essa informação AUTOBIOGRÁFICA do agente.
Igualmente à memória semântica, o acesso à memória episódica é feito a partir de uma interface onde comandos são enviados pelo agente e os resultados são fornecidos pela arquitetura; neste caso, as extensões são as seguintes:
- ^epmem.command: interface onde o agente cria comandos para serem tratados pela memória episódica
- ^epmem.result: interface onde a arquitetura cria as respostas aos comandos enviados
De forma diferente da memória semântica, as informações da memória episódica são automaticamente criadas e esse processo é governado pelo seguinte comando:
epmem --set trigger <dc | output | input>
Esse comando indica em qual ponto um SNAPSHOT do estado atual deve ser capturado: dc - "decision cycle" ou output - "output cycle". Por default SOAR utiliza output.
Para que seja utilizada, a memória semântica precisa ser habilitada, através do comando:
epmem --set learning on
É possível criar uma representação gráfica dos elementos na memória episódica, também com o uso do pacote Graphviz; o seguinte episódio:
epmem --print 1
(<id0> ^io <id1> ^reward-link <id2> ^superstate nil ^type state)
(<id1> ^input-link <id4> ^output-link <id3>)
Pode ser gravado para ser representado pelo Graphviz com o seguinte comando:
command-to-file <output-filename> epmem --viz 1
E transformado em gráfico da seguinte forma:
dot -Tpng <output-filename> -o <image-filename>.png
Durante o processo de captura de um episódio - ou seja, de um SNAPSHOT do estado atual - é possível indicar ao SOAR extensões que devem ser ignorada e NÃO devem fazer parte do episódio capturado. Isso é feito com o uso do comando:
epmem --set exclusions <attribute>
A lista de exclusões configurada pode ser obtida com o comando:
epmem --get exclusions
Por fim, para que se possa observar o processo de criação de episódios, pode ser utilizado o comando:
watch --epmem
INTERAÇÃO DO AGENTE COM A MEMÓRIA EPISÓDICA
Por default os comandos que o agente envia para a memória episódica são tratados durante a fase de OUTPUT; esse parâmetro pode ser alterado pelo seguinte comando:
epmem --set phase <selection | output>
Apenas 1 comando pode ser enviado por vez.
O tutorial segue o exemplo "epmem-tutorial.soar" para discutir os comandos suportados para a interação com a memória episódica.
Os comandos surportados são os seguintes:
- "^query <cue>": permite buscar na memória episódica um episódio que MELHOR satisfaça a condição de busca, ou seja, tenha maior semelhança com a estrutura descrita com "<cue>", comparando apenas os NÓS FOLHA. No limite, se houver mais de um episódio que contenha uma estrutura IDÊNTICA com a especificada, o episódio mais recente (ou seja, de maior "^id") é retornado.
- "^previous <id>": busca o episódio anterior ao ÚLTIMO episódio que foi recuperado da memória episódica.
- "^next <id>": busca o episódio posterior ao ÚLTIMO episódio que foi recuperado da memória episódica.
Seguindo o programa exemplo, logo de início é criada a seguinte estrutura no estado atual:
p s1 -d 2
(S1 ^epmem E1 ^feature value3 ^feature2 value ^id E2 ^id E3 ^io I1 ^name epmem
^operator O2 + ^other-id E4 ^reward-link R1 ^smem S2 ^superstate nil
^type state)
(E1 ^command C1 ^present-id 1 ^result R2)
(E2 ^sub-feature value2)
(E3 ^sub-id E5)
(I1 ^input-link I2 ^output-link I3)
(O2 ^name cbr)
(E4 ^sub-feature value2 ^sub-id E6)
(S2 ^command C2 ^result R3)
Ao se atingir a etapa de OUTPUT, é criado o episódio 1 com as seguintes informações:
epmem --print 1
(<id0> ^feature value3 ^feature2 value ^id <id2> <id3> ^io <id1> ^name epmem ^operator* <id5> ^other-id <id4> ^reward-link <id6> ^superstate nil ^type state)
(<id1> ^input-link <id8> ^output-link <id7>)
(<id2> ^sub-feature value2)
(<id3> ^sub-id <id9>)
(<id4> ^sub-feature value2 ^sub-id <id10>)
(<id5> ^name cbr)
O comando QUERY
O programa exemplo ilustra o uso do comando "^query" com as seguintes regras:
sp {epmem*apply*cbr-clean
(state <s> ^operator <op>
^feature2 <f2>
^feature <f>
^id <e2>
^id <e3>
^other-id <e4>)
(<e2> ^sub-feature value2)
(<e3> ^sub-id)
(<op> ^name cbr)
-->
(<s> ^feature2 <f2> -
^feature <f> -
^id <e2> -
^id <e3> -
^other-id <e4> -)
}
sp {epmem*apply*cbr-query
(state <s> ^operator <op>
^epmem.command <cmd>)
(<op> ^name cbr)
-->
(<cmd> ^query <n1>)
(<n1> ^feature value
^id <n2>)
(<n2> ^sub-feature value2
^sub-id <n3>)
}
A primeira regra, "epmem*apply*cbr-clean", aplica o operador "cbr" para remover algumas das estruturas inicializadas no estado e que foram capturadas no episódio 1; a segunda regra, "epmem*apply*cbr-query", aplica o mesmo operador "cbr" para criar o comando de QUERY, buscando justamente por algumas das estruturas removidas.
Representação da consulta realizada no comando "^query"
Até que o comando QUERY seja executado, SOAR captura ainda um segundo episódio - uma vez que atinge novamente a fase de OUTPUT:
NEW EPISODE: 2
CONSIDERING EPISODE (time, cardinality, score): (1, 2, 2.000000)
NEW KING (perfect, graph-match): (false, false)
Apenas o primeiro episódio é retornado, com uma pontuação de 2, que não representa um "perfect match", uma vez que havia 3 nós na consulta. De fato, os nós que pontuaram foram os seguintes:
Os valores retornados como resultado do comando são os seguintes:
p e1 -d 10
(E1 ^command C1 ^present-id 3 ^result R2)
(C1 ^query N1)
(N1 ^feature value ^id N2)
(N2 ^sub-feature value2 ^sub-id N3)
(R2 ^cue-size 3 ^graph-match 0 ^match-cardinality 2 ^match-score 2.
^memory-id 1 ^normalized-match-score 0.6666666666666666 ^present-id 3
^retrieved R4 ^success N1)
(R4 ^feature value3 ^feature2 value ^id I5 ^id I6 ^io I4 ^name epmem
^operator* O5 ^other-id O4 ^reward-link R5 ^superstate nil
^type state)
(I5 ^sub-feature value2)
(I6 ^sub-id S3)
(I4 ^input-link I7 ^output-link O6)
(O5 ^name cbr)
(O4 ^sub-feature value2 ^sub-id S4)
O episódio 1 está a partir da extensão R4.
O comando NEXT
Na sequência do programa exemplo, é aplicado o comando NEXT a partir da seguinte regra:
sp {epmem*apply*next
(state <s> ^operator <op>
^epmem.command <cmd>)
(<op> ^name next)
(<cmd> ^query <q>)
-->
(<cmd> ^query <q> -
^next <next>)
}
Uma vez executado esse comando, além da criação de um novo episódio (novo ciclo OUTPUT) é retornado o episódio 2:
NEW EPISODE: 3
p e1 -d 10
(E1 ^command C1 ^present-id 4 ^result R2)
(C1 ^next N4)
(R2 ^memory-id 2 ^present-id 4 ^retrieved R6 ^success N4)
(R6 ^io I8 ^name epmem ^operator* O7 ^reward-link R7 ^superstate nil
^type state)
(I8 ^input-link I9 ^output-link O8)
(O7 ^name next)