1. レトリーバー

Retriever(検索器)はLangChainによってカプセル化されたインターフェースで、構造化されていないクエリに基づいて関連する文書を返すことができます。レトリーバーの設計目的は、ローカルデータの問い合わせを容易にすることです。ベクトルストレージは、レトリーバーの基礎実装として使用でき、LangChainはレトリーバーインターフェースの複数の基礎実装をサポートしています。

2. レトリーバーのはじめ方

2.1. インストール

レトリーバーを取得する方法を示すために、Qdrantベクトルデータベースを例に使用します。

%pip install --upgrade --quiet qdrant-client

2.2. OpenAI APIキーの取得

OpenAIEmbeddingsを使用する前に、OpenAI APIキーを取得する必要があります。

import getpass
import os

os.environ["OPENAI_API_KEY"] = getpass.getpass("OpenAI API Key:")

2.3. 文書データのインポートとQdrantクライアントの取得

次のコードは、文書データのインポートとQdrantクライアントの取得を示しています。

from langchain_community.document_loaders import TextLoader
from langchain_community.vectorstores import Qdrant
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import CharacterTextSplitter

loader = TextLoader("../../modules/state_of_the_union.txt")
documents = loader.load()
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
docs = text_splitter.split_documents(documents)

embeddings = OpenAIEmbeddings()

qdrant = Qdrant.from_documents(
    docs,
    embeddings,
    path="/tmp/local_qdrant",
    collection_name="my_documents",
)

2.4. レトリーバーの取得

次のコードは、Qdrantからレトリーバーを取得する方法を示しています。

retriever = qdrant.as_retriever()
retriever

次のように質問に関連する文書をクエリできます。

docs = retriever.get_relevant_documents("ケタンジ・ブラウン・ジャクソンについて彼は何と言ったか")

また、次のようにレトリーバーの類似度の閾値を設定することもできます。

retriever = db.as_retriever(
    search_type="similarity_score_threshold", search_kwargs={"score_threshold": 0.5}
)

さらに、レトリーバーを最も類似した上位K件のレコードを返すように設定することもできます。たとえば、最も類似した上位2件のレコードを返すように設定する場合は以下のようにします。

retriever = db.as_retriever(search_kwargs={"k": 2})

2.5. LCELでのレトリーバーの使用

レトリーバーはRunnableオブジェクトであるため、他のRunnableオブジェクトと簡単に組み合わせてワークフローを組み立てることができます。

from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough

template = """次のコンテキストのみに基づいて質問に答えてください:

{context}

質問: {question}
"""
prompt = ChatPromptTemplate.from_template(template)
model = ChatOpenAI()

def format_docs(docs):
    return "\n\n".join([d.page_content for d in docs])

chain = (
    {"context": retriever | format_docs, "question": RunnablePassthrough()}
    | prompt
    | model
    | StrOutputParser()
)

chain.invoke("大統領は技術について何と言いましたか?")

プロセスの説明:

  • ステップ1: 辞書を生成し、contextquestionの2つのプロパティを含めます。contextパラメータは、invokeメソッドに渡されたパラメータに基づいてretrieverによって生成され、パラメータが "大統領は技術について何と言いましたか?" です。これにより、類似した質問に関連するドキュメントが取得され、format_docs関数を使用してドキュメント配列をフォーマットし、contextプロパティに割り当てられます。RunnablePassthrough関数は、チェーンのパラメータ(ユーザーの質問)をquestionプロパティにコピーします。
  • ステップ2: 最初のステップで生成された辞書をプロンプトテンプレートに渡してフォーマットします。
  • ステップ3: プロンプトテンプレートから形式化されたプロンプトをモデルに渡します。
  • ステップ4: モデル呼び出しの結果を出力パーサーStrOutputParserに渡します。

3. カスタムレトリーバー

3.1. レトリーバーインターフェースの紹介

レトリーバーインタフェースは非常にシンプルで、独自のレトリーバーを簡単に作成することができます。

4.2. カスタムリトリーバーの例

以下は、カスタムリトリーバーの例であり、カスタムリトリーバーの作成方法とそれを使用して関連文書を取得する方法を実証しています:

from langchain_core.retrievers import BaseRetriever
from langchain_core.callbacks import CallbackManagerForRetrieverRun
from langchain_core.documents import Document
from typing import List

class CustomRetriever(BaseRetriever):
  
    def _get_relevant_documents(
        self, query: str, *, run_manager: CallbackManagerForRetrieverRun
    ) -> List[Document]:
        return [Document(page_content=query]

retriever = CustomRetriever()

retriever.get_relevant_documents("bar")

上記のセクションを学ぶことで、概念、リトリーバーのカスタマイズ、およびリトリーバーの検索方法についてより深く理解することができます。