RAG2026-5일20차시 · Edge 이해7 / 18← 이전목차다음 →MetaContents

20차시. Edge 이해

4. 실습 ① — 19차시 if문을 Edge로 옮기기

출처: 원본 PDF 20차시_Edge이해.pdf 8~9쪽 · 쪽 그림 8쪽 9쪽

19차시 manual_flow의 if문을 그대로 조건부 Edge로 바꿔봅니다.

4-1) 옮기기 전 (19차시)

출력
upd = retrieve_node(state)
state = {**state, **upd}

if state["retrieval_ok"]:          # ← 이 부분
    upd = generate_node(state)
else:
    upd = fallback_node(state)

4-2) 옮긴 뒤 (20차시)

📝 src/routes.py

Python
def route_after_retrieve(state: dict) -> str:
    # 검색 결과가 있으면 생성, 없으면 안내
    return "ok" if state.get("retrieval_ok") else "empty"

📝 src/graph_v1.py

Python
from langgraph.graph import StateGraph, START, END
from graph_state import RAGState
from nodes.retrieve import retrieve_node
from nodes.generate import generate_node
from nodes.fallback import fallback_node
from routes import route_after_retrieve

g = StateGraph(RAGState)

# ── 노드 등록 ──
g.add_node("retrieve", retrieve_node)
g.add_node("generate", generate_node)
g.add_node("fallback", fallback_node)

# ── Edge 연결 ──
g.add_edge(START, "retrieve")

g.add_conditional_edges(
    "retrieve",
    route_after_retrieve,
    {"ok": "generate", "empty": "fallback"},
)

g.add_edge("generate", END)
g.add_edge("fallback", END)

app = g.compile()

🎯 무엇이 달라졌나
if문이 사라지고 선언이 되었습니다.
"retrieve 다음에는 route로 판단해서
ok면 generate, empty면 fallback으로 간다"
코드를 읽지 않아도 구조를 그림으로 뽑아볼 수 있습니다.
if문은 그게 안 됩니다.

실습 코드 · 20/routes.py

실습 코드: 실습/src/20/routes.py · 위의 원본 PDF 20차시 8~9쪽 「4-2) 옮긴 뒤 (20차시)」과 이어집니다.

Python
# ============================================================
# 20차시 | Edge 이해
# 교안: 20차시_Edge이해.pptx
# 역할: 검색 노드 다음에 갈 이름을 고르는 조건부 엣지 함수만 둔다.
# 입력: State의 retrieval_ok
# 출력: "ok" 또는 "empty". 그래프 파일의 add_conditional_edges 가 이 문자열을 노드로 바꾼다.
#
#   State  = 이 함수는 노트를 고치지 않는다. 읽기만 한다.
#   Node   = 이 함수는 노드가 아니다. 엣지를 고르는 함수다.
#   조건부 엣지 = 반환값 "ok" → generate, "empty" → fallback (연결은 graph_v1~v3).
#
# 흐름
#   retrieve 가 끝난 직후
#     retrieval_ok 참  → "ok"
#     그 외            → "empty"
# ============================================================

# 조건부 엣지 함수 route_after_retrieve. 노드가 아니다.
#   읽기: retrieval_ok
#   쓰기: 없음
def route_after_retrieve(state: dict) -> str:
    # 검색 성공 여부를 가져옵니다.
    retrieval_ok = state.get("retrieval_ok")

    # 검색에 성공한 경우
    if retrieval_ok:
        return "ok"

    # 검색에 실패한 경우
    return "empty"

실행 결과 샘플 (교안 기록. API 키가 필요한 코드는 키 없이 맞춘 예시이고, 모델의 답은 실행할 때마다 달라집니다)

출력
===== 실행 결과 샘플 =====
실행: cd 실습/src/20
      python routes.py
(실제 실행 출력)
(표준 출력 없음. 함수만 정의한다.)

코드 해설

검색 노드 다음에 갈 이름을 고르는 조건부 엣지 함수만 둔다. 입력은 State의 retrieval_ok이고, 출력은 "ok" 또는 "empty". 그래프 파일의 add_conditional_edges 가 이 문자열을 노드로 바꾼다.

