Dechive Logo
Dechive
Dev#dechive#llm#prompt#tool-use#function-calling#ai-agent#api#ai-prompting

Tool Use & Function Calling Complete Mastery — How to Make AI Use Tools Directly

How to make AI directly call APIs and execute functions. Complete mastery from the principles of Tool Use to practical design.

Introduction: The Question Left by Episode 16

In Episode 16, while covering prompt injection defense, we left this preview:

"How can we make AI directly call APIs and execute functions?"

Until now, AI has received text and output text. No matter how good the answer it generates, it's ultimately just text.

But real-world problems can't be solved with text answers alone.

There's a difference between AI telling you the average temperature of Seoul from its training data in response to "What's the weather in Seoul right now?" and actually calling the weather service API to give you the current temperature at this very moment.

There's a difference between AI saying "Check the database" when asked "Can you check this customer's recent orders?" and actually querying the database directly and retrieving the results.

This is Tool Use, or Function Calling. It's a technology where AI doesn't just generate text, but directly calls external functions and APIs to fetch real data and perform actual tasks.

In this episode, we'll cover Tool Use from how it works, to how to define tools, to how to use them in practice—from start to finish.


1. What is Tool Use?

1.1. The Simplest Analogy

Imagine you hired a person. This person is very smart and knowledgeable. But there are two versions.

Version A: They only have books and memory. When you ask, they tell you what they know. But they can't make phone calls, search on a computer, or actually manipulate anything.

Version B: In addition to books and memory, they have a phone, computer, calculator, and database access. When needed, they directly call to verify, query the database, and use the calculator to produce accurate numbers.

AI before Tool Use is Version A. AI with Tool Use applied is Version B.

# AI without Tool Use
User: "What's the current dollar/won exchange rate?"
AI: "Based on my training data, it's around 1,300 won."
    (← Not the actual rate)

# AI with Tool Use
User: "What's the current dollar/won exchange rate?"
AI: Calls exchange_rate_api("USD", "KRW")
    → Result: 1,378.50 won
AI: "The current dollar/won exchange rate is 1,378.50 won." (← Actual rate)

1.2. How It Works

Tool Use operates in this sequence:

1. Developer tells AI about available tools
        ↓
2. User sends a question
        ↓
3. AI determines "what tools do I need to answer this?"
        ↓
4. AI generates a tool call request (it doesn't actually call it)
        ↓
5. Developer code actually calls the API/function
        ↓
6. Returns the result to AI
        ↓
7. AI generates the final answer based on the result

Important point: AI doesn't directly call APIs. AI generates a request saying "I need to call this tool with these parameters," and our code handles the actual call. This is how developers maintain security and control.


2. How to Define Tools

2.1. The 4 Elements of Tool Definition

When telling AI about tools, you must clearly specify four things:

# Tool definition based on Claude API
tool = {
    "name": "get_weather",           # 1. Tool name
    "description": "Returns the current weather of a specific city. "
                   "Always use this tool when weather-related questions come up.",  # 2. Description
    "input_schema": {                # 3. Parameter schema
        "type": "object",
        "properties": {
            "city": {
                "type": "string",
                "description": "City name to query weather. Both Korean and English are acceptable."
            },
            "unit": {
                "type": "string",
                "enum": ["celsius", "fahrenheit"],
                "description": "Temperature unit. Default is celsius."
            }
        },
        "required": ["city"]          # 4. Required parameters
    }
}

Name: Should be concise and meaningful like a function name. get_weather is better than weather, and search_product_by_name is better than search.

Description: The most important. AI uses this to decide when to use the tool. Write clearly "when to use it."

Parameters: Write type, allowed values (enum), and description carefully. If parameter descriptions are insufficient, AI will insert wrong values.

required: Specify required parameters. If not specified, AI might omit them.

2.2. Good Tool Description vs Bad Tool Description

# ❌ Bad description — unclear when to use
{
    "name": "search",
    "description": "Search.",
    "input_schema": {
        "type": "object",
        "properties": {
            "q": {"type": "string"}
        }
    }
}

# ✅ Good description — clear when and how to use
{
    "name": "web_search",
    "description": "Use this to look up latest news, real-time information, and events after the training data cutoff. "
                   "Use this tool in all situations where current information the model doesn't know is needed. "
                   "Search results are higher quality when using concise core keywords.",
    "input_schema": {
        "type": "object",
        "properties": {
            "query": {
                "type": "string",
                "description": "Search keyword or question. Keep it concise within 5 words."
            },
            "language": {
                "type": "string",
                "enum": ["ko", "en"],
                "description": "Search language. Default is ko."
            }
        },
        "required": ["query"]
    }
}

3. Practical Implementation — Based on Claude API

3.1. Basic Structure

import anthropic
import json

client = anthropic.Anthropic()

# Tool definition
tools = [
    {
        "name": "get_weather",
        "description": "Query current weather of a city. Must use this for weather-related questions.",
        "input_schema": {
            "type": "object",
            "properties": {
                "city": {
                    "type": "string",
                    "description": "City name to query weather"
                }
            },
            "required": ["city"]
        }
    }
]

# Actual function (implemented by us)
def get_weather(city: str) -> dict:
    # In reality, this would call a weather API
    return {"city": city, "temperature": 18, "condition": "clear", "humidity": 55}

# Step 1: Send question to AI + pass tool list
response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    tools=tools,
    messages=[{"role": "user", "content": "How's the weather in Seoul?"}]
)

