This note covers function calling in concrete implementation detail โ the structured schema-based mechanism, standardized across most major LLM APIs, that underlies the tool calling concept from the previous note.
The Function Schema โ Precisely Defining What the Model Can Call
function_schema = {
"name": "search_flights",
"description": "Search for available flights between two cities on a given date",
"parameters": {
"type": "object",
"properties": {
"origin": {"type": "string", "description": "Departure city or airport code"},
"destination": {"type": "string", "description": "Arrival city or airport code"},
"date": {"type": "string", "description": "Travel date in YYYY-MM-DD format"},
"max_price": {"type": "number", "description": "Maximum price in USD, optional"}
},
"required": ["origin", "destination", "date"]
}
}
This JSON-schema-style definition precisely specifies the function's name, purpose, and exactly what arguments it expects (including their types and which are required) โ the model uses this structured definition, not free-form English, to reliably generate correctly-formatted calls.
Code โ A Complete Function Calling Exchange
import json
response = llm_client.chat(
messages=[{"role": "user", "content": "Find me a flight from Delhi to Mumbai on March 15th"}],
functions=[function_schema]
)
if response.function_call:
args = json.loads(response.function_call.arguments)
# args = {"origin": "Delhi", "destination": "Mumbai", "date": "2026-03-15"}
result = search_flights(**args) # actually execute the real function
# Feed the result back for the model to produce a natural-language final answer
final_response = llm_client.chat(
messages=[
{"role": "user", "content": "Find me a flight from Delhi to Mumbai on March 15th"},
{"role": "assistant", "function_call": response.function_call},
{"role": "function", "name": "search_flights", "content": json.dumps(result)}
]
)
print(final_response.content)
Why Structured Schemas Matter for Reliability
Requiring the model to produce output conforming to a strict, predefined schema (rather than free-form text describing what it wants to do) makes the model's intent programmatically parseable and directly executable, with far less ambiguity or fragile text-parsing required on the application side. Modern LLM APIs typically fine-tune models specifically to reliably produce this structured format when given a function schema, making the mechanism dependable in production.
Multiple and Parallel Function Calls
Many modern APIs support the model requesting multiple function calls in a single turn (e.g. checking weather in three different cities simultaneously) โ the application executes all requested calls, often in parallel, and feeds all results back together, which is notably more efficient than requiring one full round-trip per individual function call.
Common Mistakes
- Defining overly broad, ambiguous functions (e.g. one generic "do_anything" function) rather than well-scoped, specifically-described functions โ this makes it much harder for the model to reliably choose the correct function and populate its arguments correctly.
- Not handling the case where the model requests a function call with missing required arguments or a malformed schema โ production systems need to validate and gracefully handle these cases rather than assuming perfect model output every time.
Interview Relevance
Q: "Why do modern LLM APIs use structured JSON schemas for function calling instead of having the model describe its intended action in free-form natural language?" A structured schema (defined parameter names, types, and required fields) makes the model's output programmatically parseable and directly executable by application code, with minimal ambiguity. Free-form natural language would require fragile, error-prone text parsing to extract the intended function and arguments, and would be far more likely to produce inconsistent or incorrectly-formatted results. Models are typically specifically trained to reliably produce schema-conforming output when given a function definition, making structured function calling dependable enough for real production use.
Practice Question
Why is it valuable for an LLM API to support requesting multiple function calls in parallel within a single turn, rather than requiring one call per round-trip?