見出し画像

ベクターデータベースPineconeとGroqとLlama 3を組み合わせてRAGを試す


データアップロード可能なPinecone

GroqのAPIのことを調べていて、Examplesの「Presidential Speeches RAG with Pinecone」で初めて知ったPinecone

ベクトル型のデータを保存できるベクトルデータベースです。
制限はありますが無料枠もあります。
自分で用意したデータをアップロードすることもできます。
ちなみに、Pineconeは和訳すると「松ぼっくり」です。

Groqと組み合わせて使ってみました。

  1. 用意したテキストをEmbeddingして、Pineconeベクターデータベースにアップロード(upsert)します。

  2. ユーザーが入力した質問を inputで取得します。

  3. Groqを使用して、質問を理解・分析し、意味や意図を把握します。

  4. Pineconeベクターデータベースを使用して、質問に関連する文書を検索します。

  5. 検索された文書の内容を基に、Groqを使用してユーザーの質問に対する応答を生成します。

  6. 生成された応答が英語の場合、Groqを使用して日本語に翻訳します。

  7. 生成された日本語の応答をユーザーに表示します。

GroqとLlama 3を使い質疑応答

上記フローを、ChatGPTやGemini、Claudeに聞きながらコーディングしました。
自分は生成AIを使いながらでないと、Pythonでコーディングはできないので、所々適切でない記述があるかもしれませんがご容赦ください。

パッケージのインストール

# パッケージのインストール
!pip install pinecone-client
!pip install sentence-transformers
!pip install groq
!pip install langchain_pinecone
!pip install langchain_community

GroqとPineconeのAPIキーを設定

import os
from google.colab import userdata
from groq import Client

# Groq APIキー
os.environ["GROQ_API_KEY"] = userdata.get("GROQ_API_KEY")
client = Client()

# Pinecone APIキー
os.environ["PINECONE_API_KEY"] = userdata.get("PINECONE_API_KEY")

Embeddingしたテキストをベクターデータベースにアップ

# ライブラリのインポート
import os
from pinecone import Pinecone, ServerlessSpec
from sentence_transformers import SentenceTransformer

# Pinecone APIキーを環境変数から取得
api_key = os.getenv('PINECONE_API_KEY')

# Pineconeクライアントを初期化
pc = Pinecone(api_key=api_key)

# 新しいインデックス名を指定
index_name = "sample-text04"

# サーバレス仕様を設定
# テキスト文書のベクトル表現を格納する先の指定としてクラウドプロバイダー (aws) とリージョン (us-east-1) も設定
serverless_spec = ServerlessSpec(
    cloud='aws',
    region='us-east-1'
)

# インデックスの作成または取得
if index_name not in [index.name for index in pc.list_indexes()]:
    pc.create_index(
        name=index_name,
        dimension=384,  # all-MiniLM-L6-v2モデルの出力次元
        metric='cosine',
        spec=serverless_spec
    )

# インデックスを取得
index = pc.Index(index_name)

# SentenceTransformerモデルをロード。テキスト文書をベクトル表現に変換するために使用
model = SentenceTransformer('all-MiniLM-L6-v2')

