15차시. 기본 RAG 앱 만들기
2. 목표 구조 — 4개 모듈
출처: 원본 PDF 15차시_기본RAG앱만들기.pdf 5쪽 · 쪽 그림 5쪽
역할별로 딱 네 개면 충분합니다.
rag_app/
├── config.py⚙️ 모든 설정값
├── indexer.py🗂️ 문서 → 인덱스 (준비 단계)
├── rag.py🧠 검색 + 생성 (핵심 로직)
├── app.py💬 사용자 인터페이스
│
├── prompts.py📝 프롬프트 보관
├── data/📁 원본 문서
└── faiss_index/💾 저장된 인덱스
| 파일 | 책임 | 바뀌는 이유 |
|---|---|---|
| config.py | 설정값만 보관 | 튜닝할 때 |
| indexer.py | 문서 로딩·분할·인덱싱 | 문서 형식이 바뀔 때 |
| rag.py | 검색·생성 로직 | 알고리즘이 바뀔 때 |
| app.py | 입력받고 출력하기 | 화면이 바뀔 때 |
💡 좋은 분리의 기준
"이 파일은 언제 바뀌는가?"
바뀌는 이유가 다르면 다른 파일이어야 합니다.
· 튜닝하려고 → config만 열면 됩니다
· 화면을 바꾸려고 → app만 열면 됩니다
· 검색을 개선하려고 → rag만 열면 됩니다
한 파일을 고치는데 다른 파일도 같이 고쳐야 한다면
분리가 잘못된 것입니다.
실습 코드 · rag_app/prompts.py
실습 코드: 실습/src/rag_app/prompts.py · 위의 원본 PDF 15차시 5쪽 「2️⃣ 목표 구조 — 4개 모듈」과 이어집니다.
# ==============================================================
# 15차시. PPT 15차시_기본RAG앱만들기. 코드 폴더는 src/rag_app
# PPT: 15차시 문서 질의응답
# 역할: Generator에 넣을 프롬프트 세 장. {context}와 {question} 칸을 가진 문자열 틀입니다.
# 읽는 키: 없음
# 쓰는 키: 없음. PROMPTS 딕셔너리에 v1, v2, v3
# 그래프:
# 검색된 문서 문자열(context) + 질문(question)
# |
# v
# PROMPTS[PROMPT_VER] --> LLM
# --------------------------------------------------------------
# v1은 자료와 질문만 있는 짧은 틀입니다.
# v2는 자료 안에 있는 내용만 답하고, 없으면 '자료에서 확인할 수 없습니다'라고 하며, 문장 끝에 [1]을 달라는 규칙이 있습니다.
# v3는 질문의 일부만 자료에 있으면 그 부분만 답하라는 문장이 추가된 틀입니다.
# rag.py의 체인은 이 틀 | LLM | 문자열 변환 순서입니다.
# ==============================================================
# ChatPromptTemplate는 {context}, {question} 칸이 있는 문자열을 프롬프트 틀로 만듭니다.
from langchain_core.prompts import ChatPromptTemplate
# ── V1: 최소 버전 (비교 대조군) ──
# 자료와 질문만 넣는 가장 짧은 틀입니다. 다른 버전과 비교하는 기준으로 둡니다.
RAG_PROMPT_V1 = ChatPromptTemplate.from_template(
"아래 자료를 참고해서 질문에 답해주세요.\n\n"
"[자료]\n{context}\n\n"
"[질문] {question}"
)
# ── V2: 규칙 강화 버전 (권장) ──
# 자료 밖 내용 금지, 없으면 '확인할 수 없습니다', 근거 번호 달기 규칙이 들어 있습니다.
RAG_PROMPT_V2 = ChatPromptTemplate.from_template(
"당신은 사내 문서를 안내하는 담당자입니다.\n\n"
"[규칙]\n"
"1. 아래 [자료]에 있는 내용만 근거로 답하십시오.\n"
"2. 자료에 없는 내용은 '자료에서 확인할 수 없습니다'라고 "
"명시하십시오.\n"
"3. 추측하거나 일반 상식으로 보충하지 마십시오.\n"
"4. 각 문장 끝에 근거 번호를 [1] 형식으로 표기하십시오.\n"
"5. 3~5문장으로 간결하게 답하십시오.\n\n"
"[자료]\n{context}\n\n"
"[질문]\n{question}"
)
# ── V3: 부분 답변 허용 버전 (과잉 거부 방지) ──
# V2에서 규칙 2번만 바꿔, 일부만 자료에 있으면 그 부분은 답하게 합니다.
RAG_PROMPT_V3 = ChatPromptTemplate.from_template(
"당신은 사내 문서를 안내하는 담당자입니다.\n\n"
"[규칙]\n"
"1. 아래 [자료]에 있는 내용만 근거로 답하십시오.\n"
"2. 질문의 일부만 자료에 있다면, 있는 부분은 답하고 "
"없는 부분만 '자료에서 확인할 수 없습니다'라고 하십시오.\n"
"3. 추측하거나 일반 상식으로 보충하지 마십시오.\n"
"4. 각 문장 끝에 근거 번호를 [1] 형식으로 표기하십시오.\n"
"5. 3~5문장으로 간결하게 답하십시오.\n\n"
"[자료]\n{context}\n\n"
"[질문]\n{question}"
)
# rag.py가 PROMPTS[config.PROMPT_VER]로 틀을 고릅니다. 버전을 바꿀 때는 config.py만 고칩니다.
PROMPTS = {"v1": RAG_PROMPT_V1,
"v2": RAG_PROMPT_V2,
"v3": RAG_PROMPT_V3}실행 결과 샘플 (교안 기록. API 키가 필요한 코드는 키 없이 맞춘 예시이고, 모델의 답은 실행할 때마다 달라집니다)
===== 실행 결과 샘플 =====
직접 실행 시 출력 없음
직접 실행해도 출력이 없습니다. 틀 문자열만 정의합니다.코드 해설
Generator에 넣을 프롬프트 세 장. {context}와 {question} 칸을 가진 문자열 틀입니다.
코드 앞에서 고정해 두는 말입니다.
- 읽는 키: 없음
- 쓰는 키: 없음. PROMPTS 딕셔너리에 v1, v2, v3
- 그래프:
- 검색된 문서 문자열(context) + 질문(question)
- |
- v
- PROMPTS[PROMPT_VER] --> LLM
- v1은 자료와 질문만 있는 짧은 틀입니다.
함수로 나누지 않고 위에서 아래로 실행됩니다.
RAG_PROMPT_V1 = ChatPromptTemplate.from_template(— ── V1: 최소 버전 (비교 대조군) ──RAG_PROMPT_V2 = ChatPromptTemplate.from_template(— ── V2: 규칙 강화 버전 (권장) ──RAG_PROMPT_V3 = ChatPromptTemplate.from_template(— ── V3: 부분 답변 허용 버전 (과잉 거부 방지) ──
파일 맨 아래 실행 결과 샘플 주석은 실행되지 않습니다. 화면 출력은 다음 절에 다시 적어 두었습니다.