Tool 구현 — 근본적인 구조
Tool의 세 가지 구성 요소
1. 인터페이스 — LLM이 이해하는 명세 (입출력 정의)
2. 실행 코드 — 실제로 작업하는 함수
3. 에러 처리 — 실패했을 때 LLM에게 뭘 전달할지
기반 클래스
# tools/base.py
class Tool:
def __init__(self, name, description, params, returns, when_to_use):
self.name = name
self.description = description
self.params = params
self.returns = returns
self.when_to_use = when_to_use
def run(self, **kwargs) -> dict:
raise NotImplementedError
def to_prompt(self) -> str:
return f"""
## Tool: {self.name}
설명: {self.description}
입력: {self.params}
출력: {self.returns}
사용 시점: {self.when_to_use}
"""
모든 Tool이 이 클래스를 상속해서 run()만 구현.
에러 처리 원칙
에러를 exception으로 터뜨리면 LLM이 뭘 해야 할지 모른다. 에러를 LLM이 읽을 수 있는 구조화된 응답으로 반환해야 한다.
# 나쁨: exception 터짐 → LLM이 할 수 있는 것 없음
# 좋음: 구조화된 에러 반환 → LLM이 재시도 or 사용자 알림 결정
return {"success": False, "error": "timeout",
"message": "다운로드 시간 초과. 나중에 다시 시도해라."}
핵심 원칙
1. 모든 Tool은 항상 dict를 반환한다
{"success": True/False, ...}
2. 에러는 exception이 아니라 응답으로
LLM이 다음 행동을 결정할 수 있게
3. Side effect는 명확하게 반환
파일 생성 → local_path 반환
세션 생성 → session_id 반환
파일 구조
tools/
├── base.py ← Tool 기반 클래스
├── file_tools.py ← download_file, convert_to_md
├── browser_tools.py ← login, navigate, search
├── db_tools.py ← query_db, insert_db
└── rag_tools.py ← index_to_rag, search_rag
tool_registry.py ← 등록 + System prompt 자동 생성
agent.py ← Tool 실행 루프
브라우저 Tool (Playwright)
class LoginTool(Tool):
def __init__(self):
super().__init__(name="login", ...)
self._sessions = {}
def run(self, url, credentials_key):
try:
creds = get_credentials(credentials_key)
pw = sync_playwright().start()
context = pw.chromium.launch().new_context()
# 로그인 처리...
session_id = generate_id()
self._sessions[session_id] = context
return {"success": True, "session_id": session_id}
except Exception as e:
return {"success": False, "error": "auth_failed",
"message": str(e)}
관련 개념
- tool-registry-architecture — Tool Registry 구조
- tool-system-prompt-design — Tool 명세 작성법
- tool-cot-execution — LLM CoT 실행 흐름