Ai Agent Function Calling
Function Calling (function calling) is the core mechanism that allows LLMs to use external tools.
Function Calling enables the model to decide when to call a tool, which tool to call, and what parameters to pass.
### Why Do We Need Function Calling?
Imagine an LLM as a smart but handless consultant. It knows a lot of knowledge, but cannot:
* Get real-time information (such as current weather, stock prices)
* Perform calculations (such as complex mathematical operations)
* Operate external systems (such as sending emails, reading/writing files)
Function Calling gives the LLM hands, allowing it to break through its own limitations and perform actual tasks.
### How Function Calling Works
The basic process of Function Calling is as follows:
!(#)
### Core Concepts
* **Tool Definition**: Describes a tool's functionality, parameters, and return values
* **Tool Selection**: The LLM chooses the appropriate tool based on the user's question
* **Parameter Extraction**: The LLM extracts the parameters needed by the tool from the question
* **Result Processing**: Integrates the tool execution results into the final response
### Example Explanation
Suppose we have a weather query tool. When a user asks, "What's the weather like in Beijing today?", here's what happens:
1. The LLM identifies that it needs to call the weather query tool
2. Extracts parameters from the question: `city="Beijing"`, `date="Today"`
3. Calls the weather API to fetch data
4. Integrates the weather data into a friendly response and returns it to the user
* * *
## Tool Definition and Description Writing
To let the LLM correctly use tools, we first need to clearly define them. A good tool definition should be like a clear manual, helping the LLM understand:
* What does this tool do?
* When should it be used?
* What parameters are needed?
* What format should the parameters be in?
### Structure of Tool Definitions
A complete tool definition usually includes the following parts:
## Example
# Example Structure of Tool Definition
weather_tool ={
"name": "get_weather",# Tool name
"description": "Get weather information for a specified city",# Tool description
"parameters": {# Parameter definitions
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "City name, e.g., 'Beijing', 'Shanghai'"
},
"date": {
"type": "string",
"description": "Date, format 'YYYY-MM-DD', or 'today', 'tomorrow'",
"enum": ["Today","Tomorrow","Day after tomorrow"]
}
},
"required": # Required parameters
}
}
### Writing High-Quality Tool Descriptions
#### 1. Descriptions Should Be Clear and Specific
* **Poor Description**: Query weather
* **Good Description**: Retrieve weather information for a specific city on a particular date, including temperature, humidity, wind speed, and weather conditions (sunny, rainy, cloudy, etc.)
#### 2. Parameter Descriptions Should Be Detailed
**Poor Parameter Description**:
"city": {"type": "string"}
**Good Parameter Description:**
"city": { "type": "string", "description": "Full city name, e.g., 'BeijingCity', 'ShanghaiCity'. Do not use abbreviations or pinyin."}
#### 3. Use Enumerations to Limit Options
For limited options, use enums (enumeration) to help the LLM understand:
"unit": { "type": "string", "description": "Temperature unit", "enum": ["celsius", "fahrenheit"], "default": "celsius"}
#### 4. Provide Example Values
Provide examples in descriptions to help the LLM understand the format:
"date": { "type": "string", "description": "Date, format should be 'YYYY-MM-DD', e.g., '2024-06-15'"}
### Practical Tool Definition Examples
#### 1. Calculator Tool
calculator_tool = { "name": "calculate", "description": "Perform mathematical calculations, supporting basic operations such as addition, subtraction, multiplication, division, and exponentiation", "parameters": { "type": "object", "properties": { "expression": { "type": "string", "description": "Mathematical expression, e.g., '2 + 3 * 4', 'sqrt(16)', 'sin(30)'" } }, "required": }}
#### [](#)2. Search Tool
search_tool = { "name": "search_web", "description": "Search for the latest information on the internet", "parameters": { "type": "object", "properties": { "query": { "type": "string", "description": "Search keywords, preferably specific and clear" }, "num_results": { "type": "integer", "description": "Number of results to return, default is 5", "default": 5, "minimum": 1, "maximum": 10 } }, "required": }}
#### 3. File Operation Tool
file_tool = { "name": "read_file", "description": "Read the content of a specified file", "parameters": { "type": "object", "properties": { "file_path": { "type": "string", "description": "Complete file path, e.g., '/home/user/document.txt'" }, "encoding": { "type": "string", "description": "File encoding, default is 'utf-8'", "default": "utf-8" } }, "required": }}
### Best Practices for Tool Definitions
* **Clear and Concise Names**: Start with verbs, e.g., `get_`, `calculate_`, `search_`
* **Complete and Detailed Descriptions**: Explain the toolβs purpose, applicable scenarios, and limitations
* **Thorough Parameter Validation**: Define parameter types, ranges, and format requirements
* **Provide Default Values**: Offer reasonable defaults for optional parameters
* **Consider Error Scenarios**: Describe possible errors and limitations in the description
### Validating Tool Definitions
After defining a tool, validation testing should be performed:
def validate_tool_definition(tool_def): """Validate whether the tool definition is complete""" required_fields = ["name", "description", "parameters"] for field in required_fields: if field not in tool_def: return False, f"Missing required field: {field}" # Check parameter structure if "properties" not in tool_def: return False, "Missing properties field in parameter definition" return True, "Tool definition is complete"# Test validation is_valid, message = validate_tool_definition(weather_tool)print(f"Validation result: {is_valid}, message: {message}")
* * *
## Parameter Extraction and Validation
After the LLM extracts parameters from the user's question, these parameters need to be validated and processed to ensure the tool can execute correctly.
### Parameter Extraction Process
When the LLM decides to call a tool, it analyzes the user input and extracts the parameters required by the tool:
User Input: "Query the weather temperature in Beijing tomorrow"
Tool: get_weather
Extracted Parameters: {"city": "Beijing", "date": "Tomorrow"}
### Challenges in Parameter Extraction
Parameter extraction may encounter the following issues:
* **Missing Information**: The user did not provide all necessary information
* **Format Mismatch**: The provided format does not match the tool's requirements
* **Ambiguous Interpretation**: The same information might have multiple interpretations
* **Context Dependency**: Parameters require understanding from conversation history
### Methods for Parameter Validation
#### 1. Type Validation
Ensure parameter types meet the requirements:
def validate_parameters(params, tool_def): """Validate parameter types""" errors = [] for param_name, param_def in tool_def.items(): if param_name in params: param_value = params expected_type = param_def.get("type") # Type check if expected_type == "string" and not isinstance(param_value, str): errors.append(f"Parameter '{param_name}' should be string type") elif expected_type == "integer" and not isinstance(param_value, int): errors.append(f"Parameter '{param_name}' should be integer type") elif expected_type == "number" and not isinstance(param_value, (int, float)): errors.append(f"Parameter '{param_name}' should be number type") elif expected_type == "boolean" and not isinstance(param_value, bool): errors.append(f"Parameter '{param_name}' should be boolean type") return errors
#### 2. Range Validation
Check if parameter values fall within allowed ranges:
def validate_range(params, tool_def): """Validate parameter ranges""" errors = [] for param_name, param_def in tool_def.items(): if param_name in params: param_value = params # Check minimum value if "minimum" in param_def and param_value param_def: errors.append(f"Parameter '{param_name}' cannot be greater than {param_def['maximum']}") # Check enum values if "enum" in param_def and param_value not in param_def: errors.append(f"Parameter '{param_name}' must be one of {param_def['enum']}") return errors
#### 3. Required Parameter Validation
Ensure all required parameters are provided:
def validate_required(params, tool_def): """Validate required parameters""" errors = [] required_params = tool_def.get("required", []) for param_name in required_params: if param_name not in params: errors.append(f"Missing required parameter: {param_name}") return errors
### Complete Parameter Validation System
## Example
class ParameterValidator:
"""Parameter Validator"""
def __init__ (self, tool_def):
self.tool_def= tool_def
def validate(self, params):
"""Execute full parameter validation"""
all_errors =[]
# Check required parameters
required_errors =self.validate_required(params)
all_errors.extend(required_errors)
# Check parameter types
type_errors =self.validate_type(params)
all_errors.extend(type_errors)
# Check parameter ranges
range_errors =self.validate_range(params)
all_errors.extend(range_errors)
# Check extra parameters (undefined parameters)
extra_errors =self.validate_extra(params)
all_errors.extend(extra_errors)
return len(all_errors)==0, all_errors
def validate_required(self, params):
"""Validate required parameters"""
errors =[]
required_params =self.tool_def.get("required",[])
for param_name in required_params:
if param_name not in params or paramsis None:
errors.append(f"Missing required parameter: {param_name}")
return errors
def validate_type(self, params):
"""Validate parameter types"""
errors =[]
for param_name, param_value in params.items():
if param_name in self.tool_def:
param_def =self.tool_def
expected_type = param_def.get("type")
if expected_type =="string"and not isinstance(param_value,str):
errors.append(f"Parameter '{param_name}' should be string type, actual is {type(param_value).__name__}")
elif expected_type =="integer"and not isinstance(param_value,int):
errors.append(f"Parameter '{param_name}' should be integer type, actual is {type(param_value).__name__}")
elif expected_type =="number"and not isinstance(param_value,(int,float)):
errors.append(f"Parameter '{param_name}' should be number type, actual is {type(param_value).__name__}")
elif expected_type =="boolean"and not isinstance(param_value,bool):
errors.append(f"Parameter '{param_name}' should be boolean type, actual is {type(param_value).__name__}")
return errors
def validate_range(self, params):
"""Validate parameter ranges"""
errors =[]
for param_name, param_value in params.items():
if param_name in self.tool_def:
param_def =self.tool_def
# Check minimum value
if"minimum"in param_def and param_value param_def:
errors.append(f"Parameter '{param_name}' cannot be greater than {param_def['maximum']}")
# Check enum values
if"enum"in param_def and param_value not in param_def:
errors.append(f"Parameter '{param_name}' must be one of {param_def['enum']}")
return errors
def validate_extra(self, params):
YouTip