[ Webhosting profitux.cz ]
hertpl: Undefined index: page_title(template line: 74) in templates/articles_print.html on line 39

Programování inteligentních agentů - 4. díl

Komunikace v jazyce 3APL

Úvod

V předešlých dílech seriálu o programování inteligentních agentů jsme si ukázali, jak je možno v jazyce 3APL vytvořit inteligentního agenta. Dnes - na Silvestra 2007 - si ukážeme co dělat v případě, že chceme mít agentů několik a to navíc tak, aby vzájemně spolupracovali na řešeném problému.

Komunikace agentů

Nezbytnou součástí týmového řešení problému je dobrá komunikace mezi jednotlivými řešiteli. Nejinak je tomu i v multi-agentních systémech. Jazyk 3APL tedy pochopitelně obsahuje i prostředky pro zachycení průběhu komunikace mezi agenty.

Chceme-li něco sdělit jinému agentovi, máme k dispozici akci "Send". Akce "Send" je vestavěnou akcí jazyka 3APL a má následující syntax:

Send(jmenoAgenta, typZpravy, obsahZpravy)

Pokud agent A pošle zprávu agentovi B, neboli provede akci Send(B,X,Y), do jeho belief-báze se uloží sent(B,X,Y) a do belief-báze agenta B se uloží received(A,X,Y). Takto je tedy možno na přijetí (nebo i odeslání) nějaké zprávy patřičně reagovat.

Příklad - Základní škola

Pro demonstraci toho, jak komunikace v 3APL funguje si ukážeme si program, ve kterém se agenti třeťáčci ptají agenta učitelky na výsledek nějakého příkladu. Zdrojové soubory si můžete stáhnout jako ZIP archiv.

Předpokládejme, že je hodina matematiky a ve třídě 3.APL jsou dva žáčci, Karlík a Pepík. Oba dva žáčci mají nějaké příklady, které mají spočítat. Karlík umí dobře sčítat, ale násobení mu moc nejde. Pepík přesně naopak. Paní učitelka naproti tomu (překvapivě) zvládá obě dvě tyto početní operace. Žáčci si sami počítají a pokud si s nějakým příkladem nevědí rady, volají na učitelku "Prosím, prosím!". Učitelka někoho z hlásících se žáků vyvolá, tj. řekne nějakému žáčkovi "Co chceš?!". Žáček jí nato sdělí svuj dotaz a paní učitelka mu na něj odpoví... V jednu chvíli může být pochopitelně vyvolaný pouze jeden žák.

Nejdříve si napíšeme učitelku, ta je o něco málo jednodušší než oba žáčci:

PROGRAM "ucitelka"
 
CAPABILITIES {
  {soucet(A,B,SoucetAB)} OdpovedelaJsemSoucet(Zak,A,B) {
                                                   NOT vyvolalaJsemZaka(),
                                                   NOT received(Zak, inform, kolikJeSoucet(A,B)),
                                                   NOT sent(Zak, inform, noPreceSoucet(A,B,SoucetAB))
                                                 }
 
  {soucin(A,B,SoucinAB)} OdpovedelaJsemSoucin(Zak,A,B) {
                                                   NOT vyvolalaJsemZaka(),
                                                   NOT received(Zak, inform, kolikJeSoucin(A,B)),
                                                   NOT sent(Zak, inform, noPreceSoucin(A,B,SoucinAB))
                                                 }
 
  {true} VyvolalaJsem(Zak) {
                             NOT sent(Zak, inform, muzes),
                             NOT received(Zak, inform, prosim),
                             vyvolalaJsemZaka()
                           }
 
}
 
BELIEFBASE {
  soucet(A,B,SoucetAB) :- SoucetAB is A + B.
  soucin(A,B,SoucinAB) :- SoucinAB is A * B.
}
 
PLANBASE {}
 
GOALBASE {
}
 
PG-RULES {
  <- received(Zak, inform, prosim) AND NOT vyvolalaJsemZaka() | { Vyvolej(Zak); }
  <- received(Zak, inform, kolikJeSoucet(A,B)) | { OdpovezZakoviSoucet(Zak, A, B); }
  <- received(Zak, inform, kolikJeSoucin(A,B)) | { OdpovezZakoviSoucin(Zak, A, B); }
}
 
PR-RULES {
  Vyvolej(Zak) <- true |
  {
    IF (NOT vyvolalaJsemZaka()) THEN {
      Send(Zak, inform, muzes());
      VyvolalaJsem(Zak);
    }
  }
  OdpovezZakoviSoucet(Zak, A, B) <- soucet(A, B, SoucetAB) |
  {
    Send(Zak, inform, noPreceSoucet(A,B,SoucetAB));
    OdpovedelaJsemSoucet(Zak,A,B);
  }
 
  OdpovezZakoviSoucin(Zak, A, B) <- soucin(A, B, SoucinAB) |
  {
    Send(Zak, inform, noPreceSoucin(A,B,SoucinAB));
    OdpovedelaJsemSoucin(Zak,A,B);
  }
}

To bychom snad měli. Teď jsou na řadě náši malí neposedové - začněme Kájou:

PROGRAM "zak_karel"
 