주석에 적힌 흐름입니다.

출력
흐름
retrieve 가 끝난 직후
retrieval_ok 참  → "ok"
그 외            → "empty"

코드 앞에서 고정해 두는 말입니다.

  • 20차시 | Edge 이해
  • 교안: 20차시_Edge이해.pptx
  • State = 이 함수는 노트를 고치지 않는다. 읽기만 한다.
  • Node = 이 함수는 노드가 아니다. 엣지를 고르는 함수다.
  • 조건부 엣지 = 반환값 "ok" → generate, "empty" → fallback (연결은 graph_v1~v3).

함수와 클래스로 나누어 읽습니다.

  • route_after_retrieve: 조건부 엣지 함수 route_after_retrieve. 노드가 아니다. 읽기: retrieval_ok 쓰기: 없음

파일 맨 아래 실행 결과 샘플 주석은 실행되지 않습니다. 화면 출력은 다음 절에 다시 적어 두었습니다.

실습 코드 · 20/graph_v1.py

실습 코드: 실습/src/20/graph_v1.py · 위의 원본 PDF 20차시 8~9쪽 「4-2) 옮긴 뒤 (20차시)」과 이어집니다.

Python
# ============================================================
# 20차시 | Edge 이해
# 교안: 20차시_Edge이해.pptx
# 역할: 검색 성공이면 생성, 실패면 안내로 가는 그래프를 만들기만 한다.
#       이 파일은 compile 까지 하고 invoke 하지 않는다. 화면 출력도 없다.
# 입력: 없음 (실행 시)
# 출력: 변수 app. 다른 파일이 import 해 쓸 수 있다.
#
#   State  = 18차시 RAGState. 노드들이 나눠 쓰는 공유 노트.
#   Node   = retrieve, generate, fallback (19차시 함수를 이름만 등록).
#   Edge   = START → retrieve, generate → END, fallback → END.
#   조건부 엣지 = retrieve 다음 route_after_retrieve.
#            "ok" → generate, "empty" → fallback.
#   compile = 연결을 실행 앱으로 확정. 여기서 끝이다.
#
# 노드가 읽고 쓰는 칸 (함수 본문은 19차시)
#   retrieve: 읽기 query, question, top_k
#             쓰기 documents, scores, retrieval_ok, log
#   generate: 읽기 documents, question
#             쓰기 answer, insufficient, has_citation, log
#   fallback: 읽기 tried_queries
#             쓰기 answer, grade, log
#   log 는 reducer 라 노드가 돌려준 한 줄이 기존 리스트 뒤에 붙는다.
#
# 흐름
#   START
#     |
#     v
#   retrieve
#     |
#     +-- ok ----→ generate → END
#     |
#     +-- empty --→ fallback → END
# ============================================================

import os
import sys
import datetime

from langgraph.graph import StateGraph, START, END

# ============================================================
# 1. 다른 폴더에 있는 모듈을 사용할 수 있도록 경로를 추가합니다.
# ============================================================

CURRENT_DIR = os.path.dirname(__file__)

sys.path.insert(0, os.path.join(CURRENT_DIR, "..", "18"))
sys.path.insert(0, os.path.join(CURRENT_DIR, "..", "19"))

# ============================================================
# 2. 그래프 구성에 필요한 기능을 가져옵니다.
# ============================================================

# 그래프에서 사용할 State
from graph_state import RAGState

# 검색을 담당하는 Node
from retrieve import retrieve_node

# 답변 생성을 담당하는 Node
from generate import generate_node

# 검색 실패 시 안내를 담당하는 Node
from fallback import fallback_node

# 검색 결과에 따라 다음 경로를 결정하는 함수
from routes import route_after_retrieve

# ============================================================
# 3. RAGState를 사용하는 그래프를 만듭니다.
# ============================================================

graph = StateGraph(RAGState)

# ============================================================
# 4. 그래프에서 사용할 Node를 등록합니다.
# ============================================================

