IBM Cloud Docs
对话的表达式语言方法

对话的表达式语言方法

您可以处理从用户语句中提取的值,并将其引用到上下文变量、条件或响应中的其他位置。

使用表达式语法的位置

要在其他变量中扩展变量值,或应用方法输出文本或上下文变量,请使用 <? expression ?> 表达式语法。 例如:

  • 在对话节点文本响应中引用用户输入

    You said <? input.text ?>.
    
  • 通过 JSON 编辑器递增数字属性

    "output":{"number":"<? output.number + 1 ?>"}
    
  • 检查对话节点条件中是否有特定实体值

    @city.toLowerCase() == 'paris'
    
  • 检查对话节点响应条件中是否有特定日期范围

    @sys-date.after(today())
    
  • 通过上下文编辑器将元素添加到上下文变量数组

上下文变量数组
上下文变量名 上下文变量值
toppings <? context.toppings.append( 'onions' ) ?>

您还可以在对话节点条件和对话节点响应条件中使用 SpEL 表达式。

当在节点条件中使用 SpEL时,不需要使用周围的 <? ?> 语法。

以下部分描述了您可以用来处理数值的方法。 这些值按数据类型进行组织:

数组

要检查某个节点条件或响应条件中是否存在某个数组中的值,不能在设置了这些数组值的同一节点中使用以下方法进行检查。

JSONArray.addAll(JSONArray)

此方法将一个数组附加到另一个数组。

对于以下对话运行时上下文:

{
  "context": {
    "toppings_array": ["onion", "olives"],
    "more_toppings": ["mushroom","pepperoni"]
  }
}

进行以下更新:

{
  "context": {
    "toppings_array": "<? $toppings_array.addAll($more_toppings) ?>"
  }
}

结果: 方法本身返回 null。 但是,将更新第一个数组以包含第二个数组中的值。

{
  "context": {
    "toppings_array": ["onion", "olives", "mushroom", "pepperoni"]
  }
}

JSONArray.append(object)

此方法用于将新值附加到 JSONArray,并返回修改后的 JSONArray。

对于以下对话运行时上下文:

{
  "context": {
    "toppings_array": ["onion", "olives"]
  }
}

进行以下更新:

{
  "context": {
    "toppings_array": "<? $toppings_array.append('ketchup', 'tomatoes') ?>"
  }
}

结果:

{
  "context": {
    "toppings_array": ["onion", "olives", "ketchup", "tomatoes"]
  }
}

JSONArray.clear()

此方法用于清除数组中的所有值并返回 null。

在输出中使用以下表达式来定义一个字段,用于清除保存到其值的上下文变量的数组 ($toppings_array)。

{
  "output": {
    "array_eraser": "<? $toppings_array.clear() ?>"
  }
}

然后,如果您引用$toppings_array上下文变量,它只返回“[]”。

JSONArray.contains(Object value)

如果输入 JSONArray 包含输入值,那么此方法会返回 true。

对于由前一个节点或外部应用程序设置的此对话框运行时上下文:

{
  "context": {
    "toppings_array": ["onion", "olives", "ham"]
  }
}

对话节点或响应条件:

$toppings_array.contains('ham')

结果:true,因为数组中包含元素 ham

JSONArray.containsIgnoreCase(Object value)

如果输入 JSONArray 包含输入值 (无论该值是以大写还是小写字母指定),那么此方法将返回 true

对于由前一个节点或外部应用程序设置的此对话框运行时上下文:

{
  "context": {
    "toppings_array": ["onion", "olives", "ham"]
  }
}

对话节点或响应条件:

$toppings_array.containsIgnoreCase('HAM')

结果: true,因为数组包含元素 ham 并且忽略大小写。

JSONArray.containsIntent(String intent_name, Double min_score, [Integer top_n] )

如果 true JSONArray 明确包含指定的意向,并且该意向的置信度分数等于或高于指定的最小分数,那么此方法会返回 intents。 您可以选择指定一个数字,以指示该意向必须包含在数组中排名前几位(该数字)元素内。 如果指定负数,那么将忽略 top_n 参数。

如果指定的意向不在数组中,并且其置信度分数低于最小置信度分数,或者该意向的数组索引低于指定的索引位置,那么会返回 false

服务会自动生成 intents 数组,其中列出每当提交用户输入时,服务都会在输入中检测到的意向。 该数组会按置信度从高到低列出服务检测到的所有意向。

可以在节点条件中使用此方法,这样不仅可以检查是否存在意向,还可以设置置信度分数阈值,必须达到此阈值,才会处理节点并返回其响应。

例如,如果要在仅当满足以下条件时才触发对话节点,请在节点条件中使用以下表达式:

  • #General_Ending 意向存在。
  • #General_Ending 意向的信任度得分超过80%。
  • #General_Ending 意向是意向数组中排名前 2 位的意向之一。
intents.containsIntent("General_Ending", 0.8, 2)

JSONArray.filter(temp, "temp.property operator comparison_value")

通过将每个数组元素值与指定的值进行比较来过滤数组。 此方法类似于集合投影。 集合投影返回基于数组元素名称/值对中的名称过滤的数组。 filter 方法返回基于数组元素名称/值对中的值过滤的数组。

过滤表达式由以下值组成:

  • temp:对每个数组元素求值时临时使用的变量的名称。 例如,city

  • property:要与 comparison_value 进行比较的元素属性。 将 property 指定为在第一个参数中命名的临时变量的属性。 请使用语法:temp.property。 例如,如果 latitude 是数组中名称/值对的有效元素名称,请将 property 指定为 city.latitude

  • operator:要用于将属性值与 comparison_value 进行比较的运算符。

    支持的运算符如下:

    支持的过滤器运算符
    运算符 描述
    == 等于
    > 大于
    < 小于
    >= 大于或等于
    <= 小于或等于
    != 不等于
  • comparison_value:要与每个数组元素属性值进行比较的值。 要指定可以根据用户输入变化的值,请将上下文变量或实体用作值。 如果您指定了一个可变的值,请添加逻辑以确保 comparison_value 值在评估时有效,否则将发生错误。

过滤器示例 1

例如,可以使用 filter 方法来对包含一组城市名称及其人口数字的数组求值,以返回仅包含人口超过 500 万的城市的更小数组。

以下 $cities 上下文变量包含对象的数组。 每个对象都包含 namepopulation 属性。

