Pink Bobblehead Bunny LangGraph: ์—์ด์ „ํŠธ ์‹œ์Šคํ…œ ๊ตฌ์ถ• ์™„๋ฒฝ ๊ฐ€์ด๋“œ ๐Ÿš€
 

LangGraph: ์—์ด์ „ํŠธ ์‹œ์Šคํ…œ ๊ตฌ์ถ• ์™„๋ฒฝ ๊ฐ€์ด๋“œ ๐Ÿš€

์ตœ๊ทผ AI ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜, ํŠนํžˆ LLM(๊ฑฐ๋Œ€ ์–ธ์–ด ๋ชจ๋ธ)์„ ํ™œ์šฉํ•œ ์„œ๋น„์Šค๋Š” ํญ๋ฐœ์ ์œผ๋กœ ์ฆ๊ฐ€ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋งŽ์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์—ฌ์ „ํžˆ 'ํ”„๋กฌํ”„ํŠธ-์‘๋‹ต'์ด๋ผ๋Š” ๋‹จ์ˆœํ•œ ํŒจํ„ด์— ๋จธ๋ฌผ๋Ÿฌ ์žˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ์งˆ๋ฌธํ•˜๋ฉด, LLM์ด ํ•œ ๋ฒˆ์˜ ๋‹ต๋ณ€์„ ์ƒ์„ฑํ•˜๋Š” ์‹์ด์ฃ . ์ด๋Š” ๋งˆ์น˜ LLM์„ ๋˜‘๋˜‘ํ•œ '์กฐ์ˆ˜'๋กœ๋งŒ ํ™œ์šฉํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ํ˜„๋Œ€์˜ ๋ณต์žกํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์€ ๋‹จ์ˆœํ•œ ์งˆ์˜์‘๋‹ต์„ ๋„˜์–ด์„ญ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” AI๊ฐ€ ๋™์ ์ด๊ณ , ์œ ์—ฐํ•˜๋ฉฐ, ๊ณผ๊ฑฐ์˜ ๋Œ€ํ™”์™€ ๋งฅ๋ฝ์„ '๊ธฐ์–ต(Stateful)'ํ•˜๊ธธ ์›ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๊ณ ๊ฐ์„ผํ„ฐ ์ฑ—๋ด‡์ด ๋‹จ์ˆœํžˆ ๋‹ต๋ณ€๋งŒ ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ์Šค์Šค๋กœ DB๋ฅผ ์กฐํšŒํ•˜๊ณ , ์›น ๊ฒ€์ƒ‰์„ ์ˆ˜ํ–‰ํ•˜๋ฉฐ, ๋‹ต๋ณ€์ด ๋ถˆํ™•์‹คํ•˜๋ฉด ์‚ฌ๋žŒ(๋‹ด๋‹น์ž)์—๊ฒŒ ์Šน์ธ์„ ์š”์ฒญํ•˜๋Š” ๋“ฑ, ๋Šฅ๋™์ ์ธ ์›Œํฌํ”Œ๋กœ์šฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธธ ๊ธฐ๋Œ€ํ•ฉ๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ์š”๊ตฌ ์†์—์„œ '์—์ด์ „ํ‹ฑ AI ์‹œ์Šคํ…œ(Agentic AI Systems)'์ด๋ผ๋Š” ๊ฐœ๋…์ด ๋ถ€์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด ์‹œ์Šคํ…œ์˜ ์ค‘์‹ฌ์—๋Š” LLM์„ '์กฐ์ˆ˜'๊ฐ€ ์•„๋‹Œ, ์ „์ฒด ์›Œํฌํ”Œ๋กœ์šฐ๋ฅผ ์ œ์–ดํ•˜๋Š” '๋‘๋‡Œ' ๋˜๋Š” '์ง€ํœ˜์ž'๋กœ ๋งŒ๋“œ๋Š” ๊ฐ•๋ ฅํ•œ ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

์˜ค๋Š˜ ์šฐ๋ฆฌ๊ฐ€ ๊นŠ์ด ์žˆ๊ฒŒ ๋‹ค๋ฃฐ ์ฃผ์ œ๊ฐ€ ๋ฐ”๋กœ '๋žญ๊ทธ๋ž˜ํ”„(LangGraph)'์ž…๋‹ˆ๋‹ค. LangGraph๋Š” LangChain ์œ„์— ๊ตฌ์ถ•๋œ ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ด์…˜ ํ”„๋ ˆ์ž„์›Œํฌ๋กœ, ๋ณต์žกํ•˜๊ณ  ์ƒํƒœ ๊ธฐ๋ฐ˜์ด๋ฉฐ ์ˆœํ™˜์ ์ธ(cyclic) ์›Œํฌํ”Œ๋กœ์šฐ๋ฅผ ๊ตฌ์ถ•ํ•˜๋Š” ๋ฐ ์ตœ์ ํ™”๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๊ธ€์—์„œ๋Š” LangGraph๊ฐ€ ๋ฌด์—‡์ธ์ง€, ๊ธฐ์กด LangChain์˜ ํ•œ๊ณ„๋ฅผ ์–ด๋–ป๊ฒŒ ๊ทน๋ณตํ–ˆ๋Š”์ง€, ๊ทธ ํ•ต์‹ฌ ์•„ํ‚คํ…์ฒ˜๋Š” ๋ฌด์—‡์ด๋ฉฐ, ์ด๋ฅผ ํ™œ์šฉํ•ด ์‹ค๋ฌด์ ์ธ ์—์ด์ „ํ‹ฑ ์‹œ์Šคํ…œ์„ ๊ตฌ์ถ•ํ•˜๋Š” 5๋‹จ๊ณ„ ์ ‘๊ทผ๋ฒ•๊ณผ ๊ธฐ์ˆ ์  ๊ณผ์ œ๊นŒ์ง€, A๋ถ€ํ„ฐ Z๊นŒ์ง€ ์‹ฌ์ธต์ ์œผ๋กœ ๋ถ„์„ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.


1. ์—์ด์ „ํ‹ฑ AI ์‹œ์Šคํ…œ์ด๋ž€ ๋ฌด์—‡์ธ๊ฐ€? ๐Ÿค”

LangGraph๋ฅผ ์ดํ•ดํ•˜๊ธฐ ์ „์—, ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“ค๊ณ ์ž ํ•˜๋Š” '์—์ด์ „ํ‹ฑ ์‹œ์Šคํ…œ'์ด ๋ฌด์—‡์ธ์ง€ ๋ช…ํ™•ํžˆ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์—์ด์ „ํ‹ฑ ์‹œ์Šคํ…œ์€ LLM์„ ์ œ์–ด ๋ฃจํ”„(control loop)์˜ ์ค‘์‹ฌ์— ๋ฐฐ์น˜ํ•˜์—ฌ, ๊ณ ์ •๋œ ์ˆœ์„œ๋ฅผ ๋”ฐ๋ฅด๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ๋™์ ์œผ๋กœ ๋‹ค์Œ ํ–‰๋™์„ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค๊ณ„๋œ ์‹œ์Šคํ…œ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

1.1. LLM ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ž์œจ์„ฑ ์ŠคํŽ™ํŠธ๋Ÿผ

