見出し画像

LlamaIndex 0.7.0 の新機能

以下の記事が面白かったので、軽くまとめました。

LlamaIndex 0.7.0: Better Enabling Bottoms-Up LLM Application Development

前回

1. LlamaIndex 0.7.0

「LlamaIndex 0.7.0」では、下位レベルでモジュール性 / カスタマイズ性を改善し、データに対するLLMアプリケーションのボトムアップ開発を可能にするというテーマを継続していますLLM、レスポンスシンセサイザー、ドキュメントとノードオブジェクトなど、主要な抽象化の使用をさらに制御できるようになりました。

・LLM抽象化のスタンドアロン化
元はLangChainのクラスを利用していました。

・応答合成モジュールのカスタマイズ
応答合成モジュールとプロンプトの定型文を分離して、カスタマイズしやすくました。

・メタデータ管理機能
ドキュメント / ノードオブジェクトにメタデータ管理機能が追加されました。

2. LLM抽象化のスタンドアロン化

2-1. LLM抽象化のスタンドアロン化

LLM抽象化のスタンドアロン化しました。OpenAI、HuggingFace、PaLMに対応しています。既存のLlamaIndexシステム (クエリエンジン、レトリバー) の一部として使用できます。

・コードベースのよりクリーンな抽象化
以前は、LLMPredictorクラスには、基礎となるLangChain LLMクラスで大量のリーク抽象化がありました。これは、LLMの抽象化を推論しにくくし、カスタマイズしにくくしました。

・ややクリーンなdev UX
以前は、デフォルトのLLMをカスタマイズしたい場合(例えば "text-davinci-003 "を使いたい場合)、適切なLangChainクラスをインポートし、それをLLMPredictorでラップし、ServiceContextに渡す必要がありました。LLMの抽象化をインポートして、ServiceContextに差し込むだけで利用できるようになりました。もちろん、LangChainのLLMを使うこともできます。

ボトムアップ開発に役立つ
より大きなシステムの一部として接続する前に、これらのLLMモジュールを独立させることは理にかなっています。

2-2. 使用例

LLM抽象化は、completeエンドポイントとchatエンドポイントの両方をサポートしています。主な違いは、completeは単純な文字列入力を取り込み、CompletionResponse(テキスト出力+追加フィールドを含む)を出力するように設計されていることです。chatはChatMessageを取り込み、ChatResponse(チャットメッセージ+追加フィールドを含む)を出力します。

単独でLLM抽象化を使用する手順は、次のとおりです。

from llama_index.llms import OpenAI

# completeエンドポイントの使用
resp = OpenAI().complete('Paul Graham is ')
print(resp)

# Rawオブジェクトの取得
resp_raw = resp.raw


# chatエンドポイントの使用
from llama_index.llms import ChatMessage, OpenAI
messages = [
    ChatMessage(role='system', content='You are a pirate with a colorful personality'),
    ChatMessage(role='user', content='What is your name')
]
resp = OpenAI().chat(messages)
print(resp)

# Rawオブジェクトの取得
resp_raw = resp.raw


# streamingエンドポイントの使用
from llama_index.llms import OpenAI
llm = OpenAI()
resp = llm.stream_complete('Paul Graham is ')
for delta in resp:
    print(delta, end='')

LlamaIndexシステムでLLM抽象化を使用する手順は、次のとおりです。

from llama_index.llms import OpenAI
from llama_index.service_context import ServiceContext
from llama_index import VectorStoreIndex

# LLM抽象化の利用
llm = OpenAI(model='gpt-3.5-turbo', temperature=0)
service_context = ServiceContext.from_defaults(llm=llm)
index = VectorStoreIndex.from_documents(docs, service_context=service_context)
response = index.as_query_engine().query("<question>")

2-3. リソース

OpenAI LLM
Using LLM in LLMPredictor
Changing LLM within Index/Query Engine
Defining a custom LLM Model

3. 応答合成モジュールのカスタマイズ

3-1. 応答合成モジュールのカスタマイズ

応答合成モジュールの役割は、コンテキストを入力として取り込み、LLMを使用して応答を生成することです。

基本的に、応答合成モジュールは、コンテキストリストの長さに関係なく、コンテキストに対して応答を生成する必要があります。これは本質的に、LLM 開発者 / AIエンジニア が作成する必要がある「定型文」です。