[
   {
      "name":"Tokyo",
      "population":9273000
   },
   {
      "name":"Rome",
      "population":2868104
   },
   {
      "name":"Beijing",
      "population":20693000
   },
   {
      "name":"Paris",
      "population":2241346
   }
]

在以下示例中,任意临时变量名称为 city。 SpEL 表达式会过滤 $cities 数组,以仅包含人口超过 500 万的城市:

$cities.filter("city", "city.population > 5000000")

此表达式将返回以下过滤后的数组:

[
   {
      "name":"Tokyo",
      "population":9273000
   },
   {
      "name":"Beijing",
      "population":20693000
   }
]

您可以使用集合投影来创建一个新的数组,其中仅包含过滤器方法返回的数组中的城市名称。 然后,可以使用 join 方法将数组中的两个 name 元素值显示为字符串,并使用逗号和空格分隔各值。

The cities with more than 5 million people include <?  T(String).join(", ",($cities.filter("city", "city.population > 5000000")).![name]) ?>.

生成的响应为:The cities with more than 5 million people include Tokyo, Beijing.

过滤器示例 2

过滤法的优点在于无需硬编码 comparison_value 值。 在这个例子中,用上下文变量替换了硬编码的值5000000。

在此示例中,$population_min 上下文变量包含数字 5000000。 任意临时变量名称为 city。 SpEL 表达式会过滤 $cities 数组,以仅包含人口超过 500 万的城市:

$cities.filter("city", "city.population > $population_min")

此表达式将返回以下过滤后的数组:

[
   {
      "name":"Tokyo",
      "population":9273000
   },
   {
      "name":"Beijing",
      "population":20693000
   }
]

当您比较数字值时,请确保在触发 filter 方法之前将比较中涉及的上下文变量设置为有效值。如果要将其与数组元素进行比较的数组元素可能包含该数组元素,那么 Null 可以是有效值。 例如,如果东京的人口名称和值对为 "population":null,比较表达式为 "city.population == $population_min",那么 null 将是 $population_min 上下文变量的有效值。

可以使用类似于以下内容的对话节点响应表达式:

The cities with more than $population_min people include <?  T(String).join(", ",($cities.filter("city", "city.population > $population_min")).![name]) ?>.

生成的响应为:The cities with more than 5000000 people include Tokyo, Beijing.

过滤器示例 3

在此示例中,实体名称用作 comparison_value。 用户输入为:What is the population of Tokyo?。 任意临时变量名称为 y。 您创建了一个名为 @city 的实体,用于识别城市名称,包括 Tokyo

$cities.filter("y", "y.name == @city")

此表达式将返回以下数组:

[
   {
      "name":"Tokyo",
      "population":9273000
   }
]

您可以使用集合投影来获取只包含原始数组中 population 元素的数组,然后使用 get 方法来返回 population 元素的值。

The population of @city is: <? ($cities.filter("y", "y.name == @city").![population]).get(0) ?>.

表达式会返回:The population of Tokyo is 9273000.

JSONArray.get(Integer)

此方法用于返回 JSONArray 中的输入索引。

对于由前一个节点或外部应用程序设置的此对话框运行时上下文:

{
  "context": {
    "name": "John",
    "nested": {
      "array": [ "one", "two" ]
    }
  }
}

对话节点或响应条件:

$nested.array.get(0).getAsString().contains('one')

结果: True 因为嵌套数组包含 作为值。one

响应:

"output": {
  "generic" : [
    {
      "values": [
        {
        "text" : "The first item in the array is <?$nested.array.get(0)?>"
        }
      ],
      "response_type": "text",
      "selection_policy": "sequential"
    }
  ]
  }

JSONArray.getRandomItem()

此方法用于返回输入 JSONArray 中的随机项。

对于以下对话运行时上下文:

{
  "context": {
    "toppings_array": ["onion", "olives", "ham"]
  }
}

对话节点输出:

{
  "output": {
  "generic" : [
    {
      "values": [
        {
    "text": "<? $toppings_array.getRandomItem() ?> is a great choice!"
        }
      ],
      "response_type": "text",
      "selection_policy": "sequential"
    }
  ]
  }
}

结果: "ham is a great choice!""onion is a great choice!""olives is a great choice!"

生成的输出文本是随机选择的。

JSONArray.indexOf(value)

此方法返回数组中与指定为参数的值相匹配的元素的下标号,如果在数组中找不到该值,那么会返回 -1。 该值可以是字符串 ("School"),整数 (8) 或双精度 (9.1)。该值必须完全匹配并且区分大小写。

例如,以下上下文变量包含数组:

{
  "context": {
    "array1": ["Mary","Lamb","School"],
    "array2": [8,9,10],
    "array3": [8.1,9.1,10.1]
  }
}

以下表达式可用于确定在其中指定值的数组下标:

<? $array1.indexOf("Mary") ?> returns `0`
<? $array2.indexOf(9) ?> returns `1`
<? $array3.indexOf(10.1) ?> returns `2`

例如,要获取意向数组中某个元素的下标,此方法很有用。 您可以将 indexOf 方法应用于在每次对用户输入求值时生成的意向数组,以确定特定意向的数组下标号。

intents.indexOf("General_Greetings")

如果您想知道特定意图的置信度得分,可以将前面的表达式作为 index值传递给表达式,语法如下 intents[index].confidence。 例如:

intents[intents.indexOf("General_Greetings")].confidence

JSONArray.join(String delimiter)

此方法用于将此数组中的所有值连接成一个字符串。 数值转换为字符串,并以输入分隔符分隔。

对于以下对话运行时上下文:

{
  "context": {
    "toppings_array": ["onion", "olives", "ham"]
  }
}

对话节点输出:

{
  "output": {
  "generic" : [
    {
      "values": [
        {
    "text": "This is the array: <? $toppings_array.join(';') ?>"
        }
      ],
      "response_type": "text",
      "selection_policy": "sequential"
    }
  ]
  }
}

结果:

This is the array: onion;olives;ham;

如果用户输入中提到了多个配料,而您定义了一个名为 @toppings 的实体,它可以识别提到的配料,那么您可以在响应中使用以下表达式列出提到的配料:

So, you'd like <? @toppings.values.join(',') ?>.

如果定义将多个值存储在 JSON 数组中的变量,那么可以从该数组返回值的子集。 使用 join() 方法来正确设置它们的格式。

集合投影