๋ชจ๋“  LLM ์•ฑ์ด ๋™์ผํ•œ ์ˆ˜์ค€์˜ ์ž์œจ์„ฑ์„ ๊ฐ–๋Š” ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค. ์ €๋Š” ๊ฐœ์ธ์ ์œผ๋กœ ์ž์œจ์„ฑ ์ˆ˜์ค€์„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด 4๋‹จ๊ณ„๋กœ ๊ตฌ๋ถ„ํ•ฉ๋‹ˆ๋‹ค.

  • Level 0: ๋‹จ์ˆœ ํ”„๋กฌํ”„ํŠธ (Single Prompt)
    • ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ํ˜•ํƒœ๋กœ, ChatGPT์™€ ๊ฐ™์€ ์ธํ„ฐํŽ˜์ด์Šค์—์„œ ์‚ฌ์šฉ์ž๊ฐ€ ์ง์ ‘ ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ž…๋ ฅํ•˜๊ณ  ๋‹จ์ผ ์‘๋‹ต์„ ๋ฐ›์Šต๋‹ˆ๋‹ค.
  • Level 1: ๊ณ ์ •๋œ ์ฒด์ธ (Fixed Chain)
    • LangChain์˜ LCEL (LangChain Expression Language)์„ ์‚ฌ์šฉํ•ด [ํ”„๋กฌํ”„ํŠธ -> LLM -> ์ถœ๋ ฅ ํŒŒ์„œ]์ฒ˜๋Ÿผ ์ •ํ•ด์ง„ ์ˆœ์„œ(DAG, Directed Acyclic Graph)๋กœ ํŒŒ์ดํ”„๋ผ์ธ์„ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  • Level 2: ๋„๊ตฌ ์‚ฌ์šฉ ์—์ด์ „ํŠธ (Tool-using Agent)
    • ReAct (Reason + Act)์™€ ๊ฐ™์€ ํŒจํ„ด์„ ์‚ฌ์šฉํ•ด, LLM์ด '์ƒ๊ฐ'ํ•˜๊ณ  '๋„๊ตฌ(Tool)'๋ฅผ ์„ ํƒํ•˜์—ฌ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. (e.g., LangChain์˜ ๊ธฐ๋ณธ Agents) ํ•˜์ง€๋งŒ ์—ฌ์ „ํžˆ ์ƒํƒœ ๊ด€๋ฆฌ๊ฐ€ ์–ด๋ ต๊ณ  ํ๋ฆ„์ด ๊ฒฝ์ง๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • Level 3: ๋™์ /์ƒํƒœ ๊ธฐ๋ฐ˜ ์—์ด์ „ํŠธ (Stateful Agent)
    • ๋ฐ”๋กœ LangGraph๊ฐ€ ์ง€ํ–ฅํ•˜๋Š” ๋ชฉํ‘œ์ž…๋‹ˆ๋‹ค. LLM์ด ์›Œํฌํ”Œ๋กœ์šฐ์˜ '์ƒํƒœ'๋ฅผ ๋ช…ํ™•ํžˆ ์ธ์ง€ํ•˜๊ณ , ์กฐ๊ฑด์— ๋”ฐ๋ผ ๋ถ„๊ธฐ(branching)ํ•˜๊ฑฐ๋‚˜, ํŠน์ • ์ž‘์—…์„ ๋ฐ˜๋ณต(looping)ํ•˜๊ณ , ํ•„์š”์‹œ ์ธ๊ฐ„์˜ ๊ฐœ์ž…(human-in-the-loop)์„ ์š”์ฒญํ•˜๋Š” ๋“ฑ ๊ณ ๋„๋กœ ๋™์ ์ธ ์ž‘์—… ์ˆ˜ํ–‰์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

1.2. ์—์ด์ „ํ‹ฑ ์‹œ์Šคํ…œ์˜ 3๋Œ€ ํ•ต์‹ฌ ์š”์†Œ

์—์ด์ „ํ‹ฑ ์‹œ์Šคํ…œ์€ ๋‹ค์Œ ์„ธ ๊ฐ€์ง€ ์š”์†Œ๊ฐ€ ์œ ๊ธฐ์ ์œผ๋กœ ๊ฒฐํ•ฉ๋˜์–ด ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

  1. ๐Ÿง  LLM as a "Brain": LLM์ด ์‚ฌ์šฉ์ž ์ž…๋ ฅ๊ณผ ํ˜„์žฌ ์ปจํ…์ŠคํŠธ(์ƒํƒœ)๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋‹ค์Œ์— ์ˆ˜ํ–‰ํ•  ์ž‘์—…์„ ๋™์ ์œผ๋กœ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค. (e.g., "๋ฌธ์„œ ๊ฒ€์ƒ‰์ด ํ•„์š”ํ•œ๊ฐ€?", "์›น ๊ฒ€์ƒ‰์ด ํ•„์š”ํ•œ๊ฐ€?", "์ธ๊ฐ„์˜ ์Šน์ธ์ด ํ•„์š”ํ•œ๊ฐ€?")
  2. ๐Ÿ› ๏ธ ๋„๊ตฌ ํ†ตํ•ฉ (Tool Integration): LLM์˜ ํ•œ๊ณ„๋ฅผ ๋ณด์™„ํ•˜๊ธฐ ์œ„ํ•ด ์™ธ๋ถ€ ๋„๊ตฌ(๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค, ์›น ๊ฒ€์ƒ‰ API, ๋งž์ถคํ˜• ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง)๋ฅผ ํ†ตํ•ฉํ•˜์—ฌ ๊ธฐ๋Šฅ์„ ํ™•์žฅํ•ฉ๋‹ˆ๋‹ค.
  3. ๐Ÿ—ƒ๏ธ ์ƒํƒœ ๋ฐ ๋ฉ”๋ชจ๋ฆฌ (State & Memory): ๋‚ด์žฅ๋œ ์ง€์†์„ฑ(persistence) ๋ฐ ๋ฉ”๋ชจ๋ฆฌ ๊ธฐ๋Šฅ์„ ํ™œ์šฉํ•ด ๋Œ€ํ™” ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์—ฌ๋Ÿฌ ํ„ด์— ๊ฑธ์นœ ์žฅ๊ธฐ์ ์ธ ์ƒํ˜ธ์ž‘์šฉ์—์„œ๋„ ์ผ๊ด€์„ฑ์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, ๊ณ ๋„ํ™”๋œ RAG(๊ฒ€์ƒ‰ ์ฆ๊ฐ• ์ƒ์„ฑ) ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์ƒ๊ฐํ•ด ๋ด…์‹œ๋‹ค. ๊ธฐ์กด RAG๊ฐ€ [์ฟผ๋ฆฌ -> ๊ฒ€์ƒ‰ -> LLM ์‘๋‹ต]์˜ ๋‹จ๋ฐฉํ–ฅ ํ๋ฆ„์ด์—ˆ๋‹ค๋ฉด, ์—์ด์ „ํ‹ฑ RAG๋Š” ๋‹ค๋ฆ…๋‹ˆ๋‹ค.

