👨‍💻

LLM RAGで検索システムを作ってみよう

2025/01/07に公開

はじめに

RAGはなんとなく知ってるが、とりあえず実装して触ってみたいというニーズがありそうだと思い、簡単な検索システムを作ってみます。
使用しているライブラリはVersionで使い方の変更があると思いますので、あくまでも現時点(2025年1月)で動くものになります。

RAGとは

詳細はまたRAGについての記事を書こうと思っていますが、LLMは事前学習された知識に関しては回答をしてくれますが、最新のニュースや一般的ではない情報(例えば社内情報など)については回答ができません。
そこで外部の知識をLLMに検索させて回答させようということで、RAGが誕生しました。

https://arxiv.org/abs/2005.11401

これが2022年5月なので、GPT3.5が発表される2022年11月末より少し前ということになりますね。

今回作成する検索システム

いくつか社内文書を作って、それに関する質問に回答するシステムを作成します。

  • 必要なもの
    • OpenAI APIキー(取得方法は各々調べてください)

データ用意

社内文書を作成しましょう。といっても実際に会社のを使用できないので、ChatGPTに生成してもらいましょう。

生成してもらったものは以下です。これをpdfにして保存してください。
(全て架空です)

pdf文書たち
  • 就労規則.pdf
就労規則
第1章 総則
第1条(目的)
この就労規則(以下「本規則」という)は、株式会社サンプルテック(以下「当社」という)における就業に関する基本的な事項を定め、従業員が安心して業務に従事できる環境を整備することを目的とする。

第2条(適用範囲)
本規則は、当社に雇用されるすべての従業員(正社員、契約社員、パートタイム従業員、アルバイトなど)に適用される。ただし、雇用形態や役職により、一部の規定を適用しない場合がある。

第2章 勤務管理
第3条(勤務時間)

原則として、始業時刻は9時00分、終業時刻は18時00分とし、休憩時間は12時00分から13時00分までの1時間とする。
業務の都合により、始業・終業時刻を繰り上げまたは繰り下げることがある。
フレックスタイム制度、在宅勤務制度を利用する場合は、別途定める社内規程に従うものとする。
第4条(休日・休暇)

休日は原則として土曜日・日曜日とし、国民の祝日、年末年始(12月29日〜1月3日)も休日とする。
有給休暇は入社後6ヶ月勤務した場合に10日付与し、その後は労働基準法の定めに従って付与する。
慶弔休暇、産前産後休暇、育児休業、介護休業などは別途定める社内規程に従う。
第5条(残業・深夜労働)

従業員は所定労働時間を超えて労働する場合、事前に上長の許可を得るものとする。
残業手当、休日出勤手当、深夜手当は労働基準法および当社賃金規程に基づき支給する。
第3章 服務規律
第6条(遵守事項)

従業員は、法令・社内規程を遵守し、職務の遂行にあたって誠実に行動する。
従業員は、勤務時間中は業務に専念し、正当な理由なく職場を離脱してはならない。
第7条(機密保持)

従業員は、在職中および退職後においても、業務上知り得た機密情報を第三者に漏洩してはならない。
機密情報の取扱いに関する詳細は、別途定める情報セキュリティポリシーに従う。
第8条(ハラスメントの禁止)

職務上または人間関係上の地位や立場を利用して、他の従業員に対していじめや差別などの不当な言動を行うことを禁止する。
ハラスメントに該当する行為が確認された場合、懲戒処分を含む厳正な措置を講じる。
第4章 懲戒および退職
第9条(懲戒の種類)

戒告、減給、出勤停止、諭旨解雇、懲戒解雇などの種類を設ける。
懲戒の適用は就業規則に定める手続きに基づき、慎重に判断する。
第10条(退職)

従業員が自己都合により退職する場合は、原則として退職希望日の1ヶ月前までに会社に届け出る。
会社都合で解雇する場合は、労働基準法に定める解雇予告期間を設けるか、解雇予告手当を支払う。
第5章 雑則
第11条(規則の改廃)

