๐Ÿงญ LangGraph ์™„์ „ ์ •๋ณต โ‘ข โ€” ์ƒํƒœ ๊ด€๋ฆฌ์™€ ํ”ผ๋“œ๋ฐฑ: Reducer & MessageGraph

okorionยท2025๋…„ 10์›” 4์ผ

โ€œํ•œ ๋ฒˆ์˜ ๋Œ€ํ™”๊ฐ€ ์•„๋‹ˆ๋ผ, ์ง€์†์ ์ธ ๋งฅ๋ฝ์„ ๊ฐ€์ง„ ํ๋ฆ„์œผ๋กœ.โ€
LangGraph๋ฅผ โ€˜์ƒํƒœ ๊ธฐ๋ฐ˜ ์—์ด์ „ํŠธโ€™๋กœ ๋ฐœ์ „์‹œํ‚ค๋Š” ํ•ต์‹ฌ ๋‹จ๊ณ„.


๐Ÿงฉ 1. ์™œ ์ƒํƒœ(State)๊ฐ€ ์ค‘์š”ํ•œ๊ฐ€?

LLM์„ ๋‹จ์ˆœ ํ˜ธ์ถœ๊ธฐ๋กœ๋งŒ ์“ฐ๋ฉด, โ€œ์งˆ๋ฌธ โ†’ ๋‹ต๋ณ€ โ†’ ๋โ€์—์„œ ๋ฉˆ์ถฅ๋‹ˆ๋‹ค.
ํ•˜์ง€๋งŒ LangGraph๋Š” ์ƒํƒœ๋ฅผ ์ „์ด์‹œํ‚ค๋ฉฐ LLM์˜ ๋Œ€ํ™”๋ฅผ ์ด์–ด๊ฐ€๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด,

  • ์ด์ „ ๋‹ต๋ณ€์„ ๊ธฐ์–ตํ•˜๊ณ 
  • ์ƒˆ๋กœ์šด ์ž…๋ ฅ๊ณผ ํ†ตํ•ฉํ•˜๋ฉฐ
  • ์ƒํ™ฉ์— ๋”ฐ๋ผ ์žฌํ‰๊ฐ€ํ•˜๋Š” ํ๋ฆ„์ด ๊ฐ€๋Šฅํ•˜์ฃ .

์ด ๋ชจ๋“  ๊ฑธ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ๋ฐ”๋กœ Reducer์™€ MessageGraph์ž…๋‹ˆ๋‹ค.


โš™๏ธ 2. Reducer๋ž€ ๋ฌด์—‡์ธ๊ฐ€?

Reducer๋Š” โ€œ์ƒํƒœ๋ฅผ ๋ˆ„์  ๊ด€๋ฆฌํ•˜๋Š” ํ•จ์ˆ˜โ€์ž…๋‹ˆ๋‹ค.
์—ฌ๋Ÿฌ ๋…ธ๋“œ์˜ ๊ฒฐ๊ณผ๋ฅผ ํ•˜๋‚˜์˜ ์ƒํƒœ๋กœ ํ•ฉ์น˜๊ฑฐ๋‚˜ ๊ฐฑ์‹ ํ•  ๋•Œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

๐Ÿ“˜ ์ฐธ๊ณ : ์ด๋ฆ„์€ React์˜ useReducer ํŒจํ„ด๊ณผ ์œ ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, ๋Œ€ํ™” ๊ธฐ๋ก์„ ๋ˆ„์ ํ•˜๋ ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ•ฉ๋‹ˆ๋‹ค.

from langgraph.graph import StateGraph

class ChatState:
    history: list[str]
    user_input: str
    bot_reply: str

def reducer(prev: ChatState, new: ChatState):
    prev.history.append(f"User: {new.user_input}")
    prev.history.append(f"Bot: {new.bot_reply}")
    return prev

์ด Reducer๋ฅผ ๊ทธ๋ž˜ํ”„์— ๋“ฑ๋กํ•˜๋ฉด,
๊ฐ ๋…ธ๋“œ ์‹คํ–‰ ํ›„ ์ƒํƒœ๊ฐ€ ๋ˆ„์  ์ €์žฅ๋ฉ๋‹ˆ๋‹ค.


