bind_tools

coticoger·2026년 4월 23일

Agent

목록 보기
4/5

LagnGraph에서 bind_tools와 tool_calls는 어떻게 동작할까?

LangCahin이나 LangGraph로 에이전트를 구현하다 보면 다음과 같은 코드 구조를 자주 본다.

model_with_tools = llm.bind_tools([exa_search])
response = await model_with_tools.ainvoke(messages)

그리고 이후 다음과 같은 조건문이 등장한다

if response.tool_calls:

여기서 다음과 같은 질문이 생긴다

  • tool_calls는 어디서 생성되는가?
  • LLM이 실제로 tool을 실행하는가?
  • LangGraph에서는 어떻게 다음 노드를 결정하는가?

전체 실행 흐름 요약

messages 입력
   ↓
LLM 실행
   ↓
LLM이 tool 필요 여부 판단
   ├─ 필요 없음 → 일반 응답 반환
   └─ 필요 있음 → tool_calls 포함 응답 반환
   ↓
Python 코드가 tool_calls 확인
   ↓
tool 실행
   ↓
다음 노드 이동

중요한 점은 다음과 같다
LLM은 tool을 실행하지 않는다. tool을 사용 여부를 계획(tool_calls)만 생성한다.

bind_tools의 역할

model_with_tools = llm.bind_tools([exa_search])

이 코드의 의미는 LLM에게 사용할 수 있는 tool 목록을 알려준다

tool_calls는 언제 생성?

response = await model_with_tools.invoke(messages)

입력(messages)를 받은 LLM이 내부적으로 판단해서 생성한다.

가능한 결과는 두 가지이다

  1. tool이 필요 없는 경우
messages = [
	SystemMessage(content = "You are a researcher"),
    HumanMessage(content = "안녕")
    ]
    
response = await llm.ainvoke(messages)

실제 결과는 다음과 같다
content = "안녕하세요! 어떻게 도와드릴까요?"
resonse.tool_calls는 []가 나타난다

  1. tool이 필요한 경우
messages = [
	SystemMessage(content = "You are a researcher"),
    HumanMessage(content = "LangGraph agent architecture 검색해줘")
    ]
    
response = await llm.ainvoke(messages)

content = ""
response.tool_calls는 [{'name': 'exa_search', 'args': {'query': 'LangGraph agent architecture'}, 'id': 'id', 'type': 'tool_call'}] 같이 나타났다

실제 response에 포함되는 정보는 tool_calls, response_metadata, usage_metadata로 구성되어 있다.

tool_calls 구조

필드예시의미사용 목적
name"exa_search"호출할 tool 이름tool 선택
args{"query": "LangGraph"}tool 입력 파라미터tool 실행
id"call_xxx"tool 호출 식별자응답 매칭

response_metadata 구조

필드예시의미중요도
model_name"gpt-4o-mini"사용된 모델 이름중간
finish_reason"stop"응답 종료 이유높음
token_usage{...}토큰 사용 통계중간
system_fingerprint"fp_xxx"서버 식별값낮음
service_tier"default"서비스 레벨낮음

usage_metadata 구조

필드예시의미활용
input_tokens49입력 토큰 수비용 분석
output_tokens11출력 토큰 수비용 분석
total_tokens60총 토큰 수비용 계산

bind_tools와 tools = []의 차이

겉으로는 동일하지만 내부에서 누가 tool을 실행하느냐가 다르다

bind_tools

tool_llm = llm.bind_tools([tool1])
response = tool_llm.invoke(messages)

create agent

research_agent = create_agent(
	llm,
    tools = [tool1, tool2],
    system_prompt = prompt
   )

예시를 통해서 차이를 봐보자

"LangGraph 구조 검색해줘"라는 동일한 질문을 각각 넣었다고 가정하자

  1. bind_tools 방식
AIMessage(
    content="검색하겠습니다",
    tool_calls=[{
        "name": "search_web",
        "args": {"query": "LangGraph 구조"}
    }]
)

다음과 같은 response를 받는다. 여기서 중요한 점은 검색이 아직 실행되지 않았다는 것이다.
즉 search_web 실행이 필요하다는 계획만 생성된 상태이다.

그래서 아래와 같은 코드를 통해서 직접 실행을 해줘야한다

for tc in response.tool_calls:
	result = search_web.invoke(tc['args'])
  1. create agent 방식
agent = create_agent(llm, tools = [search_web])
result = agent.invoke(messages)

이 경우 내부에서 자동으로 다음과 같이 실행된다.

LLM 실행
→ tool_calls 생성
→ search_web 실행
→ 결과 다시 LLM 전달
→ 최종 응답 생성

그래서 반환값은 이미 "LangGraph 구조는 다음과 같습니다..." 처럼 검색 완료된 상태이다.

왜 LangGraph에서는 bind_tools를 쓸까?

LangGraph 구조를 보면 다음과 같다

researcher node
 ↓
tool 실행 필요?
 ↓ yes
tools node 이동
 ↓
다시 researcher node

즉, 노드 이동을 직접 제어해야 한다. 하지만 create_agent를 쓰면 제어가 어려워지게 된다.

항목bind_toolscreate_agent
tool 실행 여부 판단LLMLLM
tool 실행개발자LangChain
loop 관리개발자LangChain
LangGraph 사용 적합성매우 높음낮음
코드 제어권높음낮음

0개의 댓글