"BOKU"のITな日常

還暦越えの文系システムエンジニアの”BOKU”は新しいことが大好きです。

AIと雑談できる「Talk API」を使ってチャットボットもどきを作る/python+django2.0+bootstrap4

今回はdjango2.0+bootstrap4をベースに、簡単なチャットボットもどきを作ります。

f:id:arakan_no_boku:20190320212948j:plain

 

この記事の前提

 

djangoのインストール・設定や、bootstrapを使うための設定などは、以下で説明している内容を前提にしています。

不明点があれば、以下も参照ください。

arakan-pgm-ai.hatenablog.com

arakan-pgm-ai.hatenablog.com

arakan-pgm-ai.hatenablog.com

 

 

AI部分はA3RT提供のTalk API

 

チャットボットに使えるようなモデルを一から学習させると大変なので、ありもののAPIを使います。 

リクルートさんが下記のサイトで、色々と面白そうなAPIを提供してくれていて、その中でぴったりの「Talk API」ってのがあります。

a3rt.recruit-tech.co.jp

これを使います。

 

Talk API

 

これは、チャットボット用のAPIです。

引用すると。

Talk APIはChatbotを作成するためのAPIです。

Recurrent Neural Network(LSTM)を用いた入力文からの応答文生成による日常会話応答機能を提供します。

Talk APIを活用したChatbotによって様々なアプリケーション上でユーザとの対話を自動化し、 どのようなタイミングにおいても即座にユーザからの問いかけに対して応答することができます。

雑談のためのチャットボット。

無駄に凄い・・のが憎いです。

 

APIKEYを取得する

 

これは恐ろしく簡単です。

Talk APIのページにいって、最下段までスクロールすると、ボタンがあります。

f:id:arakan_no_boku:20190122232400j:plain

API KEY発行ボタンを押すと、メールアドレスの入力を即されます。

f:id:arakan_no_boku:20190122232450j:plain

入力するものは、これだけ。

あとは、送られてくるメールにあるURLをクリックして本登録すると、再度メールにKEYが送られてきます。

メールアドレス以外の個人情報など、いっさいいりません。

この潔さが、リクルートですねえと妙な関心をしてしまいました。

 

APIを使って応答メッセージを取得するソースコード

 

puthonでやります。

まず、Talk APIを使って応答を受け取る処理のソースコードです。

import requests
import json

class Talk:
    def __init__(self):
        self.key = 'XXXXXXXXXXXXXXXXX'
        self.api = 'https://api.a3rt.recruit-tech.co.jp/talk/v1/smalltalk'

    def get(self,talking):
        url = self.api
        r = requests.post(url,{'apikey':self.key,'query':talking})
        data = json.loads(r.text)
        if data['status'] == 0:
            t = data['results']
            ret = t[0]['reply']
        else:
            ret = '・・・・・・・・・'
        return ret

'XXXXXXXXXXXXXXXXXXX'の部分は取得したAPI-KEYに置き換える必要があります。

 

ソースコードの補足

 

やってることは、API-KEYと入力したフレーズをパラメータにセットして、URLに対してPOSTし、受け取ったJSONのレスポンスをdataに取り出してます。

data['status']==0が正常終了で、以外だとエラーなので無言('・・・・・・・・・・・')を返すようにしてます。

応答メッセージの取り出し部分は以下です。

 t = data['results']
ret = t[0]['reply']

 これで、とりあえずAPIから応答をとることができます。

使い方としては、talk.pyとかに保存したとしたら。

talk = Talk()

talk.get('こんにちは')

で、APIからの応答メッセージを受け取る感じですね。 

 

チャットボットもどき画面の方針

 

TalkAPIから応答を受け取るクラスを作ったので、djangoを使って画面を作ります。

なので、動きとしては入力したメッセージ文をSUBMITで、djangoに渡して、APIを通じて応答を受け取り、「入力したメッセージ文」と「応答メッセージ」がセットでポコッと表示される感じになります。

もうひと手間かけて、JavaScriptAjaxとかを使って、メッセージ文をひとつずつ表示するようにする(上記のAPIの応答もCallback関数で受け取って処理する方法になります)と、よりそれっぽい画面になるのですが・・まあ、デモですし、意外に違和感なかったりしたので、手抜きしてます(笑)

 

ソースコードと補足説明

 

まずは、ソースから。

最初に作るのは、画面(HTML)です。

例によって、djangoテンプレート+bootstrap4です。

base.htmlは、こちらの記事で用意したものを流用してます。

 

demo02.html

 

{% extends 'base.html' %}
{% load static %}
{% load bootstrap4 %}
{% load widget_tweaks %}

{% block header %}
<link rel="stylesheet" href="{% static "css/dj_talk.css" %}"></link>
{% endblock %}

{% block title %}
  チャットボットもどき
{% endblock %}

