51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

人工智能标记语言(AIML)

人工智能标记语言(Artificial Intelligence Markup Language,AIML)是由 Richard Wallace 和世界各地的自由软件社区在 1995 年至 2002 年发明的。它是一种基于 XML 的语言,可用于创建智能机器人或虚拟助。

AIML中的主要元素是模式(pattern)、模板(template)、类别(category)和变量(variable)。

  1. 模式(pattern)是用户可能会输入的问题或陈述
  2. 模板(template)是机器人对该问题或陈述的响应
  3. 类别(category)是模式和模板的组合
  4. 变量(variable)用于存储和处理用户输入和机器人输出中的信息

基于 AIML 可以快速开发基于规则的机器人,并可以轻松地扩展和修改现有机器人的功能。它的缺点是需要手工编写大量的匹配规则,当然构建工作也可以基于一些开源的 AIML 语料库,这样会节省不少的时间。安装命令如下:

pip install aiml

这篇文章简单介绍下,AIML 的 Hello World 程序,以及其常用的一些标签。

https://www.udemy.com/course/artificial-intelligence-markup-language/

  1. Hello World {#title-0} =========================

一个基于 AIML 的聊天机器人构建的基本过程有:

  1. 首先,创建对话语料。这个对话语料其实就是针对不同领域、不同意图、或者不同侧重点的对话规则,每一类的规则创建一个 .aiml 文件
  2. 然后,使用 aiml 库加载学习我们创建的一系列规则
  3. 最后,输入问题匹配规则输出响应

创建一个语料文件,暂时命名为 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/

  1. <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 和输入的问题,都已经用空格隔开了。

  1. <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 作为输出。

  1. <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, 你叫什么名字?
很高兴认识你: 刘 德 华
我知道你叫刘 德 华
  1. <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)
我读过,你觉得这本书怎么样?
我不会饿,不需要吃饭哦!
什么不好?
  1. <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> 标记在记录变量时候,并不会将变量的值输出。

  1. <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)
张三名字真简洁!
李四,我是赵六啊!
我不认识你
我不理解你的问题!
  1. <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: 足   球   比   赛   一   般   多   少   人   参   加   ?
  1. <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!
你也好!
你也好!
  1. <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]
赞(0)
未经允许不得转载:工具盒子 » 人工智能标记语言(AIML)