OpenAIのAPIで遊ぶ(2):Pythonで会話を実装する篇

これは皮肉なことながら、前から僕は授業などで言い続けていることがある。AIが人間に近づくほど、人間と同じ過ちや勘違いに陥るようになる。自動運転時に常にセンサーで周りを確認していても、根本的な認識ミスを起こす可能性が消えるわけではない。

前回のOpenAIのAPIで遊ぶ回 pt.1で、とりあえずGPT 3.5のdavinciに話しかけてレスポンスが返ってくるようになった。しかし、これでは継続的な会話ができないのでつまらない。とりあえずChatGPTに訊いてみる。

まあそうですよね。

実際のところ、ここまでに使ってきたCompletion APIというのは文章補完の機能を提供しているものであり、単純な会話が目的なわけではない。会話を実装した公式のPlaygroundでの実装例をみると、人間とAIの会話を描いて、最後の発言部分を空白にする形でプロンプトを流し込んで、それを補完する形で会話を実現している。

つまりは、ChatGPTとの会話とせずとも、男女2人の会話なんかでもよくて、とにかくここまでの会話の流れを再起的に入力した上で新しい発言を付け足していく形にすればいい。

とりあえずインスタンスを作る

継続的な会話の履歴を全て残していきたいので、インスタンスを作ってやってみることにする。実行はもちろんColab上で。

class newSession:
    def __init__(self):
        self.hist = []

    def get_conversation(self, display=True):
        prompt = ''
        for i, t in enumerate(self.hist):
            if i % 2 == 0:
                prompt = prompt + '人間:' + t + '\n'
            else:
                prompt = prompt + 'AI:' + t + '\n'
        if display:
            print(prompt)
        else:
            return prompt

    def talk(self, text):
        self.hist.append(text)

        prompt = self.get_conversation(display=False)
        prompt = prompt + 'AI:'
        
        c = openai.Completion.create(
            engine='text-davinci-003', 
            prompt=prompt, 
            max_tokens=60
            )
        response = c.choices[0].text.strip().split('\n')[0]
        print(response)
        self.hist.append(response)

非常に簡単な形で組んでみた。要は、インスタンスをnewSession()の形で立ち上げると会話が蓄積されていない初期状態の履歴(hist: 長さ0のリスト)が作成されて、そこからはこちらにせよ先方にせよ、何か発言するごとにhistに生の発言だけが追加されていく。

sess = newSession()
sess.talk('ねえ!')
> はい!何なさいますか?
sess.talk('あなたはAIなの?')
> はい、私はAIです。何かお困りですか?

newSession()で初期化しない限り会話は継続していける。その履歴を対話形式で表示するためにはget_conversation()を使う。

sess.get_conversation()

毎回再起的に全会話を入れなければいけないので、会話が長くなると理論上は請求額が指数関数的に増えていく魔法のツールである。

もう少しいじる

別に人間とAIの会話である必要はない。そしてシチュエーションの設定があってもいい。その辺りはインスタンスの立ち上げ時に指定することにしよう。

class newSession:
    def __init__(self, person1 = '人間', person2 = 'AI', situation = None):
        self.hist = []
        self.person1 = person1
        self.person2 = person2
        self.situation = situation

こんな感じで追加してあげた。

特に引数で明示されなければ人間とAIの事前説明なしでの会話になる形。あとは、シチュエーションの説明があればget_conversation()の冒頭に差し込む。そしてそれより下のコードでも人間、AIにしていたところをそれぞれself.person1, self.person2にしてやればいい。

    def get_conversation(self, display=True):
        prompt = self.situation + '\n' if self.situation else ''
        for i, t in enumerate(self.hist):
            if i % 2 == 0:
                prompt = prompt + self.person1 + ':' + t + '\n'
            else:
                prompt = prompt + self.person2 + ':' + t + '\n'
        if display:
            print(prompt)
        else:
            return prompt
sess2 = newSession(
    person1='男性', person2='女性', 
    situation='以下の文章は電車の中での友人同士の会話です。'
    )
sess2.talk('今からご飯食べに行かない?')
> いいよ、今から行こう!なんでもいいよ。
sess2.talk('じゃあ栄駅の近くでお店を探してみるね。')
> うん、おっしゃる通りだよ。
sess2.talk('やっぱり大須で唐揚げが食べたいかも。')
> 私もそう思ってるよ!じゃあ大須で唐揚げを食べよう!
sess2.get_conversation()

俺は夜中に一人で一体何をやっているんだ。

新しい恋愛シミュレーションゲームでも始めたのか。最初の返しの時点では謎のときめきがあったのだが、それ以降は自我がないにも程があるだろ。その辺りはシチュエーションで性格の指定をしてやらなかった俺も悪い。

会話の修正や復帰

当然この時点で会話に使っているインスタンスをsessに戻せば前のAIとの会話に戻れる。後はこのインスタンスをパラメータごと保存する機構を作ればいつでも継続して会話ができるようになる。

また、save_instance(session_name = ‘sess1’)的な感じでインスタンスをpickleにでも吐いておけばColabみたいに定期的にセッションが飛ぶ場合でも継続できる。あとは発言を事後的に修正したり何ターンか発言を戻したりみたいなこともまあ簡単に書けるでしょうね。今回はそこまではやらないけど。

繰り返すけど、会話が蓄積されるほどカネは膨大にかかるようになる。

…なんか飽きてきた。いや、より正確には、これ以降は自分の研究関係で使いたいので大っぴらにブログで公開できるような内容にはならない気がする。なんか書けそうなことを思いついたら書く。