让自家的智能语音助手实现todo任务的添加

我家的树莓派在成为了"智能语音助手"后,经过rasa学习训练,已经可以帮忙查日期/时间,查天气预报,进行一些简单的闲聊。但是,我希望它的功能还可以再强大些,比如说,可以帮我记录todo任务。为了实现这一目标,又花了一周时间,终于在今天实现了这个功能。

要实现这个功能,说白了,就是定义一个todo class,然后通过rasa 的自定义actions来调用这个class,从而实现todo task的创建、查询、删除这几个基本功能。

插一句话:接下来分享的代码,都是基于我的1.4.0版rasa来说的,要在其他版本上使用,需要根据相应版本的规则自行适配。

1 增加nlu.md中的intents

2 domain.yml作相应调整(只列新增部分)

3 stories.md新增故事

4 在actions.py中定义domain中出现的actions和forms

4.1 显示任务列表

python 复制代码
class ActionShowTasks(Action):
    def name(self) -> Text:
        return "action_show_tasks"
    
    def run(
        self,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: Dict[Text, Any],
    ) -> List[Dict[Text, Any]]:

        todos = todo.Todo()
        todolist = todos.all()
        result = ""
        for todo_dict in todolist:
            result += " 编号 " + str(todo_dict.id) + " 任务 " + todo_dict.content
        dispatcher.utter_message(text=result)

        return []

4.2 新增todo task

python 复制代码
class AddTaskForm(FormAction):
    def name(self) -> Text:
        return "add_task_form"

    @staticmethod
    def required_slots(tracker: Tracker) -> List[Text]:
        return ["content"]

    def slot_mappings(self) -> Dict[Text, Union[Dict, List[Dict]]]:
        return {
            "content": [self.from_entity(entity="content"), self.from_text()]
        }

    def submit(
        self,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: Dict[Text, Any],
    ) -> Dict[Text, Any]:
        
        todos = todo.Todo()
        content = tracker.get_slot("content")
        if content is not None and content != "":
            todos.content = content
            todos.save()
            todolist = todos.all()
            msg = "新增任务 编号 " + str(todolist[-1].id) + " 任务 " + todolist[-1].content
        else:
            msg = "任务创建失败"
        dispatcher.utter_message(text=msg)
        return [SlotSet("content", None)]

4.3 查询todo task

python 复制代码
class ActionQueryTaskForm(FormAction):
    def name(self) -> Text:
        return "query_task_form"

    @staticmethod
    def required_slots(tracker: Tracker) -> List[Text]:
        return ["id"]
    
    def slot_mappings(self) -> Dict[Text, Union[Dict, List[Dict]]]:
        return {
            "id": [self.from_entity(entity="id"), self.from_text()]
        }

    def submit(
        self,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: Dict[Text, Any],
    ) -> Dict[Text, Any]:
        
        todos = todo.Todo()
        tid = tracker.get_slot("id")
        msg = "任务不存在"
        if tid is not None and tid != "": 
           if is_int(tid) and int(tid) >0:
              task = todos.getById(tid)
              if task is not None:
                  msg = "定位任务 编号 " + str(task.id) + " 任务 " + task.content
        dispatcher.utter_message(text=msg)
        return [SlotSet("id", None)]

4.4 删除指定todo task

python 复制代码
class ActionDeleteTaskForm(FormAction):
    def name(self) -> Text:
        return "delete_task_form"
    
    @staticmethod
    def required_slots(tracker: Tracker) -> List[Text]:
        return ["id"]

    def slot_mappings(self) -> Dict[Text, Union[Dict, List[Dict]]]:
        return {
            "id": [self.from_entity(entity="id"), self.from_text()]
        }

    def submit(
        self,
        dispatcher: CollectingDispatcher,
        tracker: Tracker,
        domain: Dict[Text, Any],
    ) -> Dict[Text, Any]:
        
        tid = tracker.get_slot("id")
        msg = "任务不存在"
        if tid is not None and tid != "":
           if is_int(tid) and int(tid) > 0:
              todos = todo.Todo()
              taskid = int(tid)
              todos.id = taskid
              todos.delete()
              msg = "任务已删除"
        dispatcher.utter_message(text=msg)
        return [SlotSet("id", None)]

