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

18차시. State 이해

4. 실습 ① — State 정의하고 흐름 보기

출처: 원본 PDF 18차시_State이해.pdf 9쪽 · 쪽 그림 9쪽

📝 src/state_demo.py

출력
from typing import TypedDict, List
from langgraph.graph import StateGraph, START, END

class DemoState(TypedDict):
    question: str
    documents: List[str]
    answer: str

def retrieve(state: DemoState):
    print("[retrieve] 받은 State:", state)
    return {"documents": ["조각A", "조각B"]}

def generate(state: DemoState):
    print("[generate] 받은 State:", state)
    n = len(state["documents"])
    return {"answer": f"{n}건의 근거로 만든 답변"}

g = StateGraph(DemoState)
g.add_node("retrieve", retrieve)
g.add_node("generate", generate)
g.add_edge(START, "retrieve")
g.add_edge("retrieve", "generate")
g.add_edge("generate", END)

app = g.compile()
final = app.invoke({"question": "환불 규정은?"})
print("\n[최종] State:", final)

실행 결과
─────────────────────────────────────────────
─────────
[retrieve] 받은 State: {'question': '환불 규정은?'}
[generate] 받은 State: {'question': '환불 규정은?',
                        'documents': ['조각A', '조각B']}

[최종] State: {'question': '환불 규정은?',
               'documents': ['조각A', '조각B'],
               'answer': '2건의 근거로 만든 답변'}

🔍 출력에서 읽어낼 것
① 처음엔 question만 있습니다
invoke에 넣은 값만 들어 있습니다
② generate 차례가 되니 documents가 생겼습니다
retrieve가 반환한 것이 State에 합쳐졌습니다
③ question은 계속 살아 있습니다
아무도 반환하지 않았는데도 유지됩니다
④ 최종 State에 모든 것이 쌓였습니다
노드를 지날 때마다 내용이 늘어났습니다

실습 코드 · 18/state_demo.py

실습 코드: 실습/src/18/state_demo.py · 위의 원본 PDF 18차시 9쪽 「4️⃣ 실습 ① — State 정의하고 흐름 보기」과 이어집니다.

Python
# ============================================================
# 18차시 | State 이해
# 교안: 18차시_State이해.pptx
# 역할: 두 노드가 같은 State(공유 노트)를 이어 받는 모습을 출력으로 확인한다.
# 입력: question, documents, answer
# 출력: documents 와 answer 가 채워진 최종 State
#
#   State  = 그래프 전체의 공용 딕셔너리. 노드 사이 지역 변수가 아니다.
#            앞 노드가 쓴 키가 다음 노드의 인자로 그대로 들어온다.
#   Node   = retrieve, generate. 고친 키만 return 하면 그 칸만 덮어쓴다.
#   Edge   = START → retrieve → generate → END. 갈림길은 없다.
#   START / END = 시작과 끝.
#   compile = 연결 확정. invoke = 초기 State로 끝까지 실행한 최종 State.
#
# 흐름
#   START
#     |
#     v
#   retrieve     읽기 question / 쓰기 documents
#     |
#     v
#   generate     읽기 question, documents / 쓰기 answer
#     |
#     v
#    END
# ============================================================

from typing import TypedDict
from langgraph.graph import StateGraph, START, END

class DemoState(TypedDict):
    """그래프 전체에서 공유할 데이터를 정의합니다."""
    question: str          # 사용자 질문
    documents: list[str]   # 검색된 문서
    answer: str            # 최종 답변

# 노드 retrieve
#   읽기: question (출력으로 노트 전체를 보여 준다)
#   쓰기: documents
def retrieve(state: DemoState):
    """질문을 받아 관련 문서를 검색하는 노드입니다."""

    # 현재 State가 어떻게 전달되었는지 확인합니다.
    print("[retrieve] 받은 State:", state)

    # 실제 검색 대신 테스트용 문서를 반환합니다.
    documents = ["조각A", "조각B"]

    # documents만 State에 업데이트합니다.
    return {"documents": documents}

# 노드 generate
#   읽기: question, documents
#   쓰기: answer
def generate(state: DemoState):
    """검색된 문서를 이용해 답변을 만드는 노드입니다."""

    # retrieve에서 추가한 documents도 함께 전달됩니다.
    print("[generate] 받은 State:", state)

    # 검색된 문서 개수를 계산합니다.
    document_count = len(state["documents"])

    # 문서 개수를 이용해 간단한 답변을 만듭니다.
    answer = f"{document_count}건의 근거로 만든 답변"

    # answer만 State에 업데이트합니다.
    return {"answer": answer}

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

# 그래프에 노드를 등록합니다.
graph.add_node("retrieve", retrieve)
graph.add_node("generate", generate)

# START → retrieve → generate → END 순서로 연결합니다.
graph.add_edge(START, "retrieve")
graph.add_edge("retrieve", "generate")
graph.add_edge("generate", END)

# compile: 직선 연결을 실행 앱으로 확정한다.
app = graph.compile()

# 처음 사용할 State를 만듭니다.
initial_state = {
    "question": "환불 규정은?",
    "documents": [],
    "answer": ""
}

# invoke: 초기 노트를 넣고 END까지의 최종 노트를 받는다.
final_state = app.invoke(initial_state)

# 모든 노드 실행이 끝난 최종 State를 출력합니다.
print("\n[최종] State:", final_state)

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

출력
===== 실행 결과 샘플 =====
실행: cd 실습/src/18
      python state_demo.py
(실제 실행 출력)
[retrieve] 받은 State: {'question': '환불 규정은?', 'documents': [], 'answer': ''}
[generate] 받은 State: {'question': '환불 규정은?', 'documents': ['조각A', '조각B'], 'answer': ''}

[최종] State: {'question': '환불 규정은?', 'documents': ['조각A', '조각B'], 'answer': '2건의 근거로 만든 답변'}
경로: retrieve → generate → END

코드 해설

두 노드가 같은 State(공유 노트)를 이어 받는 모습을 출력으로 확인한다. 입력은 question, documents, answer이고, 출력은 documents 와 answer 가 채워진 최종 State입니다.

주석에 적힌 흐름입니다.

출력
흐름
START
|
v
retrieve     읽기 question / 쓰기 documents
|
v

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

  • 18차시 | State 이해
  • 교안: 18차시_State이해.pptx
  • State = 그래프 전체의 공용 딕셔너리. 노드 사이 지역 변수가 아니다.
  • 앞 노드가 쓴 키가 다음 노드의 인자로 그대로 들어온다.
  • Node = retrieve, generate. 고친 키만 return 하면 그 칸만 덮어쓴다.
  • Edge = START → retrieve → generate → END. 갈림길은 없다.
  • START / END = 시작과 끝.
  • compile = 연결 확정. invoke = 초기 State로 끝까지 실행한 최종 State.

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

  • DemoState: 그래프 전체에서 공유할 데이터를 정의합니다.
  • retrieve: 노드 retrieve 읽기: question (출력으로 노트 전체를 보여 준다) 쓰기: documents
  • generate: 노드 generate 읽기: question, documents 쓰기: answer

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

← 이전 절18차시 · 3. State의 동작 규칙 4가지다음 절 →18차시 · 5. Reducer — 덮어쓰지 않고 쌓기