문서 10개로 시작하는 최소 RAG 챗봇
사내 규정 문서 10개를 임베딩으로 검색하고, 찾은 문서로만 답하는 챗봇을 Python으로 만들어 봅니다. 검색과 답을 따로 시험해 고치는 법까지 다룹니다.
18분2026.10 기준
이 글을 마치면
- 문서를 한 주제씩 나눠 임베딩하고, 질문과 가까운 문서를 찾는 검색 함수를 만들 수 있습니다.
- 찾은 문서에 근거해서만 답하고 근거 문서 ID를 붙이는 챗봇을 만들 수 있습니다.
- 시험 질문으로 검색과 답을 따로 채점하고, 어디를 고쳐야 할지 판단할 수 있습니다.
이런 분께
- 'RAG 쉽게 이해하기'를 읽고 직접 만들어 보고 싶은 개발자
- 사내 문서 챗봇을 작게 먼저 시험해 보려는 팀
준비물
- Python과 anthropic·voyageai·numpy 패키지 (pip install -U anthropic voyageai numpy)
- Claude API 키와 임베딩 서비스 키 (이 글은 Anthropic 문서가 소개하는 Voyage AI를 씁니다. 요금은 각 서비스 가격표 참고)
- 'LLM API 첫 호출하기' 글을 마친 상태
단계별 따라하기
문서 10개를 한 주제씩 준비하기
검색은 문서 단위로 이뤄지니 한 문서에는 한 주제만 담고, ID와 제목을 붙여 두세요. 긴 규정집이라면 제목 단위로 잘라 몇 문장짜리 조각으로 만듭니다.
사실 문서가 10개뿐이라면 전부를 프롬프트에 넣어도 됩니다. Anthropic은 지식의 양이 20만 토큰(약 500쪽)보다 적으면 RAG 없이 통째로 넣어도 된다고 안내합니다. 이 글은 문서가 늘어날 때를 대비해 RAG의 뼈대를 작게 만들어 보는 연습입니다.
이 글에서 쓰는 사내 규정 문서 10개 (가상의 회사) ID 제목 내용 D01 연차 신청 연차는 사용일 3일 전까지 인사 시스템에서 신청합니다. 반차는 오전·오후로 나눠 쓸 수 있습니다. D02 재택근무 재택근무는 주 2회까지 할 수 있고, 전날 오후 6시까지 팀장에게 알립니다. D03 회의실 예약 회의실은 사용일 2주 전부터 사내 예약 시스템에서 예약할 수 있습니다. 4시간을 넘겨 쓰려면 총무팀 승인이 필요합니다. D04 출장비 국내 출장 숙박비는 1박에 10만 원까지 지원합니다. 영수증은 출장을 마친 뒤 7일 안에 올립니다. D05 업무용 카드 정산 업무용 카드 사용 내역은 매달 5일까지 경비 시스템에서 정산합니다. D06 노트북 반출 회사 노트북을 밖으로 가져갈 때는 보안팀에 반출 신청을 합니다. D07 업무 장비 신청 모니터·키보드 같은 업무 장비는 IT 지원 페이지에서 신청하며, 보통 3영업일 안에 받습니다. D08 복지 포인트 복지 포인트는 1년에 60만 포인트가 지급되고, 그해 12월 31일까지 쓸 수 있습니다. D09 교육비 지원 업무 관련 교육비는 1년에 100만 원까지 지원하며, 수강 전에 팀장 승인을 받습니다. D10 건강검진 건강검진은 1년에 한 번 회사가 지정한 병원에서 받을 수 있습니다. 확인해 보기
- 문서마다 주제가 하나인지, 다른 문서와 내용이 겹치지 않는지 보세요. 실명이나 연락처 같은 개인정보가 들어 있다면 빼고 시작합니다.
임베딩으로 가까운 문서 찾기
임베딩은 글의 의미를 숫자 목록(벡터)으로 바꾼 것입니다. 질문과 문서를 같은 방식으로 바꾼 뒤 서로 가까운 순서로 고르면, 단어가 달라도 뜻이 비슷한 문서를 찾을 수 있습니다.
Anthropic은 임베딩 모델을 따로 제공하지 않고, 공식 문서에서 Voyage AI를 예로 듭니다. 문서가 10개 정도면 벡터 데이터베이스 없이 메모리 속 배열 하나로 충분합니다.
프롬프트 예시 사내 규정 문서 10개를 docs 리스트(id, title, text)로 만들어 뒀어. voyageai의 voyage-4 모델로 문서를 임베딩해 numpy 배열로 두고, 질문과 가장 가까운 문서 3개를 돌려주는 search 함수를 Python으로 써 줘.
AI 답변 예시 문서는 input_type="document"로, 질문은 input_type="query"로 임베딩하는 것이 핵심입니다. 문서 임베딩은 한 번 만들어 두고 다시 씁니다.
코드 예시import numpy as np import voyageai vo = voyageai.Client() # VOYAGE_API_KEY 환경 변수를 읽음 docs = [ {"id": "D03", "title": "회의실 예약", "text": "회의실은 사용일 2주 전부터 사내 예약 시스템에서 예약할 수 있습니다."}, # D01~D10을 같은 모양으로 넣기 ] texts = [d["title"] + ": " + d["text"] for d in docs] doc_vecs = np.array(vo.embed(texts, model="voyage-4", input_type="document").embeddings) def search(question, k=3): q = vo.embed([question], model="voyage-4", input_type="query").embeddings[0] scores = doc_vecs @ np.array(q) top = np.argsort(scores)[::-1][:k] return [docs[i] for i in top]input_type="document"- 문서용 임베딩을 만듭니다. 질문은 input_type="query"로 따로 만들며, 둘을 구분하면 검색 품질이 좋아집니다.
doc_vecs @ np.array(q)- 문서마다 질문과의 유사도 점수를 계산합니다. Voyage 임베딩은 길이가 1로 맞춰져 있어 내적이 곧 코사인 유사도입니다.
np.argsort(scores)[::-1][:k]- 점수가 높은 순서로 k개를 고릅니다.
확인해 보기
- LLM을 붙이기 전에 search("회의실 며칠 전부터 예약돼요?")를 실행해 D03이 맨 앞에 오는지 보세요. 검색이 틀리면 뒤의 답도 틀립니다.
찾은 문서로만 답하게 하기
검색한 문서 3개를 ID와 함께 질문 앞에 붙여 Claude에 보냅니다. 시스템 프롬프트에는 문서에 근거해서만 답하고, 근거 문서 ID를 달고, 문서에 없으면 모른다고 답하라는 규칙을 둡니다.
모른다고 답해도 된다고 허락해 두면 지어낸 답이 줄어듭니다. 답에 붙은 ID로 사람이 근거를 바로 확인할 수도 있습니다.
프롬프트 예시 앞의 search 함수로 찾은 문서 3개를 근거로 Claude가 답하는 answer 함수를 써 줘. 모델은 claude-opus-5-5야. 문서에 근거해서만 답하고, 답 끝에 근거 문서 ID를 [D03]처럼 붙이고, 문서에 없으면 “문서에서 찾지 못했습니다”라고만 답하게 해 줘.
AI 답변 예시 검색 결과를 문서 ID가 붙은 글로 묶어 질문 앞에 넣고, 규칙은 시스템 프롬프트에 둡니다.
실행 결과 예: 회의실은 사용일 2주 전부터 사내 예약 시스템에서 예약할 수 있습니다. [D03]
코드 예시import anthropic client = anthropic.Anthropic() SYSTEM = ( "너는 사내 규정 안내 챗봇이야. 아래 문서에 근거해서만 답하고, 답 끝에 근거 문서 ID를 [D03]처럼 붙여. " "문서에 없는 내용이면 “문서에서 찾지 못했습니다”라고만 답해." ) def answer(question): hits = search(question) context = "\n".join("[" + d["id"] + "] " + d["title"] + ": " + d["text"] for d in hits) response = client.messages.create( model="claude-opus-5-5", max_tokens=1024, system=SYSTEM, messages=[{"role": "user", "content": "문서:\n" + context + "\n\n질문: " + question}], ) return next(b.text for b in response.content if b.type == "text") print(answer("회의실은 며칠 전부터 예약할 수 있나요?"))search(question)- 2단계의 검색 함수로 관련 문서 3개를 가져옵니다.
"[" + d["id"] + "] "- 문서마다 ID를 붙여 답에서 근거를 [D03]처럼 가리킬 수 있게 합니다.
system=SYSTEM- 문서 밖의 내용은 답하지 않고, 없으면 찾지 못했다고 말하라는 규칙입니다.
확인해 보기
- 답 끝의 ID가 가리키는 문서를 열어 답과 내용이 맞는지 보세요.
- 문서에 없는 질문(예: 주차 지원)을 넣어 “문서에서 찾지 못했습니다”라고 답하는지도 확인하세요.
시험 질문 만들기
검색과 답이 잘 되는지 보려면 정답을 아는 질문이 필요합니다. 문서 제목을 그대로 묻는 질문만 넣지 말고, 직원이 실제로 쓰는 말투와 줄임말, 문서에 없는 질문도 섞으세요.
프롬프트 예시 아래 사내 규정 문서 10개로 챗봇 시험 질문 5개를 만들어 줘. 4개는 문서로 답할 수 있는 질문인데 직원이 실제로 쓸 법한 말투와 줄임말로 쓰고, 1개는 문서에 없는 질문으로 해 줘. 표로 정리해 줘: 질문 / 정답 문서 ID (없으면 '없음') [문서 10개 붙여 넣기]
AI 답변 예시 이렇게 만들 수 있습니다.
시험 질문 5개 (예시) 질문 정답 문서 ID 회의실은 며칠 전부터 예약할 수 있나요? D03 출장 가서 호텔비는 얼마까지 돼요? D04 집에서 일하려면 언제까지 말해야 해요? D02 법카 정산은 언제까지예요? D05 주차 지원이 있나요? 없음 확인해 보기
- 실제 문의 기록이 있다면 거기서 질문을 골라 넣는 것이 가장 좋은 시험입니다. 개인정보는 지우고 쓰세요.
검색과 답을 따로 채점하고 고치기
시험 결과는 검색과 답으로 나눠 기록하세요. 정답 문서가 상위 3개 안에 없으면 프롬프트를 아무리 고쳐도 답이 맞을 수 없습니다. 검색은 맞았는데 답이 틀렸을 때 비로소 프롬프트를 고칩니다.
이 예에서는 직원들이 '법카'라고 부르는 것을 문서는 '업무용 카드'라고 써서 검색이 놓쳤습니다. 임베딩은 뜻이 비슷한 말은 잘 찾지만 줄임말이나 고유한 이름은 놓칠 수 있습니다. 문서에 '업무용 카드(법인카드, 법카)'처럼 다른 이름을 함께 적고, 문서가 많아지면 키워드 검색(BM25)을 함께 쓰는 방법을 검토하세요.
시험 질문 5개를 돌린 결과 (예시) 질문 정답 검색 (상위 3개) 답 회의실은 며칠 전부터… D03 ○ (1위) ○ 출장 가서 호텔비는… D04 ○ (1위) ○ 집에서 일하려면… D02 ○ (2위) ○ 법카 정산은… D05 × (3위 안에 없음) × (찾지 못했다고 답함) 주차 지원이 있나요? 없음 해당 없음 ○ (찾지 못했다고 답함) 합계 4개 중 3개 5개 중 4개 확인해 보기
- D05에 다른 이름을 적고 문서 임베딩을 다시 만든 뒤, 같은 5개 질문을 다시 돌려 검색이 4개 중 4개로 오르는지 보세요.
- 문서를 고칠 때마다 시험 질문 전체를 다시 돌려, 고친 곳 때문에 다른 질문이 틀어지지 않았는지도 확인합니다.
자주 하는 실수
- 검색 결과는 보지 않고 답이 틀릴 때마다 프롬프트만 고치기
- 문서를 고친 뒤 임베딩을 다시 만들지 않기
- 문서에 없으면 모른다고 답해도 된다는 규칙을 빼서 그럴듯한 답을 지어내게 두기
참고 자료
- 임베딩 — Claude Platform 문서 (Anthropic)
- 환각 줄이기 — Claude Platform 문서 (Anthropic)
- Introducing Contextual Retrieval — Anthropic
- Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks — arXiv (Lewis et al., 2020)