collection projection SpEL 表达式从包含对象的数组中抽取子集合。 集合投影的语法为 array_that_contains_value_sets.![value_of_interest]

例如,以下上下文变量定义用于存储航班信息的 JSON 数组。 每个航班都有两个数据点,时间和航班代码。

"flights_found": [
  {
    "time": "10:00",
    "flight_code": "OK123"
  },
  {
    "time": "12:30",
    "flight_code": "LH421"
  },
  {
    "time": "16:15",
    "flight_code": "TS4156"
  }
]

要仅返回航班代码,可以使用以下语法来创建集合投影表达式:

<? $flights_found.![flight_code] ?>

此表达式将 flight_code 值的数组作为 ["OK123","LH421","TS4156"] 返回。 更多详情,请参阅 Spring Expression Language(SpEL)文档

如果将 join() 方法应用于返回的数组中的值,那么航班代码将以逗号分隔列表形式显示。 例如,可以在响应中使用以下语法:

The flights that fit your criteria are:
  <? T(String).join(",", $flights_found.![flight_code]) ?>.

结果: The flights that match your criteria are: OK123,LH421,TS4156.

JSONArray.joinToArray(template, retainDataType)

此方法从数组中的每个项抽取信息,并构建根据您指定的模板进行格式化的新数组。 模板可以是字符串,JSON 对象或数组。 此方法根据模板的类型返回字符串数组,对象数组或数组数组。

此方法对于将信息格式化为可作为对话节点输出的一部分返回的字符串,或者对于将数据转换为其他结构以便可将其与外部 API 配合使用非常有用。

在模板中,可以使用以下语法来引用源数组中的值,其中 {property} 表示源数组中属性的名称。

%e.{property}%

例如,假设助手将包含飞行详细信息的数组存储在上下文变量中。 存储的数据可能如下所示:

"flights": [
      {
        "flight": "AZ1040",
        "origin": "JFK",
        "carrier": "Alitalia",
        "duration": 485,
        "destination": "FCO",
        "arrival_date": "2019-02-03",
        "arrival_time": "07:00",
        "departure_date": "2019-02-02",
        "departure_time": "16:45"
      },
      {
        "flight": "DL1710",
        "origin": "JFK",
        "carrier": "Delta",
        "duration": 379,
        "destination": "LAX",
        "arrival_date": "2019-02-02",
        "arrival_time": "10:19",
        "departure_date": "2019-02-02",
        "departure_time": "07:00"
      },
      {
        "flight": "VS4379",
        "origin": "BOS",
        "carrier": "Virgin Atlantic",
        "duration": 385,
        "destination": "LHR",
        "arrival_date": "2019-02-03",
        "arrival_time": "09:05",
        "departure_date": "2019-02-02",
        "departure_time": "21:40"
      }
    ]

要构建以用户可读形式描述这些飞行的字符串数组,可以使用以下表达式:

${Flight_data}.joinToArray("Flight %e.flight% to %e.destination%", true)

此表达式将返回以下字符串数组: ["Flight AZ1040 to FCO","Flight DL1710 to LAX","Flight VS4379 to LHR"]

可选的 retainDataType 参数指定方法是否应保留返回数组中所有输入值的数据类型。 如果 retainDataType 设置为 false 或省略,那么在某些情况下,输入数组中的字符串可能会转换为返回数组中的数字。 例如,如果输入数组中的所选值为 "1""2""3",那么返回的数组可能为 [ 1, 2, 3 ]。 要避免意外的类型转换,请为此参数指定 true

复杂模板

更复杂的模板可能包含以可阅读布局显示信息的格式。 对于复杂模板,您可能希望将模板存储在上下文变量中,然后可以将该变量传递到 joinToArray 方法而不是字符串。

例如,此复杂模板包含数组元素的子集,用于添加标签和格式化:

<br/>Flight number: %e.flight% <br/> Airline: %e.carrier% <br/> Departure date: %e.departure_date% <br/> Departure time: %e.departure_time% <br/> Arrival time: %e.arrival_time% <br/>

确保显示助手输出的通道集成支持您在模板中使用的任何格式。

如果创建名为 Template 的上下文变量,并将此模板指定为其值,那么可以在表达式中使用该变量:

${Flight_data}.joinToArray(${Template})

运行时,响应如下:

Flight number: AZ1040
Airline: Alitalia
Departure date: 2019-02-02
Departure time: 16:45
Arrival time: 07:00

Flight number: DL1710
Airline: Delta
Departure date: 2019-02-02
Departure time: 07:00
Arrival time: 10:19

Flight number: VS4379
Airline: Virgin Atlantic
Departure date: 2019-02-02
Departure time: 21:40
Arrival time: 09:05

JSON 对象模板

您可以将模板定义为 JSON 对象,而不是字符串。 这提供了一种方法来标准化来自不同系统的信息的格式,或者将数据转换为外部服务所需的格式。

在此示例中,模板定义为 JSON 对象,用于从存储在 Flight data 上下文变量中的数组中指定的元素中抽取飞行详细信息:

{
  "departure": "Flight %e.flight% departs on %e.departure_date% at %e.departure_time%.",
  "arrival": "Flight %e.flight% arrives on %e.arrival_date% at %e.arrival_time%."
}

通过使用此模板,joinToArray() 方法将返回具有指定结构的新对象数组。

JSONArray.remove(Integer)

此方法用于从 JSONArray 除去索引位置中的元素,并返回更新后的 JSONArray。

对于以下对话运行时上下文:

{
  "context": {
    "toppings_array": ["onion", "olives"]
  }
}

进行以下更新:

{
  "context": {
    "toppings_array": "<? $toppings_array.remove(0) ?>"
  }
}

结果:

{
  "context": {
    "toppings_array": ["olives"]
  }
}

JSONArray.removeValue(object)

此方法用于从 JSONArray 中除去第一次出现的值,并返回更新后的 JSONArray。

对于以下对话运行时上下文:

{
  "context": {
    "toppings_array": ["onion", "olives"]
  }
}

进行以下更新:

{
  "context": {
    "toppings_array": "<? $toppings_array.removeValue('onion') ?>"
  }
}

结果:

{
  "context": {
    "toppings_array": ["olives"]
  }
}

JSONArray.set(Integer index, Object value)

此方法用于将 JSONArray 的输入索引设置为输入值,并返回修改后的 JSONArray。

对于以下对话运行时上下文:

{
  "context": {
    "toppings_array": ["onion", "olives", "ham"]
  }
}