以前はこれを LlamaIndex の内部抽象化として (ResponseSynthesizer) 使用していましたが、外部向けのUXはユーザーにとって不親切でした。 応答を収集する実際の部分 (ResponseBuilder) はカスタマイズが難しく、ResponseSynthesizer 自体が不要なレイヤーを追加していました。

簡単にインポートできるスタンドアロンモジュールのセットが用意されました。 以前は、クエリ エンジンで response_mode を設定すると、これらが自動的にセットアップされていました。より直接的に利用できるようになり、ユーザーが利用しやすくなりました。

以下は、llama_index.response_synthesizer から利用できる、すべての新しい応答合成 モジュールのリストです。

・Refine : LLM をクエリし、各テキストチャンクを個別に送信。最初のLLM呼び出しの後、既存の回答もLLMに送信され、次のテキストチャンクを使用して更新と改良が行われる。
・Accumulate : 複数のテキストチャンクにわたって同じプロンプトを使用してLLMをクエリし、フォーマットされた応答のリストを返す。
・CompactAndAccumulate : Refineと同じだが、各LLM呼び出しにできるだけ多くのテキストを入れる。
・CompactAndAccumulate : Accumulateと同じだが、できるだけ多くのテキストを入れる。
・TreeSummarize : 提供されたテキストチャンクからボトムアップサマリーを作成し、ルートサマリーを返す。
・SimpleSummarize : すべてのテキストチャンクを結合して切り詰め、単一のLLM呼び出しで要約。

3-2. 使用例

クエリエンジンで応答シンセサイザーを直接設定することも、response_mode に関連する応答シンセサイザーをフェッチさせることもできます。さらに、これらのシンセサイザーを低レベルモジュールとして直接呼び出して使用することもできます。

from llama_index import ServiceContext
from llama_index.response_synthesizers import CompactAndRefine

# 応答合成モジュールの準備
response_synthesizer = CompactAndRefine(
    service_context=service_context.from_defaults()
)

# 応答の生成
response = response_synthesizer.get_response(
    "What skills does Bob have?",
    text_chunks=[" ..."]  # Bobのスキルに関するコンテキスト
)

3-3. リソース

Low-level API Usage Pattern
Custom Retrievers

4. メタデータ管理機能

4-1. メタデータ管理機能

LLM アプリケーションでデータに対して良好なパフォーマンスを実現したい場合は、ドキュメントに実際にクエリに関連するコンテキストが含まれていることを確認する必要があります。これを行う 1 つの方法は、ドキュメント レベルと、ドキュメントがテキスト チャンク (ノード) に解析された後の両方で、適切なメタデータを追加することです。

ドキュメント内でメタデータフィールドを定義したり、IDをカスタマイズしたり、LLMの使用法や埋め込みの使用法に合わせてメタデータのテキスト/形式をカスタマイズしたりすることができます。

4-2. メタデータフィールドの定義

document = Document(
    text='text', 
    metadata={
        'filename': '<doc_file_name>', 
        'category': '<category>'
    }
)

4-3. IDのカスタマイズ

各ドキュメントの ID は複数の方法で設定できます。

・コンストラクタ : document = Document(text="text", doc_id_="id")
・オブジェクト構築後 : document.doc_id = "id"
・SimpleDirectoryReader : SimpleDirectoryReader(filename_as_id=True).load_data()

4-4. LLMと埋め込みのメタデータのカスタマイズ

上記のように、有用な情報を含むメタデータを設定できます。デフォルトでは、すべてのメタデータが埋め込みモデルとLLMによって表示されます。 ただし、埋め込みにバイアスをかけるためのデータのみを含めたり、LLMの追加情報としてデータのみを含めたりしたい場合もあります。

新しいDocumentオブジェクトを使用すると、各メタデータフィールドの用途を設定できます。

document = Document(
    text='text', 
    metadata={
        'filename': '<doc_file_name>', 
        'category': '<category>'
    },
    excluded_llm_metadata_keys=['filename', 'category'],
    excluded_embed_metadata_keys=['filename']
)

4-5. メタデータ形式テンプレートのカスタマイズ

メタデータがテキストに挿入される場合、メタデータは非常に特殊な形式に従います。 この形式は複数のレベルで設定できます。

