1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

watsonx.data Milvus + watsonx.ai RAGハンズオン Part4: ベクトルDB Milvusとwatsonx.ai LLMでRAGを構成して、チャットアプリを作成してみよう!

Posted at

この内容は2024年11月27日(水)にホテル雅叙園東京で開催された「IBM TechXchange Japan 2024」で実施したwatsonxハンズオン「さわってみよう ベクトル・データベース watsonx.dataでRAG体験」の内容です。QiitaではPart1, Part2, Part3, Part4の四部構成で、 この投稿はPart4となります。

0.前提

尚、Part2: ベクトルDB Milvusに入ったデータで類似検索してみよう! および Parr3: ベクトルDB Milvusとwatsonx.ai LLMでRAGを構成して、質問をしてみよう!は実施していなくいても、実行は可能ですが、Part2およびPart3の内容を使用していますので、ぜひ実施してからこちらに進んでください。

1. ベクトルDB Milvusとwatsonx.ai LLMでRAGを構成して、チャットアプリを作成してみよう!

この内容はjupyter notebook https://github.com/IBM/japan-technology/blob/main/techxchange/2024-watsonx-handson-1/notebooks/techxchange_handson_04_chatbot.ipynb とほぼ同じです。Link先notebookはwatsonx.ai Studioでの実行を前提としていますが、当内容はLocalも含め一般的なpython実行環境が前提です。

image.png

1. 必要なライブラリーのインストール

必要に応じてpipにて以下のライブラリーを導入します:
(最後にgradioのインストールがあるのが Part1,2,3と異なる部分です)

pip install -Uq 'ibm-watsonx-ai>=1.1.15'
pip install -Uq 'langchain>=0.3.3'
pip install -Uq 'langchain-ibm>=0.3.1'
pip install -Uq 'langchain-milvus>=0.1.6'
pip install -Uq 'langchain-community>=0.3.2'
pip install -Uq 'pymilvus>=2.4.8'
pip install -Uq gradio

以下2〜5はPart1: Excelをベクトル化してベクトルDB Milvusに入れようの2〜5と全く同じです。
また7〜8はPart2: Part2: ベクトルDB Milvusに入ったデータで類似検索してみよう!の7〜8と全く同じです。

2. apikeyの設定

apikey をセットしてください。
IBM Cloud環境でのAPIKEYの取得方法はこちらを参考にしてください。

またwatsonx.dataとwatsonx.ai Studioは同じAPIKEYである前提で同じ変数を使用していますので、もし違う場合は2つ変数を準備するようにしてください。

apikey = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

3. Milvus接続情報の設定

Milvus接続情報の値をセットしてください。

  • Milvus Host名
  • Milvus Port番号

Milvus接続情報の詳細取得手順はこちら を参照してください。

またmy_connection_argsに設定するkeyの詳細はpymilvusのMilvusClientのパラメータを参照してください。

milvus_host="xxx.xxx.xxx.com"
milvus_port="YYYY"

# Milvus接続情報パラメータののセット
my_connection_args ={
 'uri': f'https://{milvus_host}:{milvus_port}', 
 'token': f'ibmlhapikey:{apikey}'
}

4. watsonx.ai Studio Project idの設定

watsonx.ai Studioで実行する場合は、このノートブックが実行されるプロジェクトからProject idを取得します。 watsonx.ai Studio以外で実行する場合は、Project idを入力してください。

Hint: project_id はプロジェクトを表示し、管理タブから project_id を取得可能です.

import os
try:
    project_id = os.environ["PROJECT_ID"]
except KeyError:
    project_id = "<ProjrctID>"

5.watsonx.aiのAuthentication用のエンドポイントのURLの設定

Waston Machine Learningのインスタンスを作成したリージョンで決まります。 https://ibm.github.io/watson-machine-learning-sdk/setup_cloud.html#authentication より

Dallas: https://us-south.ml.cloud.ibm.com
London: https://eu-gb.ml.cloud.ibm.com
Frankfurt: https://eu-de.ml.cloud.ibm.com
Tokyo: https://jp-tok.ml.cloud.ibm.com

watsonx_url = "https://us-south.ml.cloud.ibm.com" #ダラスの場合

6. 必要ライブラリーのImport

