ローカルLLMに小説を書いてもらう v2
はじめに
以前、「ローカルLLMに小説を書いてもらう」という記事を投稿しました。
この時はそれぞれ単独のプロンプトで小説家と編集者を演じさせましたが、今回はもうすこしシステマチックに、段階を踏んで小説を生成させてみます。
プロンプトの検討等にはkgmkm氏のリポジトリや記事を参考にさせていただきました。この場を借りてお礼申し上げます。
仕組みを相談するのにClaude (3.5 Sonnet)とやり取りをしていましたので、この記事の草稿も書いてもらいました。所々、なんとなく冗長だったり文体が違ったりしますが、面倒なのでそのままにしてあります(すみません)。
生成スクリプト
生成スクリプトとプロンプト定義はgistに置きました。
https://gist.github.com/kohya-ss/68d41a9720bfbdfd87869ec970142f4b
概要
近年、大規模言語モデル(LLM)の発展により、AIによる文章生成の可能性が大きく広がっています。今回はローカル環境で動作するLLMを使用して(9B~100Bクラス)、8,000〜10,000字程度の短編小説を生成してみます。現時点では「作品として読める」ものを生成するのは厳しいと思いますが、いくつかの工夫で品質向上を図ってみます。
今回は前回と異なり、小説執筆プロセスを複数のタスクに分割し、段階的に詳細化していく方法を採用しました。この方法により、ローカルLLMの制限を克服しつつ、一貫性のある物語を生成することを目指しています。
現在の生成プロセスは以下の6段階で構成されています:
プロット生成(100字程度)
登場人物設定(主要人物の列挙)
キャラクター設定(詳細な人物設定)
アウトライン作成(4〜5章に分割)
シーン分割(各章を4〜5シーンに分割)
本文生成
この段階的なアプローチにより、物語の全体像から細部まで、一貫性を保ちながら生成することが可能になった……とまではいいませんが、少なくとも一気に生成するよりはマシに思えます。
主な課題
前回までの試みからいくつかの課題がわかっています。
ローカルLLMのコンテキスト長の制限
ローカルで動作するLLMは、最新のクラウドベースのモデルと比較して、モデル自身の制約や、VRAM量も制限から、扱えるコンテキスト長(一度に処理できるテキストの長さ)が制限されています。この制限により、長文の生成や、前後の文脈を考慮した一貫性のある文章の生成が困難になります。
一貫性の維持
小説全体を通して、キャラクターの性格、設定、物語の展開に一貫性を持たせることは、人間の作家にとっても難しい課題です。AIによる生成では、この一貫性の維持がさらに困難になります。特に、異なる章や場面で、キャラクターの行動や性格が矛盾しないようにすることが大きな課題となります。
課題への解決策
これらの課題に対して、今回は以下のような解決策を実装しました。
エージェントの導入(小説家と編集者)
前回と同様に、物語の生成プロセスに、「小説家」と「編集者」という2つの役割を持つAIエージェントを導入しました。小説家エージェントは実際の文章生成を担当し、編集者エージェントはレビューと改善提案を行います。このチェック体制により、品質と一貫性の向上を図りました。
プロンプト設計の工夫
各段階で使用するプロンプトでは、エージェントの役割や期待される出力を明確に指示しました。システムプロンプトでは各エージェントの基本的な性格付けを行い、ユーザープロンプトでは具体的なタスク指示と前段階までの情報提示を行います。
情報の引き継ぎ方法の最適化
ローカルLLMのコンテキスト長の制限を考慮し、各段階で必要最小限の情報のみを提供する方法を採用しました。長くなる情報(過去のシーン分割や本文)は省略し、重要なポイントのみを引き継ぐことで、制限内で効果的に情報を伝えています。
ただ、この方法だと前の章の本文を参照できず、文体に一貫性が持てなくなります。そこで、前の章の末尾、10行程度を参照情報としてプロンプトで与えることにしました。導入したばかりのため今のところ効果は不明ですが、最適な行数や与え方には工夫の余地もありそうです。
生成プロセスの詳細
今回試した生成プロセスは、前述のように以下の6段階で構成されています。このプロセスはわりと適当に決めました。
各段階で小説家エージェントと編集者エージェントが協働し、段階的に物語を構築していきます。
プロット生成(100字程度)
小説全体の概要を簡潔に表現します。
主要な登場人物、設定、中心的な出来事を含みます。
登場人物設定(主要人物の列挙)
プロットに基づいて、主要な登場人物をリストアップします。
各キャラクターの役割(主人公、ライバル、親友など)と性別を決定します。
キャラクター設定(詳細な人物設定)
各登場人物の詳細な背景、性格、外見、行動原理を設定します。
物語を通じての成長や変化も考慮します。
アウトライン作成(4〜5章に分割)
プロットを基に、物語全体の構造を4〜5章に分けて詳細化します。
各章の主要な出来事、展開、登場人物の動きを決定します。
シーン分割(各章を4〜5シーンに分割)
各章をさらに4〜5つのシーンに分割します。
各シーンの具体的な内容、登場人物の行動、感情の変化を決定します。
本文生成
シーン分割に基づいて、実際の小説本文を生成します。
描写、会話、心理描写などを織り交ぜながら、読者を引き込む文章を目指します。
各段階で、小説家エージェントが内容を生成し、編集者エージェントがレビューを行います。NGの場合は改善点を指摘し、小説家エージェントが修正を行います。このプロセスを繰り返すことで、品質の向上と一貫性の維持を図っています。
その他、LLMが生成する挨拶文やコメント(「以上が原稿になります」のような)を削除するために、都度LLMを呼び出します。
たとえば恋愛小説を生成対象としたプロット生成での例です(以降、生成例はgemma-2-9b-it-Q8_0によるもの)。小説家エージェントの生成が次のようなものだとすると:
以下のように加工して編集者エージェントのプロンプトにします。
これに対する編集者エージェントの生成は以下のようになりました。
出力例の紹介
本プロジェクトで生成された短編小説の一部を紹介します。以下は、生成されたプロットと本文の一部です。モデルはgemma-2-9b-it-Q8_0です。生成結果全体は記事末尾に添付しておきます。
プロット
さきほどの例のリテイク後です。
本文(冒頭と末尾)
9Bモデルとしてはまあまあそれらしい結果になっているようです。
技術的な詳細と細かい工夫
使用したローカルLLMの詳細
今回はGemma-2-9BやCommand-R+などで試しました。小説家と編集者でやり取りする結果、コンテキストはどうしても長くなるので、コンテキスト長が16K以上取れるモデルが良さそうです。
LLMライブラリ
ごく普通のチャット形式のプロンプトなので、llama-cpp-pythonまたはtransformersの両対応としました。
編集者のレビュープロセスの改善策
編集者エージェントによるレビューの質を向上させるため、以下のような工夫を実装しました。
初回レビューの強制NG設定
プロット、アウトライン、シーン分割、テキスト本文について、初回レビューは必ずNGを出すように設定可能にしました。
これにより、小説家エージェントに改善の機会を与え、より質の高い文章生成を促しています。
自然なNG誘導
初回NG時には、レビュー依頼のユーザープロンプトに「(編集長からのアドバイス:今回は初回のレビューなのでNGを出しましょう!)」という文言を追加しました。
これにより、生成されたテキストを加工するのではなく、AIエージェント間の自然なやり取りの中でNGを出すことができます。
詳細なフィードバック生成
当初は編集者エージェントは、最初にOK/NG判定、その後にアドバイス、という順で生成していましたが、詳細なフィードバックを生成してから、最後にOK/NGを判定する方法を採用しました。
これにより、より具体的で有用な改善提案が可能になりました。NGを出す率も上がった気がします。
今後の展望とまとめ
今回は段階的な生成プロセスとAIエージェントの導入により、一定の成果を上げることができましたが、まだ多くの改善の余地がありそうです。
プロセスの再検討
現在の六段階のプロセスは検討の余地がありそうです。LLMの進化によっても変わってくるでしょう。
プロンプトの最適化
小説家、編集者とも、より効果的なプロンプト設計を行うと生成品質を上げられそうです。また使用するモデルの性格による調整は必須になりそうです。
一貫性維持のための方法の改善
章をまたぐ際の情報の引き継ぎ方法をさらに改善し、物語全体の一貫性を高める工夫ができるかもしれません。
LLMによる分担
やり取りの回数が多いため全体では相当の時間が掛かります。軽いモデルで設定を行い、本文は重いモデルで生成する、といった工夫も有効かもしれません。
またはサービス型のLLMをここぞというときに使うのもありかも。
人間の作家との協働モデル
どうにもAIは後半のタスクになるほどボロが出てくるので、協働モデルというほどではありませんが、AIによる下書き生成と人間の作家による仕上げはありだと思います。
また生成プロセスごとに介入して、生成結果を修正、ブラッシュアップしてから次のステップに進む、というのは有効そうです。
(というか、少しでも最終出力をまともにしたいなら、現状ほぼ必須のようです。)
前回の記事から二か月弱ですが、個人的にも多少LLMの理解が進み、ある程度の改善ができたように思います。またこの間、特に30B以下の比較的小型のLLMはかなり精度が向上したようです。
今後のLLMの進化や、AIとの共同作業なども非常に楽しみになってきました。最後にClaude 3.5 Sonnetが書いたまとめの文章をそのまま置いておきます。
生成結果
Gemma-2-9Bの生成結果です。最後のテキストからは章の見出しが消えていますが、これは校正エージェントが「テキスト本文ではない」という理由で削除したためです。
この記事が気に入ったらサポートをしてみませんか?