下载本文的PDF版本 PDF

会话式界面框架设计

结合机器学习的最新进展与早期方法

扎卡里·特尔曼

会话式界面是一个永远处于变革世界边缘的想法。其潜力是不可否认的:每个人都拥有与生俱来的、未被开发的会话专业知识。我们可以摒弃视觉界面所需的嵌套菜单;用户可以命名的任何东西都可以立即触手可及。我们可以将自然语言变成声明式脚本语言,并将操作系统变成 IDE(集成开发环境)。

然而,现实并没有达到这种潜力。大多数人对手机和智能设备中的会话代理的使用仅限于提醒和计时器——如果他们使用它们的话。Semantic Machines 正在创建一个会话式界面框架,旨在释放这种潜力。它目前正在为 Outlook Mobile 中的会话式界面提供支持,其他产品也将陆续推出。

为了实现这一目标,Semantic Machines 框架结合了机器学习的一些最新进展,以及可以追溯到人工智能研究早期的概念和方法。为了理解原因,让我们回顾 50 年,看看有史以来最早也是最成功的会话代理之一。

 

历史回顾

1972 年,特里·威诺格拉德发表了一篇论文(“理解自然语言”,《认知心理学》3(1),1-191),描述了他于 1960 年代后期从事的一个软件项目。该项目允许用户指示一个名为 SHRDLU 的虚拟机械臂与一个由桌子、盒子和一些不同形状和颜色的积木组成的世界进行交互。用户可以与 SHRDLU 进行对话,提出问题和发出指令

 

- 拿起一个大的红色积木。
好的。

- 找到一个比你现在拿着的积木更高的积木,然后把它放到盒子里。
通过“它”,我假设你的意思是比我拿着的积木更高的那个积木。
好的。

- 盒子里有什么?
蓝色金字塔和蓝色积木。

- 有多少积木不在盒子里?
四个。

- 它们中至少有一个比我告诉你要拿起的那个更窄吗?
是的,红色立方体。

- 它被支撑着吗?
是的,被桌子支撑着。

- 桌子可以拿起积木吗?
不能。

- 金字塔可以被积木支撑吗?
可以。

- 金字塔可以支撑金字塔吗?
我不知道。

- 堆叠两个金字塔。
(尝试中)我不能。

 

威诺格拉德的项目代表了人工智能研究历史上的一个关键点。早期的努力更加雄心勃勃;西蒙、肖和纽厄尔的通用问题求解器 (GPS) 是一个于 1958 年推出的计算机程序,它不仅被认为是一种实现类人行为的方法,而且还被认为是一种人类认知的描述性模型。正如早期人工智能研究的规范一样,他们将问题简化为搜索问题。给定一个初始状态和一个期望的最终状态,GPS 将搜索所有可能的行动序列,直到找到一个导致该最终状态的行动序列。由于搜索树的分支因子会非常高——在大多数情况下,你几乎可以做任何事情——GPS 需要使用启发式方法(来自希腊语 heureka,如“我找到了!”)来确定哪些行动在给定情况下可能有用。

有了描述思维引擎的这项研究,剩下的就是知识工程:为人类生活的所有方面创建一个可能的行动和相关启发式方法的存储库。不幸的是,这被证明比预期的要困难。随着各种知识工程项目的停滞,研究人员专注于“微观世界”中的问题解决:虚拟环境,在这些环境中,状态易于表示,可能的行动易于枚举。威诺格拉德的微观世界是这些努力的顶峰;SHRDLU 对其环境及其可用于描述该环境的英语子集的掌握是不言而喻的。

尽管如此,尚不清楚如何将微观世界变成更有用的东西;SHRDLU 环境的边界在其实现的每个层面都被依赖。哲学教授和早期人工智能研究的主要批评家休伯特·德雷福斯将这些项目描述为“为巧妙选择的问题提供的临时解决方案,这些问题给人以复杂的智力活动的错觉”。最终,德雷福斯被证明是正确的;每次尝试推广或拼接这些项目都失败了。

接下来发生的事情是一个熟悉的故事:1970 年代中期,研究经费枯竭,标志着人工智能寒冬的开始。在 1980 年代通过销售所谓的“专家系统”商业化过去的研究的一些失败尝试之后,该领域沉寂了数十年,然后是通常被称为机器学习的统计技术的复兴。

