python を使った会話AIの実現
- 評価
- クリップ 1
- VIEW 3,686
前提・実現したいこと
前提
docomo apiからRepl-AIに変更し、1度返事を得ることができた。
実現したいこと
連続した会話をしたい。
※初心者で的を射ない質問で大変申し訳ありません。
どうしても明日中に会話をさせるAIを完成させたく質問させていただきました。
発生している問題・エラーメッセージ
1.repl-AIからの返答を音声で話してくれない
speakの戻り値に問題があるのでしょうか?
2.テキストでの返答はあるが、連続した会話にならない
ここがよくわかりません
Traceback (most recent call last):
File "/home/pi/ai/ai/ai-dialoguereplret.py", line 175, in <module>
speak(reply)
File "/home/pi/ai/ai/ai-dialoguereplret.py", line 112, in speak
print(commands.getoutput(cmdline))
File "/usr/lib/python2.7/commands.py", line 50, in getoutput
return getstatusoutput(cmd)[1]
File "/usr/lib/python2.7/commands.py", line 59, in getstatusoutput
pipe = os.popen('{ ' + cmd + '; } 2>&1', 'r')
UnicodeEncodeError: 'ascii' codec can't encode characters in position 7-15: ordinal not in range(128)
使用言語
python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import requests
import json
import os
import commands
import time
GOOGLE_APIKEY = 'AIzaSyCcd2JFQt_1D6cBgFOHcJv53Wn1QmfM-_4'
DOCOMO_APIKEY = '6b57447369556f3432332e5a73453553776d64624d7a4a447235314530477767476b393975313748765234'
LISTEN_SECONDS = 7
VOICE_IN_PATH = '/home/pi/tmp.flac'
VOICE_OUT_PATH = '/home/pi/tmp.wav'
REPLAI_API_KEY = "tIbYtdhqLp4GFArA93Np791lpiBM1f7M6pUAns61";
REPLAI_API_BOTID = "sample";
REPLAI_API_URLBASE = "https://api.repl-ai.jp/v1/";
REPLAI_API_TOPICID = "docomoapi"
# 録音
def listen(seconds):
print 'lestening...'
cmdline = 'AUDIODEV=hw:1 rec -c 1 -r 17000 ' + VOICE_IN_PATH + \
' trim 0 ' + str(seconds)
os.system(cmdline)
return os.path.getsize(VOICE_IN_PATH)
# 音声認識
def recognize():
print('recognizing...')
f = open(VOICE_IN_PATH, 'rb')
voice = f.read()
f.close()
url = 'https://www.google.com/speech-api/v2/recognize?xjerr=1&client=chromium&'\
'lang=ja-JP&maxresults=10&pfilter=0&xjerr=1&key=' + GOOGLE_APIKEY
hds = {'Content-type': 'audio/x-flac; rate=17000'}
try:
reply = requests.post(url, data=voice, headers=hds).text
except IOError:
return '#CONN_ERR'
except:
return '#ERROR'
print 'results:', reply
objs = reply.split(os.linesep)
for obj in objs:
if not obj:
continue
alternatives = json.loads(obj)['result']
if len(alternatives) == 0:
continue
return alternatives[0]['alternative'][0]['transcript']
return ""
# 対話
def request_replai_api(url, params):
print(url)
headers = {}
headers['content-type'] = 'application/json'
headers['x-api-key'] = REPLAI_API_KEY
r = requests.post(url, headers=headers, json=params)
return r.json()
def registration():
url = requests.compat.urljoin(REPLAI_API_URLBASE, 'registration')
params = {}
params['botId'] = REPLAI_API_BOTID
return request_replai_api(url, params)['appUserId']
# 初回の発話(initialize=True)時は tipic_id を設定する
def dialogue(app_id, message, initialize=False, topic_id=None):
url = requests.compat.urljoin(REPLAI_API_URLBASE, 'dialogue')
params = {}
params['appUserId'] = app_id
params['botId'] = REPLAI_API_BOTID
params['voiceText'] = message
if initialize and topic_id:
params['initTalkingFlag'] = True
params['initTopicId'] = topic_id
else:
params['initTalkingFlag'] = False
return request_replai_api(url, params)['systemText']['utterance']
# 動作確認
app_id = registration()
first_time = True
while True:
if first_time:
ret = dialogue(app_id, 'こんにちは', True, REPLAI_API_TOPICID)
print(ret)
first_time = False
else:
ret = dialogue(app_id, 'さようなら')
print(ret)
break
time.sleep(10)
# 読み上げ
def speak(message):
print('speaking...' + message)
JDIC_DIR='/var/lib/mecab/dic/open-jtalk/naist-jdic/'
VOICE_DATA='/home/pi/ai/ai/mei/mei_happy.htsvoice'
cmdline = 'echo ' + message + ' | open_jtalk -x ' + JDIC_DIR + \
' -m ' + VOICE_DATA + ' -ow ' + VOICE_OUT_PATH + \
' -s 17000 -p 100 -a 0.03'
print(commands.getoutput(cmdline))
os.system('play ' + VOICE_OUT_PATH)
def current_milli_time():
return int(round(time.time() * 1000))
if __name__ == '__main__':
#first time record will be failed.
listen(1)
speak('こんにちは、今日の調子はどうですか?')
no_word = 0
wifi_err = 0
try:
while True:
# 録音
t0 = current_milli_time()
size = listen(LISTEN_SECONDS)
t = current_milli_time() - t0
if (t < 2000):
print 'USB microphone not available'
speak('マイクロホンを確認してください。')
time.sleep(10)
continue
print 'listened:' + str(t) + 'ms'
print 'voice data size=' + str(size)
# 音声認識
t0 = current_milli_time()
message = recognize().encode('utf-8')
print 'recognized:' + str(current_milli_time() - t0) + 'ms'
if (message == '#CONN_ERR'):
print 'internet not available'
speak('イーターネット接続を確認してください。')
time.sleep(10)
continue
elif (message == '#ERROR'):
print 'voice recognize failed'
speak('音声認識サービスを確認してください。')
time.sleep(10)
continue
# あいさつ
if (len(message) <= 1):
no_word = no_word + 1
if (no_word >= 5):
speak('こんにちは、ラズパイです。')
no_word = 0
continue
# 対話
speak('')
t0 = current_milli_time()
reply = dialogue(app_id, message, initialize=False, topic_id=None)
print 'replied:' + str(current_milli_time() - t0) + 'ms'
if (reply == '#ERROR'):
print 'dialogue failed'
speak('雑談会話サービスを確認してください。')
time.sleep(10)
continue
# 読み上げ
t0 = current_milli_time()
speak(reply)
print 'talked:' + str(current_milli_time() - t0) + 'ms'
except KeyboardInterrupt:
pass
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
checkベストアンサー
+2
とりあえず、システムに組み込む前にRepl.AI単体で動作確認を行ったほうが良いかと思います。
で、Repl.AIの会話APIが動作しない原因ですが、
1つ目として、
https://repl-ai.jp/docs/references/dialogue
を見る限り、dialogure API
のRequest URLは "https://api.repl-ai.jp/v1/dialogue" となっておりますので
url = 'REPLAI_API_URL'\
'REPLAI_API_KEY={}'.format(REPLAI_API_KEY)
は明らかな間違いです。
2つ目は、初回発話時は、Request BodyにてinitTalkingFlag
を True
にする必要があり、その際に initTopicId
にてシナリオIDを指定する必要がありますので、この処理を追加する必要があります。
initTopicId
の項目に 【initTalkingFlagがtrueの際は必須。】の記述があります
最後に3つ目として、dialogue API
では Request Body に utt
パラメータが不要となります。
以上の修正で、とりあえず動くのではないでしょうか。
以下に私がAPIの動作確認したコードを挙げておきます(エラー処理の類は一切入っておりません)
appUserId
や TopicID
を取り回すにはクラス化したほうが楽かと思います。
"""
import requests
import time
REPLAI_API_KEY = "********************************************"
REPLAI_API_BOTID = "sample"
REPLAI_API_URLBASE = "https://api.repl-ai.jp/v1/"
REPLAI_API_TOPICID = "docomoapi"
def request_replai_api(url, params):
print(url)
headers = {}
headers['content-type'] = 'application/json'
headers['x-api-key'] = REPLAI_API_KEY
r = requests.post(url, headers=headers, json=params)
return r.json()
def registration():
url = requests.compat.urljoin(REPLAI_API_URLBASE, 'registration')
params = {}
params['botId'] = REPLAI_API_BOTID
return request_replai_api(url, params)['appUserId']
# 初回の発話(initialize=True)時は tipic_id を設定する
def dialogue(app_id, message, initialize=False, topic_id=None):
url = requests.compat.urljoin(REPLAI_API_URLBASE, 'dialogue')
params = {}
params['appUserId'] = app_id
params['botId'] = REPLAI_API_BOTID
params['voiceText'] = message
if initialize and topic_id:
params['initTalkingFlag'] = True
params['initTopicId'] = topic_id
else:
params['initTalkingFlag'] = False
return request_replai_api(url, params)['systemText']['expression']
# 動作確認
app_id = registration()
first_time = True
while True:
if first_time:
ret = dialogue(app_id, 'こんにちは', True, REPLAI_API_TOPICID)
print(ret)
first_time = False
else:
ret = dialogue(app_id, 'さようなら')
print(ret)
break
time.sleep(10)
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.11%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
質問への追記・修正、ベストアンサー選択の依頼
namnium1125
2018/01/15 21:16 編集
<code>(または</>)をクリックして現れる```内にコードを書くか、コードを```で囲んだ方が回答がつきやすくなると思いますよ。
masaroad612
2018/01/16 17:50
アドバイスありがとうございます。以降の質問から気をつけたいです