对话节点输出:

{
  "context": {
    "toppings_array": "<? $toppings_array.set(1,'ketchup')?>"
  }
}

结果:

{
  "context": {
    "toppings_array": ["onion", "ketchup", "ham"]
  }
}

JSONArray.size()

此方法用于以整数形式返回 JSONArray 的大小。

对于以下对话运行时上下文:

{
  "context": {
    "toppings_array": ["onion", "olives"]
  }
}

进行以下更新:

{
  "context": {
    "toppings_array_size": "<? $toppings_array.size() ?>"
  }
}

结果:

{
  "context": {
    "toppings_array_size": 2
  }
}

JSONArray split(String regexp)

此方法用于通过输入正则表达式来拆分输入字符串。 结果为字符串的 JSONArray。

对于以下输入:

"bananas;apples;pears"

以下语法:

{
  "context": {
    "array": "<?input.text.split(";")?>
  }
}

将生成以下输出:

{
  "context": {
    "array": [ "bananas", "apples", "pears" ]
  }
}

com.google.gson.JsonArray 支持

除了内置方法外,还可以使用 com.google.gson.JsonArray 类的标准方法。

新增数组

new JsonArray().append('value')

要定义一个由用户提供的值组成的数组,可以实例化一个数组。 对数组实例化后,还必须向数组中添加一个占位符值。 可以使用以下语法来执行此操作:

{
  "context":{
    "answer": "<? output.answer?:new JsonArray().append('temp_value') ?>"
  }

日期和时间

有多种方法可用于处理日期和时间。

有关如何在用户输入中识别和抽取日期和时间信息的信息,请参阅 @sys-date 和 @sys-time 实体

对于可调用方法的日期/时间文字,支持以下字符串格式。

  • 仅适用于时间: HH:mm:ssHH:mm
  • 仅适用于日期: yyyy-MM-dd
  • 对于日期和时间: yyyy-MM-dd HH:mm:ss
  • 对于具有时区的日期和时间: yyyy-MM-dd HH:mm:ss VV。 V 符号来自 DateTimeFormatter,代表 IANA 时区数据库(TZDB)格式的时区,例如欧洲/伦敦。

.after(String date or time)

确定日期/时间值是否晚于 date/time 自变量。

.before(String date or time)

确定日期/时间值是否早于 date/time 自变量。

例如:

  • @sys-time.before('12:00:00')

  • @sys-date.before('2016-11-21')

  • 如果您比较不同的项目,例如 time vs. datedate vs. timetime vs. date and time,该方法将返回false,并在响应JSON日志 output.log_messages 中打印异常。

    例如,@sys-date.before(@sys-time)

  • 如果您比较 date and time vs. time,该方法会忽略日期,只比较时间。

now(String timezone)

返回 yyyy-MM-dd HH:mm:ss 格式的当前日期和时间的字符串。 (可选) 指定 timezone 值以获取特定时区的当前日期和时间,返回格式为 yyyy-MM-dd HH:mm:ss 'GMT'XXX 的字符串。

  • 静态函数。
  • 可以对此函数返回的日期/时间值调用其他日期/时间方法,并且可以将其作为这些方法的自变量传递。
  • 用户界面会自动为您创建一个 $timezone 上下文变量,以便您在“试试看”面板中进行测试时返回正确的时间。 如果未传递时区,那么将使用 UI 自动设置的时区。 在 UI 外部,GMT 用作时区。 要了解用于指定时区的语法,请参阅 系统实体支持的时区

使用 now() 的示例首先检查在助手以特定于上午的问候语进行响应之前是否为上午。

{
  "conditions": "now().before('12:00:00')",
  "output": {
      "generic": [
        {
        "values": [
          {
          "text": "Good morning!"
          }
        ],
        "response_type": "text",
        "selection_policy": "sequential"
        }
      ]
  }
}

使用now()函数与时区返回当前时间(英国)的示例:

{
  "output": {
      "generic": [
        {
        "values": [
          {
          "text": "The current date and time is: <? now('Europe/London') ?>"
          }
        ],
        "response_type": "text",
        "selection_policy": "sequential"
        }
      ]
  }
}

您可以将硬编码的时区值替换为上下文变量,以便根据传递给表达式的时区动态更改时间。 例如:<? now('$myzone') ?>$myzone 上下文变量可以在一个交谈中设置为 'Australia/Sydney',并在另一个交谈中设置为 'Mexico/BajaNorte'

.reformatDateTime(String format)

将日期和时间字符串格式设置为用户输出的所需格式。

根据指定格式返回已设置格式的字符串:

  • MM/dd/yyyy 返回 12/31/2016
  • h a 返回 10pm

要返回星期几:

  • EEEE 表示星期二
  • E 表示 Tue
  • u 用于日期索引(1 = 星期一,……,7 = 星期日)

例如,此上下文变量定义会创建 $time 变量,用于将值 17:30:00 保存为 5:30 PM

{
  "context": {
    "time": "<? @sys-time.reformatDateTime('h:mm a') ?>"
  }
}

格式遵循 Java SimpleDateFormat 规则。

注意:当您尝试仅格式化时间时,日期会被视为 1970-01-01

.sameMoment(String date/time)

  • 确定日期/时间值是否等于 date/time 自变量。

.sameOrAfter(String date/time)

  • 确定日期/时间值是否晚于或等于 date/time 自变量。
  • 类似于 .after()

.sameOrBefore(String date/time)

  • 确定日期/时间值是否早于或等于 date/time 自变量。

today()

返回 yyyy-MM-dd 格式的当前日期的字符串。

  • 静态函数。
  • 可以对此函数返回的日期值调用其他日期方法,并且可以将其作为这些方法的自变量传递。
  • 如果设置了上下文变量 $timezone,那么此函数会返回客户机时区中的日期。 否则,将使用 GMT 时区。

输出字段中使用了 today() 的对话节点示例:

{
  "conditions": "#what_day_is_it",
  "output": {
    "generic": [
      {
        "values": [
          {
          "text": "Today's date is <? today() ?>."
          }
        ],
        "response_type": "text",
        "selection_policy": "sequential"
      }
    ]
  }
}

结果: Today's date is 2018-03-09.

日期和时间计算

使用以下方法来计算日期,其中 <date>yyyy-MM-ddyyyy-MM-dd HH:mm:ss 格式指定。

方法 描述
<date>.minusDays(n) 返回指定日期之前 n 天的日期。
<date>.minusMonths(n) 返回指定日期之前 n 个月的日期。
<date>.minusYears(n) 返回指定日期之前 n 年的日期。
<date>.plusDays(n) 返回指定日期之后 n 天的日期。
<date>.plusMonths(n) 返回指定日期之后 n 个月的日期。
<date>.plusYears(n) 返回指定日期之后 n 年的日期。

要获取明天的日期,请指定以下表达式:

{
  "output": {
    "generic": [
      {
        "values": [
          {
          "text": "Tomorrow's date is <? today().plusDays(1) ?>."
          }
        ],
        "response_type": "text",
        "selection_policy": "sequential"
      }
    ]
  }
}

如果今天是 2018 年 3 月 9 日,那么结果为:Tomorrow's date is 2018-03-10.

要获取从今天起一周之后的日期,请指定以下表达式:

{
  "output": {
    "generic": [
      {
        "values": [
          {
          "text": "Next week's date is <? @sys-date.plusDays(7) ?>."
          }
        ],
        "response_type": "text",
        "selection_policy": "sequential"
      }
    ]
  }
}

如果 @sys-date 实体捕获的日期是今天的日期,即 2018 年 3 月 9 日,那么结果为:Next week's date is 2018-03-16.

要获取上个月的日期,请指定以下表达式:

{
  "output": {
    "generic": [
      {
        "values": [
          {
          "text": "Last month the date was <? today().minusMonths(1) ?>."
          }
        ],
        "response_type": "text",
        "selection_policy": "sequential"
      }
    ]
  }
}

如果今天是 2018 年 3 月 9 日,那么结果为:Last month the date was 2018-02-9.

使用以下方法来计算时间,其中以格式 HH:mm:ss 指定了 <time>

方法 描述
<time>.minusHours(n) 返回指定时间之前 n 小时的时间。
<time>.minusMinutes(n) 返回指定时间之前 n 分钟的时间。
<time>.minusSeconds(n) 返回指定时间之前 n 秒的时间。
<time>.plusHours(n) 返回指定时间之后 n 小时的时间。
<time>.plusMinutes(n) 返回指定时间之后 n 分钟的时间。
<time>.plusSeconds(n) 返回指定时间后 n 秒的时间。

要获取从现在起一小时后的时间,请指定以下表达式:

{
  "output": {
    "generic": [
      {
        "values": [
          {
          "text": "One hour from now is <? now().plusHours(1) ?>."
          }
        ],
        "response_type": "text",
        "selection_policy": "sequential"
      }
    ]
  }
}

如果现在是早上 8 点,那么结果为:One hour from now is 09:00:00.

要获取 30 分钟前的时间,请指定以下表达式:

{
  "output": {
    "generic": [
      {
        "values": [
          {
          "text": "A half hour before @sys-time is <? @sys-time.minusMinutes(30) ?>."
          }
        ],
        "response_type": "text",
        "selection_policy": "sequential"
      }
    ]
  }
}

如果 @sys-time 实体捕获的时间为早上 8 点,那么结果为:A half hour before 08:00:00 is 07:30:00.

要重新设置所返回时间的格式,可以使用以下表达式:

{
  "output": {
    "generic": [
      {
        "values": [
          {
          "text": "6 hours ago was <? now().minusHours(6).reformatDateTime('h:mm a') ?>."
          }
        ],
        "response_type": "text",
        "selection_policy": "sequential"
      }
    ]
  }
}

如果现在是下午 2:19,那么结果为:6 hours ago was 8:19 AM.

使用时间范围

要根据今天的日期是否位于特定时间范围内来显示响应,可以使用与时间相关的方法的组合。 例如,如果您在每年的节日期间运行特殊报价,那么可以检查今天的日期是否位于今年的 11 月 25 日与 12 月 24 日之间。 首先,将相关日期定义为上下文变量。

在以下开始和结束日期上下文变量表达式中,日期是通过将当前年份的派生值与硬编码的月份和日期值连接起来构建的。

"context": {
   "end_date": "<? now().reformatDateTime('Y') + '-12-24' ?>",
   "start_date": "<? now().reformatDateTime('Y') + '-11-25' ?>"
 }

在响应条件中,可以指示仅在当前日期位于定义为上下文变量的开始日期和结束日期之间时,才显示响应。

now().after($start_date) && now().before($end_date)

java.util.Date 支持

除了内置方法外,还可以使用 java.util.Date 类的标准方法。

要获取从今天起一周之后的日期,可以使用以下语法。

{
  "context": {
    "week_from_today": "<? new Date(new Date().getTime() +
      (7 * (24*60*60*1000L))) ?>"
  }
}