# retrieve: 읽기 query/question/top_k, 쓰기 documents/scores/retrieval_ok/log
graph.add_node("retrieve", retrieve_node)
# generate: 읽기 documents/question, 쓰기 answer/insufficient/has_citation/log
graph.add_node("generate", generate_node)
# fallback: 읽기 tried_queries, 쓰기 answer/grade/log
graph.add_node("fallback", fallback_node)

# ============================================================
# 5. 그래프의 실행 순서를 연결합니다.
# ============================================================

# 그래프가 시작되면 먼저 문서를 검색합니다.
graph.add_edge(START, "retrieve")

# 조건부 엣지. 고르는 함수는 State를 고치지 않고 "ok" 또는 "empty" 만 반환한다.
graph.add_conditional_edges(
    "retrieve",
    route_after_retrieve,
    {
        "ok": "generate",       # 검색 성공 → 답변 생성
        "empty": "fallback"     # 검색 실패 → 안내 메시지
    }
)

# 답변 생성이 끝나면 그래프를 종료합니다.
graph.add_edge("generate", END)

# 안내 메시지 출력이 끝나면 그래프를 종료합니다.
graph.add_edge("fallback", END)

# ============================================================
# 6. 완성된 그래프를 실행 가능한 형태로 만듭니다.
# ============================================================

# compile: 엣지를 확정한다. invoke 는 이 파일에서 호출하지 않는다.
app = graph.compile()

실행 결과 샘플 (교안 기록. API 키가 필요한 코드는 키 없이 맞춘 예시이고, 모델의 답은 실행할 때마다 달라집니다)

출력
===== 실행 결과 샘플 =====
실행: cd 실습/src/20
      python graph_v1.py
예시 출력 (API 키 필요, 실행하지 않음)
(print 가 없다. import 때 검색 인덱스를 읽고 compile 만 한다.)
경로: retrieve → generate → END

코드 해설

검색 성공이면 생성, 실패면 안내로 가는 그래프를 만들기만 한다. 입력은 없음 (실행 시)이고, 출력은 변수 app. 다른 파일이 import 해 쓸 수 있다.

주석에 적힌 흐름입니다.

출력
흐름
START
|
v
retrieve
|
+-- ok ----→ generate → END
|
+-- empty --→ fallback → END

코드 앞에서 고정해 두는 말입니다.

  • 20차시 | Edge 이해
  • 교안: 20차시_Edge이해.pptx
  • 이 파일은 compile 까지 하고 invoke 하지 않는다. 화면 출력도 없다.
  • State = 18차시 RAGState. 노드들이 나눠 쓰는 공유 노트.
  • Node = retrieve, generate, fallback (19차시 함수를 이름만 등록).
  • Edge = START → retrieve, generate → END, fallback → END.
  • 조건부 엣지 = retrieve 다음 route_after_retrieve.
  • "ok" → generate, "empty" → fallback.

함수로 나누지 않고 위에서 아래로 실행됩니다.

  • CURRENT_DIR = os.path.dirname(__file__) — 1. 다른 폴더에 있는 모듈을 사용할 수 있도록 경로를 추가합니다.
  • graph = StateGraph(RAGState) — 3. RAGState를 사용하는 그래프를 만듭니다.
  • graph.add_node("retrieve", retrieve_node) — 4. 그래프에서 사용할 Node를 등록합니다. retrieve: 읽기 query/question/top_k, 쓰기 documents/scores/retrieval_ok/log
  • graph.add_node("generate", generate_node) — generate: 읽기 documents/question, 쓰기 answer/insufficient/has_citation/log
  • graph.add_node("fallback", fallback_node) — fallback: 읽기 tried_queries, 쓰기 answer/grade/log
  • graph.add_edge(START, "retrieve") — 5. 그래프의 실행 순서를 연결합니다. 그래프가 시작되면 먼저 문서를 검색합니다.
  • graph.add_conditional_edges( — 조건부 엣지. 고르는 함수는 State를 고치지 않고 "ok" 또는 "empty" 만 반환한다.
  • graph.add_edge("generate", END) — 답변 생성이 끝나면 그래프를 종료합니다.
  • graph.add_edge("fallback", END) — 안내 메시지 출력이 끝나면 그래프를 종료합니다.
  • app = graph.compile() — 6. 완성된 그래프를 실행 가능한 형태로 만듭니다. compile: 엣지를 확정한다. invoke 는 이 파일에서 호출하지 않는다.

파일 맨 아래 실행 결과 샘플 주석은 실행되지 않습니다. 화면 출력은 다음 절에 다시 적어 두었습니다.

실습 코드 · 20/graph_v2.py

실습 코드: 실습/src/20/graph_v2.py · 위의 원본 PDF 20차시 8~9쪽 「4-2) 옮긴 뒤 (20차시)」과 이어집니다.

