如何准备使用Python编写Mycroft AI的第一项技能

本文介绍了如何准备使用Python编写开源语音助手Mycroft的技能,强调了从理解意图解析到创建Python草图的步骤。作者通过一个将项目添加到OurGroceries应用程序的例子,展示了如何规划、编写和测试技能代码,同时讨论了Mycroft Skills Kit的使用和技能结构。
摘要由CSDN通过智能技术生成

随着最近全球范围内的大流行和全家待诊订单,我一直在寻找可以代替我平时活动的方法。 我开始更新家用电子设备的设置,并且其中一部分是深入研究家庭自动化。 我的一些朋友使用亚马逊的Alexa打开和关闭房屋中的灯,这在某种程度上很有吸引力。 但是,我是一个注重隐私的人,所以我从来都不会对Google或Amazon一直在听家人的设备感到真正的不舒服(在本次对话中,我将不理会手机)。 我已经了解开源语音助手Mycroft大约四年了,但是由于与该项目的较早斗争,我从未对其进行过仔细的研究。 自从我第一次偶然发现该项目以来,该项目已经走了很长一段路,并且为我检查了很多盒子:

  • 自托管
  • 易于入门(通过Python)
  • 开源的
  • 注重隐私
  • 互动聊天频道

在本系列的第一篇文章中,我介绍了Mycroft,在第二篇文章中 ,我谈到了人工智能技能的概念。 在最基本的形式中,一项技能是一段代码,可以执行该代码以实现意图所需的结果。 意图试图确定你想要什么 ,和技能是迈克罗夫特响应的方式。 如果您可以想到结果,则可能有一种方法可以创造出使之实现的技能。

从本质上讲,Mycroft技能只是Python程序。 通常,它们分为三个或四个部分:

  1. 导入部分是您加载完成任务所需的任何Python模块的地方。
  2. 可选功能部分包含在主类部分之外定义的代码段。
  3. 课堂部分是所有魔术发生的地方。 一个类应始终将MycroftSkill作为参数。
  4. Mycroft使用create_skill()部分来加载您的技能。

在编写技能时,我通常会先编写一个标准的Python文件,以确保我的代码能够实现我认为的功能。 我之所以这样做,主要是因为我习惯的工作流程(包括调试工具)在Mycroft生态系统之外。 因此,如果需要逐步执行代码,我会发现使用我的IDE( PyCharm )及其内置工具更加熟悉,但这是个人喜好。

该项目的所有代码都可以在我的GitLab存储库中找到。

关于意图解析器

PadatiousAdapt意图解析器,我在一篇文章中进行了介绍。 为什么? 首先,本教程旨在提供一些具体示例,您可以根据自己的技能考虑使用某些功能。 其次,Padatious意图非常简单,但是不支持正则表达式 ,而Adapt则很好地使用了正则表达式。 同样,Padatious意向不是上下文感知的,这意味着,尽管您可以提示用户一个响应,然后按照一些决策树矩阵对其进行解析,但最好使用Mycroft内置的Adapt意向解析器上下文处理程序。 请注意,默认情况下,Mycroft假定您正在使用Padatious意向处理程序。 最后,很高兴注意到Adapt是关键字意图分析器。 如果您不是正则表达式忍者,这会使繁琐的解析变得麻烦。 (我不是。)

实施3T

在开始写技能之前,请考虑以下3个T: 仔细考虑 ! 与撰写论文大纲时类似,当您开始发展一项技能时,写下您想要的技能。

本教程将逐步编写Mycroft技能,以将项目添加到OurGroceries应用程序(与我无关)。 实际上,这是我妻子的主意。 她想要一个可以在手机上使用的应用程序来管理购物清单。 我们尝试了将近十二种应用程序来满足我们的个性化需求-我需要一种API或一种易于与后端交互的方法,而且她拥有大量的标准,其中最重要的标准之一就是易于使用她的电话。 在她列出了必备物品,必备物品和愿望清单之后,我们选择了OurGroceries。 它没有API,但确实有一种通过JSON与之交互的方式。 在PyPI中甚至有一个方便的库,称为py-our-groceries (我为此贡献了少量)。

一旦有了目标和目标平台,我就开始概述所需的技能:

  1. 登录/验证
  2. 获取当前杂货清单的列表
  3. 将商品添加到特定的杂货清单
  4. 将项目添加到特定列表下的类别
  5. 添加类别(因为OurGroceries允许将项目放置在类别中)

考虑到这一点,我开始草绘所需的Python。 这是我想出的。

创建Python草图

通过阅读py-our-groceries库的示例,我发现只需要导入两件事: asyncioourgroceries

很简单。 接下来,我知道我需要使用usernamepassword进行身份验证,并且知道该程序需要执行的任务。 所以我的草图最终看起来像这样:


   
   
import asyncio
from ourgroceries import OurGroceries
import datetime
import json
import os

USERNAME = ""
PASSWORD = ""
OG = OurGroceries ( USERNAME , PASSWORD )

def fetch_list_and_categories ( ) :
    pass

def return_category_id ( ) :
    pass


