はじめに
「ChatGPTに自社の製品マニュアルを学習させたい」「社内規定を知っているAIアシスタントを作りたい」——そんな要望を持つ企業が急増しています。
しかし、LLMを自前でファインチューニング(追加学習)するには、大量のGPU・専門知識・膨大なコストが必要です。
そこで注目されているのが RAG(Retrieval-Augmented Generation)です。LLM自体は変更せず、「質問に関係するドキュメントを検索してから回答させる」という仕組みで、独自知識を持ったチャットボットを低コストで実現できます。
本記事では、Amazon Bedrock の Knowledge Bases を使い、S3にドキュメントをアップロードするだけで動作するRAGチャットボットを構築する手順を解説します。
RAGとは何か
RAGは以下の3ステップで動作します。
- 検索(Retrieve):ユーザーの質問に関連する文書をベクトルDBから検索
- 文脈付与(Augment):検索結果をプロンプトに追加
- 生成(Generate):文脈を踏まえてLLMが回答を生成
通常のLLMは学習データの知識しか持ちませんが、RAGを使うとリアルタイムで外部ドキュメントを参照しながら回答できます。
RAGとファインチューニングの違い
| RAG | ファインチューニング | |
|---|---|---|
| コスト | 低い | 高い(GPU必須) |
| 更新の容易さ | ◎ ドキュメント追加だけ | △ 再学習が必要 |
| 出典の明示 | ○ できる | × 難しい |
| 専門知識 | 不要 | 必要 |
社内ナレッジや製品情報のように頻繁に更新されるドキュメントを扱う場合はRAGが最適です。
今回構築するシステムの全体像
[ユーザー] → [Next.js フロントエンド]
↓
[API Route(Node.js)]
↓
[Amazon Bedrock Knowledge Bases]
↙ ↘
[Amazon OpenSearch Serverless] [Amazon S3]
(ベクトルDB) (ドキュメント保管)
↓
[Claude 3 Sonnet]
↓
[回答をユーザーへ]使用するAWSサービス:
| サービス | 役割 |
|---|---|
| Amazon S3 | ドキュメント(PDF・テキスト)の保管 |
| Amazon Bedrock Knowledge Bases | RAGパイプラインの管理 |
| Amazon OpenSearch Serverless | ベクトルDB(埋め込みの保存・検索) |
| Amazon Bedrock (Claude 3 Sonnet) | 回答生成LLM |
前提条件
- AWSアカウントを持っていること
- AWS CLIが設定済みであること(
aws configure完了) - Node.js v20以上がインストールされていること
- Amazon Bedrockで Claude 3 Sonnet と Titan Embeddings V2 のモデルアクセスを有効化済みであること
Bedrockのモデルアクセス有効化
AWSコンソール → Amazon Bedrock → 「モデルアクセス」から以下を有効化します:
- Anthropic Claude 3 Sonnet(回答生成用)
- Amazon Titan Embeddings V2(ベクトル化用)
有効化には数分かかる場合があります。
ステップ1:S3バケットにドキュメントをアップロード
まず、チャットボットに学習させたいドキュメントをS3に保存します。
対応ファイル形式
- PDF(マニュアル・仕様書・報告書)
- テキストファイル(.txt)
- Word文書(.docx)
- Markdown(.md)
- HTML
バケットの作成
AWSコンソール → S3 → 「バケットを作成」
- バケット名:
my-rag-documents-XXXX(一意の名前) - リージョン:
ap-northeast-1(東京) - その他はデフォルト
ドキュメントのアップロード
ドキュメントをS3にアップロードします。CLIを使う場合:
aws s3 cp ./documents/ s3://my-rag-documents-XXXX/ --recursiveポイント:ドキュメントの品質がRAGの精度に直結します。読みやすく整理されたテキストほど、検索精度が上がります。
ステップ2:Amazon Bedrock Knowledge Baseを作成
2-1. Knowledge Baseの作成開始
AWSコンソール → Amazon Bedrock → 「Knowledge bases」→「Knowledge baseを作成」
基本設定:
- 名前:
my-knowledge-base - 説明:任意
- IAMロール:「新しいサービスロールを作成」を選択
2-2. データソース(S3)の設定
- データソースのタイプ:Amazon S3
- S3 URI:
s3://my-rag-documents-XXXX/
2-3. 埋め込みモデルの選択
埋め込みモデルにはドキュメントをベクトルに変換する役割があります。
- Amazon Titan Embeddings V2(推奨)
- 1,536次元のベクトル
- 日本語対応
- コストパフォーマンスが高い
2-4. ベクトルDBの設定
Knowledge Basesはベクトルストアとして Amazon OpenSearch Serverless を自動で作成できます。
- 「新しいベクトルストアをすばやく作成する」を選択
- OpenSearch Serverlessコレクションが自動作成されます
注意:OpenSearch Serverlessは最低料金が発生します(約$700/月〜)。コストを抑えたい場合は後述のコスト最適化を参照してください。
2-5. 確認と作成
設定を確認して「Knowledge baseを作成」をクリックします。作成には5〜10分かかります。
ステップ3:データソースの同期
Knowledge Baseを作成したら、S3のドキュメントをベクトル化して検索インデックスに取り込む「同期」作業が必要です。
同期の実行
AWSコンソール → 作成したKnowledge Base → 「データソース」タブ → 「同期」ボタンをクリック
同期が完了すると、ドキュメントがチャンク(小さな断片)に分割され、それぞれのベクトルがOpenSearch Serverlessに保存されます。
同期のステータス確認
- 完了(Completed):検索可能な状態
- 処理中(In progress):しばらく待つ
- 失敗(Failed):IAM権限やファイル形式を確認
ステップ4:APIの実装
Next.jsのAPI Routeを使って、Bedrockを呼び出すバックエンドを実装します。
プロジェクトのセットアップ
npx create-next-app@latest rag-chatbot --typescript --tailwind --app
cd rag-chatbot
npm install @aws-sdk/client-bedrock-agent-runtime環境変数の設定
プロジェクトルートに .env.local を作成します:
AWS_REGION=ap-northeast-1
AWS_ACCESS_KEY_ID=your_access_key
AWS_SECRET_ACCESS_KEY=your_secret_key
BEDROCK_KNOWLEDGE_BASE_ID=XXXXXXXXXX
BEDROCK_MODEL_ARN=arn:aws:bedrock:ap-northeast-1::foundation-model/anthropic.claude-3-sonnet-20240229-v1:0API Routeの作成
app/api/chat/route.ts を作成します:
import { NextRequest, NextResponse } from "next/server";
import {
BedrockAgentRuntimeClient,
RetrieveAndGenerateCommand,
} from "@aws-sdk/client-bedrock-agent-runtime";
const client = new BedrockAgentRuntimeClient({
region: process.env.AWS_REGION!,
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
},
});
export async function POST(req: NextRequest) {
const { message, sessionId } = await req.json();
if (!message) {
return NextResponse.json({ error: "メッセージが必要です" }, { status: 400 });
}
const command = new RetrieveAndGenerateCommand({
input: { text: message },
retrieveAndGenerateConfiguration: {
type: "KNOWLEDGE_BASE",
knowledgeBaseConfiguration: {
knowledgeBaseId: process.env.BEDROCK_KNOWLEDGE_BASE_ID!,
modelArn: process.env.BEDROCK_MODEL_ARN!,
retrievalConfiguration: {
vectorSearchConfiguration: {
numberOfResults: 5,
},
},
generationConfiguration: {
promptTemplate: {
textPromptTemplate:
"あなたは親切なアシスタントです。以下の情報を参考に、日本語で回答してください。\n\n$search_results$\n\n質問:$query$",
},
},
},
},
sessionId: sessionId || undefined,
});
const response = await client.send(command);
const answer = response.output?.text ?? "回答を生成できませんでした。";
const citations = response.citations?.map((c) =>
c.retrievedReferences?.map((r) => r.location?.s3Location?.uri)
).flat().filter(Boolean);
return NextResponse.json({
answer,
citations,
sessionId: response.sessionId,
});
}ポイント解説:
RetrieveAndGenerateCommand:検索と生成を1回のAPIコールで完結させるコマンドnumberOfResults: 5:参照するドキュメントチャンクの数sessionId:会話履歴を維持するためのセッションIDcitations:回答の根拠となったS3ファイルのURIが返ってくる
ステップ5:フロントエンドの実装
シンプルなチャット画面を app/page.tsx に実装します:
"use client";
import { useState, useRef } from "react";
type Message = {
role: "user" | "assistant";
content: string;
citations?: string[];
};
export default function ChatPage() {
const [messages, setMessages] = useState<Message[]>([]);
const [input, setInput] = useState("");
const [loading, setLoading] = useState(false);
const sessionIdRef = useRef<string | null>(null);
const sendMessage = async () => {
if (!input.trim() || loading) return;
const userMessage = input;
setInput("");
setMessages((prev) => [...prev, { role: "user", content: userMessage }]);
setLoading(true);
const res = await fetch("/api/chat", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ message: userMessage, sessionId: sessionIdRef.current }),
});
const data = await res.json();
sessionIdRef.current = data.sessionId;
setMessages((prev) => [
...prev,
{ role: "assistant", content: data.answer, citations: data.citations },
]);
setLoading(false);
};
return (
<div className="max-w-2xl mx-auto p-4 h-screen flex flex-col">
<h1 className="text-xl font-bold mb-4">RAGチャットボット</h1>
<div className="flex-1 overflow-y-auto space-y-4 mb-4">
{messages.map((m, i) => (
<div key={i} className={m.role === "user" ? "text-right" : "text-left"}>
<div className={
m.role === "user"
? "inline-block bg-blue-500 text-white rounded-lg px-4 py-2"
: "inline-block bg-gray-100 rounded-lg px-4 py-2"
}>
{m.content}
</div>
{m.citations && m.citations.length > 0 && (
<div className="text-xs text-gray-400 mt-1">
参照: {m.citations.join(", ")}
</div>
)}
</div>
))}
{loading && <div className="text-gray-400">回答を生成中...</div>}
</div>
<div className="flex gap-2">
<input
className="flex-1 border rounded-lg px-4 py-2"
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && sendMessage()}
placeholder="質問を入力..."
/>
<button
onClick={sendMessage}
className="bg-blue-500 text-white px-4 py-2 rounded-lg"
>
送信
</button>
</div>
</div>
);
}動作確認
開発サーバーを起動して動作確認します:
npm run devブラウザで http://localhost:3000 を開き、アップロードしたドキュメントに関する質問を入力してみましょう。S3に保存した内容をもとに回答が返ってくれば成功です。
コスト最適化のポイント
OpenSearch Serverlessのコスト
OpenSearch Serverlessは最低2OCU(OpenSearch Compute Unit)が必要で、コストが高くなりがちです。開発・検証フェーズでは以下の代替手段を検討してください。
| ベクトルストア | 月額目安 | 特徴 |
|---|---|---|
| OpenSearch Serverless | $300〜 | フルマネージド、スケーラブル |
| Pinecone(外部) | 無料〜$70 | 小規模なら無料枠あり |
| pgvector(Aurora) | $50〜 | PostgreSQL互換 |
リクエストコスト
- Claude 3 Sonnet:入力$3/100万トークン、出力$15/100万トークン
- Titan Embeddings V2:$0.02/100万トークン(同期時のみ発生)
ドキュメントの更新方法
新しいドキュメントをS3にアップロードしたら、Knowledge Baseの「同期」を再実行するだけで最新情報を反映できます。コードの変更は不要です。
# CLIで同期を実行する場合
aws bedrock-agent start-ingestion-job \
--knowledge-base-id XXXXXXXXXX \
--data-source-id YYYYYYYYYYよくある問題と対処法
Q. 同期でエラーが出る
IAMロールにS3の読み取り権限が付与されているか確認してください。Knowledge Base作成時に自動作成されたIAMロールの権限が、バケットポリシーと一致していない場合があります。
Q. 回答の精度が低い
- ドキュメントの品質を見直す(箇条書きより段落文章が有効)
numberOfResultsを増やして参照チャンク数を増やす- プロンプトテンプレートを調整して回答形式を指定する
Q. 日本語の精度が悪い
Titan Embeddings V2は日本語対応していますが、英語に比べると精度が落ちる場合があります。Cohere Embed Multilingual(Bedrock経由)も選択肢として検討してください。
まとめ
Amazon BedrockのKnowledge Basesを使うと、ベクトルDB・埋め込み処理・検索パイプラインをAWSが管理してくれるため、コード量を最小限にRAGシステムを構築できます。
- S3にドキュメントをアップロード
- Knowledge Baseを作成して同期
RetrieveAndGenerateCommand1つで検索+生成
ドキュメントを差し替えるだけで知識を更新できるのがRAGの強みです。社内FAQ・製品マニュアル・規定集など、さまざまな用途に応用してみてください。