本記事では、Azure AI SearchとLangChainを使用して、Retrieval-Augmented Generation (RAG) システムを構築する方法を解説します。

RAGは生成AIの精度を向上させるための有力な技術であり、Azure AI Searchの強力な検索機能とLangChainの柔軟な処理フレームワークを組み合わせることで、効率的で高精度な情報検索と生成が可能になります。

Azure AI Searchとは

Azure AI Searchはクラウド型の検索サービスです。全文検索、ベクトル検索、ハイブリッド検索、セマンティック検索(ランク付け)を利用できます。

Azure AI Searchでは、インデックス内に複数のJSON形式のドキュメントを格納する形式でデータを保存します。インデックスでは、フィールド名、データ型(String, Int, Double等)、フィールド属性(取得可能,フィルター可能,ソート可能,キー等)を設定して作成できます。 

Azure AI Searchのデプロイ

1. Azureポータルにログインし、「Azure AI services」を検索し、「AI Search」の「作成」ボタンを押下。
2.「基本」タブで、「リソースグループ」と「サービス名」を入力。また、「価格レベル」を選択します。
3. 「スケール」タブで、レプリカ(インスタンス数)とパーティション(ストレージ)の設定を行います。例えば、ユーザー数が多い場合はレプリカを増やし、データ量が多い場合はパーティションを増やします。
4.「ネットワーク」タブで、エンドポイントの接続を、「公開」か「プライベート」を選択します。
5. 「確認および作成」タブで、「作成」を押下します。

LangChainとAzure AI SearchでのRAGの手順

LangChainとAzure AI Searchを用いたRAGの手順は以下の通りです。

①AI Document IntelligenceでドキュメントをMarkdownに変換する
②LangChainでMarkdownを見出し位置でチャンク分割する(セマンティックチャンク)
③チャンク化した内容をベクトル化し、Azure AI Searchに登録する
④ユーザーからの質問を受けて、Azure AI Searchを検索する
⑤検索結果をLLMに入力して回答を生成する

本記事では、Microsoftのサンプルコードを元に③~⑤を解説します。
①~②は、前回記事のAzure AI Document IntelligenceとLangChainを活用したRAGの実装をご確認ください。

※なお、Azure AI Document Intelligenceを使用して、ドキュメント内の図からマークダウンを出力し、図を切り抜き、図のコンテンツ(キャプション付き)をAzure Open AI GPT-4Vモデルに送信して、セマンティックチャンキングする方法については、こちらのサンプルコードをご確認ください。

③チャンク化した内容をベクトル化し、Azure AI Searchに登録する

チャンク化したドキュメントをベクトル化し、Azure AI searchのベクトルストアに格納します。

ベクトル化は、AzureOpenAI Serviceのembedding modelを活用します。LangchainのAzureOpenAIEmbeddings関数で、AzureOpenAIのEmbeddingモデルをラップして利用します。

Azure AI SearchのインスタンスもLangChainのAzureSerach関数から実施可能です。
import os
from langchain_openai import AzureChatOpenAI, AzureOpenAIEmbeddings
#from langchain.vectorstores.azuresearch import AzureSearch
from langchain_community.vectorstores.azuresearch import AzureSearch from langchain.schema import StrOutputParser from langchain.schema.runnable import RunnablePassthrough
# AOAIのEmbeddingsモデルをラップ aoai_embeddings = AzureOpenAIEmbeddings( azure_deployment="<Azure OpenAI embeddings model>", openai_api_version="<Azure OpenAI API version>", ) vector_store_address: str = os.getenv("AZURE_SEARCH_ENDPOINT") vector_store_password: str = os.getenv("AZURE_SEARCH_ADMIN_KEY")

index_name: str = "<your index name>" #Azure AI Search上に作成するIndex名 # ベクトルストアのインスタンスを構築 vector_store: AzureSearch = AzureSearch( azure_search_endpoint=vector_store_address, azure_search_key=vector_store_password, index_name=index_name, embedding_function=aoai_embeddings.embed_query ## AOAIのembeddingsモデル ) # 前の記事でチャンク化した情報(splits)を格納する # 格納するタイミングでベクトル化も行われる vector_store.add_documents(documents=splits)

④ユーザーの質問文(クエリ)からAzure AI Searchを検索する

次にベクトルストアから、質問に対して関連した情報を検索します。Retriever というインスタンスを作成して情報を取り出していきます。

search_typeを"similarity"で類似検索が設定でき、search_kwargsで検索件数を設定します。Retrieverインスタンスに質問を与えると、関連情報を検索することができます。
# Retrieve relevant chunks based on the question
# 先ほど作成したベクトルストアをRetrieverとして定義
retriever = vector_store.as_retriever(search_type="similarity", search_kwargs={"k": 4})

retrieved_docs = retriever.get_relevant_documents("この調査の目的は何ですか?")
print(retrieved_docs[0].page_content)

⑤検索結果をLLMに入力して回答を生成する

最後にRAGのChainを構築していきます。Chainとはその名の通り、LLM や Prompt などのコンポーネントを繋げて、システムを作り上げる機能です。

質問に基づいて関連するチャンクを検索し、検索結果をLLMに渡して回答生成します。検索結果のテキスト文(context)は複数件数あるため、テキストを連結してLLMに渡します。

Chain を実装するにあたり、LangChain Expression Language (LCEL)といった記述方法を利用しています。 LLM や Prompt 等のコンポーネントを | で繋げるだけで簡単に Chain を構築することが可能です。
# prompt
prompt = """
You are an assistant for question-answering tasks. Use the following pieces of retrieved context to answer the question. If you don't know the answer, just say that you don't know. Use three sentences maximum and keep the answer concise. Question: {question} Context: {context} Answer:
""" llm = AzureChatOpenAI( openai_api_version="<Azure OpenAI API version>", azure_deployment="<your chat model deployment name>", temperature=0, ) # ベクトルストアから取り出したdocumentからpage_contentの内容だけを抽出して連結 def format_docs(docs): return "\n\n".join(doc.page_content for doc in docs) rag_chain = ( {"context": retriever | format_docs, "question": RunnablePassthrough()} | prompt | llm | StrOutputParser() )

# 質問に対する回答を生成 question = "この調査の目的は何ですか?" answer = rag_chain({"context": retrieved_docs, "question": question}) print(answer)

これで、Azure AI SearchとLangChainを使ったRAGの実装が完了しました。以上の手順を参考にして、実際のプロジェクトに適用してみてください。

参考記事

Form-Recognizer-Toolkit/SampleCode/Python/sample_rag_langchain.ipynb
【徹底解説】Document Intelligenceを利用してRAGを構築する
基本概念から理解するAzure AI Search - Azure OpenAI Serviceとの連携まで
LangChainとRAGで実現する先進的なAIチャットボットの開発