总的来说,人工智能研究的早期时代被视为历史的好奇心;一群研究人员对他们可以实现的目标做出了非常乐观的预测,但失败了。他们可能有什么可以教给我们的呢?当然,最好展望研究的前沿,而不是回顾这些被遗弃的微观世界。

然而,我们必须承认,与现代会话代理相比,威诺格拉德的 SHRDLU 具有惊人的复杂性。这些代理在一个名为“槽位和意图”的模型上运行,这实际上是反向的填字游戏。给定用户的一些文本(话语),系统识别相应的模板(意图),然后提取出话语的片段(槽位)。然后将这些片段输入到一个执行与意图相关的任务的函数中。

例如,考虑一个函数 order_pizza(size, toppings)。槽位和意图框架可以轻松地在“给我订一个中号意大利辣香肠和蘑菇披萨”和 order_pizza("medium", ["pepperoni", "mushrooms"]) 之间提供映射。它允许将语言问题与订购披萨所需的实际业务逻辑分离开来。

但是考虑一下与 SHRDLU 对话中的第二个话语

- 找到一个比你现在拿着的积木更高的积木,然后把它放到盒子里。

由于以下几个原因,很难将此话语建模为意图。它描述了两个动作,但是由于每个意图都映射到一个函数,因此您必须定义一个复合函数

find_block_and_put_into_box(...)

并为您想要支持的任何其他复合动作定义类似的函数。

但即使这还不够;通过简单地调用

find_block_and_put_into_box("比你现在拿着的更高的")

您正在让语言问题渗透到业务逻辑中。最多,您希望业务逻辑解释诸如 更高更窄 等单个词,但这将需要一个更具体的函数

find_block_which_is_X_than_held_block_and_put_in_box("更高")

问题在于自然语言是组合的,而槽位和意图框架不是。开发人员必须枚举他们希望支持的这些原语的每种配置,而不是定义一组可以自由组合的原语(“找到一个积木”、“比...更高”、“拿着的积木”等)。在实践中,这导致会话代理的焦点狭窄且容易混淆。

威诺格拉德的 SHRDLU 尽管有其局限性,但却灵活得多。在 Semantic Machines,我们正在构建一个对话系统,该系统将保留这种灵活性,同时避免大多数限制。

 

计划

在 Semantic Machines 对话系统中,话语被翻译成小程序,由于 历史原因,这些小程序被称为计划。给定有问题的话语

- 找到一个比你现在拿着的积木更高的积木,然后把它放到盒子里

我们的计划模型,它是一个 基于 Transformer 的编码器-解码器神经网络,将返回如下内容

find_block(
  (b: Block) =>
    taller_than(b, held_block())
)
put_in_box(the[Block]())

这是用 Express 呈现的,Express 是一种内部语言,其语法模仿 Scala。请注意,计划中的每个符号几乎都与话语的一部分一一对应,甚至包括一个特殊的 the() 函数,该函数解析“它”指的是什么。这是因为计划模型仅用于翻译话语,而不是解释话语。

这样做的原因并不立即显而易见;对于大多数经验丰富的开发人员来说,诸如 taller_than 之类的函数似乎是不必要的间接层。为什么不直接内联它呢?

find_block(
  (b: Block) =>
    b.height > held_block().height
)

然而,这种间接性是有价值的。在正常的代码库中,函数名称不会公开;只要对团队中的其他人有意义,就可以为它们分配任何含义。相反,这些函数是系统和用户之间的接口,它们的含义由用户的意图定义。随着时间的推移,这种含义几乎肯定会变得更加细致。例如,我们可能会意识到,当人们说“比...更高”时,他们指的是明显更高

def taller_than(a: Block, b: Block) =
  (a.height - b.height) > HEIGHT_EPSILON

如果维护了间接层,那么这是一个对函数定义进行简单的一行更改,并且计划模型的训练数据集保持不变。但是,如果函数已内联,则必须仔细迁移训练数据集;a.height > b.height 应该仅在它对应于话语中的“比...更高”时才更新。

专注于翻译使训练数据永恒,即使我们修改语义,也允许数据集单调增长。将每个自然语言概念与一个函数匹配,使语义保持显式和一致。然而,这种方法假设含义在很大程度上与上下文无关。我们的计划模型受到语言类型系统的约束,因此如果话语中没有提及积木,它将不会使用与积木相关的函数;否则,您可以假设“比...更高”始终可以翻译为 taller_than

