14 フォーム

formはVoiceXMLドキュメントの主要な要素である。

formは以下のものを含む。

  1. フォーム項目の集合。フォーム処理アルゴリズムのメインループで訪れる要素である。フォーム項目はフィールド項目とコントロール項目がある。フィールド項目は、フォームのフィールド項目変数を定義する。コントロール項目はフィールドの集合の制御を助ける。
  2. field項目以外の変数の宣言。
  3. イベントハンドラ。
  4. "filled"アクション。フィールド項目の特定の組合せに値が入ると実行される手続き型論理のブロックである。

formの属性:

id formの名前
scope formの文法のデフォルトでの有効範囲。属性値がdialogであれば、formの文法はそのフォーム内でのみアクティブである。属性値がdocumentであれば、formは同じドキュメント内の全ての対話の間でアクティブである。属性値がdocumentで、そのドキュメントがアプリケーションルートドキュメントなら、formの文法はこのアプリケーションの全てのドキュメント内の全ての対話の間でアクティブである。dialogをscopeの属性値として持つformの文法はそのフォームだけでアクティブである。

この節ではformのいくつかの概念について述べ、それからそれらの処理の詳細な例をあげる。

14.1 6.1. フォーム処理

formは暗黙のフォーム処理アルゴリズム(FIA)により処理される。FIAのメインループではフォーム項目を選択して訪れることを繰り返す。選択されるフォーム項目は、ガード条件が満たされていない最初の項目である。フィールド項目のデフォルトのガード条件は、フィールド項目変数が値を持っていること、である。すなわちフィールド項目しか含まない単純なフォームであれば、ユーザは各フィールド項目について、順々に入力が促される。

フォーム項目の処理は一般的には次のようなものである:

  1. 1つまたはそれ以上のプロンプトを選択し再生する。
  2. ユーザ入力を収集する。
  3. 新たに値を埋められたフィールドに属する<filled>アクションを処理する。

移動のための制御命令を処理したときにはFIAは終了する。(例:<goto>による他の対話かドキュメントへの遷移、または<submit>によるドキュメントサーバへのデータの提供)またFIAは選択すべきフォーム項目が残っていないとき暗黙の<exit>により終了する。

14.2 フォーム項目

フォームのフォーム項目はフォーム処理アルゴリズムのメインループ内で訪れられることのできる要素である。フィールド項目は指定されたフィールドの値の収集をFIAに指示する。FIAがコントロール項目を選択した場合、コントロール項目は実行可能な手続き型コードのブロックを含むか、混合主導型フォーム用の初期プロンプトを出力し、値の収集処理を初期化することをFIAに伝える。

14.2.1 フィールド項目

フィールド項目はユーザから値を集めるためにフィールド項目変数を指定する。フィールド項目はユーザが何を言うべきか、または何をキー入力するべきかを教えるprompt、入力文を定義するgrammar、そして結果として生じるイベントを処理するイベントハンドラを持つ。フィールド項目はフィールド項目変数に値が代入された直後に行うべき行動を定義する<filled>要素を持つ。フィールド項目は次のものに細分される。

<field> 音声認識またはDTMF文法によって値を得るフィールド項目。
<record> ユーザにより録音されたオーディオクリップを値に持つフィールド項目。例えば、<record>要素はボイスメールメッセージを収集することができる。
<transfer> ユーザを別の電話番号に転送するフィールド項目。もしその転送がコントロールを返してきたときは、フィールド変数はresult statusにセットされる。
<object> このフィールド項目はさまざまなパラメータを持ったプラットフォーム依存"object"を呼び出す。プラットフォームオブジェクトの結果は、1つまたは複数のプロパティを伴うECMAScriptのオブジェクトである。あるプラットフォームオブジェクトは、クレジットカードの情報を収集する組み込み対話であるかもしれない。または、特有のDTMFテキスト入力方法を使用したテキストメッセージの収集を行うものかもしれない。
<subdialog> <subdialog>フィールド項目は関数呼び出しのようなものである。処理中のページ上の他の対話や他のVoiceXMLドキュメントを呼び出す。その結果としてECMAScriptのオブジェクトを返す。

14.2.2 コントロール項目

以下の2種類のコントロール項目が存在する。

