.lg 文件格式
适用于:SDK v4
.lg 文件描述具有实体引用及其组合的语言生成模板。 本文介绍了使用 .lg 文件格式表达的各种概念。
特殊字符
注释
使用 > 创建注释。 分析器将跳过以此前缀开头的所有行。
> This is a comment.
转义字符
使用 作为转义字符\。
# TemplateName
- You can say cheese and tomato \[toppings are optional\]
数组和对象
创建数组
若要创建数组,请使用 ${[object1, object2, ...]} 语法。 例如,该表达式:
${['a', 'b', 'c']}
返回数组 ['a', 'b', 'c']
。
创建对象
若要创建对象,请使用 ${{key1:value1,key2:value2, ...}} 语法。 例如,该表达式:
${{user: {name: "Wilson", age: 27}}}
返回下面的 JSON 对象:
{
"user": {
"name": "Wilson",
"age": 27
}
}
模板
“模板”是语言生成系统的核心概念。 每个模板都有一个名称和以下内容之一:
- one-of 变体文本值的列表
- 结构化内容定义
- 条件的集合,每个条件都有:
- [自适应表达式][3]
- 每个条件的 one-of 变体文本值的列表
模板名称
模板名称区分大小写,只能包含字母、下划线和数字。 下面名为 TemplateName
的模板的示例。
# TemplateName
模板不能以数字开头,以 . 拆分的模板名称的任何部分都不能以数字开头。
模板响应变体
变体表示为 Markdown 列表。 可使用 、' 或 字符为每个变体添加前缀-+。
# Template1
- text variation 1
- text variation 2
- one
- two
# Template2
* text variation 1
* text variation 2
# Template3
+ one
+ two
简单响应模板
简单响应模板包括用于组合和扩展的文本的一个或多个变体。 LG 库将从提供的变体中随机选择一个变体。
下面是一个简单模板示例,其中包含两个变体。
> Greeting template with two variations.
# GreetingPrefix
- Hi
- Hello
带条件的响应模板
带条件的响应模板允许你根据条件创作所选内容。 所有条件均使用 [自适应表达式][3] 表示。
重要
条件模板不能嵌套在单个条件响应模板中。 在结构化响应模板中使用组合来嵌套条件。
If-else 模板
If-else 模板允许你构建一个模板,该模板基于条件的级联顺序选择集合。 计算是自上而下的,当条件计算为 true
或命中 ELSE 块时停止。
条件表达式括在大括号 ${} 中。 下面举一个演示简单的 IF ELSE 条件响应模板定义的例子。
> time of day greeting reply template with conditions.
# timeOfDayGreeting
- IF: ${timeOfDay == 'morning'}
- good morning
- ELSE:
- good evening
再举一个演示 if-else 条件响应模板定义的例子。 请注意,你可以在任何条件的变体中包含对其他简单或条件响应模板的引用。
# timeOfDayGreeting
- IF: ${timeOfDay == 'morning'}
- ${morningTemplate()}
- ELSEIF: ${timeOfDay == 'afternoon'}
- ${afternoonTemplate()}
- ELSE:
- I love the evenings! Just saying. ${eveningTemplate()}
Switch 模板
使用 Switch 模板,你可以设计一个将表达式的值与 CASE 子句相匹配,并根据该情况生成输出的模板。 条件表达式括在大括号 ${} 中。
下面介绍如何在 LG 中指定 SWITCH CASE DEFAULT 块。
# TestTemplate
- SWITCH: ${condition}
- CASE: ${case-expression-1}
- output1
- CASE: ${case-expression-2}
- output2
- DEFAULT:
- final output
下面是更复杂的 SWITCH CASE DEFAULT 示例:
> Note: Any of the cases can include reference to one or more templates.
# greetInAWeek
- SWITCH: ${dayOfWeek(utcNow())}
- CASE: ${0}
- Happy Sunday!
-CASE: ${6}
- Happy Saturday!
-DEFAULT:
- ${apology-phrase()}, ${defaultResponseTemplate()}
注意
与条件模板一样,Switch 模板也不能嵌套。
结构化响应模板
结构化响应模板允许你定义支持主要 LG 功能的复杂结构(如模板化、组合和替换),同时将结构化响应的解释留给 LG 库的调用方。
对于机器人应用程序,我们本机支持:
- 活动定义
- 卡片定义
请参阅结构响应模板获取详细信息。
模板组合和扩展
对模板的引用
变体文本可以包括对另一个命名模板的引用,以帮助组合和解决复杂的响应。 对其他命名模板的引用使用大括号来表示,例如 ${<TemplateName>()}。
> Example of a template that includes composition reference to another template.
# GreetingReply
- ${GreetingPrefix()}, ${timeOfDayGreeting()}
# GreetingPrefix
- Hi
- Hello
# timeOfDayGreeting
- IF: ${timeOfDay == 'morning'}
- good morning
- ELSEIF: ${timeOfDay == 'afternoon'}
- good afternoon
- ELSE:
- good evening
调用 GreetingReply
模板可能会生成以下扩展解决方案之一:
Hi, good morning
Hi, good afternoon
Hi, good evening
Hello, good morning
Hello, good afternoon
Hello, good evening
实体
直接在 one-of 变体文本中使用时,实体引用使用大括号表示,如 ${entityName
};用作参数时,实体引用没有大括号。
实体可用作参数的情况:
在变体中使用预生成函数
[预生成函数][4] 由 [自适应表达式][3] 支持,也可以在 one-of 变体文本中内联使用,以实现更强大的文本撰写。 要使用内联表达式,只需将其括在大括号中即可。
# RecentTasks
- IF: ${count(recentTasks) == 1}
- Your most recent task is ${recentTasks[0]}. You can let me know if you want to add or complete a task.
- ELSEIF: ${count(recentTasks) == 2}
- Your most recent tasks are ${join(recentTasks, ', ', ' and ')}. You can let me know if you want to add or complete a task.
- ELSEIF: ${count(recentTasks) > 2}
- Your most recent ${count(recentTasks)} tasks are ${join(recentTasks, ', ', ' and ')}. You can let me know if you want to add or complete a task.
- ELSE:
- You don't have any tasks.
上面的示例使用 [联接][5] 预生成函数列出 recentTasks
集合中的所有值。
给定的模板和预生成函数共享相同的调用签名,模板名称不能与预生成函数名称相同。
模板名称不应与预生成函数名称匹配。 预生成函数优先。 要避免此类冲突,可以在引用模板名称时前置 lg.
。 例如:
> Custom length function with one parameter.
# length(a)
- This is use's customized length function
# myfunc1
> will call prebuilt function length, and return 2
- ${length('hi')}
# mufunc2
> this calls the lg template and output 'This is use's customized length function'
- ${lg.length('hi')}
变体中的多行文本
每个 one-of 变体都可以包括用三重引号括起来的多行文本。
# MultiLineExample
- ```This is a multiline list
- one
- two
```
- ```This is a multiline variation
- three
- four
```
多行变体可以通过将请求的操作括在大括号 ${} 中来请求模板扩展和实体替换。
# MultiLineExample
- ```
Here is what I have for the order
- Title: ${reservation.title}
- Location: ${reservation.location}
- Date/ time: ${reservation.dateTimeReadBack}
```
借助多行支持,你可以让语言生成子系统完全解析复杂的 JSON 或 XML(如用于控制机器人口头应答的 SSML 包装文本)。
参数化模板
要帮助实现上下文可重用性,可以参数化模板。 模板的不同调用方可以传递不同的值,以便用于扩展解决方法。
# timeOfDayGreetingTemplate (param1)
- IF: ${param1 == 'morning'}
- good morning
- ELSEIF: ${param1 == 'afternoon'}
- good afternoon
- ELSE:
- good evening
# morningGreeting
- ${timeOfDayGreetingTemplate('morning')}
# timeOfDayGreeting
- ${timeOfDayGreetingTemplate(timeOfDay)}
导入外部引用
可以将语言生成模板拆分为单独的文件,并从另一个文件中引用模板。 可以使用 Markdown 样式链接导入在另一个文件中定义的模板。
[Link description](filePathOrUri)
目标文件中定义的所有模板都将被拉入。 请确保模板名称在正在拉入的文件之间是唯一的(或具有 # \<namespace>.\<templatename>
的命名空间)。
[Shared](../shared/common.lg)
LG 插入的函数
[自适应表达式][3] 提供了注入一组自定义函数的能力。 阅读 [从 LG 库注入的函数][13],了解更多信息。
选项
开发人员可以设置解析器选项以进一步自定义输入的计算方法。 使用 > !#
表示法设置解析器选项。
重要
在文件中找到的最后一个设置胜过在同一文档中找到的任何先前设置。
Strict 选项
如果开发人员不希望允许 null 计算结果为 null,则可以实现 strict 选项。 下面是一个简单的 strict 选项的示例:
> !# @strict = true
# template
- hi
如果启用 strict 选项,则 null 错误将引发友好消息。
# welcome
- hi ${name}
如果名称为 null,则诊断将为“‘name’ 计算结果为 null。计算 ‘- hi ${name}’ 时出现 [welcome] 错误。”。 如果 strict 设置为 false 或未设置,则将给出兼容的结果。 上述示例将生成“hi null”。
replaceNull 选项
开发人员可以使用 replaceNull 选项来创建委托以替换计算表达式中的 null 值:
> !# @replaceNull = ${path} is undefined
在上面的示例中,path
变量中的 null 输入将替换为“未定义 ${path}”。 在 user.name
为 null 的以下输入中:
hi ${user.name}
将导致“未定义 hi user.name”。
lineBreakStyle 选项
开发人员可以使用 lineBreakStyle 选项来设置 LG 系统如何呈现换行符的选项。 目前支持两种模式:
default
:多行文本中的换行符创建普通换行符。markdown
:多行文本中的换行符将自动转换为两行以创建新行
下面的示例演示如何将 lineBreakStyle 选项设置为 markdown
:
> !# @lineBreakStyle = markdown
命名空间选项
可以为要导出的 LG 模板注册命名空间。 如果未指定命名空间,则会将命名空间设置为不带扩展名的文件名。
下面的示例演示如何将命名空间选项设置为 foo
:
> !# @Namespace = foo
导出选项
可以指定要导出的 LG 模板列表。 已导出模板可以像预生成函数一样调用。
下面的示例演示如何将导出选项设置为 template1, template2
:
> !# @Namespace = foo
> !# @Exports = template1, template2
# template1(a, b)
- ${a + b}
# template2(a, b)
- ${join(a, b)}
使用 foo.template1(1,2), foo.template2(['a', 'b', 'c'], ',')
调用这些导出的模板。
缓存范围
通过缓存范围选项,可以控制 LG 计算器何时之前遇到过的表达式重新求值,以及何时存储并使用缓存结果。
- 全局缓存在求值的生命周期内有效。 LG 缓存所有求值结果,如果模板名称和参数相同,则从缓存返回结果。
- 本地缓存范围是默认范围。 在同一层中,如果上一个模板已使用相同的模板名称和相同的参数调用,则直接返回缓存的结果。
- 无缓存范围禁用所有缓存范围,并且每次都返回新结果。
> !# @cacheScope= global // global cache
> !# @cacheScope= local // local cache
> !# @cacheScope= none // none cache
> !# @cacheScope= xxx // fallback to local cache
请注意,缓存范围选项不区分大小写。
> !# @cacheScope= global // ok
> !# @CACHESCOPE= global // ok
> !# @cachescope= global // ok
请注意,缓存范围遵循 Microsoft Entrance .lg 文件的范围。
假设你有两个文件:a.lg
和 b.lg
,如下所示:
a.lg
> !# @cacheScope= global
[import](b.lg)
b.lg
> !# @cacheScope= none
# template1
- ${template2()} ${template2()}
# template2
- ${rand(1, 10000000)}
如果运行以下代码,你会注意到由于 a.lg 中的 global
缓存范围选项,template2
使用首次求值结果的缓存结果:
var templates = Templates.ParseFile("a.lg");
var result = templates.Evaluate("template1"); // the second "template2" would use the cache of the first evaluate result
重新执行标记影响
如果模板名称以“!”结尾,则模板强制重新执行。 无论缓存范围如何,此结果都不会添加到缓存中。
假设你有以下模板:
# template2
- ${template1()} ${template1!()} ${template1()}
template1!()
将触发并将结果添加到缓存中。 第二个 template1()
堵塞第一个 template1()
的结果。 最终调用使用缓存中存储的结果。
全局缓存范围示例
假设你有以下模板:
# template1
- ${template2()} ${template3()}
# template2
- ${rand(1, 10)}
- abc
- hi
# template3
- ${template2()}
将对 template2
重新求值一次,template3
中的第二次执行将应用第一次执行的缓存。
以下代码片段中提供了另一个示例:
var templates = Templates.ParseFile("xxx.lg");
var result1 = templates.Evaluate("template", null, new EvaluationOptions { CacheScope = LGCacheScope.Global});
// The second evaluation would drop all the results cached before.
var result2 = templates.Evaluate("template", null, new EvaluationOptions { CacheScope = LGCacheScope.Global});
模板使用 Templates.ParseFile()
函数分析,模板求值结果存储在其中 result1
中。 请注意,第二个求值结果 result2
会删除以前缓存的所有结果。
本地缓存范围示例
以下示例演示本地缓存范围何时正常工作,何时不能正常工作。 假设 t()
和 subT()
是采用参数的模板:
> Cache works, the second template call would re-use the first's result.
# template1
- ${t(param)} ${t(param)}
> Cache doesn't work because param1's value is different with param2's. value)
# template2
- ${t(param1)} ${t(param2)}
> Cache doesn't work because of different layers.
# template3
- ${subT(param1)} ${t(param2)}
# subT(param)
- ${t(param)}
其他资源
- C++ API 参考
- JavaScript API 参考
- 阅读使用自适应工具进行调试,了解如何分析和调试 .lg 文件。 [1]: https://github.com/Microsoft/botbuilder-tools/blob/master/packages/Ludown/docs/lu-file-format.md [3]: ../v4sdk/bot-builder-concept-adaptive-expressions.md [4]: ../adaptive-expressions/adaptive-expressions-prebuilt-functions.md [5]: ../adaptive-expressions/adaptive-expressions-prebuilt-functions.md#join [6]: https://github.com/microsoft/botframework-cli/tree/master/packages/chatdown [7]: https://github.com/microsoft/botframework-cli/blob/master/packages/chatdown/docs/chatdown-format.md [8]: https://github.com/microsoft/botframework-cli/blob/master/packages/chatdown/docs/examples/CardExamples.chat [9]: https://github.com/microsoft/botframework-cli/blob/master/packages/chatdown/docs/chatdown-format.md#message-commands [10]: https://github.com/microsoft/botframework-cli/blob/master/packages/chatdown/docs/chatdown-format.md#message-cards [11]: https://github.com/microsoft/botframework-cli/blob/master/packages/chatdown/docs/chatdown-format.md#message-attachments [12]: https://github.com/microsoft/botframework-cli/blob/master/packages/chatdown/docs/chatdown-format.md [13]: ../language-generation/functions-injected-from-language-generation.md