表达式说明(旧)
说明:自2024年6月15日起,UNIT 企业版公有云服务为提升业务系统安全性不再使用此旧版本表达式说明,请参考表达式说明(新),私有化交付项目除外。
本章介绍 UNIT企业版产品表达式配置使用规范。
表达式可以用在会话树节点配置中,节点触发条件和节点执行回复内容的配置
平台使用的表达式标识
- 无前缀:表示内置DialogState对象的属性(welcome, conversation_start, anything_else, true, false, query等)
#
:表示引用意图@
:表示引用实体&
:表示引用上下文context中的某个key{%
:模板标识前缀%}
:模板标识后缀
触发/回复条件表达式举例
Condition类型的表达式必须合法,且在创建此类表达式时需保证引用的意图或实体已存在,其求值结果只能是true/false,逻辑操作符(not, and, or, !, &&, ||
)的操作数的类型必须为String、Integer、Boolean、null。如:
welcome
(首轮会话,且用户输入的query为空时,结果为true)conversation_start
(首轮会话,则为true)anything_else
(query非空时,结果为true)not query
(用户输入的文本数据为空时,结果为true)not @sys_date
(sys_开头的表示系统实体,若命中,因取反,故结果为false)@car_type
(若命中自定义实体car_type,则结果为true)&channel
(context中包含key: channel,且对应的value非空,则为true)#get_weather
(自定义意图识别,若命中此意图,则为true)&name == "baidu"
(若context中,name对应的value为字符串"baidu",则为true)@location?.indexOf('北京') > 0
(建议使用?.
)&month and &month >= 1
(若context中month对应的value非空且大于等于1,则为true){12,24,36}.contains(&month)
(若context中month对应的value值为12,24,36中一个,则为true)
Context 对象及包含的表达式举例
context为json对象(key-value结构),其value为表达式字符串;在进行求值时,正常返回各子表达式结果用于填充context各value,若因对象不存在或解析异常,则原样返回表达式串,如:
{"ques":"@ques","quenum":"12"}
(对于数字,直接写在json串的双引号中即可){"user":"'张女士'"}
(对于常量字符串,需显式用单引号'
或者\"
括起来){"date":"@sys_date"}
(sys_date为系统实体,系统实体见2.4节){"month":"@sys_number"}
{"sex":"@sex"}
{"car":"@car_type"}
{"location":"{% @location %}"}
{"date":"entities['sys_date'][1].value"}
(entities为map,按key取值;entities['sys_date']结果为List,故按下标取其中第2个元素;entities['sys_date']``[1]
结果为RecognizedEntity对象,使用.value
获取其value
属性的值){"entity":"entities.toString()"}
(输出整个map对象)- entities['sys_date'].![value] ,遍历所有实体值
回复内容表达式
Output 为字符串表达式,其所有需要解析的表达式必须用模板标识包含起来,表达式用法同上;在进行求值时,若出现不能解析的情形,原样返回表达式串,如:
intent={% &intent %}, entitySize={% &entities?.size() %}
(为更好可读性,{%
之后,%}
之前各加一个空格)
不支持及特殊Case
==
操作符的使用,在对实体值进行==
判断时,须保证左右操作数为字符串,如:(若@month
实体值为"8")"@month == '8'"
的值为true,而"@month == 8"
的值为false。其它比较操作符>,>=,<,<=
不受此约束。
常用配置举例
日期常用表达式
- 获取当前年:
T(java.util.Calendar).getInstance().get(T(java.util.Calendar).YEAR)
- 获取当前月:
T(java.util.Calendar).getInstance().get(T(java.util.Calendar).MONTH)+1
- 获取当前日:
T(java.util.Calendar).getInstance().get(T(java.util.Calendar).DAY_OF_MONTH)
- 获取指定的年:
T(Integer).parseInt(@sys_date.substring(0,4))
或者@sys_year
- 获取指定的月:
T(Integer).parseInt(@sys_date.substring(5,7))
或者@sys_month
- 获取指定的日:
T(Integer).parseInt(@sys_date.substring(8,10))
- 指定日期的7天后:
new java.text.SimpleDateFormat("yyyy-MM-dd").format(new java.util.Date(new java.text.SimpleDateFormat("yyyy-MM-dd").parse(@sys_date).getTime()+7 * 24 * 60 * 60 * 1000))
- 判断用户输入的时间是否为6个月前:
(new java.util.Date().getTime() - new java.text.SimpleDateFormat("yyyy-MM-dd").parse(@sys_date).getTime()) / 1000 >= 6 * 30 * 24 * 3600
- 将@sys_date的日期格式(yyyy-MM-dd)转化为更友好的,如:yyyy年MM月dd日
new java.text.SimpleDateFormat("yyyy年MM月dd日").format(new java.text.SimpleDateFormat("yyyy-MM-dd").parse(@sys_date))
注:其它格式只需要自己修改表达式中的yyyy年MM月dd日
即可 - 将@sys_month的日期格式(yyyy-MM)转化为更友好的,如:yyyy年MM月
new java.text.SimpleDateFormat("yyyy年MM月").format(new java.text.SimpleDateFormat("yyyy-MM").parse(@sys_month))
注:同例9,其它格式只需要自己修改表达式中的yyyy年MM月
即可;另外,格式上也支持时间,例如HH时mm分ss秒
- 获取当前日期
new java.text.SimpleDateFormat("yyyy-MM-dd").format(new java.util.Date())
;其中"yyyy-MM-dd"可替换成自己需要的格式,同上
获取实体的值及原始值
@entity
返回实体entity
的识别值;举例:用户query为:”我的户号是123456789012345678“,表达式@sys_number
的求值结果为1.23456789012345678E18
@entity.original
返回实体entity
的原始值;举例:用户query为:“我的户号是123456789012345678”,表达式@sys_number.original
的求值结果为123456789012345678
,该表达式在收集原始数字时经常用到。
数字运算&计数
@sys_number
加1:T(Integer).parseInt(@sys_number) + 1
- 在会话树中进行计数:可以使用变量在会话过程中进行计数,在会话树变量赋值依次配置:
number
、表达式 、&number ? &number+1 : 1
,表示对number
变量进行计数累加,初始值为1 - 会话树节点访问计数:
sys_counter
为系统内置变量名,其可自动对会话过程中节点访问次数进行计数,使用方法:sys_counter['会话树节点名称']
,该表达式返回指定定节点的访问次数。 - 获取@sys_number实体识别到的原始数值(以0开头的数字除外):
@sys_number.indexOf('E') > -1 ? @sys_number.substring(0, @sys_number.indexOf('E')).replace(".", "") : @sys_number
- 获取@sys_number识别到的数字的长度(以0开头的数字除外)
@sys_number.indexOf('E') > -1 ? T(Integer).parseInt(@sys_number.substring(@sys_number.indexOf('E') + 1))+1 : @sys_number.length()
- 货币实体sys_money默认返回的value单位为“分”,为了获取“元”,表达式为:
T(Float).parseFloat(@sys_money) / 100
(需要确保sys_money实体存在) - 数字格式转换,如想将某个变量xxx(数值),保留2位小数:
new java.text.DecimalFormat("0.00").format(new Double(&xxx))
- 数字格式转换,如想将@sys_number的结果,保留2位小数:
new java.text.DecimalFormat("0.00").format(T(Double).parseDouble(@sys_number))
多实体值收集
UNIT企业版支持多实体值收集,可以通过entities['实体名']获取实体对象列表,下面以sys_number实体进行举例,其它实体类似:
- 收集用户query中的所有数值,如收集“我可以接受的价格范围是1000到2000元”中的所有数值,可以配置表达式:
entities['sys_number'].![value]
进行收集,该表达式的值为:[1000, 2000] - 若要收集用户query中原始的数字(如用户query中包含的是身份证号,直接使用1的办法,可能收集到的是精度丢失的科学计数法;或者数字前的0可能被丢弃),可以配置表达式:
entities['sys_number'].![original]
,如用户query为“我的编号是01234和123456789”,该表达式的值为:[01234, 123456789]
节点条件及回复使用正则表达式
会话树节点条件中“高级模式”,支持使用正则表达式作为条件,语法为:query matches '正则表达式'
,注意:query必须完全命中正则表达式(从头到尾),如:
- 需要在某个节点收集户号(户号规则为10位数字),用户query为“我的户号是1234567890”,条件表达式
query matches '.*(?<!\d)\d{10}(?!\d).*'
的求值结果即为true;但表达式query matches '(?<!\d)\d{10}(?!\d)'
的求值结果为false,所以需要使用第一种表达式写法,对首尾不关注的数据使用.*
进行匹配。
query抽取
在会话树节点配置中,有些复杂的场景,需要对query进行抽取,如:正则抽取、包含于抽取等,现举例说明使用方法:
- 对query中的信息进行正则提取。如:需要从query中抽取出南航航班号,假设南航航班号规则为:
CZ+4位数字
;则提取可能包含的全部航班号的表达式为:extractQuery("CZ\d{4}")
;如用户query为:我考虑的航班号有CZ1234, CZ3091
,则上述表达式识别结果为一个列表:["CZ1234", "CZ3091"]
;如果明确包含航班号,且只想取一个,则表达式可写为:extractQuery("CZ\d{4}")[0]
。 - 从query中抽取出所有从属于某个candidate列表(或逗号分隔的字符串)的数据。如:用户query为
我说的是上海呀
,而context为{"city_list":["北京", "天津", "上海"], "cities":"北京,天津,上海"}
,则getQueryContains(&city_list)
、getQueryContains(&cities)
返回值均为["上海"]
。 - 返回query中抽取出所有从属于某个candidate列表(或逗号分隔的字符串)的数据的个数。如:用户query为
我说的是上海呀
,而context为{"city_list":["北京", "天津", "上海"], "cities":"北京,天津,上海"}
,则countQueryContains(&city_list)
、countQueryContains(&cities)
返回值均为1
。 - 判断当前query包含的实体句式是否满足当前正则表达式. 常见的用法如:
queryMatchEntity('.*我要[@car_type]吧')
, 一般用在收集实体时, 如用户query为emmmm算了,我要大型车吧
则直接召回. 使用说明:方法参数内必须包含实体引用 且只包含一个; 其他case,如多实体值提取,建议升级使用复合实体提供的实体值提取能力,
节点回复话术循环遍历
背景:
业务系统返回的数据是个列表,如查询到最近一个月的交易记录,会有多条交易信息,需要支持在会话树中根据这些多条交易信息,构造出一个用户可识别的答案,即需要表达式支持对列表进行遍历。
语法:
对函数类的表达式进行求值,格式为:<f:函数标识 input=输入表达式 output=输出模板类表达式/> 其中,
* 函数标识,支持的有:list(表示枚举)
* 输入表达式:支持所有可求值的表达式,返回类型需与函数标识一致,如:list
* 输出模板表达式:支持所有可求值的模板表达式(与回复话术一致)。
* 当函数标识为list时,内置的变量有:&list_index表示list的下标、&list_element表示list中的元素
* 如果list中每个元素为Map,则Map中key的取法为:list_key
举例:
例1. 某个dialogState.context为:
{"order_list":[{"date": "2019-09-08", "money": 100}, {"date": "2019-09-10", "money":200}]}
* 节点中回复话术配置为:
您的账单是:<f:list input=&order_list output=第{% &list_index + 1 %}笔:日期{% &list_date %}, 金额:{% &list_money %};/>
* 对应的求值结果为:
您的账单是:第1笔:日期2019-09-08, 金额:100;第2笔:日期2019-09-10, 金额:200;
例2. 某个dialogState.context为:
{"date_list":["2019-09-08", "2019-09-10"]}
* 节点中回复话术配置为:
<f:list input=&date_list output=第{% &list_index + 1 %}项:{% &list_element %},/>
* 对应的求值结果为:
第1项:2019-09-09,第2项:2019-09-10,
计算文本与query的相似度
在会话树节点的高级表达式中可以配某个文本与query的相似度大于或小于某个阈值的条件,可以用simWithQuery,此函数会计算出某个文本信息与query的相似度
举例及语法说明:
例1:在某个会话树节点的条件想配一条text文本语句:”我想租车“,跟它的相似度大于0.75 语法:simWithQuery('我想租车') > 0.75
例2:在某个会话树节点的回复话术中可以配成模板形式 语法:{% simWithQuery('我想租车') %}
高级开放内置系统变量
sys_ss_exist
, boolean类型,以变量形式存在于context中 sys_开头,用户无法修改,表示任务式对话中,当前场景栈中是否存在没有引导完成的场景. 比如:在一些流程中的对话叶子节点上用户配置了引导话术流程做场景结束的引导推荐,此时若场景栈中存在未完成的场景,dm同样也会触发主动引导,那么两种引导话术在回复上会存在缠绕关系, 因此开放系统内置高级状态以系统变量方式提供给用户,用户可以据此判断更灵活的完成复杂场景的流程配置;
基本表达式语法
请参考Spel