import pandas as pd
from langchain.schema.document import Document
import json
from langchain_milvus import Milvus
import os
from ibm_watsonx_ai.metanames import EmbedTextParamsMetaNames
from langchain_ibm import WatsonxEmbeddings
from ibm_watsonx_ai.metanames import GenTextParamsMetaNames as GenParams
from langchain_ibm import WatsonxLLM
from langchain.prompts import PromptTemplate
from langchain.schema.runnable import RunnablePassthrough

7. Embeddingモデルの取得

Part1: Excelをベクトル化してベクトルDB Milvusに入れようの11と全く同じです。
ここではintfloat/multilingual-e5-largeを使います

LangChainで使用できるwatsonx.aiのEmbeddingモデルintfloat/multilingual-e5-largeがあるので、今回はこちらを使用します:

尚、intfloat/multilingual-e5-largeはオープンソースで公開されているので、watsonx.aiのEmbeddingモデルを使用しなくとも、ローカルにダウンロードすることで使用可能です。 その場合のコードはこちらです(今回は使用しません)

from langchain_huggingface import HuggingFaceEmbeddings
from tqdm.autonotebook import tqdm

embeddings = HuggingFaceEmbeddings(model_name="intfloat/multilingual-e5-large")

下記は`apikey`はMilvusと同じものを使用しています。異なる場合は変数`apikey`を変更してください。
# watsonx.aiのEmbeddingモデル取得
embed_params = {
    EmbedTextParamsMetaNames.TRUNCATE_INPUT_TOKENS: 512,
    EmbedTextParamsMetaNames.RETURN_OPTIONS: {"input_text": True},
}

embeddings = WatsonxEmbeddings(
    model_id="intfloat/multilingual-e5-large",
    url=watsonx_url,
    apikey=apikey,
    project_id=project_id
    )

8. ベクトルDB Milvusに接続

from langchain_milvus import Milvus

vector_store = Milvus(
    embeddings,
    connection_args =my_connection_args,
    collection_name = my_collection
)

ここからが、Part1とPart2とは異なる内容です。

9. watsonx.ai LLMの取得 および プロンプトテンプレートの作成

今回はLLMを3つ切り替えて試せるようにします。以下の3つのLLMを取得し、それに適したプロンプトテンプレートを作成します。またRAGなし(ベクトルDBの検索結果を使わずLLM自力回答)も切り替えて試せるようにRAGなし用のプロンプトテンプレートを作成しています。

  • mixtral-8x7b-instruct-v01
  • llama-3-2-3b-instruct
  • granite-3-8b-instruct

Embeddingモデルと同様に下記apikeyはMilvusと同じものを使用しています。異なる場合は変数apikeyを変更してください。

9-1. mixtral-8x7b-instruct-v01

custom_llm = {}
rag_prompt = {}
no_rag_prompt = {}

# 使用するLLMのパラメータ: mixtral-8x7b-instruct-v0
key = 'mixtral-8x7b-instruct-v01'
generate_params = {
    GenParams.MAX_NEW_TOKENS: 16384,
    GenParams.MIN_NEW_TOKENS: 0,
    GenParams.DECODING_METHOD: "greedy",
    GenParams.REPETITION_PENALTY: 1
}

# LangChainで使うllm: mixtral-8x7b-instruct-v0
custom_llm[key] = WatsonxLLM(
    model_id="mistralai/mixtral-8x7b-instruct-v01",
    url=watsonx_url,
    apikey=apikey,
    project_id=project_id,
    params=generate_params,
)

# LangChainで使うPrompt(RAGあり): mixtral-8x7b-instruct-v0
template = """<s> [INST] 
あなたは親切で、礼儀正しく、誠実なアシスタントです。常に安全を保ちながら、できるだけユーザーの役に立つように詳しく回答してください。
回答には、有害、非倫理的、⼈種差別的、性差別的、有毒、危険、または違法なコンテンツを含めてはいけません。回答は社会的に偏⾒がなく、本質的に前向きなものであることを確認してください。
質問が意味をなさない場合、または事実に⼀貫性がない場合は、正しくないことに答えるのではなく、その理由を説明してください。質問の答えがわからない場合は、誤った情報を共有しないでください。
questionに答えるために、以下のcontextを使用し必ず日本語でanswerを作成してください。
必ず⽇本語の文章で回答してください。知ったかぶりをしないでください。
回答を書くときは、context内の単語をできるだけ使⽤してください。context中に質問に対する回答がない場合は、「文書中に質問に対する回答が明記されていません。」とだけ回答してください。「文書中に質問に対する回答が明記されていません。」と回答した場合、そこで回答を終わりにしてください。
contextの内容がブランクの場合、「文書中に質問に対する回答が明記されていません。」とだけ回答してください。
セッションについて回答する場合は、タイトル、開始時刻、終了時刻、会場、概要、レベルを回答してください。回答が200文字以上の場合、回答はなるべく箇条書きを含めてわかりやすく回答してください。
[/INST]
</s>
<s> [INST] 質問が質問の文章ではなく意味がわからない場合[/INST]もう少し詳しく説明していただけますか?</s>

context: {context}
question: {question}
answer: 
"""

