🐈

# ローカルLLM+LangchainでRAGする

に公開

ローカルLLM便利やんということでいろいろとやってみています。

これまでの記録:
https://zenn.dev/hi/articles/b85edebde2caa1

https://zenn.dev/hi/articles/b2424189179cd7

今回はRAGにチャレンジ。ということで、LANGCHAINのドキュメント進めるかぁと思っていたら、なんと日経に連載されている方の記事が公開されていました。

なので詳しく知りたい方は↓の方へ!

https://www.ntt-tx.co.jp/column/250624/

私はJupyterLabで動かしているので、コマンドラインにはせずそのまま動かしたりちょっと深く掘ってみたりという感じです。

まずインストールなど

まずはインストールです。

$ uv pip install langchain_ollama langchain_chroma langchain_text_splitters langchain_community cryptography 

でRAGに使うファイル。今回は次のファイルが面白そうだったので使ってみました。帝国データバンクの花火の有料化のレポートです。

https://www.tdb.co.jp/report/industry/20250728-hanabi25y/

本とはオルツの調査を使いたかったが、なぜか PyPDFLoaderでエラーが出ました。

セッティング

モデルなどのセッティングします。今回はRAGということで、Embeddingモデルをまずはダウンロードします。

$ ollama pull nomic-embed-text

色々インポートします。

# LangChain core
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough


# LangChain その他モジュール
from langchain_community.document_loaders import DirectoryLoader, PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_chroma import Chroma
from langchain_ollama import OllamaLLM, OllamaEmbeddings

モデル、エンベディングモデル、ベクトルデータベースをセッティング。

model = OllamaLLM(model='gemma3')
emb = OllamaEmbeddings(model='nomic-embed-text')
db = Chroma(collection_name='langchain', embedding_function=emb, persist_directory='./chroma_db')

ファイル読み込みとデータベースへの登録

まずはファイルを読み込みます。

file_path = './data/hanabi.pdf'
loader = PyPDFLoader(file_path)
docs = loader.load()
print(docs)

これで、3ページ分のメタデータやらなんやらが見れるようになります。リストに入っているので、次のような感じで1ページずつデータは見れます。

print(docs[0].page_content)

次に文章を分割して、dbに入れます。

txt_split = RecursiveCharacterTextSplitter(chunk_size=100, chunk_overlap=25, separators=['\n\n', '。'])
splits = txt_split.split_documents(docs)
db.add_documents(splits)
print(splits)

ここで、どんな感じにdbに格納されるかが観察できます。
ページ内容だけ見ようと思うと次のような感じで観察できます。

for num, s in enumerate(splits):
    print(num, s.page_content)

dbの中身も次のような感じでチラ見できるようです。

db._collection.peek()

RAGしてみる

ここからは、ちょっと、パラメータを触ることでどう違いが出るのかを試してみました。しかしPDFが3ページと短かったので、あまり効力なしでした。次回、いろいろファイルを混ぜたりして使いたいです。

ret1 = db.as_retriever(search_kwarg={"k": 1})
ret2 = db.as_retriever(search_type='similarity', search_kwargs={'k': 5})
ret3 = db.as_retriever(search_type='mmr', search_kwarg={'k': 5, 'lambda_mult': 0.5})
for name, rt in (('normal', ret1), ('sim', ret2), ('mmr', ret3)):
    ans = rt.invoke('花火大会の有料席の平均価格は?')
    print(f'---- {name} ----')
    for d in ans:
        print(d.metadata['page'], d.page_content[:60].replace('\n',''))

ちなみにこのコードはRAGに使われるために選ばれた文章が見れます。

まとめ

という訳で、RAGにチャレンジしてみました。
4Bでもいい感じですね。この辺り運用費用が気になるところですが、APIより安く済むならいいなぁと思いましたが、どうなんでしょうか?

あと、今回は初めてCHATGPTのSTUDYモードを使って学習しました。なんか質問とか入れてきて、おもろい感じで学べました。

次回のアクションを考えてというと示唆に富んだものとなりました。新しい学習体系凄い。

あと、CHATGPTとRAGするか直接データをプロンプトに入れるか、話していたら結構盛り上がってよかったです。RAGにするメリットも分かって良かったです。

次に続く。

Discussion