LoginSignup
10
7

LangChain を使ってローカルファイルを RAG で回答させてみる

Last updated at Posted at 2023-09-12

やりたいこと

業務で Azure OpenAI Service を使う機会があり、LangChain を使っていろいろとやってみたので複数回で記事にまとめます。今回は、ローカルのファイル内容を RAG で LLM から回答させる実装を行います。
すでに多くの方が情報発信していますが、その1つとして参考にしてもらえると幸いです!

他の記事

  1. Langchain を使って LLM から回答させてみる
  2. LangChain を使ってローカルファイルを RAG で回答させてみる ← イマココ
  3. LangChain を使って Azure Cognitive Search のデータを RAG で回答させてみる

前提

  • WSL (Ubuntu 22.04) 上で行います
  • Python で実装します
  • LLM のモデルには、GPT-3.5 を使用します
  • Azure OpenAI Service で gpt-35-turbo, text-embedding-ada-002 モデルのデプロイメントは作成済み
  • Azure OpenAI Service を使うため、OpenAI 社のものを使う場合と異なる場合があります
  • Azure OpenAI Service は作成済み

RAG とは

Retrieval Augmented Generation の略。GPT-3.5 のモデルの場合、2021年9月までの情報しか学習されていないので、それ以降の情報について質問しても回答は得られない。そういった場合に、未学習の情報をプロンプトに組み込むことでその情報を踏まえて LLM から回答してもらう手法。
社内文書の内容や FAQ データなどを RAG で回答させるなどのユースケースが多い。

実装

未学習のデータ

RAG 用に未学習のデータを作成する。テニスに関する内容なのはテニスが趣味だから :tennis:
GPT-3.5 が未学習のデータならなんでも可。data/test.txt として保存しておく。

test.txt
1. ロジャー・フェデラーは2022年に惜しまれながらプロテニスプレイヤーを引退しました。
2. ノバク・ジョコビッチは2023年の全豪オープン、全仏オープン、全米オープンを勝利し、グランドスラムの勝利数が24回になりました。

パラメータ設定

OpenAI 関連のパラメータを事前に .env ファイルとして定義しておく。コードの中で環境変数として読み込み使用する。今回は分かりやすさのために引数に設定する形にするが、DEPLOYMENT_NAME 以外のパラメータは環境変数に設定されていれば引数で与える必要はない。
OPENAI_EMBEDDINGS_DEPLOYMENT_NAME は RAG を行う上で未学習の情報をベクトル化する際に使用するモデル。Azure OpenAI Service だと text-embedding-ada-002 が推奨されている。

.env
OPENAI_API_TYPE=azure
OPENAI_API_VERSION=2023-05-15
OPENAI_API_BASE={YOUR_OPENAI_API_BASE}
OPENAI_API_KEY={YOUR_OPENAI_API_KEY}
DEPLOYMENT_NAME={YOUR_DEPLOYMENT_NAME}
OPENAI_EMBEDDINGS_DEPLOYMENT_NAME={YOUR_EMBEDDINGS_DEPLOYMENT_NAME}

使用するライブラリ

バージョンは任意のもので可。pip install -r requirements.txt でインストールする。

requirements.txt
python-dotenv==1.0.0
langchain==0.0.268
openai==0.27.8
tiktoken==0.4.0
chromadb==0.4.10

コード本体

rag.py
import os
import dotenv
from langchain.chains import ConversationalRetrievalChain
from langchain.chat_models import AzureChatOpenAI
from langchain.document_loaders import TextLoader
from langchain.embeddings import OpenAIEmbeddings
from langchain.memory import ConversationBufferMemory
from langchain.schema import AIMessage, HumanMessage
from langchain.text_splitter import CharacterTextSplitter
from langchain.vectorstores import Chroma

dotenv.load_dotenv()

OPENAI_API_TYPE = os.getenv('OPENAI_API_TYPE')
OPENAI_API_VERSION = os.getenv('OPENAI_API_VERSION')
OPENAI_API_BASE = os.getenv('OPENAI_API_BASE')
OPENAI_API_KEY = os.getenv('OPENAI_API_KEY')
DEPLOYMENT_NAME = os.getenv('DEPLOYMENT_NAME')
OPENAI_EMBEDDINGS_DEPLOYMENT_NAME = os.getenv(
    'OPENAI_EMBEDDINGS_DEPLOYMENT_NAME')

llm = AzureChatOpenAI(openai_api_version=OPENAI_API_VERSION,
                      openai_api_base=OPENAI_API_BASE,
                      openai_api_type=OPENAI_API_TYPE,
                      openai_api_key=OPENAI_API_KEY,
                      deployment_name=DEPLOYMENT_NAME,
                      temperature=0)

embedding = OpenAIEmbeddings(openai_api_version=OPENAI_API_VERSION,
                             openai_api_base=OPENAI_API_BASE,
                             openai_api_type=OPENAI_API_TYPE,
                             openai_api_key=OPENAI_API_KEY,
                             model=OPENAI_EMBEDDINGS_DEPLOYMENT_NAME)

loader = TextLoader('data/test.txt')
docs = loader.load()
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
documents = text_splitter.split_documents(documents=docs)
vector_store = Chroma.from_documents(documents=documents, embedding=embedding)
memory = ConversationBufferMemory(memory_key='chat_history',
                                  return_messages=True)
qa = ConversationalRetrievalChain.from_llm(
    llm=llm, retriever=vector_store.as_retriever(), memory=memory)

# 作成した Index が k (Default: 4)より少ないと余計なッセージが表示される
# 表示させたくない場合は k を小さくする
# qa = ConversationalRetrievalChain.from_llm(
#     llm=llm, retriever=vector_store.as_retriever(search_kwargs={'k': 1}), memory=memory)

while (True):
    prompt = input('Q: ')

    if prompt == 'q' or not prompt:
        break

    answer = qa.run(prompt)
    print('A: ', answer)

print('-- Chat History --')
for msg in memory.chat_memory.messages:
    if type(msg) == AIMessage:
        role = 'ai'
    elif type(msg) == HumanMessage:
        role = 'human'
    else:
        role = 'system'

    print(role, '\t>> ', msg.content)

実行してみる

以下のような形で LLM とチャット形式でやりとりできる。test.txt の内容を踏まえて回答してくれている!

Number of requested results 4 is greater than number of elements in index 1, updating n_results = 1 というメッセージは作成した Index が k (Default: 4) より少ない場合に表示されるもの。表示させたくない場合は、k の値を小さくする (上記コード本体のコメントを参照)。

image.png

ということで

LangChain を使って、未学習のデータを RAG で LLM に回答させるコードを実装しました!
次回は、Azure Cognitive Search をベクトルストアにするケースを行います。

次回

LangChain を使って Azure Cognitive Search のデータを RAG で回答させてみる

以上です。

10
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
7