質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

91.02%

  • Python

    5533questions

    Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

python を使った会話AIの実現

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 401

masaroad612

score 6

前提・実現したいこと

前提
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ページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • namnium1125

    2018/01/15 21:54 編集

    <code>(または</>)をクリックして現れる```内にコードを書くか、コードを```で囲んだ方が回答がつきやすくなると思いますよ。

    キャンセル

  • masaroad612

    2018/01/16 17:50

    アドバイスありがとうございます。以降の質問から気をつけたいです

    キャンセル

回答 1

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)

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/01/16 20:15

    ご丁寧にありがとうございました。
    プログラミング初心者なので非常に助かります。
    直していただいたプログラムを動かして見たところ動作確認ができました。

    本体のプログラムに組み込んで見たところ、音声認識までは動きました。
    def dialogueの中にあるmessageを
    def speakの中にあるmessageへ送ることができると会話ができるのかと思いますが、解決方法がわからないでおります。

    御教授いただければと思います。

    キャンセル

  • 2018/01/16 21:08

    Repl-AIからの返答を音声出力したいということでしょうか?

    キャンセル

  • 2018/01/17 08:08

    その通りです

    キャンセル

  • 2018/01/17 08:32

    それでしたら、コレまでのコード同様、dialogureの戻り値を speak()に渡すと良いのではないですか

    reply = dialogue(app_id, 'さようなら')
    speak(reply)

    キャンセル

  • 2018/01/17 08:36

    それから APIリファレンスによると dialogure APIの戻り値は
    expression: システムからの返答。
    utterance: 音声合成用テキスト。
    となってますので、
    request_replai_api(url, params)['systemText']['expression']

    request_replai_api(url, params)['systemText']['utterance']
    としたほうが良いかもしれませんね

    キャンセル

  • 2018/01/17 20:39

    いつも回答ありがとうございます。
    非常に勉強になりますが、ピントのずれた回答や、回答の意図を理解していない部分が多々あるもしれません。
    お許しください。

    キャンセル

  • 2018/01/18 13:28

    docomo apiからRepl-AIに変更し、1度返事を得ることができたましたが、それ以降プラグラムが作動しないため質問を追記しました、よろしくお願いします。

    キャンセル

  • 2018/01/19 11:11

    回等にも書いておりますが
    dialogue API は初回だけ
    'initTalkingFlag':True
    'initTopicId' : トピックID
    を送る必用があります。
    私のコードの場合は
    初回は
    replay = dialogue(app_id, message, True, トピックID)
    の形式で実行し、2回目以降は
    replay = dialogue(app_id, message)
    の形式で実行するようになっておりますので、そのように実装するとよいかと思います。
    私のサンプルコードの場合は first_time というフラグ変数を作成し、ループ内で初回かどうかを判定しております。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 91.02%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • Python

    5533questions

    Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。