Function Calling: Structured Tool Use for Large Language Models › Worked Example: Multi-Step Calculation
Language AI HandbookAll chapters Function Calling: Structured Tool Use for Large Language Models › Worked Example: Multi-Step CalculationOne section · Sections in this chapter5 / 12 ↓
Reading tools Definitions & inline links
Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.
Article links
Make inline references clickable
Let's walk through a concrete example involving a calculator tool to illustrate the full function calling cycle.
Scenario: You ask, "If I have 150 apples and give away 30%, then buy 2 dozen more, how many do I have?"
While modern LLMs can solve this arithmetically, we'll assume our model has been instructed to use a calculator for precision. This scenario demonstrates how function calling handles multi-step reasoning where natural language must be translated into mathematical expressions.
Step 1: Schema Definition
calculator_schema = {
"name": "calculate",
"description": "Evaluate a mathematical expression safely",
"parameters": {
"type": "object",
"properties": {
"expression": {
"type": "string",
"description": "Mathematical expression to evaluate, e.g., '(150 * 0.7) + 24'",
}
},
"required": ["expression"],
},
}Step 2: Initial Query
Your message enters the conversation context along with the available function definitions. The model processes this input through its attention layers, comparing the query against the calculator description to determine that mathematical evaluation is required.
Step 3: Call Generation
The model recognizes the arithmetic nature of the query and generates:
{
"name": "calculate",
"arguments": "{\"expression\": \"150 * 0.7 + 24\"}"
}Note that the model has translated "give away 30%" into the multiplicative factor 0.7 and "2 dozen" into 24. This shows its ability to perform semantic translation into executable parameters. This translation step shows the model's role as an interface layer between human communication patterns and machine-executable instructions. The model must understand that "giving away 30%" leaves 70%, and that a "dozen" equals twelve. This makes two dozen equal twenty-four.
The translation is non-trivial. The model must correctly chain two operations: first apply the percentage reduction, then add the new quantity. Expressing this as a single expression 150 * 0.7 + 24 requires understanding operator precedence (multiplication before addition) and recognizing that the 30% reduction and the subsequent purchase are independent operations on the running count.
Step 4: Execution
The application layer parses the JSON, validates that the expression contains only safe mathematical operations (preventing code injection), and evaluates:
import ast
import operator
def safe_calculate(expression):
"""Safely evaluate mathematical expression using AST parsing.
All nodes are validated against an allowlist before any evaluation occurs."""
allowed_operators = {
ast.Add: operator.add,
ast.Sub: operator.sub,
ast.Mult: operator.mul,
ast.Div: operator.truediv,
ast.USub: operator.neg,
}
def eval_node(node):
if isinstance(node, ast.Num): # Python 3.7
return node.n
elif isinstance(node, ast.Constant): # Python 3.8+
return node.value
elif isinstance(node, ast.BinOp):
op_type = type(node.op)
if op_type in allowed_operators:
return allowed_operators[op_type](
eval_node(node.left), eval_node(node.right)
)
elif isinstance(node, ast.UnaryOp):
if isinstance(node.op, ast.USub):
return -eval_node(node.operand)
raise ValueError(f"Unsupported operation: {type(node)}")
tree = ast.parse(expression, mode="eval")
result = eval_node(tree.body)
return result
result = safe_calculate("150 * 0.7 + 24")Calculation result: 129.0
The calculation yields 129.0 apples, confirming that after giving away 30% (45 apples) from the original 150 and adding 24 more, you have 129 apples remaining.
Step 5: Observation Injection
The result 129.0 is formatted as a tool message and appended to the context. This injection step is transparent to you but important for the model's reasoning chain. The observation provides grounding. This keeps the final answer is based on actual computation rather than estimation.
Step 6: Final Response
The model now generates: "You would have 129 apples."
This example demonstrates the complete loop: intent recognition, parameter extraction, safe execution, and natural language synthesis based on structured data. It illustrates how function calling creates a division of labor: the model handles language understanding and translation, while specialized tools handle precise computation. This separation of concerns allows each component to excel at what it does best, combining the linguistic flexibility of LLMs with the computational accuracy of traditional software.
The example also illustrates why safe execution matters. The safe_calculate function uses Python's AST module to parse the expression before evaluating it, explicitly checking that only arithmetic operations are present. The entire expression tree is walked and validated against an allowlist before any computation occurs. This architecture is a standard pattern for secure expression evaluation in function calling contexts.