LangGraph 공식문서를 번역한 내용입니다. 필요한 경우 부연 설명을 추가하였고 이해하기 쉽게 예제를 일부 변경하였습니다. 문제가 되면 삭제하겠습니다.
https://langchain-ai.github.io/langgraph/how-tos/tool-calling/
이 가이드는 LangGraph의 사전 구축된 ToolNode
를 도구 호출에 사용하는 방법을 다룬다.
ToolNode
는 그래프 상태(메시지 목록 포함)를 입력으로 받아 도구 호출 결과를 포함한 상태 업데이트를 출력하는 LangChain Runnable이다. 이 노드는 LangGraph의 사전 구축된 ReAct 에이전트와 잘 작동하도록 설계되었지만, 상태에 적절한 리듀서(예: MessagesState
)가 있는 메시지 키가 포함된 모든 StateGraph와도 함께 사용할 수 있다.
준비
우선, 필요한 패키지를 설치하자.
pip install langgraph langchain_openai
도구 정의
from langchain_core.messages import AIMessage
from langchain_core.tools import tool
from langgraph.prebuilt import ToolNode
@tool
def get_weather(location: str):
"""Call to get the current weather."""
if location in ["서울", "인천"]:
return "현재 기온은 20도이고 구름이 많아."
else:
return "현재 기온은 30도이며 맑아"
@tool
def get_coolest_cities():
"""Get a list of coolest cities"""
return "서울, 인천"
tools = [get_weather, get_coolest_cities]
tool_node = ToolNode(tools)
수동으로 ToolNode 호출
ToolNode
는 메시지 목록을 포함한 그래프 상태에서 작동한다. 이 노드는 목록의 마지막 메시지가 tool_calls
파라미터를 가진 AIMessage
일 것으로 기대한다.
먼저, 도구 노드를 수동으로 호출하는 방법을 살펴보자.
message_with_single_tool_call = AIMessage(
content="",
tool_calls=[
{
"name": "get_weather",
"args": {"location": "서울"},
"id": "tool_call_id",
"type": "tool_call",
}
],
)
result = tool_node.invoke({"messages": [message_with_single_tool_call]})
print(result)
{'messages': [ToolMessage(content='현재 기온은 20도이고 구름이 많아.', name='get_weather', tool_call_id='tool_call_id')]}
일반적으로 AIMessage
를 수동으로 생성할 필요는 없으며, 도구 호출을 지원하는 LangChain의 채팅 모델이 이를 자동으로 생성한다.
또한 AIMessage
의 tool_calls
파라미터에 여러 도구 호출을 전달하면 ToolNode
를 사용하여 병렬 도구 호출을 할 수 있다.
message_with_multiple_tool_calls = AIMessage(
content="",
tool_calls=[
{
"name": "get_coolest_cities",
"args": {},
"id": "tool_call_id_1",
"type": "tool_call",
},
{
"name": "get_weather",
"args": {"location": "서울"},
"id": "tool_call_id_2",
"type": "tool_call",
},
],
)
result = tool_node.invoke({"messages": [message_with_multiple_tool_calls]})
print(result)
{'messages': [ToolMessage(content='서울, 인천', name='get_coolest_cities', tool_call_id='tool_call_id_1'), ToolMessage(content='현재 기온은 20도이고 구름이 많아.', name='get_weather', tool_call_id='tool_call_id_2')]}
챗 모델 사용
예제에서는 OpenAI의 채팅 모델을 사용한다. 도구 호출이 가능한 채팅 모델을 사용하려면 먼저 모델이 사용 가능한 도구를 인식하도록 해야 한다. 이를 위해 ChatOpenAI
모델에서 .bind_tools
메서드를 호출한다.
from dotenv import load_dotenv
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
from langgraph.prebuilt import ToolNode
load_dotenv()
@tool
def get_weather(location: str):
"""Call to get the current weather."""
if location in ["서울", "인천"]:
return "현재 기온은 20도이고 구름이 많아."
else:
return "현재 기온은 30도이며 맑아"
@tool
def get_coolest_cities():
"""Get a list of coolest cities"""
return "서울, 인천"
tools = [get_weather, get_coolest_cities]
tool_node = ToolNode(tools)
model_with_tools = ChatOpenAI(model="gpt-4o-mini", temperature=0).bind_tools(tools)
result = model_with_tools.invoke("서울 날씨 어때?").tool_calls
print(result)
[{'name': 'get_weather', 'args': {'location': '서울'}, 'id': 'call_Ri016cJtayiot5WbA6SfKgRU', 'type': 'tool_call'}]
보는바와 같이, 채팅 모델에서 생성된 AI 메시지는 이미 tool_calls
가 채워져 있으므로 이를 그대로 ToolNode
에 전달할 수 있다.
result = tool_node.invoke({"messages": [model_with_tools.invoke("서울 날씨 어때?")]})
print(result)
{'messages': [ToolMessage(content='현재 기온은 20도이고 구름이 많아.', name='get_weather', tool_call_id='call_fXsYbdTIQmi9sCpL9FUZe1x3')]}
ReAct Agent
다음으로, LangGraph 그래프 내에서 ToolNode
를 사용하는 방법을 살펴보자. 먼저 ReAct 에이전트의 그래프 구현을 설정해 보자. 이 에이전트는 일부 쿼리를 입력받아 충분한 정보를 얻을 때까지 도구를 반복적으로 호출하여 쿼리를 해결한다. 우리는 방금 정의한 도구들과 함께 ToolNode
와 OpenAI 모델을 사용할 것이다.
from dotenv import load_dotenv
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph, MessagesState, START, END
from langgraph.prebuilt import ToolNode
load_dotenv()
@tool
def get_weather(location: str):
"""Call to get the current weather."""
if location in ["서울", "인천"]:
return "현재 기온은 20도이고 구름이 많아."
else:
return "현재 기온은 30도이며 맑아"
@tool
def get_coolest_cities():
"""Get a list of coolest cities"""
return "서울, 인천"
tools = [get_weather, get_coolest_cities]
tool_node = ToolNode(tools)
model_with_tools = ChatOpenAI(model="gpt-4o-mini", temperature=0).bind_tools(tools)
def should_continue(state: MessagesState):
messages = state["messages"]
last_message = messages[-1]
if last_message.tool_calls:
return "tools"
return END
def call_model(state: MessagesState):
messages = state["messages"]
response = model_with_tools.invoke(messages)
return {"messages": [response]}
workflow = StateGraph(MessagesState)
workflow.add_node("agent", call_model)
workflow.add_node("tools", tool_node)
workflow.add_edge(START, "agent")
workflow.add_conditional_edges("agent", should_continue, ["tools", END])
workflow.add_edge("tools", "agent")
app = workflow.compile()
from IPython.display import Image, display
try:
display(
Image(
app.get_graph().draw_mermaid_png(
output_file_path="how-to-call-tools-using-toolnode-with-chatmodel.png"
)
)
)
except Exception:
pass
시작해보자.
for chunk in app.stream(
{"messages": [("human", "서울 날씨 어때?")]}, stream_mode="values"
):
chunk["messages"][-1].pretty_print()
================================ Human Message =================================
서울 날씨 어때?
================================== Ai Message ==================================
Tool Calls:
get_weather (call_94wPLRZA6ekh2tYM30VFJMTY)
Call ID: call_94wPLRZA6ekh2tYM30VFJMTY
Args:
location: 서울
================================= Tool Message =================================
Name: get_weather
현재 기온은 20도이고 구름이 많아.
================================== Ai Message ==================================
현재 서울의 날씨는 기온 20도이며, 구름이 많습니다.
for chunk in app.stream(
{"messages": [("human", "가장 추운 도시 날씨 어때?")]},
stream_mode="values",
):
chunk["messages"][-1].pretty_print()
================================ Human Message =================================
가장 추운 도시 날씨 어때?
================================== Ai Message ==================================
Tool Calls:
get_coolest_cities (call_N6AYNe0j9SRwfVZDVnPuUhq6)
Call ID: call_N6AYNe0j9SRwfVZDVnPuUhq6
Args:
================================= Tool Message =================================
Name: get_coolest_cities
서울, 인천
================================== Ai Message ==================================
Tool Calls:
get_weather (call_xSXsCQowPtOKcHNk2n5keQrH)
Call ID: call_xSXsCQowPtOKcHNk2n5keQrH
Args:
location: 서울
get_weather (call_rJcfhDHX7N8yNZB0T7f37mll)
Call ID: call_rJcfhDHX7N8yNZB0T7f37mll
Args:
location: 인천
================================= Tool Message =================================
Name: get_weather
현재 기온은 20도이고 구름이 많아.
================================== Ai Message ==================================
가장 추운 도시인 서울과 인천의 날씨는 현재 기온이 20도입니다. 날씨가 따뜻하네요!
ToolNode
는 도구 실행 중 발생할 수 있는 오류를 처리할 수도 있다. 이를 활성화하거나 비활성화하려면 handle_tool_errors=True
로 설정하면 되며, 기본값은 활성화되어 있다. 오류 처리에 대한 자세한 내용은 여기에서 확인할 수 있다.