Panduan lengkap membuat chatbot RAG yang memungkinkan basis pengetahuan internal dicari dengan Claude, diimplementasikan untuk sekitar 5.000 dokumen berbahasa Jepang. Publikasi ini mencakup sekitar 30 baris kode serta perkiraan biaya operasional bulanan.
Gambaran umum
- Mengubah dokumen menjadi vektor embedding (text-embedding-3-large)
- Mengubah pertanyaan pengguna menjadi vektor dengan model yang sama, lalu mencari 5 hasil teratas
- Mengirim context yang diperoleh ke Claude Sonnet 4.6 untuk menghasilkan jawaban
- Mengurangi biaya pemanggilan ulang sebesar 90% dengan prompt caching
1) Menyematkan dokumen (sekali saja)
# 1) 5,000 件の社内文書を埋め込みベクトル化
from openai import OpenAI
# 埋め込みは Kunavo では提供していません(/v1/embeddings は実装済みの
# ワイヤフォーマットですが、有効なモデルがないため呼ぶと失敗します)。
# 埋め込みは OpenAI に直接、生成は Kunavo に — クライアントは 2 つ持ちます。
embedder = OpenAI(api_key="sk-...") # OpenAI 本家
kunavo = OpenAI(
api_key="sk-kn-...",
base_url="https://api.kunavo.com/v1",
)
documents = load_documents() # [{id, text, metadata}, ...]
# バッチ 100 件ずつで埋め込みを呼ぶ
batches = [documents[i:i+100] for i in range(0, len(documents), 100)]
all_vectors = []
for batch in batches:
resp = embedder.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 dokumen × rata-rata 500 token berarti 2,5 juta token. Embedding tidak disediakan di Kunavo, sehingga hanya tahap ini yang ditagihkan langsung oleh OpenAI — periksa harga satuannya di halaman harga OpenAI. Angka tidak dicantumkan di sini karena jika menulis harga sesuatu yang tidak saya jual, harga itu bisa menjadi usang tanpa disadari siapa pun. Proses ini dijalankan sekali, lalu hasilnya disimpan di pgvector.
2) Pencarian dan pembuatan jawaban saat kueri
# 2) ユーザー質問 → 関連文書を検索 → Claude に投げて回答
def answer(question: str) -> str:
# 質問を埋め込み化
q_embed = embedder.embeddings.create( # 埋め込みは OpenAI 直
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 = kunavo.chat.completions.create( # 生成は Kunavo
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.contentToken yang digunakan pada setiap kueri: embedding pertanyaan (~$0,000005) + sekitar 5K token input Claude Sonnet 4.6 (context) × $1,20/1M = $0,006 + sekitar 500 token output × $6,00/1M = $0,003. Sekitar $0,009 per kueri (sekitar 1,4 yen).
3) Penghematan tambahan dengan prompt caching
Karena system prompt yang sama dikirim setiap kali, Anthropic prompt caching dapat digunakan:
# 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%Jika system prompt yang terdiri dari 3.000 token tetap stabil dan sama, mulai pemanggilan kedua, input untuk bagian tersebut dihitung dengan tarif 10%. Dalam pengukuran nyata, biaya turun hingga sekitar $0,004 per kueri (sekitar 0,6 yen).
Perkiraan biaya operasional bulanan
| Penggunaan | Biaya | Catatan |
|---|---|---|
| Embedding awal | $0,25 | Sekali saja |
| 1.000 kueri/hari | Sekitar $270/bulan | tanpa caching |
| 1.000 kueri/hari | Sekitar $110/bulan | dengan caching |
| 10.000 kueri/hari | Sekitar $1.100/bulan | dengan caching, skala bot Slack |
Hal-hal yang perlu diperhatikan khusus untuk bahasa Jepang
- Konsumsi token: Bahasa Jepang terkadang mengonsumsi 2–3 kali lebih banyak token daripada bahasa Inggris (karena alasan pengodean kanji dan kana). Batasi context dari 5K → 3K, lalu andalkan akurasi pencarian
- Model embedding: text-embedding-3-large mendukung banyak bahasa dan memiliki akurasi bahasa Jepang yang memadai. Jika banyak istilah internal, disarankan menyimpan kamus sinonim secara terpisah
- Claude vs Gemini 2.5 Pro: Claude lebih ketat dalam ringkasan panjang dan kutipan, sedangkan Gemini 2.5 Pro memiliki cakupan pencarian yang lebih luas. Praktik umumnya adalah memulai dengan Sonnet 4.6 dan meningkatkan ke Opus 4.7 bila diperlukan
- Penanggulangan halusinasi: Tegaskan dalam system prompt, “Jika tidak ada di context, jawab ‘Informasi tidak tersedia’”. Jika masih bocor, batasi max_tokens, minta ID dokumen sumber dikembalikan, lalu tampilkan tautan sumber di UI
Daftar periksa sebelum produksi
- Alur pembaruan indeks embedding (pembangunan ulang harian dan mingguan dengan cron)
- Fallback saat gagal (dua tingkat, misalnya Claude → Gemini 2.5 Flash)
- Batas laju dan batas biaya (cap harian seperti $50 per kunci di /app/keys)
- Log dan pemantauan (lacak biaya harian melalui dasbor usage)
- Undang-Undang Transaksi Komersial Tertentu (untuk pelanggan Jepang) — /legal/tokutei-shoutorihiki
Untuk memulai, lakukan pendaftaran gratis. Isi saldo mulai $10 dan mulai menggunakan layanan dengan penagihan berbasis penggunaan; lihat /docs/quickstart atau /docs/caching untuk detail.