🦜

任意のWebページをEmbeddingしてVertexAI Searchで検索する

2024/02/07に公開

はじめに

この記事ではLangChainとVertexAIを使ってLLMから応答を得る記事です。
今回は任意のWebページをEmbeddingしてLLMに渡し、QAを実行する内容となっています。

手順

任意のWebページをEmbeddingしてVertexAI Searchで検索するまでの手順を以下に記載します。

おおまかな流れ

  • Vertex AI Searchのエンドポイントを構築する
  • インデックス処理を実行してベクターストアを構築する
  • LLMを使って応答を得る

事前準備

スクリプトを実行するにあたってはいくつかのパッケージをインストールする必要があります。具体的には以下の内容です。

# Basic python packages
urllib3==1.26.18
unstructured==0.6.6
tabulate==0.9.0
pytesseract==0.3.10
tiktoken==0.4.0
langchain-google-vertexai==0.0.3
langchain==0.1.0
# Load Web Page Documents
beautifulsoup4==4.12.2
html2text==2020.1.16

また、コンテナで動かす場合は以下のdockerfileを参考にしてください。

FROM python:3.9-slim as builder

ENV APP_HOME /app
WORKDIR $APP_HOME
COPY . ./

RUN pip install --no-cache-dir --upgrade pip && pip install --no-cache-dir -r requirements.txt

CMD ["python","app.py"]

Vertex AI Searchのエンドポイントを構築する流れ

  • Vertex AI Search のインデックスを作成
    • 事前にコンソールで作成してINDEX_IDをコピーしておく
  • Vertex AI Search のインデックスをエンドポイントにデプロイ

なお、エンドポイントの作成は一度だけ実行します。

from google.cloud import aiplatform

PROJECT_ID = os.environ.get("PROJECT_ID", "")
LOCATION = os.environ.get("PROJECT_ID", "asia-northeast1")

# VertexAIのIndex IDを指定する
INDEX_ID = ""
# INDEX_ID = "projects/〜/locations/asia-northeast1/indexes/〜"

aiplatform.init(project=PROJECT_ID, location=LOCATION)

# Endpointを作成する
index_endpoint = aiplatform.MatchingEngineIndexEndpoint.create(
    project=PROJECT_ID,
    location=LOCATION,
    display_name="index-endpoint-ai",
    description="search-endpoint",
    public_endpoint_enabled=False,
)

# IndexをEndpointにデプロイする
index = aiplatform.MatchingEngineIndex(index_name=INDEX_ID, location=LOCATION)
deployed_index = index_endpoint.deploy_index(
    index=index,
    deployed_index_id="deployed_index_id",
    display_name="display-name-ai-index",
    machine_type="e2-standard-2",
    min_replica_count=1,
    max_replica_count=1,
)

インデックス処理を実行してベクターストアを構築する流れ

  • 特定のURLからWebページを取得する
  • 取得したWebページをhtmlパース
  • htmlパースしたテキストを分割する
  • ベクターストアに格納する
import os

from google.cloud import aiplatform

from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_loaders.recursive_url_loader import RecursiveUrlLoader

from langchain_google_vertexai.embeddings import VertexAIEmbeddings
from langchain_community.vectorstores import MatchingEngine
from langchain_community.document_transformers import Html2TextTransformer

PROJECT_ID = os.environ.get("PROJECT_ID", "")
LOCATION = os.environ.get("PROJECT_ID", "asia-northeast1")

# VertexAIのIndex IDを指定する
INDEX_ID = ""

# VertexAIのENDPOINT IDを指定する
ENDPOINT_ID = ""

# embedding データの保存先
GCS_BUCKET_NAME = ""

use_embedding_model_name = "textembedding-gecko@003"

loader = RecursiveUrlLoader("https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/configuration-function-zip.html")
documents = loader.load()

# pip install --user html2text
html2text = Html2TextTransformer()
docs_transformed = html2text.transform_documents(documents)

# split the documents into chunks
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=1000,
    chunk_overlap=50,
    separators=["\n\n", ".", "!", "?", ",", " ", "。"],
)

doc_splits = text_splitter.split_documents(docs_transformed)

aiplatform.init(project=PROJECT_ID, location=LOCATION)

vector_store = MatchingEngine.from_components(
    embedding=VertexAIEmbeddings(model_name=use_embedding_model_name),
    project_id=PROJECT_ID,
    region=LOCATION,
    gcs_bucket_name=GCS_BUCKET_NAME,
    index_id=INDEX_ID,
    endpoint_id=ENDPOINT_ID,
)

vector_store.add_documents(doc_splits)

LLMを使って応答を得る流れ

  • Matching Engine を起動する
  • ベクターストアオブジェクトを呼び出す
  • ベクターストアを検索する(近傍値=3)
  • RetrievalQAを実行してLLMから応答を得る
import json

from google.cloud import aiplatform
from langchain_google_vertexai import VertexAI
from langchain_google_vertexai.embeddings import VertexAIEmbeddings
from langchain_community.vectorstores import MatchingEngine

from langchain.chains import RetrievalQA

PROJECT_ID = os.environ.get("PROJECT_ID", "")
LOCATION = os.environ.get("PROJECT_ID", "asia-northeast1")

# VertexAIのIndex IDを指定する
INDEX_ID = ""

# VertexAIのENDPOINT IDを指定する
ENDPOINT_ID = ""

# embedding データの保存先
GCS_BUCKET_NAME = ""

use_embedding_model_name = "textembedding-gecko@003"
use_chat_model_name = "text-bison-32k"

aiplatform.init(project=PROJECT_ID, location=LOCATION)

vector_store = MatchingEngine.from_components(
    embedding=VertexAIEmbeddings(model_name=use_embedding_model_name),
    project_id=PROJECT_ID,
    region=LOCATION,
    gcs_bucket_name=GCS_BUCKET_NAME,
    index_id=INDEX_ID,
    endpoint_id=ENDPOINT_ID,
)

NUMBER_OF_RESULTS = 3

query = """
命令セットアーキテクチャを変更する前に確認することは何ですか?
"""

retriever = vector_store.as_retriever(
    search_type="similarity",
    search_kwargs={"k":NUMBER_OF_RESULTS})

chat = VertexAI(model_name=use_chat_model_name, temperature=0)
qa_chain = RetrievalQA.from_chain_type(llm=chat, chain_type="stuff", retriever=retriever)

result = qa_chain.run(query)

print(result)

まとめ

3工程で簡単にRAG構成を作成できました。しかし、課題がいくつか残っています。
具体的には以下のとおりです。

  • ベクトル化したWebページの保存と管理
  • 複数のWebページをベクトルデータベースに格納した時のドキュメント検索

などなどです。
現在検討中なので何かわかったらまた記事にしようと思います。

Discussion