コードを使ってインデックスを構築して使用する方法

重要

この記事で説明する機能の一部は、プレビューでのみ使用できる場合があります。 このプレビューはサービス レベル アグリーメントなしで提供されており、運用環境ではお勧めしません。 特定の機能はサポート対象ではなく、機能が制限されることがあります。 詳しくは、Microsoft Azure プレビューの追加使用条件に関するページをご覧ください。

この記事では、インデックスを作成し、それをコードから使う方法について説明します。 インデックスをローカルに作成するには、promptflow-rag パッケージを使います。 クラウド上にリモート インデックスを作成するには、azure-ai-ml パッケージを使います。 langchain を使ってインデックスを使います。

前提条件

以下が必要です。

  • AI Studio ハブプロジェクト

  • サンプル製品と顧客データのインデックスを作成するための Azure AI 検索サービスへの接続。 Azure AI 検索サービスがない場合は、Azure portal から作成するか、こちらの手順を参照してください。

  • 埋め込み用モデル:

    • Azure OpenAI から ada-002 埋め込みモデルを使用できます。 デプロイ手順については、こちらをご覧ください。
    • または、AI Studio プロジェクトにデプロイされている別の埋め込みモデルを使用することもできます。 この例では、Cohere の多言語埋め込みを使用しています。 このモデルのデプロイ手順については、こちらをご覧ください。

インデックスをローカルで構築して使う

インデックスをローカルで構築して使用できます。

ローカル インデックス操作に必要なパッケージ

次の、ローカル インデックスの作成に必要なパッケージをインストールします。

pip install promptflow-rag langchain langchain-openai

AI 検索をローカルで使うように構成する

Azure AI 検索をインデックス ストアとして使います。 まず、次のコードを使って Azure AI 検索サービスを設定します。

import os
# set credentials to your Azure AI Search instance
os.environ["AZURE_AI_SEARCH_KEY"] = "<your-ai-search-key>"
os.environ["AZURE_AI_SEARCH_ENDPOINT"] = "https://<your-ai-search-service>.search.windows.net"

Azure OpenAI 埋め込みを使ってローカルでインデックスを構築する

Azure OpenAI 埋め込みを使うインデックスを作成するには、モデルに接続するための環境変数を構成します。

import os
# set credentials to your Azure OpenAI instance
os.environ["OPENAI_API_VERSION"] = "2023-07-01-preview"
os.environ["AZURE_OPENAI_API_KEY"] = "<your-azure-openai-api-key>"
os.environ["AZURE_OPENAI_ENDPOINT"] = "https://<your-azure-openai-service>.openai.azure.com/"

次に、build_index 関数を使ってインデックスを構築します。

from promptflow.rag.config import LocalSource, AzureAISearchConfig, EmbeddingsModelConfig
from promptflow.rag import build_index

local_index_aoai=build_index(
    name="<your-index-name>" + "aoai",  # name of your index
    vector_store="azure_ai_search",  # the type of vector store
    embeddings_model_config=EmbeddingsModelConfig(
        model_name="text-embedding-ada-002",
        deployment_name="text-embedding-ada-002", # verify if your deployment name is same as model name
    ),
    input_source=LocalSource(input_data="<path-to-your-local-files>"),  # the location of your file/folders
    index_config=AzureAISearchConfig(
        ai_search_index_name="<your-index-name>" + "-aoai-store", # the name of the index store inside the azure ai search service
    ),
    tokens_per_chunk = 800, # Optional field - Maximum number of tokens per chunk
    token_overlap_across_chunks = 0, # Optional field - Number of tokens to overlap between chunks
)

上記のコードはインデックスをローカルに構築します。 環境変数を使って AI 検索サービスを取得し、Azure OpenAI 埋め込みモデルに接続します。

AI Studio プロジェクトにデプロイされた他の埋め込みモデルを使ってローカルでインデックスを構築する

AI Studio プロジェクトにデプロイされた埋め込みモデルを使うインデックスを作成するには、以下に示すように、ConnectionConfig を使ってモデルへの接続を構成します。 subscriptionresource_groupworkspace は、埋め込みモデルがインストールされているプロジェクトを参照します。 connection_name はモデルの接続名を参照します。これは AI Studio プロジェクトの設定ページにあります。

from promptflow.rag.config import ConnectionConfig

my_connection_config=ConnectionConfig(
    subscription_id="<subscription_id>",
    resource_group_name="<resource_group_name>",
    workspace_name="<ai_studio_project_name>",
    connection_name="<serverless_connection_name>"
    )

次に、build_index 関数を使ってインデックスを構築します。

from promptflow.rag.config import LocalSource, AzureAISearchConfig, EmbeddingsModelConfig
from promptflow.rag import build_index