๐Ÿง  3. MessageGraph โ€“ ๋ฉ”์‹œ์ง€ ๊ธฐ๋ฐ˜ ์ƒํƒœ ํ๋ฆ„

LangGraph์˜ MessageGraph๋Š”
LLM๊ณผ ์‚ฌ์šฉ์ž ๋ฉ”์‹œ์ง€๋ฅผ โ€œ์ƒํƒœ ๊ฐ์ฒดโ€๋กœ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

๊ธฐ์กด LangChain์˜ ConversationBufferMemory์™€ ์œ ์‚ฌํ•˜์ง€๋งŒ,
LangGraph์—์„œ๋Š” โ€œ๊ทธ๋ž˜ํ”„ ๋‹จ์œ„โ€๋กœ ์ƒํƒœ๋ฅผ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


๐ŸŽฏ ์˜ˆ์‹œ: ๊ฐ„๋‹จํ•œ ๋Œ€ํ™” ๊ทธ๋ž˜ํ”„

from langgraph.graph import MessageGraph
from langchain_openai import ChatOpenAI

# ๋ฉ”์‹œ์ง€ ๊ธฐ๋ฐ˜ ๊ทธ๋ž˜ํ”„ ์ƒ์„ฑ
graph = MessageGraph()
llm = ChatOpenAI(model="gpt-4o-mini")

# ๋…ธ๋“œ: ๋Œ€ํ™” ์ฒ˜๋ฆฌ
def chat_node(messages):
    response = llm.invoke(messages)
    messages.append({"role": "assistant", "content": response.content})
    return messages

# ๊ทธ๋ž˜ํ”„ ๊ตฌ์„ฑ
graph.add_node("chat", chat_node)
graph.set_entry_point("chat")

# ์‹คํ–‰ (์—ฌ๋Ÿฌ ๋ฒˆ ๋Œ€ํ™” ์‹œ ์ƒํƒœ๊ฐ€ ๋ˆ„์ ๋จ)
app = graph.compile()
state = []
state = app.invoke(state + [{"role": "user", "content": "LangGraph๋ž€ ๋ญ์•ผ?"}])
state = app.invoke(state + [{"role": "user", "content": "ReAct ํŒจํ„ด์ด๋ž‘ ๋‹ฌ๋ผ?"}])

for msg in state:
    print(f"{msg['role']}: {msg['content']}")

๐Ÿ“ค ์ถœ๋ ฅ ์˜ˆ์‹œ:

user: LangGraph๋ž€ ๋ญ์•ผ?
assistant: LangGraph๋Š” LangChain ์œ„์—์„œ ์›Œํฌํ”Œ๋กœ์šฐ๋ฅผ ๊ทธ๋ž˜ํ”„๋กœ ํ‘œํ˜„ํ•˜๋Š” ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค.
user: ReAct ํŒจํ„ด์ด๋ž‘ ๋‹ฌ๋ผ?
assistant: ReAct๋Š” ์‚ฌ๊ณ -ํ–‰๋™-๊ด€์ฐฐ ๋ฃจํ”„ ํŒจํ„ด์ด๊ณ , LangGraph๋Š” ์ด๋ฅผ ๊ตฌ์กฐ์ ์œผ๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿง  ์ด ์ƒํƒœ(messages)๋Š” ๋‹ค์Œ LLM ํ˜ธ์ถœ์—์„œ๋„ ๊ทธ๋Œ€๋กœ ์ด์–ด์ง‘๋‹ˆ๋‹ค.
์ฆ‰, ๊ทธ๋ž˜ํ”„ ์ฐจ์›์˜ Memory๊ฐ€ ์ƒ๊ธด ๊ฒƒ์ด์—์š”.


๐Ÿ” 4. ํ”ผ๋“œ๋ฐฑ ๋ฃจํ”„(Feedback Loop) ์„ค๊ณ„

LangGraph์—์„œ๋Š” ๋…ธ๋“œ ๊ฐ„ ์—ฃ์ง€๋ฅผ ์ˆœํ™˜์‹œ์ผœ
์ž์ฒด ํ‰๊ฐ€(Self-Evaluation) ๋˜๋Š” ์ž๋™ ์ˆ˜์ •(Revision) ํ๋ฆ„์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

from langgraph.graph import StateGraph

