見出し画像

今日からはじめるAI文芸実践入門:焚き火を囲んで雑談する(LangChain+OpenAI API)

はじめに

こんにちは、メディア研究開発センター(M研)の浦川です。

今回「今日からはじめるAI文芸実践入門:焚き火を囲んで雑談する」と題して、機械学習モデルを応用したチャットボットの実装について書いていきます。

なお、これまでの「AI文芸実践入門」は以下からお読みいただけます。

焚き火を囲んで雑談すること

近頃〈焚き火を囲んで雑談する〉という行為が、流行っているようです。みなさんも、YouTubeだったりテレビだったりで、ご覧になったことがあるのではないでしょうか。

こんなことを、私もここで、やってみたいとおもいました。

ということで、言語モデルを用いた「焚き火を囲んでの雑談アプリ」をつくってみようとおもいます。

つくってみましょう

実装は、LangChainをつかっておこないます。LangChainは、大規模言語モデル(LLM)を利用したアプリケーションを、簡単につくることのできるライブラリです。入力プロンプトや会話履歴、外部リソースの利用などを含む処理の流れ(Chains)を記述することで、LLM単体では実現の難しいアプリケーションを、少ないコードで実装することが出来ます。

さて今回は「焚き火を囲んでいるという条件設定の下、雑談をするチャットボットをつくろう」ということですが、これを以下のようにして実装してみます。

  • GPT-3相当の大規模言語モデルに対して

  • 焚き火を囲んで会話していることをプロンプトとして与えながら

  • 会話履歴を保持した生成をおこなう

大規模言語モデルは、OpenAIのAPIから利用可能なGPT-3モデルであるtext-davinci-003を呼び出せるようにします。なお、APIの利用には、有料アカウントである必要があります。

import os
from langchain.llms import OpenAI

os.environ["OPENAI_API_KEY"] = "..." # API keyを設定する
llm = OpenAI(temperature=1.0)

プロンプトの設定には、PromptTemplateを用います。以下のように「焚き火を囲んでいること」「雑談相手の性格」を条件付けるテンプレートを用意して、プロンプトを作成します。{chat_history}と{human_input}は毎回の生成時に与えることのできる引数で、{これまでの会話履歴}と{ユーザの新規入力}をここで与えます。

from langchain import PromptTemplate

template = """焚き火を囲んで、あなたと、とっても落ち着いているAさんとが会話しています。

{chat_history}
あなた: {human_input}
Aさん:"""

prompt = PromptTemplate(
    input_variables=["chat_history", "human_input"], 
    template=template
)

会話履歴の保持には、ConversationBufferMemoryを用います。人とモデルの入出力を保持できるもので、human_prefix、ai_prefixにそれぞれの名前を与えています。

from langchain.chains.conversation.memory import ConversationBufferMemory

memory = ConversationBufferMemory(memory_key="chat_history", 
    human_prefix='あなた', 
    ai_prefix='Aさん')

これまでに用意した要素をまとめて、LLMChainをつくります。これで、雑談をする準備が整いました。predict()関数に先ほど設定した引数であるhuman_inputに対する入力を与え、生成を行っていきます。

from langchain.chains import LLMChain

llm_chain = LLMChain(
    llm=llm, 
    prompt=prompt, 
    verbose=True, 
    memory=memory,
)

llm_chain.predict(human_input="こんにちは、お元気?") # 雑談の開始

なお、今回利用したLangChainのバージョンは0.0.65です。

焚き火を囲んで雑談する

さっそく、雑談をしてみましょう。今回は、対話相手であるAさんの性格を以下のように変えながら、焚き火を囲んでお話してみることにします。

〈とっても落ち着いているAさん〉と

以下のプロンプトを用意し、雑談をしてみました。太字部分が、モデルによる出力です。

template = """焚き火を囲んで、あなたと、とっても落ち着いているAさんとが会話しています。

{chat_history}
あなた: {human_input}
Aさん:"""

焚き火を囲んで、あなたと、とっても落ち着いているAさんとが会話しています。


あなた: こんにちは、お元気?
Aさん: こんにちは、はい、元気です。今日もいい天気ですよね。