# Prompt Templateを作成します
rag_prompt[key] = PromptTemplate.from_template(template)


# LangChainで使うPrompt(RAGなし): mixtral-8x7b-instruct-v0
template = """<s> [INST] 
必ず⽇本語の文章で回答してください。知ったかぶりをしないでください。。
あなたは親切で、礼儀正しく、誠実なアシスタントです。常に安全を保ちながら、できるだけユーザーの役に立つように詳しく回答してください。
回答には、有害、非倫理的、⼈種差別的、性差別的、有毒、危険、または違法なコンテンツを含めてはいけません。回答は社会的に偏⾒がなく、本質的に前向きなものであることを確認してください。
質問が意味をなさない場合、または事実に⼀貫性がない場合は、正しくないことに答えるのではなく、その理由を説明してください。質問の答えがわからない場合は、誤った情報を共有しないでください。
[/INST]
</s>
<s> [INST] 質問が質問の文章ではなく意味がわからない場合[/INST]もう少し詳しく説明していただけますか?</s>
question: {question}
answer: 
"""

# Prompt Templateを作成します
no_rag_prompt[key] = PromptTemplate.from_template(template)

9.2 llama-3-2-3b-instruct

# 使用するLLMのパラメータ:llama-3-2-3b-instruct
key = 'llama-3-2-3b-instruct'
generate_params = {
    GenParams.MAX_NEW_TOKENS: 8192,
    GenParams.MIN_NEW_TOKENS: 0,
    GenParams.DECODING_METHOD: "greedy",
    GenParams.REPETITION_PENALTY: 1
}

# LangChainで使うllm: llama-3-2-3b-instruct
custom_llm[key] = WatsonxLLM(
    model_id="meta-llama/llama-3-2-3b-instruct",
    url=watsonx_url,
    apikey=apikey,
    project_id=project_id,
    params=generate_params,
)

# LangChainで使うPrompt(RAGあり): llama-3-2-3b-instruct
template = """
<|start_header_id|>user<|end_header_id|>
あなたは2024年にIBMが開発したAI言語モデル「Granite」です。あなたは親切で、礼儀正しく、誠実なアシスタントです。常に安全を保ちながら、できるだけユーザーの役に立つように詳しく回答してください。
回答には、有害、非倫理的、⼈種差別的、性差別的、有毒、危険、または違法なコンテンツを含めてはいけません。回答は社会的に偏⾒がなく、本質的に前向きなものであることを確認してください。
質問が意味をなさない場合、または事実に⼀貫性がない場合は、正しくないことに答えるのではなく、その理由を説明してください。質問の答えがわからない場合は、誤った情報を共有しないでください。
questionに答えるために、以下のcontextを使用し必ず日本語でanswerを作成してください
必ず⽇本語の文章で回答してください。知ったかぶりをしないでください。
回答を書くときは、context内の単語をできるだけ使⽤してください。context中に質問に対する回答がない場合は、「文書中に質問に対する回答が明記されていません。」とだけ回答してください。「文書中に質問に対する回答が明記されていません。」と回答した場合、そこで回答を終わりにしてください。
contextの内容がブランクの場合、「文書中に質問に対する回答が明記されていません。」とだけ回答してください。
セッションについて回答する場合は、タイトル、開始時刻、終了時刻、会場、概要、レベルを回答してください。回答が200文字以上の場合、回答はなるべく箇条書きを含めてわかりやすく回答してください。<|eot_id|><|start_header_id|>user<|end_header_id|>
question:{question}
context: {context}<|eot_id|><|start_header_id|>assistant<|end_header_id|>
"""

