LangGraph의 다양한 출력 방식

1. 출력 방식들

  • 기본적인 출력 방식 : 단일 출력
메서드 설명 주요 특징
invoke 그래프를 한 번 실행하고 최종 상태(State)를 반환한다. 동기 방식이며, 실행이 끝날 때까지 대기함.
ainvoke invoke의 비동기 버전. await를 사용하여 비동기 환경에서 실행 가능
batch 여러 개의 입력을 동시에 처리. 리스트 형태의 입력을 받아 병렬로 처리한 뒤 결과 리스트를 반환함
  • 스트리밍 출력 : 노드 단위의 결과물을 출력
메서드 설명 주요 특징
stream 각 노드의 결과를 노드별로 출력 • 노드 단위의 결과를 각각 출력
• 챗봇의 스트리밍 출력과는 다름
astream stream에 더해 비동기 처리 기능을 더한 방식  
스트리밍 주요 모드 설명 출력 결과
values • 그래프의 상태 전체를 스트리밍
• 노드가 실행될 때마다 업데이트된 전체 상태를 출력
전체 상태
updates • 각 노드 실행 후 변경사항(Delta)만 출력
• 각 노드의 반환값을 추적하는 데 용이
변경 사항
debug • 그래프 실행의 상세한 내부 이벤트 모드 출력
• 개발 단계에서 흐름 파악에 용이
 

2. 예시 랭그래프

  • 각 출력의 차이를 보기 위해 아래와 같은 랭그래프를 정의했다.
  • 아래 랭그래프 애플리케이션은 사용자가 입력한 숫자보다 작은 숫자가 나올 때까지 1~100 사이 숫자를 랜덤 반복 생성하는 애플리케이션이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# Graph

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

class AppState(TypedDict):
    input:int
    value:int
    count:int
    response:str

def input_routing_function(state:AppState):
    return state["input"] < 0

def generate_node(state:AppState):
    return {"value" : random.randint(1, 100), "count" : state["count"] + 1}

def routing_function(state:AppState):
    return state["input"] > state["value"]

def terminate_node(state:AppState):
    if "value" not in state.keys():
        return {"response" : f"사용자가 입력한 {state['input']}은(는) 0보다 작습니다."}
    return {"response" : f"{state['count']} 번 반복 실행됐습니다."}

graph = StateGraph(AppState)
graph.add_node("generate", generate_node)
graph.add_node("terminate", terminate_node)

graph.add_conditional_edges(START, input_routing_function, {True:"terminate", False:"generate"})
graph.add_conditional_edges("generate", routing_function, {True:"terminate", False:"generate"})
graph.add_edge("terminate", END)

app = graph.compile()

3. 기본 출력

(1) invoke

1
2
# invoke
app.invoke({"input":10, "count":0})
  • 출력
1
{'input': 10, 'value': 1, 'count': 8, 'response': '8 번 반복 실행됐습니다.'}

(2) ainvoke

1
2
3
# ainvoke
result = await app.ainvoke({"input":10, "count":0})
print(result)
  • 출력
1
{'input': 10, 'value': 9, 'count': 9, 'response': '9 번 반복 실행됐습니다.'}

(3) batch

1
2
3
4
5
6
7
# batch
works = [
    {"input":10, "count":0},
    {"input":20, "count":0},
    {"input":30, "count":0}
    ]
app.batch(works)
  • 출력
1
2
3
4
5
[
  {'input': 10, 'value': 1, 'count': 1, 'response': '1 번 반복 실행됐습니다.'},
  {'input': 20, 'value': 3, 'count': 18, 'response': '18 번 반복 실행됐습니다.'},
  {'input': 30, 'value': 5, 'count': 16, 'response': '16 번 반복 실행됐습니다.'}
  ]

5-1. stream - values

  • stream_modevalues 로 지정하면 된다.
1
2
3
4
# stream : values
mode = "values"
for result in app.stream({"input":10, "count":0}, stream_mode=mode):
    print(result)
  • 출력
1
2
3
4
5
6
7
{'input': 10, 'count': 0}
{'input': 10, 'value': 83, 'count': 1}
{'input': 10, 'value': 47, 'count': 2}
{'input': 10, 'value': 99, 'count': 3}
{'input': 10, 'value': 93, 'count': 4}
{'input': 10, 'value': 5, 'count': 5}
{'input': 10, 'value': 5, 'count': 5, 'response': '5 번 반복 실행됐습니다.'}

5-2. stream - updates

  • stream_modeupdates 로 지정하면 된다.
    • 또한, stream_mode 를 지정하지 않으면, 기본값으로 updates가 적용된다.
1
2
3
4
# stream : updates
mode = "updates"
for result in app.stream({"input":10, "count":0}, stream_mode=mode):
    print(result)
  • 출력
1
2
3
4
{'generate': {'value': 96, 'count': 1}}
{'generate': {'value': 36, 'count': 2}}
{'generate': {'value': 1, 'count': 3}}
{'terminate': {'response': '3 번 반복 실행됐습니다.'}} # -> 전체 내용이 아닌, 업데이트 된 내용만 출력