# Step 2: Check if AI requested tool call
if response.stop_reason == "tool_use":
    tool_use_block = next(b for b in response.content if b.type == "tool_use")
    
    tool_name = tool_use_block.name          # "get_weather"
    tool_input = tool_use_block.input        # {"city": "Seoul"}
    tool_use_id = tool_use_block.id
    
    # Step 3: Actually call the function
    result = get_weather(**tool_input)
    
    # Step 4: Return result to AI and request final answer
    final_response = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=1024,
        tools=tools,
        messages=[
            {"role": "user", "content": "How's the weather in Seoul?"},
            {"role": "assistant", "content": response.content},
            {
                "role": "user",
                "content": [{
                    "type": "tool_result",
                    "tool_use_id": tool_use_id,
                    "content": json.dumps(result, ensure_ascii=False)
                }]
            }
        ]
    )
    
    print(final_response.content[0].text)
    # Output: "The current weather in Seoul is clear, with a temperature of 18 degrees and 55% humidity."

3.2. Defining Multiple Tools Together

In real services, there are multiple tools. When providing multiple tools together, AI chooses based on the situation.

tools = [
    {
        "name": "get_weather",
        "description": "Query current weather of a city.",
        "input_schema": {
            "type": "object",
            "properties": {
                "city": {"type": "string", "description": "City name"}
            },
            "required": ["city"]
        }
    },
    {
        "name": "search_restaurant",
        "description": "Search for restaurants in a specific area. Use for restaurant, dining, and cafe-related questions.",
        "input_schema": {
            "type": "object",
            "properties": {
                "location": {"type": "string", "description": "Area to search"},
                "cuisine": {"type": "string", "description": "Food type (Korean, Japanese, Western, etc.)"},
                "max_results": {"type": "integer", "description": "Maximum number of results. Default is 5."}
            },
            "required": ["location"]
        }
    },
    {
        "name": "calculate",
        "description": "Calculate a mathematical expression. Use for all situations requiring numeric calculations.",
        "input_schema": {
            "type": "object",
            "properties": {
                "expression": {"type": "string", "description": "Expression to calculate. Example: '(100 * 1.1) + 50'"}
            },
            "required": ["expression"]
        }
    }
]

# AI decides which tool to use on its own
response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    tools=tools,
    messages=[{"role": "user", "content": "Tell me 3 Japanese restaurants in Gangnam."}]
)
# AI chooses search_restaurant tool and
# inserts parameters: location="Gangnam", cuisine="Japanese", max_results=3

4. Parallel Tool Calling

When AI needs to call multiple tools simultaneously, we can process them in parallel. This is much faster than waiting for them one by one.

# Question: "Compare weather in Seoul and Busan"
# AI requests to query both cities simultaneously

response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    tools=tools,
    messages=[{"role": "user", "content": "Compare weather in Seoul and Busan"}]
)

# response.content contains two tool_use blocks
tool_uses = [b for b in response.content if b.type == "tool_use"]

# Execute both tools in parallel
import concurrent.futures

def execute_tool(tool_use_block):
    if tool_use_block.name == "get_weather":
        result = get_weather(**tool_use_block.input)
        return tool_use_block.id, result

with concurrent.futures.ThreadPoolExecutor() as executor:
    futures = [executor.submit(execute_tool, t) for t in tool_uses]
    results = [f.result() for f in concurrent.futures.as_completed(futures)]

# Return all results to AI at once
tool_results = [
    {
        "type": "tool_result",
        "tool_use_id": tool_id,
        "content": json.dumps(result, ensure_ascii=False)
    }
    for tool_id, result in results
]

final_response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    tools=tools,
    messages=[
        {"role": "user", "content": "Compare weather in Seoul and Busan"},
        {"role": "assistant", "content": response.content},
        {"role": "user", "content": tool_results}
    ]
)

5. Forcing Tool Use

By default, AI decides whether to use tools. But in certain situations, you can force it to always use a tool, or the opposite, never use it.

# Always use tools (force specific tool)
response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    tools=tools,
    tool_choice={"type": "tool", "name": "get_weather"},  # Must use this tool
    messages=[{"role": "user", "content": "How's the weather in Seoul?"}]
)

# AI decides whether to use tools (default)
response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    tools=tools,
    tool_choice={"type": "auto"},  # Default
    messages=[...]
)

# Disable tool use (text answer only)
response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    tools=tools,
    tool_choice={"type": "none"},  # Disable tool use
    messages=[...]
)

6. Real-World Use Cases

6.1. Customer Service Chatbot

