人工智能标记语言(Artificial Intelligence Markup Language,AIML)是由 Richard Wallace 和世界各地的自由软件社区在 1995 年至 2002 年发明的。它是一种基于 XML 的语言,可用于创建智能机器人或虚拟助。
AIML中的主要元素是模式(pattern)、模板(template)、类别(category)和变量(variable)。
- 模式(pattern)是用户可能会输入的问题或陈述
- 模板(template)是机器人对该问题或陈述的响应
- 类别(category)是模式和模板的组合
- 变量(variable)用于存储和处理用户输入和机器人输出中的信息
基于 AIML 可以快速开发基于规则的机器人,并可以轻松地扩展和修改现有机器人的功能。它的缺点是需要手工编写大量的匹配规则,当然构建工作也可以基于一些开源的 AIML 语料库,这样会节省不少的时间。安装命令如下:
pip install aiml
这篇文章简单介绍下,AIML 的 Hello World 程序,以及其常用的一些标签。
https://www.udemy.com/course/artificial-intelligence-markup-language/
- Hello World {#title-0} =========================
一个基于 AIML 的聊天机器人构建的基本过程有:
- 首先,创建对话语料。这个对话语料其实就是针对不同领域、不同意图、或者不同侧重点的对话规则,每一类的规则创建一个 .aiml 文件
- 然后,使用 aiml 库加载学习我们创建的一系列规则
- 最后,输入问题匹配规则输出响应
创建一个语料文件,暂时命名为 test.aiml,内容如下:
<aiml version="1.0.1" encoding="UTF-8">
<category>
<pattern>你好</pattern>
<template>你也好啊,朋友。</template>
</category>
<category>
<pattern>你叫什么名字啊?</pattern>
<template>我叫刘德华。</template>
</category>
<category>
<pattern>你今年几岁了?</pattern>
<template>我刚诞生 1000 年!</template>
</category>
</aiml>
接下来创建 main.py 文件,内容如下:
import aiml
def test():
# 初始化 AIML 对象
robot = aiml.Kernel()
# 学习 test.aiml 语料文件
robot.learn('test.aiml')
# 输入问题,返回响应
response = robot.respond('你好')
print(response)
response = robot.respond('你叫什么名字啊?')
print(response)
response = robot.respond('你今年几岁了?')
print(response)
if __name__ == '__main__':
test()
程序输出结果:
Loading test.aiml...done (0.17 seconds)
你也好啊,朋友。
我叫刘德华。
我刚诞生 1000 年!
这里需要注意的是,上面例子中输入的问题要和 test.aiml 中定义的 pattern 完全匹配,否则将不会得到对应的相应。这给我们构造对话规则带来很大的麻烦,所以,接下来需要学习一些 AIML 中的特殊标签,来帮助我们增加规则构造的灵活性。
Doc:http://www.aiml.foundation/
- <star/> 标记 {#title-1} ==========================
我们在预先设定问题时,可以使用 * 号这样的通配符来进行模糊匹配,并可以在回答中通过使用 <star/> 来引用模糊匹配到的内容。下面举个使用例子:
假设问题 pattern 设置为:我 喜 欢 *
那么,当输入问题为:我 喜 欢 篮 球,上面的 pattern 也能够匹配到,并且我们在 template 中使用 <star/> 就使用匹配到的 "篮球"
我们编写 test.aiml 文件内容如下:
<aiml version="1.0.1" encoding="UTF-8">
<category>
<pattern>我 喜 欢 *</pattern>
<template>我知道了,你喜欢<star/></template>
</category>
<category>
<pattern>* 是 *</pattern>
<template>原来<star index="1"/>是<star index="2"/>啊!</template>
</category>
</aiml>
上述内容中,<star index="1″> 表示引用第一个通配符 * 号匹配到的内容,如果 index="2″ 的话,那么就匹配第二个 * 号匹配到的内容。main.py 文件内容如下:
import aiml
def test():
robot = aiml.Kernel()
robot.learn('test.aiml')
response = robot.respond('我 喜 欢 苹 果')
print(response)
# xml 中问题以空格隔开,并且输入问题也用空格隔开
response = robot.respond('A I M L 是 人 工 智 能 标 记 语 言')
print(response)
if __name__ == '__main__':
test()
输出内容:
Loading test.aiml...done (0.18 seconds)
我知道了,你喜欢苹 果
原来A I M L是人 工 智 能 标 记 语 言啊!
这里你可能注意到,在 test.aiml 和 main.py 中我设置的 pattern 和输入的问题,都已经用空格隔开了。
- <srai> 标记 {#title-2} =========================
假设:我们定义了一组 pattern 和 template,对于 pattern 还有一不同的表达方式,我们都可以通过 <srai> 标签将这些表达转到先前定义的 pattern 上进行匹配。
<aiml version="2.0" encoding="UTF-8">
<category>
<pattern>再 见</pattern>
<template>再见,我的朋友</template>
</category>
<category>
<pattern>拜 拜</pattern>
<template><srai>再 见</srai></template>
</category>
<category>
<pattern>拜 拜 *</pattern>
<template><srai>再 见</srai></template>
</category>
<category>
<pattern>A I M L</pattern>
<template>AIML 是人工智能标记语言啊</template>
</category>
<category>
<pattern>* A I M L</pattern>
<template><srai>A I M L</srai></template>
</category>
<category>
<pattern>A I M L *</pattern>
<template><srai>A I M L</srai></template>
</category>
<category>
<pattern>* A I M L *</pattern>
<template><srai>A I M L</srai></template>
</category>
</aiml>
main.py 内容如下:
import aiml
def test():
robot = aiml.Kernel()
robot.learn('test.aiml')
response = robot.respond('再 见')
print(response)
response = robot.respond('拜 拜')
print(response)
response = robot.respond('拜 拜 ,我 的 朋 友')
print(response)
response = robot.respond('A I M L')
print(response)
response = robot.respond('我 以 前 听 过 A I M L')
print(response)
response = robot.respond('A I M L 是 一 个 什 么 东 西')
print(response)
response = robot.respond('我 以 前 听 过 A I M L , 但 是 不 理 解')
print(response)
if __name__ == '__main__':
test()
程序输出结果:
再见,我的朋友
再见,我的朋友
再见,我的朋友
AIML 是人工智能标记语言啊
AIML 是人工智能标记语言啊
AIML 是人工智能标记语言啊
AIML 是人工智能标记语言啊
我们将 "拜 拜" 和 "拜 拜 ,我 的 朋 友" 都转到 "再见" 这个 pattern 进行匹配。第二个例子是将输入中包含了指定的 "A I M L" 的所有 pattern 都转到 "A I M L" pattern 对应的 template 作为输出。
- <set> 和 <get> 标记 {#title-3} ==================================
<set> 和 <get> 标记用于定义一些变量,这些变量可以保存聊天过程中的一些重要信息,以便于在下文中引用该信息。例如:聊天上文中提到了用户的名字,那么可以用 <set> 标记存储下来,下文中需要使用该名字时,就用 <get> 获得改名字。test.aiml 文件内容如下:
<aiml version="1.0.1" encoding="UTF-8">
<category>
<pattern>你 叫 什 么 名 字?</pattern>
<template>我叫 Robot, 你叫什么名字?</template>
</category>
<category>
<pattern>我 叫 *</pattern>
<template>很高兴认识你: <set name="name"><star/></set></template>
</category>
<category>
<pattern>你 还 记 得 我 叫 什 么 吗 ?</pattern>
<template>我知道你叫<get name="name"></get></template>
</category>
</aiml>
main.py 文件内容如下:
import aiml
def test():
robot = aiml.Kernel()
robot.learn('test.aiml')
response = robot.respond('你 叫 什 么 名 字 ?')
print(response)
response = robot.respond('我 叫 刘 德 华')
print(response)
response = robot.respond('你 还 记 得 我 叫 什 么 吗 ?')
print(response)
if __name__ == '__main__':
test()
程序输出结果:
Loading test.aiml...done (0.16 seconds)
我叫 Robot, 你叫什么名字?
很高兴认识你: 刘 德 华
我知道你叫刘 德 华
- <that> 标记 {#title-4} =========================
<that> 标记可以用来限定上文。这么说其实很不明确。我们举个例子:
Human:你读过《实战俄语》吗 ?
Robot:我读过,你觉得这本书怎么样?
Human:不错
Robot:我也觉得这本书不错!
上面的对话中,当 Robot 说过 "我读过,你觉得这本书怎么样?" ,此时 Human 再回答 "不错",那么 Robot 才会输出 "我也觉得这本书不错!"
也就是说,Robot 最后一句话的输出必须之前输出过 "我读过,你觉得这本书怎么样?" 这句话。这个 that 其实就是将上文的某个输出作为当前 pattern 匹配的限制条件。再举个例子:
Human:不 好
Robot:什么不好?
假设没有上文限制条件,那么当 Human 直接输入 "不好",则 Robot 直接匹配不带限制条件的 "不好" 对应的输出。
再比如,如下的例子:
Human:你读过《实战俄语》吗 ?
Robot:我读过,你觉得这本书怎么样?
Human:你吃饭了吗?
Robot:我不会饿,不需要吃饭哦!
Human:不好
Robot:什么不好?
这个例子中,虽然上文中存在上文 "我读过,你觉得这本书怎么样?",但是中间被其他问题打断了,那么 AIML 会匹配不带限制条件的 "不好" 对应的输出。
我们看下完整的例子,test.aiml 文件内容如下:
<aiml version="1.0.1" encoding="UTF-8">
<category>
<pattern>你 读 过 《 实 战 俄 语 》 吗 ?</pattern>
<template>我读过,你觉得这本书怎么样?</template>
</category>
<category>
<pattern>你 吃 饭 了 吗?</pattern>
<template>我不会饿,不需要吃饭哦!</template>
</category>
<category>
<pattern>不 错</pattern>
<that>我读过,你觉得这本书怎么样?</that>
<template>我也觉得这本书不错!</template>
</category>
<category>
<pattern>不 错</pattern>
<template>我也觉得这本书不错!</template>
</category>
<category>
<pattern>不 好</pattern>
<template>什么不好?</template>
</category>
<category>
<pattern>不 好</pattern>
<that>我读过,你觉得这本书怎么样?</that>
<template>我也觉得马马虎虎吧!</template>
</category>
</aiml>
main.py 文件内容如下:
import aiml
def test01():
robot = aiml.Kernel()
robot.learn('test.aiml')
response = robot.respond('你 读 过 《 实 战 俄 语 》 吗 ?')
print(response)
response = robot.respond('不 错')
print(response)
def test02():
robot = aiml.Kernel()
robot.learn('test.aiml')
response = robot.respond('你 读 过 《 实 战 俄 语 》 吗 ?')
print(response)
response = robot.respond('不 好')
print(response)
def test03():
robot = aiml.Kernel()
robot.learn('test.aiml')
response = robot.respond('不 好')
print(response)
def test04():
robot = aiml.Kernel()
robot.learn('test.aiml')
response = robot.respond('你 读 过 《 实 战 俄 语 》 吗 ?')
print(response)
response = robot.respond('你 吃 饭 了 吗?')
print(response)
response = robot.respond('不 好')
print(response)
if __name__ == '__main__':
test01()
test02()
test03()
test04()
程序输出结果:
Loading test.aiml...done (0.16 seconds)
我读过,你觉得这本书怎么样?
我也觉得这本书不错!
Loading test.aiml...done (0.00 seconds)
我读过,你觉得这本书怎么样?
我也觉得马马虎虎吧!
Loading test.aiml...done (0.00 seconds)
什么不好?
Loading test.aiml...done (0.00 seconds)
我读过,你觉得这本书怎么样?
我不会饿,不需要吃饭哦!
什么不好?
- <think> 标记 {#title-5} ==========================
我们在第 4 部分介绍 <set> 用法时,知道该标记可以定义一些变量,以便用于到下文中的对话。但是,细心的你可能发现了,<set> 在设置变量的时候,会将变量的值输出。如果我们就像在后台偷偷设置,而不进行显示的话,可以将其放在 <think> 标记内部。test.aiml 内容如下:
<aiml version="1.0.1" encoding="UTF-8">
<category>
<pattern>你 叫 什 么 名 字?</pattern>
<template>我叫 Robot, 你叫什么名字?</template>
</category>
<category>
<pattern>我 叫 *</pattern>
<!-- 将set操作放到think标记内,就不会进行输出操作 -->
<template>很高兴认识你! <think><set name="name"><star/></set></think></template>
</category>
<category>
<pattern>你 还 记 得 我 叫 什 么 吗 ?</pattern>
<template>我知道你叫<get name="name"></get></template>
</category>
</aiml>
main.py 内容如下:
import aiml
def test():
robot = aiml.Kernel()
robot.learn('test.aiml')
response = robot.respond('你 叫 什 么 名 字 ?')
print(response)
response = robot.respond('我 叫 刘 德 华')
print(response)
response = robot.respond('你 还 记 得 我 叫 什 么 吗 ?')
print(response)
if __name__ == '__main__':
test()
程序输出结果:
Loading test.aiml...done (0.05 seconds)
我叫 Robot, 你叫什么名字?
很高兴认识你!
我知道你叫刘 德 华
可以看到 <set> 标记在记录变量时候,并不会将变量的值输出。
- <condition> 标记 {#title-6} ==============================
从名字上来看,该比较可以设置条件。也就是说,template 内部可以设置多条不同的输出,具体输出什么值要看能够满足哪个条件。test.aiml 文件内容如下:
<aiml version="1.0.1" encoding="UTF-8">
<category>
<pattern>*</pattern>
<template>我不理解你的问题!</template>
</category>
<category>
<pattern>名 字 是 * </pattern>
<template>
<!-- 先设置条件变量 -->
<think><set name="username"><star /></set></think>
<!-- 根据username选择不同分支输出 -->
<condition name="username">
<li value="张 三">张三名字真简洁!</li>
<li value="李 四">李四,我是赵六啊!</li>
<li>我不认识你</li>
</condition>
</template>
</category>
</aiml>
main.py 内容如下:
import aiml
def test():
robot = aiml.Kernel()
robot.learn('test.aiml')
response = robot.respond('名 字 是 张 三')
print(response)
response = robot.respond('名 字 是 李 四')
print(response)
response = robot.respond('名 字 是 王 五')
print(response)
response = robot.respond('你 知 道 我 是 谁 吗 ?')
print(response)
if __name__ == '__main__':
test()
程序输出结果:
Loading test.aiml...done (0.16 seconds)
张三名字真简洁!
李四,我是赵六啊!
我不认识你
我不理解你的问题!
- <topic> 标记 {#title-7} ==========================
通过 <topic> 标记,我们可以将多个 pattern 按照主题进行分组。需要注意的是,当确定一个主题时,只能匹配该主题内部定义的 pattern,否则会匹配失败。test.aiml 文件内容如下:
<aiml version="1.0.1" encoding="UTF-8">
<category>
<pattern>我 们 聊 下 关 于 足 球 的 话 题 吧 ?</pattern>
<!-- 输出内容,并设定接下来要聊天的主题 -->
<template>好哇,你想了解哪些关于足球的问题呢?<think><set name="topic">足球</set></think></template>
</category>
<category>
<pattern>我 们 聊 下 关 于 篮 球 的 话 题 吧 ?</pattern>
<!-- 输出内容,并设定接下来要聊天的主题 -->
<template>好哇,你想了解哪些关于篮球的问题呢?<think><set name="topic">篮球</set></think></template>
</category>
<!-- 如果 topic=足球的话,那么问篮球主题的问题会找不到匹配的问题 -->
<topic name="足球">
<category>
<pattern>足 球 比 赛 一 般 多 少 人 参 加 ?</pattern>
<template>这个我不太清楚啊!</template>
</category>
<category>
<pattern>中 国 男 足 球 技 怎 么 样 ?</pattern>
<template>这么说吧,那些世界列强从没赢过中国足球。</template>
</category>
</topic>
<!-- 如果 topic=篮球的话,那么问足球主题的问题会找不到匹配的问题 -->
<topic name="篮球">
<category>
<pattern>中 国 篮 球 明 星 都 有 谁 ?</pattern>
<template>我就知道姚明,别的我就不清楚了!</template>
</category>
<category>
<pattern>中 国 男 篮 能 打 得 过 中 国 男 足 吗 ?</pattern>
<template>够呛,因为人多的不要脸啊!</template>
</category>
</topic>
</aiml>
main.py 文件内容如下:
import aiml
def test():
robot = aiml.Kernel()
robot.learn('test.aiml')
# 切换到足球话题
response = robot.respond('我 们 聊 下 关 于 足 球 的 话 题 吧 ?')
print(response)
response = robot.respond('中 国 男 足 球 技 怎 么 样 ?')
print(response)
# 切换到篮球话题
response = robot.respond('我 们 聊 下 关 于 篮 球 的 话 题 吧 ?')
print(response)
# 错误: 由于单签主题是篮球,询问足球的问题,此时会提示匹配不到问题答案
response = robot.respond('足 球 比 赛 一 般 多 少 人 参 加 ?')
print(response)
response = robot.respond('中 国 男 篮 能 打 得 过 中 国 男 足 吗 ?')
print(response)
if __name__ == '__main__':
test()
程序输出结果:
Loading test.aiml...done (0.18 seconds)
好哇,你想了解哪些关于足球的问题呢?
这么说吧,那些世界列强从没赢过中国足球。
好哇,你想了解哪些关于篮球的问题呢?
够呛,因为人多的不要脸啊!
WARNING: No match found for input: 足 球 比 赛 一 般 多 少 人 参 加 ?
- <random> 标记 {#title-8} ===========================
有时候我们想从多个回答中随机选择一个,就可以使用 <random> 标记来完成。test.aiml 文件内容如下:
<aiml version="1.0.1" encoding="UTF-8">
<category>
<pattern>你 好</pattern>
<template>
<!-- 随机选择一个回复 -->
<random>
<li>HI 朋友!</li>
<li>你也好!</li>
<li>Hello Friend!</li>
</random>
</template>
</category>
</aiml>
main.py 文件内容如下:
import aiml
def test():
robot = aiml.Kernel()
robot.learn('test.aiml')
response = robot.respond('你 好')
print(response)
response = robot.respond('你 好')
print(response)
response = robot.respond('你 好')
print(response)
if __name__ == '__main__':
test()
程序执行结果:
Loading test.aiml...done (0.16 seconds)
Hello Friend!
你也好!
你也好!
- <system> 标记 {#title-9} ============================
<system> 标记中可以编写一些命令,例如:ls,或者使用 python 命令执行一个 python 文件,并将文件的 print 结果作为响应。test.aiml 文件内容如下:
<aiml version="1.0.1" encoding="UTF-8">
<category>
<pattern>生 成 一 个随 机 列 表</pattern>
<template>
<!--执行一个python文件-->
<system>python my.py</system>
</template>
</category>
</aiml>
my.py 内容如下:
import random
def test():
number = [random.randint(0, 10) for _ in range(10)]
return str(number)
if __name__ == '__main__':
print(test())
main.py 内容如下:
import aiml
def test():
robot = aiml.Kernel()
robot.learn('test.aiml')
response = robot.respond('生 成 一 个随 机 列 表')
print(response)
if __name__ == '__main__':
test()
程序输出内容如下:
Loading test.aiml...done (0.16 seconds)
[5, 0, 4, 6, 8, 3, 8, 6, 0, 10]