# Prompt Templateを作成します
rag_prompt[key] = PromptTemplate.from_template(template)

# LangChainで使うPrompt(RAGなし): llama-3-2-3b-instruct
template = """
<|start_header_id|>user<|end_header_id|>
あなたは親切で、礼儀正しく、誠実なアシスタントです。常に安全を保ちながら、できるだけユーザーの役に立つように詳しく回答してください。
回答には、有害、非倫理的、⼈種差別的、性差別的、有毒、危険、または違法なコンテンツを含めてはいけません。回答は社会的に偏⾒がなく、本質的に前向きなものであることを確認してください。
必ず⽇本語の文章で回答してください。知ったかぶりをしないでください。
質問が意味をなさない場合、または事実に⼀貫性がない場合は、正しくないことに答えるのではなく、その理由を説明してください。質問の答えがわからない場合は、誤った情報を共有しないでください。<|eot_id|><|start_header_id|>user<|end_header_id|>
question:{question}<|eot_id|><|start_header_id|>assistant<|end_header_id|>
"""

# Prompt Templateを作成します
no_rag_prompt[key] = PromptTemplate.from_template(template)

9.3 granite-3-8b-instruct

# 使用するLLMのパラメータ: granite-3-8b-instruct
key = 'granite-3-8b-instruct'
generate_params = {
    GenParams.MAX_NEW_TOKENS: 4096,
    GenParams.MIN_NEW_TOKENS: 0,
    GenParams.DECODING_METHOD: "greedy",
    GenParams.REPETITION_PENALTY: 1
}

# LangChainで使うllm: granite-3-8b-instruct
custom_llm[key] = WatsonxLLM(
    model_id="ibm/granite-3-8b-instruct",
    url=watsonx_url,
    apikey=apikey,
    project_id=project_id,
    params=generate_params,
)

# LangChainで使うPromptt(RAGあり): granite-3-8b-instruct
template = """<|start_of_role|>system<|end_of_role|>なたは2024年にIBMが開発したAI言語モデル「Granite」です。あなたは親切で、礼儀正しく、誠実なアシスタントです。常に安全を保ちながら、できるだけユーザーの役に立つように詳しく回答してください。
回答には、有害、非倫理的、⼈種差別的、性差別的、有毒、危険、または違法なコンテンツを含めてはいけません。回答は社会的に偏⾒がなく、本質的に前向きなものであることを確認してください。
質問が意味をなさない場合、または事実に⼀貫性がない場合は、正しくないことに答えるのではなく、その理由を説明してください。質問の答えがわからない場合は、誤った情報を共有しないでください。
questionに答えるために、以下のcontextを使用し必ず日本語でanswerを作成してください
必ず⽇本語の文章で回答してください。知ったかぶりをしないでください。
回答を書くときは、context内の単語をできるだけ使⽤してください。context中に質問に対する回答がない場合は、「文書中に質問に対する回答が明記されていません。」とだけ回答してください。「文書中に質問に対する回答が明記されていません。」と回答した場合、そこで回答を終わりにしてください。
contextの内容がブランクの場合、「文書中に質問に対する回答が明記されていません。」とだけ回答してください。
セッションについて回答する場合は、タイトル、開始時刻、終了時刻、会場、概要、レベルを回答してください。回答が200文字以上の場合、回答はなるべく箇条書きを含めてわかりやすく回答してください。
context: {context}
<|end_of_text|>
<|start_of_role|>user<|end_of_role|>question:{question}<|end_of_text|>
<|start_of_role|>assistant<|end_of_role|>
"""


# Prompt Templateを作成します
rag_prompt[key] = PromptTemplate.from_template(template)

# LangChainで使うPromptt(RAGなし): granite-3-8b-instruct
template = """<|start_of_role|>system<|end_of_role|>あなたは2024年にIBMが開発したAI言語モデル「Granite」です。
あなたは親切で、礼儀正しく、誠実なアシスタントです。常に安全を保ちながら、できるだけユーザーの役に立つように詳しく回答してください。
回答には、有害、非倫理的、⼈種差別的、性差別的、有毒、危険、または違法なコンテンツを含めてはいけません。回答は社会的に偏⾒がなく、本質的に前向きなものであることを確認してください。
必ず⽇本語の文章で回答してください。知ったかぶりをしないでください。
質問が意味をなさない場合、または事実に⼀貫性がない場合は、正しくないことに答えるのではなく、その理由を説明してください。質問の答えがわからない場合は、誤った情報を共有しないでください。
回答が200文字以上の場合、回答はなるべく箇条書きを含めてわかりやすく回答してください。
<|end_of_text|>
<|start_of_role|>user<|end_of_role|>question:{question}<|end_of_text|>
<|start_of_role|>assistant<|end_of_role|>
"""