local_index_cohere=build_index(
    name="<your-index-name>" + "cohere",  # name of your index
    vector_store="azure_ai_search",  # the type of vector store
    embeddings_model_config=EmbeddingsModelConfig(
        model_name="cohere-embed-v3-multilingual", # in this example we use cohere multi lingual embedding
        connection_config=my_connection_config # created in previous step
    ),
    input_source=LocalSource(input_data="<path-to-your-local-files>"),  # the location of your file/folders
    index_config=AzureAISearchConfig(
        ai_search_index_name="<your-index-name>" + "cohere-store", # the name of the index store inside the azure ai search service
    ),
    tokens_per_chunk = 800, # Optional field - Maximum number of tokens per chunk
    token_overlap_across_chunks = 0, # Optional field - Number of tokens to overlap between chunks
)

上記のコードはインデックスをローカルに構築します。 環境変数を使って AI 検索サービスと接続構成を取得し、埋め込みモデルに接続します。

ローカル インデックスの使用

作成されたローカル インデックスは、検索クエリに使うための langchain 取得コンポーネントとして使用できます。

from promptflow.rag import get_langchain_retriever_from_index

# Get the OpenAI embedded Index
retriever=get_langchain_retriever_from_index(local_index_aoai)
retriever.get_relevant_documents("<your search query>")

# Get the Cohere embedded Index
retriever=get_langchain_retriever_from_index(local_index_cohere)
retriever.get_relevant_documents("<your search query>")

AI Studio プロジェクトへのインデックスの登録 (省略可能)

必要に応じて、自分またはプロジェクトにアクセスできる他のユーザーがクラウドから使用できるように、AI Studio プロジェクトにインデックスを登録できます。 続行する前に、リモート操作に必要なパッケージをインストールしてください。

プロジェクトに接続する

# connect to the AI Studio project
from azure.identity import DefaultAzureCredential
from azure.ai.ml import MLClient

client=MLClient(
    DefaultAzureCredential(), 
    subscription_id="<subscription_id>",
    resource_group_name="<resource_group_name>",
    workspace_name="<ai_studio_project_name>"
    )

上記のコードの subscriptionresource_groupworkspace は、接続するプロジェクトを参照します。

インデックスを登録する

from azure.ai.ml.entities import Index

# register the index with Azure OpenAI embeddings
client.indexes.create_or_update(
    Index(name="<your-index-name>" + "aoai", 
          path=local_index_aoai, 
          version="1")
          )

# register the index with cohere embeddings
client.indexes.create_or_update(
    Index(name="<your-index-name>" + "cohere", 
          path=local_index_cohere, 
          version="1")
          )

Note

環境変数は、ローカル環境での利便性のために用意されています。 ただし、環境変数を使用して作成されたローカル インデックスを登録すると、環境変数からのシークレットがクラウド インデックスに転送されないため、インデックスが期待どおりに機能しない可能性があります。 この問題に対処するには、登録する前に、ConnectionConfig または connection_id を使用してローカル インデックスを作成します。

AI Studio プロジェクトにインデックスを (リモートで) 構築する

クラウドで AI Studio プロジェクトにインデックスを構築します。

リモート インデックス操作に必要なパッケージ

次の、リモート インデックス作成に必要なパッケージをインストールします。

pip install azure-ai-ml promptflow-rag langchain langchain-openai

AI Studio プロジェクトに接続する

まず、プロジェクトに接続します。 次のコードの subscriptionresource_groupworkspace は、接続するプロジェクトを参照します。

# connect to the AI Studio project
from azure.identity import DefaultAzureCredential
from azure.ai.ml import MLClient

client=MLClient(
    DefaultAzureCredential(), 
    subscription_id="<subscription_id>",
    resource_group_name="<resource_group_name>",
    workspace_name="<ai_studio_project_name>"
    )

AI 検索サービス接続を取得する

このプロジェクトには AI 検索サービスへの接続が必要です。 プロジェクトから詳細を取得します。

ai_search_connection = client.connections.get("<ai_search_connection>")

埋め込みモデルに接続する

Microsoft Entra ID 接続または API キーベースの接続を使って Azure OpenAI に接続できます。

from azure.ai.ml.entities import IndexModelConfiguration
## aoai connections - entra id
aoai_connection = client.connections.get("<your_aoai_entra_id_connection>")
embeddings_model_config = IndexModelConfiguration.from_connection(
    aoai_connection, 
    model_name="text-embedding-ada-002",
    deployment_name="text-embedding-ada-002") # verify if your deployment name is same as model name

## OR you can connect using API Key based connections 
from azure.ai.ml.entities import IndexModelConfiguration
## aoai connections - API Key
aoai_connection = client.connections.get("<your_aoai_connection>", populate_secrets=True)
embeddings_model_config = IndexModelConfiguration.from_connection(
    aoai_connection, 
    model_name="text-embedding-ada-002",
    deployment_name="text-embedding-ada-002")

サーバーレス接続を使って、AI Studio プロジェクトにデプロイされた埋め込みモデル (Azure OpenAI 以外のモデル) に接続できます。

from azure.ai.ml.entities import IndexModelConfiguration
serverless_connection = client.connections.get("<my_embedding_model_severless_connection_name>")
embeddings_model_config = IndexModelConfiguration.from_connection(cohere_serverless_connection)

インデックスを構築する入力データを選ぶ