该表达式首先获取自1970年1月1日00:00:00协调世界时以来的当前日期(以毫秒为单位)。 此表达式还会计算 7 天的毫秒数。 ((24*60*60*1000L) 表示一天的毫秒数。) 然后,将当前日期加上 7 天。 结果就是从今天起一周之后的完整日期。 例如,Fri Jan 26 16:30:37 UTC 2018。 时间采用全球标准时区。 您始终可以将 7 更改为可以传入的变量(例如,$number_of_days)。 在计算表达式之前,请确保其值已设置。

如果您想将日期与服务生成的其他日期进行比较,则必须重新设置日期格式。 系统实体 (@sys-date) 和其他内置方法 (now()) 会将日期转换为 yyyy-MM-dd 格式。

{
  "context": {
    "week_from_today": "<? new Date(new Date().getTime() +
      (7 * (24*60*60*1000L))).format('yyyy-MM-dd') ?>"
  }
}

重新格式化日期后,结果为 2018-01-26。 现在,您可以在响应条件中使用 @sys-date.after($week_from_today) 这样的表达式,将用户输入的日期与上下文变量中保存的日期进行比较。

以下表达式计算从此刻起 3 小时之后的时间。

{
  "context": {
    "future_time": "<? new Date(new Date().getTime() + (3 * (60*60*1000L)) -
      (5 * (60*60*1000L))).format('h:mm a') ?>"
  }
}

(60*60*1000L) 值表示一小时的毫秒数。 此表达式将当前时间加上 3 小时。 然后,它从协调世界时区减去5小时,重新计算出东部标准时间时区的时间。 此表达式还会重新设置日期值的格式,以包含小时和分钟以及“上午”或“下午”。

数字

这些方法可帮助您获取数字值并重新设置其格式。