"์—์ด์ „ํŠธ๋Š” ๋จผ์ € ๋ฒกํ„ฐ DB์—์„œ ๋ฌธ์„œ๋ฅผ ๊ฒ€์ƒ‰(Tool 1)ํ•˜๊ณ , ํ•ด๋‹น ๋ฌธ์„œ์˜ ์ ์ ˆ์„ฑ์„ ์Šค์Šค๋กœ ํ‰๊ฐ€(LLM Brain)ํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ๋ฌธ์„œ๊ฐ€ ๋ถˆ์ถฉ๋ถ„ํ•˜๋‹ค๊ณ  ํŒ๋‹จ๋˜๋ฉด, ์›น ๊ฒ€์ƒ‰(Tool 2)์„ ์ถ”๊ฐ€๋กœ ์ˆ˜ํ–‰ํ• ์ง€ ๊ฒฐ์ •(LLM Brain)ํ•œ ๋’ค, ๋ชจ๋“  ์ •๋ณด๋ฅผ ์ข…ํ•ฉํ•˜์—ฌ ์ตœ์ข… ์‘๋‹ต์„ ์ƒ์„ฑ(LLM Brain)ํ•ฉ๋‹ˆ๋‹ค."

์ด๋Ÿฌํ•œ ๋ณต์žกํ•œ '๊ฒฐ์ •'๊ณผ 'ํ๋ฆ„ ์ œ์–ด'๋ฅผ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•˜๋Š” ๊ฒƒ์ด ๋ฐ”๋กœ LangGraph์ž…๋‹ˆ๋‹ค.


2. LangGraph ์•„ํ‚คํ…์ฒ˜ ์‹ฌ์ธต ๋ถ„์„ ๐Ÿ—๏ธ

๊ทธ๋ ‡๋‹ค๋ฉด LangGraph๋Š” ๊ธฐ์ˆ ์ ์œผ๋กœ ์–ด๋–ป๊ฒŒ ์ด๋Ÿฐ ๋ณต์žกํ•œ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ• ๊นŒ์š”?

2.1. LangChain (LCEL) vs. LangGraph: ์™œ ์ƒˆ๋กœ์šด ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ํ•„์š”ํ–ˆ๋‚˜?

LangChain์€ LCEL(LangChain Expression Language)์„ ํ†ตํ•ด Runnable ๊ฐ์ฒด๋“ค์„ ํŒŒ์ดํ”„(|)๋กœ ์—ฐ๊ฒฐํ•˜์—ฌ ๋ฉ‹์ง„ ์ฒด์ธ์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ LCEL์€ ๊ทผ๋ณธ์ ์œผ๋กœ DAG (Directed Acyclic Graph, ๋ฐฉํ–ฅ์„ฑ ๋น„์ˆœํ™˜ ๊ทธ๋ž˜ํ”„)์ž…๋‹ˆ๋‹ค. ์ฆ‰, ๋ฐ์ดํ„ฐ ํ๋ฆ„์ด ํ•œ ๋ฐฉํ–ฅ์œผ๋กœ๋งŒ ํ๋ฅด๊ณ , ์ˆœํ™˜(Loop)์ด๋‚˜ ๋ณต์žกํ•œ ์กฐ๊ฑด๋ถ€ ๋ถ„๊ธฐ๊ฐ€ ๋งค์šฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“ค๋ ค๋Š” ์—์ด์ „ํ‹ฑ ์‹œ์Šคํ…œ์€ ๋ณธ์งˆ์ ์œผ๋กœ ์ˆœํ™˜์ (Cyclic)์ž…๋‹ˆ๋‹ค. [์ƒ๊ฐ -> ํ–‰๋™ -> ๊ด€์ฐฐ -> ๋‹ค์‹œ ์ƒ๊ฐ...]ํ•˜๋Š” ReAct ํŒจํ„ด ์ž์ฒด๊ฐ€ ์ˆœํ™˜ ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค.

LangGraph๋Š” ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋“ฑ์žฅํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฆ„ ๊ทธ๋Œ€๋กœ '๊ทธ๋ž˜ํ”„(Graph)' ๊ตฌ์กฐ๋ฅผ ์ „๋ฉด์— ๋‚ด์„ธ์›๋‹ˆ๋‹ค. LCEL์ด DAG์— ๋จธ๋ฌด๋ฅธ๋‹ค๋ฉด, LangGraph๋Š” ์ƒํƒœ๋ฅผ ๊ฐ€์ง€๋Š” ์ˆœํ™˜ ๊ทธ๋ž˜ํ”„(Stateful Cyclic Graph)๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค.

2.2. ํ•ต์‹ฌ ๊ตฌ์„ฑ์š”์†Œ 1: ๋…ธ๋“œ (Nodes) ๐Ÿ“ฆ

๋…ธ๋“œ(Node)๋Š” ์›Œํฌํ”Œ๋กœ์šฐ์˜ ๊ฐœ๋ณ„ ์ž‘์—… ๋˜๋Š” ๊ธฐ๋Šฅ์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

  • LLM ํ˜ธ์ถœ
  • ๋ฒกํ„ฐ DB ๊ฒ€์ƒ‰ ๋‹จ๊ณ„
  • ์‚ฌ์šฉ์ž ์ฟผ๋ฆฌ ์žฌ์ž‘์„ฑ
  • ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ ๋กœ์ง
  • ์™ธ๋ถ€ API (๋„๊ตฌ) ํ˜ธ์ถœ

๊ฐ ๋…ธ๋“œ๋Š” Python ํ•จ์ˆ˜ ๋˜๋Š” LCEL Runnable๋กœ ๊ตฌํ˜„๋˜๋ฉฐ, ํŠน์ • ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•˜์—ฌ ๋ชจ๋“ˆํ™”๋˜๊ณ  ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

2.3. ํ•ต์‹ฌ ๊ตฌ์„ฑ์š”์†Œ 2: ์—ฃ์ง€ (Edges) ๐Ÿ”—

์—ฃ์ง€(Edge)๋Š” ๋…ธ๋“œ ๊ฐ„์˜ ์—ฐ๊ฒฐ์„ ์ •์˜ํ•˜์—ฌ ์‹คํ–‰ ํ๋ฆ„์„ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค. LangGraph์˜ ํ•ต์‹ฌ์ ์ธ ๊ฐ•๋ ฅํ•จ์€ ๋ฐ”๋กœ ์ด ์—ฃ์ง€์— ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์ผ๋ฐ˜ ์—ฃ์ง€ (Direct Edges): (A, B)์ฒ˜๋Ÿผ A ๋…ธ๋“œ๊ฐ€ ๋๋‚˜๋ฉด ๋ฌด์กฐ๊ฑด B ๋…ธ๋“œ๋กœ ์ด๋™ํ•˜๋Š” ๋‹จ์ˆœ ์—ฐ๊ฒฐ์ž…๋‹ˆ๋‹ค.
  • ์กฐ๊ฑด๋ถ€ ์—ฃ์ง€ (Conditional Edges): (A, {condition_function})์ฒ˜๋Ÿผ A ๋…ธ๋“œ์˜ ์‹คํ–‰ ๊ฒฐ๊ณผ(์ƒํƒœ)๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ, ํŠน์ • ํ•จ์ˆ˜(condition_function)๊ฐ€ ๋‹ค์Œ์— ์ด๋™ํ•  ๋…ธ๋“œ(B, C, ๋˜๋Š” D)๋ฅผ ๋™์ ์œผ๋กœ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.

์ด '์กฐ๊ฑด๋ถ€ ์—ฃ์ง€' ๋•๋ถ„์— LLM์˜ ์ถœ๋ ฅ์„ ๋ฐ”ํƒ•์œผ๋กœ "๋‹ค์Œ์—” ์ด ๋„๊ตฌ๋ฅผ ์จ์•ผ๊ฒ ๋‹ค" ๋˜๋Š” "์ด์ œ ์‘๋‹ต์„ ์ƒ์„ฑํ•ด๋„ ๋˜๊ฒ ๋‹ค"์™€ ๊ฐ™์€ ๋ณต์žกํ•œ ๋ถ„๊ธฐ ๋กœ์ง(branching)๊ณผ ๋ฐ˜๋ณต ๋กœ์ง(looping)์ด ๊ฐ€๋Šฅํ•ด์ง‘๋‹ˆ๋‹ค.