本規則の改廃は、会社が必要と認めた場合に行い、改廃内容を従業員に周知することによって効力が生じる。
本規則に定めのない事項については、労働基準法その他の法令に準拠し、会社と従業員との協議により決定する。
  • ミッションビジョンバリュー.pdf
社内のミッション、ビジョン、バリュー
ミッション(Mission)
当社のミッションは、「情報とテクノロジーを通じて、人々の生活をより豊かにするサービスを提供する」ことです。私たちは革新的な発想と高い技術力を活かし、社会課題の解決に取り組みます。

ビジョン(Vision)
グローバル展開: 国内外を問わず、誰もが便利に利用できる技術プラットフォームを構築する。
継続的なイノベーション: 社員一人ひとりが変化を恐れず、新しいアイデアを追求し続ける文化を育む。
社会貢献: 環境や社会に配慮した事業活動を展開し、持続可能な未来を目指す。
バリュー(Value)
挑戦(Challenge): 失敗を恐れず、新たな可能性に挑戦し続ける。
協働(Collaboration): チームワークを重視し、多様なバックグラウンドを持つ人材が協力し合う。
誠実(Integrity): お客様、パートナー、社員に対して公正かつ透明な態度で接する。
  • 会社情報.pdf
会社情報
会社概要
会社名: 株式会社サンプルテック
本社所在地: 東京都中央区架空町1-2-3 サンプルビル8F
設立: 20XX年4月1日
資本金: 3億円
社員数: 350名(2025年1月現在)
事業内容:

ソフトウェア開発
ITコンサルティング
AI・データ分析ソリューション提供
クラウドインフラ構築・運用
組織体制
代表取締役社長: 山田 太郎(やまだ たろう)

大手IT企業出身。新規事業立ち上げの経験が豊富。サンプルテック創業以来のCEO。
取締役副社長: 佐藤 花子(さとう はなこ)

財務・経理分野に強みを持ち、企業の財務戦略をリード。
取締役CTO: 高橋 健二(たかはし けんじ)

システムアーキテクチャ設計の第一人者。クラウド分野で多数の特許を取得。
取締役COO: 鈴木 次郎(すずき じろう)

営業畑でキャリアを積み、複数の大規模プロジェクトを成功に導いた。
執行役員: 田中 和子(たなか かずこ)

人事・総務部門を統括し、人材育成プログラムの開発に取り組む。
主な事業実績
AIを活用した顧客分析システム開発
大手通信事業者に導入し、顧客の解約率を20%以上削減。
クラウド化支援コンサルティング
中堅製造業向けにオンプレミス環境をクラウドへ移行し、運用コストを30%削減。
モバイルアプリ開発
小売チェーン向けに公式アプリを開発し、ダウンロード数100万件を突破。
働き方の特徴
フレックス制度: コアタイム(11:00〜15:00)のみ出社を必須とし、朝型や夜型など個人の生活スタイルに合わせた働き方が可能。
在宅勤務: 状況に応じてリモートワークを許可。オンライン会議ツールとチャットツールを活用し、スムーズなコミュニケーションを実現。
福利厚生: 社員食堂補助、スポーツジム割引、確定拠出年金制度など。
  • その他QA.pdf
その他Q&A
Q1: 在宅勤務の費用負担について
質問: 在宅勤務をする際に、インターネット回線や光熱費などの負担は会社がしてくれるのでしょうか?

回答: 現在のところ、在宅勤務に伴うインターネット回線や光熱費の負担は、個人にてご負担いただいております。ただし、将来的にリモートワークの頻度や環境整備の状況を鑑み、一定の補助制度を導入する可能性があります。

Q2: 年次有給休暇の申請方法
質問: 年次有給休暇の申請はどのように行えば良いですか?

回答: 社内ポータルサイトにある「休暇申請フォーム」から申請が可能です。やむを得ない事情で事前に申請できなかった場合は、上長に口頭で連絡した上で、後日フォームにて申請を完了してください。

Q3: 社内副業は可能か
質問: 本業以外に、社外のプロジェクトに参加して収入を得ることは可能ですか?

