2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Ubuntu上のOllama+Open WebUIで構築したローカルLLMでRAG(後編)

Posted at

はじめに

前編では環境構築から始め、ファイルを添付してRAGが問題なくできるところまでを確認しました。
今回はOpen WebUIでのRAGがどのように実行されているのか、コードを実際に見ていきます。

確認したバージョン
・Open WebUI Ver. 0.6.2
・Ollama Ver. 0.6.1

スクリーンショット 2025-04-09 10.53.55.png

ファイルアップロードのコード実装

Open WebUIには事前に貯めておきたい知識を置いておくナレッジベースという機能があります。
ファイルを添付したときにどのように処理されているのかを確認していきます。

なお、公式のドキュメントにはドキュメントパース部分はget_loader関数を見ろ、書いてあります。

スクリーンショット 2025-03-22 9.39.56.png

ただしそのリンクは古くなっており、現在(v0.6.2)のコードではありません。

この部分を追うために、テストファイルを添付した際のログから確認していきます。

テスト用のナレッジベースを作り、テストファイルとしてはRFC3501のPDF版であるrfc3501.txt.pdfを添付します。

スクリーンショット 2025-03-24 21.12.18.png

アップロードの際にどのような処理が行われているのか、ログを確認します。

docker container logs open-webui

スクリーンショット 2025-04-02 18.28.18.png

画像の1行目にopen_webui.routers.files:upload_fileという記載があります。
これはopen_webui/routers/files.pyupload_file関数が呼ばれていることを意味します。
GitHubのコードで言うとこの81行目です。

スクリーンショット 2025-04-13 0.21.18.png

このupload_file関数の一番重要なファイルを処理する部分は上記のリンク部分より少し進んだprocess_file関数(125行目)です。

スクリーンショット 2025-04-03 21.36.50.png

このファイルを処理していると思われるprocess_file関数はopen_webui/routers/retrieval.pyで定義されています。
GitHubのコードはこの946行目です。

スクリーンショット 2025-04-08 18.24.55.png

この関数を読み進めるとloaderを読み込み(1021行目)、そのload関数を使ってドキュメントを読み込んでいること(1030行目)がわかります。

スクリーンショット 2025-04-08 18.27.08.png

そこでさらにLoaderクラスを確認していきますと、load関数の具体的な中身が見えてきます。
Loaderクラスを定義しているのはopen_webui/retrieval/loaders/main.pyであり、コードはこの169行目です。

スクリーンショット 2025-04-08 18.29.16.png

_get_loader関数では、self.engineの値に分かれてその処理内容が記載されています。

スクリーンショット 2025-04-05 0.11.15.png

デフォルトの設定だとself.engineにはtikadocument_intelligenceなどは入らないため、一番下のelseに分岐します。
このself.engineの値は管理者パネルの「ドキュメント」の「Content Extraction Engine」に当たります。

スクリーンショット 2025-04-08 18.30.30.png

「Content Extraction Engine」に「デフォルト」を指定した場合は、一番下のelseに行くため、コードにあるように拡張子ごとに処理が分岐されています。

例えば拡張子が「.pdf」になっているPDFファイルの場合を見てみますと、「PyPDFLoader」つまりLangChainのpyPDFLoaderを使っていること(245行目)がわかります。

スクリーンショット 2025-04-08 18.33.45.png

公式のget_loader関数のリンクは古くなっていると先程述べましたが、backend/open_webui/retriebal/loaders/main.pyの192行目_get_loader関数が2025年4月のVer0.6.2時点では最新の箇所ということになります。

話をprocess_file関数に戻します。
添付したファイルをこのloader関数に従ってテキストにした後の処理です。
次に大事な処理としてsave_docs_to_vector_db関数があります。
GitHubのコードはこの1073行目です。

スクリーンショット 2025-04-08 18.38.47.png

これは先ほどのログ(docker container logs open-webui)の2行目でも表示されていた関数になります。
open_webui.routers.retrieval:save_docs_to_vector_dbの部分)

このsave_docs_to_vector_db関数が実際に定義されているのは、ログにもあるように
open_webui/routers/retrieval.pyです。
GitHubのコードだとこの785行目になります。

スクリーンショット 2025-04-08 18.41.49.png

save_docs_to_vector_db関数を読み進めていきますと、テキスト分割において、チャンクの設定(chunk_sizechunk_overlap)が使われています。

スクリーンショット 2025-04-08 18.43.17.png

管理者パネルの「ドキュメント」内で設定できる「チャンクサイズ」や「チャンクオーバーラップ」がここで使われていることが確認できます。

単語 意味
チャンクサイズ Text SpliterCharactorの場合は、RecursiveCharacterTextSplitterを使っており、文字数と同値
チャンクオーバーラップ Text SpliterCharactorの場合は、連続したチャンクで被る文字数。チャンクオーバーラップは文字列分割で大事な情報が落ちないように、文脈を保持するために使用します

スクリーンショット 2025-04-02 21.12.44.png

さらにsave_docs_to_vector_db関数を読み進めていくとembeddingstextsを与えています。

スクリーンショット 2025-04-08 18.46.12.png

これは埋め込みモデル(embedding model)にチャンクサイズで分割したテキストを与えていることがわかります。
よく読むとreplace("¥n", " ")も実行しているので、改行は半角空白に置き換わっていることもわかります。

