โ์ด์ LLM์ ๋ง๋ฟ ์๋๋ผ ํ๋ํ ์ ์๋ค.โ
LangGraph์์ Tool์ ๋ ธ๋๋ก ์ฐ๊ฒฐํ์ฌ, LLM์ด ์ง์ ๋๊ตฌ๋ฅผ ํ์ฉํ๋ ๋ฐฉ๋ฒ.
LangGraph์์ Tool์ โLLM์ด ์ง์ ํธ์ถ ๊ฐ๋ฅํ ์ธ๋ถ ํจ์ํ ๋ฆฌ์์คโ์
๋๋ค.
์ฆ, LLM์ด ์์ฐ์ด๋ก ๋ฌธ์ ๋ฅผ ์ดํดํ๊ณ , ๊ทธ์ ๋ฐ๋ผ Python ํจ์๋ API๋ฅผ ์ง์ ์คํํ ์ ์๋๋ก ํ๋ ์ฐ๊ฒฐ ๋จ์์์.
์๋ฅผ ๋ค์ด LLM์ด โ์์ธ์ ๋ ์จ ์๋ ค์คโ๋ผ๊ณ ํ์ ๋,
โ get_weather("Seoul") ํจ์๋ฅผ ์ง์ ์คํํ๋ ๊ฒ์ด ๋ฐ๋ก Tool ํธ์ถ์
๋๋ค.
| ๋น๊ต ํญ๋ชฉ | LangChain Agent | LangGraph ToolNode |
|---|---|---|
| ํธ์ถ ๋ฐฉ์ | LLM์ด ReAct ํจํด์ ํตํด Tool ํธ์ถ | ๊ทธ๋ํ ๋ด์์ Tool์ด ํ๋์ Node๋ก ํํ |
| ์ ์ด ๋ฐฉ์ | ์ถ์์ (์์ด์ ํธ๊ฐ ์์์) | ๋ช ์์ (๊ฐ๋ฐ์๊ฐ ํ๋ฆ ์ค๊ณ) |
| ์ฅ์ | ๋น ๋ฅธ ์์ | ๊ตฌ์กฐ์ ๋๋ฒ๊น , ์กฐ๊ฑด ์ ์ด |
| ์์ | initialize_agent(tools, llm) | graph.add_node("weather_tool", tool_func) |
LangGraph๋ โ์์ด์ ํธ์ ํ๋(Act)โ์ ๊ฐ๋ฐ์๊ฐ ์ง์ ์ค๊ณํ ์ ์๊ฒ ํด์ค๋๋ค.
์ฆ, ์๋ํ๋ณด๋ค ํต์ ๊ฐ๋ฅํ ์์จ์ฑ์ ์ ๊ณตํฉ๋๋ค.
from langgraph.graph import StateGraph
from langchain_openai import ChatOpenAI
import requests
# ์ํ ์ ์
class WeatherState:
city: str
report: str
# Tool (์ธ๋ถ API ํธ์ถ)
def get_weather(state: WeatherState):
city = state.city
# ์ค์ ๋ก๋ OpenWeather API ๋ฑ ํธ์ถ ๊ฐ๋ฅ
fake_response = {"Seoul": "๋ง์ โ๏ธ", "Busan": "ํ๋ฆผ ๐ฅ๏ธ"}
state.report = fake_response.get(city, "์ ๋ณด ์์")
print(f"[Tool ํธ์ถ] {city} ๋ ์จ: {state.report}")
return state
# LLM ๋
ธ๋
def ask_user(state: WeatherState):
llm = ChatOpenAI(model="gpt-4o-mini")
prompt = f"{state.city}์ ๋ ์จ๋ฅผ ์์ฝํด์ ๋งํด์ค: {state.report}"
reply = llm.invoke(prompt)
print(f"[LLM ์๋ต] {reply.content}")
return state
# ๊ทธ๋ํ ๊ตฌ์ฑ
graph = StateGraph(WeatherState)
graph.add_node("weather_tool", get_weather)
graph.add_node("llm_summary", ask_user)
graph.add_edge("weather_tool", "llm_summary")
graph.set_entry_point("weather_tool")
graph.set_finish_point("llm_summary")
app = graph.compile()
app.invoke({"city": "Seoul"})
๐งฉ ์คํ ๊ฒฐ๊ณผ ์์:
[Tool ํธ์ถ] Seoul ๋ ์จ: ๋ง์ โ๏ธ
[LLM ์๋ต] ์์ธ์ ๋ ์จ๋ ๋ง์ผ๋ฉฐ, ์ข์ ๋ ์จ์
๋๋ค.
์ด๋ ๊ฒ LangGraph์์๋ LLM๊ณผ Tool์ ํธ์ถ ์์๋ฅผ ๋ช ์์ ์ผ๋ก ์ ์ํฉ๋๋ค.
LangGraph์์๋ ์ฌ๋ฌ ToolNode๋ฅผ ๋ณ๋ ฌ ์คํํ ์๋ ์์ต๋๋ค.
์๋ฅผ ๋ค์ด, โ๋ ์จ + ๋ด์ค + ํ์จโ์ ๋์์ ๊ฐ์ ธ์ค๋ ๊ตฌ์กฐ์ฃ .
from langgraph.graph import StateGraph
import asyncio
class MultiState:
weather: str
news: str
fx: str
async def get_weather(state): state.weather = "๋ง์"; return state
async def get_news(state): state.news = "๊ตญ์ ๋ด์ค ์
๋ฐ์ดํธ"; return state
async def get_fx(state): state.fx = "1 USD = 1,350 KRW"; return state
async def merge_result(state):
print(f"{state.weather} / {state.news} / {state.fx}")
return state
graph = StateGraph(MultiState)
graph.add_node("weather", get_weather)
graph.add_node("news", get_news)
graph.add_node("fx", get_fx)
graph.add_node("merge", merge_result)
# ๋ณ๋ ฌ ์คํ ์ฃ์ง ๊ตฌ์ฑ
graph.add_edge("weather", "merge")
graph.add_edge("news", "merge")
graph.add_edge("fx", "merge")
graph.set_entry_point("weather") # ๋ณ๋ ฌ ์์์
graph.set_finish_point("merge")
app = graph.compile()
asyncio.run(app.ainvoke({}))
๐ Mermaid ์๊ฐํ:

