はじめに
本記事は、「Quarkus LangChain4j・MCP入門」シリーズの第4弾です。
- Quarkus LangChain4j入門:AI ServiceからLLMを呼び出す
- Quarkus LangChain4j入門:Tool CallingでJavaメソッドをLLMから呼び出す
- Quarkus MCP Server入門:Javaの業務処理をAI向けToolとして公開する
第1回から第3回では、LLMへの問い合わせ、Tool Calling、MCP Serverによる外部機能との連携を扱いました。
今回は、Quarkus LangChain4jのEasy RAGとPostgreSQLの拡張機能であるpgvectorを使用し、独自の運用ドキュメントを検索して回答を生成するRAGアプリケーションを実装します。
本記事では、RAG、Embedding、ベクトル検索の基本的な仕組みを説明したうえで、デモアプリケーションの実装と動作を確認します。
実装には、前回と同様に、オープンソースのQuarkusをベースとするIBM Enterprise Build of Quarkusを使用します。
なお、本番運用向けには、IBMによるサポートと長期ライフサイクルが提供される製品版も用意されています。
今回作成するアプリケーション
今回作成するsupport-knowledge-assistantは、Markdown形式の運用ドキュメントを検索し、障害対応に関する質問へ回答するデモアプリケーションです。
ユーザーが質問を送信すると、質問と意味の近い文書断片をpgvectorから検索し、その内容を基にChat Modelが回答を生成します。
curl -X POST http://localhost:8080/knowledge/ask \
-H "Content-Type: text/plain" \
-H "Accept: application/json" \
-d "payment-serviceでConnection timeoutが発生しています。確認項目と対応方法を教えてください。"
アプリケーションの構成は次のとおりです。
System Messageには、運用ドキュメントを根拠として回答することや、ドキュメントで確認できない情報を事実として補完しないことを記述します。
ただし、System Messageによる指示だけでハルシネーションを完全に防げるわけではありません。検索結果やモデルの出力を検証する仕組みは、実運用時に別途検討する必要があります。
アプリケーションのソースコードは、以下のGitHubリポジトリーで公開しています。
RAGとベクトル検索
RAGとは
RAG(Retrieval-Augmented Generation)は、ユーザーの質問に関連する情報を外部データソースから検索し、その検索結果をコンテキストとしてLLMへ渡すことで、回答生成を補助する手法です。
社内の運用手順書や個別システムの構成など、独自の情報に基づく回答を生成するには、それらの情報をLLMへ適切に提供する必要があります。RAGを利用することで、モデル自体を再学習させることなく、関連するドキュメントを回答生成に活用できます。
基本的な流れは次のとおりです。
- ユーザーが質問する
- 質問に関連する文書を検索する
- 質問と検索結果をLLMへ渡す
- LLMが検索結果を参照して回答を生成する
RAGでは、アプリケーションが質問に関連する文書を検索し、その検索結果を質問とともにLLMへ渡します。LLMは、渡された文書を参考に回答を生成します。
Embeddingとは
Embedding(埋め込み)とは、文章の意味的な特徴を数値の配列で表現したものです。この数値の配列をベクトルと呼びます。
「データベースへの接続がタイムアウトしました」
↓ Embedding Model
[0.021, -0.184, 0.537, ...]
意味の近い文章は、Embedding Modelによって比較的近いベクトルへ変換されます。例えば、次の2つは異なる表現ですが、意味は似ています。
Connection timeoutが発生しました
接続処理が時間切れになりました
今回使用するtext-embedding-3-smallは、標準設定では文章を1536次元のベクトルへ変換します。
ベクトル検索とは
ベクトル検索とは、質問と文書をそれぞれベクトルへ変換し、ベクトル間の距離や類似度を基に、意味の近い文書を探す検索方法です。
キーワード検索では文字列の一致が中心になりますが、ベクトル検索では表現が異なっていても、意味の近い文書を取得できる可能性があります。
質問
「DBへの接続が時間切れになりました」
検索対象の文書
「データベース接続のタイムアウトを確認する」
ただし、ベクトル検索は必ず正しい文書を返すわけではありません。文書の分割方法、検索件数、類似度のしきい値、Embedding Modelなどによって検索結果は変わります。
Chat ModelとEmbedding Modelの違い
RAGでは、主にChat ModelとEmbedding Modelを使い分けています。
| モデル | 役割 | 使用するタイミング |
|---|---|---|
| Embedding Model | 文章をベクトルへ変換する | 文書登録時、質問時 |
| Chat Model | 質問と検索結果から回答を生成する | 最終回答の生成時 |
本アプリケーションでは、次のモデルを使用します。
| 用途 | モデル |
|---|---|
| Embedding生成 | text-embedding-3-small |
| 回答生成 | gpt-4o-mini |
どちらもOpenAI API経由で利用するため、モデルをローカルへダウンロードして起動する必要はありません。
ローカル環境では、QuarkusアプリケーションとPostgreSQLおよびpgvectorを起動します。Embedding Modelは文章をベクトルへ変換し、pgvectorは生成されたベクトルを保存、検索します。
Quarkus LangChain4jのExtensionを使用すると、OpenAIのChat ModelとEmbedding Model、pgvectorとの連携を設定中心で構成できます。
さらにEasy RAGを利用することで、文書の読み込み、分割、Embedding生成、保存、検索といったRAGに必要な処理を個別に実装せず、少ないコードでRAGアプリケーションを構築できます。
RAGアプリケーションの処理
RAGアプリケーションの処理を、文書登録と問い合わせに分けて整理します。
文書登録時の処理
アプリケーション起動時に、Easy RAGが次の処理を実行します。
-
knowledgeディレクトリ内のMarkdown文書を読み込む - 文書を検索しやすい大きさの文書断片へ分割する
- 各文書断片をOpenAI Embedding APIでベクトルへ変換する
- 文書断片とEmbeddingをpgvectorへ保存する
文書を小さな単位へ分割する処理は、一般にChunkingと呼ばれます。本記事では、分割後の単位を「文書断片」と表記します。
文書断片とEmbeddingを保存し、ベクトル検索を提供する仕組みをEmbedding Storeと呼びます。本記事では、PostgreSQLの拡張機能であるpgvectorをEmbedding Storeとして使用します。
問い合わせ時の処理
ユーザーから質問を受け取ると、次の処理を実行します。
- 質問を文書登録時と同じEmbedding Modelでベクトルへ変換する
- pgvectorに保存されたEmbeddingと比較する
- 意味の近い文書断片を最大5件取得する
- 質問と文書断片をChat Modelへ渡す
- Chat Modelが回答を生成する
文書と質問は、同じEmbedding Modelと互換性のある設定でベクトル化する必要があります。
Easy RAGとは
Easy RAGは、Quarkus LangChain4jが提供するRAG向けのExtensionです。
今回の構成では、次の処理をEasy RAGが担当します。
- 指定された場所から文書を読み込む
- 文書を複数の文書断片へ分割する
- 文書断片のEmbeddingを生成する
- pgvectorへ文書断片とEmbeddingを保存する
- 質問に関連する文書断片を検索する
- 検索結果をAI Serviceへ渡す
本アプリケーションでは、独自の文書取り込み処理やRetrieverを実装しません。RAGの一連の処理を、主にapplication.propertiesの設定で構成します。
検証環境
本記事では、以下の環境でアプリケーションの動作を確認しています。
| 項目 | 内容 |
|---|---|
| OS | macOS 26.5.2 |
| CPUアーキテクチャ | Apple Silicon(arm64) |
| Java | OpenJDK 25.0.1 (GraalVM CE) |
| Maven | 3.9.10 |
| Quarkus | IBM Enterprise Build of Quarkus 3.33.2 |
| Quarkus LangChain4j | 1.7.6.redhat-00001 |
| PostgreSQL / pgvector | pgvector/pgvector:pg16 |
| コンテナ実行環境 | Podman 5.5.0 |
| Chat Model | gpt-4o-mini |
| Embedding Model | text-embedding-3-small |
使用するExtension
pom.xmlに定義した主な依存関係は以下のとおりです。
| Extension | 役割 |
|---|---|
quarkus-langchain4j-openai |
OpenAIのChat ModelとEmbedding Modelを利用する |
quarkus-langchain4j-easy-rag |
文書の読み込み、分割、登録、検索を構成する |
quarkus-langchain4j-pgvector |
pgvectorをEmbedding Storeとして利用する |
quarkus-jdbc-postgresql |
PostgreSQLへ接続する |
quarkus-rest-jackson |
REST APIとJSONシリアライズを提供する |
依存関係のバージョンは、Red Hat build of QuarkusのBOMで管理します。
プロジェクト構成
主なディレクトリとファイルは以下のとおりです。
support-knowledge-assistant/
├── compose.yaml
├── pom.xml
└── src/
├── main/
│ ├── java/dev/autonomura/knowledge/
│ │ ├── ai/
│ │ │ └── KnowledgeAssistant.java
│ │ └── api/
│ │ ├── KnowledgeAnswer.java
│ │ └── KnowledgeResource.java
│ └── resources/
│ ├── application.properties
│ └── knowledge/
│ ├── connection-timeout-guide.md
│ ├── database-connection-pool.md
│ ├── gateway-fallback-procedure.md
│ ├── incident-escalation-policy.md
│ └── payment-service-troubleshooting.md
└── test/
└── java/dev/autonomura/knowledge/api/
└── KnowledgeResourceTest.java
src/mainには、AI Service、REST API、APIレスポンスを定義する3つのJavaクラスがあります。
src/testには、REST APIの動作を確認するKnowledgeResourceTestがあります。
RAGの基本処理をExtensionへ委ねることで、アプリケーション固有のコードを少なくしています。
PostgreSQLとpgvectorの起動
compose.yamlを使用して、PostgreSQLとpgvectorを起動します。
services:
postgres:
image: pgvector/pgvector:pg16
ports:
- "5432:5432"
environment:
POSTGRES_DB: knowledge_db
POSTGRES_USER: demo
POSTGRES_PASSWORD: demo-password-local
volumes:
- pgvector-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U demo -d knowledge_db"]
interval: 10s
timeout: 5s
retries: 5
volumes:
pgvector-data:
pgvector/pgvector:pg16は、PostgreSQL 16でpgvectorを利用できるコンテナイメージです。
pgvector-dataというnamed volumeを使用するため、コンテナを再作成しても、volumeを削除しない限りPostgreSQLのデータを保持できます。
Podmanを使用する場合は、次のコマンドで起動します。
podman compose up -d
podman compose ps
Dockerを使用する場合は、podmanをdockerへ読み替えてください。
デモ用ドキュメントの作成
src/main/resources/knowledge配下に、システム運用を想定した5つのMarkdownファイルを配置します。これらは、すべてデモ用の架空データです。
| ファイル名 | 内容 |
|---|---|
payment-service-troubleshooting.md |
payment-serviceの障害調査手順 |
connection-timeout-guide.md |
Connection timeoutの一般的な調査手順 |
database-connection-pool.md |
データベース接続プールの運用手順 |
gateway-fallback-procedure.md |
gateway障害時のフォールバック手順 |
incident-escalation-policy.md |
障害のエスカレーション条件 |
複数文書から情報を取得できることを確認するため、サービス固有の手順と一般的な障害対応手順を別ファイルにしています。
例えば、payment-service-troubleshooting.mdでは接続プールの確認に触れ、詳細な手順はdatabase-connection-pool.mdへ記載しています。
アプリケーションの設定
src/main/resources/application.propertiesの設定を、OpenAI、Easy RAG、pgvector、DataSourceに分けて説明します。
OpenAIの設定
quarkus.langchain4j.openai.chat-model.model-name=gpt-4o-mini
quarkus.langchain4j.openai.chat-model.temperature=0.2
quarkus.langchain4j.openai.embedding-model.model-name=text-embedding-3-small
-
chat-model.model-nameには、回答生成に使用するChat Modelを指定します。 -
chat-model.temperatureには、生成結果のランダム性を調整する値を指定します。本デモでは回答のばらつきを抑えるため、0.2に設定します。 -
embedding-model.model-nameには、文書と質問のEmbedding生成に使用するモデルを指定します。
OpenAI APIキーは設定ファイルへ直接記載せず、環境変数で渡します。
export QUARKUS_LANGCHAIN4J_OPENAI_API_KEY=...
Easy RAGの設定
quarkus.langchain4j.easy-rag.path=knowledge
quarkus.langchain4j.easy-rag.path-type=classpath
quarkus.langchain4j.easy-rag.path-matcher=glob:**.md
quarkus.langchain4j.easy-rag.recursive=true
quarkus.langchain4j.easy-rag.max-segment-size=500
quarkus.langchain4j.easy-rag.max-overlap-size=50
quarkus.langchain4j.easy-rag.max-results=5
quarkus.langchain4j.easy-rag.ingestion-strategy=on
-
pathとpath-typeで文書の読み込み元を指定します。 -
path-matcherでMarkdownファイルだけを対象にします。 -
max-segment-sizeで1つの文書断片の最大サイズを指定します。 -
max-overlap-sizeで隣接する文書断片の重複範囲を指定します。 -
max-resultsで検索結果として取得する文書断片の最大件数を指定します。 -
ingestion-strategy=onで、アプリケーション起動時に文書を取り込みます。
max-segment-sizeとmax-overlap-sizeの単位や正確な分割動作は、使用するExtensionのバージョンに依存します。本記事では、Red Hat build of Quarkusで利用するExtensionの設定に基づいています。
pgvectorの設定
quarkus.langchain4j.pgvector.table=embeddings
quarkus.langchain4j.pgvector.dimension=1536
quarkus.langchain4j.pgvector.create-table=true
quarkus.langchain4j.pgvector.drop-table-first=false
quarkus.langchain4j.pgvector.use-index=false
quarkus.langchain4j.pgvector.register-vector-pg-extension=true
-
tableでEmbeddingを保存するテーブル名を指定します。 -
dimensionでベクトルの次元数を指定します。text-embedding-3-smallの標準設定に合わせて1536とします。 -
create-table=trueで、テーブルが存在しない場合に作成します。 -
drop-table-first=falseで、起動時に既存テーブルを削除しないようにします。 -
use-index=falseで、本デモではベクトルインデックスを使用しません。 -
register-vector-pg-extension=trueで、アプリケーション起動時にPostgreSQLのvector拡張を有効化します。
Embedding Modelを変更する場合は、出力されるベクトルの次元数を確認し、dimensionを一致させる必要があります。
DataSourceの設定
quarkus.datasource.db-kind=postgresql
quarkus.datasource.username=demo
quarkus.datasource.password=demo-password-local
quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/knowledge_db
quarkus.datasource.devservices.enabled=false
データベース名、ユーザー名、パスワードは、compose.yamlの設定と一致させます。
quarkus.datasource.devservices.enabled=falseを設定し、Quarkus Dev Servicesによるデータベースコンテナの自動起動を無効にします。これにより、compose.yamlで起動したPostgreSQLへ接続します。
AI Serviceの実装
KnowledgeAssistantは、LLMとのインターフェースとなるAI Serviceです。
@RegisterAiService
public interface KnowledgeAssistant {
@SystemMessage("""
あなたはシステム運用を支援するアシスタントです。
提供された運用ドキュメントの内容を根拠として回答してください。
ドキュメントで確認できない内容を、確認済みの事実として補完しないでください。
回答に必要な情報がドキュメントに含まれていない場合は、その旨を明示してください。
回答は簡潔かつ実務的にしてください。
存在しない文書名や出典を生成しないでください。
""")
String ask(@UserMessage String question);
}
@RegisterAiServiceを付与すると、Quarkus LangChain4jがAI Serviceの実装を生成し、CDI Beanとして登録します。
Easy RAGが有効な場合、検索された文書断片はAI Serviceへ渡されるコンテキストへ追加されます。そのため、KnowledgeAssistantには検索処理を直接実装しません。
@SystemMessageでは、次の回答方針を定義しています。
- 運用ドキュメントを根拠として回答する
- ドキュメントで確認できない情報を事実として補完しない
- 必要な情報がない場合は、その旨を回答する
- 存在しない文書名や出典を生成しない
REST APIの実装
KnowledgeAnswer
KnowledgeAnswerは、回答をJSON形式で返すためのJava Recordです。
public record KnowledgeAnswer(String answer) {
}
回答文字列を直接返さずレスポンス型を定義することで、将来、出典などの項目を追加しやすくなります。
KnowledgeResource
KnowledgeResourceは、質問を受け付けるRESTエンドポイントです。
@Path("/knowledge")
public class KnowledgeResource {
@Inject
KnowledgeAssistant assistant;
@POST
@Path("/ask")
@Consumes(MediaType.TEXT_PLAIN)
@Produces(MediaType.APPLICATION_JSON)
public KnowledgeAnswer ask(String question) {
if (question == null || question.isBlank()) {
throw new BadRequestException("question must not be blank");
}
return new KnowledgeAnswer(assistant.ask(question));
}
}
POST /knowledge/askは、text/plain形式の質問を受け取り、application/json形式の回答を返します。
空文字列または空白だけの質問は、400 Bad Requestとして拒否します。不正な入力をREST APIの境界で拒否することで、不要なOpenAI API呼び出しを防ぎます。
各クラスの責務は次のとおりです。
| クラス | 責務 |
|---|---|
KnowledgeAssistant |
LLMへの問い合わせと回答方針を定義する |
KnowledgeAnswer |
APIレスポンスの型を定義する |
KnowledgeResource |
HTTPリクエストの受付、入力チェック、AI Serviceへの委譲を行う |
REST APIのテスト
KnowledgeResourceTestでは、KnowledgeAssistantをMockitoでモックし、実際のOpenAI APIやpgvectorを呼び出さずにREST APIをテストします。
主に、次の動作を確認します。
- 正常な質問に対して
200 OKとJSON形式の回答を返す - 空白だけの質問に対して
400 Bad Requestを返す - 不正な入力では
KnowledgeAssistantを呼び出さない
テスト時は、Quarkusの設定検証を通すためのダミーAPIキーを指定し、Easy RAGによる文書取り込みを無効にします。
アプリケーションの起動
最初に、PostgreSQLとpgvectorを起動します。
podman compose up -d
OpenAI APIキーを環境変数へ設定します。
export QUARKUS_LANGCHAIN4J_OPENAI_API_KEY=sk-...
Quarkusを開発モードで起動します。
mvn quarkus:dev
ingestion-strategy=onの場合、起動時にMarkdown文書が読み込まれ、文書断片のEmbeddingが生成されます。生成されたEmbeddingと文書断片はpgvectorへ保存されます。
動作確認
ケース1:複数文書の情報を利用する
payment-service固有の手順と、Connection timeoutの一般的な確認事項などが回答へ反映されることを確認します。
curl -X POST http://localhost:8080/knowledge/ask \
-H "Content-Type: text/plain" \
-H "Accept: application/json" \
-d "payment-serviceでConnection timeoutが発生しています。運用手順書を基に、確認項目と対応方法を教えてください。"
実行結果は次のとおりです。
{
"answer": "Connection timeoutが発生している場合の確認項目と対応方法は以下の通りです。\n\n## 確認項目\n\n1. **アプリケーションログの確認**: 直近15分のアプリケーションログを確認し、Connection timeoutの発生時刻と頻度を整理します。\n2. **設定値の確認**: `PAYMENT_DB_HOST`と`PAYMENT_DB_PORT`の設定値が運用台帳と一致しているか確認します。\n3. **疎通確認**: payment-serviceから決済DBへの疎通確認を実施し、名前解決とTCP接続が成功するか確認します。\n4. **接続プールの監視**: 監視ダッシュボードで`paymentPool`の使用中接続数が上限に達していないか確認します。\n\n## 対応方法\n\n1. **設定値の修正**: 設定値に誤りがあれば、正しい接続先へ修正して再デプロイします。\n2. **ネットワーク確認**: 疎通不良がある場合は、ネットワーク経路、セキュリティグループ、およびDB側の接続許可設定を確認します。\n3. **接続プールの確認**: 接続プールが枯渇している場合は、長時間実行SQLと未解放コネクションの有無を確認します。\n4. **エスカレーション**: 障害が15分以上継続する場合は、障害連絡フローに従ってエスカレーションします。"
}
サービス固有の調査手順に加えて、接続プールやエスカレーションに関する情報が回答へ反映されました。
なお、今回のAPIレスポンスには検索した文書名を含めていません。そのため、この結果だけで、どの文書断片が取得されたかを厳密に確認することはできません。検索元を明示したい場合は、取得した文書のメタデータや出典をレスポンスへ追加する設計が必要です。
ケース2:特定文書の情報を利用する
接続プールに焦点を当てた質問を送信します。
curl -X POST http://localhost:8080/knowledge/ask \
-H "Content-Type: text/plain" \
-H "Accept: application/json" \
-d "接続プールが枯渇した場合の確認手順を教えてください。"
実行結果は次のとおりです。
{
"answer": "接続プールが枯渇した場合の確認手順は以下の通りです。\n\n1. 監視ダッシュボードで`paymentPool`の使用中接続数、待機数、上限値を確認します。\n2. アプリケーションログから、接続取得待ちの警告や`Connection is not available, request timed out`の発生を確認します。\n3. 長時間実行されているSQLとトランザクションを確認します。\n4. 接続を解放していない処理やリトライの増加がないか確認します。"
}
接続プールの確認手順を中心とした回答が返りました。
ケース3:関連文書がない場合
運用ドキュメントに含まれない話題を質問します。
curl -X POST http://localhost:8080/knowledge/ask \
-H "Content-Type: text/plain" \
-H "Accept: application/json" \
-d "量子コンピュータの基本的な仕組みを教えてください。"
実行結果は次のとおりです。
{
"answer": "量子コンピュータの基本的な仕組みについての情報は、提供された運用ドキュメントには含まれていません。したがって、その内容についてはお答えできません。"
}
System Messageで、ドキュメントに含まれない情報を補完しないよう指示しているため、運用ドキュメントから回答できない旨が返りました。
以上3つのケースの回答をMarkdownファイルの内容と照合したところ、関連する手順や条件が回答へ適切に反映されていることを確認できました。関連情報がない質問に対しても、ドキュメントから回答できない旨が返されています。
文書の再取り込みとデータの永続化
ingestion-strategyによる起動時の制御
quarkus.langchain4j.easy-rag.ingestion-strategyは、アプリケーション起動時に文書取り込みを実行するかどうかを制御します。
| 値 | 起動時の動作 |
|---|---|
on |
文書を読み込み、Embeddingの生成とpgvectorへの保存を実行する |
off |
起動時の文書取り込みを実行しない |
ingestion-strategyは、データの永続化を制御する設定ではありません。
データの永続化はPostgreSQLとnamed volumeが担当し、ingestion-strategyは起動時に取り込み処理を実行するかどうかを制御します。
データの永続化
Embeddingデータは、PostgreSQLのデータとしてpgvector-data volumeに保存されます。コンテナを停止または再作成しても、volumeを削除しない限りデータは保持されます。
文書に変更がなく、登録済みデータを利用する場合は、ingestion-strategy=offで起動することで、起動時のEmbedding生成を省略できます。
文書を変更して最初から登録し直す場合は、デモ環境では次のコマンドでvolumeを削除できます。
podman compose down -v
podman compose up -d
その後、ingestion-strategy=onでQuarkusを起動し、文書を再登録します。
実運用で検討すべきこと
今回のアプリケーションは、RAGとベクトル検索の基本動作を確認するためのデモです。実運用では、少なくとも次の点を検討する必要があります。
-
文書の更新と重複管理
ingestion-strategy=onで同じ文書を繰り返し取り込む場合、構成やExtensionの動作によっては、同じ内容が重複登録される可能性があります。
文書ID、更新日時、ハッシュ値などを利用し、追加、更新、削除をどのように反映するか設計する必要があります。 -
Embedding Model変更時の再登録
Embedding Modelを変更すると、ベクトルの次元数だけでなく、ベクトル空間の特性も変わります。モデルを変更する場合は、既存のEmbeddingを新しいモデルで再生成する必要があります。 -
検索精度の調整
以下の設定や設計は、検索結果と回答品質に影響します。実際の質問と期待する回答を用意し、検索結果と回答を継続的に評価することが重要です。- 文書断片のサイズ
- オーバーラップのサイズ
- 検索結果の取得件数
- 類似度のしきい値
- 文書の構造と記述方法
- Rerankingの有無
-
ベクトルインデックス
本デモではデータ量が少ないため、ベクトルインデックスを使用していません。文書量が増える場合は、検索速度と検索精度を確認したうえで、pgvectorのインデックス利用を検討します。 -
アクセス制御と機密情報
社内文書を利用する場合は、ユーザーごとの参照権限を検索結果へ反映する必要があります。また、OpenAI APIへ送信する文書や質問に、機密情報や個人情報が含まれる場合の取り扱いも設計する必要があります。 -
APIキーの管理
本記事では環境変数を使用しますが、本番環境では、利用するプラットフォームのシークレット管理機能などを使用してください。 -
出典の提示
今回のAPIは回答だけを返します。回答の根拠を利用者が確認できるようにするには、文書名、セクション、URLなどの出典情報を保持し、回答とともに返す設計が必要です。
まとめ
これまでの記事では、Quarkus LangChain4jを使用したLLMの呼び出しやTool Calling、MCPによる外部機能との連携を紹介しました。
今回はさらに、RAGとベクトル検索の基本的な仕組みを整理したうえで、Quarkus LangChain4jのEasy RAGとpgvectorを使用し、独自の運用ドキュメントを検索して回答へ活用するRAGアプリケーションを実装しました。
今回のポイントは以下のとおりです。
- Easy RAGにより、文書の読み込み、分割、Embedding生成、保存、検索を設定中心で構成できる
- Quarkus LangChain4jのExtensionにより、OpenAIのChat ModelとEmbedding Model、pgvectorを少ないコードで連携できる
- AI Serviceへ検索結果が自動的に追加されるため、RAG固有の処理を個別に実装せず、独自ドキュメントに基づく回答を生成できる
これにより、LLM単体での回答生成や外部機能の呼び出しに加えて、アプリケーション固有の知識を回答へ活用する方法を確認できました。
RAGの回答品質は、モデルだけでなく、文書の構造や分割方法、検索条件、プロンプトにも左右されます。今回のEasy RAGによる実装を、より実践的な検索や文書管理について検討するための第一歩にできればと思います。
最後までご覧いただき、ありがとうございました。
本記事が、QuarkusアプリケーションへRAGとベクトル検索を組み込む際の参考になれば幸いです。