つまり改行はOpen WebUIの埋め込みモデルでは空白と同じになるということがここからわかります。

このベクトル化した結果をDBに入れて、save_docs_to_vector_db関数は終わります。

スクリーンショット 2025-04-08 18.47.59.png

以上で、ファイルをアップロードした際にOpen WebUIでどのような処理が行われるのかを見てきました。

簡単にファイルアップロードの流れをまとめると

  1. 添付されたファイルを拡張子に沿ってパースする
  2. 埋め込みモデル(embedding model)を使ってベクトル化する
  3. ベクトル化したものをDBに保存する

という形になります。

RAGのコード実装

次にRAGを実施しているときのスクリプト部分を確認します。
「#」からすでに作成されたナレッジベース内のコレクションを選択し、RAGを動かした時のログを見ていきます。
例では「人物紹介」というコレクションを使っています。この中身は前編で解説しているので、詳細が気になる人はそちらを見てください。

スクリーンショット 2025-04-12 21.53.06.png

ログは以下です。

docker container logs open-webui

スクリーンショット 2025-04-12 21.56.14.png

大事なのは、ログの4行目に表示されているopen_webui.retrieval.utils:query_docです。
これはopen_webui/retrieval/utils.pyの76行目に当たります。

スクリーンショット 2025-04-03 21.43.55.png

見るとわかりますが、この関数のメインの処理はVECTOR_DB_CLIENT.searchだけです。
この関数を追う前にquery_doc関数がどこから来たのかを先に確認します。

これはopen_webui/routers/retrieval.pyquery_doc_handler関数から呼ばれています。
こちらのGitHubのコードはこの1535行目です。

スクリーンショット 2025-04-12 22.05.47.png

このquery_doc_handler関数で大事なことは2点です。
(1) 管理者パネルの「ドキュメント」の「ブリッジ検索」がONになっている場合は1541行目のif文がtrueになるため、呼ばれる関数はquery_doc関数ではなく、query_doc_with_hybrid_search関数になります。

スクリーンショット 2025-04-12 23.38.22.png

「ブリッジ検索」をONにした場合はBM25アルゴリズムを使ったり、リランキングしたりしてコレクションの検索をするようになります。記事が冗長になるのでこれ以上は踏み込みませんが、詳細が気になる人は下のquery_docと同じようにコードを読んでいけば分かると思います。

(2)query_doc関数に渡しているのは以下の内容です。

変数 解説
collection_name これはナレッジベースで作られたコレクションの名前です
query_embedding 埋め込み関数(embedding function)にクエリを渡したもの、つまりクエリをベクトル化したものです
k これは管理者パネルの「ドキュメント」内の「トップK」で指定できる数字で、検索の上位K件を結果に用います
user これもそのままユーザです(一般ユーザと管理者ユーザでできることが異なるので、この値を渡していると思われますが要調査)

この(1)(2)の情報を踏まえて、query_doc関数内のVECTOR_DB_CLIENT.searchを追っていきます。

VECTOR_DB_CLIENTopen_webui/retrieval/vector/connector.pyです。
GitHubのコードだとここですが、そんなに中身があるわけではなく、VECTOR_DBで値を分岐させているだけです。

スクリーンショット 2025-04-09 11.11.17.png

VECTOR_DBの値が大事ですが、これは一番上に書かれているようにopen_webui/config.pyに書かれているので、そちらにいきます。GitHubのコードはこの1616行目に書かれています。

スクリーンショット 2025-04-12 21.29.17.png

これは記載の通りOSの環境変数のVECTOR_DBの値を使います。
設定されていない場合は第二引数のchromaが指定されるということなので、デフォルトだとDBとしては使うものはChromaになります

なお、この環境変数の値は公式ドキュメントにも記載があり、コードと同じ挙動が書かれていることが確認できます。

スクリーンショット 2025-04-12 21.37.37.png

それではVECTOR_DB_CLIENT.searchに話を戻しましょう。

デフォルトではDBはChromaを用いるのでconnector.pyの記載に従い、open_webui/retrieval/vector/dbs/chroma.pyを見にいきます。
GitHubのコードだとこの66行目search関数が記載されています。

スクリーンショット 2025-04-12 21.40.50.png

71行目では引数に与えられたコレクション名からDB内に保存されているコレクションを選び、73行目でそのコレクションの検索をしています。
Chroma DBのコードの詳細は別の記事に譲りますが、ここではクエリをベクトル化したものを渡して、近しい文書を検索しているだけです。

80行目にも記載がありますが、コサイン類似度を使ってクエリ(をベクトル化したもの)とコレクションに保存された文書(をベクトル化したもの)の類似度を出していることがわかります。1

以上で、Open WebUIでRAGがどのような処理が行われるのかを見てきました。

大きな流れとしては

  1. 埋め込み関数を使ってクエリをベクトル化する
  2. コサイン類似度を使って、クエリに近い文章を検索する
  3. 検索で得られた上位K件の文章を使い、LLMに渡して回答生成をする
    という形になります。

終わりに

似たようなことをやっている方もいますが、UIが少し古くなっているのと個人的にはLoader以外も気になったので色々調べてみました。

Open WebUI+OllamaでローカルLLMを構築し、RAGをやりたい人の参考になれば幸いです。

もし記事の内容で誤りなどがあれば、コメント欄でご指摘ください。

  1. コサイン類似度についてよくわからない方は別の記事を参照してください。

2
1
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
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?