Function Calling: Structured Tool Use for Large Language Models › Worked Example: Multi-Step Calculation

Michael BrenndoerferFebruary 2, 20264 min read
Language AI HandbookAll chapters
Part 48 of 67Tool Use and Agents
Chapter 364Function Calling: Structured Tool Use for Large Language Models › Worked Example: Multi-Step Calculation
Reading section 5 of 12 · Worked Example: Multi-Step Calculation · 4 minSection 5 of 12 · 4 min

Book contents

Section 5 of 12 · 4 min read

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

In[8]:
Code
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:

In[9]:
Code
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")
Out[10]:
Console
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.