Python
# ============================================================
# 20차시 | Edge 이해
# 교안: 20차시_Edge이해.pptx
# 역할: v1 과 같은 갈림길을 만든 뒤, 문자 그림과 png 파일로 연결을 확인한다.
#       질문을 invoke 하지는 않는다.
# 입력: 없음
# 출력: draw_ascii() 문자 그림, 그리고 현재 폴더의 graph.png
#
#   State  = RAGState (18차시).
#   Node   = retrieve, generate, fallback.
#   Edge   = START → retrieve, generate → END, fallback → END.
#   조건부 엣지 = "ok" → generate, "empty" → fallback.
#   compile = 그림을 그리기 전에 연결을 확정한다.
#
# 노드 칸
#   retrieve: 읽기 query/question/top_k
#             쓰기 documents/scores/retrieval_ok/log
#   generate: 읽기 documents/question
#             쓰기 answer/insufficient/has_citation/log
#   fallback: 읽기 tried_queries
#             쓰기 answer/grade/log
#
# 흐름
#   START
#     |
#     v
#   retrieve
#     |
#     +-- ok ----→ generate → END
#     |
#     +-- empty --→ fallback → END
# ============================================================

import os
import sys

from langgraph.graph import StateGraph, START, END

# ============================================================
# 1. 필요한 모듈 경로 설정
# ============================================================

# 현재 파일이 있는 폴더를 가져옵니다.
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))

# 18차시 폴더를 Python 모듈 검색 경로에 추가합니다.
sys.path.insert(0, os.path.join(CURRENT_DIR, "..", "18"))

# 19차시 폴더를 Python 모듈 검색 경로에 추가합니다.
sys.path.insert(0, os.path.join(CURRENT_DIR, "..", "19"))

# ============================================================
# 2. RAG 그래프에 필요한 모듈 가져오기
# ============================================================

# 그래프에서 사용할 State 자료형입니다.
from graph_state import RAGState

# 문서를 검색하는 노드입니다.
from retrieve import retrieve_node

# 검색된 문서를 이용해 답변을 생성하는 노드입니다.
from generate import generate_node

# 검색 결과가 없을 때 안내 메시지를 만드는 노드입니다.
from fallback import fallback_node

# 검색 성공 여부에 따라 다음 경로를 결정하는 함수입니다.
from routes import route_after_retrieve

# ============================================================
# 3. LangGraph 그래프 생성
# ============================================================

# RAGState를 사용하는 빈 그래프를 만듭니다.
graph = StateGraph(RAGState)

# ============================================================
# 4. Node 등록
# ============================================================

# retrieve: 읽기 query/question/top_k, 쓰기 documents/scores/retrieval_ok/log
graph.add_node("retrieve", retrieve_node)

# generate: 읽기 documents/question, 쓰기 answer/insufficient/has_citation/log
graph.add_node("generate", generate_node)

# fallback: 읽기 tried_queries, 쓰기 answer/grade/log
graph.add_node("fallback", fallback_node)

# ============================================================
# 5. Edge 연결
# ============================================================

# 그래프가 시작되면 먼저 문서를 검색합니다.
graph.add_edge(START, "retrieve")

# 조건부 엣지: 검색 성공(ok)과 실패(empty)가 서로 다른 노드로 갈라진다.
graph.add_conditional_edges(
    "retrieve",
    route_after_retrieve,
    {
        "ok": "generate",
        "empty": "fallback"
    }
)