回答: 当社では、社内規程に違反しない範囲での副業を認めています。ただし、競合他社での業務や、当社の機密情報を侵害する恐れがある活動は禁止されます。副業を希望する場合は、必ず「副業許可申請書」を総務部へ提出し、承認を得てください。

Q4: 研修制度について
質問: 新入社員研修やスキルアップ研修はどのようなものがありますか?

回答: 新入社員研修としては、ビジネスマナー研修や技術基礎研修などを実施し、当社における開発プロセスを学んでいただきます。スキルアップ研修は、外部講師を招いたセミナーやオンライン研修プログラムを定期的に実施しています。必要に応じて各部門長が研修参加を指示・推薦することもあります。

Q5: ハラスメント相談窓口
質問: ハラスメントに関する悩みを相談したいのですが、どこに連絡すれば良いでしょうか?

回答: 総務部に設置されたハラスメント相談窓口または社内掲示板に記載してある専用の相談フォームをご利用ください。匿名での相談も受け付けており、内容に応じて人事部や法務部とも連携し、適切な対応を行います。

Q6: 資格取得支援制度はあるのか
質問: 資格取得を考えているのですが、会社として支援制度はありますか?

回答: 業務に関連する資格の場合、受験料やテキスト代の一部を会社が負担する制度があります。対象となる資格は管理職と総務部で協議のうえ決定しており、受験の前に申請手続きを行ってください。

実装

フォルダ構成

以下のような構成にします。

  • create_vectore_store.pyでベクターストア(後述)を作成
  • rag_run.pyで実際に質問に対する回答をみます
.
├── data/
│   ├── 就労規則.pdf
│   ├── ミッションビジョンバリュー.pdf
│   ├── 会社情報.pdf
│   └── その他QA.pdf
├── requirements.txt
├── create_vector_store.py
└── rag_run.py

コード

  • requirements.txt
langchain_community
pypdf
openai
tiktoken
faiss-cpu
  • create_vectore_store.py
import os
from pathlib import Path

from langchain_community.document_loaders import PyPDFLoader
from langchain_community.vectorstores import FAISS
from langchain_community.embeddings import OpenAIEmbeddings
from langchain.text_splitter import CharacterTextSplitter

# OpenAI API Key
os.environ["OPENAI_API_KEY"] = "{your api key}"

# File path for saving FAISS index
FAISS_INDEX_PATH = "./vector_store_index"

# Step 1: Load PDFs and extract documents
pdf_files = [str(path) for path in Path("data").glob("*.pdf")]

documents = []
for file in pdf_files:
    loader = PyPDFLoader(file)
    documents.extend(loader.load())

# Step 2: Split documents into chunks
text_splitter = CharacterTextSplitter(
    chunk_size=500,  # Each chunk's maximum size
    chunk_overlap=50  # Overlapping characters between chunks
)

split_documents = text_splitter.split_documents(documents)

# Step 3: Create embeddings and FAISS index
embeddings = OpenAIEmbeddings()
vector_store = FAISS.from_documents(split_documents, embeddings)

# Save the FAISS index for future use
vector_store.save_local(FAISS_INDEX_PATH)
print("Vector store created and saved successfully.")
  • rag_run.py
import os

from langchain_community.vectorstores import FAISS
from langchain_community.embeddings import OpenAIEmbeddings
from langchain_community.chat_models import ChatOpenAI
from langchain.chains import RetrievalQA
from langchain_core.prompts import ChatPromptTemplate
from langchain.chains.question_answering import load_qa_chain

os.environ["OPENAI_API_KEY"] = "{your api key}"

# File path for loading FAISS index
FAISS_INDEX_PATH = "./vector_store_index"

# Load the existing FAISS vector store
print("Loading the vector store...")
vector_store = FAISS.load_local(FAISS_INDEX_PATH, OpenAIEmbeddings(), allow_dangerous_deserialization=True)

# Step 1: Define a Prompt Template
prompt_template = ChatPromptTemplate.from_template(
    """以下のコンテキストを基に質問に答えてください:
    {context}

    質問: {question}
    回答:
    """
)

