-
LangChain @tool 데코레이터의 3요소 — 에이전트가 도구를 이해하는 방법IT 2026. 6. 22. 23:00
LangChain 에이전트에게 도구를 건네줄 때 "함수만 넘기면 되겠지"라고 생각하기 쉽다. 실제로 함수 하나가 에이전트에게 제대로 인식되려면 세 가지 요소가 맞물려야 한다. 하나라도 빠지면 에이전트는 그 도구를 아예 사용하지 않거나, 엉뚱한 인자를 집어넣는다. 이 글은 그 세 요소가 무엇이고 각각이 왜 필요한지를 구체적인 실패 사례와 함께 설명한다.
에이전트가 도구를 고르는 방법
에이전트는 도구 목록을 텍스트 설명으로 전달받는다. LLM 입장에서 "도구를 안다"는 것은 그 설명을 읽을 수 있다는 뜻이다. 함수 로직을 직접 보는 게 아니라, 메타데이터 — 이름, 파라미터 타입, 용도 설명 — 를 보고 판단한다.
위 흐름에서 핵심은 LLM이 도구 목록의 description을 읽는 단계다. LLM이 description을 읽어 도구가 "어떤 상황에서 쓰이는지" 판단하고, 인자를 생성하는 단계에서 파라미터 타입 정보를 보고 올바른 형식으로 값을 채운다. description이 모호하면 "적합한 도구가 있는가?" 분기에서 "없음"으로 빠질 수 있고, 타입 힌트가 없으면 인자 생성 단계에서 문자열과 정수를 혼동한다.
3요소: @tool / 타입 힌트 / docstring
LangChain이 도구 메타데이터를 구성할 때 사용하는 세 가지 출처를 순서대로 살펴본다.
1.
@tool데코레이터 — 등록 게이트데코레이터 없이 평범한 함수를
tools=[]에 넣으면 LangChain이 이를 인식하지 못한다.@tool은 함수를BaseTool인스턴스로 변환하는 등록 게이트다. 이 과정에서 함수 이름이 도구 이름이 되고, 이후 LLM이 이 이름을 호출 대상으로 사용한다.from langchain_core.tools import tool # @tool 없이 넘기면 에이전트가 인식 못 함 # def multiply_numbers(a: int, b: int) -> int: ... # 일반 함수 @tool def multiply_numbers(a: int, b: int) -> int: """두 수를 곱해 반환한다. 곱셈 계산이 필요할 때 사용한다.""" return a * b2. 타입 힌트 — 인자 스키마 생성기
LangChain은 타입 힌트를 파싱해 JSON Schema를 만들고, 이 스키마를 LLM에게 넘긴다. LLM은 스키마를 보고 어떤 필드에 어떤 타입을 채워야 하는지 판단한다. 타입 힌트가 없으면 스키마가 비어 인자 검증이 불가능해지고, LLM이 임의의 키 이름으로 인자를 만들어낼 수 있다.
@tool def get_weather_for_location(city: str, country_code: str) -> str: """특정 도시의 현재 날씨를 반환한다. city: 도시 이름 (예: Seoul, Tokyo) country_code: ISO 2자리 국가 코드 (예: KR, JP) """ # 타입 힌트 덕분에 LLM은 city=str, country_code=str 임을 안다 return f"{city}, {country_code}의 날씨: 맑음 25도"3. docstring — LLM이 읽는 설명서
docstring은 에이전트가 언제 이 도구를 써야 하는지 판단하는 핵심 근거다. "이 함수가 무엇을 하는가"보다 "어떤 상황에서 이 도구를 선택해야 하는가"를 중심으로 써야 한다. docstring이 없으면 description이 빈 문자열이 되어 에이전트가 도구를 선택할 근거를 잃는다.
중요한 점은, docstring은 단순한 코드 설명이 아니라 에이전트(LLM)가 도구 선택을 판단하는 프롬프트라는 것이다. LangChain은
@tool로 등록된 함수의 docstring을 그대로description필드에 담아 LLM에게 전달한다. 즉, 도구를 선택하는 순간 LLM이 읽는 텍스트가 바로 docstring이다. 개발자가 "날씨를 반환한다"고 짧게 쓰면, LLM은 "내 질문이 날씨와 관련이 있는가?"를 스스로 판단해야 한다. 하지만 "날씨, 기온, 강수 여부를 묻는 질문에 사용한다"처럼 발동 조건을 명시하면 LLM이 훨씬 정확하게 판단한다.위 흐름에서 핵심은 LLM이 사용자 메시지와 도구의 description을 비교하는 단계다. LLM은 사용자 메시지 전체와 도구의 description을 비교해 일치 여부를 판단한다. description이 짧고 모호하면 발동 조건 일치 여부를 판단하는 단계에서 일치를 놓칠 확률이 높아진다. 특히 도메인 특화 도구일수록 — 게임, 인사, 커머스 등 — 발동 조건을 구체적으로 기술하는 것이 중요하다.
# 나쁜 예: 구현 설명에 집중, 발동 조건 없음 @tool def get_weather_for_location_bad(city: str, country_code: str) -> str: """날씨 조회""" # LLM이 "언제 써야 하나?"를 판단할 근거가 없음 return f"{city}, {country_code}의 날씨 정보" # 좋은 예: 발동 조건과 예시 질문을 명시 @tool def get_weather_for_location(city: str, country_code: str) -> str: """특정 도시의 현재 날씨를 반환한다. 날씨, 기온, 강수 여부를 묻는 질문에 사용한다. 예: '서울 날씨 어때?', '도쿄 오늘 비 오나?'""" return f"{city}, {country_code}의 날씨 정보" # 또 다른 나쁜 예: 구현 설명에 집중 @tool def multiply_numbers_bad(a: int, b: int) -> int: """a와 b를 곱한다.""" # 에이전트에게 "언제 쓰나?"를 알려주지 않음 return a * b # 좋은 예: 선택 기준을 명시 @tool def multiply_numbers(a: int, b: int) -> int: """두 수를 곱해 반환한다. 곱셈 계산이 필요할 때 사용한다.""" return a * bget_weather_for_location_bad의"""날씨 조회"""는 개발자에게는 충분한 설명이지만, LLM에게는 발동 조건이 없다. 사용자가 "내일 도쿄에 우산이 필요할까?"라고 물으면 이 docstring만 보고는 연관성을 판단하기 어렵다.get_weather_for_location처럼 "날씨, 기온, 강수 여부"라는 연관 키워드와 예시 질문을 함께 써주면 LLM이 훨씬 넓은 범위의 질문에서 이 도구를 올바르게 선택한다.각 요소가 빠졌을 때 무슨 일이 생기나
❌ @tool 데코레이터 누락
❌ 타입 힌트 누락
❌ docstring 누락
세 다이어그램을 나눠 그린 이유는 각 실패가 발생하는 시점이 다르기 때문이다. 데코레이터 누락은 에이전트 초기화 시점에서 터지고, 타입 힌트 누락은 도구 호출 실행 시점에서 터진다. docstring 누락은 에러 없이 조용히 실패하는 가장 찾기 어려운 케이스다 — 도구가 목록에는 있지만 에이전트가 선택하지 않아서 사용자 입장에서는 "도구가 동작을 안 한다"처럼 보인다.
세 요소가 모두 갖춰진 완성 예제
from langchain_core.tools import tool @tool # ① 등록 게이트 def multiply_numbers(a: int, b: int) -> int: # ② 타입 힌트 """두 수를 곱해 반환한다. 곱셈 계산이 필요할 때 사용한다.""" # ③ docstring return a * b @tool def get_weather_for_location(city: str, country_code: str) -> str: """특정 도시의 현재 날씨를 반환한다. 도시명과 ISO 국가 코드를 받아 날씨 정보를 반환한다. city: 도시 이름 (예: Seoul, Tokyo) country_code: ISO 2자리 국가 코드 (예: KR, JP) """ return f"{city}, {country_code}의 날씨 정보" # 에이전트에 등록 tools = [multiply_numbers, get_weather_for_location]세 요소 중 하나라도 빠지면 에이전트는 다른 두 요소를 알고 있더라도 도구를 올바르게 활용하지 못한다. 특히 docstring은 코드 품질 도구(린터, 타입 체커)가 잡아주지 않아 누락되기 쉽다. 도구를 작성할 때는 "에이전트가 이 설명만 보고 언제 이 도구를 선택해야 하는지 판단할 수 있는가"를 스스로 점검하는 습관이 필요하다.
정리
@tool데코레이터는 함수가 에이전트에게 보이는 문을 여는 역할을 한다. 타입 힌트는 에이전트가 올바른 형식으로 인자를 만들 수 있게 해주는 설계도다. docstring은 에이전트가 도구를 선택하는 근거가 되는 설명서다. 세 요소는 독립적으로 동작하는 것처럼 보이지만, 실제로는 서로가 서로를 완성한다. LangChain 도구를 처음 작성할 때 이 세 가지를 체크리스트처럼 확인하면 디버깅에 소모하는 시간을 크게 줄일 수 있다.
이 글은 생성형 AI의 도움을 받아 작성되었습니다. 원본 자료를 기반으로 AI가 초안을 생성하고, 작성자가 검토·편집하였습니다.
'IT' 카테고리의 다른 글
Retriever를 에이전트 도구로 — RAG 패턴 구현 (0) 2026.06.24 LLM에 코드 실행 능력 붙이기 — PythonAstREPLTool (0) 2026.06.24 Pydantic Field로 LLM 출력 스키마를 제약하는 방법 (0) 2026.06.23 ProviderStrategy vs ToolStrategy — 구조화 출력 전략 선택 (0) 2026.06.23 LangChain ToolRuntime으로 런타임 컨텍스트 주입하기 (0) 2026.06.23 LangChain이 LLM을 다루는 방식: 추상화, 팩토리, 체이닝 (0) 2026.06.22 LangChain이 등장한 이유 — LLM 시대의 새로운 개발 패러다임 (0) 2026.06.22 JSON-RPC의 id는 누가 정하고 충돌하면 어떻게 되나 (0) 2026.06.21 서브에이전트를 200% 활용하는 노하우 — description부터 병렬 실행까지 (0) 2026.06.20 플러그인으로 서브에이전트 배포하기 — /plugin install부터 마켓플레이스까지 (0) 2026.06.19