# 답변 생성이 끝나면 그래프를 종료합니다.
graph.add_edge("generate", END)

# 검색 실패 안내가 끝나도 그래프를 종료합니다.
graph.add_edge("fallback", END)

# ============================================================
# 6. 그래프 실행 준비
# ============================================================

# compile: 문자 그림과 png 를 뽑을 수 있는 앱으로 확정한다.
app = graph.compile()

# ============================================================
# 7. 그래프 구조 확인
# ============================================================

# draw_ascii: 노드 상자와 화살표를 문자로 출력한다. invoke 는 아니다.
print(app.get_graph().draw_ascii())

# ============================================================
# 8. 그래프를 PNG 이미지로 저장
# ============================================================

# 그래프 구조를 PNG 이미지 데이터로 변환합니다.
png_data = app.get_graph().draw_mermaid_png()

# PNG 데이터를 graph.png 파일로 저장합니다.
with open("graph.png", "wb") as file:
    file.write(png_data)

print("graph.png 저장 완료")

실행 결과 샘플 (교안 기록. API 키가 필요한 코드는 키 없이 맞춘 예시이고, 모델의 답은 실행할 때마다 달라집니다)

출력
===== 실행 결과 샘플 =====
실행: cd 실습/src/20
      python graph_v2.py
예시 출력 (원본 폴더에 graph.png 를 쓰므로 실행하지 않음)
(draw_ascii 는 __start__ → retrieve 에서 generate 와 fallback 으로 갈라져 __end__ 로 모이는 상자 그림)
graph.png 저장 완료
경로: retrieve → generate → END

코드 해설

v1 과 같은 갈림길을 만든 뒤, 문자 그림과 png 파일로 연결을 확인한다. 입력은 없음이고, 출력은 draw_ascii() 문자 그림, 그리고 현재 폴더의 graph.png입니다.

주석에 적힌 흐름입니다.

출력
흐름
START
|
v
retrieve
|
+-- ok ----→ generate → END
|
+-- empty --→ fallback → END

코드 앞에서 고정해 두는 말입니다.

  • 20차시 | Edge 이해
  • 교안: 20차시_Edge이해.pptx
  • 질문을 invoke 하지는 않는다.
  • State = RAGState (18차시).
  • Node = retrieve, generate, fallback.
  • Edge = START → retrieve, generate → END, fallback → END.
  • 조건부 엣지 = "ok" → generate, "empty" → fallback.
  • compile = 그림을 그리기 전에 연결을 확정한다.

함수로 나누지 않고 위에서 아래로 실행됩니다.

  • CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) — 1. 필요한 모듈 경로 설정 현재 파일이 있는 폴더를 가져옵니다.
  • sys.path.insert(0, os.path.join(CURRENT_DIR, "..", "18")) — 18차시 폴더를 Python 모듈 검색 경로에 추가합니다.
  • sys.path.insert(0, os.path.join(CURRENT_DIR, "..", "19")) — 19차시 폴더를 Python 모듈 검색 경로에 추가합니다.
  • graph = StateGraph(RAGState) — 3. LangGraph 그래프 생성 RAGState를 사용하는 빈 그래프를 만듭니다.
  • graph.add_node("retrieve", retrieve_node) — 4. Node 등록 retrieve: 읽기 query/question/top_k, 쓰기 documents/scores/retrieval_ok/log
  • graph.add_node("generate", generate_node) — generate: 읽기 documents/question, 쓰기 answer/insufficient/has_citation/log
  • graph.add_node("fallback", fallback_node) — fallback: 읽기 tried_queries, 쓰기 answer/grade/log
  • graph.add_edge(START, "retrieve") — 5. Edge 연결 그래프가 시작되면 먼저 문서를 검색합니다.
  • graph.add_conditional_edges( — 조건부 엣지: 검색 성공(ok)과 실패(empty)가 서로 다른 노드로 갈라진다.
  • graph.add_edge("generate", END) — 답변 생성이 끝나면 그래프를 종료합니다.
  • graph.add_edge("fallback", END) — 검색 실패 안내가 끝나도 그래프를 종료합니다.
  • app = graph.compile() — 6. 그래프 실행 준비 compile: 문자 그림과 png 를 뽑을 수 있는 앱으로 확정한다.

파일 맨 아래 실행 결과 샘플 주석은 실행되지 않습니다. 화면 출력은 다음 절에 다시 적어 두었습니다.

실습 코드 · 20/graph_v3.py

실습 코드: 실습/src/20/graph_v3.py · 위의 원본 PDF 20차시 8~9쪽 「4-2) 옮긴 뒤 (20차시)」과 이어집니다.

