Guida completa alla realizzazione di un chatbot RAG che renda ricercabile con Claude una knowledge base interna, implementata per circa 5.000 documenti in giapponese. Include circa 30 righe di codice e una stima dei costi operativi mensili.
Panoramica
- Converti i documenti in vettori di embedding (text-embedding-3-large)
- Converti la domanda dell'utente in un vettore con lo stesso modello e cerca i 5 risultati principali
- Invia il context recuperato a Claude Sonnet 4.6 per generare la risposta
- Riduci del 90% il costo delle richiamate con il prompt caching
1) Crea gli embedding dei documenti (una sola volta)
# 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 documenti × una media di 500 token equivalgono a 2,5 milioni di token. Poiché Kunavo non offre embedding, solo questo passaggio viene fatturato direttamente da OpenAI — verifica il prezzo unitario nella pagina delle tariffe di OpenAI. Non riportiamo una cifra qui perché, se scriviamo il prezzo di qualcosa che non vendiamo, quel prezzo può diventare obsoleto senza che nessuno se ne accorga. Esegui questo processo una sola volta e salva il risultato in pgvector.
2) Ricerca e generazione delle risposte al momento della query
# 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 utilizzati per ogni query: embedding della domanda (~$0,000005) + circa 5K token di input di Claude Sonnet 4.6 (context) × $1,20/1M = $0,006 + circa 500 token di output × $6,00/1M = $0,003. Circa $0,009 per query (circa 1,4 yen).
3) Riduci ulteriormente il costo con il prompt caching
Poiché invii ogni volta lo stesso prompt di sistema, il prompt caching di Anthropic è efficace:
# 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%Se il prompt di sistema rimane stabile e identico per 3.000 token, dalla seconda richiesta in poi quella parte dell'input viene calcolata al 10% della tariffa. Nelle misurazioni reali, il costo scende fino a circa $0,004 per query (circa 0,6 yen).
Stima dei costi operativi mensili
| Utilizzo | Costo | Note |
|---|---|---|
| Embedding iniziali | $0,25 | Una tantum |
| 1.000 query/giorno | Circa $270/mese | senza caching |
| 1.000 query/giorno | Circa $110/mese | con caching |
| 10.000 query/giorno | Circa $1.100/mese | con caching, dimensioni da bot Slack |
Considerazioni specifiche per il giapponese
- Consumo di token: il giapponese può consumare 2-3 volte più token dell'inglese (per ragioni legate alla codifica di kanji e kana). Riduci il context da 5K a 3K e punta sulla precisione della ricerca
- Modello di embedding: text-embedding-3-large supporta più lingue e offre una precisione sufficiente anche in giapponese. Se sono presenti molti termini interni, è consigliabile mantenere separatamente un dizionario dei sinonimi
- Claude vs Gemini 2.5 Pro: Claude è più rigoroso nei riassunti lunghi e nelle citazioni, mentre Gemini 2.5 Pro offre una ricerca più ampia. Il percorso standard è iniziare con Sonnet 4.6 ed effettuare l'escalation a Opus 4.7 quando necessario
- Mitigazione delle allucinazioni: indica esplicitamente nel prompt di sistema di rispondere «non ci sono informazioni» quando la risposta non è nel context. Se il problema persiste, limita max_tokens, fai restituire l'ID del documento di origine e mostra il link alla fonte nell'interfaccia
Checklist per il rilascio in produzione
- Flusso di aggiornamento dell'indice degli embedding (ricostruzione giornaliera o settimanale tramite cron)
- Fallback in caso di errore (doppio livello, ad esempio Claude → Gemini 2.5 Flash)
- Limiti di frequenza e tetto dei costi (cap giornaliero per chiave, ad esempio $50, in /app/keys)
- Log e monitoraggio (tracciamento dei costi giornalieri nella dashboard usage)
- Legge sulle transazioni commerciali specificate (per i clienti in Giappone) — /legal/tokutei-shoutorihiki
Per iniziare, registrati gratuitamente. Ricarica a partire da $10 e inizia a usare il servizio con fatturazione a consumo; per i dettagli consulta /docs/quickstart o /docs/caching.