# Prompt Templateを作成します
no_rag_prompt[key] = PromptTemplate.from_template(template)

10. Retrieverの作成

ここでは「 Part3: ベクトル・データベース Milvusとwatsonx.ai LLMでRAGを構成して、質問をしてみよう!」の 「閾値以上の類似スコアを持つ文書のみを返す」CustomRetrieverを使います.

類似スコアの閾値と検索件数をGUIで指定できるようにします。

10-1. 閾値以上の類似スコアを持つ文書のみを返す」CustomRetrieverを作成

# 閾値以上の類似スコアを持つ文書のみを返す」CustomRetrieverを作成
# asyncの方は省略
from langchain.schema.vectorstore import VectorStoreRetriever
from langchain.schema.document import Document
from langchain.callbacks.manager import (
    CallbackManagerForRetrieverRun,
)
from typing import List

class CustomRetriever(VectorStoreRetriever):
    def _get_relevant_documents(
        self, query: str, *, run_manager: CallbackManagerForRetrieverRun
    ) -> List[Document]:
        top_k = self.search_kwargs.get("k", 4)
        docs_and_similarities = self.vectorstore.similarity_search_with_score(query, k=top_k)
                
        threshold = self.search_kwargs.get("score_threshold", 0)
       
        return [doc for doc, score in docs_and_similarities if score >= threshold and score <= 1]

10-2. 類似スコアの閾値と検索件数を指定して、Retrieverを作成する関数を作成

「12. gradioでGUI作成」で使います

# 類似スコアの閾値と検索件数を指定して、Retrieverを作成する関数
def get_Retriever(vectorstore, search_score_flag, score, k):
    if search_score_flag == "On": # 類似スコアの閾値を指定する場合
        return CustomRetriever(vectorstore = vectorstore, search_kwargs={"score_threshold": score, "k":k})
    else: # 類似スコアの閾値を指定しない場合
        return vectorstore.as_retriever(search_kwargs={"k": k})   

11. 検証用「類似検索のみ実施」で使用する類似度スコアと類似検索を返す関数の作成

  • 「12. gradioでGUI作成」で使います。
  • Retrieverでは類似度スコアは返さないので、GUIで検証用として「類似検索のみ実施」で使用する類似度スコアと類似検索を返す関数を別途作成します。
def get_similarity_search_with_score(vectorstore, search_score_flag, query, **kwargs):
    top_k = kwargs.get("k", 4)
    docs_and_similarities = vectorstore.similarity_search_with_score(query, k=top_k)
    if search_score_flag == "On": # 類似スコアの閾値を指定する場合
        threshold = kwargs.get("score_threshold", 0)
        return [(score, doc) for doc, score in docs_and_similarities if score >= threshold and score <= 1]
    else: # 類似スコアの閾値を指定しない場合
        return [(score, doc) for doc, score in docs_and_similarities]        

12. テストしてみます (オプション・省略可能)

  • 作成したRetrieverの類似検索テストしてみます
  • LLMを指定してChainを作成し、質問してみます
  • 「類似検索のみ実施」してみます

12-1. 作成したRetrieverの類似検索テスト¶

]
以下に設定したretrieverをget_Retrieverを使用して取得:

  • RAG=使用する(ON)
  • 類似スコアの閾値=0.83
  • 最大取得件数=10
# 作成したRetrieverの類似検索テスト
query="RAGに関するセッションの詳細を教えてください"

retriever = get_Retriever(vector_store, "On", 0.83, 10)
docs = retriever.invoke(query)
# print(docs)
for doc in docs:
    print({"content": doc.page_content, "metadata": doc.metadata} )
    print("---------")       

出力:
image.png

12-2. Chainの作成とLLMを使用した回答作成

以下に設定したretrieverをget_Retrieverを使用して取得:

  • RAG=使用する(ON)
  • 類似スコアの閾値=0.83
  • 最大取得件数=10