โ
ToolNode๋ ๋น๋๊ธฐ(async) ํจ์๋ก ์ ์ํ๋ฉด ์๋ ๋ณ๋ ฌ ์คํ์ด ๊ฐ๋ฅํฉ๋๋ค.
LangGraph์ ๋
ธ๋๋ Runnable ๊ฐ์ฒด๋ฅผ ๊ทธ๋๋ก ์ฌ์ฉํ ์๋ ์์ต๋๋ค.
์ฆ, LangChain์์ ์ด๋ฏธ ๋ง๋ ์ฒด์ธยทToolยทLLM์ ๊ทธ๋ํ์ ์ฝ์
๊ฐ๋ฅํ์ฃ .
from langchain.tools import Tool
from langgraph.graph import StateGraph
from langchain_openai import ChatOpenAI
# LangChain Tool ์ ์
def calc(x, y): return x + y
sum_tool = Tool(name="add", func=calc, description="๋ ์๋ฅผ ๋ํจ")
class CalcState:
result: int
def run_tool(state: CalcState):
state.result = sum_tool.run(5, 7)
return state
def summarize(state: CalcState):
llm = ChatOpenAI(model="gpt-4o-mini")
reply = llm.invoke(f"7๊ณผ 5๋ฅผ ๋ํ๋ฉด {state.result}์
๋๋ค. ํ ์ค ์์ฝํด์ค.")
print(reply.content)
return state
graph = StateGraph(CalcState)
graph.add_node("tool", run_tool)
graph.add_node("summary", summarize)
graph.add_edge("tool", "summary")
graph.set_entry_point("tool")
graph.set_finish_point("summary")
app = graph.compile()
app.invoke({})
์ด๋ ๊ฒ ํ๋ฉด ๊ธฐ์กด LangChain ์์ฐ์ ๊ทธ๋๋ก ์ฌ์ฌ์ฉํ๋ฉด์ LangGraph์ ๊ตฌ์กฐ์ ์ ์ด๋ฅผ ์น์ ์ ์์ต๋๋ค.
LLM์ ํ๋จ์ ๋ฐ๋ผ ํน์ Tool๋ง ์คํํ๋๋ก ํ ์๋ ์์ต๋๋ค.
def choose_tool(state):
# LLM์ด ํด ์ ํ ๋ก์ง ์ํํ๋ค๊ณ ๊ฐ์
state.tool_choice = "weather" if "๋ ์จ" in state.query else "news"
return state
graph.add_node("chooser", choose_tool)
graph.add_conditional_edges("chooser", lambda s: s.tool_choice)
์ด ๋ฐฉ์์ ๋ค์ ํ์ฐจ์ ReAct ๊ทธ๋ํ ์ค๊ณ์ ๊ธฐ์ด๊ฐ ๋ฉ๋๋ค. (LLM์ด ์ค์ค๋ก โ์ด๋ค ๋๊ตฌ๋ฅผ ์ธ์ ์ธ์งโ ๊ฒฐ์ )
๐ 5ํธ: ReAct ํจํด๊ณผ LangGraph ์์ด์ ํธํ
| ์ฃผ์ | ํ์ต ์ด์ | ์ถ์ฒ ํ์ต ๋ฐฉํฅ |
|---|---|---|
| LangChain Runnable ์ฒด๊ณ | LangGraph ๋ด๋ถ ํธ์ถ ๊ตฌ์กฐ์ ํต์ฌ | RunnableLambda, RunnableParallel, RunnableMap |
| Async & Await ๋ณ๋ ฌ Tool ์ค๊ณ | ์ฌ๋ฌ API Tool ๋ณ๋ ฌ ์คํ | asyncio, await app.ainvoke() |
| Tool ์ ํ ๋ ผ๋ฆฌ(Policy Design) | LLM์ โ์ด๋ค ๋๊ตฌ๋ฅผ ์ธ์งโ ํ๋จ ์ ์ด | ToolCondition, LLM ์ ํ ํ๋กฌํํธ |
| Custom ToolKit ๊ตฌ์ฑ | ํ๋ก์ ํธ๋ณ Tool ์ธํธ ์ค๊ณ | โ๊ฒ์, ์์ฝ, ๊ณ์ฐ, DBโ ๋ฑ ๊ธฐ๋ฅํ |
| Graph Logging & Visualization | Tool ์คํ ํ๋ฆ ์ถ์ | graph.visualize("toolflow.png"), ์ํ ๋ก๊ทธ |
| LangGraph + LangServe | Tool ํฌํจ ๊ทธ๋ํ๋ฅผ ์๋น์คํ | REST API / WebSocket ๊ธฐ๋ฐ ๋ฐฐํฌ |
- ToolNode๋ โLLM์ ์โ์ด๋ค.
- LangGraph์์ Tool์ ๋ ธ๋๋ก ์ฐ๊ฒฐํ๋ฉด, LLM์ด ์ง์ APIยทํจ์ยท๋ฐ์ดํฐ๋ฅผ ํ์ฉํ ์ ์๋ค.
- ๋ณ๋ ฌ ํธ์ถ, ์กฐ๊ฑด ํธ์ถ, Runnable ํตํฉ์ผ๋ก ์ค์ ์์ด์ ํธ ์ํคํ ์ฒ๊ฐ ์์ฑ๋๋ค.
๐ก ๋ค์์ LLM์ด ์ค์ค๋ก โ์ด๋ค ๋๊ตฌ๋ฅผ ์ธ์งโ ๊ฒฐ์ ํ๊ณ ํผ๋๋ฐฑ์ ํตํด ์์ ํ๋ ๋จ๊ณ๋ค.