📚 Agentic AI - 기업용 자율 에이전트 개발 5장 · 에이전트 ③ CSV 데이터분석 Agentic AI란

@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 데코레이터
넘기기 configtools= 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장에서 본 것과 똑같은 구조입니다 — 이름, 설명, 인자와 타입.


스스로 해 보기

  1. run_pandas.description 을 전부 출력해 보세요. 모델이 읽는 내용 전체입니다.
  2. 독스트링에서 "규칙 1: 매출을 집계할 때는..." 을 지우고 "카테고리별 매출 상위 3개는?" 을 물어 보세요. 숫자가 달라지나요? (실행 전에 예측하세요)
  3. 독스트링에서 표 목록을 지우면 어떻게 되나요?

핵심 정리

  • @tool 데코레이터 한 줄로 함수가 LangChain 도구가 됩니다.
  • 도구가 되면 tool.invoke({"인자이름": 값}) 으로 부릅니다. LangChain의 공통 이름입니다.
  • 데코레이터 없이 tool(함수) 로 감쌀 수도 있습니다. 8장에서 씁니다.
  • 독스트링과 타입힌트가 설명서라는 원칙은 3장과 똑같습니다.
  • 인자가 파이썬 식 전체이므로, 표·열·값·규칙·예시를 전부 적어 줘야 합니다.
  • "매출 집계에서 취소·환불 제외" 같은 규칙은 모델이 알 수 없습니다. 알려 주는 것이 사람의 일입니다.
  • 중요한 규칙은 독스트링과 시스템 지시 양쪽에 적어 둡니다.
← 이전 절02. LangChain 설치와 모델 가져오기다음 절 →04. create_agent 한 줄이 대신해 주는 것