基礎構造 |
概念 全ての embryo はSSTPサーバとしての機能を持ちます。SSTPサーバはSSTPクライアントと一定のプロトコルで通信を行い、リクエストに従って様々な動作をします。この構造によりどのようなプログラムでも embryo をイベント等の「表現体」として使ったり、あるいは偽AIに適切な情報を与えることで偽AIのポテンシャルをより強く引き出したりすることが可能になります。 このサービスはOSに依存しない形で行われるため、可能性はローカルマシン内で閉じません。インターネット等を経由して別のマシンのクライアントから別のマシンの embryo にSSTPパケットを送付することや、webサーバからクライアントマシンに対してSSTPパケットを送信すること等も可能です。 |
動作 embryo はポート9801番をリスニングします。クライアントはこのポートに接続し、SSTPプロトコルでパケットを送付します。送付後、リクエストが正しいか否かに関わらず約2秒以内にステータスコードが返り、リクエストが正しければ embryo はパケットからデータを取り出し適切な動作をします。 1つのサーバに接続できるクライアントは1つだけで、既にクライアントがいる場合 Conflict が返ります。通信は極めて短時間で終了することを前提としており、終了しなかった場合は Request Timeout で強制切断されます。 |
SSTPプロトコル仕様 |
文字コード リクエスト本体と基本的なヘッダ(DBCS が必要ないヘッダ)は ASCII コードのみで構成されます。 Sender/Script/Document/Songname/Sentence 等 ASCII コードだけでは実用にならないヘッダ内の文字列には以下のキャラクターセットおよびエンコードが使用できます。 ASCII Shift_JIS ISO-2022-JP EUC-JP UTF-8 これらは明示的に指定しなくてはなりません。 指定は以下のようにして行います。 SEND SSTP/1.1 Sender: カードキャプター Script: \0\s0汝のあるべき姿に戻れ。\e Option: nodescript,notranslate Charset: UTF-8 [EOD] このように記述すると Sender および Script ヘッダは UTF-8 として解釈され、embryo 到着時点で内部コード(環境依存コード)に変換されます。 Charset に ASCII を指定した場合、送信された文字列はいかなる変換も行われません。 |
NOTIFY/1.0 例 NOTIFY SSTP/1.0 Sender: さくら Event: OnMusicPlay Reference0: 元祖高木ブー伝説 Reference1: 筋肉少女帯 Charset: Shift_JIS [EOD] NOTIFY/1.0 は汎用的なイベント通知を行うためのリクエストです。NOTIFY で渡されたデータは SSTP サーバを介して SHIORI/2.2 リクエストとして SHOIRI に到達し、SHIORI はイベントに対する反応を行います。 NOTIFY を用いる SSTP クライアントを作成するにあたり、プログラマは SHIORI がどのような仕組みでイベントに反応を行うのかを必ずしも知っておく必要はありません。しかし知っておいた方が全体の構造を理解しやすくなります。 →SHIORI/2.2 仕様書
イベント識別子は SHIORI がイベントの種類を判断する際に使用されます。命名規則は特にありませんが、簡潔かつユニークなネーミングを心がけて下さい。 参照情報ヘッダにはイベントを正しく解釈する上で必要な背景情報を記入します。例えば上記の例では音楽再生開始イベント OnMusicPlay に曲名とアーティスト名が参照情報として与えられています。このような配慮を行うことで SHIORI はより的確な反応を返すことができます。 |
NOTIFY/1.1 例 NOTIFY SSTP/1.1 Sender: さくら Event: OnMusicPlay Reference0: 元祖高木ブー伝説 Reference1: 筋肉少女帯 IfGhost: なる,ゆうか Script: \h\s0‥‥\w8\w8高木ブーだね。\u\s0‥‥\e IfGhost: さくら,うにゅう Script: \h\s0‥‥\w8\w8高木ブーだね。\u\s0‥‥\w8\w8むう。\e Charset: Shift_JIS [EOD] NOTIFY/1.1 は保険反応付きイベント通知を行うためのリクエストです。基本的な動作は NOTIFY/1.0 と同じですが、SHIORI が当該イベントを解釈しなかったとき(無反応だったとき)は Script ヘッダで識別されるスクリプトが表示されます。 NOTIFY/1.1 は NOTIFY/1.0 と SEND/1.0 〜 SEND/1.4 の利点全てを併せ持ち、固有の SHIORI の存在とも矛盾しない完全なイベント通知リクエストです。現状イベント通知のために SEND を使用している全ての SSTP クライアントは SEND リクエストの送信を止め、全てのイベントに独自の識別子を与え、それを NOFITY/1.1 として送信しなくてはなりません。また今後新たに作成される SSTP クライアントはイベント通知の際 必ず NOTIFY リクエストを使用しなくてはなりません。 NOTIFY/1.1 の仕様は NOTIFY/1.0 と SEND/1.0 〜 SEND/1.4 の仕様そのままです。各リクエスト固有のヘッダについてはそれぞれの仕様書を参照して下さい。 |
SEND/1.1 SEND SSTP/1.1 Sender: カードキャプター Script: \h\s0汝のあるべき姿に戻れ。\e Option: nodescript,notranslate Charset: Shift_JIS [EOD] SENDリクエストはembryoスクリプト送信だけを目的としたリクエストです。Script ヘッダで送られたデータがそのままスクリプトとして解釈され、セリフとして発言されます。 改行コードは CR+LF です。最後に空行を送ると通信終了となり、サーバ側からステータスコードと、必要があれば追加データが返ります。追加データはSJISで返ります。 各ヘッダの意味は以下の通りです。
Sender および Script は必須です。どちらが欠けても Bad Request になります。残りのヘッダは Optional です。 *
Option ヘッダで使用できるコマンドは以下の通りです。
nodescript オプションを使用すると SSTP マーカーを表示せずにスクリプトを表示することができます。ただしこのスイッチはローカルマシンからのリクエストのみ有効です。外からの送信に対してはこのスイッチの設定に関わらず常にSSTP マーカーが表示されます。 notranslate オプションを設定すると当該スクリプトはトランスレートせずに表示されます。 |
SEND/1.2 SEND SSTP/1.2 Sender: カードキャプター Script: \h\s0どんな感じ?\n\n\q0[#temp0][まあまあ]\q1[#temp1][今ひとつ]\z Entry: #temp0,\h\s0ふーん。\e Entry: #temp1,\h\s0酒に逃げるなヨ!\e Charset: Shift_JIS [EOD] SEND/1.2 は選択肢インターフェースを実現するための仕様です。上記の例では embryo は2分岐の選択肢を表示し、「まあまあ」を選ぶとサーバ上で「ふーん」というセリフが発言され、同時にクライアントには「まあまあ」という戻り値が返ります。 戻り値はSJISで返ります。 サーバ上で選択肢が選択されるまで双方の処理は基本的にブロックされます。このケースではSSTPの2秒ルールは適用されず、返値が得られるまでサーバは応答を返しません。正しく選択肢が選ばれた場合は 200 および返値が、タイムアウトした場合は 204 が、SSTPブレイクを受けた場合は 210 が返ります。 追加された各ヘッダの意味は以下の通りです。
Entryヘッダで送られたエントリはそのセッション1回きりの一時的なスクリプトとしてサーバに格納されます。embryoスクリプトにおけるエントリとスクリプトの関係はスクリプトリファレンスを参照して下さい。一時スクリプトはセッション中完全なエントリとして機能しますが、セッション終了次第全て破棄されます。権限は SSTP と同レベルで危険なタグは通りません。 |
SEND/1.3 SEND SSTP/1.3 Sender: カードキャプター HWnd: 1024 Script: \h\s0どんな感じ?\n\n\q0[#temp0][まあまあ]\q1[#temp1][今ひとつ]\z Entry: #temp0,\m[1025,0,0]\h\s0ふーん。\m[1025,0,1]\e Entry: #temp1,\m[1025,1,0]\h\s0酒に逃げるなヨ!\m[1025,1,1]\e Charset: Shift_JIS [EOD] SEND/1.3 はウインドウメッセージングによりクライアントが embryo の動作状況を精細に把握するための仕様です。Windowsという固有のOSに傾倒した内容となっており、やや異色な仕様です。 上記の例では embryo は2分岐の選択肢を表示し、「まあまあ」を選ぶとサーバ上で「ふーん」というセリフが発言され、その表示が終了すると同時に postmessage(1024,1025,0,0) が実行されます。同様に「今ひとつ」を選択すると postmessage(1024,1025,1,0) が実行されます。このような動作によりクライアントはembryoスクリプトデコーダが今どこを読んでいるのかを100%完全に知ることができます。 追加された各ヘッダの意味は以下の通りです。
HWndヘッダは以降の \m タグのメッセージ送付を受けるウインドウハンドルです。適切なウインドウプロシージャを持つハンドルを指定して下さい。 |
SEND/1.4 SEND SSTP/1.4 Sender: カードキャプター IfGhost: さくら,うにゅう Script: \h\s0さくらだー。\w8\n\n%j[#mainblock] IfGhost: せりこ,まるちい Script: \h\s0せりこだー。\w8\n\n%j[#mainblock] IfGhost: さくら,ケロ Script: \u\s0わいのはモダン焼きにしてや〜。\w8\h\s0はいはい。\e Entry: #mainblock,\s7寝言は寝てから言えっ!\w8\u\s0落ち着けっ!\e Charset: Shift_JIS [EOD] SEND/1.4 は固有のゴーストに最適化されたシナリオを送信するための仕様です。 上記の例では、ゴーストがさくらだった場合は「さくらだー」、せりこだった場合は「せりこだー」と発言し、その後両方に共通する本文が続きます。さくらでもせりこでもなかった場合、そのゴーストが allowembryo ==1 ならさくら&うにゅうのセリフを喋り(従来通りの動作)、allowembryo==0 なら Refuse が返ります(ゴーストがセリフを拒否)。SSTP/1.4 はこの構造により「SSTP でゴーストのイメージを壊される」という問題に対して選択肢を提示しています。 allowembryo 設定についてはシェルの文書を参照して下さい。 追加された各ヘッダの意味は以下の通りです。
IfGhost ヘッダは [name],[name] の形式で指定し、1つ目が本体側、2つ目がうにゅう側の名前です。これは大文字小文字などを含め完全に一致する必要があります。 |
EXECUTE/1.0 例 EXECUTE SSTP/1.0 Sender: サンプルプログラム Command: GetName Charset: Shift_JIS [EOD] EXECUTEリクエストは(主に出力を伴わない)汎用的なコマンド実行を目的としたリクエストです。 各ヘッダの意味は以下の通りです。
この2つは必須です。どちらが欠けても Bad Request になります。 改行コードは CR+LF です。最後に空行を送ると通信終了となり、サーバ側からステータスコードと、必要があれば追加データが返ります。追加データはSJISで返ります。 Commandヘッダで使用できるコマンドは以下の通りです。
getnameコマンドを送ると現在動作中のキャラクタの名前が返ります。クライアントはこのデータを取得することで現在サーバが「誰」なのか知ることができ、キャラクタによってセリフの内容を変えたり、あるいは送信を中止したりすることができます。 実装されていないコマンドを出すと Not Implemented が返ります。 |
EXECUTE/1.1 例 EXECUTE SSTP/1.1 Sender: カードキャプター Command: SetCookie[visitcount,1] Charset: Shift_JIS [EOD] EXECUTE SSTP/1.1 Sender: カードキャプター Command: GetCookie[visitcount] Charset: Shift_JIS [EOD] EXECUTE/1.1 は長い寿命を持つ変数の保持を目的としたリクエストです。SSTP はこれをいわゆる Cookie 動作によって実現します。 追加ヘッダはありません。 追加されたコマンドは以下の通りです。
適切な引数を設定して SetCookie を送るとそのデータがサーバに格納されます。格納したデータは GetCookie で引き出すことができます。 格納されたデータは書いたクライアント本人にしか読み出せないことに注意して下さい。例えば A というサーバが格納した visitcount というデータを B というサーバが読み出そうとしても失敗します。これはセキュリティ保持と変数名バッティング回避の2つの目的があります。 Get しようとしたデータが存在しない場合はヌルではなく No Content が帰ります。 |
EXECUTE/1.2 例 EXECUTE SSTP/1.2 Sender: カードキャプター Command: GetVersion Charset: Shift_JIS [EOD] EXECUTE/1.2 は本体バージョン識別を目的としたリクエストです。 追加ヘッダはありません。 追加されたコマンドは以下の通りです。
GetVersion を指示すると、SSTP サーバはバージョンを識別できる文字列を返します。それは例えば以下のようなものです。 "inverse" 17.00 |
EXECUTE/1.3 例 EXECUTE SSTP/1.3 Sender: カードキャプター Command: Quiet Charset: Shift_JIS [EOD] EXECUTE/1.3 は本体を静かにさせるリクエストです。 追加ヘッダはありません。 追加されたコマンドは以下の通りです。
Quiet を指示するとサーバはしばらくの間自分の意志で喋らなくなります。つまり、連続した SEND を送ることが分かっている場合(人格が大きく SSTP クライアント側に移る場合)、最初に Quiet を指示することでその連続的発言を邪魔されないようにできます。 Quiet セッションは Restore が指示されるか、あるいはリクエストなしで約16秒間放置することで解除されます。 サーバに Quiet を指示できるのは、現段階ではサーバと同一の IP を持つクライアントだけです。 |
GIVE/1.0 例 GIVE SSTP/1.1 Sender: カードキャプター Document: こんにちはさくらです。闇の力を秘めし鍵よ真の姿を我の前に示せレリーズ。汝のあるべき姿に戻れクロウカード。 Charset: Shift_JIS [EOD] GIVEリクエストは embryo にデータを与え、それを処理させたり、あるいは反応させたりするリクエストです。 このリクエストは現段階ではローカルマシンからのリクエストに限ります(外から来たものは蹴ります)。 各ヘッダの意味は以下の通りです。
Senderといずれかのソースデータ、この2つは必須です。どちらが欠けても Bad Request になります。ソースデータが複数ある場合はいずれか1つが優先されます。 改行コードは CR+LF です。最後に空行を送ると通信終了となり、サーバ側からステータスコードが返ります。 ソースデータの種類およびそれに対するサーバの動作は以下の通りです。
Documentは文章データです。 embryo は送られたデータから自分が理解できる単語を拾い、それを以降の会話に生かします。送ったデータが正しく学習されているかどうかはAIステートダイアログで確認できます。 Songnameは曲名です。 embryo は送られた曲名をsongデータベースと照合し、ヒットすればその曲についての何らかの発言をします(songオーバーライド仕様書を参照して下さい)。この処理はリクエスト受理後即座に行われます。 送る文字の文字コードは問いません。html等の書式記号が含まれたものでも構いません。全てサーバ側で対応します。 |
COMMUNICATE/1.1 例 COMMUNICATE SSTP/1.1 Sender: カードキャプター Sentence: 今日は寒いなー。 Option: substitute Charset: Shift_JIS [EOD] COMMUNICATEリクエストは偽AIに質問や同意を求める文章などを送り、それに答えさせ、必要があれば返答を受け取るリクエストです。主にユーザとの対話や別の人工知能との会話での使用を想定しています。 このリクエストは現段階ではローカルマシンからのリクエストに限ります(外から来たものは蹴ります)。 各ヘッダの意味は以下の通りです。
Sender と Sentence は必須です。どちらが欠けても Bad Request になります。 改行コードは CR+LF です。最後に空行を送ると通信終了となり、サーバ側からステータスコードと、必要があれば追加データが返ります。 送る文字の文字コードは問いません。サーバ側で対応します。 *
Option ヘッダで使用できるコマンドは以下の通りです。
substitute オプションを使用すると Sentence 文字列をうにゅうがセリフとして発言します。この構造により、セリフの発言能力がないクライアントが COMMUNICATE リクエストを利用する際、うにゅうをセリフの代理発言者として利用することができます。 |
COMMUNICATE/1.2 例 COMMUNICATE SSTP/1.2 Sender: 双葉 HWnd: 0 Sentence: \0\s0どうも。\e Surface: 0,10 Reference0: N/A Charset: Shift_JIS [EOD] COMMUNICATE/1.2 は 2サーバ間での対話を行うための仕様です。この仕様は SHIORI/2.3 の存在と極めて深い関係があります。このリクエストは SSTP サーバでかつ SHIORI サーバでもあるサーバプログラム(例えば embryo)以外が行ってはなりません。 SHIORI/2.3 仕様書 embryo は COMMUNICATE/1.2 リクエストに対する SHIORI の返答を Port もしくは HWnd ヘッダから得られる送信元サーバ(話しかけたサーバ)に送り返します。この構造により 2サーバは事実上の会話を行います。 このリクエストは現段階ではローカルマシンからのリクエストに限ります。
|
ステータスコード 2xx - 処理完了
4xx - リクエストエラー
5xx - サーバエラー
|
レギュレーション 通信はコネクト終了後サーバ側のローカルタイムで最長でも2秒以内に終了する必要があります。2秒で終わらなかった場合は Request Timeout が返り強制切断されます。 リクエストヘッダの最大長は2KB=2048バイト(ローカルマシンからは16KB=16384バイト)に制限されています。これより長いヘッダを送ると Bad Request となり直ちに切断されます。 |
サンプルコード |
※注: 以下はあくまでもアルゴリズムを理解するための単純なサンプルコードであり、実用には耐えません。欠陥があります。 Perl sub sendsstp { use Socket; $script=$_[0]; $sender='カードキャプター'; $port=9801; $addr=$ENV{'HTTP_X_FORWARDED_FOR'}; if (index($addr,'.')==-1) { $addr = $ENV{'REMOTE_ADDR'}; } $proto = getprotobyname('tcp'); socket(S, PF_INET, SOCK_STREAM, $proto); $ent = sockaddr_in($port, inet_aton($addr)); connect(S, $ent) || die; select(S); $| = 1; select(STDOUT); print S "SEND SSTP/1.1\r\n"; print S "Sender: $sender\r\n"; print S "Script: $script\\e\r\n"; print S "\r\n"; $result = <S>; while (<S>) { print; } close(S); return($result); } * $s=sendsstp('\0\s0‥‥\e'); * 戻り値は必ずしも必要ないので、 <img src="http://..../sstp.cgi" width=1 height=1> こういう手もあります。 |
embryo の存在をローコストに確認する方法 |
MUTEX MUTEX "sakura" の存在をチェックすることで確実に存在を判定できます。メモリオブジェクトの文書を参照して下さい。 |
戻る |