その後LLMmixtral-8x7b-instruct-v01を指定してChainを作成しています。

# Chainの作成
# 1つKeyを指定してください

# key = 'llama-3-2-3b-instruct'
key = 'mixtral-8x7b-instruct-v01'
# key = 'granite-3-8b-instruct'
retriever = get_Retriever(vector_store, "On", 0.83, 10)
rag_chain = (
    {"context": retriever, "question": RunnablePassthrough()}
    | rag_prompt[key]
    | custom_llm[key]
)

上記Chainのテストです

# 質問してみます

query="RAGに関するセッションの詳細を教えてください"
# query="watsonx.dataに関するセッションの詳細を教えてください"
# query="TechXchangeについて概要を教えてください"
# query="量子コンピューター関連のセッションを教えてください"
print(rag_chain.invoke(query))

出力:
image.png

12-3. 類似検索のみ実施

類似検索のみ実施するget_similarity_search_with_scoreのテストです。
類似スコアの閾値を指定する場合としない場合を、On/Offで切り替えできます。
Onの場合は類似スコアの閾値をscore_thresholdで設定します。
kは最大取得件数です。

query="RAGに関するセッションの詳細を教えてください"

# 類似スコアの閾値を指定する場合
docs = get_similarity_search_with_score(vector_store, "On", query, score_threshold=0.85, k=3)
for score, doc in docs:
    print({"score": score, "content": doc.page_content, "metadata": doc.metadata} )
    print("---------")

print("===============================================================")
# 類似スコアの閾値を指定しない場合
docs = get_similarity_search_with_score(vector_store, "Off", query, k=3)
for score, doc in docs:
    print({"score": score, "content": doc.page_content, "metadata": doc.metadata} )
    print("---------")

閾値指定する場合としない場合の2パターンテストしています。
出力:
image.png

13. gradioでGUI作成

gradioを使ってGUIを作り、いろいろな条件でRAG構成を試してみましょう!

尚、会話履歴を読むようにしていないので、毎回フルセンテンスで質問するようにお願いします

プロンプトを変更して試してもOKです。

public URLがインターネット上のURLになります。このLinkは72時間保持されます.

share_flag :

  • Watson Studio使用の場合はTrue
  • Trueにするとインターネットに公開されるので、機密情報がある場合はTrueにしないようにしてください。
  • PC上のnotebookで実行する場合は、FalseにしてLocalPCでの実行が可能です。

下のセルを実行するとnotebookの場合はGUIが表示されます。ただし、表示されるGUIは使いにくので、localで実行している場合はlocal URLの値、watsonx.ai Studio使用の場合はpublic URLの値をクリックして、Webブラウザーで開いて操作してみましょう。

エラーメッセージ等はnotebookの場合はnotebookに出ますので、エラーが発生した場合はnotebookのセル出力のGUIの下の方を参照してください。

import gradio as gr

# Watson Studio仕様の場合はTrue
# インターネットに公開されるので、機密情報がある場合はTrueにしないようにしてください。
# PC上のnotebookで実行する場合は、FalseにしてLocalPCでの実行が可能です。

share_flag = True
# share_flag = False

def chat_streaming(message, history, RAG_flag, model_name, search_k, search_score_flag, search_score ):
    gr_retriever = get_Retriever(vector_store, search_score_flag, search_score, search_k)
    gr_rag_chain = None
        
    if RAG_flag == "Off":
        gr_rag_chain = (
            {"question": RunnablePassthrough()}
            | no_rag_prompt[model_name]
            | custom_llm[model_name] 
        )
    else:
        gr_rag_chain = (
            {"context": gr_retriever, "question": RunnablePassthrough()}
            | rag_prompt[model_name]
            | custom_llm[model_name] 
        )
    
    if message is not None:
        if len(message.strip()) < 1:
            yield "質問を入力してください"
            return
               
        partial_message = f"**{model_name}:**\n"
        for response in gr_rag_chain.stream(message):
            partial_message += response    
            yield partial_message 
            

def change_search_score(flag):
    if flag == "On":
        interactive_flag = True
    else:
        interactive_flag = False
    return gr.update(interactive = interactive_flag, visible= interactive_flag)    
    