CAPABILITIES {
  {received("ucitelka", inform, muzes)} JsiVyvolan() {
                                                       NOT received("ucitelka", inform, muzes),
                                                       NOT sent("ucitelka", inform, prosim)
                                                     }
  {true} MamOdpoved(A,B,SoucinAB) {
                                    NOT nevimKolikJeSoucin(A,B),
                                    vimSoucin(A,B,SoucinAB),
                                    NOT sent("ucitelka", inform, kolikJeSoucin(A,B)),
                                    NOT received("ucitelka", inform, noPreceSoucin(A,B,SoucinAB))
                                  }
  {soucet(A,B,SoucetAB)} Vyres(A, B) {vimSoucet(A,B,SoucetAB), NOT nevimKolikJeSoucet(A,B)}
  {true} NevsimlaSiMe() {NOT sent("ucitelka", inform, prosim)}
}
 
BELIEFBASE {
  nevimKolikJeSoucet(4,5).
  nevimKolikJeSoucin(7,11).
  nevimKolikJeSoucet(8,12).
  nevimKolikJeSoucin(9,8).
  nevimKolikJeSoucin(6,10).
  nevimKolikJeSoucet(2,2).
  nevimKolikJeSoucin(2,3).
  soucet(A,B,SoucetAB) :- SoucetAB is A + B.
}
 
PLANBASE {}
 
GOALBASE {
}
 
PG-RULES {
  <- received("ucitelka", inform, muzes) AND nevimKolikJeSoucin(A,B) |
  {
    JsiVyvolan();
    PtejSe(A,B);
    CekejNaSoucin(A,B);
  }
 
  <- received("ucitelka", inform, noPreceSoucin(A,B,SoucinAB)) |
  {
    MamOdpoved(A,B,SoucinAB);
  }
 
  <- nevimKolikJeSoucin(_,_) |
  {
    HlasSe();
  }
  <- nevimKolikJeSoucet(A,B) |
  {
    Vyres(A,B);
  }
}
 
PR-RULES {
  HlasSe() <- true |
  {
    IF (NOT sent("ucitelka", inform, prosim)) THEN {
      Send("ucitelka", inform, prosim());
    } ELSE {
      NevsimlaSiMe();
    };
    IF (NOT received("ucitelka", inform, muzes)) THEN {
      HlasSe();
    }
  }
 
  PtejSe(A,B) <- true |
  {
    Send("ucitelka", inform, kolikJeSoucin(A,B));
  }
 
  CekejNaSoucin(A,B) <- true |
  {
    IF (NOT received("ucitelka", inform, noPreceSoucin(A,B,_))) THEN {
      CekejNaSoucin(A,B);
    }
  }
}

Tak, a teď Pepa - je to dost podobné:

PROGRAM "zak_pepa"
 
CAPABILITIES {
  {received("ucitelka", inform, muzes)} JsiVyvolan() {
                                                       NOT received("ucitelka", inform, muzes),
                                                       NOT sent("ucitelka", inform, prosim)
                                                     }
  {true} MamOdpoved(A,B,SoucetAB) {
                                                       NOT nevimKolikJeSoucet(A,B),
                                                       vimSoucet(A,B,SoucetAB),
                                                       NOT sent("ucitelka", inform, kolikJeSoucet(A,B)),
                                                       NOT received("ucitelka", inform, noPreceSoucet(A,B,SoucetAB))
                                                      }
  {soucin(A,B,SoucinAB)} Vyres(A, B) {vimSoucin(A,B,SoucinAB), NOT nevimKolikJeSoucin(A,B)}
  {true} NevsimlaSiMe() {NOT sent("ucitelka", inform, prosim)}
}
 
BELIEFBASE {
  nevimKolikJeSoucet(4,5).
  nevimKolikJeSoucin(7,11).
  nevimKolikJeSoucet(8,12).
  nevimKolikJeSoucin(9,8).
  nevimKolikJeSoucin(6,10).
  nevimKolikJeSoucet(2,2).
  nevimKolikJeSoucin(2,3).
  soucin(A,B,SoucinAB) :- SoucinAB is A * B.
}
 
PLANBASE {}
 
GOALBASE {
}
 
PG-RULES {
  <- received("ucitelka", inform, muzes) AND nevimKolikJeSoucet(A,B) |
  {
    JsiVyvolan();
    PtejSe(A,B);
    CekejNaSoucet(A,B);
  }
 
  <- received("ucitelka", inform, noPreceSoucet(A,B,SoucetAB)) |
  {
    MamOdpoved(A,B,SoucetAB);
  }
 
  <- nevimKolikJeSoucet(_,_) |
  {
    HlasSe();
  }
  <- nevimKolikJeSoucin(A,B) |
  {
    Vyres(A,B);
  }
}
 
PR-RULES {
  HlasSe() <- true |
  {
    IF (NOT sent("ucitelka", inform, prosim)) THEN {
      Send("ucitelka", inform, prosim());
    } ELSE {
      NevsimlaSiMe();
    };
    IF (NOT received("ucitelka", inform, muzes)) THEN {
      HlasSe();
    }
  }
 
  PtejSe(A,B) <- true |
  {
    Send("ucitelka", inform, kolikJeSoucet(A,B));
  }
 
  CekejNaSoucet(A,B) <- true |
  {
    IF (NOT received("ucitelka", inform, noPreceSoucet(A,B,_))) THEN {
      CekejNaSoucet(A,B);
    }
  }
}

