RAG2026-5일31차시 · 예외 처리 설계6 / 19← 이전목차다음 →MetaContents

31차시. 예외 처리 설계

3. Fallback 계층화

출처: 원본 PDF 31차시_방어적코딩실습.pdf 7쪽 · 쪽 그림 7쪽

지금까지 fallback은 하나였습니다. 상황별로 나눕니다.

3-1) 상황별 안내 문구

📝 src/nodes/fallback.py

Python
import config

MESSAGES = {
    # 검색은 됐지만 관련 자료가 없음
    "no_match": (
        "'{q}'에 대한 내용을 제공된 자료에서 찾지 못했습니다.\n"
        "· 질문을 조금 다르게 표현해보시거나\n"
        "· 문서에 포함된 주제인지 확인해주세요."
    ),
    # 재작성까지 했는데도 못 찾음
    "exhausted": (
        "'{q}'에 대해 여러 방식으로 검색했지만 "
        "관련 자료를 찾지 못했습니다.\n"
        "해당 내용이 문서에 없을 가능성이 높습니다.\n"
        "담당 부서에 직접 문의해주세요."
    ),
    # 시스템 오류
    "system_error": (
        "일시적인 시스템 오류가 발생했습니다.\n"
        "잠시 후 다시 시도해주세요.\n"
        "문제가 계속되면 관리자에게 문의해주세요."
    ),
    # API 한도 초과
    "rate_limit": (
        "현재 요청이 많아 처리가 지연되고 있습니다.\n"
        "잠시 후 다시 시도해주세요."
    ),
    # 검증 실패했지만 답변은 있음
    "unverified": None,     # 답변을 그대로 쓰고 경고만 붙임
}

def _pick_reason(state) -> str:
    if state.get("gen_error") or state.get("fail_reason") == "search_error":
        return "system_error"
    if state.get("rewrites", 0) >= getattr(config, "MAX_REWRITE", 2):
        return "exhausted"
    if state.get("answer") and state.get("grade") != "pass":
        return "unverified"
    return "no_match"
def fallback_node(state) -> dict:
    kind = _pick_reason(state)
    q = state.get("question", "")

    # 검증만 실패한 경우 - 기존 답변을 살린다
    if kind == "unverified":
        return {
            "answer": state["answer"],
            "verified": False,
            "grade": "unverified",
            "log": ["fallback: 미검증 답변 제공"],
        }

    return {
        "answer": MESSAGES[kind].format(q=q[:40]),
        "verified": False,
        "grade": "giveup",
        "fallback_kind": kind,
        "log": [f"fallback: {kind}"],
    }
← 이전 절31차시 · 2. 부분 실패를 허용하는 설계다음 절 →31차시 · 4. 왜 안내 문구를 나누는가