現在、多くのプロダクトでRAG(検索拡張生成)の導入が進んでいますが、DjangoアプリケーションにAWS Bedrockを統合する際の実装パターンにはいくつかの選択肢があります。
本記事では、**「開発者(Developer)」**の視点から、Djangoを使ってAWS Bedrock Knowledge Basesを呼び出すための最短ルートと、実運用で重要となる「マルチテナント(会社ごとのナレッジ分離)」の設計について解説します。
TL;DR(結論)
- 最短ルート: DjangoアプリからBedrock RAGを呼ぶなら
bedrock-agent-runtimeのretrieve_and_generateAPIを使うのが最も実装コストが低い。 - マルチテナント設計: 会社ごとにナレッジを分離するなら、DB(例:
KnowledgeBaseSettings)にknowledge_base_idを持たせて動的に切り替える設計が安全。 - 本番運用の勘所: リージョンの統一、適切な例外処理、設定の外出し(ARN/ID)が品質を左右する。
1. BedrockのAPIは2種類ある:問い合わせ用と管理用
実装に入る前に、混乱しやすいBoto3のクライアント種別について整理しておきます。このリポジトリの実装では、Bedrockを以下の2系統で使い分けています。
- 問い合わせ(RAG実行):
bedrock-agent-runtime- ユーザー質問 → 検索 → 生成 → 回答 のフローを担当します。今回の記事(前編)はこちらに集中します。
- 管理(KB作成/同期):
bedrock-agent- Knowledge Base作成、Data Source作成、Ingestion(同期)開始を担当します。これらは次回の「後編」で解説します。
2. 最小のRAG呼び出し(Knowledge Base固定)
まずは、最もシンプルな実装例を見てみましょう。「特定のKnowledge Base(KB)に対して質問を投げ、回答を得る」だけの関数です。
retrieve_and_generate APIを使用することで、「検索(Retrieve)」と「生成(Generate)」を1回のリクエストでまとめて実行できます。
Python
import boto3
def ask_bedrock(question: str) -> str:
"""
固定のKnowledge Baseに対してRAGを実行する最小サンプル
"""
# ランタイムクライアントの作成
# ※リージョンは実際にKBを作成したリージョンを指定してください
kb = boto3.client("bedrock-agent-runtime", region_name="ap-northeast-1")
response = kb.retrieve_and_generate(
input={"text": question},
retrieveAndGenerateConfiguration={
"type": "KNOWLEDGE_BASE",
"knowledgeBaseConfiguration": {
# 実際には環境変数などで管理することを推奨
"knowledgeBaseId": "KB_ID_EXAMPLE_123456",
"modelArn": "arn:aws:bedrock:ap-northeast-1::foundation-model/anthropic.claude-3-5-sonnet-YYYYMMDD-v1:0",
},
},
)
# UI/APIに返すテキスト部分のみを抽出
return response["output"]["text"]
実装のポイント
- シンプルさ: 検索クエリを投げてContextを取得し、それをLLMに投げる…という手順を自前で書く必要がありません。
- レスポンス:
response["output"]["text"]を返すだけで、チャットUIやAPIレスポンスとして利用可能です。
3. 会社(Company)ごとにKBを切り替える(マルチテナントRAG)
SaaSなどを開発する場合、「A社のユーザーにはA社のマニュアルから回答させたい(B社の情報は絶対に出さない)」という要件が発生します。
これを実現するために、データベースにKBの設定情報を持たせる設計例を紹介します。
モデル設計のイメージ
DjangoのModelとして KnowledgeBaseSettings を用意し、会社(Company)モデルと紐づけます。
company: Companyモデルへの外部キーknowledge_base_id: その会社用のKB IDregion_name: リージョン(会社によってデータの置き場所が違う場合に備える)
実装コード
Python
import boto3
from django.conf import settings
from accounts.models import KnowledgeBaseSettings
def ask_bedrock_company(question: str, company) -> str:
"""
会社ごとのKnowledge Base IDを使用してRAGを実行する
"""
# DBからその会社の設定を取得
kb_settings = KnowledgeBaseSettings.objects.get(company=company)
# クライアント作成(リージョンも設定から取得可能にする)
kb = boto3.client(
"bedrock-agent-runtime",
region_name=kb_settings.region_name or "ap-northeast-1",
)
response = kb.retrieve_and_generate(
input={"text": question},
retrieveAndGenerateConfiguration={
"type": "KNOWLEDGE_BASE",
"knowledgeBaseConfiguration": {
# DBに保存されたKB IDを使用
"knowledgeBaseId": kb_settings.knowledge_base_id,
"modelArn": "arn:aws:bedrock:ap-northeast-1::foundation-model/anthropic.claude-3-5-sonnet-YYYYMMDD-v1:0",
},
},
)
return response["output"]["text"]
なぜDBにIDを持つのが良いのか
- データ漏洩リスクの低減: アプリケーションロジックでIDを切り替えることで、物理的・論理的にナレッジへのアクセスを分離できます。
- 運用の分離: KBの作成や同期といった運用タスクを、アプリの問い合わせロジックから切り離せます。
- 拡張性: 後述する「会社追加時のKB自動作成」と組み合わせることで、スケーラブルな運用が可能になります。
4. 例外処理と設定管理(本番運用のために)
プロトタイプでは気になりませんが、本番運用では「例外処理」と「設定の持ち方」が品質を左右します。
例外処理の重要性
Bedrock呼び出しは外部API通信であるため、タイムアウトや権限エラー、あるいは「KBがまだ作成されていない」といった状況が起こり得ます。
try-except で例外を捕捉し、ログを残すことは必須ですが、**「呼び出し元に何を返すか」**も重要です。
Noneを返すとフロントエンドがクラッシュする可能性があります。- 「現在ナレッジベースを準備中です」や「検索に失敗しました」といった、ユーザーフレンドリーなエラーメッセージを返す設計にしておきましょう。
- ログには必ず
company_idや(マスクした上で)knowledge_base_idを紐づけて、どこのテナントで問題が起きているか追跡できるようにします。
設定の持ち方(推奨)
| 設定項目 | 推奨される保管場所 | 理由 |
| BEDROCK_REGION | 環境変数 / Django settings | 基本的なリージョンは統一されていることが多いため |
| MODEL_ARN | 環境変数 / Django settings | モデルのバージョンアップ時に一括で切り替えるため |
| knowledge_base_id | DB (各テナントの設定) | 会社ごとに異なるため |
| data_source_id | DB (各テナントの設定) | 同期処理などで使用するため |
| bucket_name | DB (各テナントの設定) | 会社ごとにS3バケットを分ける場合があるため |
前編まとめ
- DjangoでBedrock RAGを実装する場合、
retrieve_and_generateが最も低コストです。 - マルチテナントSaaSでは、DBに
knowledge_base_idを持たせることで、セキュアかつ柔軟にナレッジを分離できます。 - 本番リリースに向けて、例外時の振る舞いと設定値の管理場所を整理しておきましょう。
次回の**【後編】**では、運用者向けに「会社追加時にKBを自動作成・同期(Ingestion)する方法」や「監視の勘所」について解説します。