Introduction
前回 は vLLM + Open WebUI によるローカル LLM 環境を構築したが、次は OpenSearch を使って RAG を試してみる。
RAG に必要なのは下記。
- Retrieval
- 質問からセマンティックに意味を理解し必要なデータを取ってくる
- ベクトルデータベース (Vector Database)
- 埋め込みモデル (Embedding Model)
- 再ランカー (Re-ranking、Rerank)
- 検索結果のランクを調整する
- 質問からセマンティックに意味を理解し必要なデータを取ってくる
- Augmented
- プロンプトを補完するようなコンテキストを前段の結果から付与し最終的なプロンプトを生成
- Generation
- プロンプトから応答を生成
- LLM
- プロンプトから応答を生成
サンプルコードは https://github.com/takuya-takeuchi/Demo/tree/master/AI/RAG/LlamaIndex/00_GetStarted。
How to do?
vLLM か Ollama をモデルの推論に使えるが今回は Ollama を採用。
Ollama は前回セットアップ済みなので、 OpenSearch、埋め込みモデルを用意し、python スクリプトから RAG の性能を確認してみる。
1. OpenSearch のインストール
docker で自動起動も含めて設定。$OPENSEARCH_INITIAL_ADMIN_PASSWORD には、OpenSearch の管理者ユーザのパスワードを設定する。
パスワードは 8 文字以上で、大文字、小文字、数字、および強力な特殊文字をそれぞれ1文字以上含む という条件を満たすこと。
さもなくば、起動時にエラーになる (1敗目)P@ssword123 は条件を満たしているが、 P@ssword 部分が頻出ワードのため弱いパスワードとして判断されて弾かれる (2敗目)。-e "plugins.security.disabled=true" を外すと、https を強制され、後述の RAG で詰む (3敗目)
1 | $ mkdir ~/.cache/opensearch |
ホスト側の公開ポートはお好みで。
しばらくしたら起動が完了するので疎通確認を行う。
1 | $ curl -X GET "http://localhost:9200" -H "Authorization: Basic $(echo -n "admin:${OPENSEARCH_INITIAL_ADMIN_PASSWORD}" | base64)" --insecure |
GUI が欲しいなら opensearchproject/opensearch-dashboards も起動する。
2. データの準備
LLM のモデルにもよるが、モデルがカバーしていない知識に関するデータを用意する。
今回は 2026年度の JRA (日本中央競馬会) 主催の重賞レースの情報を TSV 形式で 1 ファイル用意した。
1 | 月日 グレード レース名 競馬場 性齢 コース 距離 優勝馬 騎手 |
これを適当なフォルダに放り込む (今回は documents/日本/JRA/重賞.tsv として用意)。
3. スクリプトの用意
下記のコードを用意。
1 | # -*- coding: utf-8 -*- |
処理の流れは下記となる。
- 埋め込みモデルの初期化
- LLM モデルの初期化
- OpenSearch の接続クライアント初期化
- 入力データの指定をチェック
4.1. ドキュメントフォルダを指定していれば、指定フォルダを再帰的に検索し csv/tsv を読み取りデータベースに登録する形式に整形し、埋め込みモデルを使用してベクトルデータとして登録及びインデックスの作成
4.2. ドキュメントフォルダを指定しない場合は、既にデータベースにベクトルデータが登録済みだと判断し、データベースからインデックスの作成 - インデックスを使用してクエリから検索を実行
- 有効な参考情報があるかどうかをチェック
6.1. 有効な参考情報がある場合は、プロンプトに参考情報と元のクエリを埋め込み LLM で最終的な回答を生成
6.2. 有効な参考情報がない場合は、元のクエリをそのまま使って、LLM で回答を生成
4. テスト
下記のモデルを使用。
- 埋め込みモデル
- kun432/cl-nagoya-ruri-large
- 日本語に対応した埋め込みモデル。名古屋大学情報学部大学院情報学研究科で開発されたモデル。Apache 2.0 License
- kun432/cl-nagoya-ruri-large
- LLM モデル
- gemma4:26b
- 2026年4月発表
- gemma4:26b
いずれのモデルも Open WebUI 経由で事前にプルしておくこと。
各種サーバは下記を使用。
- Ollama
- OpenSearch
事前知識のみで未知の質問に回答
まず、入力データの登録なしで推論を実行。
1 | $ python qa.py --embedding_model kun432/cl-nagoya-ruri-large \ |
ハルシネーションもなく素直な回答を返してくれました。
事前知識+参考情報で未知の質問に対して回答
続いて、先述の競馬データを登録し、同じ質問を実行。
1 | $ python qa.py --embedding_model kun432/cl-nagoya-ruri-large \ |
回答の大枠は変わりませんが、入力されたデータが競馬に関することを理解しています。
また、競馬に関する情報を誤って使っていることもなし。
これは事前に用意した下記のテンプレート qa_template.txt が役に立っていると推測。
1 | 以下は検索で得られた参考情報です。 |
最後に競馬に関する質問を 2 つ実施。
事前知識+参考情報で参考情報のみに含まれる質問に対して回答
武豊騎手騎乗のアドマイヤテラのレースについて質問。
1 | $ python qa.py --embedding_model kun432/cl-nagoya-ruri-large \ |
正しい回答なので問題なし。
事前知識+参考情報で参考情報のみに含まれていないが関連性の高い質問に対して回答
愛知杯に関する質問だが、参考情報に入っていない 2025年のレースについて質問。
1 | $ python qa.py --embedding_model kun432/cl-nagoya-ruri-large \ |
きちんと参考情報に入っていないことを告げた上で自身の知識に基づいて回答を実施。
しかし、事前知識が間違っているのか、微妙なミスが。
- 時期
- ここ 30年は 1-3月開催。10-11月開催だったのは 1982年までで、12 月開催は一度もない
- コース
- 距離は 1400m で、1400m になったのも 2025年でそれ以前はずっと 2000m
- ダートだったのは初回開催から1969年までで、以降は芝
正解なのはグレードと競馬場。
5. まとめ
LlamaIndex の参考コードが大抵 load_index_from_storage によるローカルにインデックスを永続化を前提としてコピペの嵐で大変困ったが、何とか形になった。
