Tool function là Python function bình thường — nhưng có 2 khác biệt:
- Viết tool function theo best practices: descriptive names, validation, error messages
- Hiểu Claude "thấy" error → tự retry với corrected params
- Viết 3 function mẫu: datetime, duration, reminders
- Connect function với schema
Best practices
1. Descriptive names
Tên function + param phải tự-documenting.
2. Validate inputs
# ❌
def f(x, y): ...
# ✅
def get_current_datetime(date_format): ...
def calculate_shipping_cost(weight, destination): ...2. Validate inputs
3. Meaningful error messages
def get_weather(location, unit="F"):
if not location:
raise ValueError("Location cannot be empty")
if unit not in ("F", "C"):
raise ValueError(f"Unit must be F or C, got {unit}")
# ... actual logic3. Meaningful error messages
Claude đọc error, có thể retry với correction.
4. Return strings (thường)
Claude dễ process string. Nếu function return dict/list, Claude vẫn OK nhưng string thường preferred:
# ❌
raise Exception("error")
# ✅
raise ValueError(f"Invalid date format '{fmt}'. Use Python strftime syntax (e.g., '%Y-%m-%d')")4. Return strings (thường)
# OK
return {"temp": 72, "condition": "sunny"}
# Better for Claude
return "Current weather in SF: 72°F, sunny with 10mph wind"3 tool functions mẫu
1. get_current_datetime
2. add_duration_to_datetime
from datetime import datetime
def get_current_datetime(date_format="%Y-%m-%d %H:%M:%S"):
"""Get current date/time."""
if not date_format:
raise ValueError("date_format cannot be empty")
try:
return datetime.now().strftime(date_format)
except ValueError as e:
raise ValueError(f"Invalid format string: {date_format}. {str(e)}")
# Test
print(get_current_datetime()) # "2026-04-20 14:30:25"
print(get_current_datetime("%H:%M")) # "14:30"
print(get_current_datetime("%A")) # "Monday"2. add_duration_to_datetime
3. set_reminder
from datetime import datetime, timedelta
def add_duration_to_datetime(
datetime_str: str,
duration: int,
unit: str = "days"
) -> str:
"""Add duration to a datetime."""
if unit not in ("seconds", "minutes", "hours", "days", "weeks"):
raise ValueError(f"Unit must be seconds/minutes/hours/days/weeks, got {unit}")
try:
dt = datetime.fromisoformat(datetime_str)
except ValueError:
raise ValueError(f"Invalid datetime: '{datetime_str}'. Expected ISO format like '2026-04-20T14:30:00'")
delta = timedelta(**{unit: duration})
result = dt + delta
return result.isoformat()
# Test
print(add_duration_to_datetime("2026-04-20T00:00:00", 103, "days"))
# "2026-08-01T00:00:00"3. set_reminder
def set_reminder(
reminder_text: str,
remind_at_iso: str
) -> str:
"""Set a reminder. Returns confirmation."""
if not reminder_text.strip():
raise ValueError("Reminder text cannot be empty")
try:
remind_at = datetime.fromisoformat(remind_at_iso)
except ValueError:
raise ValueError(f"Invalid datetime format: {remind_at_iso}")
if remind_at < datetime.now():
raise ValueError("Reminder time must be in the future")
# Store in DB (simplified)
reminder_id = f"rem_{int(datetime.now().timestamp())}"
# save to db...
return f"Reminder set: '{reminder_text}' at {remind_at.strftime('%Y-%m-%d %H:%M')}. ID: {reminder_id}"Pattern: Try-except với informative error
Claude có thể retry:
- First call: SELECT * FROM usrs → error "table usrs not found"
- Retry: SELECT * FROM users → works
def query_database(sql: str) -> str:
if not sql.strip().lower().startswith("select"):
raise ValueError("Only SELECT queries allowed. Got: " + sql[:50])
try:
results = db.execute(sql).fetchall()
return format_results(results)
except db.OperationalError as e:
raise ValueError(f"SQL error: {str(e)}. Check syntax.")
except db.TimeoutError:
raise TimeoutError("Query too slow. Try narrower WHERE clause.")Pattern: Side effects + confirmation
Cho tools có side effect (create, delete, send), return rõ ràng:
Return confirmation giúp Claude biết action thành công.
def send_email(to: str, subject: str, body: str) -> str:
if not is_valid_email(to):
raise ValueError(f"Invalid email: {to}")
try:
email_service.send(to=to, subject=subject, body=body)
return f"Email sent successfully to {to}. Subject: '{subject}'"
except EmailServiceError as e:
raise RuntimeError(f"Email failed: {e}")Function + schema pairing
Usage:
# tools.py
def get_weather(location: str, unit: str = "F") -> str:
# ... implementation
pass
get_weather_schema = {
"name": "get_weather",
# ... schema
}
# Dispatcher
TOOLS = {
"get_weather": get_weather,
"get_current_datetime": get_current_datetime,
"add_duration_to_datetime": add_duration_to_datetime,
"set_reminder": set_reminder,
}
def run_tool(name: str, input: dict) -> str:
"""Execute tool by name with args."""
if name not in TOOLS:
raise ValueError(f"Unknown tool: {name}")
return TOOLS[name](**input)Function + schema pairing (tiếp)
tool_call = response.content[-1] # ToolUseBlock
result = run_tool(tool_call.name, tool_call.input)Security considerations
Tool có thể run code / modify data — nguy hiểm
Validate strictly:
Never. Dùng sandbox (Docker, subprocess với limits).
Rate limiting
Tool gọi external API → có thể abuse.
def execute_python(code: str) -> str:
# DANGEROUS — code injection
return str(eval(code))Rate limiting
Whitelisting
from functools import lru_cache
from time import time
@lru_cache(maxsize=100)
def _cached_weather(location: str, hour: int):
return weather_api.get(location)
def get_weather(location: str, unit: str = "F"):
# Cache per hour
hour = int(time()) // 3600
return _cached_weather(location, hour)Whitelisting
Pass user_id trong context từ session, không trust Claude pass.
def get_order_status(order_id: str, user_id: str) -> str:
order = db.get(order_id)
if order.user_id != user_id:
raise PermissionError("Order not yours")
return order.statusAnti-patterns
❌ No validation
Claude pass location="" → crash or wrong query.
Fix: Validate + raise.
❌ Silent failure
Fix: Raise với message. Claude sẽ retry.
❌ Return nothing
try:
do_something()
except:
return "" # Claude không biết có lỗi❌ Return nothing
Fix: Return "Saved with ID: xyz". Claude xác nhận.
❌ Complex nested return
def save_to_db(data):
db.save(data)
# không return gì❌ Complex nested return
Claude xử lý OK nhưng string prose thường clearer.
Fix: Return formatted string hoặc flat dict.
return {"a": {"b": {"c": {"d": "value"}}}}Áp dụng ngay
Bài tập 1: Viết 3 tool function (30 phút)
Chọn 3 tool cho app của bạn. Viết function với:
Test mỗi function với valid + invalid input.
Bài tập 2: Pair với schema (15 phút)
Viết schema cho mỗi function. Commit cả 2 cùng file tools.py:
- [ ] Type hints
- [ ] Docstring
- [ ] Input validation
- [ ] Error message
- [ ] Return formatted string
def my_tool(...): ...
my_tool_schema = {...}Tóm tắt
🎯 Tool function = Python function + robust validation.
🎯 Error message informative — Claude đọc và retry được.
🎯 Return string formatted giúp Claude process dễ hơn.
🎯 Security: validate, sandbox, rate-limit, whitelist.
🎯 Function + schema pair: my_tool + my_tool_schema.