如上就是四个功能的实现代码。其中,定位和删除需要判断输入的slot值是否为数字,就定义了一个检测函数来实现数字判断。

python 复制代码
def is_int(string: Text) -> bool:
    try:
       int(string)
       return True
    except ValueError:
       return False

actions.py文件头部需要引用的模块罗列如下:

python 复制代码
from typing import Any, Text, Dict, List, Union, Optional
from datetime import datetime, timedelta
from rasa_sdk import Action, Tracker
from rasa_sdk.executor import CollectingDispatcher
from rasa_sdk.forms import FormAction
from rasa_sdk.events import SlotSet
import ssl
from urllib import request, parse
import json
import todo

完成上述四个文件的编写后,在模型训练前请确认一下config.yml,我的模型用的是MitieNLP和jieba,网上大多数人用的是SpacyNLP,所以你要根据自己的实际情况来修改。如下是我的config.yml。

python 复制代码
# Configuration for Rasa NLU.
# https://rasa.com/docs/rasa/nlu/components/
language: zh
# pipeline: supervised_embeddings
pipeline:
  - name: MitieNLP
    model: data/total_word_feature_extractor_zh.dat
  - name: JiebaTokenizer
  - name: MitieEntityExtractor
  - name: EntitySynonymMapper
  - name: RegexFeaturizer
  - name: MitieFeaturizer
  - name: SklearnIntentClassifier

# Configuration for Rasa Core.
# https://rasa.com/docs/rasa/core/policies/
policies:
  - name: MemoizationPolicy
  - name: KerasPolicy
  - name: MappingPolicy
  - name: FormPolicy

执行rasa train完成模型训练,同时记得执行rasa run actions --actions actions来注册todo相关的四个actions。模型生成后,运行rasa shell测试通过,就可以让智能语音助手来执行对应的todo指令了。

写在后面:

1.最后的语音对话图请忽略我的识别耗时,直接用sherpa-ncnn的测试代码跑wav识别耗时很少,可能还是我的代码需要进一步优化。我所使用的语音助手整合代码请看这篇博文《树莓派智能语音助手之功能整合

2.todo的新增,查询和删除使用了form,但我暂时没有找到rasa1.4.0对应form的全部完整定义规则,导致实际运行时会出现"This can throw of the prediction. Make sure to include training examples in your stories for the different types of slots this action can return.

"的warning,不影响运行结果,但应该是属于stories定义没写完整。有知道怎么解决的朋友也可以教教我。

  1. 由于我在查询和删除form中使用了同一个slot------"id",结果当我执行完查询马上执行删除时,tracker.get_slot('id')会直接读取前一个form中的slot值。因此需要reset slot,即把class结束的return\[\]改成"return SlotSet("id", None)"。

  2. actions中import todo所引用的todo class代码请看这篇博文《用python实现todo功能》。

  3. 若不清楚怎么进行rasa的模型训练,可以看这篇博文《树莓派智能语音助手之首次RASA模型训练》。

相关推荐
m0_617493941 小时前
Python OpenCV 透视变换(Perspective Transform)详解与实战
开发语言·python·opencv
小李不困还能学1 小时前
PyCharm下载安装与配置教程
ide·python·pycharm
博观而约取厚积而薄发1 小时前
Pytest 从入门到精通,一篇就够(超详细实战教程)
python·测试工具·单元测试·自动化·pytest
imzed2 小时前
使用 Playwright + Pytest 构建 Web UI 自动化测试框架
python·自动化·pytest
普通网友2 小时前
pytest一些常见的插件
开发语言·python·pytest
时空系2 小时前
Python 高性能高压缩打包器 —— 基于 JianPy 语义分析引擎
python
cndes2 小时前
给Miniconda换源,让包下载更迅速
开发语言·python
Metaphor6923 小时前
使用 Python 添加、隐藏和删除 PDF 图层
python·pdf·图层
丨白色风车丨3 小时前
【Python 计算机视觉】基于 Dlib+OpenCV 实现实时人眼疲劳检测(闭眼预警)
python·opencv·计算机视觉