AzureからGPTを使ってみる | RAG(Retriever編)
LLMに独自データから回答してもらうために、
・独自データとして利用したいドキュメントをチャンクに分割
・分割したドキュメントをベクトル化して、データベースへ格納、類似度から検索
については以前に記事にしたので、今回は、LangChainのRetrieverを使って、LLMに独自ドキュメントから回答をしてもらうことにしましょう。
Retrieverとは?
簡単に言えば、Retrieverは「最適な情報を見つけ出すための検索エンジン」と考えるとわかりやすいです。
LangChainの「Retriever」は、外部のデータや情報を効率的に取得するためのモジュールです。具体的には、検索機能のような役割を果たします。
Retrieverの主な役割
質問に関連する情報の検索:ユーザーが質問をしたときに、データベースやドキュメント、Webサイトなどの外部情報源から関連する情報を取得します。
検索アルゴリズムの利用:Retrieverは、キーワード検索やベクトル検索など、さまざまな検索アルゴリズムを使って、関連性の高い情報を効率的に抽出します。
他のLangChainコンポーネントとの連携:RetrieverはLangChainの他のモジュール(例えば、チェーンやエージェント)と連携し、AIの応答を強化します。Retrieverが取得した情報を元に、LLMが質問に答える際に正確で具体的な回答を生成します。
Retrieverを実装する
「ジョジョの奇妙な冒険」についてのwikipediaを、markdown記法で成形したドキュメントを使ってみます。
#langchain-chroma=0.1.3
#langchain-openai=0.1.15
#langchain-text-splitters=0.2.2
from langchain_chroma import Chroma
from langchain_openai import AzureOpenAIEmbeddings
from langchain_text_splitters import MarkdownHeaderTextSplitter
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from dotenv import load_dotenv
import os
# OpenAI APIキーの設定
dotenv_path = ".env"
load_dotenv(dotenv_path)
OPENAI_API_BASE = os.getenv('OPENAI_API_BASE')
OPENAI_API_VERSION = os.getenv('OPENAI_API_VERSION')
OPENAI_API_KEY = os.getenv('OPENAI_API_KEY')
os.environ["AZURE_OPENAI_API_KEY"] = OPENAI_API_KEY
os.environ["AZURE_OPENAI_ENDPOINT"] = OPENAI_API_BASE
# Load example document
with open("jojo.md") as f:
state_of_the_union = f.read()
# Markdown見出しで文章を分割します
headers_to_split_on = [
("#", "Header 1"),
("##", "Header 2"),
("###", "Header 3"),
("####", "Header 4"),
]
markdown_splitter = MarkdownHeaderTextSplitter(
headers_to_split_on=headers_to_split_on,
strip_headers = False #コンテンツび分割されるヘッダーを含めるかどうか
)
md_header_splits = markdown_splitter.split_text(state_of_the_union)
#embeddingには、Azureからtext-embedding-ada-002を使用します。
embed = AzureOpenAIEmbeddings(
model = 'text-embedding-ada-002',
)
documents = md_header_splits
# VectorStoreの準備
vectorstore = Chroma.from_documents(
documents,
embedding=embed,
#collection_name="example_collection",
#persist_directory="./chroma_langchain_db", # Where to save data locally, remove if not neccesary
)
# Retrieverの準備
retriever = vectorstore.as_retriever()
# プロンプトテンプレートの準備
message = """
提供されたコンテキストのみを使用して、この質問に答えてください。
{question}
Context:
{context}
"""
prompt = ChatPromptTemplate.from_messages([("human", message)])
まずは、promptまでをchainでつないで出力を見てみます。
# ユーザー入力
user_input="カーズとは?"
# RAGチェーンの準備
rag_chain = {"context": retriever, "question": RunnablePassthrough()} | prompt
# chainの実行
result = rag_chain.invoke(user_input)
print(result)
(*出力は一部省略して、見やすいように加工しています。)
しっかりカーズについてのドキュメントを検索できていますが、最後のスターダストクルセダーズ編のドキュメントはカーズとは関係ないと思われます。
LLMに独自ドキュメントから回答してもらう
LLMまでchainでつなげてみましょう。
#langchain -openai=0.1.15 #langchain =0.2.7
from langchain_openai import AzureChatOpenAI
llm = AzureChatOpenAI(
api_version=OPENAI_API_VERSION,
azure_deployment="gpt4o",# azure_deployment = "deployment_name"
verbose=True
)
# RAGチェーンの準備
rag_chain = {"context": retriever, "question": RunnablePassthrough()} | prompt | llm
# ユーザー入力
user_input="カーズとは?"
# chainの実行
response = rag_chain.invoke(user_input)
print(response.content)
うまく出力できました!
一応、ブラウザからGPT4oの回答を見てみます。
「ジョジョの奇妙な冒険」に登場する”カーズ”とは?
すでにジョジョについては、相当くわしいようでした。
おまけ:webからの情報をRetrieverを使ってLLMに回答させる
wikipediaのドラゴンボールから回答してもらいます。
#pip install unstructured
import requests
import re
from langchain.document_loaders import UnstructuredHTMLLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_openai import AzureChatOpenAI
# Download the content
response = requests.get("https://ja.wikipedia.org/wiki/ドラゴンボール")
# Write it to a file
with open("DB.html", "w", encoding="utf-8") as f:
f.write(response.text)
# Load it with an HTML parser
doc = UnstructuredHTMLLoader("DB.html").load()
document = doc[0]
document.page_content = re.sub("\n\n+", "\n", document.page_content)
all_documents = []
# HTML内のデータを分割する用のSplitterを用意
text_splitter = RecursiveCharacterTextSplitter(chunk_size=200, chunk_overlap=0)
all_documents += text_splitter.create_documents([document.page_content])
embed = AzureOpenAIEmbeddings(
model = 'text-embedding-ada-002',
)
# VectorStoreの準備
vectorstore = Chroma.from_documents(
all_documents,
embedding=embed,
#collection_name="example_collection",
#persist_directory="./chroma_langchain_db", # Where to save data locally, remove if not neccesary
)
# Retrieverの準備
retriever = vectorstore.as_retriever(
search_type="similarity",
search_kwargs={"k": 2},
)
# プロンプトテンプレートの準備
message = """
提供されたコンテキストのみを使用して、この質問に答えてください。
{question}
Context:
{context}
"""
prompt = ChatPromptTemplate.from_messages([("human", message)])
llm = AzureChatOpenAI(
api_version=OPENAI_API_VERSION,
azure_deployment="gpt4o",# azure_deployment = "deployment_name"
verbose=True
)
# RAGチェーンの準備
rag_chain = {"context": retriever, "question": RunnablePassthrough()} | prompt | llm
# 質問応答
response = rag_chain.invoke("孫悟空について教えてください")
print(response.content)
この記事が気に入ったらサポートをしてみませんか?