2.4. ํ•ต์‹ฌ ๊ตฌ์„ฑ์š”์†Œ 3: ๊ณต์œ  ์ƒํƒœ (Shared State) ๐Ÿ—ƒ๏ธ

LangGraph์˜ ๋ชจ๋“  ๋งˆ๋ฒ•์€ ์ƒํƒœ(State) ๊ฐ์ฒด์—์„œ ์ผ์–ด๋‚ฉ๋‹ˆ๋‹ค. ์ด ์ƒํƒœ ๊ฐ์ฒด๋Š” ์‹œ์Šคํ…œ ์ „์ฒด์˜ '๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ' ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

  • ์ƒํƒœ ๊ด€๋ฆฌ: ๋ชจ๋“  ๋…ธ๋“œ๋Š” ์ด ๊ณตํ†ต ์ƒํƒœ๋ฅผ ์ฝ๊ณ (read), ์ผ๋ถ€๋ฅผ ์ˆ˜์ •ํ•˜์—ฌ(update) ๋‹ค์Œ ๋…ธ๋“œ๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ณต์œ  ์ปจํ…์ŠคํŠธ(์‚ฌ์šฉ์ž ์ฟผ๋ฆฌ, ๋Œ€ํ™” ๊ธฐ๋ก, ๋„๊ตฌ ์ถœ๋ ฅ ๋“ฑ)๋ฅผ ํ†ตํ•ด ์›Œํฌํ”Œ๋กœ์šฐ๊ฐ€ ๋™์ ์œผ๋กœ ๋ฐœ์ „ํ•ฉ๋‹ˆ๋‹ค.
  • ๊ตฌํ˜„: ๋ณดํ†ต Python์˜ TypedDict๋‚˜ Pydantic ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ƒํƒœ์˜ ์Šคํ‚ค๋งˆ๋ฅผ ๋ช…ํ™•ํ•˜๊ฒŒ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ์ด ์Šคํ‚ค๋งˆ๋Š” ๊ทธ๋ž˜ํ”„์˜ 'ํ˜ˆ๊ด€'์„ ํ๋ฅด๋Š” 'ํ˜ˆ์•ก'์˜ ์„ฑ๋ถ„์„ ์ •์˜ํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

2.5. ํ•ต์‹ฌ ๊ตฌ์„ฑ์š”์†Œ 4: ์ง€์†์„ฑ (Persistence) ๐Ÿ’พ

๊ทธ๋ž˜ํ”„๊ฐ€ ์•„๋ฌด๋ฆฌ ์ž˜ ์ž‘๋™ํ•ด๋„, ์„œ๋ฒ„๊ฐ€ ์žฌ์‹œ์ž‘๋˜๊ฑฐ๋‚˜ ์‚ฌ์šฉ์ž๊ฐ€ ๋Œ€ํ™”๋ฅผ ๋‚˜์ค‘์— ์ด์–ด๊ฐ€๊ณ  ์‹ถ์„ ๋•Œ ๋งฅ๋ฝ์ด ์‚ฌ๋ผ์ง„๋‹ค๋ฉด ๋ฌด์šฉ์ง€๋ฌผ์ž…๋‹ˆ๋‹ค.

LangGraph๋Š” Checkpointer๋ผ๋Š” ๊ฐ•๋ ฅํ•œ ์ง€์†์„ฑ ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

  • ์—ญํ• : ์›Œํฌํ”Œ๋กœ์šฐ์˜ '์ƒํƒœ'๋ฅผ ๋งค ๋‹จ๊ณ„๋งˆ๋‹ค (๋˜๋Š” ํŠน์ • ๋…ธ๋“œ ์ดํ›„์—) ์™ธ๋ถ€ ์ €์žฅ์†Œ(e.g., Sqlite, Redis, PostgreSQL)์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.
  • ์žฅ์ :
    1. ์žฅ๊ธฐ ์ƒํ˜ธ์ž‘์šฉ: ์‚ฌ์šฉ์ž๊ฐ€ ๋ฉฐ์น  ๋’ค์— ๋‹ค์‹œ ์ ‘์†ํ•ด๋„ ์ด์ „ ๋Œ€ํ™” ์ƒํƒœ๋ฅผ ๊ทธ๋Œ€๋กœ ๋ณต์›ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    2. ์˜ค๋ฅ˜ ๋ณต๊ตฌ: ์›Œํฌํ”Œ๋กœ์šฐ ์ค‘๊ฐ„์— ํŠน์ • ๋„๊ตฌ API๊ฐ€ ์‹คํŒจํ•˜๋”๋ผ๋„, ๋งˆ์ง€๋ง‰ '์ฒดํฌํฌ์ธํŠธ'๋ถ€ํ„ฐ ๋‹ค์‹œ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    3. Human-in-the-loop: ์›Œํฌํ”Œ๋กœ์šฐ๋ฅผ ํŠน์ • ์ง€์ ์—์„œ '์ผ์‹œ ์ค‘์ง€'์‹œํ‚ค๊ณ , ์ธ๊ฐ„์˜ ์Šน์ธ์„ ๋ฐ›์€ ๋’ค ๋‹ค์‹œ ์ด์–ด๊ฐˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

3. LangGraph์˜ ์ฃผ์š” ํŠน์ง• ๋ฐ ํ™œ์šฉ ์‚ฌ๋ก€ ๐ŸŒŸ

์ด๋Ÿฌํ•œ ์•„ํ‚คํ…์ฒ˜๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ LangGraph๋Š” ๋ช‡ ๊ฐ€์ง€ ๊ฐ•๋ ฅํ•œ ํŠน์ง•์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

3.1. ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ ํ†ตํ•ฉ (Tool Integration)

์—์ด์ „ํŠธ ์‹œ์Šคํ…œ์€ LLM์ด ๋ชปํ•˜๋Š” ์ผ(e.g., ์‹ค์‹œ๊ฐ„ ์›น ๊ฒ€์ƒ‰, DB ์กฐํšŒ)์„ ํ•˜๊ธฐ ์œ„ํ•ด ์™ธ๋ถ€ ๋„๊ตฌ์— ์˜์กดํ•ฉ๋‹ˆ๋‹ค. LangGraph๋Š” ToolNode์™€ ๊ฐ™์€ ํ—ฌํผ๋ฅผ ์ œ๊ณตํ•˜์—ฌ ๋„๊ตฌ ์—ฐ๋™์„ ๋งค์šฐ ์‰ฝ๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

์กฐ๊ฑด๋ถ€ ์—ฃ์ง€์™€ ๋„๊ตฌ ํ†ตํ•ฉ์ด ๋งŒ๋‚˜๋ฉด, "LLM์ด ์–ด๋–ค ๋„๊ตฌ๋ฅผ ์–ธ์ œ ํ˜ธ์ถœํ• ์ง€" ๋™์ ์œผ๋กœ ๊ฒฐ์ •ํ•˜๊ณ , ๋„๊ตฌ ์‹คํ–‰ ๊ฒฐ๊ณผ๋ฅผ ๋‹ค์‹œ ์ƒํƒœ์— ๋ฐ˜์˜ํ•˜์—ฌ ๋‹ค์Œ LLM์˜ ํŒ๋‹จ ๊ทผ๊ฑฐ๋กœ ์‚ฌ์šฉํ•˜๋Š” ์ •๊ตํ•œ ์›Œํฌํ”Œ๋กœ์šฐ๊ฐ€ ์™„์„ฑ๋ฉ๋‹ˆ๋‹ค.