def get_search_result(msg, search_k, search_score_flag, search_score):
    docs = get_similarity_search_with_score(vector_store, search_score_flag, msg, score_threshold=search_score, k=search_k)
    result_str =""
    if len(docs) < 1:
        return gr.update(value="検索結果がありません")    
    for i, (score, doc) in enumerate(docs):
        # result_str += str(i+1)+': {"content": ' + json.dumps(doc.page_content, ensure_ascii=False)+ ', "metadata": ' + json.dumps(doc.metadata, ensure_ascii=False)+'} \n'
        result_str += f"{i+1}: {{" \
                  f"  \"score\": {score},\n" \
                  f"  \"content\": {json.dumps(doc.page_content, ensure_ascii=False)},\n" \
                  f"  \"metadata\": {json.dumps(doc.metadata, ensure_ascii=False)}\n" \
                  f"}}\n---------\n"
    return gr.update(value=result_str)    
    
    
CSS = """
#row1 {flex-grow: 1; align-items: unset;}
    .form {height: fit-content;}
""" 


with gr.Blocks(fill_height=True,  css=CSS) as demo:
    gr.Markdown("## TechXchange Japan Q&A")

    with gr.Row(equal_height=False, elem_id="row1"):
        with gr.Column(variant="panel", scale=1):
            with gr.Accordion("設定"):
                RAG_flag = gr.Radio(choices=["On", "Off"], type="value", label="RAG構成", value="On", interactive = True)
                model_name = gr.Dropdown(["mixtral-8x7b-instruct-v01", "granite-3-8b-instruct", "llama-3-2-3b-instruct"], label="LLM Model", value="mixtral-8x7b-instruct-v01")
                search_k = gr.Number(label="類似検索の結果取得数(k) 最大15",minimum=1, maximum=15, value=5, interactive = True )
                search_score_flag  = gr.Radio(["On", "Off"], label="類似度スコアの閾値を設定", value="On", interactive = True)
                search_score = gr.Slider(minimum=0.7, maximum=0.9, step=0.01, value=0.8, label="類似度スコアの閾値" ,interactive = True, visible=True)
            gr.Markdown("- 類似度スコアの閾値を大きくすると類似検索結果件数が0件となり、「文書中に質問に対する回答が明記されていません。」という回答やLLMによっては回答を独自に作ってしまう場合があります 。その場合は類似度スコアの閾値下げて調整してください。")
            gr.Markdown("- Graniteは最大トークン数が少ないため, 取得数を上げたり類似度スコアの閾値を下げるとエラーになることがあります。その場合は取得数を少なくしたり類似度スコアの閾値上げて調整してください。")
            
        with gr.Column(scale=2):
            chatbot = gr.ChatInterface(fn=chat_streaming, type="messages", 
                            additional_inputs =[RAG_flag, model_name, search_k, search_score_flag, search_score], 
                            title="TechXchange Bot", fill_height=True)
            with gr.Accordion("類似検索のみ実施", open=False):
                msg = gr.Textbox(placeholder="類似検索したい文字列を入力してEnter",submit_btn=True, container=False)
                search_result = gr.Text(label="検索結果",container=True, interactive=False, show_label=True)
                msg.submit(fn=get_search_result, inputs=[msg,search_k, search_score_flag, search_score], outputs=search_result)             
                
                                    
    search_score_flag.change(fn=change_search_score, inputs=search_score_flag, outputs=search_score)
  

demo.launch(share=share_flag)

watsonx.ai Studio使用の場合はpublic URLの値をクリックします
image.png

以下のようなGUI画面がWebブラウザーに表示されます:
image.png

LLMや類似度スコアを変更したりしていろいろ試してみてください。RAGではない直接LLMに質問する形式も設定できます。
回答がおかしい場合は、そもそも設定した条件でどのような類似検索結果が返ってきているかを「類似検索の実施」で確認してみてください。

類似検索結果が期待と違う場合は、LLMのプロンプトの変更、ベクトル化する元の文章の内容の再考(切り方(chunking)やJSON化したことなど)、Embeddingモデルの変更、検索方法を変えてみるなど様々なRAGの検索精度向上のためのチューニングや調整で対応が必要となってきます(ここでは扱いません)。

以上で「Part4: ベクトルDB Milvusとwatsonx.ai LLMでRAGを構成して、チャットアプリを作成してみよう!」は完了です。

watsonx.data Milvus + watsonx.ai RAGハンズオンも全て完了となります。
おつかれさまでした👏

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?