사내 지식 베이스를 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
# 埋め込みは 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건 × 평균 500토큰으로 총 250만 토큰입니다. Kunavo는 임베딩을 제공하지 않으므로 이 단계만 OpenAI에 직접 청구됩니다—— 단가는 OpenAI 가격 페이지에서 확인하세요. 여기서 수치를 쓰지 않는 이유는 직접 판매하지 않는 상품의 가격을 적으면 오래되어도 아무도 알아차리지 못하기 때문입니다. 이 처리는 한 번만 실행하고 결과를 pgvector에 저장해 둡니다.
2) 쿼리 시 검색 및 답변 생성
# 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.content각 쿼리에서 사용하는 토큰: 질문 임베딩(~$0.000005) + Claude Sonnet 4.6 입력 약 5K토큰(context) × $1.20/1M = $0.006 + 출력 약 500토큰 × $6.00/1M = $0.003. 쿼리당 약 $0.009(약 1.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토큰이 안정적으로 동일하다면 두 번째 요청부터 해당 부분의 입력은 요금의 10%로 계산됩니다. 실제 측정에서는 쿼리당 약 $0.004(약 0.6엔)까지 내려갑니다.
월간 운영 비용 추정
| 사용량 | 비용 | 비고 |
|---|---|---|
| 초기 임베딩 | $0.25 | 일회성 |
| 쿼리 1,000건/일 | 약 $270/월 | caching 없음 |
| 쿼리 1,000건/일 | 약 $110/월 | caching 있음 |
| 쿼리 10,000건/일 | 약 $1,100/월 | caching 있음, Slack 봇 규모 |
일본어 특유의 주의점
- 토큰 소비: 일본어는 영어보다 2~3배 많은 토큰을 소비할 수 있습니다(한자와 가나의 인코딩 특성 때문). context를 5K에서 3K로 줄이고 검색 정확도로 승부하세요.
- 임베딩 모델: text-embedding-3-large는 다국어를 지원하며 일본어 정확도도 충분합니다. 사내 용어가 많은 경우 동의어 사전을 별도로 관리하는 것을 권장합니다.
- Claude vs Gemini 2.5 Pro: 장문 요약과 인용의 엄밀함은 Claude가, 검색 범위의 폭은 Gemini 2.5 Pro가 강합니다. Sonnet 4.6으로 시작하고 필요에 따라 Opus 4.7로 에스컬레이션하는 것이 일반적인 방식입니다.
- 환각 대응: 시스템 프롬프트에 “context에 없는 경우 ‘정보가 없습니다’라고 답한다”고 명시하세요. 그래도 누락되면 max_tokens를 줄이고 출처 문서 ID를 반환하게 하여 UI에 출처 링크를 표시하세요.
프로덕션 투입 체크리스트
- 임베딩 인덱스 업데이트 흐름(cron으로 매일·매주 재구축)
- 실패 시 폴백(Claude → Gemini 2.5 Flash 등 2단계 구성)
- 속도 제한 및 비용 상한(/app/keys에서 키별 일일 $50 등으로 제한)
- 로그 및 모니터링(usage 대시보드에서 일일 비용 추적)
- 특정상거래법(일본 고객 대상) — /legal/tokutei-shoutorihiki
시작하려면 무료 등록하세요. $10부터 충전해 종량제로 사용을 시작하고, 자세한 내용은 /docs/quickstart 및 /docs/caching을 참조하세요.