Llamaindex を用いてCSVデータからchatGPTちゃんに回答させてみようと思います。
とりあえずやってみた系の記事なので、雑ですがご了承下さい。
準備
今回は居酒屋的のメニュー的なものを適当に作りました。
項目,タイトル,価格,説明
鴨料理,鴨フィレ肉のカシスソース,930円,鴨フィレ肉を浅めにソテーしロゼ色に。甘酸っぱいカシスと赤ワインのソースで。ピンクペッパーがとても合います。赤ワイン。
鴨料理,鴨刺し,860円,"皮面をパリッと焼き、肉にも焼き目をつけた状態をお刺身。"
鴨料理,鴨南蛮うどん,830円,"鴨と長ネギをごま油で炒め、味を出しています。
合わせだしも引いています。汁まで美味しい一品。小さめの為〆に最適です。"
馬料理,ユッケ,930円,"言わずと知れた生肉ユッケ。
...
以下のライブラリをインストールします。
pip install -U llama-index
pip install -U langchain
llama-indexはEmbeddingsによる検索を簡単に実装できるPythonライブラリです。
langchainはLlamaindexなどのツール群をOpenAI APIと連携できるライブラリです。
こちらは色々とできそうですが詳細割愛します。
近々こちらにスコープして書いてみます。
とりあえずやってみる
分割して記載していきます。
import os
import logging
import sys
from llama_index import ServiceContext, GPTSimpleVectorIndex, LLMPredictor
from langchain.chat_models import ChatOpenAI
from llama_index.prompts.prompts import RefinePrompt, QuestionAnswerPrompt
from llama_index import download_loader
os.environ["OPENAI_API_KEY"] = "<OpenAIのAPIキー>"
# ログレベルの設定
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG, force=True)
# データの読み込み(CSV)
SimpleCSVReader = download_loader("SimpleCSVReader")
# データの読み込み
documents = SimpleCSVReader().load_data(file="data.csv")
今回使用したSimpleCSVReaderについてはこちら
# tempareture=0をしていて、正確性を最重視
# gpt-3.5-turbo を指定(デフォルトは davinci )
# トークン数制限を500に
service_context = ServiceContext.from_defaults(
llm_predictor = LLMPredictor(llm= ChatOpenAI(temperature=0,
model_name="gpt-3.5-turbo",
max_tokens=500)))
# 読み込んだcsvデータをもとに Embbeddings API を通信してベクター取得しIndexを生成
index = GPTSimpleVectorIndex.from_documents(documents, service_context=service_context)
# jsonでローカルに保存
index.save_to_disk("index.json")
ここまでで、csvに入っていたデータをベクトル化しました。
# 質問文
qry ="ラーメンはありますか?"
#質問用のQAプロンプトを生成
QA_PROMPT_TMPL = (
"私たちは以下の情報をコンテキスト情報として与えます。 \n"
"---------------------\n"
"{context_str}"
"\n---------------------\n"
"あなたはAIとして、この情報をもとに質問を日本語で答えます。前回と同じ回答の場合は同じ回答を行います。: {query_str}\n"
)
qa_prompt = QuestionAnswerPrompt(QA_PROMPT_TMPL)
#回答要求用のプロンプトを生成
REFINE_PROMPT = ("元の質問は次のとおりです: {query_str} \n"
"既存の回答を提供しました: {existing_answer} \n"
"既存の答えを洗練する必要があります \n"
"(必要な場合のみ)以下にコンテキストを追加します。 \n"
"------------\n"
"{context_msg}\n"
"------------\n"
"新しいコンテキストを考慮して、元の答えをより良く洗練して質問に答えてください。\n"
"コンテキストが役に立たない場合は、元の回答と同じものを返します。")
refine_prompt = RefinePrompt(REFINE_PROMPT)
ここではプロンプトの定義をしています。
日本語での回答を強制したかったのでこのようにしました。
公式ではこちらに記載されてます。
# 先程生成したベクトルファイルを読み込む
indexFile = GPTSimpleVectorIndex.load_from_disk("index/index.json", service_context=service_context)
# ベクトル検索 + Chat Completion API 実行 + 検索結果TOP3を返す
response = indexFile.query(qry,
text_qa_template=qa_prompt,
refine_template=refine_prompt,
similarity_top_k=3)
#結果確認
print(response)
結果
申し訳ありませんが、この情報にはラーメンの項目が含まれていません。
代わりに、焼酎割りやワインカクテル、ウィスキーやカクテル、
そして梅酒やソフトドリンクなどがあります。
ラーメンをお探しの場合は、他の店舗を探してみてください。
なんでラーメンないんだよっ!!
はい。でもその通りです。csvデータにラーメンは含んでません。
質問文を変えてみました。
# 質問文
qry ="さっぱりしたものが食べたいです。"
結果
当店では、さっぱりとしたメニューを豊富に取り揃えています。
鴨南蛮うどんや春菊のチョレギサラダ、梅水晶などがおすすめです。
また、サワーも白桃&コラーゲンサワーや北海道ハスカップサワー、
アセロラ&ヒアルロンサワーなど、様々な種類をご用意しております。
さらに、ハイボールや焼酎割り、本日のグラスワインもお楽しみいただけます。
焼酎は、(芋)大隅、(芋)金黒、(芋)農家の嫁、(麦)村正、(麦)吟麗玄海、(米)もっこす、(米)鳥飼、(泡盛)三年古酒 瑞泉など、多彩なラインナップを揃えています。
また、日本酒やウィスキー、カクテル、梅酒、ソフトドリンクも充実しています。
お好みに合わせてお選びください。
お酒のメニューを多くしてたから、やたら飲ませようとしてきます。
これがさっぱりしているお酒なのかは分かりませんが、まあ合ってそう。
さいごに
とりあえずやってみた感じですが、簡単にそれっぽい回答を得ることはできました。
ただ公式通りやって動かない事もあったりで…進化が早い分すぐにパラメータが変わったりなんだりしそうですね。まだ実運用には怖いかな…
参考にさせて頂いた記事等