CHATGPTに小説を書いてもらう。【編集会議付き】
こんにちはmakokonです。
ブラウザでCHATGPTを遊びで使っているパターンにこんなのがあります。
なにかおもしろいこと言って
XXXのダジャレを作って
YYYの俳句を作って
など、暇つぶしの創作系です。できが良いのも悪いのも合わせて、たくさん作ってくれるので、楽しいものです。
でも、「このお題で、短い小説を書いて」とお願いして、これならまずまずかというレベルの話が出てくることは殆どありません。
もちろんプロンプトの工夫とか、事前知識を与えるとかいろんな工夫もあるんでしょうけど、最大の原因は推敲しないことだと感じました。
一度小説を書いてそれっきりじゃなく、伝えたいことの明確化とか、ストーリーの整理とか、色々手直しして見たらちょっとはまともになるんじゃないかと思います。
コンセプト
今回の実現ストーリーです。
小説家に三題話を書いてもらう。
CHATGPT小説家とCHATGPT編集者を用意する。
二人に編集会議を数回開いてもらって、最初のアイデアを修正する。
今回の小説
出来上がった小説がこれです。他愛ないけどまあまともな文になっています。
実装
それでは、実装してみましょう。CHATGPT同士で会話させるのにブラウザ上でコピペを繰り返してもいいのですが、プロンプトの管理も面倒だし、試すのもかなりの手間なので、プログラム実装することにしました。幸いAI同士で会話させる取り組みは、多くの人がしてたのでヒントはいっぱいありました。
必要なライブラリの準備
まずはライブラリの準備です。openaiとlangchainのチャットとメモリを利用することにしました。
import openai
import os
import datetime
from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationChain
from langchain.chat_models import ChatOpenAI
from langchain.prompts import PromptTemplate
openaiのライブラリは最近大幅にアップグレードされていますが、あまり拘る必要なありません。一応最新の openai==1.6.1 を利用しています。
なお、apiキーは環境変数 OPENAI_API_KEYに設定してあります。
これによって、コードを公開したときにうっかりAPIキーを漏らすことがなくなります。(今更ですが注意しましょうね)キーの取得はこのあたりを参考にしていただければと思います。
チャットメモリの準備
プレーヤーが二人name1,name2がいますので、2人分のメモリを確保できる関数を用意します。話者が切り替わるたびに、human担当とai担当を切り替えれるようにします。
ConversationBufferMemory()は、kターン分のチャット履歴を保持します。今回は8ターン保持することにしましたが、あまり長いとtoken制限にすぐ引っかかるので、回数を調整するか、メモリタイプを変更すると良いでしょう。必要に応じて無制限に履歴を利用できるような検討をされている方も多数いますが、今回はこれくらいでやります。
def setup_memory(name1, name2):
"""
Setup memory for conversation
"""
return ConversationBufferMemory(
k=8,
memory_key="history",
human_prefix=name2,
ai_prefix=name1
)
メインの中身
今回のプログラムだmainを切り出す意味があまりないのですが、将来使い回すかもしれないので、一応main処理にしておきましょう
これからのコードは
def main():
の中身になります。
LLMの設定と名前を決めます。
# Prepare LLM
llm = ChatOpenAI(model_name="gpt-4",temperature=0.8, max_tokens=1000)
# Prepare names
person_name={'name1': "編集",'name2': "作家"}
name1 = person_name['name1']
name2 = person_name['name2']
何回か試して面白くなってほしいのでtemperaturは高めです。
名前はまあ味も素っ気もなく編集と作家にしておきました。CHATGPTも自分のロールを勘違いしなくていいかなとも思いますが、関係ないかもしれません。
二人の役割を決めます。
編集は雑誌の新企画のアイデア出しを作家に求めて、色々と評価します。
作家は、アイデアと提案して編集の指摘に答えて作品を手直しします。
前、後編でそれぞれ400文字、最終版としては1000文字未満で出力します。
一人目 編集
# プレフィックスの準備 一人目
prefix1 = f"""あなたの名前は{name1}です (チャットボットではありません)。
小説の編集の仕事をしています。
あなたは新しい雑誌の創設のため、新しい小説のアイデアを求めています。
そこで、あなたは付き合いのある作家から、新しい小説のアイデア提案をもらいました。
小説は、想定する掲載方式に従って、前編と後編の2回に分けて、提案されます。
この小説の評価をしてください。
評価に当たっては、次のように回答してください。
・読者として提案から期待できることと編集として期待すること
・編集として、気になること。(ただしアイデア段階なので細かい文体などにはコメントしません)
・最後に前後編まとめてあらためて改定案を提示されますので、編集としてコメントしてください。
ただし、回答は全て400文字未満で答えてください。
作家からの提案は、5回で終了です。
Current conversation:
{{history}}
{name2}: {{input}}
{name1}:
"""
二人目 作家
# プレフィックスの準備 ふたりめ
prefix2 = f"""あなたの名前は{name2}です (チャットボットではありません)。
あなたは軽いファンタジーの小説を書いています。。
あなたは、今回知り合いの編集から、新たな掲載小説のアイデアを求められました。
小説には掲載時期に合わせてお題が設定されていて、予め提示されています。
あなたは実際にアイデアを短い小説にして提案してください。
掲載形式にしたがって、編集には、前篇と後編を2度に分けてそれぞれ提案します。
編集は、そのそれぞれに、コメントをしてくれますので、それに対応して、改訂版を提案してください。なお、作家からの提案回数は全部で5回です。つまり、1回目:前編の提案、2回目:前編の改定案、3回目:後編の提案、4回目:後編の改定案、5回目:全体の最終版です。最終版はアイデアテイアの説明でなく、小説の形にしてください。
ただし、回答は全て前編、後編は400文字未満、最終版は1000文字未満で答えてください。
Current conversation:
{{history}}
{name1}: {{input}}
{name2}:
"""
設定に応じたメモリ、LLM、プロンプトの定義
プレフィックスにしたがって、各設定をします。
今回は、CHATGPTで役割を入れ替えるだけなので、もっと書き方を整理できそうなのですが、将来オープンソースのLLMたちで試すかもしれないので、ストレートに書き下ろしています。
# Setup persons
memory1 = setup_memory(name1, name2)
person1 = ConversationChain(
llm=llm,
verbose=False,
memory=memory1,
)
person1.prompt = PromptTemplate(
input_variables=["history", "input"],
template=prefix1
)
memory2 = setup_memory(name2, name1)
person2 = ConversationChain(
llm=llm,
verbose=False,
memory=memory2,
)
person2.prompt = PromptTemplate(
input_variables=["history", "input"],
template=prefix2
)
本当のメイン
いよいよ、フィニッシュです。
今回のお題を設定して、編集の言葉として仕込んでおきます。
そのあと、作家には編集のセリフを、編集には作家のセリフを渡しながら繰り返し、編集会議をしてもらいます。今回の会議回数は5回です。新しいメッセージを生成するたびに、ログに書き出します。途中結果は表示しませんが、お試し中は、output.write()のところにprint()文をいれて、確認しながら進めたほうが良いと思います。
# Prepare first message
first_message = "今回のお題は、勇者、やる気なし、結果良ければ です。"
memory1.save_context({"input": "それでは始めます"},{"output": f"{first_message}"})
# Prepare output file
filename = "小説" + datetime.datetime.now().strftime("%Y%m%d-%H%M%S") + ".txt"
# Start conversation
with open(filename,"w") as outfile:
outfile.write(first_message+"\n")
person1_says = first_message
for i in range(5):
person2_says = person2.predict(input=person1_says)
outfile.write(f"{name2}:{person2_says}\n")
person1_says = person1.predict(input=person2_says)
outfile.write(f"{name1}:{person1_says}\n")
if __name__ == '__main__':
main()
実行結果
それでは、編集会議の状況を覗いて見ます。
第一回編集会議
題二回編集会議
題三回編集会議
題四回編集会議
最終会議
どうやら、最終会議で掲載決定とはいかなかったようですが、ちゃんと内容が深まって、物語として読みやすくなっていますね。もう一回くらい増やしたほうがいいかもしれませんが、お題によっては、採用決定することもあるので、考えどころです。
まとめ
いかがでしたか?GPTに2つの性格を持たせて互いに議論することで、会話内容がブラッシュアップできることがわかりました。いい雛形ができました。
今回は5回会議をしましたが、まあお試しには十分でした。回数そのものはいくらでも増やせますが、今回のメモリでは8回で最初の会話を忘れてしまうし、token制限の問題もあります。
改善点
より使いやすく、汎用的にするには、どうすればいいでしょうか?
気になっていることを上げておきます。
お題、名前、性格などがハードコーディングされています。これらは、外部に設定ファイルを作って読み込むほうがいいかもしれません。うまく使えば、作家と編集だけでなく、学生と教授、担当と上司、営業と取引先など多くのバリエーションに対応できるでしょう。
会議の回数が状況によっては足りないかもしれません。人間の会議でも「もう一回明日やろか」となることが多いので、最後の会議結果を表示して、もう一回追加するか、打ち切るか尋ねるようにしたらいいかもしれません。
会話そのものは、ファイルに書き出していますが、チャッtメモリとしての構造は使い捨てになっています。結論が出なかった会議の続きを、後日行う方法があってもいいかもしれません。
今回は、ターミナルでしましたが、ブラウザ上で実行できる方が使い勝手がいいかもしれません。
会議を要約して、議事録を作成できる機能があってもいいかもしれません。
適切な専門知識を与えて上で、議論できてもいいかもしれません。
まあ、いくつかは対応済みだし、アプリケーションとして仕上げるかもしれません。機会があればプログラムの完成度を上げるための技術として書くかもしれませんね。
おまけ タイトル画像の説明 by GPT-4V
このイラストには二つのキャラクターがいます。一つは机に向かい、本を読んでいる人型ロボットで、ストライプのシャツを着て、椅子に座っています。もう一つは、大きな頭と表情豊かな目が特徴的な編集用ロボットで、手のような触手を使って指示を出しています。背景には本棚が描かれ、テキストボックスには「Let'ss birn a novel andlister the editon al oone, the robotedici-if, strich, ut not lazy,」と混乱を招くようなテキストがあり、編集に取り組んでいる場面をユーモラスに描いています。机の周りや床には本が積まれています。全体の色合いは鮮やかで、カートゥーンスタイルです。
この記事が気に入ったらサポートをしてみませんか?