수민 '-'

플오그래밍

제가 작성하는 모든 글은 절대 상업적인 이용이 아니며, 그저 개인적인 공부 용도로만 사용하는 것임을 밝힙니다.

LangGraph로 챗봇 만들기 - Tool Calling Agent, Tavily, ToolNode, create_react_agent

Tool Calling Agent는 자신이 가진 지식만 사용하는 것이 아니라, 필요하면 외부 도구(API, 웹 검색, DB, 코드 실행기 등)를 호출해 문제를 해결하는 에이전트다. 쉽게 말해, “대화만 하는 AI”가 아니라 필요할 때 검색/계산/조회 같은 도구를 직접 쓰는 AI가 된다.

이번 글에서는 (1) Tavily로 웹 검색 도구를 붙이고, (2) LangChain에서 도구 바인딩을 하고, (3) LangGraph에서 ToolNode로 “LLM의 tool_calls를 실제 실행”까지 연결해 웹 검색 챗봇을 만드는 흐름을 정리한다. 원문 흐름은 랭그래프를 이용한 간단한 챗봇을 바탕으로 재구성했다.


1. Tool Calling Agent란?

Tool Calling Agent는 다음 순서로 움직인다.

1) 사용자 질문 이해
2) “도구가 필요하다” 판단
3) 어떤 도구를 어떤 입력으로 호출할지 결정(tool_calls 생성)
4) 도구 실행 결과를 받아서
5) 최종 답변 생성

이 구조를 잡아두면, 단순 Q&A를 넘어 “실제로 무언가를 처리하는 챗봇”으로 확장하기가 훨씬 쉬워진다.

원문 이미지:


2. Tavily: 웹 검색을 도구로 붙이기

Tavily는 웹을 검색해 AI가 최신/정확한 정보를 답변에 반영할 수 있게 돕는 검색 API다.

2-1. 설치와 키 설정

!pip install -U tavily-python
import getpass
import os


def _set_env(var: str):
    if not os.environ.get(var):
        os.environ[var] = getpass.getpass(f"{var}: ")


_set_env("OPENAI_API_KEY")
_set_env("TAVILY_API_KEY")

2-2. 검색 호출

from tavily import TavilyClient

tavily_client = TavilyClient()
response = tavily_client.search("What is AI Agent?", max_results=3)
print(response["results"])
  • max_results: 결과 최대 개수

“검색 결과를 한 덩어리 컨텍스트로 뽑기”도 가능하다.

context = tavily_client.get_search_context(query="What is AI Agent?")
print(context)

짧은 Q&A 스타일로 답만 받는 방식도 있다.

answer = tavily_client.qna_search(query="What is AI Agent?")
print(answer)

3. LangChain Tool로 TavilySearch 붙이기

LangChain Tool로 붙이면, LLM이 “도구 호출” 형태로 Tavily를 사용할 수 있다.

!pip install langchain_tavily
from langchain_tavily import TavilySearch

tool = TavilySearch(max_results=3)
tool.invoke("What's a 'node' in LangGraph?")

.invoke()는 문자열만 받을 수도 있고, Tool Call 이벤트처럼 딕셔너리 형태로 받을 수도 있다.

invoke_with_toolcall = tool.invoke(
    {
        "args": {"query": "What's a 'node' in LangGraph?"},
        "type": "tool_call",
        "id": "foo",
        "name": "tavily_search",
    }
)
print(invoke_with_toolcall.content)

4. 도구 바인딩: LLM이 tools를 “호출”할 수 있게 만들기

도구 바인딩은 말 그대로 “LLM에게 쓸 수 있는 도구 목록을 쥐여주는 단계”다.

from langchain_core.tools import tool


@tool
def add(a: int, b: int) -> int:
    return a + b


@tool
def multiply(a: int, b: int) -> int:
    return a * b
!pip install langchain_openai
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(model="gpt-5-nano")
llm_with_tools = llm.bind_tools([add, multiply])

resp = llm_with_tools.invoke("What is 3 * 12? Also, what is 11 + 49?")
print(resp.tool_calls)

여기서 포인트는:

  • LLM이 답변을 “그냥 텍스트로” 할 수도 있고
  • tool_calls를 만들어서 “도구를 실행해줘”라고 요청할 수도 있다는 점이다

5. ToolNode: tool_calls를 실제 실행으로 연결하는 브리지

LLM이 tool_calls를 만들었다고 해서 도구가 자동 실행되는 건 아니다.
그 “실행”을 담당하는 게 ToolNode다.

흐름은 보통 이렇게 간다.

LLM 노드 (tool_calls 생성)
  → ToolNode (도구 실행)
  → LLM 노드 (도구 결과를 보고 다음 행동 결정)
  → (필요하면 반복)

원문에서는 ToolNode를 직접 흉내 낸 BasicToolNode 예시도 있다. 핵심은:

  • 마지막 메시지의 tool_calls를 순회하고
  • tool name으로 실제 도구를 찾아 invoke 한 뒤
  • ToolMessage로 결과를 messages에 다시 넣는다는 점이다

6. (빠른 구성) create_react_agent로 ReAct 에이전트 만들기

create_react_agent는 ReAct 패턴(Reason + Act)에 맞춰 에이전트를 빠르게 구성하는 팩토리다.
LLM + tools만 넣으면 “생각 → 도구 호출 → 관찰 → 답변” 루프를 기본 형태로 만들어준다.

from langgraph.prebuilt import create_react_agent
from langchain_openai import ChatOpenAI
from langchain_tavily import TavilySearch

tool = TavilySearch(max_results=2)
llm = ChatOpenAI(model="gpt-5-nano")

agent = create_react_agent(llm, tools=[tool])
response = agent.invoke({"messages": [{"role": "user", "content": "What is LangGraph?"}]})
print(response)

마치며

  • Tool Calling Agent는 “대화형 모델 + 도구 실행”을 결합해, 챗봇을 실제 업무 시스템으로 확장하는 기본 패턴이다.
  • Tavily 같은 검색 도구를 붙이면 “최신 정보”가 필요한 질문에서 품질이 확 달라진다.
  • LangGraph에서는 ToolNode가 LLM의 tool_calls를 실제 실행으로 연결해주는 핵심 브리지다.
  • 빠르게 시작하려면 create_react_agent로 기본 루프를 만들고, 이후 State/조건 분기/재시도 등을 붙여서 점점 에이전틱 워크플로우로 키우는 방식이 좋다.

참고