当然,对于诸如“它”、“那个”或“它们”之类的不定冠词来说,情况并非如此;它们的含义完全取决于对话前面所说的内容。在 Semantic Machines 系统中,所有此类引用都被翻译成对 the() 的调用。这是可能的,因为 Express 运行时保留了当前对话中每个计划的完整执行,包括所有中间结果。此数据以数据流图的形式存储,表示会话上下文:我们已经讨论过并可能希望稍后引用的内容。某些特殊函数(例如 the())可以查询该图,搜索正在引用的表达式。

在 SHRDLU 中,这些不定冠词在其解析阶段得到解析,该阶段将话语转换为其自己的计划版本。然而,解析并不总是由话语的语法结构决定的;有时您需要理解其语义。考虑以下两个命令

 

- 将红色积木放在绿色积木下面,并将金字塔放在它的上面。

- 将红色积木放在绿色积木上面,并将金字塔放在它的上面。

 

常识表明,金字塔应该放在无论哪个积木在另一个积木之上。为了根据这种常识采取行动,SHRDLU 不得不放弃对句法和语义分析的任何有意义的分离,这在一定程度上解释了为什么它如此难以扩展。在 Semantic Machines 系统中,解析由一个完全独立的模型驱动,该模型在可能的情况下使用句法启发式方法,并在必要时使用特定领域的语义。然而,对于大多数开发人员来说,知道“它”和“那个”翻译成 the() 就足够了。

 

约束

请注意,在前面显示的计划模型中,我们向 find_block 传递了一个谓词,其中包含我们希望找到的积木的标准

find_block(
  (b: Block) =>
    taller_than(b, held_block())
)

这是因为用户尚未透露他们想要哪个积木,而只是提供了查找积木的标准。这称为内涵描述,与外延描述相反,外延描述指定实际的实体或实体。在实践中,对话中引用的每个实体都是内涵引用的;对“Alice”的引用将被翻译成

the[Person](p => p.name ~= "Alice")

其中 ~= 表示类似于。执行时,the() 将尝试在对话历史记录中查找名为 Alice 的人,但不能保证一定存在。用户可能会认为,考虑到他们的身份,系统可以弄清楚他们指的是谁。也许有一个特定的 Alice 与他们一起工作,或者他们家庭中的某人名叫 Alice。在任何一种情况下,用户都认为他们提供了足够的信息,因此我们必须弄清楚在给定的上下文中什么是有意义的。

如果 the() 未能在对话上下文中找到匹配项,它将调用与 Person 数据类型关联的解析器函数。给定用户提供的谓词,Person 解析器实际上应该如何工作?我们不能简单地扫描所有可能的人的列表并将谓词用作过滤器;该数据集位于其他地方,并且不太可能容易访问。由于实际和隐私方面的考虑,它几乎肯定会通过具有访问控制和有限 API 的服务公开。

然后,解析器必须将谓词转换为对提供有关人员信息的后端服务的一个或多个查询。为此,我们必须停止将其视为谓词,而开始将其视为约束。

许多开发人员可能听说过 SAT(可满足性)求解器,它在给定一个或多个布尔值的约束的情况下,将尝试查找满足赋值。给定 a && !b,它将返回 a == true, b == false。给定 a && !a,它将说明约束不可满足。由于 各种问题 可以映射到此表示形式,因此 SAT 求解器被广泛使用。 SMT(模理论可满足性)求解器概括了此功能,它可以解决更广泛数据类型的更复杂约束。

然而,这两种求解器都没有办法指定“该值必须对应于后端服务中的实体”。即使它有,您可能也不想使用它;您不希望求解器在搜索可能值的同时向后端服务发出数十个类似于“Alice”的查询。只有构建在对话系统之上的领域开发人员才能理解其后端服务的功能和成本。例如,服务的查询 API 可能提供其自己的“类似于”运算符。然而,他们的相似性指标可能不会反映出有些人互换使用 Misha 和 Mikhail。领域开发人员必须在保留用户意图和最大限度地减少他们每次话语发出的请求数量之间保持平衡。