# Step 2: Create a Retrieval-based QA system with custom prompt
retriever = vector_store.as_retriever()
llm = ChatOpenAI(model="gpt-3.5-turbo")
qa_chain = load_qa_chain(llm=llm, prompt=prompt_template)

# Step 3: User Query
query = "有休を取得したいのですが、どのように手続きをすればいいですか?"
retrieved_docs = retriever.get_relevant_documents(query)
result = qa_chain.run(input_documents=retrieved_docs, question=query)

print("質問:", query)
print("回答:", result)

実行

  1. create_vectore_store.pyを実行する
  2. rag_run.pyを実行する

今回の質問に対して、回答は以下のようでした。

質問: 有休を取得したいのですが、どのように手続きをすればいいですか?
回答: 有給休暇を取得したい場合は、社内ポータルサイトにある「休暇申請フォーム」から申請を行ってください。もしもやむを得ない事情で事前に申請ができない場合は、上司に口頭で連絡した後、後日フォームにて申請を完了してください。申請が承認されれば、有給休暇を取得することができます。

pdfには以下のように記載があるため、概ね回答はできていそうです。

Q2: 年次有給休暇の申請方法
質問: 年次有給休暇の申請はどのように行えば良いですか?
回答: 社内ポータルサイトにある「休暇申請フォーム」から申請が可能です。やむを得 ない事情で事前に申請できなかった場合は、上長に口頭で連絡した上で、後日フォー ムにて申請を完了してください。

ざっと解説

中で使っているコードを解説します。

ベクトルストアの作成

ベクトルストアとは、文章や画像などの情報を ベクトル(数値の配列) の形で蓄積して、類似度検索を行える仕組みのことです。
またドキュメント文書をベクトル化する前に、チャンクと呼ばれる一定の文字単位に分割することがあります。
以下がパラメータで、タスクによって最適化されるべきものです。

  • chunk_size: 一つのチャンクの最大文字数
  • chunk_overlap: 次のチャンクにどれだけ前のテキストを重複して含むか

手順としては以下のようになります

  1. pdfをloadする
  2. チャンクに分割する
  3. Embeddingモデルを使ってベクトル化
    - 1つの分割された文書がベクトルの配列になります
    • 配列の型はEmbeddingモデルごとに決まっています
  4. ベクトルストアとして保存する
    • ここではmeta社が開発したFAISSを使っています
    • FAISSは高速な類似度検索が可能なツールです

プロンプトテンプレート

LLMに渡す文章の「ひな型」を定義します。
{context} と {question} が変数として入り込み、その後「回答:」を続ける形で LLM への入力が最終生成されます。

改善ポイント

簡単な検索エンジンを作成しましたが、ここから改善するとしたら以下の様なことが考えられます。

プロンプト設計

現在はシンプルな「コンテキスト + 質問 → 回答」というテンプレートを使っていますが、詳細な指示を追加することで、LLMの回答品質を向上させることが期待できます。たとえば、
「社内規定に基づいて正確に回答してください」
「回答がない場合は「該当する情報が見つかりませんでした」と答えてください」
など、追加の指示を明示することで、誤回答やhallucinationを減らす効果が考えられます。

メタデータの活用

ドキュメントの種類や作成日、バージョンなどのメタデータをFAISSに格納し、検索時にフィルタリングをすることができれば、より正確な情報のみを抽出しやすくなります。

パラメータ調整

chunk_size, chunk_overlapなどのパラメータをタスクに応じて変えることで品質向上が期待できます。

カスタムのRetrieval ChainやQA Chain

複数ドキュメント間の要約やクロスドキュメント検索をしたうえで回答する仕組みを作ると、より横断的な知識が必要な質問への対応力が上がります。(stuff, map_reduce, refine などのQA Chainのモードがある)

ログ・モニタリングの充実

どんなクエリが来てどんな回答をしたのか、LLMがどのようなステップで回答したのかなど、**LLMの挙動を記録する仕組み(ロギングや監査ログ)**があると良いかと思います。

最後に

まずは実装して触ってみたい方向けに記事を書きました。

Discussion