はじめに
LLM(大規模言語モデル)という言葉をよく聞くけれど、実際どう動くのかよくわからない。
そこで、まずは社内ドキュメントを活用した簡易的なQAシステムを、ダミーLLMで動かす例を使って理解を深めました。
全体の構成概要
[ユーザー質問] → ベクトル化 → [FAISSインデックス] → 類似文書を検索 → contextとして連結 → LLMに渡す
ステップ | 処理内容 |
---|---|
ベクトルDB読込 |
db = FAISS.load_local(...) で保存済みインデックスを読み込む |
質問から検索 |
similarity_search(question) で類似文書を取得 |
文書連結 |
"\n".join(...) で文書を一括テキスト化 |
回答生成 |
chain.run(...) でLLMから回答を得る(今回はダミー) |
1. ファイルを自然言語で解釈できるようにする(ベクトル化)
ベクトルストアの読み込みと類似検索
以下のコードは、ユーザーの質問をもとに社内文書の中から類似文を取り出し、それをひとつの文章として連結します。
embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
db = FAISS.load_local("rag_app/faiss_index", embeddings, allow_dangerous_deserialization=True)
docs = db.similarity_search(question)
context = "\n".join([doc.page_content for doc in docs])
ここで得られる context
が、質問への回答の土台となる情報になります。
2. 取得した情報をLLMで処理(今回はダミー応答)
DummyLLM
を使用し、どんな質問に対しても固定文を返すようにしています。
class DummyLLM(LLM):
def _call(self, prompt, stop=None):
return "(ダミー応答)これは社内資料の内容に基づく自動回答です。"
この llm
を以下のように LLMChain
に渡しています:
llm = DummyLLM()
chain = LLMChain(llm=llm, prompt=prompt)
そして、実際の get_answer()
の中では以下のように呼ばれます:
def get_answer(question):
docs = db.similarity_search(question)
context = "\n".join([doc.page_content for doc in docs])
answer = chain.run({"context": context, "question": question})
return answer
3. Pythonにおけるクラスとインスタンス生成
llm = DummyLLM()
この形式は Python における標準的なインスタンス生成方法です。Javaなどと違い、new
キーワードは使いません。
4. 今回省いているステップ
今回は事前にベクトル化されたデータを rag_app/faiss_index
から読み込んでいます。
つまり、ファイルを読み込み→分割→ベクトル化→保存のステップは省略されています。
5. 今後の発展(実運用への置き換え)
OpenAI API を使う場合:
from langchain.llms import OpenAI
llm = OpenAI(model_name="gpt-3.5-turbo", temperature=0)
.env
ファイルに OPENAI_API_KEY
を設定する必要があります。
ローカルLLMを使う場合:
たとえば llama.cpp
や GPT4All
, LocalGPT
などを導入すれば、
社内データを外部に出さずに回答生成が可能です。
おわりに
この検証で、LLMとベクトル検索の基本的な構成(RAG: Retrieval-Augmented Generation)の流れを把握できました。
今後は、実際の社内文書で検証する/ダミーから実稼働モデルに切り替えるといったステップで改善していく予定です。
今回のソース箇所
質問の回答で以下の画面からの回答箇所です。
画面は下記で対応しています。
https://qiita.com/kenji123/items/051a3e6282bab7e62e3a
以下の回答で動く部分です。
from langchain.embeddings import HuggingFaceEmbeddings
from langchain_community.vectorstores import FAISS
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
from langchain.llms.base import LLM
from dotenv import load_dotenv
load_dotenv()
# ローカル埋め込みモデル(無料)
embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
db = FAISS.load_local("rag_app/faiss_index", embeddings, allow_dangerous_deserialization=True)
# ローカルLLM(ダミー応答用)
class DummyLLM(LLM):
def _call(self, prompt, stop=None):
return "(ダミー応答)これは社内資料の内容に基づく自動回答です。"
@property
def _llm_type(self):
return "dummy"
llm = DummyLLM()
prompt = PromptTemplate.from_template("以下の文章を参考に質問に答えてください。\n\n{context}\n\n質問: {question}\n\n回答:")
chain = LLMChain(llm=llm, prompt=prompt)
def get_answer(question):
docs = db.similarity_search(question)
context = "\n".join([doc.page_content for doc in docs])
answer = chain.run({"context": context, "question": question})
return answer