LlamaIndex v0.8 クイックスタートガイド - Python版
Python版の「LlamaIndex」のクイックスタートガイドをまとめました。
【最新版の情報は以下で紹介】
1. LlamaIndex
「LlamaIndex」は、プライベートやドメイン固有の知識を必要とする専門知識を必要とする質問応答チャットボットを簡単に作成できるライブラリです。
2. LlamaIndexの5つのステージ
「LlamaIndex」には、5つのステージがあります。
2-1. Loading
データソース (テキストファイル、PDF、Webサイト、データベース、APIなど) からデータを読み込みます。
「Loading」の主要コンポーネントは、次のとおりです。
2-2. Indexing
データのクエリを可能にするデータ構造に変換します。
「Indexing」の主要コンポーネントは、次のとおりです。
2-3. Storing
インデックスが作成した後、インデックスと他のメタデータを保存することで、再作成する必要がなくなります。
2-4. Querying
インデックスから関連データをクエリします。
「Querying」の主要コンポーネントは、次のとおりです
2-3. Evaluation
クエリに対するレスポンスがどの程度正確、忠実、迅速であるかを客観的に測定します。
3. ドキュメントの準備
今回は、マンガペディアの「ぼっち・ざ・ろっく!」のドキュメントを用意しました。
・bocchi.txt
4. 質問応答
Google Colabでの質問応答の実行手順は、次のとおりです。
(1) パッケージのインストール。
# パッケージのインストール
!pip install llama-index==0.8.59
(2) 環境変数の準備。
以下のコードの <OpenAI_APIのトークン> にはOpenAI APIのトークンを指定します。(有料)
import os
os.environ["OPENAI_API_KEY"] = "<OpenAI_APIのトークン>"
(3) ログレベルの設定。
import logging
import sys
# ログレベルの設定
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG, force=True)
(4) Colabにdataフォルダを作成してドキュメントを配置。
左端のフォルダアイコンでファイル一覧を表示し、右クリック「新しいフォルダ」でdataフォルダを作成し、ドキュメントをドラッグ&ドロップします。
(5) ドキュメントの読み込み。
from llama_index import SimpleDirectoryReader
# ドキュメントの読み込み
documents = SimpleDirectoryReader("data").load_data()
(6) インデックスの作成。
「インデックス」は、ドキュメントの情報を保持するモジュールです。ドキュメントをチャンクに分割し、チャンク毎に埋め込みに変換して保持ます。チャンクは類似検索の対象となるデータ単位になります。
from llama_index import VectorStoreIndex
# インデックスの作成
index = VectorStoreIndex.from_documents(documents)
DEBUG:llama_index.node_parser.node_utils:> Adding chunk: 結束バンド 後藤ひとりは友達を作れない陰キャでいつも一人で過ごしていたが、中学時代にテレビのイ...
DEBUG:llama_index.node_parser.node_utils:> Adding chunk: 文化祭ライブ 夏休みに入り、後藤ひとりは知り合いも増えていたが、自分から遊びに誘うことができず...
DEBUG:llama_index.node_parser.node_utils:> Adding chunk: ひねくれ者なヨヨコは、結束バンドをライバル視しながらも、彼女たちにアドバイスを送り、ファンたち...
:
(7) クエリエンジンの作成。
「クエリエンジン」は、ユーザー入力(クエリ)と関連する情報をインデックスから取得し、それをもとに応答を生成するモジュールです。
# クエリエンジンの作成
query_engine = index.as_query_engine()
(8) 質問応答。
# 質問応答
print(query_engine.query("ぼっちちゃんの得意な楽器は?"))
後藤ひとりの得意な楽器はギターです。
ログを確認すると、以下のコンテキストを取得していることがわかります。
[Similarity score: 0.832151] 自分には何の取り柄もないのを痛感していたため、中学の頃に暗い性格の人間がバンドをやって人気者になったインタビューを読んで、ギターを始める。毎日練習したお陰でギターの腕前はプロ級になったが、結局、...
[Similarity score: 0.829139] 変わり者で一人でいるのが好きだが、後藤ひとりと違って特にコミュ障というわけではない。音楽に関しては独自の価値観を持っており、流行(はや)りに流されるのを嫌い、バンドの個性を重視している。そのため...
OpenAI APIの通信内容も確認できます。Unicodeエスケープシーケンスを日本語に戻してます。
Context information is below.
---------------------
自分には何の取り柄もないのを痛感していたため、中学の頃に暗い性格の人間がバンドをやって人気者になったインタビューを読んで、ギターを始める。毎日練習したお陰でギターの腕前はプロ級になったが、結局、...
変わり者で一人でいるのが好きだが、後藤ひとりと違って特にコミュ障というわけではない。音楽に関しては独自の価値観を持っており、流行(はや)りに流されるのを嫌い、バンドの個性を重視している。そのため...
---------------------
Given the context information and not prior knowledge, answer the question. If the answer is not in the context, inform the user that you can't answer the question.
Question: ぼっちちゃんの得意な楽器は?
Answer:
5. インデックスの保存
作成したインデックスは保存しておくと、次回に再作成する必要がなくなります。
(1) インデックスの保存。
# インデックスの保存
index.storage_context.persist()
(2) インデックスの読み込み。
from llama_index import StorageContext, load_index_from_storage
# インデックスの読み込み
storage_context = StorageContext.from_defaults(persist_dir="./storage")
index = load_index_from_storage(storage_context)
6. カスタマイズ
ユースケースに合わせて以下のコードをカスタマイズします。
from llama_index import VectorStoreIndex, SimpleDirectoryReader
# カスタマイズするコード
documents = SimpleDirectoryReader("data").load_data()
index = VectorStoreIndex.from_documents(documents)
query_engine = index.as_query_engine()
response = query_engine.query("ぼっちちゃんの得意な楽器は?")
print(response)
6-1. チャンクサイズの変更
今回は、チャンクサイズを500に設定します。
(1) ServiceContextでチャンクの最大サイズを指定。
from llama_index import ServiceContext
# ServiceContextの準備
service_context = ServiceContext.from_defaults(
chunk_size=500 # チャンクの最大サイズ
)
(2) VectorStoreIndexにServiceContextを設定。
from llama_index import VectorStoreIndex, SimpleDirectoryReader
# VectorStoreIndexにServiceContextを設定
documents = SimpleDirectoryReader("data").load_data()
index = VectorStoreIndex.from_documents(
documents,
service_context=service_context # ServiceContextの設定
)
query_engine = index.as_query_engine()
response = query_engine.query("ぼっちちゃんの得意な楽器は?")
print(response)
チャンク数は、以下のコードで確認できます。
print(len(index.index_struct.nodes_dict))
15から33に増えました。
6-2. クエリで取得するコンテキスト数の変更
今回は、5個のコンテキストを取得するようにします。デフォルトでは2個取得しています。
(1) index.as_query_engine()のsimilarity_top_kにコンテキスト数を指定。
from llama_index import VectorStoreIndex, SimpleDirectoryReader
# クエリする時により多くのコンテキストを取得
documents = SimpleDirectoryReader("data").load_data()
index = VectorStoreIndex.from_documents(documents)
query_engine = index.as_query_engine(similarity_top_k=5) # コンテキスト数
response = query_engine.query("ぼっちちゃんの得意な楽器は?")
print(response)
6-3. VectorStoreの変更
「LlamaIndex」で利用可能な「VectorStore」は、「Using Vector Stores」で確認できます。
今回は、「Chroma」を使用します。
(1) パッケージのインストール。
# パッケージのインストール
!pip install chromadb
(2) StorageContextの準備。
import chromadb
from llama_index.vector_stores import ChromaVectorStore
from llama_index import StorageContext
# StorageContextの準備
chroma_client = chromadb.PersistentClient()
chroma_collection = chroma_client.create_collection("quickstart")
vector_store = ChromaVectorStore(chroma_collection=chroma_collection)
storage_context = StorageContext.from_defaults(vector_store=vector_store)
(3) VectorStoreIndexにStorageContextを設定。
from llama_index import VectorStoreIndex, SimpleDirectoryReader
# VectorStoreIndexにStorageContextを設定
documents = SimpleDirectoryReader("data").load_data()
index = VectorStoreIndex.from_documents(
documents,
storage_context=storage_context # StorageContextの設定
)
query_engine = index.as_query_engine()
response = query_engine.query("ぼっちちゃんの得意な楽器は?")
print(response)
6-4. LLMの変更
「LlamaIndex」では、「LangChain」の「LLM」「ChatModel」も利用できます。
今回は、「gpt-4」を使用します。デフォルトは、「gpt-3.5-turbo」です。
(1) ServiceContextの準備。
from llama_index import ServiceContext
from langchain.chat_models import ChatOpenAI
# ServiceContextの準備
service_context = ServiceContext.from_defaults(
llm=ChatOpenAI(model_name="gpt-4")
)
(2) VectorStoreIndexにServiceContextを設定。
from llama_index import VectorStoreIndex, SimpleDirectoryReader
# 別のLLMの使用
documents = SimpleDirectoryReader("data").load_data()
index = VectorStoreIndex.from_documents(documents)
query_engine = index.as_query_engine(
service_context=service_context # ServiceContextの設定
)
response = query_engine.query("ぼっちちゃんの得意な楽器は?")
print(response)
6-5. レスポンスモードの変更
「LlamaIndex」の主なレスポンスモードは、次の3つです。
今回は、レスポンスモードを「refine」にします。デフォルトは「compact」です。
(1) index.as_query_engine()のresponse_modeにレスポンスモードを設定。
from llama_index import VectorStoreIndex, SimpleDirectoryReader
# レスポンスモードの変更
documents = SimpleDirectoryReader("data").load_data()
index = VectorStoreIndex.from_documents(documents)
query_engine = index.as_query_engine(response_mode="refine")
response = query_engine.query("ぼっちちゃんの得意な楽器は?")
print(response)
ぼっちちゃんの得意な楽器はギターです。彼はバンド経験があり、演奏技術も結束バンドの中でも群を抜いて高いです。彼は独自の音楽の価値観を持っており、バンドの個性を重視しています。
ログでOpenAI APIの通信内容も確認できます。
Context information is below.
---------------------
自分には何の取り柄もないのを痛感していたため、中学の頃に暗い性格の人間がバンドをやって人気者になったインタビューを読んで、ギターを始める。毎日練習したお陰でギターの腕前はプロ級になったが、結局、...
---------------------
Given the context information and not prior knowledge, answer the question. If the answer is not in the context, inform the user that you can't answer the question.
Question: ぼっちちゃんの得意な楽器は?
Answer:
You are an expert Q&A system that stricly operates in two modeswhen refining existing answers:
1. **Rewrite** an original answer using the new context.
2. **Repeat** the original answer if the new context isn't useful.
Never reference the original answer or context directly in your answer.
When in doubt, just repeat the original answer.New Context: 変わり者で一人でいるのが好きだが、後藤ひとりと違って特にコミュ障というわけではない。音楽に関しては独自の価値観を持っており、流行(はや)りに流されるのを嫌い、バンドの個性を重視している。そのため...
Query: ぼっちちゃんの得意な楽器は?
Original Answer: ぼっちちゃんの得意な楽器はギターです。
New Answer:
6-6. エンジンの変更
「LlamaIndex」には、次の3つのエンジンがあります。
今回は「Chat Engine」を使います。デフォルトは「Query Engine」です
(1) index.as_chat_engine()でチャットエンジンを取得してchat()で会話。
VectorStoreのコンテキストを利用して会話します。
from llama_index import VectorStoreIndex, SimpleDirectoryReader
# チャットで会話
documents = SimpleDirectoryReader("data").load_data()
index = VectorStoreIndex.from_documents(documents)
chat_engine = index.as_chat_engine()
response = chat_engine.chat("後藤ひとりの性格は?")
print(response)
(2) 会話履歴が必要な内容で会話。
チャットエンジンは会話履歴も利用して会話します。
response = chat_engine.chat("その人物についてどう思いますか?")
print(response)
6-7. ストリーミングの有効化
レスポンスのストリーミングを有効化します。
(1) index.as_query_engine()のstreamingにTrueを設定。
from llama_index import VectorStoreIndex, SimpleDirectoryReader
# レスポンスをストリーミングに変更
documents = SimpleDirectoryReader("data").load_data()
index = VectorStoreIndex.from_documents(documents)
query_engine = index.as_query_engine(streaming=True)
response = query_engine.query("ぼっちちゃんの得意な楽器は?")
response.print_response_stream()
6-8. プロンプトテンプレートの変更
今回は、日本語のプロンプトテンプレートに切り替えます。
(1) QAプロンプトテンプレートとRefineプロンプトテンプレートの準備。
from llama_index.llms import ChatMessage, MessageRole
from llama_index.prompts import ChatPromptTemplate
# Text QA Prompt
chat_text_qa_msgs = [
ChatMessage(
role=MessageRole.SYSTEM,
content="""あなたは世界中で信頼されているQAシステムです。
事前知識ではなく、常に提供されたコンテキスト情報を使用してクエリに回答してください。
従うべきいくつかのルール:
1. 回答内で指定されたコンテキストを直接参照しないでください。
2. 「コンテキストに基づいて、...」や「コンテキスト情報は...」、またはそれに類するような記述は避けてください。""",
),
ChatMessage(
role=MessageRole.USER,
content="""コンテキスト情報は以下のとおりです。
---------------------
{context_str}
---------------------
事前知識ではなくコンテキスト情報を考慮して、クエリに答えます。
Query: {query_str}
Answer: """
),
]
text_qa_template = ChatPromptTemplate(chat_text_qa_msgs)
# Refine Prompt
chat_refine_msgs = [
ChatMessage(
role=MessageRole.SYSTEM,
content="""あなたは世界中で信頼されているQAシステムです。
事前知識ではなく、常に提供されたコンテキスト情報を使用してクエリに回答してください。
従うべきいくつかのルール:
1. 回答内で指定されたコンテキストを直接参照しないでください。
2. 「コンテキストに基づいて、...」や「コンテキスト情報は...」、またはそれに類するような記述は避けてください。""",
),
ChatMessage(
role=MessageRole.USER,
content="""あなたは、既存の回答を改良する際に2つのモードで厳密に動作するQAシステムのエキスパートです。
1. 新しいコンテキストを使用して元の回答を**書き直す**。
2. 新しいコンテキストが役に立たない場合は、元の回答を**繰り返す**。
回答内で元の回答やコンテキストを直接参照しないでください。
疑問がある場合は、元の答えを繰り返してください。
New Context: {context_msg}
Query: {query_str}
Original Answer: {existing_answer}
New Answer: """
),
]
refine_template = ChatPromptTemplate(chat_refine_msgs)
(2) index.as_query_engine()のtext_qa_templateにQAプロンプトテンプレート、refine_templateにRefineプロンプトテンプレートを設定。
from llama_index import VectorStoreIndex, SimpleDirectoryReader
# レスポンスをストリーミングに変更
documents = SimpleDirectoryReader("data").load_data()
index = VectorStoreIndex.from_documents(documents)
query_engine = index.as_query_engine(
text_qa_template=text_qa_template, # QAプロンプトテンプレートの設定
refine_template=refine_template # Refineプロンプトテンプレートの設定
)
response = query_engine.query("ぼっちちゃんの得意な楽器は?")
response.print_response_stream()
ログでプロンプトテンプレートが変更されていることを確認できます。
コンテキスト情報は以下のとおりです。
---------------------
自分には何の取り柄もないのを痛感していたため、中学の頃に暗い性格の人間がバンドをやって人気者になったインタビューを読んで、ギターを始める。毎日練習したお陰でギターの腕前はプロ級になったが、結局、...
変わり者で一人でいるのが好きだが、後藤ひとりと違って特にコミュ障というわけではない。音楽に関しては独自の価値観を持っており、流行(はや)りに流されるのを嫌い、バンドの個性を重視している。そのため...
---------------------
事前知識ではなくコンテキスト情報を考慮して、クエリに答えます。
Query: ぼっちちゃんの得意な楽器は?
Answer:
7. LlamaHub
「LlamaIndex」がドキュメントとして読み込めるのは、テキストだけではありません。「LlamaHub」で提供しているデータコネクタを利用することで、さまざまなファイル (PDF、ePub、Word、PowerPoint、Audioなど) やWebサービス (Notion、Slack、Wikipediaなど) をドキュメントのデータソースとして利用できます。
今回は、PDFについてLlamaIndexで質問してみます。
(1) llama_hubパッケージのインストール。
# llama_hubパッケージのインストール
!pip install llama_hub
(2) ターゲットとなるPDFの準備。
今回は、PDF「GPT-4V(ision) System Card」を準備しました。
# PDFの準備
!wget https://cdn.openai.com/papers/GPTV_System_Card.pdf
(3) 「LlamaHub」のサイトを開き、目的のデータコネクタを探す。
今回は、「file/pdf」をクリックします。
(4) 使い方 (Usage) の手順に沿って、サンプルコードをコピペし、PDFのファイル名を変更。
from pathlib import Path
from llama_index import download_loader
PDFReader = download_loader("PDFReader")
loader = PDFReader()
documents = loader.load_data(file=Path('./GPTV_System_Card.pdf'))
(5) インデックスとクエリエンジンの作成。
from llama_index import VectorStoreIndex
# インデックスの作成
index = VectorStoreIndex.from_documents(documents)
# クエリエンジンの作成
query_engine = index.as_query_engine()
(5) 質問応答。
# 質問応答
print(query_engine.query("GPT-4Vの特徴は?"))
関連
この記事が気に入ったらサポートをしてみませんか?