元ネタになるようなテキスト、例えばプレスリリースのようなパブリックな文章を元にコンテンツを作成したい場合があると思います。あるいは、マニュアル・ドキュメントから使用例を考える、マーケティングやスピーチの原稿を作るといったことも考えられます。
そうしたときに、 LangChainから OpenAI の GPT-3.5-turbo の APIを利用することで、簡単に記事を量産することができます。
元記事では、URL から複数のデータを取得して結合しています。そのようにすることで、例えば、過去のブログの内容を踏まえた新しい記事の生成や、製品のマニュアルを参照したチュートリアルの作成など、副次的なコンテンツの作成が可能になる、とされています。
この記事ではこの部分は扱わずに、単に文脈から複数のコンテンツを作成します。もし、複数のコンテンツを統合してこのような作業をしたい場合は、元記事のコードを見るといいと思います。
元記事では、 GPT-3 Davinci を使用していますが、10倍高価なのと、 API の仕様が大分異なるので、この記事では LangChain の ChatOpenAI を使って記事の生成を行います。
実際に書いてみる
参考:LangChainから FAISS を使った vector index の作成と保存は過去の記事を参照
import から 保存された vector index の読み込みまで
# ChatOpenAI GPT 3.5
from langchain.chat_models import ChatOpenAI
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
from langchain.prompts.chat import (
# メッセージテンプレート
ChatPromptTemplate,
# System メッセージテンプレート
SystemMessagePromptTemplate,
# assistant メッセージテンプレート
AIMessagePromptTemplate,
# user メッセージテンプレート
HumanMessagePromptTemplate,
)
from langchain.schema import (
# それぞれ GPT-3.5-turbo API の assistant, user, system role に対応
AIMessage,
HumanMessage,
SystemMessage
)
# Embedding用
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.text_splitter import CharacterTextSplitter
# Vector 格納 / FAISS
from langchain.vectorstores import FAISS
# env に読み込ませるAPIキーの類
import key
# 環境変数にAPIキーを設定
import os
os.environ["OPENAI_API_KEY"] = key.OPEN_API_KEY
# vector index
embeddings = OpenAIEmbeddings()
db = FAISS.load_local("faiss_index", embeddings)
以前の記事の繰り返しになりますが、 Davinci 以前と、GPT-3.5-turbo(そろそろ何か愛称が欲しい)では、 API に投げるメッセージの形式が異なります。これは、 LangChain の仕様ではなく、OpenAI の API がそういう仕様になっています。
そのため、PromptTemplate, Message をSystem, Assistant, Human の3つについて import する必要があります。
prompt の作成
# 文字列定義が面倒なので直接テンプレートを作成
prompt=PromptTemplate(template="""Use the context below to write a 2000 character in Japanese blog post:
Context: {context}""", input_variables=["context"])
system_message_prompt = SystemMessagePromptTemplate(prompt=prompt)
# ユーザーからの入力
human_template="""about the topic below:
Topic: {topic}
Blog post(in Japanese):"""
# User role のテンプレートに
human_message_prompt = HumanMessagePromptTemplate.from_template(human_template)
# 先に定義したテンプレートをまとめて、ひとつのChatTemplateに
chat_prompt = ChatPromptTemplate.from_messages([system_message_prompt, human_message_prompt])
# LLM
llm = ChatOpenAI(temperature=0)
chain = LLMChain(llm=llm, prompt=chat_prompt)
まずは、System Role のテンプレートを作成します。
このテンプレートでは、英語での指示に加え(2000文字の日本語の記事を以下のcontextに従って記述せよ)、context という変数を与えるように指示しています。後で出て来ますが、この Context には、参考となるテキストが入ります。本記事の場合は、FAISS で db 変数に読み込んだ vector index による検索結果が代入されます。
続いて、 Human Role のテンプレートとなります。context に対して、 topic を指示して、その下に Blog Postを書く様に指示しています。
正直、topic も System Role に入れても構わないでしょうし、 “Blog post(in Japanese):”の部分は、Assistant のテンプレートとすべきかもしれません。この辺りは、チューニングしながら試す必要があると思います。ここでは、 ChatOpenAI の Prompt 形式でも実現できることを試すために、とりあえず分けています。
また、指示を英語にしているのは、token 数の節約のためです。
ChatOpenAI(=GPT-3.5-turbo)のインスタンスを作成し、 prompt とあわせて LLMChain のインスタンスを作成して、下準備は終わりです。
基本的にここまでの流れは、Embedding を用いた Q&A と差がありません。最後に、Agent ではなく LLMChain を作成している点が異なります。
chain.apply を使ってまとめて実行する
def generate_blog_post(topic):
docs = db.similarity_search(topic, k=4)
inputs = [{"context": doc.page_content, "topic": topic} for doc in docs]
print(chain.apply(inputs))
generate_blog_post("A社の経営課題と日本の農業法人全体の課題に関する考察")
元記事が関数化してたので関数にしていますが、テストするだけなら関数にしないでベタに書いてもいいと思います。 ChatGPT のようにユーザーからの入力を繰り返し受けたい場合は、関数化しておいた方が便利かもしれません(ループに入れても数行ですが)。
とはいえ、関数内が重要なことには変わりません。
まずは、 similarity_search メソッドにより、与えられた topic を検索します(ここではまだ、prompt の topic ではない点に注意)。また、 k=4 として、検索数を増加させています。このtopic によるデータベースの検索結果が、context となります。
人間と違って、「この記事は前に書いたな…」というような長期記憶は(現在のところ)実装が難しいため、context のソースとなるデータベースが巨大な場合、 kの数値を大きくした方が無駄なく記事が生成できるかと思います。あるいは、DBを分割して生成してもいいでしょう。
検索結果を context, 引数として与えられたtopic を topicとして(つまり、1回の関数の呼び出しではtopicは不変)、LLM に渡す input のリスト inputs を生成します。
最後に、 apply メソッドでリストの中身を使って LLM を動作させています(つまり、この例では4パターン実行されています)。
動作結果
※さすがに読みづらいので筆者により整形。しっかり読む人もあんまりいないと思いますが……。
A社の経営課題は、日本の農業法人全体の課題でもある。農業法人は、農業生産の担い手として、地域経済の活性化や食料自給率の向上に貢献している。しかし、農業法人の経営は、農業の特性上、天候や季節的な繁閑などに左右され、不安定な面がある。また、農業法人の経営者は、農業技術や経営管理のスキルを持ち合わせている必要があるため、人材確保の課題もある。
日本の 農業法人全体の課題としては、以下のようなものが挙げられる。
1. 人材確保の課題
農業法人の経営者は、農業技術や経営管理のスキルを持ち合わせている必要がある。しかし、若者の農業離れが進んでおり、人材確保が課題となっている。
2. 農業生産の安定化
農業は、天候や季節的な繁閑などに左右され、不安定な面がある。農業法人は、生産の安定化を図るために、技術革新や多角化などの取り組みが必要となる。
3. 地域と の連携
農業法人は、地域との連携が重要である。地域の需要に合わせた生産や、地域の特産品の開発などが求められる。
4. 経営管理の強化
農業法人の経営は、農業の特性上、不安定な面がある。経営管理の強化が求められる。
以上のような課題があるが、A社のように新しい収益の柱を模索し、地域との連携を図ることで、経営課題を乗り越えることができる。また、農業法人全体として、若者の農業離れを防止するために、農業の魅力を伝える取り組みや、農業技術の継承などが必要である。さらに、農業生産の安定化や経営管理の強化などにも取り組むことが求められる。’},
{‘text’: ‘A社は、長年にわたって苺の栽培と販売を行ってきたが、バブル経済崩壊後、贈答用の高級苺の売上高が減少し、経営課題に直面している。そこで、A社は、作り方にこだわった野菜の栽培を始め、サツマイモ、レタス、トマト、トウモロコシなどを栽培するようになった。しかし、農業法人全体の課題として、人手不足や高齢化、農業の技術革新の遅れなどがある。
まず、A社の経営課題について考えてみると、苺の売上高が減少したことにより、新たな商品の開発や販路の拡大が必要となっている。また、野菜の栽培に切り替えたことで、新たな技術や知識が必要となり、従業員の教育や研修が必要となっている。さらに、野菜の栽培には、農薬や肥料の使用量の管理が必要であり、環境保全にも配慮しなければならない。
次に、農業法人全体の課題について考えてみると、人手不足や高齢化が深刻な問題となっている。農業は、体力的な労働が多く、若者の就農意欲が低下しているため、人手不足が深刻化している。また、農業従事者の高齢化も 進んでおり、後継者不足が問題となっている。さらに、農業の技術革新が遅れていることも課題となっている。農業は、科学技術の進歩により、より効率的な生産が可能となっているが、その技術を導入するための費用や知 識が不足している。
以上のように、A社の経営課題と農業法人全体の課題には、共通する問題がある。それは、新たな商品や技術の開発による競争力の向上や、従業員の教育や研修、環境保全への取り組みなどが必要であることである。また、人手不足や高齢化、技術革新の遅れなどは、政府や地域社会との連携が必要であり、農業法人全体の課題として取り組む必要がある。
最後に、A社は、苺の売上減少に対応するために、野菜の栽培に切り替えたが、今後も商品開発や販路の拡大、技術革新に取り組むことが必要である。また、農業法人全体としても、人手不足や高齢化、技術革新の遅れなどに対応するために、政府や地域社会との連携を強化し、農業の持 続的な発展に取り組むことが求められている。’},
{‘text’: ‘A社は、農業の仕事が定時出社・定時退社で完結できないことや、新規就農者の確保が難しいことなど、従業員の定着が悪いという課題を抱えていました。また、農業未経験者にも中途採用の門戸を開いていたが、帰属意識の高い従業員を確保することが難しかったとのことです。このような課題は、A社だけでなく、日本の農業法人全体に共通するものです。
日本の農業法人全体の課題としては、まず、農業のイメージアップが挙げられます。農業は、古くから日本の文化や伝統の一つであり、食料自給率を高めるためにも重要な役割を果たしています。しかし、現代社会では、農業は「汚い」「低収入 」といったイメージが強く、若者の就農意欲が低下しています。このため、農業のイメージアップが必要です。
また、農業の生産性向上も課題の一つです。日本の農業は、小規模かつ労働集約的なため、生産性が低く、 収益性が悪いという問題があります。このため、農業の生産性を向上させるための取り組みが必要です。
さらに、農業のグローバル化に対応することも課題です。日本の農業は、国内市場に依存しているため、海外市場 に進出することが難しいという問題があります。しかし、グローバル化が進む現代社会においては、海外市場に進出することが必要不可欠です。
以上のような課題を解決するためには、まず、農業のイメージアップが必 要です。農業は、食料自給率を高めるためにも重要な役割を果たしていることを広く知らせることが必要です。また、農業の生産性を向上させるためには、技術革新や労働力の確保などの取り組みが必要です。さらに、海外 市場に進出するためには、海外市場に合わせた商品開発や販路の確保などの取り組みが必要です。
A社も、直営店や食品加工の分野に展開することで、新たな収益源を確保しています。また、自社工場を設置することで、外部取引先からパン生地を調達し、自社栽培の新鮮で旬の野菜やフルーツを使った商品を製造しています。このような取り組みは、農業の生産性向上や海外市場に進出するための一つの手段となります。
日本の農業法人全体が、農業のイメージアップや生産性向上、海外市場に進出するための取り組みを進めることで、より持続可能な農業を実現することができます。’},
{‘text’: ‘A社は、地域に根ざした農業を基盤に据えつつ、新たな分野に挑戦することを考えている。しかし、人手不足が顕著になってきており、生産を兼務する従業員だけでは対応できなくなりつつある。このような課題は、A社だけでなく、日本の農業法人全体に共通するものである。日本の農業法人全体の課題としては、人手不足や高齢化、農業の担い手不足などが挙げられる。これらの課題は、農業の現場だけでなく、経営面でも深刻な問題となっている。農業法人は、経営戦略の見直しや新たなビジネス モデルの構築が必要とされている。
A社が直面している課題について考えると、まずは人手不足の解消が必要である。そのためには、労働環境の改善や労働力の確保が必要となる。また、生産性の向上や省力化にも取り組む必要がある。これらの取り組みによって、従業員の負担を軽減し、生産性を向上させることができる。
さらに、A社が新たな分野に挑戦するためには、経営戦略の見直しや新たなビジネスモデルの構築が必要である。例えば、直営店に併設したオープンカフェ形式による飲食サービスは、消費者との接点を増やすことができ、自社商品に関する消費者の声を取得することができる。このような取り組みによって、新たなビジネスモデルを構築し、事業の拡大を図ることができる。
日本の農業法人全体の課題について考えると、人手不足や高齢化、農業の担い手不足などが挙げられる。これらの課題を解決するためには、農業の現場だけでなく、経営面でも取り 組む必要がある。例えば、労働環境の改善や労働力の確保、生産性の向上や省力化などが挙げられる。また、新たなビジネスモデルの構築や、農業と他の産業との連携なども重要である。
以上のように、A社が直面している課題は、日本の農業法人全体が抱える課題と共通している。これらの課題を解決するためには、経営戦略の見直しや新たなビジネスモデルの構築が必要である。また、労働環境の改善や労働力の確保、生産性の向上や省力 化などにも取り組む必要がある。日本の農業法人全体が、これらの課題に取り組むことで、持続可能な農業の発展を図ることができる。’}]
一つの記事に対して、A社の課題という、データベース内から検索可能な内容と日本の農業法人全体という記事にない(しかしGPT-3.5-turbo 内にはあるだろう) topic を与えることで両者を関連付けた記事が作成されていることが分かります。
また、元にした中小企業診断士の試験問題をざっと読むと分かりますが、出力されている記事は、試験問題で出てくる順番の内容であることが分かります。そのため、ちょっと考察が薄いような感じもしますね。また、元になる記事が短すぎたのか、GPT-3.5-turbo の限界か、文字数については全く2000文字に届いていません……。英語で400単語と指示し、それを日本語に翻訳して出力せよ、といったpromptを組む方がいいかもしれません。
発展させる場合は、similarity_search は k=4程度のまま実行させ、かつ、Chain.applyで個別に結果を生成するのではなく、prompt前に検索結果を結合させて記事を生成させる方法や、今回のように個別に記事を生成させた上で更にまとめさせる方法も考えられます(後者は面白い結果になりそうですが、token 凄そうですね……)。
まとめ
- ChatOpenAI 用の Prompt でもちゃんと LLMChain は作れる
- LLMChain.apply メソッドで複数の LLM の実行をまとめて行える
- vector database から 単純な Q&A 以上の実行をしたい場合、similarity_search で取得した内容を、改めてLLMに食わせると詳細な結果を出力できる。
- 日本語の文字数指定は難しい