3.2. ์‹ค์‹œ๊ฐ„ ์ฒ˜๋ฆฌ (Streaming) ๋ฐ ํœด๋จผ์ธ๋”๋ฃจํ”„ (Human-in-the-loop) ๐Ÿ™‹

  • ์‹ค์‹œ๊ฐ„ ์ฒ˜๋ฆฌ: LangGraph๋Š” ์‘๋‹ต์„ ํ† ํฐ ๋‹จ์œ„๋กœ ์ŠคํŠธ๋ฆฌ๋ฐ(stream())ํ•˜๋Š” ๊ฒƒ์„ ์™„๋ฒฝํ•˜๊ฒŒ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” AI๊ฐ€ "์ƒ๊ฐํ•˜๊ณ  ์ž‘์—…ํ•˜๋Š”" ๊ณผ์ •์„ ์‚ฌ์šฉ์ž์—๊ฒŒ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ณด์—ฌ์ฃผ์–ด UX๋ฅผ ๊ทน๋Œ€ํ™”ํ•ฉ๋‹ˆ๋‹ค.
  • ํœด๋จผ์ธ๋”๋ฃจํ”„ (HITL): ํ”„๋กœ๋•์…˜ ๋ ˆ๋ฒจ์˜ ์‹œ์Šคํ…œ์—์„œ๋Š” AI์˜ ๋ชจ๋“  ๊ฒฐ์ •์„ ์‹ ๋ขฐํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. LangGraph๋Š” ์›Œํฌํ”Œ๋กœ์šฐ์˜ ํŠน์ • ๋…ธ๋“œ ์ „/ํ›„(interrupt_before/interrupt_after)์— ์‹คํ–‰์„ '์ผ์‹œ ์ค‘์ง€(interrupt)'ํ•˜๊ณ  ์ธ๊ฐ„์˜ ๊ฒ€ํ† ๋‚˜ ์Šน์ธ์„ ๊ธฐ๋‹ค๋ฆฌ๊ฒŒ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. (e.g., "AI๊ฐ€ 100๋งŒ ์› ์ด์ฒด๋ฅผ ์Šน์ธํ•˜๋ ค ํ•ฉ๋‹ˆ๋‹ค. ๊ด€๋ฆฌ์ž๋‹˜, ์Šน์ธํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?")

3.3. ์ •๊ตํ•œ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ (Memory Management)

์žฅ๊ธฐ์ ์ธ ์ƒํ˜ธ์ž‘์šฉ์—์„œ ์ปจํ…์ŠคํŠธ ์œ ์ง€๋Š” ๋งค์šฐ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

  • ๋‹จ๊ธฐ ๋ฉ”๋ชจ๋ฆฌ (Short-term): ์ƒํƒœ(State) ๊ฐ์ฒด ๋‚ด์˜ messages ๋ฆฌ์ŠคํŠธ ๋“ฑ์„ ํ†ตํ•ด ํ˜„์žฌ ๋Œ€ํ™”์˜ ๋งฅ๋ฝ์„ ์ถ”์ ํ•ฉ๋‹ˆ๋‹ค.
  • ์žฅ๊ธฐ ๋ฉ”๋ชจ๋ฆฌ (Long-term): Checkpointer๋ฅผ ํ†ตํ•ด ๋Œ€ํ™” ๊ธฐ๋ก ์ „์ฒด๋ฅผ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. ๋‚˜์•„๊ฐ€, Zep๊ณผ ๊ฐ™์€ ์žฅ๊ธฐ ๋ฉ”๋ชจ๋ฆฌ ์†”๋ฃจ์…˜์ด๋‚˜ ๋ฒกํ„ฐ DB๋ฅผ ๋„๊ตฌ๋กœ ํ†ตํ•ฉํ•˜์—ฌ, ์—์ด์ „ํŠธ๊ฐ€ "์ง€๋‚œ์ฃผ์— ์‚ฌ์šฉ์ž๊ฐ€ ์„ ํ˜ธํ•œ๋‹ค๊ณ  ํ–ˆ๋˜ ์Šคํƒ€์ผ"์„ ๊ธฐ์–ตํ•˜๊ฒŒ ๋งŒ๋“ค ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

4. LangGraph๋กœ ์—์ด์ „ํŠธ ์‹œ์Šคํ…œ ๊ตฌ์ถ•ํ•˜๊ธฐ (5-Step) ๐Ÿ› ๏ธ

์ด๋ก ์€ ์ถฉ๋ถ„ํ•ฉ๋‹ˆ๋‹ค. ์ด์ œ ์‹ค์ œ๋กœ LangGraph๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ธฐ๋ณธ์ ์ธ ์—์ด์ „ํŠธ ์‹œ์Šคํ…œ์„ ๊ตฌ์ถ•ํ•˜๋Š” 5๋‹จ๊ณ„ ๊ณผ์ •์„ ์ฝ”๋“œ๋ฅผ ๊ณ๋“ค์—ฌ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๐ŸŽฏ ๊ฐ€์ƒ ์‹œ๋‚˜๋ฆฌ์˜ค: "์‚ฌ์šฉ์ž ์งˆ๋ฌธ์„ ๋ฐ›์•„, '๋‹จ์ˆœ ๋‹ต๋ณ€'์ด ๊ฐ€๋Šฅํ•œ์ง€, '๋ฌธ์„œ ๊ฒ€์ƒ‰'์ด ํ•„์š”ํ•œ์ง€ LLM์ด ํŒ๋‹จํ•˜๊ณ , ํ•„์š”์‹œ ๊ฒ€์ƒ‰ ๋„๊ตฌ๋ฅผ ์‹คํ–‰ํ•œ ๋’ค, ์ตœ์ข… ๋‹ต๋ณ€์„ ์ƒ์„ฑํ•˜๋Š” RAG ์—์ด์ „ํŠธ"

4.1. 1๋‹จ๊ณ„: ์›Œํฌํ”Œ๋กœ์šฐ ๋ฐ ์ƒํƒœ ์ •์˜ (State Definition)

๋จผ์ €, ๊ทธ๋ž˜ํ”„ ์ „์ฒด์—์„œ ๊ณต์œ ๋  '์ƒํƒœ'์˜ ์Šคํ‚ค๋งˆ๋ฅผ TypedDict๋กœ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.

import { TypedDict, Annotated } from 'typing_extensions';
from typing import List
from langgraph.graph.message import add_messages # ๋ฉ”์‹œ์ง€ ๊ธฐ๋ก์„ ์‰ฝ๊ฒŒ ์ถ”๊ฐ€/๊ด€๋ฆฌ