def add_to_my_list ( ) :
    pass

def add_category ( ) :
    pass

我不会深入探讨此草图的全部细节,因为这不在本系列的讨论范围之内。 但是,如果需要,您可以查看整个工作大纲

开始编程之前,您需要输入用户名,密码和列表ID。 用户名和密码很明显。 单击链接后,可以从URL中检索列表ID,或者可以通过编程方式进行更多选择,您可以使用所选浏览器的Developer Tools并检查对象。 这是Firefox中开发人员工具的外观:

Getting an ID
CC BY-SA史蒂夫烤箱

获得列表ID后,登录OurGroceries并获取Cookie。 为此,创建一个OurGroceries 对象 ,然后将其传递给asyncio 。 在使用它的同时,您还可以定义列表ID:


   
   
OG = OurGroceries ( USERNAME , PASSWORD )
asyncio. run ( OG. login ( ) )
MY_LIST_ID = "a1kD7kvcMPnzr9del8XMFc"

就本项目而言,您需要定义两种对象类型来帮助组织代码: groceriescategoriesfetch_list_and_categories方法非常简单:


   
   
def fetch_list_and_categories ( object_type = None ) :
    if object_type == "groceries" :
        list_to_return = asyncio. run ( OG. get_list_items ( list_id = MY_LIST_ID ) )
    elif object_type == "categories" :
        list_to_return = asyncio. run ( OG. get_category_items ( ) )
    else :
        list_to_return = None
    return ( list_to_return )

OurGroceries允许您添加多个具有相同名称的类别或项目。 例如,如果您已经在列表中添加了“肉类”并再次添加,则将看到一个名为“肉类(2)”的类别(只要您创建一个具有相同名称的类别,此数字就会增加)。 对我们来说,这是不受欢迎的行为。 我们还想尽可能避免重复,所以我做了初步的尝试来检测复数。 例如,我的代码同时检查“肉”和“肉”。 我敢肯定,有一种更智能的方式来执行这些检查,但是此示例突出了您在进行过程中可能要考虑的一些事项。 为简便起见,我将省略这些检查,因此return_category_id方法看起来像这样:


   
   
def return_category_id ( category_to_search_for , all_categories ) :
    category_to_search_for_lower = category_to_search_for. lower ( )
    category_id = None
    if len ( all_categories [ 'list' ] [ 'items' ] ) is not 0 :
        for category_heading in all_categories [ 'list' ] [ 'items' ] :
            # Split the heading because if there is already a duplicate it
            # presents as "{{item}} (2)"
            category_heading_lowered = category_heading [ 'value' ] . lower ( ) . split ( ) [ 0 ]
            if category_to_search_for_lower == category_heading_lowered:
                category_id = category_heading [ 'id' ]
                break
    return ( category_id )

要将项目添加到列表,您需要:

  1. 检查项目是否不存在
  2. 获取类别ID
  3. 将项目添加到特定类别下的列表中(如果指定)

add_to_my_list方法的最终结果如下:


   
   
def add_to_my_list ( full_list , item_name , all_categories , category = "uncategorized" ) :
    # check to make sure the object doesn't exist
    # The groceries live in my_full_list['list']['items']
    # Start with the assumption that the food does not exist
    food_exists = False
    toggle_crossed_off = False
    category_lowered = category. lower ( )
    for food_item in full_list [ 'list' ] [ 'items' ] :
        if item_name in food_item [ 'value' ] :
            print ( "Already exists" )
            food_exists = True
    if not food_exists:
        category_id = return_category_id ( category_lowered , all_categories )
        asyncio. run ( OG. add_item_to_list ( MY_LIST_ID , item_name , category_id ) )
        print ( "Added item" )

最后, add_category运行asyncio命令创建一个类别(如果尚不存在):


   
   
def add_category ( category_name , all_categories ) :
    category_id = return_category_id ( category_name , all_categories )
    if category_id is None :
        asyncio. run ( OG. create_category ( category_name ) )
        refresh_lists ( )
        print ( "Added Category" )
    else :
        print ( "Category already exists" )

现在,您应该能够测试您的草图,以确保每个功能中的所有功能都可以正常工作。 对草图满意后,您可以继续思考如何以Mycroft技能实施它。

计划Mycroft技能

您可以应用与绘制Python相同的原理来开发Mycroft技能。 官方文档建议使用称为Mycroft Skills Kit的交互式帮助程序来设置技能。 mycroft-msk create要求您:

  • 命名你的技能
  • 输入一些通常用来触发您的技能的短语
  • 确定Mycroft应该以什么对话框回应
  • 创建技能描述
  • fontawesome.com/cheatsheet选择一个图标
  • mycroft.ai/colorscolor-hex.com选择一种颜色
  • 定义技能所属的一个或多个类别
  • 指定代码的许可证
  • 说明技能是否具有依赖性
  • 指示您是否要创建GitHub存储库

这是mycroft-msk create原理的演示:

mycroft-msk create working

