世无英雄
--评新太、高阳IVR平台

新太的IVR语音平台在业内赫赫有名,号称占领SP-IVR的半壁江山,其脚本之难写早有耳闻。而最近竟然发现,高阳的脚本语言和新太脚本语言如出一辙,以高阳在金融和通信领域的实力,我断难相信,他们会失去最基本的判断力,采用这种如此原始,形同汇编的语言。

我一直坚信,写程序是一种愉悦的享受,作为语音IVR应用开发也是如此,开发者应该得到最好的工具,他们在使用高级语言的经验应该得到尊重。是什么力量能够让他们返回到汇编时代?也许是市场的力量。我只能借用三国人物的一声叹息了:

世无英雄,遂使竖子成名。

好了,看看我的具体点评吧。

(由于高阳脚本和新太脚本非常相似,以下统称为新太脚本或新太脚本语言)


一. 最低级的语言,汇编特性

让我们看看这样的代码:

[ASSIGN SR1,"I AM "]
[ASSIGN SR2,"A BOY."]
[STRCAT SR1,SR2]
[PRINTREGISTER 2] //SR1="I AM A BOY.",SR2="A BOY."

这和汇编语言有什么两样?如果采用高级语言,就是这个意思:

s = "I AM " + "A BOY";

汇编语言比较底层,编程效率较低,几乎每个操作都要借用寄存器。

用汇编语言写几十句,敲几百个字符,在高级语言里面只要一两行。

行数多了,还增加出错的概率,也增加了调试的难度。

来看看我曾经多次引用的,Ruby语言的设计者日本人Yukihiro Matsumoto的一段话:

"基于图灵机的完整性理论,每一种具备图灵完整性的语言能做的事情理论上都可以由另一种具备图灵完整性的语言所代替,只是花费的代价不同。您可以用汇编语言实现所有的功能,但是现在已经没有人再想用汇编编程序了。"

("Ruby的设计思想"-《程序员杂志》2003.12)


二. GOTO语句

任何一个学过最基础计算机理论的人都知道,GOTO语句是高级程序设计之大忌,它使程序流程任意转向,缺乏结构化。

新太和高阳的 -> 符号就是goto语句,它指向一个标号。

有人会说,C语言甚至最新的C#也有goto语句啊,事实是,你在C、C#中不用一句goto语句也能够写出好的应用程序,在新太的脚本里面可以吗?不可以,其 -> 无所不在。这除了和这种语言的汇编特性有关外,还和它依赖于“状态机”的实现方式有关,在本文的后续部分我还会阐述。

此外,最简单、最直观的循环,如:

for(i=0; i<10; i++) { ... }

在新太脚本语言里,也只能用 -> 来实现。


三. 冷冰冰的寄存器

在新太脚本里面,几乎每个操作都必须使用寄存器,如:SR1, SR2, SR3等等。在高级语言里面已经被变量所取代。

寄存器和变量相比较,最大的问题是寄存器没法命名,如比较:

在高级语言里面: isTimeOut = true; // 不用注释,望文即能生义

在新太脚本里面: [ASSIGN SR1,1] // 看不出含义

高下立现。

即使如此,新太的寄存器变量还是有很多限制,如数量的限制,最多只能多少个寄存器,如果构造大型项目则比较麻烦。此外,如果是字符串类型,还有长度限制,如最多只能256个字节,要拼接一个复杂查询的SQL语句就没办法了。


四. 缺乏表达能力的语法

编程序如同写文章,要是我们还用商周时代的甲骨文,凭那点可怜的词汇和语法,是无法写出结构清晰、段落分明的锦绣文章,甚至无法描述复杂的事物。

新太脚本在语法上没有任何现代高级语言的特性,比如:等号赋值,多个字符串相加,直观的循环语句、条件判断,复杂表达式,数组,形式参数等等。这种编程真是一个痛苦的体验。

新太脚本也提供了最基本的封装,比如函数,问题是其函数和我们任何高级语言的编程经验不相符,那就是没有形式参数,没有局部变量,一切都还要借助那几十个寄存器。