<block> プロンプトや計算に用いられる一連の手続き文であり、入力を集めるためには用いられない。実装命令文の集合。(非入力用) blockは処理される直前にtrueにセットされる(通常は暗黙の)フォーム項目変数を持っている。
<initial> この要素は、混合主導型form内の最初の相互作用を制御する。そのプロンプトはフォームレベル文法に合致するユーザの発話を促すように記述されるべきである。<initial>要素の実行中に、少なくとも1つのフィールド項目変数が認識結果で満たされたとき、<initial>のフォーム項目変数はtrueになり、したがってFIAの選択肢から消去される。

14.3 フォーム項目変数とコンディション

それぞれのフォーム項目は関連したフォーム項目変数を持ち、formの処理に入ったときは、フィールド項目のフォーム項目変数はまた、フィールド項目変数とも呼ばれ、ユーザから入力された値を持つ。デフォルトでundefinedにセットされている。フォーム項目変数はフォーム項目の処理結果を値として持つ。フォーム項目変数はname属性を使用して名前が与えられるか、もしくは匿名のままにして、内部で生成した名前を持たせることができる。

それぞれのフォーム項目はフォーム処理アルゴリズムにより選択されるフォーム項目を制御するガード条件を持っている。デフォルトのガード条件はフォーム項目変数が値を持つかどうかを調べるものである。もし持ってるなら、そのフォーム項目は処理されない。

典型的には、フィールド項目は名前が与えられ、コントロール項目には与えられない。普通フォーム項目変数は初期値を与えられておらず、追加のガード条件も指定されていない。しかし時には、より詳細な制御をする必要がある。あるフォームはフィールドを隠すために値のセットされたフォーム項目変数を持っていて、その後フィールドによる入力を行うためにクリアされる。(例:<clear>の使用)他のフィールドはそのフィールドが満たされておらず、別の2つのフィールドが満たされた場合にのみアクティブになるガード条件を持っている。block項目は条件がtrueに保たれているときのみ実行することができる。従って、どの順序でフォーム項目を選択し実行するかに関して、FIAは詳細な制御が可能である。しかし概して、多くの対話はこのレベルの複雑さに頼ることなく、形成することができる。

要約すると、すべてのフォーム項目は次のような属性を持つ。

name dialogスコープのフォーム項目変数の名前でフォーム項目の値を保持する。
expr フォーム項目変数の初期値。デフォルトはECMAScriptのundefinedである。もしある値で初期化されると、フォーム項目変数はクリアされていない限り、フォーム項目は実行されない。
cond 項目変数のテストを行うために評価する表現。もし存在しない場合、デフォルトはtrueであり、<initial>の場合は、いずれかのフィールド項目変数に値が埋められているかどうかのテストとなる。

14.4 指示的フォーム

最も単純で一般的な種類のフォームは、コンピュータによる指示的な相互作用を実現するためにフォーム項目が出現順に各々一度だけ実行されるものである。

以下にそのようなフォームを用いた天気情報サービスの例を示す。

 
     <form id="weather_info">
      <block> 天気情報サービスにようこそ。 </block>
      <field name="pref">
       <prompt> 何県ですか? </prompt>
       <grammar src="pref.gram" type="application/x-jsgf"/>
       <catch event="help">
        天気を知りたい都道府県名を言ってください。
       </catch>
      </field>
      <field name="city">
       <prompt> 何市ですか? </prompt>
       <grammar src="city.gram" type="application/x-jsgf"/>
       <catch event="help">
        天気を知りたい都市の名前を言ってください。
       </catch>
      </field>
      <block>
       <submit next="/servlet/weather" namelist="pref city"/>
      </block>
     </form>
 

対話は以下のように逐次的に進行する:

C(computer):天気情報サービスへようこそ。何県ですか?
H(human):ヘルプ
C:天気を知りたい都道府県名を言ってください。
H:京都府
C:何市ですか?
H:向日市
C:理解できませんでした。何市ですか?
H:京都市
C:午前11時の京都市の天気は快晴です...

最初の(暗黙の)フォーム項目変数の初期値はundefinedであるから、フォーム処理アルゴリズムの最初の繰り返しは1番目のblockを選択する。このblockはメインプロンプトを出力し、そのフォーム項目変数がtrueにセットされる。FIAの2番目の繰り返しで、最初のblockはフォーム項目変数が定義されているのでスキップされ、対話変数"pref"はundefinedであるので、"pref"フィールドが選択される。このフィールドはユーザにpref用の入力を促し、応答を変数"pref"にセットする。3回目のフォームの繰り返しは"city"フィールドの入力を促し、値を集める。4回目の繰り返しは最後のblockを実行し、別のURIに遷移する。

