RAG2026-5일27차시 · 기본 Modular RAG 구현14 / 18← 이전목차다음 →MetaContents

27차시. 기본 Modular RAG 구현

실습 1 — 기본 Modular RAG 구현 실습 따라 하기

관련 수업 자료: 27차시 「4. 실습 ① — 그래프 완성」(9쪽), 27차시 「6. 실습 ③ — 루프가 도는 것 확인」(13~14쪽), 27차시 「7. 실습 ④ — 개선 효과 측정」(15~16쪽) · 실습 코드 27/graph27.py, 27/test_retry.py, 27/compare_v22_v27.py

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

수업 자료와 이어지는 곳

실습 코드원본 PDF이어지는 내용
27/graph27.py9쪽27차시 「4. 실습 ① — 그래프 완성」 아래에 실었습니다
27/test_retry.py13쪽27차시 「6. 실습 ③ — 루프가 도는 것 확인」 아래에 실었습니다
27/compare_v22_v27.py15~16쪽27차시 「7. 실습 ④ — 개선 효과 측정」 아래에 실었습니다

실행 흐름

출력
retrieve → generate  (ok)
retrieve → fallback  (empty)
generate → verify
verify → END  (pass)
verify → bump  (retry)
bump → generate
verify → END  (상한)

실습 순서

라우팅 표를 코드보다 먼저 적습니다. 조건 순서에 상한이 맨 위인지 확인한 뒤, 키가 있으면 test_retry.py로 log에 bump가 남는지 봅니다.

스스로 확인

  • bump가 따로 필요한 이유를 말하는가?
  • 상한 검사가 아래에 있으면 어떤 버그가 나는가?
  • 같은 근거로 다시 써도 안 되는 실패가 재검색 문제라는 점을 아는가?

코드 설명

  • app.stream(initial_state, {"recursion_limit": 25}) 는 노드 하나가 끝날 때마다 {노드 이름: 그 노드가 돌려준 값} 을 하나씩 내줍니다. app.invoke 는 마지막 State 만 주므로, 중간에 bump 가 몇 번 끼었는지 보려면 stream 을 씁니다.
  • graph27.py 의 route_after_verify 는 pass 와 giveup 을 먼저 보고 그다음 retries >= MAX_RETRY 를 봅니다. 수업 자료 2-3) 처럼 상한을 맨 위에 두지는 않았지만, retry 로 가는 마지막 줄보다는 앞에 있어서 무한루프는 생기지 않습니다. 분기 이름도 PDF 의 regenerate 대신 retry 를 씁니다.
  • PDF 4절 코드는 상한에 닿으면 END 로 끝내는 A안이고, graph27.py 는 "giveup": "fallback" 으로 연결한 B안입니다. 같은 차시 안에서도 두 정책이 함께 보이니 경로맵의 값을 꼭 확인하십시오.
  • graph.add_edge("bump", "generate") 한 줄이 재생성 루프입니다. bump 에서 retrieve 로 돌아가지 않으므로 documents 는 그대로이고 답만 다시 씁니다. 검색 결과가 나빠서 생긴 실패는 이 루프로 고쳐지지 않습니다.
  • compare_v22_v27.py 의 contains_answer_keyword 는 먼저 is_no_answer 로 "모른다" 안내인지 봅니다. 안내 문장 안에 우연히 '환불' 이 들어 있어도 정답으로 세지 않기 위해서입니다. no_answer 질문에서 안내가 없으면 hallucination_count 가 1 늘어납니다.
← 이전 절27차시 · 체크리스트와 과제다음 절 →27차시 · 퀴즈 1 — 상한 먼저, retry일 때만 bump로