voxeo ccxml voicexml 笔记
voxeo开发文档
https://evolution.voxeo.com/docs/quickStart.jsp
w3c voicexml2.0规范 http://www.w3.org/TR/voicexml20/
ccxml和voicexml的区别
VoiceXML
VXML可以理解为另外一种表示语言,类似于HTML和WML。它是一种表述对话(dialog)的语言,用来控制业务过程中的人机交互过程,适用于面向电话、手机等终端设备的语音应用,例如自动客户服务、自助查询系统、个人消息系统等。
将VoiceXML与HTML对比,就能很容易理解了。浏览器解释后,HTML表示的内容是以文字图像方式显示在屏幕上的,VoiceXML的内容是以语言的方式播放给用户的。HTML接收用户的文字输入和鼠标点击,VoiceXML接受用户的语音输入,进行语音识别,或者是通过电话按键输入DTMF数据。
VoiceXML是一种独立的语言,不能内嵌到现有的web语言中(如HTML,WML)。
VoiceXML--语音扩展描述语言是由AT&T、IBM、Lucent Technologies、以及Motorola通过W3C协会于2000年联合推出的电话语音应用系统标准,是为语音应用制订的基于XML的语音可扩展标记语言。有了VoiceXML,互联网信息从此能够以语音的方式流向公用电话网,从而使互联网服务得以延伸到电话用户。VoiceXML彻底改变了传统的CTI(计算机电话集成系统) 的开发模式和应用范围,使公用电话网、语音处理技术、以及互联网有机地结合为一体,架起了电话用户与Web对话的桥梁。
VoiceXML的目的是用来控制语音方式的人机交互过程的,因而它缺乏对呼叫的控制能力。例如会议、呼叫控制、建立呼叫、拒绝呼叫等等都无法实现。CCXML
针对VoiceXML在呼叫控制方面的不足,CCXML补充了这部分的功能。它能够发起呼叫、过滤和路由进入的呼叫、处理外部的异步事件。并且它能支持多方会议,可以将VoiceXML实例作为参与者加入会议,并控制VoiceXML的执行和停止。与VoiceXML类似,由CCXML浏览器负责解释执行CCXML文档。
CCXML可以独立使用,但很多情况下是与VoiceXML配合使用,CCXML控制整个呼叫模型,VoiceXML控制每个呼叫中的用户交互。单纯的VoiceXML是无法实现电话QQ之类的涉及到多方通话的业务的,利用CCXML就有可能实现了。
参见:http://hi.baidu.com/ttlove%B6%A1%B6%A1/blog/item/c6c184c4a71350aa8326ac56.html
还有一个CallXML应该是voxeo自己开发的。
disconnect与exit不同,前者更适合挂断电话,后者交由voice引擎处理,在voxeo平台上就不发事件(但是会触发dialog.exit)。
The <disconnect>
element will throw a 'connection.disconnect.hangup' event, while the <exit>
element will NOT throw this event.
与tropo不同,这里需要自己在voicexml中catch这个事件来做出进一步处理。
<?xml version="1.0" encoding="UTF-8"?> <vxml version = "2.1"> <catch event="connection.disconnect.hangup"> <submit next="MyCleanupPage.jsp" namelist="var1 var2"/> <exit/> </catch> <form> <field name="dummy"> <property name="timeout" value="1s"/> <!-- set the 'noinput' timeout to 1 second --> <prompt> Thanks for calling, you may now hang up. </prompt> <!-- create a 'garbage' grammar that will never, EVER get a match --> <grammar> [(kathy bates is really hot)] </grammar> <filled> <prompt> no way this will ever happen, babycakes. </prompt> </filled> <noinput> <disconnect/> </noinput> </field> </form> </vxml>
用户挂断事件触发顺序:
ccxml 中connection.disconnected事件
↓
当前voicexml中的connection.disconnect.hangup事件
↓
ccxml 中dialog.exit事件
上面的顺序我还不能确定,从多次测试日志来看,都是先ccxml的再dialog的disconnect,但是我看日志似乎并不能保证顺序,但是dialogexit绝对是在最后触发。
voicexml使用disconnect元素事件触发顺序:
当前voicexml中的connection.disconnect.hangup事件(事件中无论是否使用exit都会退出当前dialog)
↓
ccxml 中dialog.exit事件
注意到上面没有触发ccxml的connection.disconnected事件
电话也没真正的挂断,所以需要在ccxml 中dialog.exit事件中使用ccxml的<disconnect />来挂断当前电话。
当时用了<disconnect />时再会触发ccxml的connection.disconnected事件。
对外呼叫,需要另外申请token:http://www.vxml.org/t_15.htm
开始呼叫请求参数:
session.calledid=999xxxxx //这可以认为是appid,见http://www.vxml.org/t_7.htm
session.virtualplatform=Staging
session.parentsessionid=3eb50832ca7a2104fec96deb811eeb22
session.sessionid=6949a5eb396caf6d2f250a870dea5e50 //
session.accountid=12345678
session.callerid=abc //呼叫方id,对于skype呼叫是skype账号,对于电话则是电话号码,竟然不带区号(+1)?
对于record元素,以下为例:
<?xml version="1.0" encoding="UTF-8"?> <vxml version="2.1"> <script> <![CDATA[ function hasSlot() { return undefined != application.lastresult$ && undefined != application.lastresult$.interpretation; } ]]> </script> <var name="input" /> <var name="record" /> <form id="F1"> <!-- ***************************************************************** --> <!-- for purposes of simplicity, we will use the built-in boolean type --> <!-- note that no grammar construct is needed --> <!-- ***************************************************************** --> <record name="input_record" beep="true" maxtime="10s" finalsilence="4000ms" type="audio/x-wav"> <prompt> say yes or no here to test the built in boolean grammar </prompt> <grammar type="application/grammar+xml" root="MAIN" xml:lang="en-us"> <rule id="MAIN"> <one-of> <item> yes <tag>out.slot = "yes"</tag> </item> <item> no <item repeat="0-1">nothing</item> <tag>out.who = out + "no"</tag> </item> </one-of> </rule> </grammar> <filled> <if cond="hasSlot()"> <assign name="input" expr="application.lastresult$.interpretation" /> <else /> <assign name="input" expr="'NO_MATCH'" /> </if> <assign name="record" expr="input_record" /> <goto next="#frmGO" /> </filled> <noinput> <assign name="input" expr="'NO_INPUT'" /> <goto next="#frmGO" /> </noinput> <nomatch> <assign name="input" expr="'NO_MATCH'" /> <assign name="record" expr="input_record" /> <goto next="#frmGO" /> </nomatch> </record> </form> <form id="frmGO"> <block> <submit namelist="input record" method="post" next="index.vxml" /> </block> </form> </vxml>
这个例子里包含了几个知识点:
1: ECMAScript
2:record包含grammar与识别赋值
3:grxml语法
4:识别结果赋值与影子变量
5:多个form,form提交与跳转
6:变量声明
下面逐一讲解:
1: ECMAScript
voicexml浏览器下的脚本,与javascript不是等同的。上例中我定义了个函数hasSlot,来判断是否存在被语法识别的结果。使用CDATA避免转移,如果直接用<if>元素判断则要将&&转变为&& 注意javascript种常见的!!application.lastresult$写法行不通,不能进行类型转换。
2:record包含grammar与识别赋值
一般grammar是放在field标签下,那么grammar的识别结果是赋值到了field属性的name变量中的,record下可以包含grammar但是record的name属性是保存录音的,当form提交时如果包含了record的name的值,则会以multipart/form-data方式提交,将record流包含在请求中。那么此时grammar的值就需要通过application级变量来获得。
变量作用域 参见http://www.3ucs.com/3ucs/VoiceXML2_0/VoiceXML5.1.htm
上例中就是使用了 application.lastresult$[i].interpretation这个ECMAScript变量,它包含了这次识别的语义解释,详见http://www.w3.org/TR/voicexml20/#dml3.1.5
3:grxml语法 4:识别结果赋值与影子变量
srgs语法输出匹配 参见http://www.w3.org/TR/speech-grammar/
上例中使用的是srgs的grxml形式,其规范很多,针对上例,gxml中在用户说yes时会复制out.slot属性为yes,而当用户说no时会赋值out.who为 out + “no”,这里out指代输出,slot和who使我自定义的slot属性,用户说yes时提交参数input就会是input.slot = yes,说no时提交参数就会是input.who=no,看到没,我只要求提交input 参数,但是voicexml引擎却将input作为一个数据对象,添加了识别时grxml添加的属性值。
如果不用tag,形如:
<item> yes
</item>
<item> no <item repeat="0-1">nothing</item>
</item>
那么用户说yes则就提交input=yes,用户说no就提交input=no。
参见http://www.3ucs.com/3ucs/VoiceXML2_0/VoiceXML3.6.htm
5:多个form ,form提交与跳转
一个文档可有多个form但是提交还需要用submit,form的id可作为跳转标记,使用goto标签。
一个form中的field变量别的form是不可见的,所以使用var标签申明document级别变量一边传递值。
6:变量声明
尽量不要变量重名,下级变量会覆盖上级变量。
在Tropo中有signal的概念,但是voicexml是做不到这点的,要实现signal,还要ccxml的配合:
http://docs.voxeo.com/ccxml/1.0-final/frame.jsp?page=sendevent_ccxml10.htm
http://docs.voxeo.com/ccxml/1.0-final/frame.jsp?page=ccxml10_passtovxml.htm
ccxml无法向voicexml发送事件,而一旦使用dialogstart开始voicexml过程,每个voicexml dialog的goto,submit等标签提交会继续执行提交返回的下一个dialog,除非使用exit来交还控制给ccxml。所以如果signal在每次voicexml提交后都有新的自定义事件怎么处理?
可通过http请求设置当前ccxml的变量表明当前的事件
定义两个自定义事件:
setcur_event:此事件将当前会话的signal列表存储在变量中。
trigger_event:此事件将传入的参数如上面设置的当前signal变量类表比对,如果匹配则触发对应的事件。
在voxeo平台上我还遇到这么个问题,如下代码:
test.vxml
<?xml version="1.0" encoding="UTF-8"?> <vxml version = "2.1"> <meta name="author" content="Matthew Henry"/> <meta name="copyright" content="2005 voxeo corporation"/> <meta name="maintainer" content="YOUR_EMAIL@HERE.COM"/> <form id="F1"> <block name="B_1"> <prompt> preparing to go to another page.</prompt> <goto next="test.vxml" maxage="5000" maxstale="5000" fetchtimeout="10s"/> </block> </form> </vxml>
可以看到,我想让其说一句话后继续跳到本页面test.vxml。结果是什么也听不到,一直循环跳转,我试过submit,audio,一个form跳到另一个form,都是这样,最后才搞清楚原来这种情况下,voxeo会预加载next中的页面,那么如果next和本页面相同就导致了循环儿什么都不执行。这应该是voxeo的bug。
它这平台太脆弱了,如果在event="error.*"事件中出错的话,就导致了递归错误,弄得debugger一直刷,我即使把程序删掉了还是这样,亏得它的服务器不垮。
ccxml中的EMCscript:
打日志:
ccxmllog
在event="error.*"事件中,可获得error相关信息:
event$.name 错误名称
event$.reason 错误原因
<block><prompt>结构最好放在field 中或是record中,否则voxeo直接忽略。
当使用ccxml dialogterminate结构中断当前dailog时,会触发当前dialog中的connection.disconnect.hangup事件。这不知是个bug还是什么,愚蠢之极。
fetchtimeout 需要和maxage,maxstale配合使用才起效果,而且建议以毫秒为单位
<goto next="#form1" fetchtimeout="100000" maxage="100000" maxstale="20000"/>
只允许dtmf模式:
根节点下设置 <property name="inputmodes" value="dtmf" />