Lộ trình học›Claude API›Building with the Claude API Mở bản đồ

6.32 · Tool functions — Python code Claude sẽ gọi

Bài viếtTrung cấp15 phút đọc

Tool function là Python function bình thường — nhưng có 2 khác biệt:

Sau bài này, bạn sẽ
  1. Viết tool function theo best practices: descriptive names, validation, error messages
  2. Hiểu Claude "thấy" error → tự retry với corrected params
  3. Viết 3 function mẫu: datetime, duration, reminders
  4. Connect function với schema

Mở đầu: Function đơn giản nhưng cần robust

  1. Caller là Claude, không phải devparams có thể "kỳ quặc", need validation
  2. Error visible to ClaudeClaude có thể retry nếu error clear

Best practices

1. Descriptive names

def f(x, y): ...

def get_current_datetime(date_format): ...
def calculate_shipping_cost(weight, destination): ...

Tên function + param phải tự-documenting.

2. Validate inputs

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 logic

3. Meaningful error messages

raise Exception("error")

raise ValueError(f"Invalid date format '{fmt}'. Use Python strftime syntax (e.g., '%Y-%m-%d')")

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:

return {"temp": 72, "condition": "sunny"}

return "Current weather in SF: 72°F, sunny with 10mph wind"

3 tool functions mẫu

1. get_current_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)}")


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

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()


print(add_duration_to_datetime("2026-04-20T00:00:00", 103, "days"))

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

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.")

Claude có thể retry:

  • First call: SELECT * FROM usrs → error "table usrs not found"
  • Retry: SELECT * FROM users → works

Pattern: Side effects + confirmation

Cho tools có side effect (create, delete, send), return rõ rà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}")

Return confirmation giúp Claude biết action thành công.

Function + schema pairing


def get_weather(location: str, unit: str = "F") -> str:
    # ... implementation
    pass

get_weather_schema = {
    "name": "get_weather",
    # ... schema
}


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)

Usage:

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:

def execute_python(code: str) -> str:
    # DANGEROUS — code injection
    return str(eval(code))

Never. Dùng sandbox (Docker, subprocess với limits).

Rate limiting

Tool gọi external API → có thể abuse.

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

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.status

Pass user_id trong context từ session, không trust Claude pass.

Anti-patterns

No validation

Claude pass location="" → crash or wrong query.

Fix: Validate + raise.

Silent failure

try:
    do_something()
except:
    return ""  # Claude không biết có lỗi

Fix: Raise với message. Claude sẽ retry.

Return nothing

def save_to_db(data):
    db.save(data)
    # không return gì

Fix: Return "Saved with ID: xyz". Claude xác nhận.

Complex nested return

return {"a": {"b": {"c": {"d": "value"}}}}

Claude xử lý OK nhưng string prose thường clearer.

Fix: Return formatted string hoặc flat dict.

Á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:

  • Type hints
  • Docstring
  • Input validation
  • Error message
  • Return formatted string

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:

def my_tool(...): ...
my_tool_schema = {...}

Tóm tắt bài học

Những điều cốt lõi
  • 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.
Bạn đã hoàn thành bài này chưa?Tiến độ lưu trên trình duyệt của bạn.