この例のそれぞれのフィールドは、返答を引き出すためのプロンプト、何を聞き取るか特定する文法、ヘルプイベント用のイベントハンドラを持っている。ヘルプイベントはユーザが補助を求めたときに投げ掛けられる。ヘルプイベントハンドラはこれらのイベントをキャッチし、さらに詳しいヒントを与える。

以下に指示的フォームの2つ目の例、クレジットカード情報入力を示す:

      <form id="get_card_info">
      <block>
       クレジットカードの種類、ナンバー、有効期限を入力します。
      </block>

      <field name="card_type">
       <prompt count="1"> クレジットカードの種類を入力してください。 </prompt>
       <prompt count="2"> カードの種類は? </prompt>

       <!-- this is an in line grammar -->
       <grammar>
          visa               {visa}
        | master [card]      {mastercard}
        | amex               {amex}
        | american [express] {amex}
       </grammar>
       <help>
        Visa、Mastercard、American Expressのうちどれかをお持ちですか?
       </help>
      </field>

      <!-- type="digits"のgrammarが構築される -->
      <field name="card_num" type="digits">
       <prompt count="1"> カード番号を入力してください。 </prompt>
       <prompt count="2"> カードの番号は? </prompt>
       <catch event="help">
        <if cond="card_type == 'amex'">
         15桁の番号を音声かキーで入力して下さい。
        <else/>
         16桁の番号を音声かキーで入力して下さい。
        </if>
       </catch>
       <filled>
        <if cond="card_type == 'amex' && card_num.length != 15">
         American Expressのカード番号は15桁です。
         <clear namelist="card_num"/>
         <throw event="nomatch"/>
        <elseif cond="card_type" != 'amex' && card_num.length !="16"/>
         Mastercard と Visaのカード番号は16桁です。
         <clear namelist="card_num"/>
         <throw event="nomatch"/>
        </if>
       </filled>
      </field>

      <field name="expiry_date" type="digits">
       <prompt count="1"> 有効期限を入力してください。 </prompt>
       <prompt count="2"> 有効期限はいつですか? </prompt>
       <help>
        1201のように有効期限を音声またはキーで入力してください。
       </help>
       <filled>
        <!-- validate the mmyy -->
        <var name="mm"/>
        <var name="i" expr="expiry_date.length"/>
        <if cond="i == 3">
         <assign name="mm" expr="expiry_date.substring(0,1)"/>
        <elseif cond="i == 4"/>
         <assign name="mm" expr="expiry_date.substring(0,2)"/>
        </if>
        <if cond="mm == '' || mm < 1 || mm > 12">
         <clear namelist="expiry_date"/>
         <throw event="nomatch"/>
        </if>
       </filled>
      </field>

      <field name="confirm" type="boolean">
       <prompt>
        カードの種類 <value expr="card_type"/>
        番号 <value expr="card_num"/>
        有効期限 <value expr="expiry_date"/> です。
        正しければ 「はい」 再入力するときは「いいえ」と言ってください。
       </prompt>
       <filled>
        <if cond="confirm">
         <submit next="place_order.asp"
          namelist="card_type card_num expiry_date"/>
        </if>
        <clear namelist="card_type card_num expiry_date acknowledge"/>
       </filled>
      </field>
     </form>

対話はこのように進められる:

C:クレジットカードの種類、ナンバー、有効期限を入力します。
C:クレジットカードの種類を入力してください。
H:Discover
C:理解できませんでした。 (プラットフォームに依存する初期設定メッセージ)
C:カードの種類は? (2つめのpromptが使用されている)
H:シュート (幸運にもこのプラットフォームにより"ヘルプ"として扱われた)
C:Visa、Mastercard、American Expressのうちどれかをお持ちですか?
H:えー、Amex このプラットフォームは「えー」を無視する)
C:カード番号を入力してください。
H:1 2 3 4 ....ちょっと待って...
C:理解できませんでした。
C:カード番号は?
H:123456789012345# (DTMFを使用)
C:有効期限を入力してください。
H:1 2 0 1
C:カードの種類 Amex 番号 1234567890123456、満期の日は 1201 です。
正しければ 「はい」 再入力するときは「いいえ」と言ってください。
H:はい。