# 埋め込みたい(Embedding)文章
texts = [
    "時は2030年。主人公の真田 輝(さなだ ひかる)が乗る輝光ガンダム(きこうガンダム)に乗り戦場を駆け巡る。地球連邦政府と宇宙植民地間の緊張が高まる中、真田輝は突如として現れた宇宙海賊団「ブラックフェザー」による脅威に直面する。輝光ガンダムのパイロットとして、彼は地球と宇宙の平和を守るため戦う。",
    "時は2032年。主人公の岸田 薫(きしだ かおる)が乗る薫風ガンダム(かおるふうガンダム)は、宇宙植民地での資源採掘拡大と環境保護団体「グリーンガーディアンズ」(Green Guardians)との対立に巻き込まれる。薫は薫風ガンダムで環境破壊を阻止し、宇宙植民地の未来を守るために戦う。",
    "時は2034年。主人公の加藤 涼(かとう りょう)が乗る涼影ガンダム(りょうえいガンダム)は、地球と宇宙の資源競争の調整役として活躍する。彼は新興勢力「ブラッドストリーム」(Bloodstream)の脅威と直面し、涼影ガンダムの力で新たな紛争を回避し、持続可能な未来を築くために奔走する。",
    "時は2036年。主人公の小林 美咲(こばやし みさき)が乗る美咲流星ガンダム(みさきりゅうせいガンダム)は、宇宙船の事故で孤立した植民地を救援する任務に就く。彼女は敵対するAI制御ロボット軍団「シャドウユニット」(Shadow Unit)との戦いで、生存者を救い、新たな冒険に挑む。",
    "時は2038年。主人公の大野 輝(おおの てる)が乗る輝星ガンダム(てるせいガンダム)は、地球と宇宙の経済的格差の問題に取り組む。彼は敵対する大企業コングロマリット「ネクサス・グループ」(Nexus Group)との戦いで、社会の変革を目指し、新たな未来への道を切り開く。",
    "時は2040年。主人公の石川 真理(いしかわ まり)が乗る真理星ガンダム(まりほしガンダム)は、宇宙植民地での自治運動の中で自由と平等を求める。彼女は敵対する独裁政権「シャドウロード」(Shadow Lord)との戦いで、正義のために戦い、植民地の自治を支援する。",
    "時は2042年。主人公の山田 太郎(やまだ たろう)が乗る太陽ガンダム(たいようガンダム)は、宇宙でのエネルギー危機を解決するために太陽光エネルギーの開発を推進する。彼は敵対するエネルギー独占企業「ブラックサンエナジー」(Black Sun Energy)との戦いで、持続可能なエネルギー源の普及を目指し、地球と宇宙の未来を照らす。",
    "時は2044年。主人公の中村 美穂(なかむら みほ)が乗る美穂風ガンダム(みほふうガンダム)は、宇宙での文化交流を促進するために宇宙アートフェスティバルの運営に関わる。彼女は敵対する破壊的芸術団体「クリムゾンアート」(Crimson Art)との戦いで、芸術と文化の力で宇宙社会を豊かにする。",
    "時は2046年。主人公の田中 健太(たなか けんた)が乗る健太流星ガンダム(けんたりゅうせいガンダム)は、地球連邦政府と宇宙植民地の対立を調整するために交渉の場に立つ。彼は敵対する反乱軍「レジスタンス」(Resistance)との戦いで、両者の対話を促進し、平和な解決を見出す。",
    "時は2048年。主人公の佐藤 美鈴(さとう みれい)が乗る美鈴光ガンダム(みれいこうガンダム)は、地球と宇宙の協力を促進するために国際連合宇宙平和機構の活動に参加する。彼女は敵対するテロリスト組織「ブラッドファング」(Blood Fang)との戦いで、宇宙の平和を守り、地球と宇宙の共生を促進する。"
]

# テキストをベクトル化しインデックスにアップロード
for text in texts:
    embedding = model.encode(text).tolist()
    index.upsert(vectors=[(str(hash(text)), embedding, {"text": text})])

埋め込むために用意した文章は、ChatGPTに
「未来のガンダムシリーズのストーリーを考えてください。 2025年から10年間、各年ごとに整理してください。 主人公とガンダムの名前も追加してください。 主人公は、年によっては女性にしてください。よろしくお願いします。」
と依頼して作成た架空のものです。

ベクターデータベースとLlama 3で回答を生成

from groq import Groq
from pinecone import Pinecone
import os

from langchain_community.embeddings.sentence_transformer import SentenceTransformerEmbeddings
from langchain_pinecone import PineconeVectorStore

##### get_relevant_excerpts関数:ユーザーの質問に基づいてPineconeのベクトルストアから関連する抜粋を検索します
def get_relevant_excerpts(user_question, docsearch):
    relevent_docs = docsearch.similarity_search(user_question)

    # Pineconeの類似性検索で取得した関連ドキュメントのうち、最も関連性の高い上位3件のドキュメントを抽出。各ドキュメントの間に区切り線を挿入しています。
    # (今回この動作を確認できませんでした)
    relevant_excerpts = '\n\n------------------------------------------------------\n\n'.join([doc.page_content for doc in relevent_docs[:3]])

    return relevant_excerpts