tools = [
    {
        "name": "get_order_status",
        "description": "Query order status by order number.",
        "input_schema": {
            "type": "object",
            "properties": {
                "order_id": {"type": "string", "description": "Order number (e.g., ORD-20240424-001)"}
            },
            "required": ["order_id"]
        }
    },
    {
        "name": "get_product_info",
        "description": "Query product information by product ID or name.",
        "input_schema": {
            "type": "object",
            "properties": {
                "product_id": {"type": "string", "description": "Product ID"},
                "product_name": {"type": "string", "description": "Product name"}
            }
        }
    },
    {
        "name": "create_refund_request",
        "description": "Create a refund request. Only use when the user requests a refund and conditions are verified.",
        "input_schema": {
            "type": "object",
            "properties": {
                "order_id": {"type": "string"},
                "reason": {"type": "string", "description": "Refund reason"}
            },
            "required": ["order_id", "reason"]
        }
    }
]

# User: "Where's my order ORD-20240424-001 right now?"
# AI: Calls get_order_status(order_id="ORD-20240424-001")
# Result: {"status": "in transit", "location": "Suwon distribution center", "expected": "tomorrow afternoon"}
# AI: "Your order is currently in transit at the Suwon distribution center. Expected delivery is tomorrow afternoon."

6.2. Data Analysis AI

tools = [
    {
        "name": "run_sql",
        "description": "Execute SQL query and return results. Use when data retrieval is needed.",
        "input_schema": {
            "type": "object",
            "properties": {
                "query": {
                    "type": "string",
                    "description": "SELECT SQL query to execute. INSERT/UPDATE/DELETE are not allowed."
                }
            },
            "required": ["query"]
        }
    }
]

# User: "What are the top 5 products by sales last month?"
# AI: Calls run_sql("SELECT product_name, SUM(amount) as total FROM orders WHERE ...") 
# Result: [{"product_name": "A", "total": 1500000}, ...]
# AI: Answers "The top 5 products by sales last month are A (1.5M won), B (1.3M won)..."

7. Failure Patterns

7.1. Vague Description Causes Wrong Tool Selection

# ❌ Poor descriptions for two tools
{
    "name": "search_news",
    "description": "Search."  # Too brief
},
{
    "name": "search_products",
    "description": "Search."  # Same description
}

# AI can't distinguish which tool to use
# "Find news about iPhone 16 release" → picks either one randomly

# ✅ Clear descriptions
{
    "name": "search_news",
    "description": "Search latest news articles. Use when information about news, events, or issues is needed."
},
{
    "name": "search_products",
    "description": "Search shopping mall products. Use when product information, price comparison, or purchase info is needed."
}

7.2. Failing to Validate Tool Results

# ❌ No validation, directly pass result
result = execute_tool(tool_name, tool_input)
# Even if result is an error or None, just pass it to AI

# ✅ Validate result before passing
result = execute_tool(tool_name, tool_input)

if result is None or "error" in result:
    tool_result_content = "Tool call failed. Please try again in a moment."
else:
    tool_result_content = json.dumps(result, ensure_ascii=False)

7.3. Too Many Tools

The more tools, the higher the chance AI picks wrong. Avoid more than 7 tools in a single agent.

# ❌ Too many tools (15)
tools = [search, weather, maps, news, stocks, calendar,
         email, translate, calculate, database, files,
         images, videos, music, social_media]

# ✅ Separate agents by role
# Search agent: search, news, web
# Business agent: database, calendar, email
# Utility agent: calculate, translate, weather

7.4. Infinite Loops

AI receives tool results but calls the same tool again.

# Must limit maximum call count
MAX_TOOL_CALLS = 5
tool_call_count = 0

while response.stop_reason == "tool_use":
    tool_call_count += 1
    if tool_call_count > MAX_TOOL_CALLS:
        break  # Force termination
    
    # Execute tool and request next response
    ...

Conclusion: The Moment AI Connects to the World

From Episode 1 until now, we've covered everything about prompts. Writing good instructions, structuring, designing context, building agents, securing systems.

Tool Use is the completion of all that. AI is no longer confined to a text box. It connects to real systems, fetches real data, and performs real tasks.

The core is three things. Write tool descriptions clearly so AI selects the right tool. Define parameters carefully so AI inserts the right values. Always validate results so errors don't reach users directly.

Summary of Key Principles

PrincipleCore
Description is EverythingWrite "when to use" clearly in tool description so AI chooses correctly
Meticulous ParametersFill in type, enum, description, and required so AI inserts correct values
Validation RequiredAlways write handling code for error or empty tool results
Minimize Tools7 or fewer per agent. Separate agents by role
Limit LoopsSet maximum call count to prevent infinite loops

Toward Episode 18

Now that you've mastered Tool Use, what remains is tying everything together to make AI harnessing a profession.

"How can writing good prompts become competitive advantage?"

[Episode 18: AI Harnessing as a Profession — The Future of Prompt Engineering] addresses the fundamental question that runs through this entire series.

사서Dechive 사서