from llama_index.schema import MetadataMode

document = Document(
  text='text',
  metadata={"key": "val"},
  metadata_seperator="::",
    metadata_template="{key}=>{value}",
    text_template="Metadata: {metadata_str}\\n-----\\nContent: {content}"
)
# 使用可能なモードは、 ALL, NONE, LLM, EMBED
print(document.get_content(metadata_mode=MetadataMode.ALL))
# output:
# Metadata: key=>val
# -----
# text

詳しくは、このガイドを参照。

5. 重大な変更点

5-1. 応答合成 + ノード ポストプロセッサ

ResponseSynthesizerクラスは削除され、 get_response_synthesizerに置き換えられました。 これに加えて、ノードポストプロセッサはクエリエンジンによって直接処理されるようになり、古い SentenceEmbeddingOptimizer はノード ポストプロセッサ自体になるように切り替えられました。

・旧

from llama_index import (
    VectorStoreIndex,
    ResponseSynthesizer,
)
from llama_index.indices.postprocessor import SimilarityPostprocessor
from llama_index.optimizers import SentenceEmbeddingOptimizer
from llama_index.query_engine import RetrieverQueryEngine

documents = ...
# build index
index = VectorStoreIndex.from_documents(documents)
# configure retriever
retriever = index.as_retriever(
   similarity_top_k=3
)
# configure response synthesizer
response_synthesizer = ResponseSynthesizer.from_args(
   response_mode="tree_summarize",
    node_postprocessors=[
        SimilarityPostprocessor(similarity_cutoff=0.7),
        SentenceEmbeddingOptimizer(percentile_cutoff=0.5)
    ]
)
# assemble query engine
query_engine = RetrieverQueryEngine(
    retriever=retriever,
    response_synthesizer=response_synthesizer,
)

・新

from llama_index import (
    VectorStoreIndex,
    get_response_synthesizer,
)
from llama_index.indices.postprocessor import (
    SimilarityPostprocessor,
    SentenceEmbeddingOptimizer
)

documents = ...
# build index
index = VectorStoreIndex.from_documents(documents)
# configure response synthesizer
response_synthesizer = get_response_synthesizer(
   response_mode="tree_summarize",
)
# assemble query engine
query_engine = index.as_query_engine(
  similarity_top_k=3,
    response_synthesizer=response_synthesizer,
    node_postprocessors=[
        SimilarityPostprocessor(similarity_cutoff=0.7),
        SentenceEmbeddingOptimizer(percentile_cutoff=0.5)
    ]
)

5-2. LLM Predictor

新しい LLM 抽象化を導入する際に、LLM Predictorをクリーンアップし、いくつかの非推奨の機能を削除しました。

・ChatGPTLLMPredictor と HuggingFaceLLMPredictorを削除
・LLMPredictorコンストラクタを介したキャッシュ設定のサポートを削除
・llama_index.token_counter.token_counter モジュールを削除

現在、LLM Predictorは主に、以下を処理する LLM 抽象化の上にある軽量ラッパーです。

・LLMが期待する文字列またはチャットメッセージ入力形式へのプロンプトの変換
・コールバックマネージャーへのプロンプトと応答のログ記録

5-3. チャットエンジン

文字列のタプルの代わりに、chat_history の List[ChatMessage]] を取り込むように BaseChatEngine インターフェイスを更新しました。 これにより、データモデルがLLMの入出力と一致し、同じロールを持つ連続したメッセージをより柔軟に指定できるようになります。

・旧

engine = SimpleChatEngine.from_defaults(
	chat_history=[("human message", "assistant message")],
)
response = engine.chat("new human message")

・新

engine = SimpleChatEngine.from_defaults(
    service_context=mock_service_context,
    chat_history=[
        ChatMessage(role=MessageRole.USER, content="human message"),
        ChatMessage(role=MessageRole.ASSISTANT, content="assistant message"),
    ],
)
response = engine.chat("new human message")

また、chat_history 状態をプロパティとして公開し、chat および achat エンドポイントでの chat_history のオーバーライドをサポートしました。

5-4. プロンプトヘルパー

以前に非推奨となったいくつかの引数を削除しました。 (max_input_size、embedding_limit、max_chunk_overlap)



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