アキネーターの API が存在することを知ったので、使ってみました。ついでにこれを使ったウェブアプリを作ってソースコードごと公開してみました。


【アキネーターとは】
「思い浮かべた人を当てる」ウェブサービスです。基本的にはアキネーターからの質問に「はい」「部分的にそう」「わからない」「たぶん違う」「いいえ」の5つから選んで回答していくことで対象を絞り込み、かなりの高確率でその思い浮かべた人を当ててきます。

日本語のウェブ版はこちらから遊ぶことができます。スマホのアプリも提供されています。もし知らない人がいたら、以下を試す前に是非一度使って、その凄さを体験してみてください:
https://jp.akinator.com/

2020122505


【アキネーター API とは】
このアキネーターの機能を外部から利用するための API です。Node.js 向け npm パッケージ(aki-api)として提供されています:
https://github.com/jgoralcz/aki-api

2020122504


提供されているのは参照用の API のみのように見えます。つまり新しいキャラクターを登録したり、思い浮かべた人が間違っていた時のフィードバックの機能などは API に含まれていないようです。最初の質問を返して、それに答えて、答えると次の質問を返して、また答えて、・・・を繰り返します。答えるたびにその時点での確信度も更新されます。そしてどこかのタイミングで「決定」すると、その時点での回答候補と判定確率の一覧を参照することができる(普通はその中で最も確率の高い人を答えることになります)、といった機能を持っています。

アキネーター API は Node.js で動かすこと自体は簡単ですが、ウェブアプリケーションの中で使おうとすると、少し工夫が必要です。そのあたりの解説を含めて API の説明を後述していきます。


【アキネーター API の使い方】

以下ではアキネーター API の具体的な使い方を説明します。


準備

まずアキネーター API を利用するための動作環境として Node.js 8.2.1 以上、npm 5.3.0 以上が必要です。これらのバージョン以上の Node.js および npm をインストールしておく必要があります。

アキネーター API を利用する前に以下のコマンドで aki-api パッケージをインストールしておきます:
$ npm install aki-api --save

インスタンス化するまで

ここからは実際のプログラムの内容を紹介します。

アキネーター API を利用するには、まず以下のコードでライブラリを呼び出します:
var { Aki } = require( 'aki-api' );

そして以下のようなコードで変数 aki にインスタンス化します。インスタンス化時に対応言語をパラメータで指定する必要があります(下の例では "jp"(日本)を指定しています):
var aki = new Aki( 'jp' );

なお、この時に指定可能な対応言語の一覧は以下のコードで取得できます(regions 変数内に文字列配列で格納されます):
var { regions } = require( 'aki-api' );

こうしてインスタンス化した変数 aki を使ってアキネーターが操作できるようになります。


ゲームを開始して、質問と回答を繰り返して、推測した人を特定するまで

ここからが実際のアキネーターの挙動に関わる部分の説明となります。API の肝の部分です。なおアキネーター API の多くは非同期に実行されるため、同期処理する場合は await を付けて実行する必要があります。以下の説明で await を付けて実行している部分がその該当部分だと認識ください。

まずインスタンス化したアキネーターの推測を開始するために start メソッドを実行します:
await aki.start();

スタートしたインスタンスからは(後述の win メソッドを実行するまで)いくつかのプロパティを参照できるようになります。アプリケーション化する上で必要と思われる代表的なものを以下に紹介します:
プロパティ内容
currentStepここまでの推測に要したステップ数(初期値は 0)
questionこの時点でのアキネーターからの質問内容。インスタンス化時に指定した言語での質問となる(例:「男性ですか?」)
answers質問への回答選択肢の配列。インスタンス化時に指定した言語での質問となる(例:「はい」「いいえ」「わからない」・・)
progressこの時点でのアキネーターの回答確信度(0~100)


アキネーターからの質問内容は aki.question に格納されています。その質問に対する回答選択肢は aki.answers に文字列の配列で格納されていますが、回答選択肢の内容は途中で変化することはありません(例えば 'jp' でインスタンス化した場合は、「はい」「いいえ」「わからない」「部分的にそう」「多分ちがう」の5つで常に固定されます)。

ユーザーはこの選択肢から1つ選んでアキネーターに回答するわけですが、その回答を行うメソッドが step です。パラメータとして上述の回答選択肢のインデックス番号を指定します。例えば「はい」と答える場合は「はい」は配列の最初(0番目)の選択肢なので、パラメータに 0 を指定して実行します:
await aki.step( 0 );  //. 「はい」

「いいえ」の場合は同様にパラメータに 1 を指定して実行します:
await aki.step( 1 );  //. 「いいえ」

step メソッド実行後、aki インスタンスの currentStep, question, progress の各プロパティ値が更新されます(answers プロパティは固定)。回答後にステップ数がインクリメントされ、新しい確信度と新しい質問が取得できるようになります。

