31차시. 예외 처리 설계
실습 1 — 예외 처리 설계 실습 따라 하기
관련 수업 자료: 31차시 「5. 실습 ① — 노드별 예외 처리 표준화」(11~12쪽), 31차시 「7. 실습 ③ — 운영 로그 남기기」(15~16쪽), 31차시 「8. 실습 ④ — 장애 시뮬레이션」(17~18쪽), 31차시 「6. 실습 ② — 타임아웃과 재시도」(13~14쪽) · 실습 코드 31/safe.py, 31/logger.py, 31/analyze_logs.py, 31/test_failure.py, 31/graph31.py
이 실습은 교안 해설(31_예외처리설계.md)의 실습 순서를 옮긴 것입니다. OpenAI 임베딩이나 채팅 모델을 부르는 파일은 .env 에 API 키가 있어야 돌아갑니다. 키가 없으면 실행 결과 샘플로 출력 모양을 확인합니다.
수업 자료와 이어지는 곳
| 실습 코드 | 원본 PDF | 이어지는 내용 |
|---|---|---|
31/safe.py | 11~12쪽 | 31차시 「5. 실습 ① — 노드별 예외 처리 표준화」 아래에 실었습니다 |
31/logger.py | 15쪽 | 31차시 「7. 실습 ③ — 운영 로그 남기기」 › 7-1) 구조화 로그 아래에 실었습니다 |
31/analyze_logs.py | 15~16쪽 | 31차시 「7. 실습 ③ — 운영 로그 남기기」 › 7-2) 집계로 확인할 것들 아래에 실었습니다 |
31/test_failure.py | 17~18쪽 | 31차시 「8. 실습 ④ — 장애 시뮬레이션」 아래에 실었습니다 |
31/graph31.py | 13~14쪽 | 31차시 「6. 실습 ② — 타임아웃과 재시도」 › 6-2) 전체 타임아웃 아래에 실었습니다 |
실행 흐름
노드 함수 → safe node
safe node → 다음 분기 (성공)
safe node → 기본 State와 node error (예외)
기본 State와 node error → jsonl 로그
기본 State와 node error → 다음 분기실습 순서
python safe.py와 python analyze_logs.py를 실습/src/31에서 실행합니다. 각 노드의 실패 기본값이 분기를 어디로 보내는지 표로 적습니다. 인덱스 손상을 '자료를 찾지 못했습니다'로만 숨기면 왜 문제인지 적습니다.
스스로 확인
- 부분 실패와 전체 중단을 구분하는가?
- 조용한 실패가 왜 더 위험한가?
- fallback 문장을 이유별로 나눈 이유를 아는가?
코드 설명
safe_node(fn, state)는 노드 함수를 인자로 받아 대신 실행합니다. 수업 자료의@safe_node(...)데코레이터와 달리 실습 코드는 함수를 받아 부르는 꼴이라, graph31.py 가safe_retriever_node처럼 한 번 더 감싼 함수를 만들어 노드로 등록합니다. 두 모양을 섞어 읽으면 많이들 헷갈리십니다.except Exception as e:에서 잡은 오류는str(e)로 바꿔 node_error 에 넣습니다. 잡기만 하고 아무 데도 남기지 않으면 시스템은 살아 있지만 왜 틀리는지 아무도 모르는 조용한 실패가 됩니다.safe_verifier_node는 오류가 나면 grade 를 "pass" 로 둡니다. 검증 때문에 서비스가 막히지 않게 하려는 선택입니다. 대신 verified 를 False 로 적고,ask()도 node_error 가 있으면 verified 를 True 로 바꾸지 않습니다.route_after_rewrite는query in tried_queries일 때 다시 검색합니다. 재작성 노드가 새 검색어를 tried_queries 에 등록했는지를 보는 확인이라, 언뜻 조건이 거꾸로 보여도 맞습니다.- logger.py 의
open(..., "a")는 이어 쓰기 모드입니다. 실행할 때마다 줄이 하나씩 늘어나므로 교안에서는 실행하지 않았습니다.ensure_ascii=False는 한글을 유니코드 이스케이프 문자로 바꾸지 않고 그대로 저장하게 합니다. - test_failure.py 의
patch("graph31.retriever_node", broken_node)는 with 블록 안에서만 이름을 바꿔 끼웁니다.safe_retriever_node가 실행될 때 그 이름을 찾기 때문에, 원본 파일을 고치지 않고도 고장 난 노드로 시험할 수 있습니다.