RAG2026-5일21차시 · 첫 번째 LangGraph 실습15 / 19← 이전목차다음 →MetaContents

21차시. 첫 번째 LangGraph 실습

실습 1 — 첫 번째 LangGraph 실습 실습 따라 하기

관련 수업 자료: 21차시 「3. 실습 ① — State와 더미 노드」(6~7쪽), 21차시 「5. 실습 ③ — 그래프 조립」(9~10쪽) · 실습 코드 21/mini_graph.py, 21/mini_graph2.py, 21/mini_graph_nodetest.py, 21/mini_graph_node_conn.py, 21/mini_graph_node_image.py

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

수업 자료와 이어지는 곳

실습 코드원본 PDF이어지는 내용
21/mini_graph.py6~7쪽21차시 「3. 실습 ① — State와 더미 노드」 아래에 실었습니다
21/mini_graph2.py6~7쪽21차시 「3. 실습 ① — State와 더미 노드」 아래에 실었습니다
21/mini_graph_nodetest.py6~7쪽21차시 「3. 실습 ① — State와 더미 노드」 아래에 실었습니다
21/mini_graph_node_conn.py9쪽21차시 「5. 실습 ③ — 그래프 조립」 아래에 실었습니다
21/mini_graph_node_image.py9쪽21차시 「5. 실습 ③ — 그래프 조립」 아래에 실었습니다

실행 흐름

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

실습 순서

python mini_graph_nodetest.py로 노드 반환을 보고, python mini_graph_node_conn.py로 재시도 한 번 뒤 통과하는 경로와 상한 경로를 확인합니다. 파일 하단 샘플의 경로 줄과 출력을 대조합니다.

스스로 확인

  • 4단계 순서를 말할 수 있는가?
  • invoke와 stream을 언제 쓰는지 아는가?
  • 재귀 한도 초과가 나면 상한 검사 위치부터 보는가?

코드 설명

  • log: Annotated[List[str], operator.add] 는 log 칸에 reducer 를 붙입니다. 그래프 안에서는 노드가 돌려준 ["검색 3건"] 이 기존 리스트 뒤에 이어 붙습니다. 그래서 노드는 문자열 하나라도 꼭 리스트로 감싸서 돌려줍니다.
  • mini_graph_nodetest.py 처럼 노드를 직접 부르면 reducer 도, State 갱신도 일어나지 않습니다. 반환 딕셔너리가 그대로 보일 뿐입니다. 그래서 테스트 중간에 state["documents"] = ["조각A", "조각B"] 처럼 값을 손으로 넣습니다. 출력에서 retrieve 는 3건인데 generate 가 "2건 근거"라고 하는 이유가 이것입니다.
  • graph.add_conditional_edges("verify", after_verify, {...}) 의 세 번째 인자는 경로맵입니다. 판단 함수가 돌려줄 수 있는 문자열(done, retry, giveup)을 빠짐없이 키로 적어야 합니다. 하나라도 빠지면 KeyError 가 납니다.
  • mini_graph_node_conn.py 실행 결과에서 "생성"이 두 번 찍힌 것은 bump → generate 루프가 한 번 돈 증거입니다. 처음에는 retries 가 0 이라 "부실한" 답, bump 뒤에는 1 이라 "좋은" 답이 나와 verify 를 통과합니다.
  • 원본 PDF 의 route_verify 는 상한 검사를 맨 위에 두고, 실습 코드의 after_verify 는 pass 를 먼저 봅니다. 차이는 retries 가 상한에 닿은 순간 통과한 답이 들어올 때 드러납니다. 실습 코드는 그 답을 살리고, PDF 순서는 fallback 으로 보냅니다. 여기서 많이들 헷갈리십니다.
  • app.stream(...) 은 노드 하나가 끝날 때마다 {노드이름: 그 노드가 돌려준 칸} 을 줍니다. mini_graph_node_image.py 출력에 {'bump': {'retries': 1}} 처럼 바뀐 칸만 보이는 이유입니다. 전체 State 가 필요하면 invoke 의 반환값을 봅니다.
← 이전 절21차시 · 체크리스트와 과제다음 절 →21차시 · 퀴즈 1 — '없는' 질문에 검색 실패를 내는 더미 노드