{% block content %}
<div class="container">

    <form action="" method="post">{% csrf_token %}
        <h1>雑談チャットボット</h1>
        <div class="form-group row my-4">         
            <label class="col-lg-1 col-form-label"><h4>{{form.textone.label}}</h4></label>
    		<div class="col-lg-6">       
	            {{form.textone|add_class:"form-control bg-warning"}}
    		</div>
    		<div class="col-lg-2">       
	            <button type="submit" class="btn btn-primary">送信する</button>
    		</div>
        </div>
        <div id="talkarea">
        {% for talktxt in talktxts %}
        {% if talktxt.k == 'b' %}
        	<div class="form-group row my-2">         
     			<div class="p-3 m-1 col-lg-5 bg-primary text-white">       
	           		{{ talktxt.txt }}
	        	</div> 
	        	<div class="col-lg-1"><img src="{% static "images/k.png" %}" /></div> 
	        	<div class="col-lg-4"></div>  
        	</div>
        {% else %}	
        	<div class="form-group row my-2">
        		<div class="col-lg-4"></div>
        		<div class="col-lg-1"><img src="{% static "images/b.png" %}" /></div>  
        		<div class="p-3 m-1 col-lg-5 bg-success text-white">
        			{{ talktxt.txt }}
        		</div>
        	</div>
        {% endif %}	 
        {% endfor %}
        </div>
     </form>
</div>  
{% endblock %}

<div id="textarea">は、メッセージの履歴を表示するエリアなので、高さ固定です。

 

高さ固定にするCSS

 

こんな感じのCSSをstatic/cssフォルダにおいてます。

#talkarea
{
height:600px;
overflow:auto;
}

 

HTMLの補足

 

 talktxtsにメッセージがはいってくるのを、forループで表示してます。

これは辞書を内包したリストになっていて、「talktxt.k」に'b'(入力したメッセージ)か'ai'(応答メッセージ)の識別がはいっていて、「talktxt.txt」にメッセージ本文がはいってます。

いちおう、雰囲気のため、色の右左を「k」の値で判断して振り分けてます。

form.textoneというのがテキストボックスです。

 

forms.py

 

from django import forms

class UserForm(forms.Form):
     textone = forms.CharField(label='会話',widget=forms.TextInput(attrs={'id': 'commu'}))

 

実際のメッセージを返す処理はviews.pyに書きます。 

 

views.py

 

from django.http.response import HttpResponse
from django.shortcuts import render, render_to_response
from datetime import datetime as dt
from django.contrib.staticfiles.templatetags.staticfiles import static
from . import forms
from django.template.context_processors import csrf
import os
from . import talk


# 応答用の辞書を組み立てて返す
def __makedic(k,txt):
    return { 'k':k,'txt':txt}
    
def demo(request):
    t = talk.Talk()
    if request.method == 'POST':
        # テキストボックスに入力されたメッセージ
        q = request.POST["textone"]
        # Talk-APIからの応答メッセージ取得
        a = t.get(q)
        # 描画用リストに最新のメッセージを格納する
        talktxts = []
        talktxts.append(__makedic('ai',a))
        talktxts.append(__makedic('b',q))
        # 過去の応答履歴をセッションから取り出してリストに追記する
        saveh = []
        if 'hist' in request.session:
            hists = request.session['hist']
            saveh = hists
            for h in reversed(hists):
                x = h.split(':')
                talktxts.append(__makedic(x[0],x[1]))
        # 最新のメッセージを履歴に加えてセッションに保存する
        saveh.append('b:'+ q)
        saveh.append('ai:' + a)
        request.session['hist'] = saveh
        # 描画準備
        form = forms.UserForm(label_suffix=':')
        c = {
             'form': form,
             'textone': '',
             'talktxts':talktxts
        }
    else:
        # 初期表示の時にセッションもクリアする
        request.session.clear()
        # フォームの初期化
        form = forms.UserForm(label_suffix=':')
        c = {'form': form}
        c.update(csrf(request))
    return render(request,'demo02.html',c)

セッションの処理を書いている分、行数が長くなってます。

 

views.pyの補足

 

でも、やっていることは

  • テキストボックスに入力したメッセージをキーにして、APIを呼び出して応答を得て、画面に返却するリストにセットしている。
  • 過去の応答履歴をセッションから取り出して追記している。
  • 過去の応答履歴に最新のメッセージを追加してセッションに保存している

 というだけですから、別に難しいところはありません。

注意が必要なのは、localhostでセッションを使う場合に、runserverをする前に以下の処理を実行しておく必要がありますことです。

python manage.py migrate

どうも、djangolocalhostのセッション管理に、sqllite3を使うみたいなんですね。

なので、これをしておかないと、sessionを使うと「No such table : django_session」というエラーになるので、ソースにミスはないのに・・と悩むことになります。

 

urls.py

 

最後に、url.pyにリンクを追加します。

from django.contrib import admin
from django.urls import path
from . import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('demo/',views.demo,name='demo02'),
]

 赤字の部分が追加した部分です。

HTMLは、demo02.htmlで保存している前提です。

 

作ったシステムを動かす

 

これで、プロジェクトフォルダをカレントにして、runserverします。

python manage.py runserver

で、localhost:8000/demo にアクセスして、いくつか応答してみました。

f:id:arakan_no_boku:20190124232942j:plain

右のグリーンがAPIからの応答です。

ちょっと、とんちんかん・・な感じもありますけど、たったこれだけの実装で、まがりなりにもチャットボットらしきものができてしまう。

これは、面白いですよね。

今回はこんな感じです。

ではでは。