# '์ƒํƒœ' ์ •์˜: ์šฐ๋ฆฌ ์—์ด์ „ํŠธ๊ฐ€ ๊ธฐ์–ตํ•ด์•ผ ํ•  ๋ชจ๋“  ๊ฒƒ
class AgentState(TypedDict):
    # messages๊ฐ€ ํ•ต์‹ฌ. 
    # Annotated(add_messages, ...)๋Š” ์ƒˆ ๋ฉ”์‹œ์ง€๊ฐ€ ๋“ค์–ด์˜ค๋ฉด ๊ธฐ์กด ๋ฆฌ์ŠคํŠธ์— '์ถ”๊ฐ€'ํ•˜๋ผ๋Š” ์˜๋ฏธ
    messages: Annotated[list, add_messages]
    
    # ์ถ”๊ฐ€์ ์œผ๋กœ ํ•„์š”ํ•œ ์ƒํƒœ๋“ค
    query: str                # ์‚ฌ์šฉ์ž์˜ ์›๋ณธ ์งˆ๋ฌธ
    documents: List[str]      # ๊ฒ€์ƒ‰๋œ ๋ฌธ์„œ ๋ชฉ๋ก

4.2. 2๋‹จ๊ณ„: ๋…ธ๋“œ ๊ตฌํ˜„ (Node Implementation)

๊ฐ ์ž‘์—… ๋‹จ๊ณ„(๋…ธ๋“œ)๋ฅผ Python ํ•จ์ˆ˜๋กœ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ ํ•จ์ˆ˜๋Š” AgentState๋ฅผ ์ž…๋ ฅ๋ฐ›์•„, ์ฒ˜๋ฆฌ ํ›„ ๋ณ€๊ฒฝ๋œ ์ƒํƒœ๋ฅผ dict๋กœ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

from langchain_openai import ChatOpenAI
from langchain_community.vectorstores import FAISS # ์˜ˆ์‹œ์šฉ ๋ฒกํ„ฐDB
from langchain_core.documents import Document

# --- ๊ฐ€์ƒ์˜ ๋„๊ตฌ(VectorDB)์™€ LLM ์„ค์ • ---
# (์‹ค์ œ๋กœ๋Š” embedding ๋ชจ๋ธ๊ณผ ํ•จ๊ป˜ FAISS๋ฅผ ๋ฏธ๋ฆฌ ๋นŒ๋“œํ•ด์•ผ ํ•จ)
documents_db = FAISS.from_texts(["LangGraph๋Š” ๋ฉ‹์ง€๋‹ค", "AI ์—์ด์ „ํŠธ๋Š” ์œ ์šฉํ•˜๋‹ค"], ...) 
retriever = documents_db.as_retriever()
llm = ChatOpenAI(model="gpt-4o")

# --- ๋…ธ๋“œ 1: ์‚ฌ์šฉ์ž ์งˆ๋ฌธ์„ ์ƒํƒœ์— ์ €์žฅ (Entry Point) ---
def start_node(state: AgentState):
    print("--- 1. ์›Œํฌํ”Œ๋กœ์šฐ ์‹œ์ž‘ ---")
    query = state['messages'][-1].content # ๋งˆ์ง€๋ง‰ ์‚ฌ์šฉ์ž ๋ฉ”์‹œ์ง€๋ฅผ query๋กœ
    return {"query": query}

# --- ๋…ธ๋“œ 2: ๋ฌธ์„œ ๊ฒ€์ƒ‰ (๋„๊ตฌ) ---
def retrieve_node(state: AgentState):
    print("--- 2. ๋ฌธ์„œ ๊ฒ€์ƒ‰ ์ค‘... ---")
    query = state['query']
    documents = retriever.invoke(query) # ์‹ค์ œ ๊ฒ€์ƒ‰ ์‹คํ–‰
    doc_texts = [doc.page_content for doc in documents]
    return {"documents": doc_texts}

# --- ๋…ธ๋“œ 3: ์ตœ์ข… ์‘๋‹ต ์ƒ์„ฑ (LLM) ---
def generate_node(state: AgentState):
    print("--- 3. ์ตœ์ข… ์‘๋‹ต ์ƒ์„ฑ ์ค‘ ---")
    query = state['query']
    documents = state['documents']
    
    # ํ”„๋กฌํ”„ํŠธ ๊ตฌ์„ฑ
    context = "\n\n".join(documents)
    prompt = f"๋ฌธ์„œ: {context}\n\n์งˆ๋ฌธ: {query}\n\n์œ„ ๋ฌธ์„œ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์งˆ๋ฌธ์— ๋‹ต๋ณ€ํ•˜์„ธ์š”."
    
    response = llm.invoke(prompt)
    return {"messages": [("ai", response.content)]} # AI ์‘๋‹ต์„ messages์— ์ถ”๊ฐ€

# --- ๋…ธ๋“œ 4: ๋‹จ์ˆœ ์‘๋‹ต ์ƒ์„ฑ (LLM) ---
def fallback_node(state: AgentState):
    print("--- 3a. ๋‹จ์ˆœ ์‘๋‹ต ์ƒ์„ฑ ์ค‘ (๊ฒ€์ƒ‰ ๋ถˆํ•„์š”) ---")
    query = state['query']
    prompt = f"์งˆ๋ฌธ: {query}\n\n๊ฒ€์ƒ‰ ์—†์ด, ์ผ๋ฐ˜์ ์ธ ์ง€์‹์œผ๋กœ ๋‹ต๋ณ€ํ•˜์„ธ์š”."
    
    response = llm.invoke(prompt)
    return {"messages": [("ai", response.content)]}

4.3. 3๋‹จ๊ณ„: ๊ทธ๋ž˜ํ”„ ๊ตฌ์„ฑ ๋ฐ ์—ฃ์ง€ ์—ฐ๊ฒฐ (Graph & Edges)

์ด์ œ StateGraph๋ฅผ ๋งŒ๋“ค๊ณ , ๋…ธ๋“œ์™€ ์—ฃ์ง€๋ฅผ ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ '์กฐ๊ฑด๋ถ€ ์—ฃ์ง€'๊ฐ€ ๋น›์„ ๋ฐœํ•ฉ๋‹ˆ๋‹ค.

from langgraph.graph import StateGraph, END

# --- ์กฐ๊ฑด๋ถ€ ์—ฃ์ง€์šฉ ํ•จ์ˆ˜ (LLM์ด ๊ฒฐ์ •) ---
def router_node(state: AgentState):
    print("--- 1a. ๋ผ์šฐํŒ…: ๊ฒ€์ƒ‰์ด ํ•„์š”ํ•œ๊ฐ€? ---")
    query = state['query']
    
    # (๊ฐ„๋‹จํ•œ ์˜ˆ์‹œ: ์‹ค์ œ๋กœ๋Š” ๋” ์ •๊ตํ•œ LLM ํ˜ธ์ถœ๋กœ ํŒ๋‹จ)
    if "langgraph" in query.lower() or "์—์ด์ „ํŠธ" in query.lower():
        print("-> [๊ฒฐ์ •] ๊ฒ€์ƒ‰ ํ•„์š”")
        return "retrieve" # retrieve_node๋กœ ์ด๋™
    else:
        print("-> [๊ฒฐ์ •] ๋‹จ์ˆœ ์‘๋‹ต")
        return "fallback" # fallback_node๋กœ ์ด๋™

# --- ๊ทธ๋ž˜ํ”„ ๋นŒ๋” ์‹œ์ž‘ ---
workflow = StateGraph(AgentState)

# 1. ๋…ธ๋“œ ์ถ”๊ฐ€
workflow.add_node("start", start_node)
workflow.add_node("router", router_node)
workflow.add_node("retriever", retrieve_node)
workflow.add_node("generator", generate_node)
workflow.add_node("fallback", fallback_node)

