RAG2026-5일15차시 · 기본 RAG 앱 만들기15 / 19← 이전목차다음 →MetaContents

15차시. 기본 RAG 앱 만들기

실습 1 — 기본 RAG 앱 만들기 실습 따라 하기

관련 수업 자료: 15차시 「3. 실습 ① — config.py」(6쪽), 15차시 「4. 실습 ② — indexer.py」(8~9쪽), 15차시 「2. 목표 구조 — 4개 모듈」(5쪽), 15차시 「5. 실습 ③ — rag.py (핵심)」(10쪽), 15차시 「7. 실습 ④ — app.py (터미널 버전)」(14쪽), 15차시 「8. 실습 ⑤ — Streamlit 웹 화면 (선택)」(16쪽) · 실습 코드 rag_app/config.py, rag_app/indexer.py, rag_app/prompts.py, rag_app/rag.py, rag_app/app.py, rag_app/web.py

이 실습은 교안 해설(15_기본RAG앱만들기.md)의 실습 순서를 옮긴 것입니다. OpenAI 임베딩이나 채팅 모델을 부르는 파일은 .env 에 API 키가 있어야 돌아갑니다. 키가 없으면 실행 결과 샘플로 출력 모양을 확인합니다.

수업 자료와 이어지는 곳

실습 코드원본 PDF이어지는 내용
rag_app/config.py6쪽15차시 「3. 실습 ① — config.py」 아래에 실었습니다
rag_app/indexer.py8~9쪽15차시 「4. 실습 ② — indexer.py」 아래에 실었습니다
rag_app/prompts.py5쪽15차시 「2. 목표 구조 — 4개 모듈」 아래에 실었습니다
rag_app/rag.py10쪽15차시 「5. 실습 ③ — rag.py (핵심)」 아래에 실었습니다
rag_app/app.py14쪽15차시 「7. 실습 ④ — app.py (터미널 버전)」 아래에 실었습니다
rag_app/web.py16쪽15차시 「8. 실습 ⑤ — Streamlit 웹 화면 (선택)」 아래에 실었습니다

실행 흐름

출력
config.py → indexer.py
config.py → rag.py
indexer.py → rag.py
rag.py → app.py
rag.py → web.py

실습 순서

실습/src/rag_app에서 키가 있을 때 python app.py로 질문하고 q로 종료합니다. 웹은 streamlit run web.py입니다. 빈 문자열과 문서에 없는 질문을 넣어 프로세스가 죽지 않는지 봅니다.

스스로 확인

  • 설정을 바꿀 때 여는 파일이 config 하나인가?
  • ask가 답만 돌려주지 않고 출처도 돌려주는가?
  • 화면을 콘솔에서 웹으로 바꿔도 rag.py를 그대로 쓰는가?

코드 설명

  • 실습 코드 rag_app/config.py 는 CHUNK_SIZE가 500, MIN_SCORE가 -0.01입니다. 수업 자료의 300, 0.47과 다르고, 줄 끝 주석("500→300")과도 어긋납니다. 실제로 쓰이는 것은 주석이 아니라 값입니다. 튜닝 결과를 옮길 때 값과 주석을 함께 고치십시오.
  • indexer.py 의 (index_path / "index.faiss").is_file() and (index_path / "index.pkl").is_file() 는 인덱스 파일 두 개가 모두 있을 때만 읽기로 갑니다. 하나라도 지워졌으면 PDF부터 다시 만듭니다. 문서를 바꿨다면 get_store(rebuild=True) 로 새로 만들어야 합니다.
  • rag.py 는 import되는 순간 load_dotenv(), get_store(), ChatOpenAI(...) 가 실행됩니다. 그래서 앱을 켤 때 한 번 느리고, 그 뒤 질문은 빠릅니다. get_store를 ask 안에 넣으면 질문마다 이 비용을 다시 냅니다.
  • ask의 ok 는 "앱이 정상 동작했는가"입니다. 검색 결과가 0건이면 안내 문장을 돌려주지만 ok는 True이고, 빈 질문과 API 오류일 때만 False입니다.
  • app.py 의 result.get("cited") is False 는 cited 키가 없을 때(빈 질문, 검색 0건, 오류) None이 되어 경고를 찍지 않습니다. if not result.get("cited") 로 쓰면 이런 경우에도 경고가 나와 버립니다.
  • web.py 는 입력이 있을 때마다 파일 전체가 처음부터 다시 실행됩니다. 그래서 대화 기록은 st.session_state.history 에 쌓아 두고, 매번 처음부터 다시 그립니다. 인덱스를 매번 불러오지 않게 하려면 9-1의 @st.cache_resource 를 씁니다.
← 이전 절15차시 · 체크리스트와 과제다음 절 →15차시 · 퀴즈 1 — 답과 출처를 함께 담은 딕셔너리 돌려주기