Python
# ============================================================
# 20차시 | Edge 이해
# 교안: 20차시_Edge이해.pptx
# 역할: v1 과 같은 갈림길 그래프를 두 질문으로 invoke 한다.
# 입력: 질문 문자열 → 초기 State (question, query, retries, log, tried_queries)
# 출력: Q, 답변 앞 70자, 노드 log
#
#   State  = RAGState. invoke 에 넣는 딕셔너리가 첫 공유 노트다.
#   Node   = retrieve, generate, fallback.
#   Edge   = START → retrieve → (조건부) → generate 또는 fallback → END.
#   조건부 엣지 = retrieval_ok 이면 "ok", 아니면 "empty".
#   compile 후 invoke = 질문마다 END까지 한 번에 실행하고 최종 State를 받는다.
#   log reducer = 각 노드가 돌려준 log 리스트가 한 리스트로 이어 붙는다.
#
# 노드 칸
#   retrieve: 읽기 query/question/top_k
#             쓰기 documents/scores/retrieval_ok/log
#   generate: 읽기 documents/question
#             쓰기 answer/insufficient/has_citation/log
#   fallback: 읽기 tried_queries
#             쓰기 answer/grade/log
#
# 흐름
#   START → retrieve
#              ├─ ok    → generate → END
#              └─ empty → fallback → END
# ============================================================

import os
import sys
import warnings

# 불필요한 경고 메시지를 숨깁니다.
warnings.filterwarnings("ignore")

# ============================================================
# 1. 모듈 경로 설정
# ============================================================

# 현재 파일이 있는 폴더를 가져옵니다.
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))

# 필요한 차시 폴더를 Python 검색 경로에 추가합니다.
sys.path.insert(0, os.path.join(CURRENT_DIR, "..", "18"))
sys.path.insert(0, os.path.join(CURRENT_DIR, "..", "19"))

# ============================================================
# 2. 필요한 모듈 가져오기
# ============================================================

from langgraph.graph import StateGraph, START, END

# 그래프에서 사용할 State입니다.
from graph_state import RAGState

# 문서를 검색하는 노드입니다.
from retrieve import retrieve_node

# 검색된 문서로 답변을 만드는 노드입니다.
from generate import generate_node

# 검색 결과가 없을 때 안내하는 노드입니다.
from fallback import fallback_node

# 검색 결과에 따라 다음 경로를 결정합니다.
from routes import route_after_retrieve

# ============================================================
# 3. 그래프 생성
# ============================================================

# RAGState를 사용하는 빈 그래프를 만듭니다.
graph = StateGraph(RAGState)

# ============================================================
# 4. 노드 등록
# ============================================================

# retrieve: 읽기 query/question/top_k, 쓰기 documents/scores/retrieval_ok/log
graph.add_node("retrieve", retrieve_node)

# generate: 읽기 documents/question, 쓰기 answer/insufficient/has_citation/log
graph.add_node("generate", generate_node)

# fallback: 읽기 tried_queries, 쓰기 answer/grade/log
graph.add_node("fallback", fallback_node)

# ============================================================
# 5. 노드 연결
# ============================================================

# 그래프가 시작되면 retrieve 노드로 이동합니다.
graph.add_edge(START, "retrieve")

# 조건부 엣지. 검색 성공과 실패가 다른 노드로 간다.
graph.add_conditional_edges(
    "retrieve",
    route_after_retrieve,
    {
        "ok": "generate",
        "empty": "fallback",
    },
)

# 답변 생성이 끝나면 그래프를 종료합니다.
graph.add_edge("generate", END)