# 2. ์—ฃ์ง€ ์—ฐ๊ฒฐ
workflow.set_entry_point("start") # ์‹œ์ž‘์ 
workflow.add_edge("start", "router") # ์‹œ์ž‘ -> ๋ผ์šฐํ„ฐ

# 3. **ํ•ต์‹ฌ: ์กฐ๊ฑด๋ถ€ ์—ฃ์ง€**
# router ๋…ธ๋“œ์˜ ๋ฐ˜ํ™˜๊ฐ’("retrieve" ๋˜๋Š” "fallback")์— ๋”ฐ๋ผ ๋‹ค์Œ ๋…ธ๋“œ๋ฅผ ๊ฒฐ์ •
workflow.add_conditional_edges(
    "router",
    router_node, # router_node ํ•จ์ˆ˜๋ฅผ ๋‹ค์‹œ ํ˜ธ์ถœ (๋˜๋Š” ๋ณ„๋„ router ํ•จ์ˆ˜ ์‚ฌ์šฉ)
    {
        "retrieve": "retriever",
        "fallback": "fallback"
    }
)

workflow.add_edge("retriever", "generator") # ๊ฒ€์ƒ‰ -> ์ƒ์„ฑ
workflow.add_edge("generator", END)         # ์ƒ์„ฑ -> ์ข…๋ฃŒ
workflow.add_edge("fallback", END)          # ๋‹จ์ˆœ์‘๋‹ต -> ์ข…๋ฃŒ

4.4. 4๋‹จ๊ณ„: ์ปดํŒŒ์ผ, ์ง€์†์„ฑ ์„ค์ • ๋ฐ ์‹คํ–‰ (Compile & Persistence)

๊ทธ๋ž˜ํ”„ ์„ค๊ณ„๋ฅผ ๋งˆ์ณค๋‹ค๋ฉด, compile()๋กœ ์‹คํ–‰ ๊ฐ€๋Šฅํ•œ ์•ฑ์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ์ด๋•Œ Checkpointer๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

from langgraph.checkpoint.sqlite import SqliteSaver

# (ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด ์ธ๋ฉ”๋ชจ๋ฆฌ SQLite ์‚ฌ์šฉ)
memory = SqliteSaver.from_conn_string(":memory:")

# ์ฒดํฌํฌ์ธํ„ฐ๋ฅผ ์—ฐ๊ฒฐํ•˜์—ฌ ๊ทธ๋ž˜ํ”„ ์ปดํŒŒ์ผ
app = workflow.compile(checkpointer=memory)

# --- ์‹คํ–‰ ---
# 'thread_id'๋Š” ๋Œ€ํ™” ์„ธ์…˜์„ ์‹๋ณ„ํ•˜๋Š” ๊ณ ์œ  ID
config = {"configurable": {"thread_id": "user_session_1"}}

# ์ž…๋ ฅ 1 (๊ฒ€์ƒ‰ ํ•„์š”)
inputs = [("user", "LangGraph๊ฐ€ ๋ญ์ฃ ?")]
for event in app.stream({"messages": inputs}, config=config):
    for k, v in event.items():
        if "messages" in v:
            print(f"๐Ÿค– AI: {v['messages'][-1].content}")

# ์ž…๋ ฅ 2 (๊ฒ€์ƒ‰ ๋ถˆํ•„์š”. ๋™์ผํ•œ thread_id๋กœ ์‹คํ–‰ํ•˜๋ฉด ๋Œ€ํ™”๊ฐ€ ์ด์–ด์ง)
inputs_2 = [("user", "์•ˆ๋…•? ๋ฐ˜๊ฐ€์›Œ")]
for event in app.stream({"messages": inputs_2}, config=config):
    for k, v in event.items():
        if "messages" in v:
            print(f"๐Ÿค– AI: {v['messages'][-1].content}")

4.5. 5๋‹จ๊ณ„: ํ‰๊ฐ€, ๋””๋ฒ„๊น… ๋ฐ ๋ฐ˜๋ณต (Evaluate & Iterate)

LangGraph๋Š” ์‹œ๊ฐํ™”์™€ ๋””๋ฒ„๊น…์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

  • ASCII ์‹œ๊ฐํ™”: app.get_graph().print_ascii()๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ํ„ฐ๋ฏธ๋„์—์„œ ๊ทธ๋ž˜ํ”„ ๊ตฌ์กฐ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • Mermaid ์‹œ๊ฐํ™”: app.get_graph().draw_mermaid_png() (๋˜๋Š” draw_mermaid())๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด Mermaid ๋ฌธ๋ฒ•์˜ ๋‹ค์ด์–ด๊ทธ๋žจ์„ ์ƒ์„ฑํ•˜์—ฌ, ์—์ด์ „ํŠธ์˜ ๋ณต์žกํ•œ ํ๋ฆ„์„ ํ•œ๋ˆˆ์— ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

5. ์‹ค๋ฌด์  ๊ณผ์ œ์™€ ๋ชจ๋ฒ” ์‚ฌ๋ก€ (Challenges & Best Practices) ๐Ÿ“ˆ

LangGraph๋Š” ๊ฐ•๋ ฅํ•˜์ง€๋งŒ, ๋ชจ๋“  ๊ฒƒ์„ ํ•ด๊ฒฐํ•ด ์ฃผ๋Š” ์€ํƒ„ํ™˜์€ ์•„๋‹™๋‹ˆ๋‹ค. ์‹ค๋ฌด์—์„œ ์—์ด์ „ํŠธ๋ฅผ ์„ค๊ณ„ํ•  ๋•Œ ๊ฐœ๋ฐœ์ž๋“ค์ด ๊ณ ๋ คํ•ด์•ผ ํ•  ๊ณผ์ œ๋“ค์ด ์žˆ์Šต๋‹ˆ๋‹ค.

5.1. ๋„์ „ ๊ณผ์ œ (Challenges)

  1. ๋ณต์žก์„ฑ๊ณผ ์ œ์–ด๋ ฅ์˜ ๊ท ํ˜•: ์œ ์—ฐ์„ฑ์ด ๋†’์•„์ง„๋‹ค๋Š” ๊ฒƒ์€ ๊ณง ์›Œํฌํ”Œ๋กœ์šฐ๊ฐ€ ๋ณต์žกํ•ด์ง„๋‹ค๋Š” ์˜๋ฏธ์ž…๋‹ˆ๋‹ค. ๋…ธ๋“œ์™€ ์—ฃ์ง€๊ฐ€ ์ˆ˜์‹ญ ๊ฐœ๊ฐ€ ๋˜๋ฉด ๋””๋ฒ„๊น…๊ณผ ๊ด€๋ฆฌ๊ฐ€ ์–ด๋ ค์›Œ์ง‘๋‹ˆ๋‹ค. ๋ถˆํ•„์š”ํ•œ ์˜ค๋ฒ„ํ—ค๋“œ๋ฅผ ํ”ผํ•˜๊ณ  ํ™•์žฅ์„ฑ์„ ์œ ์ง€ํ•˜๋Š” ์„ค๊ณ„๊ฐ€ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.
  2. ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ์˜ ํ•จ์ •: ๋Œ€ํ™” ๊ธฐ๋ก(messages)์ด ๊ธธ์–ด์ง€๋ฉด, ๋งค๋ฒˆ LLM์„ ํ˜ธ์ถœํ•  ๋•Œ๋งˆ๋‹ค ์ „์ฒด ๊ธฐ๋ก์„ ์ปจํ…์ŠคํŠธ์— ํฌํ•จ์‹œ์ผœ ๋น„์šฉ์ด ์ฆ๊ฐ€ํ•˜๊ณ  ํ† ํฐ ์ œํ•œ(Context Window)์— ๋ถ€๋”ชํž ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  3. ๋„๊ตฌ์˜ ์‹ ๋ขฐ์„ฑ: ์™ธ๋ถ€ API๋‚˜ ๊ฒ€์ƒ‰ ์‹œ์Šคํ…œ์€ ์–ธ์ œ๋“  ์‹คํŒจํ•˜๊ฑฐ๋‚˜(e.g., 500 Error) ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด์— ๋Œ€ํ•œ ๊ฒฌ๊ณ ํ•œ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ(Error Handling) ๋…ธ๋“œ์™€ ์žฌ์‹œ๋„(Retry) ๋กœ์ง์ด ํ•„์ˆ˜์ ์ž…๋‹ˆ๋‹ค.