有关可以在用户输入中识别和抽取数字的系统实体的信息,请参阅 @sys-number 实体

如果您希望服务识别用户输入中的特定数字格式(例如,订单号引用),请考虑创建模式实体来进行捕获。 有关更多详细信息,请参阅创建实体

如果要更改数字的小数位数(例如,用于将数字格式重新设置为货币值),请参阅 String format() 方法

toDouble()

将对象或字段转换为 Double 数字类型。 可以对任何对象或字段调用此方法。 如果转换失败,会返回 null

toInt()

将对象或字段转换为 Integer 数字类型。 可以对任何对象或字段调用此方法。 如果转换失败,会返回 null

toLong()

将对象或字段转换为 Long 数字类型。 可以对任何对象或字段调用此方法。 如果转换失败,会返回 null

如果在 SpEL 表达式中指定了长整型数字类型,那么必须在该数字后附加 L 进行标识。 例如,5000000000L。 对于任何不适合 32 位整数类型的数字,此语法都是必需的。 例如,大于2^31(2,147,483,648)或小于 -2 (-2 的数字被视为长数字类型。 长整型数字类型的最小值为 -2^63,最大值为 2^63-1(或 9,223,372,036,854,775,807)。

如果您需要判断一个数字是否太长而无法识别,请使用以下表达式检查数字中是否包含超过18个整数:

<? @sys-number.toString().length() > 18 ?>

如果您需要处理超过18个整数的数字,请考虑使用模式实体(使用正则表达式,例如 \d{20} )来处理它们,而不是使用 @sys-number

标准数学

使用 SpEL 表达式可定义标准数学公式,其中运算符使用以下符号来表示:

算术运算 符号
/

例如,在对话节点响应中,您可以添加一个上下文变量,用于捕获用户输入( @sys-number )中指定的数字,并将其保存为 $your_number。 然后,可以将以下文本添加为文本响应:

I'm doing math. Given the value you specified ($your_number), when I add 5, I get: <? $your_number + 5 ?>.
When I subtract 5, I get: <? $your_number - 5 ?>.
When I multiply it by 5, I get: <? $your_number * 5 ?>.
When I divide it by 5, I get: <? $your_number/5 ?>.

如果用户指定 10,那么生成的文本响应类似于以下内容:

I'm doing math. Given the value you specified (10), when I add 5, I get: 15.
When I subtract 5, I get: 5.
When I multiply it by 5, I get: 50.
When I divide it by 5, I get: 2.

Java 数字支持

java.lang.Math()

执行基本数字运算。

您可以使用类方法:

max()

{
  "context": {
    "bigger_number": "<? T(Math).max($number1,$number2) ?>"
  },
  "output": {
    "generic": [
      {
        "values": [
          {
          "text": "The bigger number is $bigger_number."
          }
        ],
        "response_type": "text",
        "selection_policy": "sequential"
      }
    ]
  }
}

min()

{
  "context": {
    "smaller_number": "<? T(Math).min($number1,$number2) ?>"
  },
  "output": {
    "generic": [
      {
        "values": [
          {
          "text": "The smaller number is $smaller_number."
          }
        ],
        "response_type": "text",
        "selection_policy": "sequential"
      }
    ]
  }
}

pow()

{
  "context": {
    "power_of_two": "<? T(Math).pow($base.toDouble(),2.toDouble()) ?>"
  },
  "output": {
    "generic": [
      {
        "values": [
          {
          "text": "Your number $base to the second power is $power_of_two."
          }
        ],
        "response_type": "text",
        "selection_policy": "sequential"
      }
    ]
  }
}

有关更多信息,请参阅 java.lang.Math 参考文档

java.util.Random()

返回一个随机数。 可以使用以下其中一个语法选项:

  • 要返回一个随机的布尔值(true或false),请使用 <?new Random().nextBoolean()?>
  • 要返回一个介于0(含)和1(不含)之间的随机双数,请使用 <?new Random().nextDouble()?>
  • 要返回一个介于0(含)和您指定的数字之间的随机整数,请使用 <?new Random().nextInt(n)?>,其中n是您想要的数字范围的顶部+1。 例如,如果您想返回一个介于0到10之间的随机数,请指定 <?new Random().nextInt(11)?>
  • 要返回整数值范围(-2147483648到2147483648)中的随机整数,请使用 <?new Random().nextInt()?>

例如,可以创建由 #random_number 意向触发的对话节点。 第一个响应条件可能类似于:

Condition = @sys-number
{
  "context": {
    "answer": "<? new Random().nextInt(@sys-number.numeric_value + 1) ?>"
  },
  "output": {
    "generic": [
      {
        "values": [
          {
          "text": "Here's a random number between 0 and @sys-number.literal: $answer."
          }
        ],
        "response_type": "text",
        "selection_policy": "sequential"
      }
    ]
  }
}

有关其他方法的信息,请参阅 java.util.Random 参考文档

还可以使用以下类的标准方法:

  • java.lang.Byte
  • java.lang.Integer
  • java.lang.Long
  • java.lang.Double
  • java.lang.Short
  • java.lang.Float

Objects

JSONObject.clear()

此方法用于清除 JSON 对象中的所有值并返回 null。

例如,您希望清除 $user 上下文变量中的当前值。

{
  "context": {
    "user": {
      "first_name":"John",
      "last_name":"Snow"
    }
  }
}

在输出中使用以下表达式来定义一个字段,用于清除其值的对象。

{
  "output": {
    "object_eraser": "<? $user.clear() ?>"
  }
}

如果引用$user上下文变量,则仅返回 {}

可以在 API clear() 调用主体中的 contextoutput JSON 对象上使用 /message 方法。

清除上下文

使用 clear() 方法清除 context 对象时,会清除所有变量,但以下变量除外:

  • context.conversation_id
  • context.timezone
  • context.system

警告:所有上下文变量值是指:

  • 在当前会话期间触发的节点中为变量设置的所有默认值。
  • 在当前会话期间,用户或外部服务提供的信息对默认值进行的任何更新。

要使用此方法,可以在输出对象中定义的变量内的表达式中指定该方法。 例如:

{
  "output": {
    "generic": [
      {
        "values": [
          {
          "text": "Response for this node."
          }
        ],
        "response_type": "text",
        "selection_policy": "sequential"
      }
    ],
    "context_eraser": "<? context.clear() ?>"
  }
}

清除输出

使用 clear() 方法来清除 output 对象时,将清除所有变量,但用于清除在当前节点中定义的输出对象和任何文本响应的变量除外。 该方法也不会清除以下变量:

  • output.nodes_visited
  • output.nodes_visited_details

要使用此方法,可以在输出对象中定义的变量内的表达式中指定该方法。 例如:

{
  "output": {
    "generic": [
      {
        "values": [
          {
          "text": "Have a great day!"
          }
        ],
        "response_type": "text",
        "selection_policy": "sequential"
      }
    ],
    "output_eraser": "<? output.clear() ?>"
  }
}

如果树中较早的节点定义了文本响应 I'm happy to help.,然后跳转到具有较早定义的 JSON 输出对象的节点,那么只有 Have a great day. 会显示为响应。 I'm happy to help. 输出未显示,因为该输出被清除,并被调用 clear() 方法的节点的文本响应所取代。

JSONObject.has(String)

如果复杂 JSONObject 具有输入名称的属性,那么此方法会返回 true。

对于以下对话运行时上下文:

{
  "context": {
    "user": {
      "first_name": "John",
      "last_name": "Snow"
    }
  }
}

对话节点输出:

{
  "conditions": "$user.has('first_name')"
}

结果:条件为 true,因为用户对象包含属性 first_name

JSONObject.remove(String)

此方法用于从输入 JSONObject 中除去名称的属性。 此方法返回的 JSONElement 是要除去的 JSONElement

对于以下对话运行时上下文:

{
  "context": {
    "user": {
      "first_name": "John",
      "last_name": "Snow"
    }
  }
}

对话节点输出:

{
  "context": {
    "attribute_removed": "<? $user.remove('first_name') ?>"
  }
}

结果:

{
  "context": {
    "user": {
      "last_name": "Snow"
    },
    "attribute_removed": {
      "first_name": "John"
    }
  }
}

com.google.gson.JsonObject 支持

除了内置方法外,还支持 com.google.gson.JsonObject 类的一些标准方法。

字符串

这些方法可以帮助您处理文本。

有关如何识别和抽取用户输入中特定类型 String(例如,人名和位置)的信息,请参阅系统实体

备注:关于正则表达式的方法,请参阅 RE2 语法参考,了解指定正则表达式时使用的语法详情。

String.append(Object)

此方法用于将输入对象作为字符串附加到字符串,并返回修改后的字符串。

对于以下对话运行时上下文:

{
  "context": {
    "my_text": "This is a text."
  }
}

以下语法:

{
  "context": {
    "my_text": "<? $my_text.append(' More text.') ?>"
  }
}

将生成以下输出:

{
  "context": {
    "my_text": "This is a text. More text."
  }
}

String.contains(String)

如果字符串包含输入子字符串,那么此方法会返回 true。

输入: Yes, I'd like to go.

以下语法:

{
  "conditions": "input.text.contains('Yes')"
}

结果:条件为 true

String.endsWith(String)

如果字符串以输入子字符串结尾,那么此方法会返回 true。

对于以下输入:

"What is your name?".

以下语法:

{
  "conditions": "input.text.endsWith('?')"
}

结果:条件为 true

String.equals(字符串)

如果指定的字符串与输入字符串完全相同,那么此方法将返回 true

输入:“是”

以下语法:

{
  "conditions": "input.text.equals('Yes')"
}

结果:条件为 true

如果输入为 Yes.,那么结果为 false,因为用户包含句点,并且表达式只需要精确文本 Yes 而没有任何标点符号。

String.equalsIgnoreCase(String)

如果指定的字符串等于输入字符串 (无论字母的大小写是否匹配),那么此方法将返回 true

输入: "yes"

以下语法:

{
  "conditions": "input.text.equalsIgnoreCase('Yes')"
}

结果:条件为 true

如果输入为 Yes.,那么结果为 false,因为用户包含句点,并且表达式仅期望文本 Yes(大写或小写字母),而没有任何标点符号。

String.extract(String regexp, Integer groupIndex)

此方法会返回输入中与指定的正则表达式组模式相匹配的字符串。 如果找不到匹配项,那么将返回空字符串。

此方法旨在抽取不同正则表达式模式组的匹配项,而不是抽取单个正则表达式模式的不同匹配项。 要查找不同的匹配项,请参阅 getMatch 方法。

在此示例中,上下文变量将保存与指定的正则表达式模式组相匹配的字符串。 在表达式中,定义了两个正则表达式模式组,每个组都用括号括起。 固有的第三组由两组组成。 这是第一个 groupIndex 正则表达式组;它与包含数字组和文本组的字符串匹配。 第二个正则表达式组 (groupIndex 1) 与第一次出现的数字组相匹配。 第三个组 (groupIndex 2) 与数字组后第一次出现的文本组相匹配。

{
  "context": {
    "number_extract": "<? input.text.extract('([\\d]+)(\\b [A-Za-z]+)',n) ?>"
  }
}

在 JSON 中指定正则表达式时,必须提供两个反斜杠 (\)。如果在节点响应中指定此表达式,那么仅需要一个反斜杠。 例如:

<? input.text.extract('([\d]+)(\b [A-Za-z]+)',n) ?>

输入:

"Hello 123 this is 456".

结果:

  • n=0 时,值为 123 this
  • n=1 时,值为 123
  • n=2 时,值为 this

String.find(String regexp)

如果字符串的任何分段与输入正则表达式匹配,那么此方法会返回 true。 你可以对JSONArray或JSONObject元素调用此方法,它会在进行比较之前将数组或对象转换为字符串。

对于以下输入:

"Hello 123456".

以下语法:

{
  "conditions": "input.text.find('^[^\d]*[\d]{6}[^\d]*$')"
}

结果:条件为 true,因为输入文本的数字部分与正则表达式 ^[^\d]*[\d]{6}[^\d]*$ 匹配。

String.getMatch(String regexp, Integer matchIndex)

此方法会返回输入中与指定的正则表达式模式的出现相匹配的字符串。 如果找不到匹配项,那么此方法将返回空字符串。

找到匹配项时,会将其添加到您可以视为匹配项数组的内容。 如果要返回第三个匹配项,因为数组元素计数从 0 开始,因此请为 matchIndex 值指定 2。 例如,如果输入的文本字符串包含与指定模式匹配的三个词,那么只能通过指定其索引值来返回第一个、第二个或第三个匹配项。

在以下表达式中,您将在输入中查找一组数字。 此表达式将第二个模式匹配字符串保存到 $second_number 上下文变量中,因为指定了索引值 1。

{
  "context": {
    "second_number": "<? input.text.getMatch('([\\d]+)',1) ?>"
  }
}

如果以 JSON 语法指定表达式,那么必须提供两个反斜杠 (\)。如果在节点响应中指定表达式,那么仅需要一个反斜杠。

例如:

<? input.text.getMatch('([\d]+)',1) ?>

  • 用户输入:

    "hello 123 i said 456 and 8910".
    
  • 结果: 456

在这个例子中,表达式在输入中寻找第三个文本块。

<? input.text.getMatch('(\b [A-Za-z]+)',2) ?>

对于相同的用户输入,此表达式会返回 and

String.isEmpty()

如果字符串为空字符串,但不是 null,那么此方法会返回 true。

对于以下对话运行时上下文:

{
  "context": {
    "my_text_variable": ""
  }
}

以下语法:

{
  "conditions": "$my_text_variable.isEmpty()"
}

结果:条件为 true

String.length()

此方法用于返回字符串的字符长度。

对于以下输入:

"Hello"

以下语法:

{
  "context": {
    "input_length": "<? input.text.length() ?>"
  }
}

将生成以下输出:

{
  "context": {
    "input_length": 5
  }
}

String.matches(String regexp)

如果字符串与输入正则表达式匹配,那么此方法会返回 true。

对于以下输入:

"Hello".

以下语法:

{
  "conditions": "input.text.matches('^Hello$')"
}

结果:条件为 true,因为输入文本与正则表达式 \^Hello\$ 匹配。

String.startsWith(String)

如果字符串以输入子字符串开头,那么此方法会返回 true。

对于以下输入:

"What is your name?".

以下语法:

{
  "conditions": "input.text.startsWith('What')"
}

结果:条件为 true

String.substring(Integer beginIndex, Integer endIndex)

这种方法获取一个子字符串,其中包含字符 beginIndex,最后一个字符设置为索引,位于 endIndex 之前。 不包括 endIndex 字符。

对于以下对话运行时上下文:

{
  "context": {
    "my_text": "This is a text."
  }
}

以下语法:

{
  "context": {
    "my_text": "<? $my_text.substring(5, $my_text.length()) ?>"
  }
}

将生成以下输出:

{
  "context": {
    "my_text": "is a text."
  }
}

String.toJson()

此方法解析包含 JSON 数据的字符串并返回 JSON 对象或数组,如以下示例中所示:

${json_var}.toJson()

如果上下文变量 ${json_var} 包含以下字符串:

"{ \"firstname\": \"John\", \"lastname\": \"Doe\" }"

toJson() 方法返回以下对象:

{
  "firstname": "John",
  "lastname": "Doe"
}

String.toLowerCase()

此方法将原始字符串转换为小写字母。

对于以下输入:

"This is A DOG!"

以下语法:

{
  "context": {
    "input_lower_case": "<? input.text.toLowerCase() ?>"
  }
}

将生成以下输出:

{
  "context": {
    "input_lower_case": "this is a dog!"
  }
}

String.toUpperCase()

此方法返回转换为大写字母的原始字符串。

对于以下输入:

"hi there".

以下语法:

{
  "context": {
    "input_upper_case": "<? input.text.toUpperCase() ?>"
  }
}

将生成以下输出:

{
  "context": {
    "input_upper_case": "HI THERE"
  }
}

String.trim()

此方法用于删除字符串开头和结尾的所有空格,并返回修改后的字符串。

对于以下对话运行时上下文:

{
  "context": {
    "my_text": "   something is here    "
  }
}

以下语法:

{
  "context": {
    "my_text": "<? $my_text.trim() ?>"
  }
}

将生成以下输出:

{
  "context": {
    "my_text": "something is here"
  }
}

java.lang.String 支持

除了内置方法外,还可以使用 java.lang.String 类的标准方法。

java.lang.String.format()

可以将标准 Java String format() 方法应用于文本。 请参阅 java.util.formatter 参考,了解用于指定格式细节的语法信息。

例如,以下表达式采用 3 个十进制整数(1、1 和 2)并将它们添加到语句中。

{
  "formatted String": "<? T(java.lang.String).format('%d + %d equals %d', 1, 1, 2) ?>"
}

结果:1 + 1 equals 2

要更改数字的小数位数,请使用以下语法:

{
  <? T(String).format('%.2f',<number to format>) ?>
}

例如,如果需要以美元格式化的变量 $number4.5,则响应 Your total is $<? T(String).format('%.2f',$number) ?> 将返回 Your total is $4.50.

间接数据类型转换

例如,在文本中包含表达式作为节点响应的一部分时,该值会呈现为 String。 如果希望表达式以其原始数据类型呈现,那么不要将其包含在文本内。

例如,可以将以下表达式添加到对话节点响应,以便以 String 格式返回用户输入中识别到的实体:

  The entities are <? entities ?>.

如果用户将 Hello now 指定为输入,那么 now 引用将触发 @sys-date 和 @sys-time 实体。 实体对象是一个数组,但由于表达式包含在文本中,因此实体以 String 格式返回,如下所示:

  The entities are 2018-02-02, 14:34:56.

如果响应中不包含文本,那么会改为返回数组。 例如,如果响应仅指定为表达式,而不是包含在文本内。

  <? entities ?>

实体信息以原始数据类型(数组)的形式返回。

[
  {
    "entity":"sys-date","location":[6,9],"value":"2018-02-02","confidence":1,"metadata":{"calendar_type":"GREGORIAN","timezone":"America/New_York"}
  },
  {
    "entity":"sys-time","location":[6,9],"value":"14:33:22","confidence":1,"metadata":{"calendar_type":"GREGORIAN","timezone":"America/New_York"}
  }
  ]

再例如,以下 $array 上下文变量是数组,但 $string_array 上下文变量是字符串。

{
  "context": {
    "array": [
      "one",
      "two"
    ],
    "array_in_string": "this is my array: $array"
  }
}

如果您在“试试看”面板中查看这些上下文变量的值,您会发现它们的值被指定如下:

$array : ["one","two"]

$array_in_string : "this is my array: [\"one\",\"two\"]"

您可以在$array变量上执行数组方法,例如 <? $array.removeValue('two') ?>,但不能在$array_in_string变量上执行。