Como criar um chatbot RAG que permita pesquisar uma base de conhecimento interna com o Claude, implementado em uma escala de 5.000 documentos em japonês. Publicamos um exemplo completo, incluindo cerca de 30 linhas de código e uma estimativa do custo operacional mensal.
Visão geral
- Converter documentos em vetores de embeddings (text-embedding-3-large)
- Converter a pergunta do usuário em vetor com o mesmo modelo e pesquisar os 5 principais resultados
- Enviar o context recuperado ao Claude Sonnet 4.6 para gerar a resposta
- Reduzir o custo de chamadas repetidas em 90% com o Prompt caching
1) Gerar embeddings dos documentos (uma única 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 × uma média de 500 tokens correspondem a 2,5 milhões de tokens. Como o Kunavo não oferece embeddings, somente essa etapa é cobrada diretamente pela OpenAI — consulte o preço unitário na página de preços da OpenAI. Não informamos um número aqui porque, se publicarmos o preço de algo que não vendemos, ele pode ficar desatualizado sem que ninguém perceba. Execute esse processamento uma única vez e salve o resultado no pgvector.
2) Pesquisa e geração de respostas durante a 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 usados em cada consulta: embedding da pergunta (~$0.000005) + aproximadamente 5K tokens de entrada do Claude Sonnet 4.6 (context) × $1.20/1M = $0.006 + aproximadamente 500 tokens de saída × $6.00/1M = $0.003. Aproximadamente $0.009 por consulta (cerca de 1,4 ienes).
3) Reduzir ainda mais com Prompt caching
Como o mesmo prompt de sistema é enviado todas as vezes, o Anthropic prompt caching é 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%Se o prompt de sistema permanecer estável e tiver 3.000 tokens, a partir da segunda chamada essa parte da entrada será calculada a 10% do preço. Em medições reais, o custo cai para aproximadamente $0.004 por consulta (cerca de 0,6 iene).
Estimativa do custo operacional mensal
| Uso | Custo | Observações |
|---|---|---|
| Embeddings iniciais | $0.25 | Uma única vez |
| 1.000 consultas/dia | Aproximadamente $270/mês | sem caching |
| 1.000 consultas/dia | Aproximadamente $110/mês | com caching |
| 10.000 consultas/dia | Aproximadamente $1.100/mês | com caching, escala de bot do Slack |
Pontos de atenção específicos do japonês
- Consumo de tokens: o japonês pode consumir 2–3 vezes mais tokens que o inglês (por razões relacionadas à codificação de kanji e kana). Reduza o context de 5K para 3K e compense com a precisão da pesquisa
- Modelo de embeddings: o text-embedding-3-large oferece suporte multilíngue e precisão suficiente em japonês. Se houver muitos termos internos, recomendamos manter também um dicionário de sinônimos
- Claude versus Gemini 2.5 Pro: o Claude é mais rigoroso em resumos longos e citações, enquanto o Gemini 2.5 Pro oferece uma busca mais ampla. O procedimento padrão é começar com o Sonnet 4.6 e escalar para o Opus 4.7 quando necessário
- Prevenção de alucinações: deixe explícito no prompt de sistema: “Se não estiver no context, responda ‘não há informações’”. Se ainda houver vazamentos, reduza max_tokens, faça o modelo retornar o ID do documento de origem e exiba o link da fonte na UI
Checklist para colocar em produção
- Fluxo de atualização do índice de embeddings (reconstrução diária ou semanal via cron)
- Fallback em caso de falha (duas camadas, como Claude → Gemini 2.5 Flash)
- Limites de taxa e de custo (por exemplo, limite diário de $50 por chave em /app/keys)
- Logs e monitoramento (acompanhar o custo diário no painel de usage)
- Lei de Transações Comerciais Específicas (para clientes no Japão) — /legal/tokutei-shoutorihiki
Para começar, faça um cadastro gratuito. Adicione saldo a partir de $10 e comece a usar com cobrança conforme o uso; para mais detalhes, consulte /docs/quickstart ou /docs/caching.