5-3. stream - debug

  • stream_modedebug 로 지정하면 된다.
1
2
3
4
# stream : debug
mode = "debug"
for result in app.stream({"input":10, "count":0}, stream_mode=mode):
    print(result)
  • 출력
1
2
3
4
5
6
7
8
9
10
{'step': 1, 'timestamp': '2026-05-03T09:05:50.366649+00:00', 'type': 'task', 'payload': {'id': 'f8fbe072-b092-8e55-73bd-6b79995a8250', 'name': 'generate', 'input': {'input': 10, 'count': 0}, 'triggers': ('branch:to:generate',)}}
{'step': 1, 'timestamp': '2026-05-03T09:05:50.366809+00:00', 'type': 'task_result', 'payload': {'id': 'f8fbe072-b092-8e55-73bd-6b79995a8250', 'name': 'generate', 'error': None, 'result': {'value': 92, 'count': 1}, 'interrupts': []}}
{'step': 2, 'timestamp': '2026-05-03T09:05:50.366884+00:00', 'type': 'task', 'payload': {'id': '6dd92e14-edb6-e27f-3f57-06774aed12ea', 'name': 'generate', 'input': {'input': 10, 'value': 92, 'count': 1}, 'triggers': ('branch:to:generate',)}}
{'step': 2, 'timestamp': '2026-05-03T09:05:50.367000+00:00', 'type': 'task_result', 'payload': {'id': '6dd92e14-edb6-e27f-3f57-06774aed12ea', 'name': 'generate', 'error': None, 'result': {'value': 61, 'count': 2}, 'interrupts': []}}
{'step': 3, 'timestamp': '2026-05-03T09:05:50.367065+00:00', 'type': 'task', 'payload': {'id': 'af0a517a-a3c3-6b3a-8e0c-9a4947eb7d72', 'name': 'generate', 'input': {'input': 10, 'value': 61, 'count': 2}, 'triggers': ('branch:to:generate',)}}
{'step': 3, 'timestamp': '2026-05-03T09:05:50.367176+00:00', 'type': 'task_result', 'payload': {'id': 'af0a517a-a3c3-6b3a-8e0c-9a4947eb7d72', 'name': 'generate', 'error': None, 'result': {'value': 13, 'count': 3}, 'interrupts': []}}
{'step': 4, 'timestamp': '2026-05-03T09:05:50.367232+00:00', 'type': 'task', 'payload': {'id': '08b09278-c833-66d4-4e9d-c63e5be47350', 'name': 'generate', 'input': {'input': 10, 'value': 13, 'count': 3}, 'triggers': ('branch:to:generate',)}}
{'step': 4, 'timestamp': '2026-05-03T09:05:50.367329+00:00', 'type': 'task_result', 'payload': {'id': '08b09278-c833-66d4-4e9d-c63e5be47350', 'name': 'generate', 'error': None, 'result': {'value': 4, 'count': 4}, 'interrupts': []}}
{'step': 5, 'timestamp': '2026-05-03T09:05:50.367387+00:00', 'type': 'task', 'payload': {'id': 'a5d102d1-eb46-9dd1-198c-bf36f6d1576c', 'name': 'terminate', 'input': {'input': 10, 'value': 4, 'count': 4}, 'triggers': ('branch:to:terminate',)}}
{'step': 5, 'timestamp': '2026-05-03T09:05:50.367455+00:00', 'type': 'task_result', 'payload': {'id': 'a5d102d1-eb46-9dd1-198c-bf36f6d1576c', 'name': 'terminate', 'error': None, 'result': {'response': '4 번 반복 실행됐습니다.'}, 'interrupts': []}}

6. astream

  • stream에 더해 비동기 처리가 가능한 출력 모드이다.
1
2
3
4
# astream : a
mode = "updates"
async for result in app.astream({"input":10, "count":0}, stream_mode=mode):
    print(result)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{'generate': {'value': 28, 'count': 1}}
{'generate': {'value': 55, 'count': 2}}
{'generate': {'value': 33, 'count': 3}}
{'generate': {'value': 33, 'count': 4}}
{'generate': {'value': 74, 'count': 5}}
{'generate': {'value': 31, 'count': 6}}
{'generate': {'value': 96, 'count': 7}}
{'generate': {'value': 65, 'count': 8}}
{'generate': {'value': 84, 'count': 9}}
{'generate': {'value': 37, 'count': 10}}
{'generate': {'value': 83, 'count': 11}}
{'generate': {'value': 81, 'count': 12}}
{'generate': {'value': 27, 'count': 13}}
{'generate': {'value': 75, 'count': 14}}
{'generate': {'value': 98, 'count': 15}}
{'generate': {'value': 2, 'count': 16}}
{'terminate': {'response': '16 번 반복 실행됐습니다.'}}

Comments