由于我们无法为领域开发人员完全解释约束,因此我们必须为他们提供自己的解释工具。像解析器一样解释约束的域函数称为控制器。在当前版本的 Semantic Machines 系统中,控制器通常以 TypeScript 编写,因为该语言很可能是一种编写复杂领域逻辑的熟悉且富有表现力的方式。在控制器中,谓词被转换为约束拉链,这允许它们遍历、查询和转换复杂数据类型上的约束。对于每个字段和子字段,领域开发人员可以提出各种问题:是否存在下限或上限?满足值的一个示例是什么?那是唯一满足值吗?该值是否满足约束?

最后一个问题至关重要,因为将整个约束编码到后端服务的查询中并不总是可行的。返回的结果集可能太广泛,因此必须使用约束进行后过滤。相反,与服务 API 中的查询运算符相对应的运算符(例如 ~=)可以配置为抽象命名属性。导航到 Person.name 时,您可以查找 ~= 的抽象属性并检查其参数的拉链以构建查询。

早期的人工智能研究人员设想了一个知识具有单一表示形式和单一存储库的世界。相反,我们生活在一个数据以及解释数据的能力分散且扩散的世界中。因此,我们的约束求解器必须具有非同寻常的可扩展性,从而允许开发人员将其与自己的系统和领域专业知识相结合。

 

修订

解释用户意图的一个主要挑战是所有未说出口的内容。剥夺了任何上下文,我们说的大部分内容都是模棱两可的。要解释“我要去银行”,您需要知道说话者是否在河边附近。在语言学中,研究上下文如何赋予意义的学科称为语用学。然后,我们的对话系统需要为开发人员提供工具,以便轻松指定特定领域的语用学。

例如,如果在 Outlook Mobile 中,用户说“将我与 Alice 的会议重新安排到下周”,您可以合理地假设他们指的是即将到来的会议,因为日历中几乎所有操作都侧重于即将发生的事件。如果您认为这始终是正确的,则可以将每个用户对事件的内涵进一步约束为在未来开始

 

def add_pragmatics(
  predicate: Event => Boolean,
): Event => Boolean = {
  e => predicate(e) && e.start > now()
}

 

但是,如果用户想要重新安排过去被取消的会议怎么办?将此函数应用于“将昨天与 Alice 的会议重新安排到下周”会将事件约束为既在昨天又在未来;约束将无法满足。然后,默认假设不能与用户提供的任何内容混合在一起;它们必须像任何其他默认值一样被选择性地覆盖。幸运的是,有一种解决方案可以跨越所有领域

 

def add_pragmatics(
  predicate: Event => Boolean,
): Event => Boolean = {
  revise(
    e => e.start > now(),
    predicate,
  )
}

 

在我们的系统中,revise 是一个强大的运算符,给定两个约束 a b,它将丢弃使 b 失去意义的 a 的部分,并将其余部分与 b 连接起来。考虑查询“昨天的会议”,我们在其中使用用户的意图修订一些基本语用学

 

revise(
  e =>
    e.start > now() &&
    e.attendees.contains(me()),
  e => e.start.date == yesterday(),
)

 

默认假设是被引用的事件在未来开始并且用户将参加。然而,这些默认值的第一个子句与用户的意图相矛盾。然后,修订的结果将由第二个默认子句和用户的意图组成

 

e =>
  e.start.date == yesterday() &&
  e.attendees.contains(me())

 

然而,仅仅寻找矛盾是不够的。考虑查询今年年初以来的所有事件

 