没有现代高级语言的结构,代码平铺直叙,显得没有层次,我看什么代码缩进,变量命名风格,在新太脚本里面完全可以不要。你需要的只是函数命名风格,标号命名风格。

正因为如此,我觉得如果单凭该脚本语言本身,试图构造大型复杂的IVR应用是困难的。如果你发现某项功能没有,往往你只能借助于所谓的外部网关,或者干脆等待新太的升级。这也是新太推出某项新的业务时,往往要扩充一批库函数的原因。


五. 状态机, 我们真的需要吗?

在某些情况下状态机是一个很好的概念,也是很简单的概念。概括地说就是把流程划分为多个状态,每个状态下将会执行某个动作(也可能是一组动作),根据该动作的结果决定流程走向下一个状态。

很显然,新太脚本的标号表示状态, 如 :label ;

而转移语句如 -> label 表示状态的迁移。动作就是其系统函数。

理论和实践都已经证明,当状态较少,或者是基于控制的系统,使用状态机比较有效,如著名的电梯控制。当状态很多,比如复杂的IVR应用,分支较多,看起来就像一团乱麻了,你数数复杂的新太脚本有多少个标号就明白了。

实际上,状态机的实现很容易,我曾经和一个业内朋友开玩笑说,我可以花1到2周写出一个新太的脚本引擎,相信有底层编程经验的朋友会同意我的看法。

这种把“状态机”呈现给用户的模式,必然陷入类似汇编语言的困境,脚本很难编写也很难阅读。如果以用户为中心的话--这里所说的用户当然是指IVR脚本的开发者,应该反过来,把复杂的东西留给平台去做,而给用户一个简单的开发环境。

状态机的实现模式还和语音板卡编程的历史有关,早期的板卡和操作系统都是基于单线程模式的,多条通道同时执行,系统在每个时刻必须记录通道的当前状态,使用状态机是合适的。

看看东进或三汇语音板卡的例子程序就明白了,都是一个个状态机。

多线程操作系统的出现,改变了这种状况,我们完全可以不用状态机的概念来实现一个语音平台,呈现给用户的脚本也可以像高级语言一样简单、明白,而且脚本可以绑定在单一的通道上。


六. 图形化, 最后一根不能救命的稻草

也许新太公司也意识到其脚本语言太低级,学习门槛较高,所以在近年也推出了所谓的图形化拖拉环境。

对于图形化拖拉环境,笔者以前曾经撰文评论,可参见:

http://www.bluespace.com.cn/koodoo/article_ctilan.htm

我现在的观点依然不变,简而言之:

  1. 1. 图形界面试图以不懂电脑的最终用户为对象,是错误的,IVR的开发者往往都是程序员;
  2. 2. 当流程复杂时,往往线如乱麻;
  3. 3. 当超过一幅图时,很难直接从图上看清楚它们之间的关系;
  4. 4. 图没有标准,各个厂家的大不相同,同样很难学习。
  5. 5. 现代的高级语言,其语法结构类似,如学习C语言的经验很容易转换到Pascal上;但图形拖拉界面则没有什么相同的经验可以借鉴。和高级语言不同,图形拖拉界面缺少坚实的理论基础;

而且,新太的图形拖拉界面仅仅是其脚本语言的图形化,基本上一个节点对应一条语句,脚本里面的goto语句就是‘->’符号变成了线条,标号变成了节点的命名,完全继承了脚本语言的弊端,前面指出的脚本语言的问题在图形拖拉界面下依然存在,甚至还多出一些问题。

实际上,我认为图形化不应该只是底层脚本语言的简单映射,只有业务的抽象和图形化才有意义,类似工作流,每个节点应该完成一个封装的业务,很遗憾,现在没有厂家提供这样的产品。(这个想法还不是很成熟,也许我应该实践一下,做出这样的产品,呵呵。:) )

当然,国内企业市场推广的能力是很强大的,强大的营销能力也许能够一时掩盖技术的不足。问题是,重营销而轻研发,怎么能够长久?


bluesen 2005.1.8 于深圳