5.2. ๋ชจ๋ฒ” ์‚ฌ๋ก€ (Best Practices)

์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ์™„ํ™”ํ•˜๊ธฐ ์œ„ํ•œ ๋ช‡ ๊ฐ€์ง€ ๋ชจ๋ฒ” ์‚ฌ๋ก€์ž…๋‹ˆ๋‹ค.

  • PoC๋กœ ์ž‘๊ฒŒ ์‹œ์ž‘ํ•˜๋ผ: ์ฒ˜์Œ๋ถ€ํ„ฐ ๊ฑฐ๋Œ€ํ•œ ์—์ด์ „ํŠธ๋ฅผ ๋งŒ๋“ค๋ ค ํ•˜์ง€ ๋ง๊ณ , ํ•ต์‹ฌ ๊ธฐ๋Šฅ 2~3๊ฐœ ๋…ธ๋“œ๋กœ ๊ตฌ์„ฑ๋œ ์†Œ๊ทœ๋ชจ ๊ฐœ๋… ์ฆ๋ช…(PoC) ์—์ด์ „ํŠธ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•˜์—ฌ ๋กœ์ง์„ ๊ฒ€์ฆํ•˜์„ธ์š”.
  • ๋ชจ๋“ˆํ˜• ์„ค๊ณ„๋ฅผ ์ ์šฉํ•˜๋ผ: ๊ด€๋ จ ๋…ธ๋“œ๋“ค์„ ๋ฌถ์–ด ์„œ๋ธŒ-๊ทธ๋ž˜ํ”„(Sub-graph)๋กœ ๋งŒ๋“ค๊ณ , ๊ทธ๋ž˜ํ”„๋ฅผ ์ค‘์ฒฉ(nesting)ํ•˜์—ฌ ๋ณต์žก์„ฑ์„ ๊ด€๋ฆฌํ•˜์„ธ์š”.
  • ๋ฉ”๋ชจ๋ฆฌ ์š”์•ฝ ๋…ธ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜๋ผ: ๋Œ€ํ™” ํ„ด์ด 10ํšŒ๋ฅผ ๋„˜์–ด๊ฐ€๋ฉด, ๋Œ€ํ™” ๊ธฐ๋ก์„ ์š”์•ฝํ•˜๋Š” ๋ณ„๋„์˜ LLM ๋…ธ๋“œ๋ฅผ ํŠธ๋ฆฌ๊ฑฐํ•˜์—ฌ ์ƒํƒœ์˜ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํšจ์œจ์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜์„ธ์š”.
  • ์ง€์†์ ์œผ๋กœ ๋ชจ๋‹ˆํ„ฐ๋งํ•˜๋ผ: LangSmith๋‚˜ LangFuse์™€ ๊ฐ™์€ LLM์˜ต์Šค ๋„๊ตฌ๋ฅผ ํ™œ์šฉํ•˜์—ฌ, ์—์ด์ „ํŠธ๊ฐ€ ์–ด๋–ค '์ƒํƒœ'์—์„œ ์–ด๋–ค '๊ฒฐ์ •'์„ ๋‚ด๋ฆฌ๊ณ  ์–ด๋–ค '๋„๊ตฌ'๋ฅผ ํ˜ธ์ถœํ–ˆ๋Š”์ง€ ์ถ”์ ํ•˜๊ณ  ๋””๋ฒ„๊น…ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

6. ๊ฒฐ๋ก : LangGraph, '์ƒ๊ฐํ•˜๋Š”' AI๋ฅผ ์œ„ํ•œ ์ฒญ์‚ฌ์ง„ ๐Ÿ—บ๏ธ

LangGraph๋Š” LLM ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐœ๋ฐœ์˜ ํŒจ๋Ÿฌ๋‹ค์ž„์„ ๋ฐ”๊พธ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐœ๋ฐœ์ž๋“ค์€ ์ด์ œ ์ •ํ•ด์ง„ ์ˆœ์„œ(DAG)์— ๊ฐ‡ํžˆ์ง€ ์•Š๊ณ , ๋™์ ์ด๊ณ , ์ƒํƒœ ๊ธฐ๋ฐ˜์ด๋ฉฐ, ๊ฒฌ๊ณ ํ•œ ์—์ด์ „ํŠธ ๊ธฐ๋ฐ˜ AI ์‹œ์Šคํ…œ์„ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜ํ”„ ๊ธฐ๋ฐ˜ ์•„ํ‚คํ…์ฒ˜๋ฅผ ํ™œ์šฉํ•˜๊ณ , ์™ธ๋ถ€ ๋„๊ตฌ๋ฅผ ํ†ตํ•ฉํ•˜๋ฉฐ, ๋ฉ”๋ชจ๋ฆฌ(์ƒํƒœ)๋ฅผ ํšจ๊ณผ์ ์œผ๋กœ ๊ด€๋ฆฌํ•จ์œผ๋กœ์จ, LLM์ด ์ง„์ •์œผ๋กœ ์›Œํฌํ”Œ๋กœ์šฐ๋ฅผ '์ฃผ๋„'ํ•˜๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์„ค๊ณ„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ •๊ตํ•œ RAG ์‹œ์Šคํ…œ, ๊ฐœ์ธํ™”๋œ ์ฑ—๋ด‡, ๋ณต์žกํ•œ ์—…๋ฌด ์ž๋™ํ™” ์—์ด์ „ํŠธ๊นŒ์ง€. LangGraph๋Š” ์—ฌ๋Ÿฌ๋ถ„์˜ AI ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋‹จ์ˆœํ•œ ํ”„๋กœํ† ํƒ€์ž…์—์„œ ์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๋Š” ํ”„๋กœ๋•์…˜ ์„œ๋น„์Šค๋กœ ํ™•์žฅํ•˜๋Š” ๋ฐ ํ›Œ๋ฅญํ•œ ๊ธฐ์ˆ ์  ๊ธฐ๋ฐ˜์„ ์ œ๊ณตํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ฐจ์„ธ๋Œ€ ์—์ด์ „ํŠธ ๊ธฐ๋ฐ˜ ์‹œ์Šคํ…œ์„ ํ–ฅํ•œ ์—ฌ์ •์— ๊ฐ•๋ ฅํžˆ ์ถ”์ฒœํ•˜๋Š” ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค.