今時の JavaScript で動きそうなELIZA実装は?

2019/04/16
藤田昭人


このページは 今時の開発環境で動きそうなELIZA実装は? の続編のようなものです。
今回は JavaScript 限定で実装を紹介します。

僕が勝手に信じ込んでるだけなのかもしれないのですが、スマートスピーカー用アプリ(スキルと言うらしい)の主力開発言語はJavaScriptらしいので…もちろん他の言語でもスキルは作れるんだけど、各種のHow toで例示されるのはJSが圧倒的に多い気がしてならないので。勘違い?

…と言うわけで、今時感に溢れるJSで書かれたELIZAを探してみました。実は、最初はgithubで、"ELIZA"で検索してみたら、何ページにも渡って検索結果が出てきて、次に"ELIZA JS"で検索したら25件ヒット。「まだ少し多いな」と思い"ELIZA nodejs"で検索したら9件残りました。で、しらみ潰しにチェックしたのだけど、なんか違う………結局、npmのサイトでELIZAと言う名前で調べた結果、良さげな6つが見つかったという経緯です。

まぁ「似て非なる」モノが山ほど見つかる昨今、昔みたいに気合と根性で探し回る必要が無いのはありがたいんだが、チェックしてるうちに「本当に欲しかったモノは何だったっけ…」となるのが困りもんですね。情報不足と情報過多を比べるとどちらが幸せなんだろうかねぇ?本当に。


調べてみたらベースコードはどれも Norbert Landsteiner の elizabot

…という事で npm に登録されているライブラリとして利用できる ELIZA 実装をピックアップしてみました。

結果はこの3つです。比較してみたら…中身は3つともほとんど Norbert Landsteiner が実装した elizabot のコードでした。

www.masswerk.at

このコードは前回紹介した Charles HaydenEliza test を参考に JavaScript に書き直された ELIZA 実装のようです。 素敵な事に、スクリプトは…

