Ejemplo completo para implementar un chatbot RAG que permita buscar una base de conocimientos interna con Claude, a escala de unos 5.000 documentos en japonés. Incluye aproximadamente 30 líneas de código y una estimación del coste operativo mensual.
Visión general
- Convertir los documentos en vectores de embeddings (text-embedding-3-large)
- Convertir la pregunta del usuario en un vector con el mismo modelo y buscar los 5 resultados principales
- Enviar el context recuperado a Claude Sonnet 4.6 para generar la respuesta
- Reducir un 90% el coste de las llamadas repetidas mediante Prompt caching
1) Generar embeddings de los documentos (solo una vez)
# 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 documentos × una media de 500 tokens equivalen a 2,5 millones de tokens. Como Kunavo no ofrece embeddings, este único paso se factura directamente a OpenAI—— consulta el precio unitario en la página de precios de OpenAI. No escribimos aquí una cifra porque el precio de algo que no vendemos puede quedar obsoleto sin que nadie lo advierta. Ejecuta este proceso una sola vez y guarda los resultados en pgvector.
2) Búsqueda y generación de respuestas durante la consulta
# 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.contentTokens utilizados por consulta: embedding de la pregunta (~$0.000005) + aproximadamente 5K tokens de entrada de Claude Sonnet 4.6 (context) × $1.20/1M = $0.006 + aproximadamente 500 tokens de salida × $6.00/1M = $0.003. Aproximadamente $0.009 por consulta (unos 1,4 yenes).
3) Reducir aún más mediante Prompt caching
Como enviamos el mismo prompt de sistema cada vez, el prompt caching de Anthropic resulta eficaz:
# 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%Si el prompt de sistema tiene 3.000 tokens y se mantiene idéntico, a partir de la segunda llamada esa parte de la entrada se factura al 10% del precio. En mediciones reales, el coste baja hasta aproximadamente $0.004 por consulta (unos 0,6 yenes).
Estimación del coste operativo mensual
| Uso | Coste | Notas |
|---|---|---|
| Embeddings iniciales | $0.25 | Una sola vez |
| 1.000 consultas/día | Aproximadamente $270/mes | sin caching |
| 1.000 consultas/día | Aproximadamente $110/mes | con caching |
| 10.000 consultas/día | Aproximadamente $1.100/mes | con caching, escala de un bot de Slack |
Consideraciones específicas del japonés
- Consumo de tokens: el japonés puede consumir entre 2 y 3 veces más tokens que el inglés (por razones relacionadas con la codificación de kanji y kana). Reduce el context de 5K a 3K y compite mediante la precisión de la búsqueda
- Modelo de embeddings: text-embedding-3-large admite varios idiomas y ofrece una precisión suficiente en japonés. Si abundan los términos internos, se recomienda mantener aparte un diccionario de sinónimos
- Claude frente a Gemini 2.5 Pro: Claude es más preciso para resúmenes y citas largas, mientras que Gemini 2.5 Pro ofrece una búsqueda más amplia. La práctica habitual es comenzar con Sonnet 4.6 y escalar a Opus 4.7 cuando sea necesario
- Mitigación de alucinaciones: indique explícitamente en el prompt del sistema «si no está en el contexto, responda “no hay información”». Si aun así se filtra alguna, limite max_tokens y haga que devuelva el ID del documento fuente para mostrar el enlace de la fuente en la interfaz
Lista de comprobación para el lanzamiento en producción
- Flujo de actualización del índice de embeddings (reconstrucción diaria y semanal mediante cron)
- Respaldo en caso de fallo (dos niveles, por ejemplo Claude → Gemini 2.5 Flash)
- Límites de velocidad y de costes (capas diarias de $50 por clave, por ejemplo, en /app/keys)
- Registros y monitorización (seguimiento del coste diario en el panel de uso)
- Ley de Transacciones Comerciales Especificadas (para clientes de Japón) — /legal/tokutei-shoutorihiki
Para empezar, regístrese gratis. Recargue desde $10 y empiece a usar el servicio con facturación por uso; consulte /docs/quickstart y /docs/caching para obtener más información.