30차시. 질의 재작성 및 재검색
실습 1 — 질의 재작성 및 재검색 실습 따라 하기
관련 수업 자료: 30차시 「5. 실습 ② — Rewrite Node 구현」(10~11쪽), 30차시 「6. 실습 ③ — 재검색 루프 연결」(12~13쪽), 30차시 「8. 실습 ④ — 14차시 실패 해결 확인」(16~17쪽) · 실습 코드 30/rewriter.py, 30/graph30.py, 30/test_rewrite.py
이 실습은 교안 해설(30_질의재작성기초.md)의 실습 순서를 옮긴 것입니다. OpenAI 임베딩이나 채팅 모델을 부르는 파일은 .env 에 API 키가 있어야 돌아갑니다. 키가 없으면 실행 결과 샘플로 출력 모양을 확인합니다.
수업 자료와 이어지는 곳
| 실습 코드 | 원본 PDF | 이어지는 내용 |
|---|---|---|
30/rewriter.py | 10쪽 | 30차시 「5. 실습 ② — Rewrite Node 구현」 아래에 실었습니다 |
30/graph30.py | 12~13쪽 | 30차시 「6. 실습 ③ — 재검색 루프 연결」 아래에 실었습니다 |
30/test_rewrite.py | 16~17쪽 | 30차시 「8. 실습 ④ — 14차시 실패 해결 확인」 아래에 실었습니다 |
실행 흐름
retrieve → rewrite (low_score)
verify → rewrite (research)
rewrite → tried queries에 누적
tried queries에 누적 → retrieve
rewrite → fallback (상한)실습 순서
rewriter.py에서 question을 덮어쓰지 않는지, tried_queries를 프롬프트에 넣는지 확인합니다. test_rewrite.py의 예시 경로는 classify에서 retrieve, rewrite, 다시 retrieve, generate, verify로 갑니다.
스스로 확인
- 어휘 불일치를 한 예로 말하는가?
- question과 query를 나눈 이유를 32차시 입장에서 말하는가?
- retries와 rewrites를 나눈 이유를 비용으로 말하는가?
코드 설명
"tried_queries": [new_query]는 새 검색어 하나만 리스트로 돌려줍니다. State 에서 이 키가Annotated[List[str], operator.add]로 선언되어 있어서, 그래프가 기존 목록 뒤에 이어 붙입니다. 전체 목록을 돌려주면 같은 검색어가 두 번씩 쌓입니다.- 성공했을 때
"retrieval_ok": False를 함께 돌려줍니다. 이전 검색의 결과 표시가 남아 있지 않게 지워 두고, 다음 retrieve 가 새 query 로 다시 판단하게 하려는 것입니다. new_query.strip()다음에strip('"'),strip("'")를 차례로 부릅니다. 모델이 검색어를 따옴표로 감싸 돌려주는 일이 많아서, 이것을 걷어 내야new_query in tried중복 검사가 제대로 됩니다.- 상한에 닿았을 때
rewrite_node는 rewrites 를 더 올리지 않고rewrite_ok: False만 돌려줍니다. 오류·빈값·중복일 때는 rewrites 를 1 올립니다. 실패해도 횟수를 올려야 rewrite → retrieve 가 끝없이 돌지 않습니다. graph30.py는 수업 자료의route_after_rewrite없이graph.add_edge("rewrite", "retrieve")로 곧장 돌아갑니다. 대신route_retrieve가rewrites >= MAX_REWRITE를 먼저 보고 fallback 으로 보내므로 루프가 끝납니다.ask()는recursion_limit을 25 로,test_rewrite.py는 30 으로 둡니다. 수업 자료 7절은 재작성과 재생성이 겹치는 경우를 생각해 30 정도를 권합니다. GraphRecursionError 가 나면 이 숫자부터 확인하십시오.