@tool — 함수를 도구로 등록하는 데코레이터
한 줄 요약
함수 위에 @tool 한 줄을 얹으면 LangChain 도구가 됩니다. 3장에서 배운 원칙(독스트링과 타입힌트가 설명서)은 그대로 적용됩니다.
1. 쓰는 법
아래는 6절에서 만들 code/ch05_csv_agent.py 의 일부를 미리 보는 것입니다 — 지금 붙여 넣지 않아도 됩니다.
from langchain_core.tools import tool
@tool
def run_pandas(expr: str) -> str:
"""승승장구몰 데이터를 pandas 식 한 줄로 계산한다."""
...
return str(result)
함수 위에 @tool 한 줄. 그게 전부입니다.
3장에서는 함수를 그대로 tools=[...] 에 넣었습니다. LangChain에서는 @tool 로 한 번 감쌉니다.
| 구글 SDK (3~4장) | LangChain (5장~) | |
|---|---|---|
| 등록 | tools=[get_order_status] |
@tool 데코레이터 |
| 넘기기 | config 의 tools= |
create_agent(llm, tools=[...]) |
| 설명서 | 독스트링 + 타입힌트 | 똑같음 |
2. 데코레이터가 뭔가요
@무언가 는 그 아래 함수를 다른 것으로 감싸는 파이썬 문법입니다.
아래는 6절에서 만들 code/ch05_csv_agent.py 의 일부를 미리 보는 것입니다 — 지금 붙여 넣지 않아도 됩니다.
@tool
def run_pandas(expr: str) -> str:
...
이건 사실 이것과 같습니다.
아래는 6절에서 만들 code/ch05_csv_agent.py 의 일부를 미리 보는 것입니다 — 지금 붙여 넣지 않아도 됩니다.
def run_pandas(expr: str) -> str:
...
run_pandas = tool(run_pandas) # 함수를 tool() 에 넣고, 결과를 같은 이름에 다시 담는다
tool() 이 함수를 받아 "도구 객체"로 바꿔 돌려줍니다. 그래서 run_pandas 는 더 이상 평범한 함수가 아닙니다.
아래는 개념 설명용 코드입니다 — 실습 파일에 넣지 않습니다.
>>> type(run_pandas)
<class 'langchain_core.tools.structured.StructuredTool'>
8장에서 이 사실을 써먹습니다. 4장에서 만든
search_web은 평범한 함수인데, 8장에서는 LangChain 도구로 필요합니다. 그때 이렇게 합니다.
python search_tool = tool(search_web) # 데코레이터 없이도 감쌀 수 있다
3. 부르는 법이 달라집니다
도구 객체가 됐으므로 평범한 함수처럼 부를 수 없습니다.
아래는 개념 설명용 코드입니다 — 실습 파일에 넣지 않습니다.
run_pandas("len(orders)") # ✗ 안 됨
run_pandas.invoke({"expr": "len(orders)"}) # ✓ 이렇게
invoke 에 딕셔너리로 인자를 넘깁니다. 앞 절에서 본 그 invoke 입니다 — LangChain의 공통 이름이죠.
실습 코드의 [1] 부분이 이 형태입니다.
아래는 6절에서 만들 code/ch05_csv_agent.py 의 일부를 미리 보는 것입니다 — 지금 붙여 넣지 않아도 됩니다.
print(" ", run_pandas.invoke({"expr": "len(orders)"}))
2400
조금 번거롭습니다. 그래도 이렇게 하는 이유는, 도구를 일관된 방법으로 다루기 위해서입니다.
llm.invoke(...),tool.invoke(...),agent.invoke(...)— 전부 같은 이름으로 부릅니다. 7장의 그래프도invoke로 부릅니다.
4. 설명서 원칙은 그대로입니다
3장 3절에서 배운 것이 한 글자도 안 바뀝니다.
아래는 6절에서 만들 code/ch05_csv_agent.py 의 일부를 미리 보는 것입니다 — 지금 붙여 넣지 않아도 됩니다.
@tool
def run_pandas(expr: str) -> str:
"""승승장구몰 데이터를 pandas 식 한 줄로 계산한다.
쓸 수 있는 표:
orders : 주문 2,400건. 열 = order_id, order_date, month('YYYY-MM'),
customer_id, product_id, product_name, category,
quantity, unit_price, amount,
status('배송완료'/'배송중'/'결제완료'/'취소'/'환불')
inventory : 재고. 열 = product_id, product_name, stock, warehouse, reorder_level
reviews : 리뷰 650건. 열 = product_id, product_name, rating(1~5), review_text
products : 상품 40개. 열 = product_id, product_name, category, price, rating
pd : pandas
규칙 1: 매출을 집계할 때는 status 가 '취소'·'환불'인 행을 반드시 제외한다.
규칙 2: 평점을 물으면 reviews 의 rating 을 집계한다.
products 의 rating 은 카탈로그 표시값이라 리뷰 실제 평균과 다르다.
예: "orders[~orders['status'].isin(['취소','환불'])].groupby('category')['amount'].sum().nlargest(3)"
"""
이 장에서 가장 긴 독스트링입니다. 왜 이렇게 길까요?
모델이 pandas 식을 만들려면 알아야 하는 것
① 어떤 표가 있는가 → orders, inventory, reviews, products
② 각 표에 어떤 열이 있는가 → order_id, amount, status ...
③ 값이 어떻게 생겼는가 → status 는 '배송완료'/'취소'/'환불' ...
④ 지켜야 할 규칙 → 매출 집계에서 취소·환불 제외
→ 평점은 reviews 의 rating 으로
⑤ 어떻게 쓰는지 예시 → groupby(...).sum().nlargest(3)
이걸 안 알려 주면 모델은 식을 만들 수 없습니다. 열 이름을 모르는데 groupby('category') 를 쓸 수는 없으니까요.
3장의
get_order_status는 독스트링이 한 줄이었습니다. 인자가 주문번호 하나뿐이라 설명할 게 없었죠. 여기는 인자가 파이썬 식 전체라서, 그 식을 만드는 데 필요한 모든 정보를 줘야 합니다.
④ 규칙이 특히 중요합니다
규칙이 둘 있습니다.
규칙 1: 매출을 집계할 때는 status 가 '취소'·'환불'인 행을 반드시 제외한다.
데이터를 아는 사람만 아는 규칙입니다. 모델은 orders.csv 를 본 적이 없으니 취소 건이 섞여 있다는 걸 모릅니다.
이걸 안 적으면 매출이 실제보다 많게 나옵니다. 그런데 답은 그럴듯하게 나오죠. 틀린 줄도 모르고 쓰게 됩니다.
규칙 2: 평점을 물으면 reviews 의 rating 을 집계한다.
products 의 rating 은 카탈로그 표시값이라 리뷰 실제 평균과 다르다.
더 고약한 함정입니다. rating 이라는 같은 이름의 열이 두 표에 다 있는데 값이 다릅니다.
| 표 | rating |
무엇 |
|---|---|---|
products |
4.8 | 카탈로그에 표시하는 값 |
reviews |
3.8 | 손님들이 실제로 준 점수의 평균 |
모델은 어느 쪽이 진짜인지 알 방법이 없습니다. 이름이 같으니까요. 규칙 2를 안 적으면 4.8을 보고할 수 있고, 오류도 안 나고 답도 그럴듯합니다.
이것도 "데이터를 아는 사람만 아는 규칙" 입니다. 열 이름이 겹치는데 뜻이 다른 상황은 실제 회사 데이터에서 아주 흔합니다. 그 차이를 설명해 주는 것이 사람의 일입니다. 8장에서 이 규칙이 최종 판단에 결정적인 역할을 합니다.
1장부터 반복된 그 이야기입니다 — 모델은 모르는 것을 모른다고 하지 않습니다. 그러니 알아야 할 것을 미리 알려 주는 것이 사람의 일입니다.
5. 독스트링을 어디에 적을까
같은 규칙을 시스템 지시에 적을 수도 있습니다.
아래는 개념 설명용 코드입니다 — 실습 파일에 넣지 않습니다.
SYSTEM = ("... 매출 집계에서는 취소·환불 건을 제외한다. ...")
실습 코드에는 양쪽에 다 적어 두었습니다. 4장 4절에서 정리한 구분을 다시 떠올려 보세요.
| 어디에 | 성격 | 이 규칙은? |
|---|---|---|
| 독스트링 | 그 도구 자체의 설명. 도구를 재사용해도 따라간다 | ✓ 데이터의 성질이니 도구에 붙는 게 맞다 |
| 시스템 지시 | 이 에이전트의 업무 규칙 | ✓ 분석가로서 지킬 규칙이기도 하다 |
중요한 규칙은 두 곳에 적어 두면 한쪽이 잊혀도 다른 쪽이 잡아 줍니다.
6. 확인해 보기
@tool 이 만든 명세를 직접 볼 수 있습니다.
아래는 개념 설명용 코드입니다 — 실습 파일에 넣지 않습니다.
print(run_pandas.name)
print(run_pandas.description[:120])
print(run_pandas.args)
run_pandas
승승장구몰 데이터를 pandas 식 한 줄로 계산한다.
쓸 수 있는 표:
orders : 주문 2,400건. 열 = order_id, order_date, month('YYYY-MM'), ...
{'expr': {'title': 'Expr', 'type': 'string'}}
3장에서 본 것과 똑같은 구조입니다 — 이름, 설명, 인자와 타입.
스스로 해 보기
run_pandas.description을 전부 출력해 보세요. 모델이 읽는 내용 전체입니다.- 독스트링에서 "규칙 1: 매출을 집계할 때는..." 을 지우고
"카테고리별 매출 상위 3개는?"을 물어 보세요. 숫자가 달라지나요? (실행 전에 예측하세요) - 독스트링에서 표 목록을 지우면 어떻게 되나요?
핵심 정리
@tool데코레이터 한 줄로 함수가 LangChain 도구가 됩니다.- 도구가 되면
tool.invoke({"인자이름": 값})으로 부릅니다. LangChain의 공통 이름입니다. - 데코레이터 없이
tool(함수)로 감쌀 수도 있습니다. 8장에서 씁니다. - 독스트링과 타입힌트가 설명서라는 원칙은 3장과 똑같습니다.
- 인자가 파이썬 식 전체이므로, 표·열·값·규칙·예시를 전부 적어 줘야 합니다.
- "매출 집계에서 취소·환불 제외" 같은 규칙은 모델이 알 수 없습니다. 알려 주는 것이 사람의 일입니다.
- 중요한 규칙은 독스트링과 시스템 지시 양쪽에 적어 둡니다.