フィールドはformの主要な構成要素である。フィールドは変数を宣言し、プロンプト、文法、DTMFシーケンス、ヘルプメッセージ、そしてその他のイベントハンドラを特定する。それぞれのフィールドはそのformの対話スコープのVoiceXML field項目変数を宣言する。これらは一度formが満たされると、submitされたり、他の変数にコピーされる。

独自の音声入力文法かつ/またはDTMF文法を持ち、それぞれは<grammar>、<dtmf>を使って明示的に指示されるか、またはtype属性を用いて暗黙に指定される。type属性は標準組み込み文法(例:数字、真偽、番号など)を使用するために用いられる。type属性はまた、音声合成器がフィールドの値をどのように読むかを制御する。

それぞれのフィールドは1つまたはそれ以上のプロンプトを持つことができる。プロンプトを1つ持つ場合は、値が提供されるまで繰り返し用いられる。複数の場合、count属性を与えなければならない。これらはそれぞれの試行において使用するかを決定する。例ではプロンプトはだんだん短くなっている。これを先細リプロンプト(tapered prompting)と呼ぶ。

<catch event="help">要素は、ユーザがヘルプを要求した際に何をするかを定義したイベントハンドラである。ヘルプメッセージを次第に細くすることができる。

この要素は略記が可能であり、以下の2つの要素は等価である:

 
      <catch event="help">
       Visa、Mastercard、American Expressのうちどれかをお持ちですか?
      </catch>

      <help>
       Visa、Mastercard、American Expressのうちどれかをお持ちですか?
      </help>
 

<filled>要素は、ユーザがそのフィールドへの入力を提供したときに、何をするかを定義している。1つの用法としては、上記の例のデータフィールドにおいて、文法によりなされたチェックに加え、整合性制約を指定することである。

14.5 混合主導型フォーム

前の節ではコンピュータ主導の会話を実現するフォームについて詳しく述べた。フォームをコンピュータと人間のどちらでも会話の主導権をとれる混合主導型にするために、1つまたは複数の<initial>フォーム項目と1つまたは複数のフォームレベル文法が必要である。

もしフォームがフォームレベル文法を持っていると:

  1. フィールドは任意の順番で満たすことができる。
  2. 1つのユーザ発話で1つ以上のフィールドが満たされる。

また、フォーム文法はユーザが他の対話にいるときにアクティブにすることができる。ドキュメントが例えばレンタカーフォームとホテル予約フォームのように2つのフォームを持っており、両方のフォームがそのドキュメントにおいて、アクティブである文法を持つと、ユーザはホテル予約情報入力の要求に対してレンタカーの情報を応答し、それによってコンピュータをレンタカーの話題へと導くことができる。ユーザは任意のアクティブな文法に対する入力ができ、フィールドに値をセットし、その応答行動をさせることができる。

例:以下に混合主導型を示した、天気予報サービスのversion 2を挙げる。広告そして都道府県と都市の確認とを持った実用目的の"付加価値バージョン"である。

      <form id="weather_info">
      <grammar src="cityandpref.gram" type="application/x-jsfg"/>

      <!-- 広告に割り込むことはできない -->
      <block>
       <prompt bargein="false">
        天気情報サービスへようこそ
        <audio src="http://www.online-ads.example/wis.wav"/>
       </prompt>
      </block>

      <initial name="start">
       <prompt> どの県、都市の天気情報を希望しますか? </prompt>
       <help> 天気情報を希望する県と都市の名前を言って下さい。 </help>
       <!-- もしユーザが黙り込むと1度ヒントを与え、startに再度移行する -->
       <noinput count="1"> <reprompt/> </noinput>
       <noinput count="1">
        <reprompt/> <assign name="start" expr="true"/>
       </noinput>
      </initial>

      <field name="pref">
       <prompt> 何県ですか? </prompt>
       <help> 天気情報を希望する都道府県名を言って下さい。 </help>
      </field>

      <field name="city">
       <prompt>
        天気情報を希望する <value expr="pref"> 内の都市名を言って下さい。
       </prompt>
       <help>
        天気情報を希望する<value expr="pref">内の都市名を言って下さい。
       </help>
       <filled>
        <!-- 客の大半は京都市にいる -->
        <if cond="city == '京都市' && pref == undefined">
         <assign name="pref" expr="'京都府'"/>
        </if>
       </filled>
      </field>

      <field name="go_ahead" type="boolean" modal="true">
       <prompt>
        <value expr="pref"/>,<value expr="city"/> でよろしいですか?
       </prompt>
       <filled>
        <if cond="go_ahead">
         <prompt bargein="false">
          <audio src="http://www.online-ads.example/wis2.wav"/>
         </prompt>
        </if>
        <clear namelist="start city pref go_ahead"/>
       </filled>
      </field>
     </form>

