使用 VoiceXML 开发交互式电话调查应用程序





2.00/5 (6投票s)
VoiceXML 示例。
引言
VoiceXML 是一种用于创建语音用户界面的标记语言。它使用语音识别和/或按键(DTMF 键盘)作为输入,并使用预录音频和文本转语音合成(TTS)作为输出。它基于万维网联盟的可扩展标记语言(XML),并利用网络范式进行应用程序开发和部署。借助 VoiceXML,通过使用熟悉的网络基础设施,包括工具和 Web 服务器,语音识别应用程序的开发得到了极大的简化。
通过拥有共同的语言,应用程序开发人员、平台供应商和工具提供商都可以从代码的可移植性和重用中受益。对于此示例,我们将使用 Voicent Gateway 作为我们的 VoiceXML 服务器。可以从 此处 下载免费版网关。您应该能够将此示例移植到其他 VoiceXML 网关服务器,只需进行很少的更改。
在本文中,我们将为一家汽车服务店开发一个自动客户满意度调查应用程序。此示例应用程序将执行以下操作:
- 从带来汽车进行服务的客户列表中读取信息。
我们将随机选择客户姓名、电话号码、服务日期和汽车制造商。如果您使用数据库作为客户列表,其原理也应该相同。
- 自动呼叫这些客户并收集他们对所提供服务的评分(1-5)。
调查消息:“您好,这里是 ACME 汽车服务中心,呼叫 [客户姓名]。我们已于 [服务日期] 为您的 [汽车制造商] 汽车提供了维护服务。请您为我们的服务评分,1 到 5 分,5 分为最佳。感谢您的时间。”
由于无法从答录机中收集任何反馈,因此答录机消息是:“您好,这里是 ACME 汽车服务中心,呼叫 [客户姓名]。我们已于 [服务日期] 为您的 [汽车制造商] 汽车提供了维护服务,我们感谢您的惠顾,如果您需要进一步的帮助,请联系我们。”
- 生成一份调查报告。
我们想知道拨打了多少电话,有多少电话被答录机接听,线路是否忙,以及有多少客户对每个评分等级的服务进行了评分。
本文涵盖的主题包括:
安排外呼
要进行外呼,必须向 Voicent Gateway 发送呼叫请求。网关教程已涵盖呼叫请求处理程序的基本功能。要进行呼叫,只需向呼叫调度程序发送 HTTP POST 请求。
一旦呼叫请求与呼叫调度程序安排好,它将根据其呼叫时间被放入呼叫队列。在指定的呼叫时间,网关将进行外呼。
外呼控制流程
当系统进行外呼时,控制流程会经历以下步骤:
- 根据呼叫请求中指定的 starturl 获取起始 VXML 文件。
- 如果线路有拨号音,则拨打电话号码。
- 检测线路状态,例如无应答、线路忙、答录机、真人接听。
- 如果呼叫被答录机接听,则获取答录机的起始 VXML 文件。最初获取的起始 VXML 被丢弃。
- 在网关检测到真人接听(有人说“你好”)或检测到答录机提示音后执行起始 VXML 文件。
- 网关根据 VXML 应用程序与被呼叫者进行交互。
- 网关断开呼叫。
- 网关保存呼叫状态。
对真人接听和答录机播放不同的消息
如上一节所述,网关知道两个起始 VXML 文件,一个用于真人接听,另一个用于答录机。真人接听的起始 VXML 文件在 starturl 中定义。网关首先获取没有参数的 VXML 文件,如果呼叫被答录机接听,网关会丢弃之前获取的 VXML 文件,然后获取带有“ans=t”的 VXML 文件。
例如,如果您的 starturl 定义为 http://mydomain/myapp/start.jsp。网关将通过向定义的 URL 发送 HTTP 请求来获取起始 VXML 文件,即 http://mydomain/myapp/start.jsp。如果呼叫被答录机接听,网关将向同一 URL 发送另一个带有参数 ans=t 的 HTTP 请求,即 http://mydomain/myapp/start.jsp?ans=t。
以下示例 JSP 文件将为真人接听播放 live.wav,为答录机播放 answering.wav。WAV 文件必须位于 audio 目录下。暂时,在这两个 WAV 文件中录制任何消息。我们将随着本教程的开发添加更多功能。
<?xml version="1.0"?>
<vxml version="1.0">
<%
String ans = request.getParameter("ans");
boolean isAnsweringMachine = ("t".equals(ans));
%>
<form id="td">
<block>
<% if (isAnsweringMachine) { %>
<audio src="audio/answering.wav"/>
<% } else { %>
<audio src="audio/live.wav"/>
<% } %>
</block>
</form>
</vxml>
除了简单的应用程序,大多数语音应用程序都需要动态生成的 VXML 文件。当您的应用程序需要与网站或数据库集成时,尤其如此。
知道按下了哪个键
通过动态生成的 VXML 文件,您几乎可以随心所欲地处理您的应用程序。在此示例中,我们将从客户那里收集按键 1-5。以下是更新后的 start.jsp 文件:
<?xml version="1.0"?>
<vxml version="1.0">
<%
String ans = request.getParameter("ans");
boolean isAnsweringMachine = ("t".equals(ans));
%>
<form id="td">
<% if (isAnsweringMachine) { %>
<block>
<audio src="audio/answering.wav"/>
</block>
<% } else { %>
<field name="rating">
<prompt timeout="10s">
<block>
<audio src="audio/live.wav"/>
</block>
</prompt>
<dtmf>
1 | 2 | 3 | 4 | 5
</dtmf>
<filled>
<submit next="recordrating.jsp" namelist="rating"/>
</filled>
</field>
<% } %>
</form>
</vxml>
如您所见,按键由 VXML 文件的“rating”字段捕获。此值随后提交给名为 recordrating.jsp 的下一个 JSP 文件。
<?xml version="1.0"?>
<vxml version="1.0">
<%
String key = request.getParameter("rating");
int ratetotal = 1;
Integer RateTotal = (Integer) application.getAttribute("rate" + key);
if (RateTotal != null)
ratetotal = RateTotal.intValue() + 1;
application.setAttribute("rate" + key, new Integer(ratetotal));
%>
<form id="td">
<block>
<audio src="audio/thankyou.wav"/>
</block>
</form>
</vxml>
获取线路状态:线路忙、无应答等.
VoiceXML 中的线路状态由 VXML 异常处理。以下是异常值:
"telephone.noanswer" "telephone.noline" "telephone.linebusy" "telephone.linedrop"
您当然可以在自己的 VXML 代码中捕获这些异常并相应地处理这些异常。
<form id="td">
...
<catch event="telephone.noline">
<submit next="..." namelist="..."/>
</catch>
<catch event="telephone.linebusy">
<submit next="..." namelist="..."/>
</catch>
...
</form>
知道网关正在请求哪个自定义记录
引言部分描述的调查消息:
“您好,这里是 ACME 汽车服务中心,呼叫 [客户姓名]。我们已于 [服务日期] 为您的 [汽车制造商] 汽车提供了维护服务。请您为我们的服务评分,1 到 5 分,5 分为最佳。感谢您的时间。”
当网关呼叫一位客户时,它将通过 starturl 参数中指定的 URL 获取动态生成的 VXML 文件。当我们向网关发送呼叫请求时,我们实际上不知道网关何时会回调以获取 VXML 文件。如果我们使用的是多线系统,将存在对 starturl
的并发访问。
为了解决这个问题,例如,您可以在 starturl
字符串中嵌入客户 ID。因此,当网关回拨时,它会将客户 ID 提交回 starturl
。更新后的 start.jsp 文件如下所示:
<?xml version="1.0"?>
<vxml version="1.0">
<%
String ans = request.getParameter("ans");
boolean isAnsweringMachine = ("t".equals(ans));
String customer_name = request.getParameter("customer_name");
String car_maker = request.getParameter("car_maker");
String service_date = request.getParameter("service_date");
%>
<form id="td">
<% if (isAnsweringMachine) {
int anstotal = 1;
Integer AnsTotal = (Integer) application.getAttribute("anstotal");
if (AnsTotal != null)
anstotal = AnsTotal.intValue() + 1;
application.setAttribute("anstotal", new Integer(anstotal));
%>
<block>
<audio src="audio/acme.wav"/>
<%=customer_name%>
<audio src="audio/we_provide.wav"/>
<%=car_maker%>
<audio src="audio/maintenance.wav"/>
<%=service_date%>
<audio src="audio/thanks.wav"/>
</block>
<% } else { %>
<field name="rating">
<prompt timeout="10s">
<block>
<audio src="audio/acme.wav"/>
<%=customer_name%>
<audio src="audio/we_provide.wav"/>
<%=car_maker%>
<audio src="audio/maintenance.wav"/>
<%=service_date%>
<audio src="audio/please_press_15.wav"/>
</block>
</prompt>
<dtmf>
1 | 2 | 3 | 4 | 5
</dtmf>
<filled>
<submit next="recordrating.jsp" namelist="rating"/>
</filled>
</field>
<% } %>
</form>
</vxml>
调查起始页
现在我们已经开发了应用程序 VXML 文件,我们可以开始实现调查控件和报告功能。调查起始页如下所示:

为了简化示例,起始页只接受一个逗号分隔的电话号码列表。在服务器端,应用程序将随机分配其他必要的值,例如汽车制造商和服务日期。在真实的调查应用程序中,起始页可能会执行一些数据库查询并相应地执行操作。但与 Voicent Gateway 相关的关键功能应该完全相同。
调查报告页
点击“开始调查”按钮后,surveyHandler.jsp 会将所有呼叫发送到网关,然后返回一个调查报告页面。

当您点击“显示当前调查报告”按钮时,浏览器会向同一 surveyHandler.jsp 页面发送 POST 请求,并设置 action=report。处理程序将执行以下操作:
// check call status from the list
int callsToBeMade = 0;
int callsFailed = 0;
for (int i = 0; i < m_callRecords.size(); i++) {
CallRecord rec = (CallRecord) m_callRecords.get(i);
if (rec.m_callStatus == null) {
getCallStatus(rec);
if (rec.m_callStatus == null) {
callsToBeMade++;
continue;
}
}
if (rec.m_callStatus.equals("Call Failed"))
callsFailed++;
}
// get the rest from application vars
int answeringTotal = getRateTotal(application, "anstotal");
int rate1 = getRateTotal(application, "rate1");
int rate2 = getRateTotal(application, "rate2");
int rate3 = getRateTotal(application, "rate3");
int rate4 = getRateTotal(application, "rate4");
int rate5 = getRateTotal(application, "rate5");
有关详细信息,请查看示例的源代码。音频文件也包含在示例中。它们是使用 Voicent 自然文本转语音引擎自动生成的。
历史
- 2006 年 7 月 18 日:首次发布