Data Structures (cf: "elizadata.js"):

         elizaInitials ......... Array of initial phrases
         elizaFinals  .......... Array of final phrases
         elizaQuits ............ Array of quit phrases
         elizaPres ............. Array of alternating name value pairs for preprocessing
         elizaPosts ............ Array of alternating name value pairs for postprocessing
         elizaSynons ........... Object of words and their synonyms (as array)
         elizaPostTransforms ... regexp/replacement pairs to be performed as final cleanings
         elizaKeywords ......... Array of keywords with decompositions and reasemblies

         elizaKeywords elements are of:
       
         ["<key>", <rank>,
           [
             [ // first decomposition pattern
               "<decomp>",
               ["<reasmb>", "<reasmb>", "<reasmb>"]
             ],
             [ // second decomposition pattern
               "<decomp>",
               ["<reasmb>", "<reasmb>", "<reasmb>"]
             ]
           ]
         ]

         keywords with higher rank take precedence.
         decompositions are matched in definition order.
         reasemblies are chosen by random or cycled through if the no-random flag ist set.
 
         the special keyword "xnone" holds the rules for default phrases (no match).
         
         decomposition and reasembly syntax follow the "canonical" form:

         decomposition:
           * ... any words (incl. none)
           $ ... (in first position) reassemble for memory only
           @synon ... substitute entry with synononym expression

         reassembly:
           (n) ... insert param of position n (first is "1")
                   positions are any matchings of "*" or "@synon"

         pres, posts, synonyms, keywords, decompositions all in lower case.
         all definitions are optional but at least elizaKeywords should be supplied.
         if no keywords are found `transform()' returns the default string:
         "I am at a loss for words.".

Note:   "elizaPostTransforms" is not a standard ELIZA feature and was included to provide
         a smoothing mechanism for any productions of a bot-to-bot conversation.

Hayden バージョンと同じシンタックスを使います。したがって ELIZA(3)スクリプト ー 応答を作り出す仕掛け で説明したとおりに動きます。ちなみに Eliza Test というページも用意されていて、ELIZA論文に掲載されていた対話事例がステップ実行できるようになってます。

ですが、このコードが実装されたのは 2005 年。 クライアント・サイド JavaScript として実装されており、 一部のコードは HTML に埋め込み型で記述されています。 今では一般的なプログラミング環境として認知されている nodejs は、 登場したのは 2009 年ですから無理もない話なのかもしれません。

そこで、nodojs の作法に合うよう手直しした前述の3つのモジュールが npm に登録されています。 いずれも、ソースコードgithub からダウンロード可能です。

github.com github.com github.com

いずれも原則的には Landsteiner 版のソースに module.exports = ElizaBot; を追加しただけでした。 (もう少し何か拡張しているのかと期待したのだけど…) npm のサイトでの一番人気は、登録が一番早かった elizabot です。 でもこの3つ、優劣が付けられるほどコードの差はありません。


チャットシステムと連携する JS 版 ELIZA 実装

その他、チャットシステムと連携するコードが含まれているパッケージが3つありました。

eliza (for irc)

eliza - npm github.com

これには(懐かしの)インターネット・リレー・チャット(IRC)へのインターフェースが付属しています。

hubot-eliza

hubot-eliza - npm github.com

これは github が公開する hubot にアダプトできる ELIZA です。

snarl-eliza

snarl-eliza - npm github.com

これは ELIZA を Slackプラグイン化するためのコードの模様。

今時 hubot にアダプトできさえすればどんなチャットシステムでも ELIZA が使えるようになるんだろうなぁ… とは思いましたが「今時そんな需要があるんだろうか?」というのが正直なところです。


ELIZAの JavaScript 版実装を整理してみた

冒頭で「似て非なる」と書きましたが、それどころか npm の ELIZA 関連モジュールは「似て同一なるもの」だった、というあまり締まりのない結論に行き着きましたが、 これもありがちな話でしょう。特に僕が引っかかるのは、いずれも DOCTER スクリプトが埋め込み型になっているところ。 これも欧米では ELIZA がクラシカル・ゲームの定番のひとつとなっていて、 スクリプト・エンジンと DOCTER スクリプトが一体で "ELIZA" と認知されているからでしょうか?

僕的な都合ですが「CLI ベースでスクリプトを取っ替え引っ替えして使える」ものが欲しいので、 4番目の「似て同一なるもの」を作ってみました。

github.com

Norbert Landsteiner の elizabot をベースに Tomáš Kafka が行った修正を施し、さらに elizadata.js を JSON フォーマットに書き直した doctor.json を作成しました。 macOSの環境であれば、任意のディレクトリで次の手順を実行すれば簡単に ELIZA が実行できます。あ、nodejs/npm を使ってますので、予めインストールしておいてください。

$ git clone https://github.com/m04uc513/eliza-js.git
$ cd eliza-js/
$ node elizaapp.js doctor.json test
C: Men are all alike.
E: In what way ?
C: They're always bugging us about something or other.
E: Can you think of a specific example ?
C: Well, my boyfriend made me come here.
E: Your boyfriend made you come here ?
C: He says I'm depressed much of the time.
E: I am sorry to hear that you are depressed.
C: It's true. I am unhappy
E: Do you think coming here will help you not to be unhappy ?
C: I need some help, that much seems certain.
E: What would it mean to you if you got some help ?
C: Perhaps I could learn to get along with my mother.
E: Tell me more about your family.
C: My mother takes care of me.
E: Who else in your family takes care of you ?
C: My father.
E: Your father ?
C: You are like my father in some ways.
E: What resemblence do you see ?
C: You are not very aggressive but I think you don't want me to notice that.
E: What makes you think I am not very aggressive ?
C: You don't argue with me.
E: Why do you think I don't argue with you ?
C: You are afraid of me.
E: Does it please you to believe I am afraid of you ?
C: My father is afraid of everybody.
E: What else comes to your mind when you think of your father ?
C: Bullies.
E: Does that have anything to do with the fact that your boyfriend made you come here ?
$

このプログラムは CLI で、実行方法は ELIZA(3)スクリプト ー 応答を作り出す仕掛け で紹介した Java 版に合わせてあります。doctor.json は Landsteiner の elizadata.js の記述をできるだけ修正しないようにして、 JSON フォーマットに書き換えたので、(JSONはコメントを付けれない事もあって)ちょっと見づらいです。 また Landsteiner の elizabot.js のコードは ELIZA 特有のパターンマッチング処理が読みづらい。 このあたりは、今後修正したいと考えています。

ともあれ…

これで望みの JavaScript 実装が手に入ったので先に進めます(笑)

以上