以下に初心者用でも使いやすくなった点を示した例をあげる:

C:天気予報情報サービスへようこそ。
ジョーのスパイシーシュリンプソースをよろしく。
C:天気情報を希望する県と都市の名前を言って下さい。
H:えー、京都府。
C:何市ですか?
H:舞鶴市。
C:京都府舞鶴市でよろしいですか?
H:いいえ。
C:何県ですか?
H:京都市。
C:京都府京都市でよろしいですか?
H:はい。
C:ジョーのスパイシーシュリンプソースを買うのを忘れないでね。
C:おおむね晴れが続き、夜半から下り坂です。

"go_ahead"フィールドは、trueにセットされたmodel属性を持つ。これは現在のフォーム項目内で定義されたもの以外のすべての文法を無効にするので、このフィールドでの唯一のアクティブな文法は"boolean"の組み込み文法である。

慣れたユーザはより早く情報を得ることができる。(しかし広告を飛ばすことはできない)

C:天気予報情報サービスへようこそ。
ジョーのスパイシーシュリンプソースをよろしく。
C:天気情報を..
H:京都市。
C:京都府京..
H:はい。
C:ジョーのスパイシーシュリンプソースを買うのを忘れないでね。
C:おおむね晴れが続き、夜半から下り坂です。...

フィールドの値の収集の順序制御

フォーム処理アルゴリズムはいくつかの方法でカスタマイズ可能である。1つの方法はそのフォーム項目が選択されないようにフォーム項目変数に値を割り当てる。別の方法としては、<clear>を使い、フォーム項目変数をundefinedにすることにより、強制的にFIAを再度フォーム項目に戻すという方法である。

他の方式は、<goto nextitem>を使い、次のフィールド項目を明示的に指定する方法である。これは、強制的にそのフィールド項目にただちに遷移する。もし<goto nextitem>が<filled>アクションの中で出現したとき、<filled>アクションの残りの部分と実行途中の<filled>アクションはスキップされる。

以下にexitイベントに応えて実行される<goto nextitem>の例を示す。

 
     <form id="survey_2000_03_30">
      <catch event="exit">
       <goto nextitem="confirm_exit"/>
      </catch>
      <block>
       <prompt>
        もしもし、あなたは米国外交政策に重要な質問に答えるために無作為に選ばれました。
       </prompt>
      </block>
      <field name="q1" type="boolean">
       <prompt>
        Burkina Fasoの農務省の機能を民営化するIMFに同意しますか?
       </prompt>
      </field>
      <field name="q2" type="boolean">
       <prompt>
        もし民営化するとOugadougouとBobo-Dioulassoに有益になるでしょうか?
       </prompt>
      </field>
      <field name="q3" type="boolean">
        その結果モロコシとあわの出荷が1年当たり最大4%ほど増加するかもしれない のに同意するのですか?
       </prompt>
      </field>
      <block>
       <submit next="register" namelist="q1 q2 q3"/>
      </block>
      <field name="confirm_exit" type="boolean">
       <prompt>
        exitを選びました。数十年間、米国外交政策のvis-a-vis sub-Saharan
        Africaに悪影響を及ぼしたいと確信しているのでしょうか?
       </prompt>
       <filled>
        <if cond="confirm_exit">
         分かりました。しかし米国国務省を怒らせます。
         <exit/>
        <else>
         よろしいです。出ていきましょう。
         <clear namelist="confirm_exit"/>
        </if>
       </filled>
      </field>
     </form>
 