# 검색 실패 안내가 끝나면 그래프를 종료합니다.
graph.add_edge("fallback", END)

# ============================================================
# 6. 그래프 컴파일
# ============================================================

# compile: 위 엣지를 실행 앱으로 확정한다.
app = graph.compile()

# ============================================================
# 7. 그래프 실행 함수
# ============================================================

# 노드가 아님. 질문으로 초기 State를 만들어 invoke 하고 일부를 출력한다.
def run(question):
    """질문을 그래프에 전달하고 최종 결과를 출력합니다."""

    # 그래프 실행에 필요한 초기 State를 만듭니다.
    state = {
        "question": question,
        "query": question,
        "retries": 0,
        "log": [],
        "tried_queries": [],
    }

    # invoke: 초기 State를 넣고 END까지의 최종 State를 받는다.
    result = app.invoke(state)

    # 질문과 답변을 출력합니다.
    print("Q:", question)
    print("A:", result["answer"][:70])

    # 각 노드에서 기록한 실행 과정을 출력합니다.
    for line in result["log"]:
        print("   ", line)

    print("-" * 55)

# ============================================================
# 8. 테스트 실행
# ============================================================

# 검색 결과가 있는 질문을 테스트합니다.
run("환불은 며칠 이내인가요?")

# 검색 결과가 없는 질문을 테스트합니다.
run("대표이사 이름은?")

실행 결과 샘플 (교안 기록. API 키가 필요한 코드는 키 없이 맞춘 예시이고, 모델의 답은 실행할 때마다 달라집니다)

출력
===== 실행 결과 샘플 =====
실행: cd 실습/src/20
      python graph_v3.py
예시 출력 (API 키 필요, 실행하지 않음)
Q: 환불은 며칠 이내인가요?
A: (답변 앞 70자. 본문 미기록)
    [retrieve] '환불은 며칠 이내인가요?' → (통과 건수 미기록)
    [generate] (글자 수·인용 여부 미기록)
-------------------------------------------------------
Q: 대표이사 이름은?
A: (검색이 비면 안내 문장. 본문은 이 형식으로 잘려 출력된다. 문장 미기록)
    [retrieve] '대표이사 이름은?' → (통과 건수 미기록)
    [fallback] 안내 메시지 반환
-------------------------------------------------------
기록된 사실만: 환불은 grade pass, rewrites 0, elapsed 6.69.
              대표이사는 grade giveup, rewrites 2, elapsed 3.41.
이 파일은 그 세 값을 출력하지 않는다. 유사도 점수는 적지 않는다.
경로: retrieve → generate → END

코드 해설

v1 과 같은 갈림길 그래프를 두 질문으로 invoke 한다. 입력은 질문 문자열 → 초기 State (question, query, retries, log, tried_queries)이고, 출력은 Q, 답변 앞 70자, 노드 log입니다.

주석에 적힌 흐름입니다.

출력
흐름
START → retrieve
├─ ok    → generate → END
└─ empty → fallback → END

코드 앞에서 고정해 두는 말입니다.

  • 20차시 | Edge 이해
  • 교안: 20차시_Edge이해.pptx
  • State = RAGState. invoke 에 넣는 딕셔너리가 첫 공유 노트다.
  • Node = retrieve, generate, fallback.
  • Edge = START → retrieve → (조건부) → generate 또는 fallback → END.
  • 조건부 엣지 = retrieval_ok 이면 "ok", 아니면 "empty".
  • compile 후 invoke = 질문마다 END까지 한 번에 실행하고 최종 State를 받는다.
  • log reducer = 각 노드가 돌려준 log 리스트가 한 리스트로 이어 붙는다.

함수와 클래스로 나누어 읽습니다.

  • run: 노드가 아님. 질문으로 초기 State를 만들어 invoke 하고 일부를 출력한다.

파일 맨 아래 실행 결과 샘플 주석은 실행되지 않습니다. 화면 출력은 다음 절에 다시 적어 두었습니다.

← 이전 절20차시 · 3. 분기 함수 작성 규칙 4가지다음 절 →20차시 · 5. 실습 ② — 그래프 구조 눈으로 보기