21차시. 첫 번째 LangGraph 실습
6. invoke vs stream
출처: 원본 PDF 21차시_첫번째LangGraph실습.pdf 11~12쪽 · 쪽 그림 11쪽 12쪽
그래프를 실행하는 방법은 두 가지입니다. 상황에 따라 골라 씁니다.
| 방식 | 반환 | 쓰는 상황 |
|---|---|---|
| invoke | 최종 State 하나 | 결과만 필요할 때 · 운영 환경 |
| stream | 노드별 중간 결과를 순차 반환 | 디버깅 · 진행 상황 표시 |
| astream | 비동기 스트리밍 | 웹 서비스 실시간 출력 |
| get_graph() | 그래프 구조 객체 | 구조 확인 · 시각화 |
6-1) invoke — 결과만
result = app.invoke(init("환불 규정은?"),
{"recursion_limit": 15})
print("답변:", result["answer"])
print("재시도:", result["retries"])
print("로그:", result["log"])실행 결과 예시 (코드를 읽고 만든 예상 화면입니다. 실제 실행 결과, 특히 모델의 답은 다를 수 있습니다)
→ retrieve: 3건
→ generate: 부실한 답변
→ verify: 재시도
→ bump: 1회차
→ generate: 좋은 답변
→ verify: 통과
답변: 3건 근거로 만든 좋은 답변
재시도: 1
로그: ['검색 3건', '생성(부실한)', '검증 실패', '재시도 1', '생성(좋은)', '검증 통과']6-2) stream — 과정까지
for step in app.stream(init("환불 규정은?"),
{"recursion_limit": 15}):
for node, update in step.items():
print(f"[{node}] {update}")
stream 출력
──────────────────────────────────────────
→ retrieve: 3건
[retrieve] {'documents': [...], 'log': ['검색 3건']}
→ generate: 부실한 답변
[generate] {'answer': '...부실한 답변', 'log': [...]}
→ verify: 재시도
[verify] {'grade': 'retry', 'log': ['검증 실패']}
→ bump: 1회차
[bump] {'retries': 1, 'log': ['재시도 1']}
→ generate: 좋은 답변 ← 루프가 돌았다!
[generate] {'answer': '...좋은 답변', 'log': [...]}
→ verify: 통과
[verify] {'grade': 'pass', 'log': ['검증 통과']}🎯 stream이 보여주는 것
루프가 실제로 돌았다는 증거가 보입니다.
generate가 두 번 등장하고,
두 번째에는 "좋은 답변"이 나와서 통과했습니다.
invoke만 썼다면 최종 결과만 보이니
루프가 돌았는지조차 몰랐을 겁니다.
개발 중에는 무조건 stream을 쓰세요.