class EvalState:
    question: str
    answer: str
    feedback: str
    retry: int = 0

def generate(state: EvalState):
    state.answer = f"{state.question}์˜ ๋‹ต๋ณ€์ž…๋‹ˆ๋‹ค."
    return state

def evaluate(state: EvalState):
    if "๋‹ต๋ณ€" not in state.answer:
        state.feedback = "๋ถˆ์ถฉ๋ถ„ํ•œ ๋‹ต๋ณ€"
        state.retry += 1
        return "retry"
    return "finish"

graph = StateGraph(EvalState)
graph.add_node("generate", generate)
graph.add_node("evaluate", evaluate)

graph.add_edge("generate", "evaluate")
graph.add_conditional_edges("evaluate", lambda s: "generate" if s.retry < 2 else "finish")

graph.set_entry_point("generate")
graph.set_finish_point("evaluate")

app = graph.compile()
app.invoke({"question": "LangGraph๋ž€?"})

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด LLM์˜ ๋‹ต๋ณ€ ํ’ˆ์งˆ์ด ๋ถ€์กฑํ•˜๋ฉด ์ž๋™์œผ๋กœ ๋‹ค์‹œ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
์ฆ‰, โ€œ์ž๊ฐ€ ํ‰๊ฐ€ + ์ˆ˜์ • ๋ฃจํ”„โ€๊ฐ€ ๋งŒ๋“ค์–ด์ง„ ๊ฑฐ์ฃ .


๐Ÿงฑ 5. ์ƒํƒœ ๊ด€๋ฆฌ์˜ ํŒจํ„ด๋“ค

LangGraph์—์„œ ์ƒํƒœ ๊ด€๋ฆฌ๋Š” ๋‹ค์Œ ์„ธ ๊ฐ€์ง€ ํŒจํ„ด์œผ๋กœ ๋ฐœ์ „ํ•ฉ๋‹ˆ๋‹ค.

ํŒจํ„ด์„ค๋ช…์˜ˆ์‹œ
๋‹จ์ˆœ ์ƒํƒœ(StateGraph)๊ฐ ๋…ธ๋“œ ์‹คํ–‰ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ”๋กœ ์—ฐ๊ฒฐโ€œ์ž…๋ ฅ โ†’ ๋‹ต๋ณ€ โ†’ ์ข…๋ฃŒโ€
Reducer ๋ˆ„์ ํ˜•์ƒํƒœ๋ฅผ ๊ณ„์† ๋ณ‘ํ•ฉโ€œ๋Œ€ํ™” ๊ธฐ๋ก ๋ˆ„์ โ€
Feedback ๋ฃจํ”„ํ˜•ํ‰๊ฐ€ โ†’ ์žฌ์‹คํ–‰ ๊ตฌ์กฐโ€œ์ž๊ฐ€ ์ˆ˜์ • ์—์ด์ „ํŠธโ€

์ด ์„ธ ๊ฐ€์ง€๋ฅผ ์กฐํ•ฉํ•˜๋ฉด โ€œ๊ธฐ์–ตํ•˜๊ณ , ํŒ๋‹จํ•˜๋ฉฐ, ์ˆ˜์ •ํ•˜๋Š”โ€ LLM์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


๐Ÿงฉ 6. ์‹œ๊ฐํ™” (Mermaid)

์ด ๊ตฌ์กฐ๊ฐ€ ReAct ์—์ด์ „ํŠธ ์„ค๊ณ„์˜ ์ „์ดˆ ๋‹จ๊ณ„๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.
๋‹ค์Œ ํšŒ์ฐจ์—์„œ ์‹ค์ œ โ€œThink โ†’ Act โ†’ Observeโ€ ๊ตฌ์กฐ๋กœ ๋ฐœ์ „์‹œํ‚ต๋‹ˆ๋‹ค.


๐Ÿงญ 7. ๋‹ค์Œ ํšŒ์ฐจ ์˜ˆ๊ณ 

๐Ÿ‘‰ 4ํŽธ: LangGraph์™€ Tool์˜ ์—ฐ๊ฒฐ โ€” LLM์ด ๋„๊ตฌ๋ฅผ ์ง์ ‘ ํ˜ธ์ถœํ•˜๋Š” ๊ทธ๋ž˜ํ”„