(史蒂夫烤箱, CC BY-SA 4.0

回答这些问题后,Mycroft在mycroft-core/skills/<skill name>下创建以下结构:


   
   
├── __init__.py
├── locale
│   └── en-us
│       ├── ourgroceries.dialog
│       └── ourgroceries.intent
├── __pycache__
│   └── __init__.cpython-35.pyc
├── README.md
├── settings.json
└── settingsmeta.yaml

您现在可以忽略其中的大多数文件。 在尝试进入Mycroft特定的疑难解答之前,我希望确保我的代码能正常工作。 这样,如果以后出现问题,您就会知道这与Mycroft技能的构建方式有关,而与代码本身无关。 与Python草图一样,看看Mycroft在__init__.py创建的轮廓。

所有Mycroft技能都应具有__init__.py 。 按照惯例,所有代码都应放在该文件中,尽管如果您是熟练的Python开发人员并且知道此文件的工作原理,则可以选择将代码拆分。

在Mycroft创建的文件中,您可以看到:


   
   
from mycroft import MycroftSkill , intent_file_handler


class OurGroceries ( MycroftSkill ) :
    def __init__ ( self ) :
        MycroftSkill. __init__ ( self )

    @ intent_file_handler ( 'ourgroceries.intent' )
    def handle_test ( self , message ) :
        self . speak_dialog ( 'ourgroceries' )


def create_skill ( ) :
    return OurGroceries ( )

理论上,此代码将基于您在msk create过程中创建的触发器执行。 Mycroft首先尝试查找扩展名为.dialog的文件,该文件与传递给selfspeak_dialog()的参数匹配。 在上面的示例中,Mycroft将查找一个名为ourgroceries.dialog的文件,然后说出在其中找到的短语之一。 如果失败,它将说出文件名。 我将在有关响应的后续文章中对此进行更多介绍。 如果您想尝试此过程,请随时探索在技能创建过程中可以想到的各种输入和输出短语。

尽管脚本是一个很好的起点,但我更愿意自己考虑__init__.py 。 如前所述,该技能将同时使用Adapt和Padatious意向处理程序,并且我还想演示对话上下文处理 (我将在下一篇文章中进行深入介绍)。 因此,从导入它们开始:


   
   
from mycroft import intent_file_handler , MycroftSkill , intent_handler
from mycroft. skills . context import adds_context , removes_context

如果您想知道,在Python中指定导入语句的顺序无关紧要。 导入完成后,查看类结构。 如果您想了解有关类及其使用的更多信息, Real Python对此主题有很好的入门。

如上所述,首先使用其预期功能模拟代码。 本部分使用与Python草图相同的目标,因此继续进行一些插入,这次添加一些注释以帮助您:


   
   
class OurGroceriesSkill ( MycroftSkill ) :
    def __init__ ( self ) :
        MycroftSkill. __init__ ( self )
   
    # Mycroft should call this function directly when the user
    # asks to create a new item
    def create_item_on_list ( self , message ) :
        pass
   
                # Mycroft should also call this function directly
    def create_shopping_list ( self , message ) :
        pass
   
    # This is not called directly, but instead should be triggered
    # as part of context aware decisions
    def handle_dont_create_anyways_context ( self ) :
        pass
               
    # This function is also part of the context aware decision tree
    def handle_create_anyways_context ( self ) :
        pass
   
   
    def stop ( self ) :
        pass

__init__initialize方法

一项技能具有一些您应该了解的“特殊”功能。 首次实例化此技能时,将调用__init__(self)方法。 在Python IDE中,在__init__部分之外声明的变量通常会引起警告。 因此,它们通常用于声明变量或执行设置操作。 然而,尽管你可以声明变量,旨在(后面有更多此)相匹配的技能设置文件,你不能使用迈克罗夫特方法(如self . settings . get)来检索值。 通常不适合尝试通过__init__与外界建立联系。 另外,在Mycroft中__init__函数被认为是可选的。 大多数技能都选择拥有一种技能,这被认为是“ Pythonic”的做事方式。

在技​​能完全构建并向系统注册后,将调用initialize方法。 它用于执行技能的任何最终设置,包括访问技能设置。 但是,它是可选的,我选择创建一个获取身份验证信息的函数。 如果您好奇并想_create_initial_grocery_connection ,我将其称为_create_initial_grocery_connection 。 当我开始逐步创建技能代码时,我将在下一篇文章中重新介绍这两个特殊功能。

最后,有一个特殊的函数stop() ,我没有使用过。 每当用户说“停止”时,都会调用stop方法。 如果您有一个长时间运行的过程或音频播放,则此方法很有用。

结语

因此,您现在已经有了要完成的任务的大纲。 随着时间的流逝,这肯定会增长。 随着技能的发展,您将发现技能最佳工作所需的新功能。

下次,我将讨论您将使用的意图类型,如何设置它们以及如何处理正则表达式。 我还将探讨会话上下文的概念,该上下文用于获取用户的反馈。

您有任何意见,问题或疑虑吗? 发表评论,在Twitter @linuxovens上访问我,或通过Mycroft技术聊天频道停止。

翻译自: https://opensource.com/article/20/6/mycroft-voice-assistant-skill

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值