Function Calling and Tool Use in LLMs Explained
Function calling lets a language model ask your code to look something up or take an action. This guide explains the round trip and how to define tools, then puts ten customer requests to a small local model with three tools, and shows the four ways it went wrong and the safeguards that caught them.
GloryTecks Team
Training & Placement Faculty
Last updated: 3 October 2026
A language model on its own can only produce text. It cannot check an order, read a database or send an email. Function calling, also called tool use, is how it gets those abilities without being given them directly. The model writes a request to call one of your functions. Your code decides whether to run it, runs it, and hands the result back.
That division is the whole design. The model chooses; your code acts. Most of the reliability and almost all of the safety of tool use comes from taking the second half seriously.
The round trip
OpenAI's documentation describes the flow in five steps: send a request with the tools the model may call, receive a tool call, execute the code in your application, send the result back, and receive the final reply or further calls (OpenAI: function calling). Other providers work the same way. Claude signals a tool request with a tool_use stop reason, and your code executes the call and returns the result (Anthropic: tool use). A local model served by Ollama returns tool_calls in its message, and the result goes back as a message with the tool role (Ollama: tool calling).
A tool is described by three things:
| Part | Purpose |
|---|---|
| Name | What the model writes to ask for it |
| Description | How the model decides when to use it |
| Parameters | A JSON schema: argument names, types, allowed values |
The model never sees your code, only these descriptions. It chooses a tool the way a new colleague would choose from a list of internal services: by reading what the list says.
Ten requests, three tools
The script gives a small local model three tools for an invented furniture shop: look up an order, check stock, and start a return. It sends ten customer messages and compares each tool call with the one a careful human would make. Every call is checked before anything runs, and the return tool does nothing until the customer confirms.
import json
import re
import urllib.request
URL = "http://localhost:11434/api/chat"
MODEL = "llama3.2"
OPTIONS = {"temperature": 0, "seed": 42}
# A stub shop, invented for this example
ORDERS = {"A1001": "shipped, arriving 6 October", "A1002": "being packed", "A1003": "delivered on 30 September"}
STOCK = {"desk lamp": 0, "office chair": 14, "monitor arm": 3}
def get_order_status(order_id):
return ORDERS.get(order_id, "no such order")
def check_stock(product):
return f"{STOCK[product]} in stock"
def create_return(order_id, reason):
# an action with consequences: the application asks the customer before doing it
return "not done yet: ask the customer to confirm the return"
TOOLS = [
{"type": "function", "function": {
"name": "get_order_status", "description": "Look up the delivery status of an order",
"parameters": {"type": "object", "required": ["order_id"], "properties": {
"order_id": {"type": "string", "description": "Order number, a letter A followed by four digits, e.g. A1234"}}}}},
{"type": "function", "function": {
"name": "check_stock", "description": "Check how many units of a product are in stock",
"parameters": {"type": "object", "required": ["product"], "properties": {
"product": {"type": "string", "enum": list(STOCK)}}}}},
{"type": "function", "function": {
"name": "create_return", "description": "Start a return for an order",
"parameters": {"type": "object", "required": ["order_id", "reason"], "properties": {
"order_id": {"type": "string", "description": "Order number, e.g. A1234"},
"reason": {"type": "string", "enum": ["damaged", "wrong item", "no longer needed"]}}}}},
]
FUNCTIONS = {"get_order_status": get_order_status, "check_stock": check_stock, "create_return": create_return}
SYSTEM = ("You are the support assistant of an online furniture shop. Use a tool when it helps. "
"If you need a detail the customer has not given, ask for it.")
def chat(messages):
body = {"model": MODEL, "messages": messages, "tools": TOOLS, "stream": False, "options": OPTIONS}
req = urllib.request.Request(URL, json.dumps(body).encode(), {"Content-Type": "application/json"})
with urllib.request.urlopen(req, timeout=900) as r:
return json.load(r)["message"]
def validate(name, args):
# the model's arguments are untrusted input: check them before running anything
spec = next(t["function"]["parameters"] for t in TOOLS if t["function"]["name"] == name)
for key in spec["required"]:
if key not in args:
return f"missing {key}"
for key, value in args.items():
rule = spec["properties"].get(key)
if rule is None:
return f"unknown argument {key}"
if "enum" in rule and value not in rule["enum"]:
return f"{key} must be one of {rule['enum']}"
if key == "order_id" and not re.fullmatch(r"A\d{4}", str(value)):
return "order_id must look like A1234"
return None
def show(call):
f = call["function"]
return f"{f['name']}(" + ", ".join(f"{k}={v!r}" for k, v in f["arguments"].items()) + ")"
REQUESTS = [
("Where is my order A1001?", ["get_order_status(order_id='A1001')"]),
("Has order A1003 arrived yet?", ["get_order_status(order_id='A1003')"]),
("Do you have any office chairs in stock?", ["check_stock(product='office chair')"]),
("Is the monitor arm available?", ["check_stock(product='monitor arm')"]),
("The lamp from order A1002 arrived broken. I want to send it back.", ["create_return(order_id='A1002', reason='damaged')"]),
("What are your opening hours?", []),
("Thanks, that's all!", []),
("Where is my order? I can't find the number.", []),
("Check order A1002 and tell me if desk lamps are in stock.",
["check_stock(product='desk lamp')", "get_order_status(order_id='A1002')"]),
("Where is order 1001?", ["get_order_status(order_id='1001')"]),
]
matched = 0
transcripts = {}
for i, (text, expected) in enumerate(REQUESTS, 1):
messages = [{"role": "system", "content": SYSTEM}, {"role": "user", "content": text}]
reply = chat(messages)
calls = reply.get("tool_calls") or []
shown = sorted(show(c) for c in calls)
ok = shown == sorted(expected)
matched += ok
print(f"{i:>2}. {text}")
print(f" expected: {', '.join(expected) or 'no tool'}")
print(f" model: {', '.join(shown) or 'no tool'} -> {'as expected' if ok else 'DIFFERENT'}")
# run the calls that pass validation, return results or errors, and let the model reply
messages.append(reply)
for c in calls:
name, args = c["function"]["name"], c["function"]["arguments"]
problem = validate(name, args) if name in FUNCTIONS else "unknown tool"
result = f"error: {problem}" if problem else FUNCTIONS[name](**args)
print(f" tool result: {result}")
messages.append({"role": "tool", "tool_name": name, "content": result})
final = chat(messages)["content"] if calls else reply["content"]
transcripts[i] = final
print(f"\nmatched the expected calls: {matched} of {len(REQUESTS)}")
for i in (1, 5, 8, 10):
print(f"\nfinal reply to request {i}:\n{transcripts[i]}") 1. Where is my order A1001?
expected: get_order_status(order_id='A1001')
model: get_order_status(order_id='A1001') -> as expected
tool result: shipped, arriving 6 October
2. Has order A1003 arrived yet?
expected: get_order_status(order_id='A1003')
model: get_order_status(order_id='A1003') -> as expected
tool result: delivered on 30 September
3. Do you have any office chairs in stock?
expected: check_stock(product='office chair')
model: check_stock(product='office chair') -> as expected
tool result: 14 in stock
4. Is the monitor arm available?
expected: check_stock(product='monitor arm')
model: check_stock(product='monitor arm') -> as expected
tool result: 3 in stock
5. The lamp from order A1002 arrived broken. I want to send it back.
expected: create_return(order_id='A1002', reason='damaged')
model: create_return(order_id='A1002', reason='damaged') -> as expected
tool result: not done yet: ask the customer to confirm the return
6. What are your opening hours?
expected: no tool
model: get_order_status(order_id='N/A') -> DIFFERENT
tool result: error: order_id must look like A1234
7. Thanks, that's all!
expected: no tool
model: no tool -> as expected
8. Where is my order? I can't find the number.
expected: no tool
model: get_order_status(order_id='A1234') -> DIFFERENT
tool result: no such order
9. Check order A1002 and tell me if desk lamps are in stock.
expected: check_stock(product='desk lamp'), get_order_status(order_id='A1002')
model: check_stock(product='desk lamp') -> DIFFERENT
tool result: 0 in stock
10. Where is order 1001?
expected: get_order_status(order_id='1001')
model: get_order_status(order_id='A1001') -> DIFFERENT
tool result: shipped, arriving 6 October
matched the expected calls: 6 of 10
final reply to request 1:
Thank you for providing the order ID A1001. According to our system, your order has been shipped and is expected to arrive on or around October 6th. If you have any further questions or concerns about your order, please don't hesitate to ask. Is there anything else I can assist you with?
final reply to request 5:
I've called the tool to generate a response for your situation.
To proceed with returning the lamp, I need to confirm some details with you. The tool suggests that we start by acknowledging receipt of the damaged item and requesting your confirmation on the return process.
Can you please confirm if the lamp arrived broken as you mentioned? And would you like us to provide a pre-paid return shipping label or would you prefer to arrange for your own shipping?
final reply to request 8:
It looks like we couldn't find any information on an order with that specific ID (A1234). Can you please provide me with your order number or any other details about your order, such as the date you placed it or the products you ordered? I'll do my best to help you track down the status of your order.
final reply to request 10:
Order #1001 has been shipped and is expected to arrive on October 6th. You can track the status of your order by visiting our website and logging into your account. If you have any further questions or concerns, please don't hesitate to contact us.What went right, and what went wrong
Six of ten requests produced exactly the expected calls, including the return, with the right order number and reason. The four misses are worth more than the successes.
- It invented an argument. Asked about an order without a number (request 8), the model called the lookup with
A1234. That is the example written in the tool's description. The value had the right format, so the validator let it through, and only the lookup failing stopped it. The final reply then did the right thing and asked for the number. - It guessed and presented the guess as fact. Asked about "order 1001" (request 10), it silently looked up
A1001, a real order, and told the customer that order 1001 had shipped. It also added advice about tracking on a website, which no tool had provided. - It called a tool it did not need. Asked about opening hours (request 6), it tried the order lookup with
N/A. The validator rejected it. - It missed a call. Request 9 needed two lookups, an order and a stock check. The model made only the stock check.
Two of these were caught by code that checked the arguments. One was stopped by the data itself. One, the guessed order number, reached the customer. A larger model makes fewer of these mistakes, but the categories are the same, and they are the ones to design against.
Making tool use reliable
Validate every argument in code. Treat the model's arguments as untrusted input. Check types and formats, then check them against real data: does the order exist, and does it belong to this customer?
Write descriptions without plausible example values. The invented A1234 came straight from the description. Describe the format in words, or use an example that cannot be real.
Use enums wherever the values are known. The stock and reason fields could only take listed values, and the model never got them wrong. OpenAI's guidance makes the same point: use enums and structure to prevent invalid states.
Do not make the model supply what you already know. If the logged-in customer's order is known, pass it from your code. OpenAI's guidance says the same, and keeps the number of tools available at the start of a turn small, suggesting fewer than 20.
Return errors as results. The validation messages went back to the model as tool results. That gives it a chance to recover or ask the user, instead of the application failing.
Expect zero, one or several calls. A reply can contain any number of tool calls, and code that handles only the first will drop work.
Strict mode helps, but only with the format
Hosted APIs can enforce the schema for you. OpenAI's strict mode makes calls reliably follow the function's schema, and the company recommends always enabling it. Claude's strict: true does the same. With it, malformed arguments disappear.
Two of the four failures above would have passed any schema. A1234 and A1001 were both well-formed order numbers, so the format was right and the meaning was wrong. Only checks against your own data catch that.
Actions need a person's confirmation
Reading data is one thing. Changing it is another. A model can be wrong, confused by an ambiguous message, or manipulated by text hidden in content it reads. OWASP calls the resulting risk excessive agency, with three roots: too much functionality, too many permissions and too much autonomy, such as high-impact actions taken without the user's confirmation (OWASP: excessive agency).
The return tool in the script shows the pattern: it records the intent and replies "not done yet: ask the customer to confirm". Refunds, deletions, emails and payments should work the same way. See guardrails for LLMs.
From one call to many
This article covers single rounds of tool use. When the model calls tools repeatedly, reading each result and deciding the next step, it becomes an agent. See AI agents explained. To expose the same tools to many applications through one standard interface, see MCP explained.
Common mistakes
- Running whatever the model asks for without validation.
- Examples in descriptions that look like real values.
- Free-text parameters where a list of allowed values would do.
- Too many tools with overlapping descriptions.
- Letting the model take irreversible actions without confirmation.
- Testing only the happy path. Include requests that need no tool, need two, or lack a detail.
What is function calling in LLMs?
Is function calling the same as tool use?
Does strict mode stop wrong tool calls?
Can small local models do function calling?
Working with the OpenAI API and building assistants with tool use are part of the Generative AI course.
Sources
GloryTecks Team
Training & Placement Faculty
The GloryTecks faculty team mentors learners across Data Science, AI and Data Engineering, with a focus on real-time projects and placement readiness in Hyderabad.
Want to master Generative AI?
Join GloryTecks’ expert-led Agentic AI training in Hyderabad with real-time projects and 100% placement support.
Building a Customer Support Bot with LLMs
NextPrompt Injection and LLM Security: What You Need to Know
Related articles
ChatGPT Complete Guide: Features, Prompts and Use Cases
ChatGPT went from a research preview to one of the most used software products in the world in under four years. This guide maps its main features and when they arrived, shows how to write prompts that work, sorts good uses from risky ones, and explains the limits every user should check for.
What Is Generative AI? A Complete Beginner's Guide
What generative AI is, in plain language: how language models produce text one token at a time (shown with a tiny runnable model), how chat models are trained, how image models differ, what the technology is reliable for, and why it confabulates.
RAG Architecture Explained: Retrieval-Augmented Generation
RAG gives a language model the right pages to read before it answers. This guide walks through the four stages with a pipeline that runs on a laptop, using open models and public documents, and shows three outcomes: a correct cited answer, a retrieval miss the model admits to, and a question outside the documents.