from dotenv import load_dotenv
load_dotenv(".env", override=True)๐ง ๐คDeep Agents
LLM์ด ๋ฃจํ์์ ๋๊ตฌ๋ฅผ ํธ์ถํ๋ ๊ฒ์ด ์์ด์ ํธ์ ๊ฐ์ฅ ๋จ์ํ ํํ์ ๋๋ค. ๊ทธ๋ฌ๋ ์ด ์ํคํ ์ฒ๋ โ์์โ ์์ด์ ํธ๋ฅผ ๋ง๋ค ์ ์์ผ๋ฉฐ, ๋ ๊ธธ๊ณ ๋ณต์กํ ์์ ์ ๋ํด ๊ณํํ๊ณ ํ๋ํ์ง ๋ชปํ ์ ์์ต๋๋ค.
โDeep Researchโ, โManusโ, โClaude Codeโ์ ๊ฐ์ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ค ๊ฐ์ง ๊ธฐ๋ฅ์ ์กฐํฉ์ ๊ตฌํํ์ฌ ์ด ํ๊ณ๋ฅผ ๊ทน๋ณตํ์ต๋๋ค: ๊ณํ ๋๊ตฌ, ํ์ ์์ด์ ํธ, ํ์ผ ์์คํ ์ ๊ทผ, ๊ทธ๋ฆฌ๊ณ ์์ธํ ํ๋กฌํํธ.
deepagents๋ ์ผ๋ฐ์ ์ธ ์ฉ๋๋ก ์ด๋ค์ ๊ตฌํํ๋ Python ํจํค์ง์ด๋ฏ๋ก ์ ํ๋ฆฌ์ผ์ด์
์ ์ํด Deep Agent๋ฅผ ์ฝ๊ฒ ๋ง๋ค ์ ์์ต๋๋ค.
deepagents์ ์ ์ฒด ๊ฐ์์ ๋น ๋ฅธ ์์์ ์ํด์๋ Deep Agents overview ๋ฌธ์๋ฅผ ์ฐธ๊ณ ํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
์ค์น
pip install deepagents tavily-python์ฌ์ฉ๋ฒ
ํ๊ฒฝ์ TAVILY_API_KEY๋ฅผ ์ค์ ํด์ผ ํฉ๋๋ค. ์ฌ๊ธฐ์์ ์์ฑํ ์ ์์ต๋๋ค.
import os
from typing import Literal
from deepagents import create_deep_agent
from tavily import TavilyClient
tavily_client = TavilyClient(api_key=os.environ["TAVILY_API_KEY"])
# ์น ๊ฒ์ ๋๊ตฌ
def internet_search(
query: str,
max_results: int = 5,
topic: Literal["general", "news", "finance"] = "general",
include_raw_content: bool = False,
):
"""์น ๊ฒ์ ์คํ"""
return tavily_client.search(
query,
max_results=max_results,
include_raw_content=include_raw_content,
topic=topic,
)
# ์์ด์ ํธ๋ฅผ ์ ๋ฌธ ์ฐ๊ตฌ์์ผ๋ก ์ ๋ํ๋ ์์คํ
ํ๋กฌํํธ
research_instructions = """๋น์ ์ ์ ๋ฌธ ์ฐ๊ตฌ์์
๋๋ค. ๋น์ ์ ์ญํ ์ ์ฒ ์ ํ ์ฐ๊ตฌ๋ฅผ ์ํํ๊ณ ์ ๊ตํ ๋ณด๊ณ ์๋ฅผ ์์ฑํ๋ ๊ฒ์
๋๋ค.
์ธํฐ๋ท ๊ฒ์ ๋๊ตฌ์ ์ ๊ทผํ ์ ์์ผ๋ฉฐ, ์ด๊ฒ์ด ์ ๋ณด ์์ง์ ์ฃผ์ ์๋จ์
๋๋ค.
## `internet_search`
์ฃผ์ด์ง ์ฟผ๋ฆฌ์ ๋ํด ์ธํฐ๋ท ๊ฒ์์ ์คํํ๋ ค๋ฉด ์ด ๊ธฐ๋ฅ์ ์ฌ์ฉํฉ๋๋ค. ๋ฐํํ ๊ฒฐ๊ณผ์ ์ต๋ ์, ์ฃผ์ , ์๋ณธ ์ฝํ
์ธ ํฌํจ ์ฌ๋ถ๋ฅผ ์ง์ ํ ์ ์์ต๋๋ค.
"""
# Deep Agent ์์ฑ
agent = create_deep_agent(
tools=[internet_search],
system_prompt=research_instructions,
)
agent
from utils import format_messages
# ์์ด์ ํธ ํธ์ถ
result = agent.invoke(
{"messages": [{"role": "user", "content": "langgraph๋ ๋ฌด์์ธ๊ฐ์?"}]}
)
format_messages(result)๋ ๋ณต์กํ ์์ ๋ research_agent.py๋ฅผ ์ฐธ๊ณ ํ์ธ์.
create_deep_agent๋ก ๋ง๋ ์์ด์ ํธ๋ ๋จ์ํ LangGraph ๊ทธ๋ํ์ด๋ฏ๋ก, LangGraph ์์ด์ ํธ์ ๊ฐ์ ๋ฐฉ์์ผ๋ก(์คํธ๋ฆฌ๋ฐ, ํด๋จผ-์ธ-๋-๋ฃจํ, ๋ฉ๋ชจ๋ฆฌ, ์คํ๋์ค) ์ํธ์์ฉํ ์ ์์ต๋๋ค.
์ฐธ๊ณ :
create_deep_agent๋ ๋ชจ๋ธ๋ช ์ ์๋ตํ๋ฉด ๊ธฐ๋ณธ ๋ชจ๋ธ๋ก Claude Sonnet 4.5๋ฅผ ์ฌ์ฉํ๋ค.def get_default_model() -> ChatAnthropic: """Get the default model for deep agents. Returns: ChatAnthropic instance configured with Claude Sonnet 4. """ return ChatAnthropic( model_name="claude-sonnet-4-5-20250929", max_tokens=20000, )
ํต์ฌ ๊ธฐ๋ฅ
๊ณํ ๋ฐ ์์ ๋ถํด
Deep Agents๋ ์์ด์ ํธ๊ฐ ๋ณต์กํ ์์
์ ๊ฐ๋ณ ๋จ๊ณ๋ก ๋ถํดํ๊ณ , ์งํ ์ํฉ์ ์ถ์ ํ๊ณ , ์๋ก์ด ์ ๋ณด๊ฐ ๋ํ๋๋ฉด ๊ณํ์ ์์ ํ ์ ์๋๋ก ํ๋ ๋ด์ฅ write_todos ๋๊ตฌ๋ฅผ ํฌํจํฉ๋๋ค.
์ปจํ ์คํธ ๊ด๋ฆฌ
ํ์ผ ์์คํ
๋๊ตฌ(ls, read_file, write_file, edit_file, glob, grep)๋ฅผ ํตํด ์์ด์ ํธ๋ ํฐ ์ปจํ
์คํธ๋ฅผ ๋ฉ๋ชจ๋ฆฌ๋ก ์คํ๋ก๋ํ ์ ์์ผ๋ฉฐ, ์ปจํ
์คํธ ์๋์ฐ ์ค๋ฒํ๋ก์ฐ๋ฅผ ๋ฐฉ์งํ๊ณ ๊ฐ๋ณ ๊ธธ์ด ๋๊ตฌ ๊ฒฐ๊ณผ๋ก ์์
ํ ์ ์์ต๋๋ค.
ํ์ ์์ด์ ํธ ์์ฑ
๋ด์ฅ task ๋๊ตฌ๋ฅผ ํตํด ์์ด์ ํธ๋ ์ปจํ
์คํธ ๊ฒฉ๋ฆฌ๋ฅผ ์ํด ํนํ๋ ํ์ ์์ด์ ํธ๋ฅผ ์์ฑํ ์ ์์ต๋๋ค. ์ด๋ ๋ฉ์ธ ์์ด์ ํธ์ ์ปจํ
์คํธ๋ฅผ ๊น๋ํ๊ฒ ์ ์งํ๋ฉด์ ํน์ ํ์ ์์
์ ๊น์ด ์๊ฒ ์ํํฉ๋๋ค.
์ฅ๊ธฐ ๋ฉ๋ชจ๋ฆฌ
LangGraph์ Store๋ฅผ ์ฌ์ฉํ์ฌ ์์ด์ ํธ๋ฅผ ์ค๋ ๋ ๊ฐ ์ง์์ ์ธ ๋ฉ๋ชจ๋ฆฌ๋ก ํ์ฅํฉ๋๋ค. ์์ด์ ํธ๋ ์ด์ ๋ํ์์ ์ ๋ณด๋ฅผ ์ ์ฅํ๊ณ ๊ฒ์ํ ์ ์์ต๋๋ค.
Deep Agents ์ปค์คํฐ๋ง์ด์ง
create_deep_agent์ ์ ๋ฌํ ์ ์๋ ์ฌ๋ฌ ๋งค๊ฐ๋ณ์๊ฐ ์์ด ์์ ์ ์ปค์คํ
Deep Agent๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค.
model
๊ธฐ๋ณธ์ ์ผ๋ก deepagents๋ "claude-sonnet-4-5-20250929"๋ฅผ ์ฌ์ฉํฉ๋๋ค. ๋ชจ๋ LangChain ๋ชจ๋ธ ๊ฐ์ฒด๋ฅผ ์ ๋ฌํ์ฌ ์ปค์คํฐ๋ง์ด์งํ ์ ์์ต๋๋ค.
from deepagents import create_deep_agent
from langchain.chat_models import init_chat_model
model = init_chat_model("openai:gpt-4o")
agent = create_deep_agent(
model=model,
)from deepagents import create_deep_agent
from langchain.chat_models import init_chat_model
model = init_chat_model("openai:gpt-4o")
agent = create_deep_agent(
model=model,
)system_prompt
Deep Agents๋ ๋ด์ฅ ์์คํ ํ๋กฌํํธ๋ฅผ ๊ฐ์ง๊ณ ์์ต๋๋ค. ์ด๊ฒ์ Claude Code์ ์์คํ ํ๋กฌํํธ๋ฅผ ๋ณต์ ํ๋ ค๋ ์๋์ ๊ธฐ๋ฐ์ ๋๊ณ ์๊ฐ์ ๋ฐ์ ์๋์ ์ผ๋ก ์์ธํ ํ๋กฌํํธ์ ๋๋ค. Claude Code์ ์์คํ ํ๋กฌํํธ๋ณด๋ค ๋ ์ผ๋ฐ์ ์ธ ์ฉ๋๋ก ๋ง๋ค์ด์ก์ต๋๋ค. ๊ธฐ๋ณธ ํ๋กฌํํธ๋ ๋ด์ฅ ๊ณํ ๋๊ตฌ, ํ์ผ ์์คํ ๋๊ตฌ, ํ์ ์์ด์ ํธ ์ฌ์ฉ ๋ฐฉ๋ฒ์ ๋ํ ์์ธํ ์ง์นจ์ ํฌํจํฉ๋๋ค.
๊ฐ ์ฌ์ฉ ์ฌ๋ก์ ๋ง์ถ Deep Agent๋ ๊ทธ ์ฌ์ฉ ์ฌ๋ก์ ํนํ๋ ์ปค์คํ ์์คํ ํ๋กฌํํธ๋ ํฌํจํด์ผ ํฉ๋๋ค. ์ฑ๊ณต์ ์ธ Deep Agent๋ฅผ ๋ง๋๋ ๋ฐ ์์ด ํ๋กฌํํ ์ ์ค์์ฑ์ ์๋ฌด๋ฆฌ ๊ฐ์กฐํด๋ ๋ถ์กฑํฉ๋๋ค.
from deepagents import create_deep_agent
research_instructions = """๋น์ ์ ์ ๋ฌธ ์ฐ๊ตฌ์์
๋๋ค.
๋น์ ์ ์ญํ ์ ์ฒ ์ ํ ์ฐ๊ตฌ๋ฅผ ์ํํ๊ณ ์ ๊ตํ ๋ณด๊ณ ์๋ฅผ ์์ฑํ๋ ๊ฒ์
๋๋ค.
"""
agent = create_deep_agent(
system_prompt=research_instructions,
)tools
๋๊ตฌ ํธ์ถ ์์ด์ ํธ์ ๋ง์ฐฌ๊ฐ์ง๋ก Deep Agent์ ์ ๊ทผ ๊ฐ๋ฅํ ๋๊ตฌ ์ธํธ๋ฅผ ์ ๊ณตํ ์ ์์ต๋๋ค.
import os
from typing import Literal
from deepagents import create_deep_agent
from tavily import TavilyClient
tavily_client = TavilyClient(api_key=os.environ["TAVILY_API_KEY"])
def internet_search(
query: str,
max_results: int = 5,
topic: Literal["general", "news", "finance"] = "general",
include_raw_content: bool = False,
):
"""์น ๊ฒ์ ์คํ"""
return tavily_client.search(
query,
max_results=max_results,
include_raw_content=include_raw_content,
topic=topic,
)
agent = create_deep_agent(tools=[internet_search])middleware
create_deep_agent๋ ์ปค์คํฐ๋ง์ด์ง ๊ฐ๋ฅํ ๋ฏธ๋ค์จ์ด๋ก ๊ตฌํ๋ฉ๋๋ค.
๊ธฐ๋ฅ์ ํ์ฅํ๊ณ , ๋๊ตฌ๋ฅผ ์ถ๊ฐํ๊ฑฐ๋, ์ปค์คํ ํ ์ ๊ตฌํํ๊ธฐ ์ํด ์ถ๊ฐ ๋ฏธ๋ค์จ์ด๋ฅผ ์ ๊ณตํ ์ ์์ต๋๋ค.
from deepagents import create_deep_agent
from langchain_core.tools import tool
from langchain.agents.middleware import AgentMiddleware
@tool
def get_weather(city: str) -> str:
"""๋์์ ๋ ์จ๋ฅผ ๊ฐ์ ธ์ต๋๋ค."""
return f"{city}์ ๋ ์จ๋ ๋ง์ต๋๋ค."
@tool
def get_temperature(city: str) -> str:
"""๋์์ ๊ธฐ์จ์ ๊ฐ์ ธ์ต๋๋ค."""
return f"{city}์ ๊ธฐ์จ์ ํ์จ 70๋์
๋๋ค."
class WeatherMiddleware(AgentMiddleware):
tools = [get_weather, get_temperature]
agent = create_deep_agent(
model="anthropic:claude-sonnet-4-20250514",
middleware=[WeatherMiddleware()],
)subagents
Deep Agents์ ์ฃผ์ ๊ธฐ๋ฅ์ ํ์ ์์ด์ ํธ๋ฅผ ์์ฑํ ์ ์๋ค๋ ๊ฒ์
๋๋ค. subagents ๋งค๊ฐ๋ณ์์ ์์ด์ ํธ๊ฐ ์์
์ ์์ํ ์ ์๋ ์ปค์คํ
ํ์ ์์ด์ ํธ๋ฅผ ์ง์ ํ ์ ์์ต๋๋ค. ํ์ ์์ด์ ํธ๋ ์ปจํ
์คํธ ๊ฒฉ๋ฆฌ(๋ฉ์ธ ์์ด์ ํธ์ ์ ์ฒด ์ปจํ
์คํธ๊ฐ ์ค์ผ๋๋ ๊ฒ์ ๋ฐฉ์งํ๊ธฐ ์ํด)์ ์ปค์คํ
์ง์นจ ์ ๊ณต์ ์ ์ฉํฉ๋๋ค.
subagents๋ ๊ฐ ๋์
๋๋ฆฌ๊ฐ ๋ค์ ์คํค๋ง๋ฅผ ๋ฐ๋ฅด๋ ๋์
๋๋ฆฌ ๋ชฉ๋ก์ด์ด์ผ ํฉ๋๋ค:
from collections.abc import Callable, Sequence
from typing import Any, NotRequired, TypedDict
from langchain_core.runnables import Runnable
from langchain_core.tools import BaseTool
from langchain.agents.middleware import InterruptOnConfig
from langchain.chat_models import BaseChatModel
class SubAgent(TypedDict):
name: str
description: str
prompt: str
tools: Sequence[BaseTool | Callable | dict[str, Any]]
model: NotRequired[str | BaseChatModel]
middleware: NotRequired[list[AgentMiddleware]]
interrupt_on: NotRequired[dict[str, bool | InterruptOnConfig]]
class CompiledSubAgent(TypedDict):
name: str
description: str
runnable: RunnableSubAgent ํ๋:
- name: ํ์ ์์ด์ ํธ์ ์ด๋ฆ์ด๋ฉฐ, ๋ฉ์ธ ์์ด์ ํธ๊ฐ ํ์ ์์ด์ ํธ๋ฅผ ํธ์ถํ ๋ ์ฌ์ฉํฉ๋๋ค.
- description: ๋ฉ์ธ ์์ด์ ํธ์๊ฒ ํ์๋๋ ํ์ ์์ด์ ํธ์ ์ค๋ช ์ ๋๋ค.
- prompt: ํ์ ์์ด์ ํธ์ ์ฌ์ฉ๋๋ ํ๋กฌํํธ์ ๋๋ค.
- tools: ํ์ ์์ด์ ํธ๊ฐ ์ ๊ทผํ ์ ์๋ ๋๊ตฌ ๋ชฉ๋ก์ ๋๋ค.
- model: ์ ํ์ ๋ชจ๋ธ ์ด๋ฆ ๋๋ ๋ชจ๋ธ ์ธ์คํด์ค์ ๋๋ค.
- middleware: ํ์ ์์ด์ ํธ์ ์ฒจ๋ถํ ์ถ๊ฐ ๋ฏธ๋ค์จ์ด์ ๋๋ค. ๋ฏธ๋ค์จ์ด ์๊ฐ์ ์๋ ๋ฐฉ์์ ๋ํด์๋ ์ฌ๊ธฐ๋ฅผ ์ฐธ๊ณ ํ์ธ์.
- interrupt_on: ๋๊ตฌ์ ๋ํ ํด๋จผ-์ธ-๋-๋ฃจํ ์ํธ์์ฉ์ ์ง์ ํ๋ ์ปค์คํ ์ธํฐ๋ฝํธ ๊ตฌ์ฑ์ ๋๋ค.
CompiledSubAgent ํ๋:
- name: ํ์ ์์ด์ ํธ์ ์ด๋ฆ์ด๋ฉฐ, ๋ฉ์ธ ์์ด์ ํธ๊ฐ ํ์ ์์ด์ ํธ๋ฅผ ํธ์ถํ ๋ ์ฌ์ฉํฉ๋๋ค.
- description: ๋ฉ์ธ ์์ด์ ํธ์๊ฒ ํ์๋๋ ํ์ ์์ด์ ํธ์ ์ค๋ช ์ ๋๋ค.
- runnable: ํ์ ์์ด์ ํธ๋ก ์ฌ์ฉ๋ ๋ฏธ๋ฆฌ ๊ตฌ์ถ๋ LangGraph ๊ทธ๋ํ/์์ด์ ํธ์ ๋๋ค.
SubAgent ์ฌ์ฉ
from collections.abc import Sequence
import os
from typing import Literal
from deepagents import CompiledSubAgent, SubAgent, create_deep_agent
from tavily import TavilyClient
tavily_client = TavilyClient(api_key=os.environ["TAVILY_API_KEY"])
def internet_search(
query: str,
max_results: int = 5,
topic: Literal["general", "news", "finance"] = "general",
include_raw_content: bool = False,
):
"""์น ๊ฒ์ ์คํ"""
return tavily_client.search(
query,
max_results=max_results,
include_raw_content=include_raw_content,
topic=topic,
)
research_subagent: SubAgent = {
"name": "research-agent",
"description": "๋ ์ฌํ๋ ์ง๋ฌธ์ ์กฐ์ฌํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค",
"system_prompt": "๋น์ ์ ํ๋ฅญํ ์ฐ๊ตฌ์์
๋๋ค",
"tools": [internet_search],
"model": "openai:gpt-4o", # ์ ํ์ ์ค๋ฒ๋ผ์ด๋, ๊ธฐ๋ณธ๊ฐ์ ๋ฉ์ธ ์์ด์ ํธ ๋ชจ๋ธ
}
agent = create_deep_agent(
model="anthropic:claude-sonnet-4-20250514",
subagents=[research_subagent],
)CustomSubAgent ์ฌ์ฉ
๋ ๋ณต์กํ ์ฌ์ฉ ์ฌ๋ก๋ฅผ ์ํด ์์ ์ ๋ฏธ๋ฆฌ ๊ตฌ์ถ๋ LangGraph ๊ทธ๋ํ๋ฅผ ํ์ ์์ด์ ํธ๋ก ์ ๊ณตํ ์ ์์ต๋๋ค:
from langchain.agents import create_agent
specialized_tools = []
# ๋ฏธ๋ฆฌ ๊ตฌ์ถ๋ ์ปค์คํ
์์ด์ ํธ ๊ทธ๋ํ
custom_graph = create_agent(
model=model,
tools=specialized_tools,
system_prompt="๋น์ ์ ๋ฐ์ดํฐ ๋ถ์์ ์ํ ์ ๋ฌธํ๋ ์์ด์ ํธ์
๋๋ค...",
)
# ์ปค์คํ
ํ์ ์์ด์ ํธ๋ก ์ฌ์ฉ
custom_subagent = CompiledSubAgent(
name="data-analyzer",
description="๋ณต์กํ ๋ฐ์ดํฐ ๋ถ์ ์์
์ ์ํ ์ ๋ฌธํ๋ ์์ด์ ํธ",
runnable=custom_graph,
)
subagents = [custom_subagent]
agent = create_deep_agent(
model="anthropic:claude-sonnet-4-20250514",
tools=[internet_search],
system_prompt=research_instructions,
subagents=subagents,
)interrupt_on
์์ด์ ํธ์ ์ผ๋ฐ์ ์ธ ํ์ค์ ์ผ๋ถ ๋๊ตฌ ์์ ์ด ๋ฏผ๊ฐํ ์ ์์ผ๋ฉฐ ์คํ ์ ์ ์ฌ๋์ ์น์ธ์ด ํ์ํ ์ ์๋ค๋ ๊ฒ์ ๋๋ค. Deep Agents๋ LangGraph์ ์ธํฐ๋ฝํธ ๊ธฐ๋ฅ์ ํตํด ํด๋จผ-์ธ-๋-๋ฃจํ ์ํฌํ๋ก์ฐ๋ฅผ ์ง์ํฉ๋๋ค. ์ฒดํฌํฌ์ธํฐ๋ฅผ ์ฌ์ฉํ์ฌ ์ด๋ค ๋๊ตฌ๊ฐ ์น์ธ์ ์๊ตฌํ๋์ง ๊ตฌ์ฑํ ์ ์์ต๋๋ค.
์ด๋ฌํ ๋๊ตฌ ๊ตฌ์ฑ์ ๋ฏธ๋ฆฌ ๊ตฌ์ถ๋ HITL ๋ฏธ๋ค์จ์ด์ ์ ๋ฌ๋๋ฏ๋ก ์์ด์ ํธ๊ฐ ์คํ์ ์ผ์ ์ค์งํ๊ณ ๊ตฌ์ฑ๋ ๋๊ตฌ๋ฅผ ์คํํ๊ธฐ ์ ์ ์ฌ์ฉ์์ ํผ๋๋ฐฑ์ ๊ธฐ๋ค๋ฆฝ๋๋ค.
from deepagents import create_deep_agent
from langchain_core.tools import tool
from langgraph.checkpoint.memory import InMemorySaver
@tool
def get_weather(city: str) -> str:
"""๋์์ ๋ ์จ๋ฅผ ๊ฐ์ ธ์ต๋๋ค."""
return f"{city}์ ๋ ์จ๋ ๋ง์ต๋๋ค."
# Deep Agent ์ธ์คํด์ค ์์ฑ
agent = create_deep_agent(
# ์ฌ์ฉํ LLM ๋ชจ๋ธ ์ง์
model="anthropic:claude-sonnet-4-20250514",
# ์์ด์ ํธ๊ฐ ์ฌ์ฉ ๊ฐ๋ฅํ ๋๊ตฌ ๋ชฉ๋ก
tools=[get_weather],
# ํด๋จผ-์ธ-๋-๋ฃจํ(Human-in-the-Loop) ์ค์
# ํน์ ๋๊ตฌ๊ฐ ํธ์ถ๋ ๋ ์ฌ๋์ ๊ฐ์
์ ์์ฒญํ๋๋ก ์ค์
# get_weather ๋๊ตฌ๊ฐ ํธ์ถ๋๋ ค ํ ๋:
# - "approve": ์ฌ๋์ด ์น์ธํ๋ฉด ๋๊ตฌ ์คํ
# - "edit": ์ฌ๋์ด ๋๊ตฌ ์
๋ ฅ๊ฐ์ ์์
# - "reject": ์ฌ๋์ด ๊ฑฐ์ ํ๋ฉด ๋๊ตฌ ์คํ ์ ํจ
interrupt_on={
"get_weather": {"allowed_decisions": ["approve", "edit", "reject"]},
},
checkpointer=InMemorySaver(),
)from uuid import uuid4
from langchain_core.runnables import RunnableConfig
config = RunnableConfig(configurable={"thread_id": uuid4()})
# ์์ด์ ํธ ํธ์ถ
result = agent.invoke(
{"messages": [{"role": "user", "content": "์ค๋ ์์ธ ๋ ์จ๋?"}]},
config=config,
)
result{'messages': [HumanMessage(content='์ค๋ ์์ธ ๋ ์จ๋?', additional_kwargs={}, response_metadata={}, id='4d0bf508-ca45-4015-94b6-269a11344a43'),
AIMessage(content=[{'text': '์์ธ์ ์ค๋ ๋ ์จ๋ฅผ ํ์ธํด๋๋ฆฌ๊ฒ ์ต๋๋ค.', 'type': 'text'}, {'id': 'toolu_01MeKvCBnJ66DsCnqrXvARJB', 'input': {'city': '์์ธ'}, 'name': 'get_weather', 'type': 'tool_use'}], additional_kwargs={}, response_metadata={'id': 'msg_01XYTekbagQ7pUAf4qoDn9jH', 'model': 'claude-sonnet-4-20250514', 'stop_reason': 'tool_use', 'stop_sequence': None, 'usage': {'cache_creation': {'ephemeral_1h_input_tokens': 0, 'ephemeral_5m_input_tokens': 0}, 'cache_creation_input_tokens': 0, 'cache_read_input_tokens': 5676, 'input_tokens': 3, 'output_tokens': 80, 'server_tool_use': None, 'service_tier': 'standard'}, 'model_name': 'claude-sonnet-4-20250514', 'model_provider': 'anthropic'}, id='lc_run--4b14eb35-a9af-49b0-afa7-a9ce2232c48e-0', tool_calls=[{'name': 'get_weather', 'args': {'city': '์์ธ'}, 'id': 'toolu_01MeKvCBnJ66DsCnqrXvARJB', 'type': 'tool_call'}], usage_metadata={'input_tokens': 5679, 'output_tokens': 80, 'total_tokens': 5759, 'input_token_details': {'cache_read': 5676, 'cache_creation': 0, 'ephemeral_5m_input_tokens': 0, 'ephemeral_1h_input_tokens': 0}})],
'__interrupt__': [Interrupt(value={'action_requests': [{'name': 'get_weather', 'args': {'city': '์์ธ'}, 'description': "Tool execution requires approval\n\nTool: get_weather\nArgs: {'city': '์์ธ'}"}], 'review_configs': [{'action_name': 'get_weather', 'allowed_decisions': ['approve', 'edit', 'reject']}]}, id='be4534667c08a16555d41340e83a8fcb')]}
from langgraph.graph.state import Command
result = agent.invoke(
Command(resume={"decisions": [{"type": "approve"}]}),
config=config,
)
result{'messages': [HumanMessage(content='์ค๋ ์์ธ ๋ ์จ๋?', additional_kwargs={}, response_metadata={}, id='4d0bf508-ca45-4015-94b6-269a11344a43'),
AIMessage(content=[{'text': '์์ธ์ ์ค๋ ๋ ์จ๋ฅผ ํ์ธํด๋๋ฆฌ๊ฒ ์ต๋๋ค.', 'type': 'text'}, {'id': 'toolu_01MeKvCBnJ66DsCnqrXvARJB', 'input': {'city': '์์ธ'}, 'name': 'get_weather', 'type': 'tool_use'}], additional_kwargs={}, response_metadata={'id': 'msg_01XYTekbagQ7pUAf4qoDn9jH', 'model': 'claude-sonnet-4-20250514', 'stop_reason': 'tool_use', 'stop_sequence': None, 'usage': {'cache_creation': {'ephemeral_1h_input_tokens': 0, 'ephemeral_5m_input_tokens': 0}, 'cache_creation_input_tokens': 0, 'cache_read_input_tokens': 5676, 'input_tokens': 3, 'output_tokens': 80, 'server_tool_use': None, 'service_tier': 'standard'}, 'model_name': 'claude-sonnet-4-20250514', 'model_provider': 'anthropic'}, id='lc_run--4b14eb35-a9af-49b0-afa7-a9ce2232c48e-0', tool_calls=[{'name': 'get_weather', 'args': {'city': '์์ธ'}, 'id': 'toolu_01MeKvCBnJ66DsCnqrXvARJB', 'type': 'tool_call'}], usage_metadata={'input_tokens': 5679, 'output_tokens': 80, 'total_tokens': 5759, 'input_token_details': {'cache_creation': 0, 'cache_read': 5676, 'ephemeral_5m_input_tokens': 0, 'ephemeral_1h_input_tokens': 0}}),
ToolMessage(content='์์ธ์ ๋ ์จ๋ ๋ง์ต๋๋ค.', name='get_weather', id='0a18ce11-39f6-466f-916a-90109c8bc5c6', tool_call_id='toolu_01MeKvCBnJ66DsCnqrXvARJB'),
AIMessage(content='์ค๋ ์์ธ์ ๋ ์จ๋ **๋ง์ต๋๋ค**. ์ข์ ๋ ์จ๋ค์!', additional_kwargs={}, response_metadata={'id': 'msg_01TmMB9PowPsDUxUzfVoEzSi', 'model': 'claude-sonnet-4-20250514', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'cache_creation': {'ephemeral_1h_input_tokens': 0, 'ephemeral_5m_input_tokens': 104}, 'cache_creation_input_tokens': 104, 'cache_read_input_tokens': 5676, 'input_tokens': 5, 'output_tokens': 35, 'server_tool_use': None, 'service_tier': 'standard'}, 'model_name': 'claude-sonnet-4-20250514', 'model_provider': 'anthropic'}, id='lc_run--371f3335-656e-4665-8bde-b913e679747e-0', usage_metadata={'input_tokens': 5785, 'output_tokens': 35, 'total_tokens': 5820, 'input_token_details': {'cache_read': 5676, 'cache_creation': 104, 'ephemeral_5m_input_tokens': 104, 'ephemeral_1h_input_tokens': 0}})]}
config = RunnableConfig(configurable={"thread_id": uuid4()})
result = agent.invoke(
{"messages": [{"role": "user", "content": "์ค๋ ์์ธ ๋ ์จ๋?"}]},
config=config,
)
result = agent.invoke(
Command(resume={"decisions": [{"type": "reject"}]}),
config=config,
)
result{'messages': [HumanMessage(content='์ค๋ ์์ธ ๋ ์จ๋?', additional_kwargs={}, response_metadata={}, id='1d58209f-13d9-4066-aa11-300fcd9e5f81'),
AIMessage(content=[{'text': '์์ธ์ ์ค๋ ๋ ์จ๋ฅผ ํ์ธํด๋๋ฆฌ๊ฒ ์ต๋๋ค.', 'type': 'text'}, {'id': 'toolu_01SneWTNnYpRBtKowmzWwSwe', 'input': {'city': '์์ธ'}, 'name': 'get_weather', 'type': 'tool_use'}], additional_kwargs={}, response_metadata={'id': 'msg_01ESgyzVktH7ocXAJ4LziqSM', 'model': 'claude-sonnet-4-20250514', 'stop_reason': 'tool_use', 'stop_sequence': None, 'usage': {'cache_creation': {'ephemeral_1h_input_tokens': 0, 'ephemeral_5m_input_tokens': 0}, 'cache_creation_input_tokens': 0, 'cache_read_input_tokens': 5676, 'input_tokens': 3, 'output_tokens': 80, 'server_tool_use': None, 'service_tier': 'standard'}, 'model_name': 'claude-sonnet-4-20250514', 'model_provider': 'anthropic'}, id='lc_run--f29fa7e8-7d3e-4188-bc9b-86dab8a2e857-0', tool_calls=[{'name': 'get_weather', 'args': {'city': '์์ธ'}, 'id': 'toolu_01SneWTNnYpRBtKowmzWwSwe', 'type': 'tool_call'}], usage_metadata={'input_tokens': 5679, 'output_tokens': 80, 'total_tokens': 5759, 'input_token_details': {'cache_creation': 0, 'cache_read': 5676, 'ephemeral_5m_input_tokens': 0, 'ephemeral_1h_input_tokens': 0}}),
ToolMessage(content='User rejected the tool call for `get_weather` with id toolu_01SneWTNnYpRBtKowmzWwSwe', name='get_weather', id='bfd73daf-5505-4f24-9c74-18bead370b2c', tool_call_id='toolu_01SneWTNnYpRBtKowmzWwSwe', status='error'),
AIMessage(content='์ฃ์กํฉ๋๋ค. ๋ ์จ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ฌ ์ ์์ต๋๋ค. ๋ ์จ ์ ๋ณด๋ฅผ ํ์ธํ์๋ ค๋ฉด ๋ค์๊ณผ ๊ฐ์ ๋ฐฉ๋ฒ์ ์ถ์ฒ๋๋ฆฝ๋๋ค:\n\n1. **๋ ์จ ์ฑ ์ฌ์ฉ**: ์ค๋งํธํฐ์ ๊ธฐ๋ณธ ๋ ์จ ์ฑ์ด๋ ๋ค์ด๋ฒ ๋ ์จ, ์จ๋์ฑ๋ ๋ฑ์ ์ฑ ํ์ฉ\n2. **ํฌํธ ์ฌ์ดํธ**: ๋ค์ด๋ฒ, ๋ค์ ๋ฑ์์ "์์ธ ๋ ์จ" ๊ฒ์\n3. **๊ธฐ์์ฒญ ํํ์ด์ง**: weather.go.kr์์ ์ ํํ ๊ธฐ์์ ๋ณด ํ์ธ\n4. **์ค์๊ฐ ๊ฒ์**: "์์ธ ๋ ์จ"๋ก ๊ฒ์ํ๋ฉด ํ์ฌ ๊ธฐ์จ, ์ต๋, ๊ฐ์ํ๋ฅ ๋ฑ์ ํ์ธํ ์ ์์ต๋๋ค\n\n๋ค๋ฅธ ๋์์ด ํ์ํ์๋ฉด ์ธ์ ๋ ๋ง์ํด ์ฃผ์ธ์!', additional_kwargs={}, response_metadata={'id': 'msg_01MVzFJpFx6j2kr6ZAKX4tu8', 'model': 'claude-sonnet-4-20250514', 'stop_reason': 'end_turn', 'stop_sequence': None, 'usage': {'cache_creation': {'ephemeral_1h_input_tokens': 0, 'ephemeral_5m_input_tokens': 132}, 'cache_creation_input_tokens': 132, 'cache_read_input_tokens': 5676, 'input_tokens': 7, 'output_tokens': 271, 'server_tool_use': None, 'service_tier': 'standard'}, 'model_name': 'claude-sonnet-4-20250514', 'model_provider': 'anthropic'}, id='lc_run--a5f43739-4766-4c09-aca7-07fc33339f38-0', usage_metadata={'input_tokens': 5815, 'output_tokens': 271, 'total_tokens': 6086, 'input_token_details': {'cache_read': 5676, 'cache_creation': 132, 'ephemeral_5m_input_tokens': 132, 'ephemeral_1h_input_tokens': 0}})]}
Deep Agents ๋ฏธ๋ค์จ์ด
Deep Agents๋ ๋ชจ๋์ ๋ฏธ๋ค์จ์ด ์ํคํ ์ฒ๋ก ๊ตฌ์ถ๋ฉ๋๋ค. ์๊ธฐํ๋ฏ์ด, Deep Agents๋ ๋ค์์ ์ ๊ทผํ ์ ์์ต๋๋ค:
- ๊ณํ ๋๊ตฌ
- ์ปจํ ์คํธ์ ์ฅ๊ธฐ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ ์ฅํ๊ธฐ ์ํ ํ์ผ ์์คํ
- ํ์ ์์ด์ ํธ๋ฅผ ์์ฑํ๋ ๊ธฐ๋ฅ
์ด๋ฌํ ๊ฐ ๊ธฐ๋ฅ์ ๋ณ๋์ ๋ฏธ๋ค์จ์ด๋ก ๊ตฌํ๋ฉ๋๋ค. create_deep_agent๋ก Deep Agent๋ฅผ ๋ง๋ค ๋, ์ฐ๋ฆฌ๋ ์๋์ผ๋ก TodoListMiddleware, FilesystemMiddleware, SubAgentMiddleware๋ฅผ ์์ด์ ํธ์ ์ฒจ๋ถํฉ๋๋ค.
๋ฏธ๋ค์จ์ด๋ ๊ตฌ์ฑ ๊ฐ๋ฅํ ๊ฐ๋ ์ด๋ฉฐ, ์ฌ์ฉ ์ฌ๋ก์ ๋ฐ๋ผ ์์ด์ ํธ์ ์ํ๋ ๋งํผ ๋ง๊ฑฐ๋ ์ ์ ๋ฏธ๋ค์จ์ด๋ฅผ ์ถ๊ฐํ ์ ์์ต๋๋ค. ์ฆ, ์์ ์ธ๊ธํ ๋ฏธ๋ค์จ์ด ์ค ์ด๋ ๊ฒ๋ ๋ ๋ฆฝ์ ์ผ๋ก ์ฌ์ฉํ ์ ์์ต๋๋ค!
TodoListMiddleware
๊ณํ์ ๋ณต์กํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ๋ฐ ํ์์ ์ ๋๋ค. ์ต๊ทผ์ Claude Code๋ฅผ ์ฌ์ฉํด๋ณธ ์ ์ด ์๋ค๋ฉด, ๋ณต์กํ ๋ค์ค ๋ถ๋ถ ์์ ์ ์ํํ๊ธฐ ์ ์ ์ด๋ป๊ฒ To-Do ๋ชฉ๋ก์ ์์ฑํ๋์ง ์ ์ ์์ต๋๋ค. ์๋ก์ด ์ ๋ณด๊ฐ ๋ค์ด์ค๋ฉด์ ์ด To-Do ๋ชฉ๋ก์ ์ฆ์์์ ์ด๋ป๊ฒ ์์ ํ๊ณ ์ ๋ฐ์ดํธํ ์ ์๋์ง๋ ์ ์ ์์ต๋๋ค.
TodoListMiddleware๋ ์์ด์ ํธ์ ์ด To-Do ๋ชฉ๋ก์ ์
๋ฐ์ดํธํ๊ธฐ ์ํ ํน์ ๋๊ตฌ๋ฅผ ์ ๊ณตํฉ๋๋ค. ๋ค์ค ๋ถ๋ถ ์์
์ ์คํํ๊ธฐ ์ ํ์ ์์ด์ ํธ๋ write_todos ๋๊ตฌ๋ฅผ ์ฌ์ฉํ์ฌ ์์ ์ด ํ๊ณ ์๋ ์์
๊ณผ ๋จ์ ์์
์ ์ถ์ ํ๋ผ๋ ํ๋กฌํํธ๊ฐ ํ์๋ฉ๋๋ค.
from langchain.agents import create_agent
from langchain.agents.middleware import TodoListMiddleware
# TodoListMiddleware๋ create_deep_agent์์ ๊ธฐ๋ณธ์ ์ผ๋ก ํฌํจ๋ฉ๋๋ค
# ์ปค์คํ
์์ด์ ํธ๋ฅผ ๊ตฌ์ถํ ๋ ์ปค์คํฐ๋ง์ด์งํ ์ ์์ต๋๋ค
agent = create_agent(
model="anthropic:claude-haiku-4-5",
# ์ปค์คํ
๊ณํ ์ง์นจ์ ๋ฏธ๋ค์จ์ด๋ฅผ ํตํด ์ถ๊ฐํ ์ ์์ต๋๋ค
middleware=[
TodoListMiddleware(
system_prompt="write_todos ๋๊ตฌ๋ฅผ ์ฌ์ฉํ์ฌ..." # ์ ํ์ : ์์คํ
ํ๋กฌํํธ์ ๋ํ ์ปค์คํ
์ถ๊ฐ
),
],
)FilesystemMiddleware
์ปจํ ์คํธ ์์ง๋์ด๋ง์ ํจ๊ณผ์ ์ธ ์์ด์ ํธ๋ฅผ ๊ตฌ์ถํ๋ ๋ฐ ์์ด ์ฃผ์ ๊ณผ์ ์ค ํ๋์ ๋๋ค. ํนํ ๊ฐ๋ณ ๊ธธ์ด ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ ์ ์๋ ๋๊ตฌ(์: web_search, rag)๋ฅผ ์ฌ์ฉํ ๋๋ ๋์ฑ ์ด๋ ค์ธ ์ ์์ผ๋ฉฐ, ๊ธด ToolResults๊ฐ ๋น ๋ฅด๊ฒ ์ปจํ ์คํธ ์๋์ฐ๋ฅผ ์ฑ์ธ ์ ์์ต๋๋ค.
FilesystemMiddleware๋ ์์ด์ ํธ์ ๋จ๊ธฐ ๋ฐ ์ฅ๊ธฐ ๋ฉ๋ชจ๋ฆฌ์ ์ํธ์์ฉํ ์ ์๋ ๋ค ๊ฐ์ง ๋๊ตฌ๋ฅผ ์ ๊ณตํฉ๋๋ค.
- ls: ํ์ผ ์์คํ ์ ํ์ผ ๋ชฉ๋ก ํ์
- read_file: ํ์ผ ์ ์ฒด ๋๋ ํ์ผ์ ํน์ ํ ์ ์ฝ๊ธฐ
- write_file: ํ์ผ ์์คํ ์ ์ ํ์ผ ์์ฑ
- edit_file: ํ์ผ ์์คํ ์ ๊ธฐ์กด ํ์ผ ํธ์ง
from deepagents.middleware.filesystem import FilesystemMiddleware
from langchain.agents import create_agent
# FilesystemMiddleware๋ create_deep_agent์์ ๊ธฐ๋ณธ์ ์ผ๋ก ํฌํจ๋ฉ๋๋ค
# ์ปค์คํ
์์ด์ ํธ๋ฅผ ๊ตฌ์ถํ ๋ ์ปค์คํฐ๋ง์ด์งํ ์ ์์ต๋๋ค
agent = create_agent(
model="anthropic:claude-sonnet-4-20250514",
middleware=[
FilesystemMiddleware(
# backend=..., # ์ ํ์ : ์ ์ฅ์ ๋ฐฑ์๋ ์ปค์คํฐ๋ง์ด์ง
system_prompt="ํ์ผ ์์คํ
์ ์ธ ๋...", # ์ ํ์ : ์ปค์คํ
์์คํ
ํ๋กฌํํธ ์ค๋ฒ๋ผ์ด๋
custom_tool_descriptions={
"ls": "๋ค์์ ๊ฒฝ์ฐ์ ls ๋๊ตฌ๋ฅผ ์ฌ์ฉํ์ธ์...",
"read_file": "read_file ๋๊ตฌ๋ฅผ ์ฌ์ฉํ์ฌ...",
}, # ์ ํ์ : ํ์ผ ์์คํ
๋๊ตฌ์ ๋ํ ์ปค์คํ
์ค๋ช
),
],
)SubAgentMiddleware
ํ์ ์์ด์ ํธ์ ์์
์ ์์ํ๋ ๊ฒ์ ์ปจํ
์คํธ๋ฅผ ๊ฒฉ๋ฆฌํ๋ ์ข์ ๋ฐฉ๋ฒ์ผ๋ก, ๋ฉ์ธ(๊ฐ๋
) ์์ด์ ํธ์ ์ปจํ
์คํธ ์๋์ฐ๋ฅผ ๊น๋ํ๊ฒ ์ ์งํ๋ฉด์๋ ์์
์ ๊น์ด ์๊ฒ ์ํํ ์ ์์ต๋๋ค. ํ์ ์์ด์ ํธ ๋ฏธ๋ค์จ์ด๋ฅผ ํตํด task ๋๊ตฌ๋ฅผ ํตํด ํ์ ์์ด์ ํธ๋ฅผ ์ ๊ณตํ ์ ์์ต๋๋ค.
ํ์ ์์ด์ ํธ๋ ์ด๋ฆ, ์ค๋ช , ์์คํ ํ๋กฌํํธ, ๋๊ตฌ๋ก ์ ์๋ฉ๋๋ค. ํ์ ์์ด์ ํธ์ ์ปค์คํ ๋ชจ๋ธ์ด๋ ์ถ๊ฐ ๋ฏธ๋ค์จ์ด๋ฅผ ์ ๊ณตํ ์๋ ์์ต๋๋ค. ์ด๋ ํ์ ์์ด์ ํธ์ ๋ฉ์ธ ์์ด์ ํธ์ ๊ณต์ ํ ์ถ๊ฐ ์ํ ํค๋ฅผ ์ ๊ณตํ๋ ค๋ ๊ฒฝ์ฐ ํนํ ์ ์ฉํ ์ ์์ต๋๋ค.
from deepagents.middleware.subagents import SubAgentMiddleware
from langchain_core.tools import tool
from langchain.agents import create_agent
@tool
def get_weather(city: str) -> str:
"""๋์์ ๋ ์จ๋ฅผ ๊ฐ์ ธ์ต๋๋ค."""
return f"{city}์ ๋ ์จ๋ ๋ง์ต๋๋ค."
agent = create_agent(
model="claude-sonnet-4-20250514",
middleware=[
SubAgentMiddleware(
default_model="anthropic:claude-haiku-4-5",
default_tools=[],
subagents=[
{
"name": "weather",
"description": "์ด ํ์ ์์ด์ ํธ๋ ๋์์ ๋ ์จ๋ฅผ ๊ฐ์ ธ์ฌ ์ ์์ต๋๋ค.",
"system_prompt": "get_weather ๋๊ตฌ๋ฅผ ์ฌ์ฉํ์ฌ ๋์์ ๋ ์จ๋ฅผ ๊ฐ์ ธ์ต๋๋ค.",
"tools": [get_weather],
"model": "gpt-4.1",
"middleware": [],
}
],
)
],
)๋ ๋ณต์กํ ์ฌ์ฉ ์ฌ๋ก์ ๊ฒฝ์ฐ ์์ ์ ๋ฏธ๋ฆฌ ๊ตฌ์ถ๋ LangGraph ๊ทธ๋ํ๋ฅผ ํ์ ์์ด์ ํธ๋ก ์ ๊ณตํ ์ ์์ต๋๋ค.
from langgraph import StateGraph
# ์ปค์คํ
LangGraph ๊ทธ๋ํ ์์ฑ
def create_weather_graph():
workflow = StateGraph(...)
# ์ปค์คํ
๊ทธ๋ํ ๊ตฌ์ถ
return workflow.compile()
weather_graph = create_weather_graph()
# CompiledSubAgent์ ๋ํ
weather_subagent = CompiledSubAgent(
name="weather",
description="์ด ํ์ ์์ด์ ํธ๋ ๋์์ ๋ ์จ๋ฅผ ๊ฐ์ ธ์ฌ ์ ์์ต๋๋ค.",
runnable=weather_graph,
)
agent = create_agent(
model="anthropic:claude-sonnet-4-20250514",
middleware=[
SubAgentMiddleware(
default_model="anthropic:claude-haiku-4-5",
default_tools=[],
subagents=[weather_subagent],
)
],
)๋๊ธฐ ๋ ๋น๋๊ธฐ
deepagents์ ์ด์ ๋ฒ์ ์ ๋๊ธฐ ๋ฐ ๋น๋๊ธฐ ์์ด์ ํธ ํฉํ ๋ฆฌ๋ฅผ ๊ตฌ๋ถํ์ต๋๋ค.
async_create_deep_agent๊ฐ create_deep_agent๋ก ํตํฉ๋์์ต๋๋ค.
๋๊ธฐ ๋ฐ ๋น๋๊ธฐ ์์ด์ ํธ ๋ชจ๋์ ๋ํด ํฉํ ๋ฆฌ๋ก create_deep_agent๋ฅผ ์ฌ์ฉํด์ผ ํฉ๋๋ค.
MCP
deepagents ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ MCP ๋๊ตฌ์ ํจ๊ป ์คํํ ์ ์์ต๋๋ค. ์ด๋ Langchain MCP Adapter ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ฌ ๋ฌ์ฑํ ์ ์์ต๋๋ค.
์ฐธ๊ณ : MCP ๋๊ตฌ๋ ๋น๋๊ธฐ์ด๋ฏ๋ก from deepagents import async_create_deep_agent๋ฅผ ์ฌ์ฉํ๋ ค๊ณ ํฉ๋๋ค.
(์๋ ์์ ๋ฅผ ์คํํ๋ ค๋ฉด pip install langchain-mcp-adapters๋ฅผ ์ค์นํด์ผ ํฉ๋๋ค.)
import asyncio
from deepagents import create_deep_agent
from langchain_mcp_adapters.client import MultiServerMCPClient
import nest_asyncio
nest_asyncio.apply()
async def main():
# MCP ๋๊ตฌ ์์ง
mcp_client = MultiServerMCPClient(
{
"fetch": {
"transport": "stdio",
"command": "uvx",
"args": ["mcp-server-fetch"],
}
}
)
mcp_tools = await mcp_client.get_tools()
# ์์ด์ ํธ ์์ฑ
agent = create_deep_agent(tools=mcp_tools)
# ์์ด์ ํธ ์คํธ๋ฆฌ๋ฐ
async for chunk in agent.astream(
{"messages": [{"role": "user", "content": "langgraph๋ ๋ฌด์์ธ๊ฐ์?"}]},
stream_mode="values",
):
if "messages" in chunk:
chunk["messages"][-1].pretty_print()
asyncio.run(main())================================[1m Human Message [0m=================================
langgraph๋ ๋ฌด์์ธ๊ฐ์?
================================[1m Human Message [0m=================================
langgraph๋ ๋ฌด์์ธ๊ฐ์?
==================================[1m Ai Message [0m==================================
LangGraph๋ **LangChain ์ํ๊ณ์ ์ผ๋ถ๋ก ๊ฐ๋ฐ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ**๋ก, LLM(๋๊ท๋ชจ ์ธ์ด ๋ชจ๋ธ) ์ ํ๋ฆฌ์ผ์ด์
์์ **๋ณต์กํ ์์ด์ ํธ ์ํฌํ๋ก์ฐ๋ฅผ ๊ทธ๋ํ ํํ๋ก ๊ตฌ์ถ**ํ ์ ์๊ฒ ํด์ฃผ๋ ํ๋ ์์ํฌ์
๋๋ค.
## ์ฃผ์ ํน์ง
### 1. **๊ทธ๋ํ ๊ธฐ๋ฐ ์ํฌํ๋ก์ฐ**
- ์ ํ๋ฆฌ์ผ์ด์
์ ํ๋ฆ์ ๋
ธ๋(node)์ ์ฃ์ง(edge)๋ก ๊ตฌ์ฑ๋ ๊ทธ๋ํ๋ก ํํ
- ๊ฐ ๋
ธ๋๋ ํน์ ์์
์ด๋ ํจ์๋ฅผ ๋ํ๋ด๊ณ , ์ฃ์ง๋ ์คํ ํ๋ฆ์ ์ ์
### 2. **์ํ ๊ด๋ฆฌ(Stateful)**
- ๋ํ๋ ์์
์ ์ํ๋ฅผ ์ง์์ ์ผ๋ก ๊ด๋ฆฌ
- ์ด์ ์ํธ์์ฉ์ ์ปจํ
์คํธ๋ฅผ ์ ์งํ๋ฉด์ ๋ณต์กํ ๋ค๋จ๊ณ ์์
์ฒ๋ฆฌ
### 3. **์ํ ๊ตฌ์กฐ ์ง์**
- ์ผ๋ฐ์ ์ธ DAG(๋ฐฉํฅ์ฑ ๋น์ํ ๊ทธ๋ํ)์ ๋ฌ๋ฆฌ **์ํ(cycle)**์ ํ์ฉ
- ์์ด์ ํธ๊ฐ ์ด์ ๋จ๊ณ๋ก ๋์๊ฐ๊ฑฐ๋ ๋ฐ๋ณต ์์
์ ์ํํ ์ ์์
### 4. **์ฒดํฌํฌ์ธํธ์ ์๊ฐ ์ฌํ**
- ์ํฌํ๋ก์ฐ์ ํน์ ์์ ์ ์ ์ฅํ๊ณ ๋ณต์ ๊ฐ๋ฅ
- ๋๋ฒ๊น
์ด๋ ์ค๋ฅ ๋ณต๊ตฌ์ ์ ์ฉ
## ์ฃผ์ ์ฌ์ฉ ์ฌ๋ก
- **๋ณต์กํ AI ์์ด์ ํธ**: ๋ค์ํ ๋๊ตฌ๋ฅผ ์ฌ์ฉํ๋ ์์จ ์์ด์ ํธ
- **๋ฉํฐ ์์ด์ ํธ ์์คํ
**: ์ฌ๋ฌ ์์ด์ ํธ๊ฐ ํ์
ํ๋ ์์คํ
- **๋ํํ ์ ํ๋ฆฌ์ผ์ด์
**: ๋ณต์กํ ๋ํ ํ๋ฆ์ ๊ฐ์ง ์ฑ๋ด
- **์ํฌํ๋ก์ฐ ์๋ํ**: ์กฐ๊ฑด๋ถ ๋ถ๊ธฐ์ ๋ฃจํ๊ฐ ์๋ ์์
์๋ํ
## ๊ธฐ๋ณธ ๊ตฌ์กฐ ์์
```python
from langgraph.graph import StateGraph
# ๊ทธ๋ํ ์ ์
workflow = StateGraph(State)
# ๋
ธ๋ ์ถ๊ฐ
workflow.add_node("agent", agent_function)
workflow.add_node("tool", tool_function)
# ์ฃ์ง ์ถ๊ฐ (ํ๋ฆ ์ ์)
workflow.add_edge("agent", "tool")
workflow.add_conditional_edges("tool", should_continue)
# ์ปดํ์ผ
app = workflow.compile()
```
LangGraph๋ ํนํ **๋ณต์กํ ์์ฌ๊ฒฐ์ ๋ก์ง**์ด ํ์ํ๊ฑฐ๋, **์ฌ๋ฌ ๋จ๊ณ๋ฅผ ๊ฑฐ์ณ์ผ ํ๋ AI ์ ํ๋ฆฌ์ผ์ด์
**์ ๊ตฌ์ถํ ๋ ๋งค์ฐ ์ ์ฉํฉ๋๋ค.
๋ ์์ธํ ์ ๋ณด๊ฐ ํ์ํ์๊ฑฐ๋ ํน์ ์ฌ์ฉ ์์๋ฅผ ์๊ณ ์ถ์ผ์๋ค๋ฉด ๋ง์ํด ์ฃผ์ธ์!