##### presidential_speech_chat_completion関数:Groqクライアントを使用して、関連する抜粋に基づいてユーザーの質問に対する応答を生成します。
def presidential_speech_chat_completion(client, model, user_question, relevant_excerpts):

    # システムプロンプトの設定
    system_prompt = '''
    あなたは優秀なAIアシスタントです。日本語で丁寧に回答します。
    '''

    # 事前に学習されたモデルを使用してユーザーの質問に対する応答を生成
    chat_completion = client.chat.completions.create(
        messages = [
            {
                "role": "system",
                "content":  system_prompt
            },
            {
                "role": "user",
                "content": "User Question: " + user_question + "\n\nRelevant Speech Exerpt(s):\n\n" + relevant_excerpts,
            }
        ],
        model = model
    )

    # 応答を抽出
    response = chat_completion.choices[0].message.content

    return response

##### main関数:アプリケーションのメインロジックを実行
def main():
    model = 'llama3-8b-8192'

    # all-MiniLM-L6-v2というモデルを使用して入力された文章をベクトルに変換
    embedding_function = SentenceTransformerEmbeddings(model_name="all-MiniLM-L6-v2")

    # Initialize the Groq client
    groq_api_key = os.getenv('GROQ_API_KEY')
    pinecone_api_key=os.getenv('PINECONE_API_KEY')
    #pinecone_index_name  = "sample-text01"
    #pinecone_index_name  = "sample-text02"
    #pinecone_index_name  = "sample-text03"
    pinecone_index_name = "sample-text04"
    client = Groq(
        api_key=groq_api_key
    )

    pc = Pinecone(api_key = pinecone_api_key)
    docsearch = PineconeVectorStore(index_name=pinecone_index_name, embedding=embedding_function)

    # Display the title and introduction of the application
    print("Anime TEST RAG")
    multiline_text = """
    ようこそ。質問をどうぞ。
    """

    print(multiline_text)


    # 無限ループの開始
    while True:
        # Get the user's question:ユーザーの質問を取得
        user_question = input("どうぞ質問を: ")

        # ユーザーの質問が入力された場合の処理
        if user_question:
            #pinecone_index_name  = "sample-text01"
            #pinecone_index_name  = "sample-text02"
            #pinecone_index_name  = "sample-text03" # Pineconeインデックスの設定
            pinecone_index_name = "sample-text04" # Pineconeインデックスの設定
            relevant_excerpts = get_relevant_excerpts(user_question, docsearch) # 関連する抜粋の取得。Pinecone ベクトルストアを扱うために使用されているオブジェクト(上記参照)
            response = presidential_speech_chat_completion(client, model, user_question, relevant_excerpts) # 関連抜粋を基にユーザーの質問に対する応答を生成します。
            print(response)

if __name__ == "__main__":
    main()

質問と回答の例

質問と解答例です。
ベクターデータベースに今回アップロードしたデータを参照してくれています。
ただ、日本語で回答してくれなかったり、回答に正確さが欠けているなどしたりします。

Anime TEST RAG

    ようこそ。質問をどうぞ。
    
どうぞ質問を: 地球と宇宙の経済的格差の問題に取り組む主人公の名前は?
Based on the provided text, the main character's names who tackle the issue of economic disparity between the Earth and the universe are:

1. 大野 輝 (Ōno Teru) in the year 2038.
2. There are no other main characters who specifically tackle the issue of economic disparity.

So, only 大野 輝 (Ōno Teru) is the main character who focuses on the issue of economic disparity between the Earth and the universe.

どうぞ質問を: 佐藤 美鈴が乗るガンダムは?
What a fascinating question! 😊

According to the provided information, the Gundam that Sakurai Mirei (佐藤 美鈴) rides is called the Mirei  Gundam (みれいこうガンダム).

どうぞ質問を: 女性のガンダムパイロットは?
😊

According to the excerpts, the female Gundam pilots are:

1. 小林 美咲 (Kobayashi Misaki) who pilots the 美咲流星ガンダム (Misaki Ryūsei Gundam) in 2036.
2. 中村 美穂 (Nakamura Mihō) who pilots the 美穂風ガンダム (Mihō  Gundam) in 2044.

Note that 加藤 涼 (Katō Ryō) is a male pilot and does not fit the criteria of a female pilot. 😊

どうぞ質問を: 

無料枠のPineconeの制限のためかもしれませんし、アップロードしたデータの記述の仕方が原因かもしれません。
もしかしたら、チャンキング処理もしないといけなかったのかも、など色々理由はありそうですが、初めての動作テストとしては十分かな、と思っています。

アップロードしたデータの確認

アップロードされたEmbeddingデータはPineconeで確認できます。

参考にさせてもらった投稿

この記事が気に入ったらサポートをしてみませんか?