LlamaIndex v0.6.0 - データに対する新しいクエリインターフェイス
以下の記事が面白かったので、軽く要約しました。
1. はじめに
「LlamaIndex 0.6.0」では、 開発者がクエリロジックをカスタマイズし、独自コンポーネントを定義しやすくなるように、「LlamaIndex」にいくつかの大きな変更を加えました。
・状態と計算の分離 : 状態 (データ + インデックス) を計算 (検索エンジン、クエリエンジン) と明確に分離しました。
・複雑さの段階的な開示 : 新規開発者向けの低レベルAPIを導入し、コンポーザビリティを重視し、インターフェースをより明確にすることで、カスタムビルディングブロックをより簡単に実装できるようにしました。
・ストレージの抽象化 : より柔軟で (データとインデックスの両方を保存できる)、拡張性のある (インメモリストレージを超えた一般化) ストレージ抽象化に書き直しました。
2. 状態と計算の分離
2-1. 状態と計算の分離
「LlamaIndex」は、3つの主要な抽象化機能を中心に再設計することで、状態と計算をスマートに分離しました。

・Index : 状態を管理。ストレージを抽象化し、処理されたデータと関連するメタデータのビューを公開。
・Retriever : クエリを指定してインデックスから最も関連性の高いノードを取得。
・QueryEngine : クエリと取得したノードをもとに応答を生成。
2-2. 高レベルAPI
LlamaIndexの使用は以前と同じくらい簡単で、構文を少し変更するだけです。
◎ 旧構文
index.query("ポール・グラハムとは?")◎ 新構文
query_engine = index.as_query_engine()
query_engine.query("ポール・グラハムとは?")3. 複雑さの段階的な開示
3-1. 複雑さの段階的な開示
初心者がすぐに使用できる「高レベルAPI」と、経験豊富なユーザーがカスタマイズできる「低レベルAPI」の両方を提供します。

◎ クイックセットアップ
既存のクエリエンジンを使用します。
index.as_query_engine()◎ 検索モードと合成モードの設定
パラメータで検索モードと合成モードを設定します。
index.as_query_engine(
retriever_mode=...,
response_mode=...
)◎ RetrieverとQuery Enginesの作成と設定
低レベルのcomposition APIでクエリロジックを詳細に制御します。
RetrieverQueryEngine(
VectorIndexRetriever(index, ...),
ResponseSynthesizer(...)
)◎ RetrieverとQuery Enginesのカスタム実装
既存のコンポーネントをカスタマイズしたり、独自のコンポーネントを実装します。
class CustomQueryEngine:
...3-2. 低レベルAPI
既存のコンポーネントをカスタマイズしたり、独自のコンポーネントを定義しするために、低レベルAPIを提供しています。

コアコンポーネントは、次のとおりです。
・Index : ノードの状態を保持。データの集合体を、検索や応答の合成を容易にする有用なメタデータとともに表示するものとして理解。
・Retriever : Indexから関連するノードを取得するためのロジックを保持。特定のIndexに対して定義されることが多い。
・Response synthesizer : 検索されたノードから最終的な応答を生成するための計算を管理。
・Query engine : すべてを結び付け、クリーンなクエリインターフェイスを公開。
柔軟性を示す例を次に示します。
index = GPTSimpleVectorIndex.from_documents(documents)
# Retrieverの設定
retriever = VectorIndexRetriever(
similarity_top_k=3,
vector_store_query_mode=VectorStoreQueryMode.HYBRID,
alpha=0.5,
)
# Response Synthesizerの設定
synth = ResponseSynthesizer.from_args(
response_mode='tree_summarize',
node_postprocessors=[
KeywordNodePostprocessor(required_keywords=['llama']),
],
optimizer=SentenceEmbeddingOptimizer(threshold_cutoff=0.5),
)
# Query Engineの構築
query_engine = RetrieverQueryEngine(
retriever=retriever,
response_synthesizer=synth,
)
query_engine.query("Who is Paul Graham?")4. ストレージの抽象化
4-1. ストレージの抽象化
ストレージの抽象化を書き直して、様々な種類のデータをより柔軟に取り込みやすくしました。
次図は、新しいストレージアーキテクチャを示しています。

4-2. Key Value Store
ベースレイヤーでは、あらゆるデータを保存するためにストレージの抽象化が必要です。「Key-Value Storage」 (インメモリ、ファイルシステム、MongoDB、オブジェクトストレージなど) をサポートするシステムの周りのレイヤーでは、新しい「Key Value Store」を定義します。ここから、「Document Store」と「Index Store」も定義します。
・Document Store : 取り込まれたドキュメント (Node オブジェクト) が格納される場所
・Index Store : インデックスメタデータが格納される場所
4-3. Vector Store
多くの「Vector Store」 (Pinecone、Weaviate、Chroma など) は、生データのストレージ機能を提供します 。 それらはすべて暗黙的に「Index」を提供します。埋め込みをIndex化することにより、類似性検索を通じてクエリインターフェイスを提供します。
4-4. Indexはデータに対する軽量なビューに過ぎない
この変更で強調された原則は、Indexはデータに対する軽量なビューに過ぎないということです。つまり、既存のデータに対して新しいIndexを定義しても、データが重複することはなく、新しいIndexはデータに対してメタデータを定義するのと同じようなものとして扱われます。
・Vector Index を定義すると埋め込みでデータをIndex化。
・Keywoard Index を定義するとキーワードでデータをIndex化。
既存のデータに対してメタデータやインデックスを定義することは、LLM がデータに対してさまざまな検索・合成の機能を実行できるようにするための重要な要素になります。