revise(
  e =>
    e.start > now() &&
    e.attendees.contains(me()),
  e => e.start > beginning_of_year()

 

在这种情况下,用户的意图没有被默认假设反驳,而是被默认假设暗示。如果事件在未来开始,则它必然发生在今年年初之后。如果我们不删除 e.start > now(),我们将有效地忽略用户所说的内容。

由于矛盾和暗示都与数据类型的内在属性有关(而不是与外在属性有关,例如,“这对应于后端服务中的实体”),因此我们的系统可以自行处理修订过程。开发人员可以简单地专注于为其领域定义适当的语用学。

修订运算符的存在,加上用户以意向方式说话的事实,也意味着用户能够调整和建立在他们已经说过的内容之上。

考虑话语“取消我与 Alice 的会议”。如果用户和 Alice 在同一个团队工作,他们很可能一起有多个即将到来的会议。我们可以猜测他们指的是哪一个,但在实际取消会议之前,我们将向他们展示事件的描述并要求确认。

通常,确认涉及让用户在“确定”和“取消”之间进行选择;要么我们完全按照他们想要的方式做了,要么他们需要重新开始。然而,修订意味着没有必要重新开始。如果用户在“取消我与 Alice 的会议”之后跟进“我指的是一对一的会议”,我们将使用第二个意图修订第一个意图,并寻找与 Alice 的一对一会议。

这对用户来说是非常自由的,因为这意味着他们不需要将他们想要的一切都塞进一个单一的、庞大的话语中。这类似于批处理和交互式计算之间的区别;用户可以尝试操作,查看会发生什么,并快速建立在他们的成功之上。

这对开发人员来说也是非常自由的,因为这意味着他们可以承担犯错的风险。我们提供了我们能提供的最好的工具来帮助开发人员解释用户的意图,但误解的代价很小。在最坏的情况下,用户将被迫提供越来越多的信息。

最终想法

在任何可能的情况下,业务逻辑都应由代码而不是训练数据来描述。这使我们系统的行为保持原则性、可预测性和易于更改。我们的会话式界面方法允许它们像任何其他应用程序一样构建,使用熟悉的工具、约定和流程,同时仍然利用尖端的机器学习技术。

然而,在重新审视早期人工智能研究时代的想法时,必须谨慎;如果全盘使用这些最初的想法,很可能会使我们走上与最初提出这些想法的人相同的道路。有时,就像计划一样,我们必须进行细微的修改。有时,就像约束一样,我们必须承认早期研究人员甚至没有想象到的复杂性。有时,就像修订一样,我们必须创造全新的东西。

这些经过适当清理和重新考虑的想法,可能是我们的行业与快速扩展的计算语言学前沿之间的一座重要桥梁。

 

扎卡里·特尔曼 在微软研究院工作,在那里他正在帮助设计和构建会话式界面框架。他是《Clojure 要素》的作者,并且正在撰写一本新书,暂定名为《关于软件设计》。

版权所有 © 2023 归所有者/作者所有。出版权已许可给 。

acmqueue

最初发表于 Queue 第 21 卷第 1 期
数字图书馆 中评论本文





更多相关文章

马克·鲁西诺维奇、艾哈迈德·萨利姆、圣地亚哥·扎内拉-贝格林、约纳坦·尊格 - 智能的代价
大型语言模型易于产生幻觉、提示注入和越狱,这对它们的广泛采用和负责任的使用构成了重大但可以克服的挑战。我们认为,这些问题是固有的,肯定在当前一代模型中是这样,并且可能在大型语言模型本身中也是如此,因此我们的方法永远不能基于消除它们;相反,我们应该应用“纵深防御”策略来缓解它们,并且在构建和使用这些系统时,应假设它们有时会在这些方向上失败。


索尼娅·约翰逊-于、桑克特·沙阿 - 你对人工智能一窍不通
长期以来,很难确定人工智能到底是什么。几年前,此类讨论会演变成长达数小时的会议,在会上绘制维恩图并试图绘制出人工智能的不同子领域。快进到 2024 年,我们现在都知道人工智能到底是什么了。人工智能 = ChatGPT。或者不是。


吉姆·瓦尔多、索琳·布萨尔 - GPT 和幻觉
本实验的发现支持以下假设:基于大型语言模型的 GPT 在更受欢迎且已达成普遍共识的提示上表现良好,但在有争议的主题或数据有限的主题上表现不佳。应用程序响应的可变性突显了模型依赖于其训练数据的数量和质量,这与依赖于多样化和可信贡献的众包系统类似。因此,虽然 GPT 可以作为许多日常任务的有用工具,但应谨慎解释它们对晦涩和两极分化主题的参与。


埃里克·梅杰 - 虚拟阴谋:将大型语言模型用作神经计算机
我们探讨了大型语言模型 (LLM) 如何不仅可以充当数据库,还可以充当动态的、最终用户可编程的神经计算机。此神经计算机的本机编程语言是一种受逻辑编程启发的声明性语言,该语言形式化并外化了思维链推理,因为它可能发生在大型语言模型内部。





© 保留所有权利。

© . All rights reserved.