Introduction
Function Calling is the superpower that turns LLM from a text generator into an agent. Instead of just responding with text, LLM can output structured JSON to call the functions/tools you define. This article deep-dive into the function calling mechanism of all 3 main providers.
1. How does Function Calling work?
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ User │────►│ LLM │────►│ Your │────►│ LLM │────► Final
│ Prompt │ │ (Choose │ │ Code │ │ (Process │ Response
│ │ │ tools) │ │ (Execute)│ │ result) │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
Output: Output:
tool_calls[] tool results
Key Insight
LLM doesn't actually call the function. It just outputs JSON saying "I want to call function X with args Y". Your code actually calls the function and returns the results to LLM.
2. OpenAI Function Calling
2.1 Definition of Tools
tools = [
{
"type": "function",
"function": {
"name": "search_products",
"description": "Tìm kiếm sản phẩm trong database",
"parameters": {
"type": "object",
"properties": {
"query": {"type": "string", "description": "Từ khóa tìm kiếm"},
"category": {"type": "string", "enum": ["electronics", "clothing", "books"]},
"max_price": {"type": "number", "description": "Giá tối đa (VND)"},
"sort_by": {"type": "string", "enum": ["price_asc", "price_desc", "rating"]}
},
"required": ["query"]
}
}
}
]
2.2 Parallel Function Calling
When a lot of information is needed at the same time, LLM can call multiple tools in parallel:
# User: "So sánh thời tiết Hà Nội, Đà Nẵng và Sài Gòn"
# LLM sẽ output 3 tool_calls cùng lúc!
for tool_call in message.tool_calls:
# tool_call 1: get_weather("Hanoi")
# tool_call 2: get_weather("Da Nang")
# tool_call 3: get_weather("Ho Chi Minh City")
...
3. Anthropic Tool Use
Claude uses a different syntax but the same concept:
response = anthropic_client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
tools=[{
"name": "search_products",
"description": "Tìm kiếm sản phẩm",
"input_schema": {
"type": "object",
"properties": {
"query": {"type": "string"},
"category": {"type": "string"}
},
"required": ["query"]
}
}],
messages=[{"role": "user", "content": "Tìm laptop dưới 20 triệu"}]
)
4. Build Tool Registry Pattern
class ToolRegistry:
def __init__(self):
self.tools = {}
self.schemas = []
def register(self, name, description, parameters):
def decorator(func):
self.tools[name] = func
self.schemas.append({
"type": "function",
"function": {
"name": name,
"description": description,
"parameters": parameters
}
})
return func
return decorator
def execute(self, name, args):
return self.tools[name](**args)
registry = ToolRegistry()
@registry.register("get_weather", "Lấy thời tiết", {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"]
})
def get_weather(city):
return f"Thời tiết {city}: 32°C, nắng"
Summary
- Function Calling = LLM output JSON → your code executes → result back to LLM
- OpenAI:
toolsparameters +tool_callsresponse. response - Anthropic:
toolsparameters +tool_usecontent blocks - Parallel calling helps agents process faster
- Tool Registry pattern helps manage tools cleanly
Exercises
- Implement 5 tools: calculator, weather, web_search, file_read, send_email
- Build an agent with parallel function calling
- Write good tool descriptions — compare clear vs vague descriptions
- Implement tool_choice="required" vs "auto" and observe the difference