次の種類の入力からインデックスを構築できます。

  • ローカル ファイルおよびフォルダー
  • GitHub リポジトリ
  • Azure Storage

次のコード サンプルを使って、これらのソースのいずれかを使い、input_source を構成できます。

# Local source
from azure.ai.ml.entities import LocalSource

input_source=LocalSource(input_data="<path-to-your-local-files>")

# GitHub repository
from azure.ai.ml.entities import GitSource

input_source=GitSource(
    git_url="https://github.com/rust-lang/book.git", # connecting to the RUST repo as an example
    git_branch_name="main", 
    git_connection_id="")

# Azure Storage
input_source_subscription = "<subscription>"
input_source_resource_group = "<resource_group>"
input_source_workspace = "<workspace>"
input_source_datastore = "<datastore_name>"
input_source_path = "path"

input_source = f"azureml://subscriptions/{input_source_subscription}/resourcegroups/{input_source_resource_group}/workspaces/{input_source_workspace}/datastores/{input_source_datastore}/paths/{input_source_path}"

クラウド上にインデックスを構築する

これで、ai_search_connectionembeddings_model_configinput_source を使ってインデックスを構築できるようになりました。 build_index 関数を使います。 Azure Storage URL を入力ソースとして使っている場合は、UserIdentityConfiguration も指定する必要があります。

# from azure.ai.ml.entities.credentials import UserIdentityConfiguration # user specified identity used to access the data. Required when using an azure storage URL
from azure.ai.ml.entities import AzureAISearchConfig

client.indexes.build_index(
    name="<index_name>", # name of your index
    embeddings_model_config=embeddings_model_config, 
    input_source=input_source, 
    # input_source_credential=UserIdentityConfiguration(), # user specified identity used to access the data. Required when using an azure storage URL
    index_config=AzureAISearchConfig(
        ai_search_index_name="<index_name>",  # the name of the index store in AI search service
        ai_search_connection_id=ai_search_connection.id, 
    ),
    tokens_per_chunk = 800, # Optional field - Maximum number of tokens per chunk
    token_overlap_across_chunks = 0, # Optional field - Number of tokens to overlap between chunks
)

入力ソース データのサイズによっては、上記の手順が完了するまでに時間がかかる場合があります。 ジョブが完了すると、インデックス オブジェクトを取得できます。

my_index=client.indexes.get(name="<index_name>", label="latest")

プロジェクトから登録済みインデックスを使う

プロジェクトから登録済みのインデックスを使うには、プロジェクトに接続してインデックスを取得する必要があります。 取得したインデックスは、それを使う langhcain 取得コンポーネントとして使用できます。 ここに示すように、client を使ってプロジェクトに接続できます。

from promptflow.rag import get_langchain_retriever_from_index

my_index=client.indexes.get(
    name="<registered_index_name>", 
    label="latest")

index_langchain_retriever=get_langchain_retriever_from_index(my_index.path)
index_langchain_retriever.get_relevant_documents("<your search query>")

インデックスを使用するための質疑応答機能

ローカルまたはクラウドでインデックスを構築する方法について説明しました。 このインデックスを使って、ユーザーの質問を受け入れ、インデックス データから回答を提供する QnA 関数を構築します。 まず、ここに示すように、langchain_retriever としてインデックスを取得します。 ここでは、この retriever を関数で使います。 この関数は、AzureChatOpenAI コンストラクターで定義されている LLM を使います。 インデックスを langchain_retriever として使ってデータのクエリを実行します。 コンテキストと質問を受け入れるプロンプト テンプレートを構築します。 langchain の RetrievalQA.from_chain_type を使って、これらすべてをまとめて答えを取得します。

def qna(question: str, temperature: float = 0.0, prompt_template: object = None) -> str:
    from langchain import PromptTemplate
    from langchain.chains import RetrievalQA
    from langchain_openai import AzureChatOpenAI

    llm = AzureChatOpenAI(
        openai_api_version="2023-06-01-preview",
        api_key="<your-azure-openai-api-key>",
        azure_endpoint="https://<your-azure-openai-service>.openai.azure.com/",
        azure_deployment="<your-chat-model-deployment>", # verify the model name and deployment name
        temperature=temperature,
    )

    template = """
    System:
    You are an AI assistant helping users answer questions given a specific context.
    Use the following pieces of context to answer the questions as completely, 
    correctly, and concisely as possible.
    Your answer should only come from the context. Don't try to make up an answer.
    Do not add documentation reference in the response.

    {context}

    ---

    Question: {question}

    Answer:"
    """
    prompt_template = PromptTemplate(template=template, input_variables=["context", "question"])

    qa = RetrievalQA.from_chain_type(
        llm=llm,
        chain_type="stuff",
        retriever=index_langchain_retriever,
        return_source_documents=True,
        chain_type_kwargs={
            "prompt": prompt_template,
        },
    )

    response = qa(question)

    return {
        "question": response["query"],
        "answer": response["result"],
        "context": "\n\n".join([doc.page_content for doc in response["source_documents"]]),
    }

確実に回答を得るための質問をしてみましょう。

result = qna("<your question>")
print(result["answer"])