25차시. Generator Node 분리
6. 실습 ③ — 재생성 시 피드백 반영
출처: 원본 PDF 25차시_GeneratorNode분리.pdf 12~13쪽 · 쪽 그림 12쪽 13쪽
27차시에서 재생성 루프를 만들 예정입니다. 그런데 같은 프롬프트로 다시 만들면 같은 답이나옵니다.
6-1) 문제 상황
1회차 생성 → 인용 번호가 없음 → 검증 실패
2회차 생성 → 똑같은 프롬프트 → 똑같이 인용 없음
3회차 생성 → 또 똑같음
→ 재시도가 무의미합니다6-2) 해결 — 실패 이유를 프롬프트에 넣기
📝 prompts.py에 추가
RETRY_PROMPT = ChatPromptTemplate.from_template(
"당신은 사내 문서를 안내하는 담당자입니다.\n\n"
"[이전 시도 결과]\n"
"직전 답변이 아래 이유로 반려되었습니다.\n"
" {reason}\n"
"이번에는 반드시 이 문제를 해결해서 작성하십시오.\n\n"
"[규칙]\n"
"1. 아래 [자료]에 있는 내용만 근거로 답하십시오.\n"
"2. 질문의 일부만 자료에 있다면 있는 부분만 답하십시오.\n"
"3. 각 문장 끝에 근거 번호를 [1] 형식으로 반드시 표기하십시오.\n"
"4. 3~5문장으로 간결하게 답하십시오.\n\n"
"[자료]\n{context}\n\n"
"[질문]\n{question}"
)
PROMPTS["retry"] = RETRY_PROMPT📝 generator_node 수정
def generator_node(state) -> dict:
docs = state.get("documents") or []
if not docs:
return {...}
retries = state.get("retries", 0)
reason = state.get("reason", "")
# 재시도이고 이유가 있으면 피드백 프롬프트 사용
if retries > 0 and reason:
chain = PROMPTS["retry"] | _llm | StrOutputParser()
payload = {"context": build_context(docs),
"question": state["question"],
"reason": reason}
else:
chain = _chain()
payload = {"context": build_context(docs),
"question": state["question"]}
answer = chain.invoke(payload)
...💡 이 설계의 효과
1회차 — 인용 없이 답변 → 검증 실패 (reason: "인용 없음")
2회차 — "인용을 반드시 넣으라"는 지시가 추가됨 → 성공
재시도가 의미 있는 재시도가 됩니다.
26차시 Verifier가 reason을 잘 채워줘야
이 구조가 제대로 작동합니다.