オープンソースのRAGソリューション「RAG-SaaS」を試す
ここで知った。どうやらLlamaIndex製。
GitHubレポジトリ
特徴など
RAG SaaS
RAGソリューションを迅速にシップ⚡
referred from https://github.com/adithya-s-k/RAG-SaaS検索拡張生成(RAG)とエージェントベースのアプリケーションのためのエンドツーエンドのSaaSソリューション およびエージェントベースのアプリケーション。
機能
- 🔐 ベーシック認証
- 💬 チャット履歴の追跡
- 🧠 複数のRAGのバリエーション
- 基本的なRAG
- 2つの追加構成
- 👨💼 管理ダッシュボード
- 📥 データ取り込み
- 📊 監視
- 👁️ 観測可能性
- 🔄 RAG設定の切り替え
- 🗄️ PDFアップロードのためのS3統合
- 🐳 Docker / Docker Compose による容易なデプロイ
技術スタック
- 🦙 LlamaIndex: RAGパイプラインの構築と調整用
- 📦 MongoDB:通常のデータベースとベクトルデータベースの両方として使用
- ⚡ FastAPI:バックエンドAPIフレームワーク
- ⚛️ Next.js: フロントエンドフレームワーク
- 🔍 Qdrant: 効率的な類似性検索のためのベクトルデータベース
- 👁️ Arize Phoenix: RAGシステムの監視/評価を行うための観測プラットフォーム
🌟どうしてRAG-SaaSなのか?
信頼性の高いRAGシステムの構築には、時間と労力がかかります。RAG-SaaSを利用すれば、開発者は>RAGパイプラインの微調整や開発に集中でき、アプリケーションとしてパッケージ化することに頭を悩ませる必要がなくなります。LlamaIndexのcreate-llamaを基盤として構築されたRAG-SaaSは、RAGベースのプロジェクトに強固な基盤を提供します。
RAGの構築だけでなく運用も含めてフルパッケージ化されたRAGソリューションという感じ。
なお、ライセンスはデュアルライセンスになっている。
- 学生、開発者、個人向け: Apache License 2.0
- 企業および商用利用の場合: GPL v3.0
Getting Started
ローカルのMac上で試してみる。
まずレポジトリクローン。
$ git clone https://github.com/adithya-s-k/RAG-SaaS.git && cd RAG-SaaS
環境設定を行っていく。フロントエンド・バックエンド・docker-compose.yamlでそれぞれ環境変数の設定が必要なのだが、
- フロントエンド・バックエンドとdocker-compose.yamlで環境変数が冗長
- docker-compose.yamlで設定しているならそれでいいのでは?と思ったけど、それだけだとダメなものがあった。
- URLを設定する箇所が複数あって、その際のホスト名を、dockerネットワーク内でのもの(コンテナ名)にすべきなのか、外から見たもの(今回の例だとlocalhost)にすべきなのかに迷う
- コンテナ内から"localhost"でアクセスするためにはhost.docker.internalにしないといけないはず
- ブラウザでアクセス(フロントエンドが生成するURLでバックエンド参照する場合とか)するには"localhost"にしないといけないはず
あたりがあって、結構試行錯誤した。なので一応動いたというだけで、間違っている可能性はある点についてはあらかじめご了承を。
フロントエンド
$ cp -pi frontend/.env.example frontend/.env
NEXT_PUBLIC_SERVER_URL=http://localhost:8000
NEXT_PUBLIC_CHAT_API=${NEXT_PUBLIC_SERVER_URL}/api/chat
バックエンド
$ cp -pi backend/.env.example backend/.env
MODEL_PROVIDER=openai
MODEL=gpt-4o-mini
EMBEDDING_MODEL=text-embedding-3-small
EMBEDDING_DIM=1536
CONVERSATION_STARTERS="RAGについて教えて。\nLlama Indexについて教えて。"
OPENAI_API_KEY=sk-XXXXXXXXXX
STREAM_TIMEOUT=60000
QDRANT_URL=http://localhost:6333
QDRANT_COLLECTION=default
QDRANT_API_KEY=
FILESERVER_URL_PREFIX=http://localhost:8000/api/files
APP_HOST=0.0.0.0
APP_PORT=8000
SYSTEM_PROMPT='あなたは、ユーザからの質問に答える、親切な日本語のアシスタントです。'
SYSTEM_CITATION_PROMPT='You have provided information from a knowledge base that has been passed to you in nodes of information. Each node has useful metadata such as node ID, file name, page, etc. Please add the citation to the data node for each sentence or paragraph that you reference in the provided information. The citation format is: . [citation:<node_id>]()
Example:
We have two nodes:
node_id: xyz
file_name: llama.pdf
node_id: abc
file_name: animal.pdf
User question: Tell me a fun fact about Llama.
Your answer:
A baby llama is called "Cria" [citation:xyz]().
It often lives in desert [citation:abc]().'
JWT_SECRET_KEY=jwt_secret_key
JWT_REFRESH_SECRET_KEY=jwt_refresh_secret_key
MONGODB_URI=mongodb://admin:password@mongodb:27017/
MONGODB_NAME=RAGSAAS
ADMIN_EMAIL=admin@example.com
ADMIN_PASSWORD=examplepassword
ARIZE_PHOENIX_ENDPOINT=http://localhost:4317
docker-compose.yaml
(snip)
backend:
(snip)
ports:
- '8000:8000'
environment:
# MongoDB Configuration
MONGODB_NAME: RAGSAAS
MONGODB_URI: mongodb://admin:password@mongodb:27017/
# Qdrant Configuration
QDRANT_COLLECTION: default
QDRANT_URL: http://qdrant:6333
# QDRANT_API_KEY:
OPENAI_API_KEY: sk-XXXXXXXXXX
# Backend Application Configuration
MODEL_PROVIDER: openai
MODEL: gpt-4o-mini
EMBEDDING_MODEL: text-embedding-3-small
EMBEDDING_DIM: 1536
FILESERVER_URL_PREFIX: http://backend:8000/api/files
SYSTEM_PROMPT: 'あなたは、ユーザの質問に答える、親切な日本語のアシスタントです。'
APP_HOST: 0.0.0.0
APP_PORT: 8000
ADMIN_EMAIL: admin@example.com
ADMIN_PASSWORD: examplepassword
JWT_SECRET_KEY: jwt_secret_key
JWT_REFRESH_SECRET_KEY: jwt_refresh_secret_key
ARIZE_PHOENIX_ENDPOINT: http://arizephoenix:6006
depends_on:
(snip)
frontend:
(snip)
ports:
- '3000:3000'
environment:
NEXT_PUBLIC_SERVER_URL: http://localhost:8000
NEXT_PUBLIC_CHAT_API: http://localhost:8000/api/chat
depends_on:
(snip)
起動
$ docker compose up -d
✔ Network ragsaas-network Created 0.0s
✔ Volume "rag-saas_mongodb_data" Created 0.0s
✔ Container qdrant Started 0.5s
✔ Container mongodb Started 0.5s
✔ Container arizephoenix Started 0.5s
✔ Container backend Started 0.4s
✔ Container frontend Started
色々立ち上がっている。
$ docker compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
arizephoenix arizephoenix/phoenix:latest "/usr/bin/python3.11…" arizephoenix 18 seconds ago Up 17 seconds 0.0.0.0:4317->4317/tcp, 0.0.0.0:6006->6006/tcp, 9090/tcp
backend ragsaas/backend:latest "python main.py" backend 18 seconds ago Up 17 seconds 0.0.0.0:8000->8000/tcp
frontend ragsaas/frontend:latest "docker-entrypoint.s…" frontend 17 seconds ago Up 17 seconds 0.0.0.0:3000->3000/tcp
mongodb mongo:latest "docker-entrypoint.s…" mongodb 18 seconds ago Up 17 seconds 0.0.0.0:27017->27017/tcp
qdrant qdrant/qdrant:latest "./entrypoint.sh" qdrant 18 seconds ago Up 17 seconds 0.0.0.0:6333-6334->6333-6334/tcp
ブラウザで3000番ポートにアクセスするとトップ画面が表示されるので、右上の「Sign in」をクリック。
ADMIN_EMAIL/ADMIN_PASSWORDに設定したメアド・パスワードでログイン。
ログインした。トップ画面はこんな感じ。で、入力したくなるところだけど、一旦我慢。
ブラウザで別タブを開いて、QdrantのUI http://localhost:6333/dashboard
にアクセスすると、Qdrantのコレクションの一覧画面が表示される。
設定では"default"という名前のコレクションを指定していたはずだが、作成されていない。この作成プロセスが考慮されているのかがわからないのだが、この設定がないとチャットが失敗する。ということで手動で作成する。
Qdrantのコンソールから以下を入力。
PUT collections/default
{
"vectors": {
"size": 1536,
"distance": "Cosine"
}
}
実行
右側のペーンに結果が出力される。こんな感じで出力されていればOK。コレクション画面に戻る。
"default"コレクションが作成されている。これでQdrantはOK。
RAG-SaaSの画面に戻ってチャットしてみる。せっかくなのでスターターメッセージをそのまま使ってみる。
こんな感じでレスポンスが返ってくる(実際にはストリーミング)。次の質問へのヒントなども出力されているので、あとはチャットしていけばいい。なお、日本語IMEの変換確定ENTERで送信されてしまうのはもはやお約束。
実行したチャットはスレッドとして管理される。左上のメニューをクリックすると、ChatGPTライクな感じでスレッドになっているのがわかる。
RAGをやってみる。チャット画面からはファイルアップロードするようなメニューが見当たらないが、管理者ダッシュボードから行える。管理者ダッシュボードはhttp://localhost:3000/admin
でアクセスできる。
管理者ダッシュボードでできることは以下の3つ
- ユーザ管理
- RAGのドキュメント登録
- RAGの設定
ユーザ管理は割愛。まず、RAGの設定画面を見てみる。
この画面ではシステムプロンプトとスターターメッセージを設定できる。
次に、RAGの設定画面を見てみる。
ここでRAGのコンテキストとなるドキュメントをアップロードできる。ドキュメントについてはチャンク分割等の設定を行いたいところではあるが、そのあたりは現時点ではComing Soonになっている。とりあえず適当なPDFをアップロードしてみる。
神戸市が公開している観光に関する統計・調査資料の「令和4年度 神戸市観光動向調査結果について」というPDFを使う。
PDF直
アップロードしたら"Ingest File"をクリック
どうやら30個のチャンクに分割された様子。
チャット画面に戻って、新しいスレッドを開いて、先程のPDFに関する質問を投げてみる。
神戸までの主な利用交通機関の内訳を教えて。
結果はこんな感じ。ドキュメントの内容を参照して回答していることがわかる。
"Sources: 1"となっている箇所をクリック。
ここにソースとなるドキュメントが表示されるようだが、読み込みに失敗している模様。
コードを読んでみた限り、どうもアップロードしたデータ先パスが初回起動時には存在しなくて、FastAPIで公開できていない様子。なので、docker compose restartしてやれば次回以降は正しく表示される。
なお、アップロードしたドキュメントの管理、みたいなメニューは現時点ではないので、後からアップロードしたドキュメント一覧を確認するには直接パスを見に行くしかなかったり、チャットのスレッドで読み込まれるドキュメントを選択するといったこともできないので、複数ドキュメントをアップロードするとどうもすべからく横断的に検索する模様。
最後に、実行したチャットの内容はArize Phoenixでトレーシングを確認できる。Arize Phoenixの管理画面はhttp://localhost:6006
でアクセスできる。 以下のようにプロジェクト一覧が表示される。
プロジェクトは3つあるが、"default"はArize Phoenixのデフォルトで使わない。チャットのトレーシングは"RAGSAAS-Chat"で、ドキュメント登録時のトレーシングが"RAGSAAS-Data-Ingest"になる。"RAGSAAS-Chat"をクリックすると、トレーシングが表示される。
まとめ
LlamaIndexがコアとして使われているというところでRAG-SaaSを取り上げてみた。公開されたばかりだし、個人の方によるプロジェクトという点を踏まえると企業が作ったOSSとは機能的に物足りない点が多い。なので、社内向け・プライベートの両面でも現時点では採用しにくいとは思う。
ただ、RAGをコアにしたプラットフォームサービスを作って運用まで考えると、最低限これぐらいの機能は求められるライン、というものさしにはなると思うし、色々周辺コンポーネントのインテグレーションも取捨選択が必要だと感じる。