start メソッド実行後の、アキネーターとしての基本的な挙動は、この
  • aki.question の質問をして、
  • aki.answers から選んだ選択肢(のインデックス番号)を aki.step() に与えて回答する
を繰り返していくことになります。

これを繰り返している中で、アキネーターは勝手に推測をやめることはありません。どこかで「もう充分」と判断して推測をやめる、という決断をする必要があり、その判断基準やロジックは API 利用者に委ねられています。つまり決断タイミングや基準をプログラマーが決める必要があります。例えば「アキネーターの確信度が 70% を超えたら推測を終了する」のようなルールを決める必要があります(実際に動かしてみた上での感想ですが、実際のアキネーターはもう少し慎重に 70% よりは上の確信度を推測終了のしきい値にしているように思えます)。

アキネーターの推測を終了する場合は win メソッドを実行します:
await aki.win();

win メソッドを実行すると step メソッドは実行できなくなります。また win メソッド実行直後に answers プロパティが変化し、回答の選択肢ではなく、この時点までに推測していた人の上位数名を示す配列値に変わります:

(win メソッド実行後のインスタンスのプロパティ)
プロパティ内容
answers 推測結果の配列
[
  {
    id: "nnnn",
    name: "1番確率が高いと推測した人の名前",
    description: "推測した人の職業など",
    absolute_picture_path: "推測した人の画像URL",
    proba: "この人が答の確率",
      :
  },
  {
    id: "nnnn",
    name: "2番目に確率が高いと推測した人の名前",
    description: "推測した人の職業など",
    absolute_picture_path: "推測した人の画像URL",
    proba: "この人が答の確率",
      :
  },
    :
]


win メソッド実行後の answers プロパティには答えの候補と考えられた数名の名前が含まれているので、(一般的には)配列の最初の要素の人の名前や画像を表示して「この人ですか?」と確認することになります。

ここまででアキネーターとしての挙動は完了します。もし同じインスタンス変数(aki)を使って、このまま別の人の推測を繰り返す場合は、再度インスタンスの初期化をしてから同じ流れを繰り返すことになります:
aki = new Aki( 'jp' );  //. 再初期化
await aki.start();  //. 再スタート

  :

【アキネーター API をウェブで使う場合の工夫】

アキネーター API そのものの使い方は上述のようになります。いくつかのメソッドとプロパティを使うだけでアキネーターが実装できるようになっていて、とても便利です。

ただ、この API をウェブで(不特定多数の複数人で)使おうとすると少し工夫が必要です。理由はこのインスタンス自体がセッションのような情報を持っていて、ユーザー間で共有するわけにはいかない※、という事情があるからです。

※具体的にはこんな感じです。Aさんが初期化して、ある程度進めた所で B さんが初期化してしまうと A さんのセッションがおかしくなってしまいます。また B さんもある程度進めた所でまた C さんが初期化してしまったり、セッションがおかしくなったことに気づかない A さんが B さんのセッションも更に進めてしまったりして、整合性が保てなくなってしまうことが考えられます。

したがってアキネーター API をウェブで使う場合はユーザー毎にアキネーター API インスタンスを用意する必要があります。ということはユーザーを識別する必要もでてきます。ログインを必須にするなどすればここは可能ですが、より気楽にユーザー登録なしに実行できるようにするにはクッキーを併用するなど、ちと面倒そうな工夫も裏で必要になってしまいます(そこまでの機能は API 側には実装されていなさそうです)。


【サンプル】
・・・と、この工夫に踏み込んだところまで含めてソースコードを解説すると本来のアキネーター API の説明の範囲ではなくなってしまう上にちと面倒な内容も含むので、実際に動くサンプルを参照し、必要に応じてそのソースコードを観ていただく形とさせていただきます。

実際に動くサンプルアプリはこちらです(ランプ魔神の画像はいらすとや様から拝借しました):
https://myakinator.mybluemix.net/

2020122501

2020122502
(本当は5位の本仮屋ユイカさんのつもりだった・・・)


わざと確信度が 70% を超えたタイミングで(本物のアキネーターと比べて、確信度が深まる前の段階で)win メソッドを実行して回答候補を表示するようにしています。なので本物のアキネーターと比べてズバリ当たる確率は低いのですが、同時に回答候補となった人の一覧も表示するようにしていて、その中には正解が含まれることが多いような印象を持っています(本物はもう少し確信度が高くなってから回答候補を出しているのだと推測しています)。

で、このサンプルアプリのソースコードがこちらです:
https://github.com/dotnsf/myakinator

2020122503


ユーザーごとにランダムな ID を生成し、その ID をクッキーで管理しつつ、アキネーター API のインスタンスもユーザー毎に生成して管理することで上述の問題点を回避しています。まあ興味ある方はソースコードを覗いてみてください。