もしユーザがある質問に対して"exit"と言うと、exitイベントがプラットフォームにより投げ掛けられ、<catch>イベントハンドラによりキャッチされる。このハンドラは"confirm_exit"が次に訪れるフィールドであると指定する。"confirm_exit"フィールドはアンケートが普通に終了していた場合はアクセスされることはない。なぜなら先の<block>要素が制御を登録スクリプトに移すからである。

14.6 フォーム処理アルゴリズム

フォーム処理アルゴリズム(FIA)について概念レベルでの説明は終了した。このセクションでは、フォーム処理アルゴリズムについて詳述する。

14.6.1 初期化フェーズ

formの初期化は処理に入ったときに行われる。フォームの対話スコープのプロンプトカウンタ内部変数は1にリセットされる。各変数(フォームレベルの<var>要素とフォーム項目変数)は、undefinedまたはexpr属性の値に初期化される。

14.6.2 メインループ

FIAのメインループは3つのフェーズからなる。

  1. Selectフェーズ:次に訪れられるフォーム項目が選択される。
  2. Collectフェーズ:次の満たされていないフォーム項目を訪れ、ユーザに入力を促し、適切な文法を動作可能にし、待機して、入力(発話もしくはDTMFキー入力)またはイベント(ヘルプ、未入力タイムアウト)を待機して収集する。
  3. Processフェーズ:入力は、フォーム項目を満たし、入力妥当性検証のようなアクションの為の<filled>要素の実行により処理される。イベントはそのイベントの型に適したイベントハンドラの実行により処理される。

Selectフェーズ

遷移するための次のフォーム項目選択が目的である。

  1. もし直前のメインループの実行における処理フェーズの<goto>によって$<goto nextitem>$が特定されれば、その項目が選択される。
  2. そうでなければ、ガードコンディションがfalseである最初のフォーム項目が選択される。
  3. もしどのガードコンディションもfalseでなければ、直前の実行は制御の明示的な遷移に遭遇せずに、フォームの実行を完了しているので、FIAは暗黙の<exit>オペレーションを実行する。

Collectフェーズ

Collectフェーズの目的は入力かイベントの収集である。選択されたフォーム項目は参照され、フォーム項目の種類による行動を実行する。

  1. もしfield項目が参照されるとFIAはfield項目のプロンプトカウンタとプロンプトコンディションに基礎をおくいくつかのプロンプトを選択し、待ち行列する。それから、フィールドレベル文法と高レベル文法を傾聴し、文法認識かいくつかのイベントを待つ。
  2. <initial>が参照されると、FIAは<initial>のプロンプトカウンタとプロンプトコンディションに基礎をおくプロンプトを選択し待ち行列する。それから、それはフォームレベル文法と高レベル文法を傾聴し、文法認識かいくつかのイベントを待つ。
  3. <block>要素は、そのフォーム項目変数がtrueにセットされ、そのコンテンツを評価、processフェーズを無視することにより参照される。未入力時間が続いていると判断すると、次のFIAメインループへ遷移する。

Processフェーズ

Processフェーズの目的は、Collectフェーズで収集された入力かイベントの処理である。

  1. イベントが発生すると、適切なcatch要素が検証され、実行される。これはFIAを終了する原因になるか、FIAが次のループに遷移する原因になる。
  2. 入力が<link>からの文法に合致すれば、そのリンクの遷移が実行されるか、そのイベントが投げ掛けられる。もし<link>がイベントを投げ掛けると、その要素はカレントフォーム項目の文脈内で処理される。
  3. もし入力がカレントフォーム以外のフォームで合致すると、FIAは終了し、その他のフォームは初期化され、そしてそのフォームのFIAはそのprocessフェーズ内で入力とともに始められる。
  4. もしフォーム内で文法が合致すると、以下のことが起こる。
    1. 入力の文法スロット変数はフィールド項目変数と同値が割り当てられる。
    2. <filled>動作はこれらの割り当てにより引金が引かれる。(15章参照)
    3. 特定された<filled>動作はドキュメントオーダ内で実行される。もし<goto>または<throw>に遭遇すると、残りの<filled>要素は実行されず、FIAは次のメインループ内で終了するか継続する。

Processフェーズの終了後、処理はSelectフェーズに戻ることにより継続する。より詳細なフォーム処理アルゴリズムは付録Cを参照のこと。