Lesson 31 — Beginner
Function & Tool Calling
Model sirf jawab deta hai — lekin kabhi kabhi asli kaam chahiye: database check karo, API call karo, file banao. Tool calling mein model decide karta hai kaunsa tool lagega aur arguments kya honge — execute tumhara code karta hai. Is lesson mein hum tool schema, poora loop, IDs, parallel calls aur failures dekhenge.
Tool calling kya hai
WHAT
Tool calling (function calling) mein tum model ko tools ki list dete ho — naam, description, parameter schema. Model sawaal padh kar decide karta hai kaunsa tool chahiye aur arguments kya hain, JSON ke roop mein wapas bhejta hai.
WHEN
Jab jawab static text nahi chahiye — live data (orders, weather), action (refund, send, create), ya calculation chahiye. Ya jab model ko khud decide karna ho ki kya karna hai.
WHERE
Support bots (order lookup), coding agents, copilots, automation pipelines — lagbhag har "smart" feature ke peeche tool calling chhupi hoti hai.
Sabse zaroori line: model tool chunta hai aur arguments banata hai; function tumhare system par chalti hai. Model ko khud kuch karne ki permission nahi hoti — wo sirf ek structured request bhejta hai.
Tool ka schema — naam, description, parameters
tools = [{
"type": "function",
"function": {
"name": "get_order_status",
"description": "Diye gaye order number ka current status aur ETA batata hai. Sirf tab use karo jab user order ke baare mein poochhe.",
"parameters": {
"type": "object",
"properties": {
"order_id": {"type": "string", "description": "Order number, jaise ORD-4821"},
"include_eta": {"type": "boolean", "description": "ETA bhi batana hai ya nahi"}
},
"required": ["order_id"]
}
}
}]
resp = client.chat.completions.create(model="gpt-4o-mini", messages=messages, tools=tools)
msg = resp.choices[0].message
- Name — model isi naam se call karta hai. Verb + object rakho: get_order_status.
- Description — model isi se decide karta hai kab use karna hai. Yehi uska decision context hai (agle lesson mein isko design karenge).
- parameters — JSON Schema: types, required, descriptions. Model inhi properties ke andar arguments bharta hai.
- Model jawab ke saath tool_calls de sakta hai content ke bajaye — content khali ho sakta hai, tool_calls mein naam + arguments honge.
Poora tool-calling loop
import json
def run_tool(call):
if call.function.name == "get_order_status":
args = json.loads(call.function.arguments) # string -> dict
order = db_lookup(args["order_id"]) # ASLI kaam yahan
return json.dumps(order)
return json.dumps({"error": "unknown tool"})
def chat(user_text):
messages.append({"role": "user", "content": user_text})
while True:
resp = client.chat.completions.create(
model="gpt-4o-mini", messages=messages, tools=tools
)
msg = resp.choices[0].message
messages.append(msg) # tool wala message bhi history if not msg.tool_calls:
return msg.content # final answer — loop khatam
for call in msg.tool_calls: # parallel calls bhi yahin
result = run_tool(call)
messages.append({ # ID ke saath result wapas
"role": "tool",
"tool_call_id": call.id,
"content": result,
})
print(chat("ORD-4821 ka status kya hai?"))
- Loop ke steps: user → LLM (tool_calls) → execute → role "tool" result (tool_call_id ke saath) → LLM → final answer.
- tool_call_id match karna zaroori hai — har result uski call ID ke saath jaata hai; missing ya mismatch = API error.
- Har round ka message history mein jaata hai — agli baar model ko pata hota hai pehle kya hua, isliye wo repeat nahi karta.
- Loop kitni bhi rounds chala sakta hai — ek tool ka result agla tool decide kar sakta hai (agle section mein).
Sequential, repeated, parallel aur chained calls
# SEQUENTIAL — ek ka result doosre ka input
calc("18*7") -> 126 -> convert("126 INR to USD")
# REPEATED — ek hi tool baar baar (har order ke liye)
for oid in ["ORD-1", "ORD-2"]: get_order_status(oid)
# PARALLEL — model ek hi message mein kayi calls de sakta hai
tool_calls: [get_weather("Mumbai"), get_weather("Delhi")]
# CHAINED DECISION — pehle ka result decide karta hai agla tool
search_inventory() -> "out of stock" -> notify_manager() (refund nahi!)
- msg.tool_calls ek list hai — hamesha isi tarah handle karo, ek hi assume mat karo.
- Independent calls ko parallel chala sakte ho (ThreadPool ya asyncio) — lekin pehle loop sahi chalwa lo, speed baad mein.
- Repeated calls (ek hi tool kayi baar) mein expensive tool ho toh result cache karo — wahi order dobara mat dhoondo.
- Chained workflows mein tool results ko carefully feed karo — galat ordering se model agla galat step chunta hai.
Tool failures aur malformed arguments
def safe_run(call):
try:
args = json.loads(call.function.arguments) # malformed JSON?
validate_args(args) # missing/wrong type?
return execute(call.function.name, args)
except (json.JSONDecodeError, KeyError, TypeError) as e:
return json.dumps({"error": f"bad arguments: {e}"})
except PermissionError:
return json.dumps({"error": "permission denied — user ko bolo"})
except Exception as e:
return json.dumps({"error": f"tool failed: {type(e).__name__}"})
- Model ke arguments untrusted input hain — parse, type check aur permission check execute se pehle.
- Tool ka error bhi tool result hi hai — usse messages mein wapas bhejo; model ya dobara try karega ya user ko saaf bolega.
- Malformed arguments real hain — schema ke bawajood model kabhi galat JSON bana sakta hai, kabhi missing field chhod sakta hai.
- Tool exception ko kabhi chat loop ke bahar mat phenko — wrap karo, structured error banao, loop zinda rakho.
Exercise
Question: Tool calling mein function ko actually execute kaun karta hai — model ya tumhara application code?
Question: Tool result wapas messages mein kis role se jaata hai?
Question: Tool result ko uski call se match karne ke liye kaunsa ID field lagti hai?
Question: Model ke arguments ko trusted maano ya untrusted?
Common mistakes
- Tool result history mein na daalna: — model context se bahar ho kar dobara wahi tool maangta rehne lagega. Har tool_calls ke baad role "tool" message append karo, tool_call_id ke saath.
- Arguments par bharosa kar lena: — model galat ya incomplete JSON de sakta hai. json.loads + validation + permission check execute se pehle, bina exception crash kare.
- tool_call_id match karna bhool jaana: — API error ya galat context. Har result uski call ID ke saath lautao, order pe nahi, ID pe match karo.
- Tool exception ko loop ko crash karne dena: — ek tool fail toh poora chat band. Safe wrapper lo, error string tool result mein wapas bhejo.
Waiter sawaal sunta hai, menu dekh kar order likhta hai (model tool + arguments decide) — par khana kitchen mein banta hai (tumhari function). Chitthi galat ho toh khana galat banega, isliye kitchen padh ke verify karti hai:
# Waiter (model) ne likha:
call = {"name": "get_order_status", "arguments": "{\"order_id\": \"ORD-4821\"}"}
# Kitchen (tumhara code) verify + execute
args = json.loads(call["arguments"]) # string -> dict
if not args.get("order_id"):
return {"error": "order_id missing"} # wapas waiter ke paas
return db_lookup(args["order_id"]) # asli kaam- Q: Model tool calling mein kya decide karta hai aur kya nahi? — Kaunsa tool aur arguments decide karta hai; validation, execution aur permissions tumhare code mein.
- Q: Poore tool loop ke steps? — User → LLM (tool_calls) → execute → role "tool" result (tool_call_id ke saath) → LLM → final answer.
- Q: Model ek message mein do tool calls de de toh? — tool_calls list par iterate karo, har result apni ID ke saath; independent calls parallel chal sakti hain.
- Q: Tool fail ho jaye toh kya bhejoge? — Structured error tool result ke roop mein; model ya retry karega ya user ko saaf bolega — loop crash nahi hoga.
Tool loop aur IDs clear. Agla lesson — Designing Good Tools — mein seekhenge model ko galat tool chunne hi na dene wali schema kaise likhi jaati hai.