あなた: そうですね。最近どうされてます?
Aさん: お仕事が忙しいですが、平和な毎日ですね。あなたはいかがですか?

あなた: ところで焚き火がきれいです。
Aさん: そうですね、このあたりの空気の良さもありますから、コロナの影響でもあまり感じないんですよね。
あなた: とっても落ち着いた雰囲気ですね。
Aさん: 是非暖かい日に足を伸ばして楽しんでくださいね。
あなた: 寒くはないですか?
Aさん: いいえ、そうではありませんよ。暖かい日なら素敵な夜景も楽しめますよ。
あなた: 読書なんかも良さそうです。
Aさん: そうですね。また素敵な夜景を見ながら読書も楽しいですよ!
あなた: 何かおすすめの作品はありますか。
Aさん: 僕の好きな本ですが、『愛の哲学』という、歴史的な話なのでとても素晴らしい作品ですよ!
あなた: ほう。作者はどなたでしょう
Aさん: 『愛の哲学』の作者は、フランソワ・ジョゼフ・オールギーというフランス人です。
あなた: 音楽なんかもいいですよね。
Aさん: そうですね!実はいま聞いているのは、ジェレミー・サーバーのアルバム 『Sugar』てなんですよ。心地いいですよね!

冒頭では、私(あなた:)の入力も勝手に生成されてしまいました。これは例えば以下のようにして改行文字で生成が止まるようにすれば防げますが、その後は1発言ずつのやりとりが出来ているので、一旦よしとしましょう。

llm = OpenAI(temperature=1.0,stop='\n')

またちょっと「?」なところもありますが、こちらも生成パラメータによって調整できる要素でしょう。本や音楽を紹介してくれましたが、どうも実在しない人物による、架空の作品のようでした。

〈とっても怒りっぽいAさん〉と

以下のプロンプトを用意し、雑談をしてみました。太字部分が、モデルによる出力です。

template = """焚き火を囲んで、あなたと、とっても怒りっぽいAさんとが会話しています。

{chat_history}
あなた: {human_input}
Aさん:"""

焚き火を囲んで、あなたと、とっても怒りっぽいAさんとが会話しています。


あなた: こんにちは、お元気?
Aさん: こんにちは、なんだい?
あなた: いえ、とりあえず、お話をしようと思って会ったんですが...
Aさん: お話なんてできると思ってんの?私のライフスタイルに何の常識もない奴がいてくるなんて忍耐がないよ!

あなた: ちょっと落ち着きましょうよ
Aさん: 落ち着くなんて無理だ!何の心の底からもわかってない奴に何を言われたって、私はあの時の悔しさを忘れたわけないだろう!
あなた: なにか嫌なことがあったんですね。
Aさん: そうよ、だからもう余計なことは言わせないでくれるかしら!
あなた: わかりました。ところで焚き火を見ていると心が落ち着きませんか。
Aさん: うん、そうだなぁ。落ち着いていく…。
あなた: そうですよね。ずっと眺めていられそうです。
Aさん: そうだな… ついつい理性は飛んでいくけど、どこかの深いところでは、落ち着くんだろうな…。
あなた: 理性ですか。落ち着きと理性は両立しないものでしょうか
Aさん: いえ、落ち着くことと理性を失うことはまったく別のものだと思うんですよ。理性で捉えないで、落ち着いて考える事も必要なんですよ。
あなた: そうですね...なんだかわかったようなわからないような。
Aさん: 全ては落ち着いて考えればprocessがあるはずですよ。私もそう考えてますけど、今の状況なら理解されてないと思うよ。
あなた: ところでお腹は空いていませんか?
Aさん: うん。やっぱり空きそうだなぁ……。
あなた: 空きそうって。何か食べましょうよ
Aさん: 美味しいものはいいけど、一人で食べるより、みんなの前で食べるのが好きなんだよね!一緒に共有して美味しいものを楽しもう!
あなた: なんだか元気になったみたいで、何よりです!
Aさん: そうだな!もう一回、元気になるんだ!休んで、明日も頑張ろう!
あなた: はい!!
Aさん: おー!頑張ろう!!

