5장. LLM 외부 도구 연동
@tool 로 도구 만들기
회사 내선 전화번호부를 떠올려 보세요. 부서마다 이름(요금계산팀), 하는 일(상품 요금을 계산해 드립니다), 전화할 때 준비할 것(상품코드, 인원)이 적혀 있습니다. 처음 온 직원은 이 세 줄만 보고 어느 부서에 전화할지 고릅니다. 모델에게 도구를 알려 줄 때도 이 세 가지가 전부입니다. @tool 은 파이썬 함수를 전화번호부의 한 칸으로 바꿔 줍니다.
세 상자가 도구 하나를 이루는 세 부분입니다. 파랑 상자의 이름은 함수 이름에서, 보라 상자의 설명은 docstring 에서, 하늘 상자의 인자는 타입 힌트(code: str)에서 저절로 만들어집니다. 여기서 부서 이름이 name, 하는 일이 description, 준비할 것이 args 입니다. 비유와 다른 점은, 직원은 전화해서 되물어 볼 수 있지만 모델은 이 세 줄만 보고 한 번에 인자를 채워야 한다는 것입니다. 그래서 설명을 꼼꼼히 적어야 합니다.
도구를 이루는 것
| 부분 | 만드는 곳 | 모델이 쓰는 법 | 잘 쓰는 법 |
|---|---|---|---|
이름 name | 함수 이름 | 부를 도구를 이름으로 가리킨다 | 하는 일이 보이는 영어 이름(calc_price) |
설명 description | docstring | 언제 이 도구를 쓸지 고른다 | 무엇을 하는지, 단위, 규칙을 한두 문장으로 |
인자 args | 타입 힌트와 기본값 | 인자 사전을 채운다 | 자료형을 꼭 적고, 없어도 되는 인자는 기본값 |
| 인자 설명 | docstring 의 Args: | 인자마다 무엇을 넣을지 안다 | parse_docstring=True 로 읽게 한다 |
짧은 예시
요금 계산 도구를 두 가지로 만들어 봅니다. 하나는 docstring 한 줄만, 다른 하나는 인자 설명(Args:)까지 적었습니다.
from langchain_core.tools import tool
@tool
def calc_price(code: str, adults: int, children: int = 0) -> str:
"""상품코드와 인원으로 총 여행 요금(만 원)을 계산한다."""
return "계산 결과"
# parse_docstring=True: docstring 의 Args: 아래 줄을 인자마다의 설명으로 읽는다
@tool(parse_docstring=True)
def calc_price_v2(code: str, adults: int, children: int = 0) -> str:
"""상품코드와 인원으로 총 여행 요금(만 원)을 계산한다.
Args:
code: 상품코드. 예: P01
adults: 어른 수
children: 아동(만 2~12세 미만) 수. 아동 요금은 어른의 80% 이다.
"""
return "계산 결과"
print("이름:", calc_price.name)
print("설명:", calc_price.description)
print("인자:", calc_price.args)
print("v2 의 children 설명:", calc_price_v2.args["children"]["description"])이름: calc_price
설명: 상품코드와 인원으로 총 여행 요금(만 원)을 계산한다.
인자: {'code': {'title': 'Code', 'type': 'string'}, 'adults': {'title': 'Adults', 'type': 'integer'}, 'children': {'default': 0, 'title': 'Children', 'type': 'integer'}}
v2 의 children 설명: 아동(만 2~12세 미만) 수. 아동 요금은 어른의 80% 이다.함수 이름 calc_price 가 도구 이름이 되고, docstring 이 설명이 됐습니다. 인자는 타입 힌트대로 string, integer 가 되고, 기본값을 준 children 에는 default: 0 이 붙었습니다. 둘째 도구는 Args: 아래의 글을 인자마다의 description 으로 읽어서, 모델이 "아동은 몇 살까지인지, 요금은 얼마인지"까지 압니다.
코드가 의미하는 것
이 코드는 내선 전화번호부에 같은 부서를 두 번 올려 보는 것입니다. 한 번은 하는 일만, 한 번은 준비할 것의 설명까지 적고, 전화번호부에 실린 칸을 읽어 봅니다.
| 코드 | 하는 일 | 챗봇에서 뜻하는 것 |
|---|---|---|
@tool | 함수를 LangChain 도구로 바꾼다 | 함수 이름, docstring, 타입 힌트가 전화번호부의 세 칸(이름, 하는 일, 준비할 것)이 됩니다 |
@tool(parse_docstring=True) | docstring 의 Args: 를 인자마다의 설명으로 읽는다 | 모델이 인자를 채울 때 필요한 규칙("아동은 몇 살까지인지")을 함께 받습니다 |
calc_price.name, .description, .args | 도구의 이름, 설명, 인자 스키마를 꺼낸다 | 모델에게 실제로 건네지는 내용입니다. 함수 안의 return "계산 결과" 는 여기에 나오지 않습니다 |
children: int = 0 | 기본값이 있는 인자를 둔다 | 손님이 아이 수를 말하지 않으면 모델이 이 인자를 비워도 된다는 뜻이 됩니다 |
결과에서 읽을 것은 두 가지입니다.
- 설명서는 함수의 겉에서 나옵니다.
name은calc_price, 설명은 docstring 첫 줄,args는string·integer로 바뀐 타입 힌트입니다. 두 함수의 몸통은 "계산 결과"만 돌려주는 빈 껍데기인데도 설명서는 다 만들어졌습니다. - 인자 설명은 따로 적어야 생깁니다. 첫 도구의
args에는title과type뿐이고, 둘째 도구에서만children에 "아동(만 2~12세 미만) 수. 아동 요금은 어른의 80% 이다."가 붙었습니다.
주의할 점
- docstring 을 빼면
@tool이 오류를 냅니다. 설명은 모델이 도구를 고르는 유일한 단서이기 때문입니다. - 도구는 글(str)을 돌려주게 만드는 것이 편합니다. 결과는 결국 글로 바뀌어 모델에게 가므로, 단위(만 원)까지 붙인 문장으로 돌려주면 모델이 덜 헷갈립니다.
- 타입 힌트를 빼면 모델이 인자의 자료형을 모릅니다. 숫자 인자에
"둘"같은 글이 들어오는 일을 줄이려면int,str을 꼭 적습니다.