社内ナレッジベースを Claude で検索可能にする RAG チャットボットの作り方を、 日本語ドキュメント 5,000 件規模で実装する完全例。コード約 30 行 + 月額運用コスト の見積もりまで含めて公開します。
全体像
- ドキュメントを埋め込みベクトル化(text-embedding-3-large)
- ユーザー質問を同じモデルでベクトル化、上位 5 件を検索
- 取得した context を Claude Sonnet 4.6 に投げて回答生成
- Prompt caching で再呼び出しコスト 90% 削減
1) ドキュメントを埋め込む(一回だけ)
# 1) 5,000 件の社内文書を埋め込みベクトル化
from openai import OpenAI
client = OpenAI(
api_key="sk-kunavo-...",
base_url="https://api.kunavo.com/v1",
)
documents = load_documents() # [{id, text, metadata}, ...]
# バッチ 100 件ずつで Gemini text-embedding を呼ぶ
batches = [documents[i:i+100] for i in range(0, len(documents), 100)]
all_vectors = []
for batch in batches:
resp = client.embeddings.create(
model="text-embedding-3-large",
input=[d["text"] for d in batch],
)
all_vectors.extend(resp.data)
# Postgres + pgvector に保存(または Pinecone, Qdrant など)
save_to_vector_db(documents, all_vectors)5,000 件 × 平均 500 トークンとして 250 万トークン。text-embedding-3-large は Kunavo で約 $0.10/1M tokenなので 5,000 件全部で約 $0.25。 この処理は一度だけ走らせて結果を pgvector に保存しておきます。
2) クエリ時の検索と回答生成
# 2) ユーザー質問 → 関連文書を検索 → Claude に投げて回答
def answer(question: str) -> str:
# 質問を埋め込み化
q_embed = client.embeddings.create(
model="text-embedding-3-large",
input=[question],
).data[0].embedding
# 上位 5 件を検索
relevant = vector_search(q_embed, top_k=5)
# Claude に context + question を渡す
context = "\n\n---\n\n".join(d["text"] for d in relevant)
resp = client.chat.completions.create(
model="claude-sonnet-4-6",
messages=[
{
"role": "system",
"content": (
"あなたは社内ナレッジ ベースをもとに正確に答えるアシスタントです。"
"提供された context にない情報については「情報がありません」と答えてください。"
),
},
{"role": "user", "content": f"# Context\n{context}\n\n# 質問\n{question}"},
],
max_tokens=800,
)
return resp.choices[0].message.content各クエリで使うトークン:質問の埋め込み (~$0.000005) + Claude Sonnet 4.6 の input 約 5K トークン (context) × $2.10/1M = $0.0105 + output 約 500 トークン × $10.50/1M = $0.005。1 クエリあたり 約 $0.016(約 2.4 円)。
3) Prompt caching でさらに削減
同じシステムプロンプトを毎回送るので Anthropic prompt caching が効きます:
# 3) Cache_control で再呼び出しコストを 90% 削減
# 同じ context(社内ドキュメント)を何度も使うので、Anthropic prompt caching が効く
resp = client.chat.completions.create(
model="claude-sonnet-4-6",
messages=[
{
"role": "system",
"content": [{
"type": "text",
"text": SYSTEM_PROMPT_AND_GUIDELINES, # 安定したシステムプロンプト
"cache_control": {"type": "ephemeral"},
}],
},
{"role": "user", "content": f"# Context\n{context}\n\n# 質問\n{question}"},
],
)
# 2回目以降の同じシステムプロンプトはキャッシュヒット → 入力料金の 10%システムプロンプトが 3,000 トークン安定して同じなら、2回目以降は その部分の input が 10% の料金で計算されます。 実測で 1 クエリあたり 約 $0.006(約 0.9 円) まで下がります。
月間運用コストの見積もり
| 使用量 | コスト | 備考 |
|---|---|---|
| 初期埋め込み | $0.25 | 一回限り |
| 1,000 クエリ/日 | 約 $180/月 | caching なし |
| 1,000 クエリ/日 | 約 $54/月 | caching あり |
| 10,000 クエリ/日 | 約 $540/月 | caching あり、Slack ボット規模 |
日本語ならではの注意点
- トークン消費:日本語は英語の 2-3 倍のトークンを消費する ことがあります(漢字や仮名のエンコーディング上の理由)。 context を 5K → 3K に絞り、検索精度で勝負しましょう
- 埋め込みモデル:text-embedding-3-large は多言語対応で 日本語精度も十分。社内用語が多い場合は同義語辞書を別途持つことを推奨
- Claude vs Gemini 3 Pro:長文要約・引用に厳密なのは Claude、 検索の幅が広いのは Gemini 3 Pro。Sonnet 4.6 から始めて、必要に応じて Opus 4.7 にエスカレートするのが定石
- ハルシネーション対策:「context にない場合は『情報がない』と 答える」をシステムプロンプトで明示。それでも漏れる場合は max_tokens を絞り、 引用元の文書 ID を返させて UI で出典リンクを表示
本番投入のチェックリスト
- 埋め込みインデックスの更新フロー(cron で日次・週次再構築)
- 失敗時のフォールバック(Claude → Gemini 3 Flash など 2 段構え)
- レート制限とコスト上限(/app/keys でキー毎に日次 $50 などキャップ)
- ログとモニタリング(usage ダッシュボードで日次コスト追跡)
- 特定商取引法(日本のお客様向け) — /legal/tokutei-shoutorihiki
始めるには 無料登録で $2 のクレジットを受け取り、 詳細は /docs/quickstart や /docs/caching を参照してください。