To, jak probíhá komunikace mezi žáky a paní učitelkou je vidět na následujícím obrázku (teď koukám, že screenshot je ještě z doby, kdy Karlík s Pájou chodili do 1.APL, ale snad mi to prominete...):

Komunikace žáků s paní učitelkou

Rozbor příkladu

Těm bystřejším asi došlo, že tento příklad má i jistý alegorický význam a nejedná se tedy jen o zcela bezúčelnou hříčku. Těm méně zdatným zde nabídnu možné nahlížení na příklad (prosím zde předběžně, aby mě zkušený čtenář nebral vždy za slovo).

Oba dva naši žáčkové třetí třídy vlastně - aniž by o tom věděli - provádějí "bussy-waiting" - aktivně bojují o sdílený zdroj, o paní učitelku. Neustále ji bombardují žádostmi o možnost položení dotazu. Musí to dělat proto, že učitelku chce zpovídat více žáků ("server přijímá žádosti o zpracování nějaké úlohy od více klientů") a navíc třeba (to se nám teď ale nestane) nechtějí riskovat, že by je paní učitelka přehlédla ("že by se paket s jejich žádostí někde v síti ztratil").

Pokud dostanou žáci povolení mluvit, položí dotaz a čekají na výsledek. I zde by bylo možné ošetřit, že když jim učitelka neodpoví v rozumné době, položí dotaz znova. To jsme zde vynechali a to proto, že spolehlivost doručování zpráv předpokládáme. Hlavní důvod proč žáci neustále křičí "Prosím prosím!" je ten, aby se dostali ke slovu - zkouší vlastně jestli je paní učitelka vyvolá a ta je nemůže vyvolat, když už někoho předtím vyvolala.

Zároveň je zajímavé nahlížet na příklad takto: každý žák umí jistý typ úlohy řešit sám (neboli "můžeme mít různé klienty, ti mohou být různě chytří a různé funkce si případně mohou zajišťovat sami"). Žák se učitelky ("serveru") proto ptá jen na to, co potřebuje vědět a co si neumí zjistit sám.

Temná zákoutí jazyka 3APL... aneb kde se člověk může zaseknout...

To, co je zajímavé na implementaci je to, že aktivní čekání není v jazyku 3APL vždy možné (a příště si to i ukážeme) realizovat ve while cyklu. Vůbec to nejde v případě, že chceme pracovat s externím prostředím a nějakou akci pozdržet do doby, dokud v prostředí nevnímáme nějaký jev, např. dokud neuspěje "senseAgents" (viz minulý díl). Důvodem je logika na jaké je založena unifikace (když se něco zunifikuje, je to zunifikováno v rámci "scope" trvale). Je proto vhodné nahradit while cyklus něčím jiným, zde jsme si například vystačili s rekurzivním voláním "čekací" funkce.

Zároveň si dejte pozor na to, že je rozdíl mezi zápisem:

Cekej() <- true |
{
  IF (NOT received("ucitelka", inform, noPreceSoucet(A,B,_))) THEN {
    Cekej();
  }
}

a zápisem:

Cekej() <- NOT received("ucitelka", inform, noPreceSoucet(A,B,_)) |
{
  Cekej();
}

Pokud bychom v našem "školáckém programu" použili druhý kód a zavolali akci Cekej(), program by nefungoval tak jak má. Proč?

Po zavolání Cekej() by se umístila do "PLANBASE" akce Cekej(), ale neprovedla by se (!!!) ve chvíli, kdy není naplněna strážná podmínka. Akce by tedy v "PLANBASE" zůstala - byla by naplánovaná, ale nemohla by se provést pro nesplněný předpoklad.

Naproti tomu první kód zajistí, že kdykoli se akce Cekej() umístí do "PLANBASE", provede se (!!!) a v případě, že není splněna jistá podmínka, akce se rekurzivně nezavolá a odstraní se tak (de facto pasivně) z "PLANBASE".

Povšimněte si také, že dnes poprvé používáme klauzuli PR-RULES (plan revision rules). Budeme ji používat pro "hezké obalení" akcí (používá se často také k definici složených akcí, aby se zpřehlednila sekce PG-RULES a ušetřilo se psaní kódu).


To bychom měli dnes všechno, příště si povíme něco více o interakcích agentů a oprášíme přitom některé základní poznatky z teorie her. Jako příklad si napíšeme několik strategií hrajících Axelrodův turnaj (iterované vězňovo dilema) a budeme si o nich trochu povídat a bilancovat je.

Související články:

  • Programování inteligentních agentů - 1. díl
  • Programování inteligentních agentů - 2. díl
  • Programování inteligentních agentů - 3. díl
  • Programování inteligentních agentů - 5. díl

Komentáře čtenářů

hertpl: Undefined index: comments(template line: 119) in templates/articles_print.html on line 55