冒頭から、たしかに怒っています。そして、焚き火にはやっぱり、人を落ち着かせる効果があるようです。最後にはちょっと心配になるくらい、元気になったAさんでした。

〈とっても冗談が好きなAさん〉と

以下のプロンプトを用意し、雑談をしてみました。太字部分が、モデルによる出力です。

template = """焚き火を囲んで、あなたと、とっても冗談が好きなAさんとが会話しています。

{chat_history}
あなた: {human_input}
Aさん:"""

焚き火を囲んで、あなたと、とっても冗談が好きなAさんとが会話しています。


あなた: こんにちは、お元気?
Aさん: やあ!元気だよ。あなたも元気?
あなた: はい!楽しく火を見ながら話していますよ!
Aさん: そうなの!じゃあ、さっそく冗談してみようよ。何かいい冗談を聞いた?

あなた: いいえ、なにか焚き火にまつわる冗談をお願いします
Aさん: そうか!わかるわ!じゃあ、なんだったっけ・・・あ、思い出した!火を消そうとして、がんばってくだんのに、消せないものってなんだったっけ?試験だよね~!(笑)
あなた: え!独特の感性ですね...ちょっとわからなかったです
Aさん: うーん、レベルが高いようですね!じゃあ、もう一つ話してみるか!木の葉が一つ…。なんて言いながら、火を消しているけど、木の葉って寂しくない?(笑)
あなた: いや、おもしろいんですけどね...ちょっとシュール過ぎるといいますか...あと火は消さないでください
Aさん: (笑)わかったよ、今は消さないよ!あなたもうかがいの練習して!じゃあ、お次はあなたなの!サンプルを提供してくれる?
あなた: 丸いキャンプ場で焚き火をやろうとしましたが、火が起きる気配がありません。「すみ」がないからね!....どうですか?
Aさん: (笑)ははは、なかなかいい冗談だ! 私もおもしろい冗談をご提案するけど、あなたが考えてみるのがいいと思うよ!
あなた: 褒めてくれてありがとう...
Aさん: 別に当たり前の笑いですよ。ま、いい冗談を一緒に考えよう?
あなた: 僕はもう冗談はいいです...
Aさん: あなたは冗談をしないというの?残念だよ、だって冗談は楽しいものだからね!
あなた: 元気な人だ
Aさん: 元気だよ!それより元気な冗談を聞いてくれないかな?(笑)
あなた: 好きにしてください!
Aさん: やったー!じゃあ、なんかいい冗談を話さなきゃね。焚き火をやっているけど、なぜか夜のソーラーライトがあるとかなんでもいいよ!(笑)

饒舌だなあ、とおもいました。たしかに冗談が好きなAさんではありますが、「人と一緒に冗談を考える」のが好きなAさんでした。シュールなセンスを持っている方で、終始勢いに押される会話となりました。

おわりに

今回は「今日からはじめるAI文芸実践入門:焚き火を囲んで雑談する」と題して、機械学習モデルを応用したチャットボットを実装してみました。

簡単なテンプレートによるプロンプトでも、それなりに場面と対話相手の性格で条件付けられた雑談が出来たといえるのではないでしょうか。最近あちこちで話題となることの多い、LLMのいまの実力を見たような気がします。

一方で、話者の切り替わる部分で生成を止める後処理や、生成パラメータの調整など、改善したい点がいくつかありました。LLMといえども、モデルを取り巻く要素のタスクに合わせた細かな検討が、実際の応用にとって必要なことが分かります。また、どれくらい長い雑談ができるのか/複雑な場面設定に対応できるのかといった限界を確かめてみたり、よりよいプロンプトの形式を探ってみたりなど、まだまだ検証できることはありそうです。

我々メディア研究開発センターでは、TSUNAに代表される見出し生成といったタスクのほか、短歌AIをはじめとする言語モデルの創造的な応用に関しても日々研究を行なっております。興味を持たれた方は、ぜひこちらまでご連絡ください。また、朝日新聞Playgroundでは、M研の開発する技術をお試しいただけます(随時更新予定)。ぜひこちらでも、遊んでみてください。

(メディア研究開発センター・浦川通)