Minette はチャットボットを開発するための軽量で拡張可能なフレームワークです。とても簡単に開発できる上に、スパゲッティコードになることなく複雑なBOTにまで拡張していくことができます。
-
0.4.3 Sep 5, 2020
-
0.4.2 Aug 26, 2020
- Janome 0.4に対応しました
-
0.4.1 Aug 7, 2020
- SQLAlchemyをサポートしました(試験的)。利用方法は examples/todo.py を参照ください。
テスト用のおうむ返しであれば一瞬で試すことができます。
from minette import Minette, EchoDialogService
# ビルトインされたおうむ返し部品を使ってBOTを起動
bot = Minette(default_dialog_service=EchoDialogService)
# コンソール入出力でやりとり
while True:
req = input("user> ")
res = bot.chat(req)
for message in res.messages:
print("minette> " + message.text)$ python echo.py
user> hello
minette> You said: hello
LINEBOTを作るのも同じくらい簡単です。
from flask import Flask, request
from minette import Minette, EchoDialogService
from minette.adapter.lineadapter import LineAdapter
# LINE接続部品を使ってBOTを起動
bot = LineAdapter(default_dialog_service=EchoDialogService)
# WebサーバとAPIエンドポイントの設定
app = Flask(__name__)
@app.route("/", methods=["POST"])
def handle_webhook():
bot.handle_http_request(request.data, request.headers)
return "ok"
# Webサーバの起動
app.run(port=12345)その他の例についてはexamples.ja.mdを参照してください。
$ pip install minette
Python 3.5以上。開発は主に3.7.7(on Mac OSX)で行なっています。
- LINE
- Clova
- Symphony
minette.Adapterを拡張した独自クラスを作成することで、FacebookやSlackなど他のサービスに接続することもできます。
- SQLite
- Azure SQL Database
- Azure Table Storage
- MySQL (Tested on 8.0.13)
minette.datastore パッケージ内の Context / User / MessageLog の各クラスを拡張することで、上記以外のお好みのデータベースを利用することもできます。また、SQLAlchemyがサポートしているデータベースであれば、Engineの接続文字列を指定するだけで利用できる可能性があります。
- MeCab
- Janome
クラウドサービスとして提供されているものや他の言語の形態素解析エンジンも minette.Tagger を拡張したクラスを実装することで利用できるようになります。
MeCabやJanomeのインストール方法についてはページ下部にAppendixとしてまとめましたので必要に応じて参照してください。
(必須)
- pytz >= 2018.9
- schedule >= 0.6.0
(オプション)
- line-bot-sdk >= 1.12.1 (for LINE)
- clova-cek-sdk >= 1.1.1
- sym-api-client-python >= 0.1.16 (for Symphony)
- pyodbc >= 4.0.26 (for Azure SQL Databsae)
- azure-cosmosdb-table >= 1.0.5 (for Azure Table Storage)
- MySQLdb (for MySQL)
- SQLAlchemy (for SQLAlchemyStores)
- mecab-python3 >= 1.0.1 (for MeCabTagger)
- Janome >= 0.3.8 (for Janome Tagger)
DialogService(s) と DialogRouter という2つの部品を実装するだけでチャットボットを開発することができます。
- DialogService: 対話シナリオに剃ってビジネスロジックを処理し、その結果に応じた応答メッセージを組み立てる
- DialogRouter: 発話からインテント(BOTへの指示内容)とエンティティ(指示の関連情報)を抽出し、それに応じて適切な
DialogServiceを呼び出す
上記以外の共通機能(コンテキスト管理など)はフレームワークによって行われるため、開発者はアプリケーションに集中することができます。
文脈を意識した会話を行えるようにするため、Minetteはコンテキスト管理の機能を提供しています。WebシステムでいうところのHTTPセッションに相当する機能です。これを利用することで、以前の発話における処理結果を利用することができます。
データの格納
# `context.topic.keep_on`に`True`を設定することで、次回のリクエストで格納したデータを利用することができます
context.data["pizza_name"] = "Seafood Pizza"
context.topic.keep_on = Trueデータの取得
pizza_name = context.data["pizza_name"]ユーザーはチャネルとチャネル(e.g LINE, FB Messanger etc)におけるユーザーIDの2つをキーに一意に管理されます。初回アクセス時に自動的にユーザーが登録され、また、アプリケーションロジック内で修正したユーザー情報は自動的に保存されます。
# 変更したユーザー情報は自動的に保存されます。コンテキストとは異なり、明示的に削除するまで情報は維持されます
request.user.nickname = "uezo"
request.user.data["horoscope"] = "cancer"Taggerは形態素解析のための部品で、解析結果はリクエスト情報に自動的に格納され各DialogService(アプリケーション)で利用することができます。
MinetteにはMeCabTagger、JanomeTaggerの2つのTaggerが最初から組み込まれています。
JanomeTaggerを利用するには、まずはJanome(ピュアパイソンな形態素解析エンジン)をインストールします。
$ pip install janome続いて動作確認をしてみましょう。以下の通り1つめの単語についての解析結果を表示してみます。
>>> from minette.tagger.janometagger import JanomeTagger
>>> tagger = JanomeTagger()
>>> words = tagger.parse("今日は良い天気です")
>>> words[0].to_dict()
{'surface': '今日', 'part': '名詞', 'part_detail1': '副詞可能', 'part_detail2': '', 'part_detail3': '', 'stem_type': '', 'stem_form': '', 'word': '今日', 'kana': 'キョウ', 'pronunciation': 'キョー'}DialogServiceでの利用例は以下の通り。Minetteオブジェクトを生成するときにtaggerとしてJanomeTaggerクラスを指定すれば、自動的にリクエスト本文の形態素解析が行われます。
# bot = Minette(tagger=JanomeTagger) <- Note: create bot with JanomeTagger
def process_request(self, request, context, connection):
# result of parsing morph is set in `request.words` automatically
nouns = [w.surface for w in request.words if w.part == "名詞"]定期実行のためのタスクのスケジューラーが利用可能です。
class MyTask(Task):
# `do`メソッドに定期実行したい処理内容を実装します
def do(self, arg1, arg2):
# スケジューラーのロガーが各タスクの中でも利用することができます
self.logger.info("Task started!: {} / {}".format(arg1, arg2))
# スケジューラーオブジェクトの生成
sc = Scheduler()
# 3秒ごとに`MyTask`の処理内容を実行するように登録
sc.every_seconds(MyTask, seconds=3, arg1="val1", arg2="val2")
# 定期実行の開始
sc.start()リクエスト、応答、コンテキストの対話ログが保存されます。デバッグやリリース後の改善活動にお役立てください。
Minetteは対話クラスのテストを支援するための機能を提供しています。
- テストケース毎(テスト関数毎)に異なる
channel_user_idを自動でセットします。 chatメソッドにMessageオブジェクトの生成に必要な引数を渡すことができます。bot.chat(Message(text="hello", intent="HelloIntent"))のかわりにbot.chat("hello", intent="HelloIntent")と呼び出すことができるため、テストコードをよりスッキリさせることができます。chatからも戻り値のResponseオブジェクトにresponse.messages[0].textの値をセットしたtextアトリビュートを追加します。
pytestでの利用例は以下の通り。
import pytest
from minette import Message, DialogService, Priority, Payload
from minette.test.helper import MinetteForTest
# dialogs to test
class FooDialog(DialogService):
def compose_response(self, request, context, connetion):
return "foo:" + request.text
class BarDialog(DialogService):
def compose_response(self, request, context, connetion):
context.topic.keep_on = True
return "bar:" + request.text
class PayloadDialog(DialogService):
def compose_response(self, request, context, connetion):
return "payload:" + str(request.payloads[0].content)
# bot created for each test functions
@pytest.fixture(scope="function")
def bot():
# use MinetteForTest instead of Minette
return MinetteForTest(
intent_resolver={
"FooIntent": FooDialog,
"BarIntent": BarDialog,
"PayloadIntent": PayloadDialog
},
)
# test cases function using bot
def test_example(bot):
# trigger intent
assert bot.chat("hello", intent="FooIntent").text == "foo:hello"
# empty response without intent
assert bot.chat("hello").text == ""
# trigger other intent
assert bot.chat("hello", intent="BarIntent").text == "bar:hello"
# context and topic is kept by dialog service
assert bot.chat("hi", intent="FooIntent").text == "bar:hi"
assert bot.chat("yo").text == "bar:yo"
# update topic by higher priority request
assert bot.chat("hello", intent="FooIntent", intent_priority=Priority.High).text == "foo:hello"
def test_payload(bot):
# use Message to test your dialog with payloads, channel_message and so on
assert bot.chat(Message(
intent="PayloadIntent",
type="data",
text="hello",
payloads=[Payload(content={"key1": "value1"})]
)).text == "payload:" + str({"key1": "value1"})コントリビューションガイドを参照してください🙏
Apache v2 License
- Ubuntu 16.04
$ sudo apt-get install mecab libmecab-dev mecab-ipadic
$ sudo apt-get install mecab-ipadic-utf8
- Mac OSX
$ brew install mecab mecab-ipadic git curl xz
$ pip install mecab-python3==1.0.1
0.996.1時点では 解決済みsurfaceが想定通りにならないバグ?があるため、0.7のインストールをお勧めします。
from minette.tagger.mecabtagger import MeCabTagger
bot = Minette(
tagger=MeCabTagger
)- いくつかのパッケージが変更になっており、特定ベンダーに依存しない全てのクラスは
minetteからインポートするようになりました。 Minetteインスタンスの生成方法。コンストラクターで直接生成するようになりました。SessionからContextに名称を変更しています。また、sessionという引数をとるものもすべてcontextに変更しました。minette.user.User#save()を廃止しました。アプリロジックでユーザー情報を保存するには、UserStoreインスタンスを生成してください。SessionStore->ContextStore,UserRepository->UserStore,MessageLogger->MessageLogStoreLineAdapterのHTTPリクエストを受け取るメソッドは、chatからhandle_http_requestに変更しました。
もし version 0.3 をインストールしたい場合、Githubのリリースパッケージから入手してください。
$ pip install git+https://github.com/uezo/minette-python.git@v0.3