๋‹ค์Œ ํŽธ์—์„œ๋Š” LLM์ด ์™ธ๋ถ€ API(์˜ˆ: ๋‚ ์”จ, ๋‰ด์Šค, DB ์ฟผ๋ฆฌ ๋“ฑ)๋ฅผ ์ง์ ‘ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋งŒ๋“œ๋Š” ToolNode + Runnable ๊ตฌ์กฐ๋ฅผ ๋‹ค๋ฃน๋‹ˆ๋‹ค.


๐ŸŽ“ 8. ๋” ๊นŠ์ด ๋ฐฐ์šฐ๊ธฐ ์œ„ํ•œ ๊ณ ๊ธ‰ ํ™•์žฅ ํ•™์Šต ๊ฐ€์ด๋“œ

์ฃผ์ œํ•™์Šต ์ด์œ ์ถ”์ฒœ ํ•™์Šต ๋ฐฉํ–ฅ
Functional ProgrammingReducer์™€ ์ƒํƒœ ๋ˆ„์  ์›๋ฆฌ ์ดํ•ดreduce, map, lambda ํŒจํ„ด ์—ฐ์Šต
Event-Driven ArchitectureFeedback ๋ฃจํ”„๋ฅผ ์‹œ์Šคํ…œ์ ์œผ๋กœ ํ™•์žฅ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜ ์ƒํƒœ ๊ด€๋ฆฌ ๋ชจ๋ธ
LangGraph Memory InternalsLangGraph์˜ ๋ฉ”์‹œ์ง€/๋ฉ”๋ชจ๋ฆฌ ๋‚ด๋ถ€ ๊ตฌ์กฐ ์ดํ•ดMessageGraph ๋‚ด๋ถ€ ์ƒํƒœ ์ถ”์  ์ฝ”๋“œ ๋ถ„์„
LLM ํ‰๊ฐ€ ๋ฃจํ”„ ์„ค๊ณ„(Self-RAG)LLM์ด ์Šค์Šค๋กœ ๋‹ต๋ณ€ ํ‰๊ฐ€ ๋ฐ ์ˆ˜์ •ํ‰๊ฐ€-์žฌ์ƒ์„ฑ-๊ฒ€์ฆ ํŒจํ„ด ๊ตฌํ˜„
LangGraph Callback & Logging์ƒํƒœ ๋ณ€ํ™” ์ถ”์  ๋ฐ ๋กœ๊น…graph.callbacks, state diff ๋กœ๊น…
Reinforcement-style Feedback์ ์ˆ˜ ๊ธฐ๋ฐ˜ ํ•™์Šตํ˜• ๋ฃจํ”„ ์„ค๊ณ„๋ณด์ƒ ์Šค์ฝ”์–ด, ๋ฃจํ”„ ์ œ์–ด ํ•จ์ˆ˜ ์ž‘์„ฑ

๐Ÿ“š ํ•ต์‹ฌ ์š”์•ฝ

  • LangGraph๋Š” ์ƒํƒœ ์ค‘์‹ฌ ์„ค๊ณ„๋ฅผ ํ†ตํ•ด ๋Œ€ํ™”๋ฅผ ์ง€์†์‹œํ‚จ๋‹ค.
  • Reducer๋Š” ์ƒํƒœ๋ฅผ ๋ณ‘ํ•ฉํ•˜๊ณ , MessageGraph๋Š” ๋Œ€ํ™” ๋งฅ๋ฝ์„ ๋ณด์กดํ•œ๋‹ค.
  • Feedback ๋ฃจํ”„๋ฅผ ํ†ตํ•ด ์ž๊ฐ€ ํ‰๊ฐ€ํ˜• ๊ทธ๋ž˜ํ”„๊ฐ€ ๋œ๋‹ค.

๐Ÿ’ฌ ์ฆ‰, โ€œ์งˆ๋ฌธ-์‘๋‹ต ๊ทธ๋ž˜ํ”„โ€์—์„œ โ€œํ•™์Šตํ˜• ์—์ด์ „ํŠธ ๊ทธ๋ž˜ํ”„โ€๋กœ ์ง„ํ™”ํ•œ ๊ฒƒ์ด๋‹ค.

profile
okorion's Tech Study Blog.

0๊ฐœ์˜ ๋Œ“๊ธ€