Hirenix kaise padhata hai
Ek chapter. 90 minute.
Interview ke liye taiyaar.
Har concept ek real-world problem se — jaisa production code mein aata hai, waisa. Ratna nahi padta, samajh aa jaata hai. Har question ka model answer diya hai: interviewer ko exactly kya bolna hai, aur kyun. Phir usi chapter ka AI mock interview.
- 📖Concept, 5 min meinJargon nahi — seedhi baat
- 🛠️Real-world problemJaisa production code mein aata hai
- 💬Model answerInterview mein kya bolna hai
- 🧠FlashcardsRevision 10 min mein
- 🤖AI mock interviewFollow-up bhi poochta hai
- 📊Weak topicsKahan phans rahe ho, pata chale

Farq content ka nahi, filter ka hai — sirf wahi jo production mein actually use hota hai aur interview mein actually poocha jaata hai. Kitaabi topics jo industry mein kahin nahi chalte, wo yahan nahi milenge.
What you’ll learn
- ●Function calling kya hai?
- ●Tool definitions (JSON Schema)
- Tool-call loopFree account
- Parallel tool callsFree account
- Real-world toolsFree account
- Tool securityFree account
- Tools me error handlingFree account
- Structured outputFree account
- Function calling vs RAGFree account
- RecapFree account
- ●Project: AI Assistant with tools
Function calling kya hai?
LLM ko soch ek brilliant dost jo ek sealed room me band hai — na phone hai, na internet, na clock. Usse aaj ka weather, tera bank balance, ya abhi kitne baje hain — poochh, wo kuch bhi real check nahi kar sakta. Phir bhi wo confidently jawab dega, apni memory se guess karke. Wo guess bilkul sahi bhi lag sakta hai aur bilkul galat bhi ho sakta hai.
Function calling me, tu us dost ko ek list deta hai un tools ki jo wo maang sakta hai — "yeh raha ek phone jo weather API ko call karta hai," "yeh raha database ka line." Dost khud phone dial nahi kar sakta. Wo bas itna keh sakta hai "please weather API ko Karachi ke liye dial karo" — aur tu hi actually phone uthata hai, call karta hai, aur real answer wapas batata hai. Tabhi dost ek grounded, correct reply de pata hai.
Bas yehi poora idea hai. Ch4 (RAG) ne LLM ko knowledge di thi — answer dene se pehle padhne ke liye text. Chapter 5 usse actions deta hai — real code trigger karne ki ability jo live data check kare ya duniya me kuch kare.
Har function-calling system jo 7-step flow follow karta hai:
- Tu apne available tools ko model ko JSON Schema ke roop me describe karta hai (name, description, expected arguments).
- User ek question poochhta hai.
- Model question aur tool list padhta hai, aur decide karta hai koi tool fit karta hai ya nahi.
- Agar karta hai, model ek tool call return karta hai: ek function name + arguments, JSON ke roop me — ye execution nahi, sirf ek request hai.
- Tera code us request ko dekhta hai, decide karta hai honor karna hai ya nahi, aur real Python function ko actually run karta hai.
- Tera code function ka real result ek nayi message ke roop me model ko wapas bhejta hai.
- Model wo result padhta hai aur final, natural-language answer likhta hai.
🌍 Real-world example: Ek plain LLM se poochho "Karachi me abhi weather kya hai?" aur wo ek plausible-lagne-wala temperature bana dega — usse pata karne ka koi tareeka nahi hai. Usse
get_weathertool do, aur guess karne ke bajaye wo return karega{"name": "get_weather", "arguments": {"city": "Karachi"}}. Tera code real weather API call karta hai,30°C, Sunnymilta hai, aur wo wapas de deta hai — ab model ka final answer actually sach hai.
💡 Tool / Function = code ka ek real piece (ek Python function, ek database query, ek API call) jiske baare me model ko bataya gaya hai ki wo exist karta hai aur request kar sakta hai, par khud kabhi run nahi karta.
💡 Tool call = model ki request ki koi specific tool specific arguments ke saath run ho, structured JSON ke roop me return hoti hai — tool actually run hona nahi.
💡 Tool definition = wo JSON Schema description (name, description, parameters) jo tu model ko deta hai taaki usse pata ho kaunse tools exist karte hain aur har ek ko kaunse arguments chahiye.
⚠️ RULE 3 — is chapter ki sabse important cheez: model kabhi kuch execute nahi karta. Wo sirf ek tool name aur arguments ka JSON blob return karta hai. Wo function actually run hoga ya nahi — ye decision poori tarah tere code ka hai — tu arguments validate kar sakta hai, call reject kar sakta hai, log kar sakta hai, human se confirm karwa sakta hai, ya run kar sakta hai. Model ek text predictor hai jo "yeh raha JSON jo tujhe next run karna chahiye" predict karne me bahut achha ho gaya — bas itna hi. Kisi bhi tool-using system ki har security guarantee isi ek fact se aati hai.
Standard definition (interview me bolo): Function calling (tool use) is a feature where you describe available functions to an LLM as JSON Schema; when a user's request matches one, the model returns a tool call — a function name and arguments as JSON — which your application code is responsible for validating and executing, then feeding the real result back to the model for its final answer.
from openai import OpenAI
import json
client = OpenAI()
# Step 1: describe the tool as JSON Schema (this is what the model "sees")
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a given city",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name, e.g. Karachi"}
},
"required": ["city"]
}
}
}
]
# The REAL function — this is the only thing that actually runs
def get_weather(city):
# in production this would call a real weather API
return {"city": city, "temp_c": 30, "condition": "Sunny"}
available_functions = {"get_weather": get_weather}
messages = [{"role": "user", "content": "What's the weather in Karachi?"}]
# Step 2-4: ask the model, it may return a tool call instead of text
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=messages,
tools=tools
)
message = response.choices[0].message
if message.tool_calls:
messages.append(message) # record the model's tool-call request in history
for tool_call in message.tool_calls:
func_name = tool_call.function.name
func_args = json.loads(tool_call.function.arguments)
# Step 5: YOUR code decides to run it, and actually runs it
result = available_functions[func_name](**func_args)
# Step 6: send the real result back, tagged to this exact tool call
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps(result)
})
# Step 7: ask again — now the model has real data and writes the final answer
final = client.chat.completions.create(
model="gpt-4o-mini",
messages=messages,
tools=tools
)
print(final.choices[0].message.content)
else:
print(message.content)Tool definitions (JSON Schema)
Ek bade toolbox ke har drawer pe label hota hai, socho. "misc stuff" likha drawer bekaar hai — khole bina kisi ko pata nahi chalega andar kya hai. "Phillips screwdrivers, sizes 1-3, for electronics" likha drawer bata deta hai exactly kab use karna hai. Tool definition wahi label hai, bas insaan ke liye nahi, model ke liye likha jaata hai.
Function calling me, model tera function call kare usse pehle, tu usse ek chhota JSON Schema object ki tarah describe karta hai, jisme 4 parts hote hain: name (function ka identifier, jaise get_current_weather), description (ek sentence jo model ko batata hai tool kya karta hai aur kab use karna hai), parameters (type: object ke andar ek properties map — har argument ka apna entry, apna type aur description, aur optional enum fixed allowed values ki list ke liye), aur required (un argument names ki list jo hona hi chahiye). Yahan kuch bhi execute nahi hota — ye sirf text hai jo model kisi bhi decision se pehle padhta hai.
Yahan wo part hai jo beginners miss karte hain: description fields agle developer ke liye comments nahi hain. Ye prompt engineering hain — model ke paas SIRF yahi information hai decide karne ke liye (a) kya ye tool user ke request pe apply hota hai, (b) jab multiple tools available hon toh kaunsa pick karna hai, aur (c) har argument me kya dalna hai. "Gets weather info" jaisi vague description model ko match karne ke liye kuch nahi deti, isliye ya toh wo galat tool pick karta hai, ya sahi tool ko galat arguments ke saath call karta hai, ya call hi nahi karta jab karna chahiye tha. "Get the CURRENT real-time weather for a specific city. Use this ONLY for today's weather, not forecasts." jaisi precise description model ko exactly batati hai ye tool kab fire hoga aur kab nahi (jaise, ab usse pata hai ki 5-day forecast wale question ke liye ye tool NAHI use karna).
🌍 Real-world example:
search_ordersaursearch_productsnaam ke do tools jinki description dono "searches the database" likhi ho, hamesha confuse honge — model ke paas dono ko alag batane ka koi tareeka nahi hai. Inhe rewrite karo "Search a customer's past orders by order ID or date range" aur "Search the product catalog by name or category" — model bina kisi code change ke almost hamesha sahi route karega.
💡 JSON Schema = JSON data ki shape describe karne ka ek standard tareeka (kaunse fields hain, unka type kya hai, kaunse required hain) — function calling isi exact format ko tool parameters ke liye reuse karta hai.
💡 enum = kisi field ke liye allowed values ki ek fixed list (jaise
["celsius", "fahrenheit"]) — jab bhi ek parameter chhote se known set me se ek value hi ho sakta hai, ise use karo; ye model ko invalid values invent karne se rokta hai.
💡 required = un parameter names ka array jo model ko call valid mante hue pehle fill karna hi hoga; jo list me nahi hai wo optional hai.
Ek aur habit jo jaldi build karni chahiye: tools ki count kam rakho aur har ek ko narrowly scoped rakho. 3 clearly distinct tools me se choose karne wala model, 15 overlapping tools me se choose karne wale se kaafi kam mistakes karta hai. Agar do tools almost same kaam karte hain, unhe merge karo ya unki descriptions ko itna sharp likho ki dono ke covered cases clearly alag dikhein.
Standard definition (interview me bolo): A tool definition is a JSON Schema object (name, description, parameters, required) that describes a function to the model; the description fields are prompt engineering that directly drive tool selection and argument accuracy, so vague descriptions are the single biggest cause of wrong tool calls.
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
# BAD: vague description — model can't tell WHEN to use this
"description": "Gets weather info",
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string"}
},
"required": ["location"]
}
}
},
{
"type": "function",
"function": {
"name": "get_current_weather",
# GOOD: says what it does AND when (and when NOT) to use it
"description": (
"Get the CURRENT real-time weather for a specific city. "
"Use this ONLY when the user asks about today's or right-now "
"weather, not multi-day forecasts."
),
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "City name only, e.g. 'Mumbai' or 'Delhi' — no country needed"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "Temperature unit the user wants the reply in"
}
},
"required": ["city"]
}
}
}
]Project: AI Assistant with tools
Kya bana rahe hain: Ek AI Assistant with tools — ek assistant, chaar capabilities: ek calculator (LLMs arithmetic mein weak hote hain), ek weather lookup, ek notes save/read tool (yahi ek tool disk pe likhta hai), aur ek fake web search. Ye wo project hai jahaan is chapter ka har piece — tool definitions, dispatch table, bounded loop, parallel calls, error handling, aur human confirmation gate — ek saath wire hokar ek working system banta hai, chaar alag demos nahi.
Step 0 — Poora pipeline, end to end (pehle padho)
User ek sawaal poochta hai
-> assistant tool schemas dekhta hai, decide karta hai KI/KAUNSE tools fit karte hain
-> assistant message wapas aata hai tool_calls ke saath (shaayad multiple, shaayad zero)
-> code har ek ko dispatch table mein lookup karta hai
-> read-only tools turant chalte hain; save_note human confirmation ke liye rukta hai
-> har call try/except mein wrapped hai -- errors crash nahi, tool results ban jaate hain
-> SAME message ke independent tool_calls parallel chalte hain
-> har result {"role": "tool", ...} message ke roop mein append hota hai, tool_call_id se match
-> loop repeat hota hai jab tak model bina tool_calls ke reply na kare -- max_turns pe capped
Yaad rakho is chapter ka Rule 3: model kabhi kuch execute nahi karta. Upar "tool_calls wapas aate hain" ke baad har arrow tumhara likha hua code hai, LLM ka nahi.
Step 1 — Paanchon tools ko JSON Schema mein define karo
TOOL_SCHEMAS = [
{"type": "function", "function": {
"name": "calculate",
"description": "Evaluate a pure arithmetic expression, e.g. '18% of 2400' rewritten as '2400*0.18'. Use this for ANY math -- never do arithmetic yourself.",
"parameters": {"type": "object",
"properties": {"expression": {"type": "string", "description": "A valid Python arithmetic expression, digits and + - * / ** % ( ) only"}},
"required": ["expression"]}}},
{"type": "function", "function": {
"name": "save_note",
"description": "Save text to a new note file. This WRITES to disk -- only call it when the user clearly wants something saved.",
"parameters": {"type": "object",
"properties": {"title": {"type": "string"}, "content": {"type": "string"}},
"required": ["title", "content"]}}},
# get_weather({city}), web_search({query}), read_note({title}) follow
# the exact same {"type":"function","function":{name, description, parameters}} shape.
]
Ho ye raha hai: har description real kaam kar rahi hai -- "never do arithmetic yourself" model ko calculate ki taraf le jaati hai, apne dimaag mein percentage guess karne ki jagah; "this WRITES to disk" ek signal hai jo model apni reasoning mein repeat karta hai save_note call karne se pehle. Paanch tools, narrowly scoped, deliberate hai: ek chhata vague tool in paanch ke calls churana shuru kar deta.
Step 2 — Har tool ko plain function ke roop mein implement karo
Har tool ek string return karta hai (JSON-encoded), apni boundary se aage kabhi raise nahi karta, apne caller pe blindly trust nahi karta:
calculate(expression)— expression ko AST mein parse karta hai,eval()call karne se pehle sirf arithmetic nodes whitelist karta hai. Model-supplied string jaisa__import__('os').system(...)AST-walk step pe hi reject ho jaata hai, run hone se bahut pehle.get_weather(city)/web_search(query)— fixed dicts jo real APIs ki jagah stand-in hain (Bonus dekho); dono mein se kisi ko bhi real call se swap karo bina kuch aur touch kiye.read_note/save_note—_note_path()title ko sanitise karta hai aur har file konotes/folder tak confine karta hai. Yahi security topic ka sandboxed file path rule hai: model tumse kabhinotes/ke bahar likhwaa nahi sakta, chaahe wo koi bhi title invent kare.
💡 Least privilege in practice =
calculatesirf arithmetic kar sakta hai,save_notesirf ek folder ke andar likh sakta hai. Koi bhi tool apne narrow job se zyaada damage nahi kar sakta, chaahe model use misuse karne ki koshish kare.
Step 3 — Dispatch table
TOOLS = {"calculate": calculate, "get_weather": get_weather,
"web_search": web_search, "read_note": read_note, "save_note": save_note}
WRITE_TOOLS = {"save_note"}
Ho ye raha hai: TOOLS ek tool name (wo string jo model return karta hai) ko us real function se map karta hai jo use chalata hai -- ye "model ne calculate bola" ko seedha calculate(**args) mein badalta hai, lambi if/elif chain ke bina. WRITE_TOOLS ek doosra, chhota lookup hai jo flag karta hai kaunse names ko confirmation chahiye -- baad mein naya destructive tool add karna ek line hai, har jagah code change nahi.
Step 4 — Ek tool call chalao: JSON parse -> lookup -> confirm -> execute -> catch
def execute_tool_call(call):
name, call_id = call.function.name, call.id
try:
args = json.loads(call.function.arguments)
except json.JSONDecodeError:
return {"role": "tool", "tool_call_id": call_id,
"content": json.dumps({"error": "invalid JSON arguments from model"})}
if name not in TOOLS:
return {"role": "tool", "tool_call_id": call_id,
"content": json.dumps({"error": f"unknown tool '{name}'"})}
if name in WRITE_TOOLS and not confirm(name, args):
return {"role": "tool", "tool_call_id": call_id,
"content": json.dumps({"error": "user declined this action"})}
try:
result = TOOLS[name](**args)
except Exception as e:
result = json.dumps({"error": f"tool '{name}' raised: {e}"})
return {"role": "tool", "tool_call_id": call_id, "content": result}
Ho ye raha hai -- chaar failure modes, sab handle bina loop mein raise kiye: (1) malformed JSON arguments -- **args try hone se pehle hi catch ho jaata hai; (2) hallucinated tool name -- TOOLS ke against check hota hai; (3) write tool jise human "no" bolta hai -- Step 5 ka gate handle karta hai; (4) real function khud throw karta hai -- outer try/except catch karta hai. Har path ek normal {"role": "tool", ...} message return karta hai. Model ko hamesha kuch wapas milta hai aur wo react kar sakta hai -- maafi maang sakta hai, retry kar sakta hai, ya failure explain kar sakta hai -- poora assistant crash hone ki jagah.
Step 5 — Human confirmation gate (sirf write tool se pehle)
def confirm(name, args):
print(f"\nAssistant wants to run '{name}' with {args}")
return input("Allow? [y/N]: ").strip().lower() == "y"
Ho ye raha hai: ye ek CLI stand-in hai real UI ke "Confirm" button ke liye. save_note WRITE_TOOLS mein sirf ek tool hai, to yahi ek hai jo kabhi human ke liye rukta hai; baaki chaar read-only tools turant chalte hain. Sabko gate karna assistant ko unusable bana deta; kisi ko na gate karna model ko bina kisi agree kiye files likhne deta. Ye chapter ke security rule ka concrete roop hai: destructive/irreversible actions confirm karo, aur kuch nahi.
Step 6 — Parallel tool calls, sirf tab jab wo independent hon
with ThreadPoolExecutor(max_workers=len(message.tool_calls)) as pool:
results = list(pool.map(execute_tool_call, message.tool_calls))
Ho ye raha hai: message.tool_calls ek list hai -- ek assistant message ek saath kayi tools maang sakta hai, jaise do cities ka weather plus ek calculation. Model multiple calls sirf ONE message mein tab daalta hai jab unke answers ek doosre pe depend nahi karte, to is poori list ko thread pool se concurrently chalaana safe hai. Jab calls dependent HOTE HAIN (jaise "meri note lookup karo, fir usme jo number hai uska 10% calculate karo"), model unhe bundle NAHI karta -- pehle read_note maangta hai, us result ke messages mein land hone ka wait karta hai, tabhi agle loop iteration mein calculate maangta hai. Dependency loop structure se hi enforce hoti hai, tumhare haath se kuch code karne se nahi.
Step 7 — Bounded loop (max_turns, RULE 1)
def run_assistant(user_input, max_turns=6):
messages = [{"role": "system", "content": "You are a helpful assistant with tools."},
{"role": "user", "content": user_input}]
for _ in range(max_turns):
response = client.chat.completions.create(
model="gpt-4o-mini", messages=messages,
tools=TOOL_SCHEMAS, tool_choice="auto")
message = response.choices[0].message
messages.append(message.model_dump())
if not message.tool_calls:
return message.content
with ThreadPoolExecutor(max_workers=len(message.tool_calls)) as pool:
results = list(pool.map(execute_tool_call, message.tool_calls))
messages.extend(results)
return "Reached max_turns without a final answer -- stopping to avoid a runaway loop."
Ho ye raha hai: for _ in range(max_turns) ek bare while True: (isi chapter ke Rule 1 mein bataya gaya exact bug) ki jagah leta hai -- agar model hamesha tools maangta rahe, to function turn 6 pe graceful message return karta hai, hamesha loop aur bill hone ki jagah. Assistant ka apna message messages mein append hota hai kisi bhi tool results se pehle -- ye order galat karo to API request reject kar deta hai, kyunki ek tool message hamesha us tool_calls message ke baad aana chahiye jisne use maanga tha.
Step 8 — Ek poora request trace karo, message by message
User poochta hai: "18% tip on Rs.2400 kya hoga, Mumbai aur Delhi ka weather kya hai, aur tip amount ko 'tip-calc' naam se note mein save karo."
run_assistant ke har point pe messages:
- Start:
[{role: system, ...}, {role: user, content: "What's an 18% tip on Rs.2400, ..."}] - Turn 1 reply -- ek assistant message, teen tool_calls (calculate + get_weather x2, independent -> bundled):
{role: assistant, tool_calls: [{id:"call_1", function:{name:"calculate", arguments:'{"expression":"2400*0.18"}'}}, {id:"call_2", function:{name:"get_weather", arguments:'{"city":"Mumbai"}'}}, {id:"call_3", function:{name:"get_weather", arguments:'{"city":"Delhi"}'}}]}-- kisi tool ke chalne se pehle append hota hai. - Code teenon ko parallel chalata hai, teen tool messages append hote hain (har
tool_call_idapni call se match):{role:tool, tool_call_id:"call_1", content:'{"result":432.0}'},{role:tool, tool_call_id:"call_2", content:'{"temp_c":31,"condition":"Humid, partly cloudy"}'},{role:tool, tool_call_id:"call_3", content:'{"temp_c":38,"condition":"Hot, clear sky"}'}. - Turn 2 reply -- model results dekh chuka hai, ab write tool maangta hai:
{role: assistant, tool_calls: [{id:"call_4", function:{name:"save_note", arguments:'{"title":"tip-calc","content":"Tip on Rs.2400 at 18% = Rs.432"}'}}]}. - Confirmation gate fire hota hai --
save_noteWRITE_TOOLSmein hai,confirm()prompt karta hai, human "y" type karta hai:{role:tool, tool_call_id:"call_4", content:'{"saved":true,"title":"tip-calc"}'}append hota hai. - Turn 3 reply -- model ke paas sab kuch hai jo chahiye, koi tool_calls nahi:
{role: assistant, content: "The 18% tip on Rs.2400 is Rs.432 (total Rs.2832). Mumbai is 31C and humid, Delhi is 38C and clear. I've saved the tip calculation to your notes as 'tip-calc'."}.
message.tool_calls empty hai -> loop message.content return karta hai. Teen model calls, ek confirmation, zero exceptions loop se escape hui.
Bonus features (resume/recruiter differentiators)
- Real APIs —
get_weatherko OpenWeatherMap se,web_searchko Tavily/Serper se swap karo; dispatch table, loop, confirmation gate bilkul nahi badalte -- proof ki architecture provider-agnostic hai. - Per-tool timeouts — har
TOOLS[name](**args)call ko timeout mein wrap karo taaki ek slow tool poore turn ko hang na kar sake. - Audit log — har
(tool name, args, result, approved?)tuple ko log file mein append karo; ek real security review sabse pehle yahi maangta hai.
Recruiter is project mein kya dekhta hai
- Poora function-calling loop, sahi tarike se bounded — zyaadatar tutorials
while True:dikhaate hain; ye dikhaata hai wo kyun galat hai aur kaise fix karte hain. - Parallel execution safely kiya gaya — concurrent sirf tab jab model ne khud independence prove ki.
- Ek real human-in-the-loop safety gate — wo detail jo toy demo ko deploy-worthy cheez se alag karta hai.
- Defense in depth — sandboxed file paths, raw
evalki jagah whitelisted AST-based calculator, aur har tool failure ek recoverable message ban jaata hai, crash nahi.
Standard definition (interview me bolo): An AI assistant with tools exposes a small set of narrowly scoped functions to the model as JSON Schema, dispatches any resulting tool_calls through a lookup table with exception handling so every failure becomes a tool result rather than a crash, runs independent calls from the same turn in parallel, gates destructive actions behind explicit human confirmation, and loops -- turn by turn, with a hard max_turns cap -- until the model returns a final answer with no further tool_calls.
import ast
import json
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor
from openai import OpenAI
client = OpenAI()
NOTES_DIR = Path("notes")
NOTES_DIR.mkdir(exist_ok=True)
# ---- 1. Tool schemas the model sees ----
TOOL_SCHEMAS = [
{"type": "function", "function": {
"name": "calculate",
"description": "Evaluate a pure arithmetic expression. Use this for ANY math -- never do arithmetic yourself.",
"parameters": {"type": "object",
"properties": {"expression": {"type": "string", "description": "e.g. '2400*0.18'"}},
"required": ["expression"]}}},
{"type": "function", "function": {
"name": "get_weather",
"description": "Get current weather for a named city (small fixed set of Indian cities).",
"parameters": {"type": "object",
"properties": {"city": {"type": "string"}}, "required": ["city"]}}},
{"type": "function", "function": {
"name": "web_search",
"description": "Search the web for a fact not in your training data or this conversation.",
"parameters": {"type": "object",
"properties": {"query": {"type": "string"}}, "required": ["query"]}}},
{"type": "function", "function": {
"name": "read_note",
"description": "Read a previously saved note by its title.",
"parameters": {"type": "object",
"properties": {"title": {"type": "string"}}, "required": ["title"]}}},
{"type": "function", "function": {
"name": "save_note",
"description": "Save text to a new note file. This WRITES to disk -- only call when the user clearly wants something saved.",
"parameters": {"type": "object",
"properties": {"title": {"type": "string"}, "content": {"type": "string"}},
"required": ["title", "content"]}}},
]
# ---- 2. Fake data the tools read from ----
FAKE_WEATHER = {
"mumbai": {"temp_c": 31, "condition": "Humid, partly cloudy"},
"delhi": {"temp_c": 38, "condition": "Hot, clear sky"},
"bengaluru": {"temp_c": 24, "condition": "Light rain"},
}
FAKE_SEARCH = {
"python 3.13 release date": "Python 3.13 released October 7, 2024.",
}
# ---- 3. Tool implementations ----
def calculate(expression):
try:
node = ast.parse(expression, mode="eval")
allowed = (ast.Expression, ast.BinOp, ast.UnaryOp, ast.Constant,
ast.Add, ast.Sub, ast.Mult, ast.Div, ast.Pow, ast.USub, ast.Mod)
for n in ast.walk(node):
if not isinstance(n, allowed):
raise ValueError(f"disallowed token: {type(n).__name__}")
return json.dumps({"result": eval(compile(node, "<calc>", "eval"))})
except Exception as e:
return json.dumps({"error": f"could not evaluate '{expression}': {e}"})
def get_weather(city):
data = FAKE_WEATHER.get(city.strip().lower())
if not data:
return json.dumps({"error": f"no weather data for '{city}'"})
return json.dumps(data)
def web_search(query):
result = FAKE_SEARCH.get(query.strip().lower(), f"No indexed result for '{query}'.")
return json.dumps({"result": result})
def _note_path(title):
safe = "".join(c for c in title if c.isalnum() or c in " _-").strip()
return NOTES_DIR / f"{safe}.txt" if safe else None
def read_note(title):
path = _note_path(title)
if not path or not path.exists():
return json.dumps({"error": f"note '{title}' not found"})
return json.dumps({"content": path.read_text()})
def save_note(title, content):
path = _note_path(title)
if not path:
return json.dumps({"error": "invalid title"})
path.write_text(content)
return json.dumps({"saved": True, "title": title})
# ---- 4. Dispatch table + which tools need human confirmation ----
TOOLS = {
"calculate": calculate, "get_weather": get_weather,
"web_search": web_search, "read_note": read_note, "save_note": save_note,
}
WRITE_TOOLS = {"save_note"} # destructive/irreversible -> confirm first
def confirm(name, args):
print(f"\nAssistant wants to run '{name}' with {args}")
return input("Allow? [y/N]: ").strip().lower() == "y"
# ---- 5. Run one tool call safely, always return a tool message ----
def execute_tool_call(call):
name, call_id = call.function.name, call.id
try:
args = json.loads(call.function.arguments)
except json.JSONDecodeError:
return {"role": "tool", "tool_call_id": call_id,
"content": json.dumps({"error": "invalid JSON arguments from model"})}
if name not in TOOLS:
return {"role": "tool", "tool_call_id": call_id,
"content": json.dumps({"error": f"unknown tool '{name}'"})}
if name in WRITE_TOOLS and not confirm(name, args):
return {"role": "tool", "tool_call_id": call_id,
"content": json.dumps({"error": "user declined this action"})}
try:
result = TOOLS[name](**args)
except Exception as e:
result = json.dumps({"error": f"tool '{name}' raised: {e}"})
return {"role": "tool", "tool_call_id": call_id, "content": result}
# ---- 6. The bounded loop: multi-turn, parallel-safe, max_turns capped ----
def run_assistant(user_input, max_turns=6):
messages = [
{"role": "system", "content": "You are a helpful assistant with tools."},
{"role": "user", "content": user_input},
]
for _ in range(max_turns):
response = client.chat.completions.create(
model="gpt-4o-mini", messages=messages,
tools=TOOL_SCHEMAS, tool_choice="auto",
)
message = response.choices[0].message
messages.append(message.model_dump())
if not message.tool_calls:
return message.content # no more tools requested -> final answer
with ThreadPoolExecutor(max_workers=len(message.tool_calls)) as pool:
results = list(pool.map(execute_tool_call, message.tool_calls))
messages.extend(results)
return "Reached max_turns without a final answer -- stopping to avoid a runaway loop."Function Calling aur Toolsinterview questions & answers
10 sample questions below — 131+ in the full bank inside.
Tool result message me `tool_call_id` us tool call se match kyun karna zaroori hai jo wo answer kar raha hai?
Tool_call_id wo link hai ek tool call aur uske result ke beech — agar match na ho toh model ko pata nahi chalega kaunsa result kaunsi call ke liye hai, aur conversation break ho jayega.
In simple terms: Imagine ek saath teen logo se question poocho aur sab bass answer de de — bina names ya numbers ke tum nahi jaan paoge kaun kya jawab diya. Tool_call_id ek numbering system hai: 'Question 1' → 'Answer 1'. Jaise agar model `tool_call_id: "call_42"` ke saath tool call kare toh result `{"tool_call_id": "call_42", "content": "..."}` hona chahiye.
Audit logging kya hai aur tool security ke liye kyon important hai?
Audit logging ka matlab har tool call record karna — uska naam, arguments, timestamp, aur outcome. Agar kuch galat ho (data accidentally delete hua, galat email bhej gaya), toh tere paas complete trail hai trace karne ke liye kya hua aur debug karne ke liye.
In simple terms: Ye tere tools ke liye security camera jaisa hai. Jaise tum log karo `{"tool": "delete_record", "args": {"id": 42}, "timestamp": "2025-02-10T14:30:00Z", "outcome": "deleted"}`. Baad me agar row 42 delete nahi hona chahiye tha, toh tujhe pata hai exactly kab aur kya ne use delete kiya, toh tum samajh sakta hai kya galti tha ya user ne request ki thi.
Ek sentence me bolo, LLMs ke context me function calling kya hai?
Function calling tumhe LLM ko available functions JSON Schema ke tor pe describe karne deta hai; agar request match baithta hai, model ek tool call return karta hai (function name + JSON arguments) bina execute kiye — tumhara code real function chalata hai aur result wapas bhejta hai.
In simple terms: Ise restaurant me order dene jaisa samjho — tum (user) order karte ho, waiter (model) likh ke kitchen bhejta hai, chef (tumhara code) actually pakata hai, aur waiter result le aa ta hai. Waiter kabhi pakate nahi. Jaise ek weather tool call `{"name": "get_weather", "arguments": {"city": "Delhi"}}` return karta hai — model ne weather check hi nahi ki, bas manga.
Kya LLM kabhi actually ek function execute karta ya database query karta hai?
Nahi — model sirf ek tool call return karta hai (function name + arguments). Tumhara code us call ko padhe ta hai, decide karta hai execute karna hai ya nahi, real function ya query chalata hai, aur phir result model ko ek tool message ke tor pe bhejta hai.
In simple terms: Model ek smart librarian ke jaisa hai — wo kehta hai 'shelf 3 se book X le aa', lekin khud nahi lete. Tum lete ho, padhte ho, aur batate ho kya mila. Jaise model `{"tool_call_id": "call_123", "function": "query_db"}` return karta hai lekin tumhare database ko touch nahi karta — tum karte ho.
Tool ki exception ko catch karke string me kyun return karna padta hai, uske bajaye code ko raise hone de?
Kyunki tools bahar ki duniya (APIs, databases, networks) ke saath interact karte hain jo tera code ke control se bahar reasons se fail ho sakte hain — network timeouts, server down, DB connection tootna. Tu in failures se code ke through bach nahi sakta, isliye catch karke string me return karna conversation ko zinda rakhta hai aur model ko react karne deta hai, crash hone se bachata hai.
In simple terms: Goal perfection nahi, resilience hai. Weather API 2 AM ko unreachable hona tera code ka bug nahi hai — reality hai. Tu bugs fix kar sakta hai, par infrastructure failures prevent nahi kar sakta. Jaise: `except ConnectionError as e: return json.dumps({"error": f"API down: {e}"})` — model ko pata chal gaya weather service unavailable hai aur woh user ko bata sakta hai ya alag approach try kar sakta hai, poora request die karne se pehle.
Tool-call loop ke andar jab koi tool exception throw kare toh kya hota hai?
Uncaught exception loop se bahar nikalta hai aur pure request ko crash kar deta hai — user ko answer ke jagah error stack trace milta hai. Isko rokne ke liye, tool jo bhi exception raise kare use catch karna padta hai aur ek descriptive error string tool result ke roop me return karna padta hai, taaki model use padh ke gracefully react kar sake.
In simple terms: Tool ko delivery driver samjho. Agar driver apna van crash kar de (exception), to poori delivery chain fail ho jaati hai aur customer ko kuch nahi pata. Iske bajaye driver dispatch ko problem bata de: 'Road band hai, doosra rasta try kar raha hoon' — ab dispatch reassign kar sakta hai ya customer ko inform kar sakta hai. Jaise: `try: result = get_weather(city) except Exception as e: return json.dumps({"error": str(e)})` — error normal conversation flow ka hissa ban jaata hai, usko khatam nahi karta.
Idempotent aur non-idempotent operation me kya farq hai?
Idempotent operation ek hi effect deta hai chahe tu use ek baar kare ya kai baar: ek value padhna, database search karna, weather data fetch karna. Non-idempotent operation kuch badal deta hai: payment bhej dena, email bhej dena, file delete karna. Idempotent operations ko retry karna safe hai; non-idempotent ko pehle check kiye bina retry karne se duplicates ho sakte hain.
In simple terms: Stamp sochو — agar paper par ek baar ya do baar stamp karo, same stamp shape aata hai (idempotent). Lekin agar cheque par ek baar ya do baar signature karo, bank do baar cash kar dega (non-idempotent). Jaise: `get_weather()` idempotent hai, isliye retry safe hai. `send_email()` non-idempotent hai, isliye retry se pehle check karna padta hai ki already bheja toh nahi.
Agar model ek aisa tool call kare jo exist nahi karta, ya missing ya invalid arguments de?
Model hallucinate kar sakta hai tool names ya argument values kyunki wo text generate kar raha hai, validation nahi kar raha. Real function call karne se pehle, tu validate karna padta hai ki function exist karta hai, required arguments present hain, aur un ka type sahi hai. Agar validation fail ho, error message model ko return karo bina real function ko call kiye.
In simple terms: Socho warehouse robot ko bolna item fetch karo shelf 'Atlantis' se (jo exist nahi karta) ya quantity empty string set karke. Tu robot ko blindly nahi bhejega — pehle shelf number aur quantity check karega. Jaise: `if not city or not isinstance(city, str): return "error: city is required and must be text"` — tu bad input ko *pehle* catch karta hai tool chalne se pehle, isliye model ko clear error dikhta hai aur woh clarification maang sakta hai.
Har tool call par timeout add karna chahiye? Kyun?
Haan, bilkul. Timeout ke bina, agar koi tool hang ho jaaye (slow API, dead connection, infinite loop), poora tool-call loop hamesha ke liye wait karega — model ko response nahi milega aur user ka request stuck reh jaayega. Ek timeout (jaise 10 second) ensure karta hai ki ek slow tool poore system ko freeze na kare.
In simple terms: Customer-service number par call karo aur hold par lag jaao. Agar timeout nahi hai, tu hamesha music sunta rahega aur kisi aur ko reach nahi kar paayega. Timeout ke saath, 30 second baad tu line chhod deta hai aur doosra department try karta hai. Jaise: `future.result(timeout=10)` — agar tool 10 second se zyada le, `TimeoutError` raise hota hai, tu use catch karke error string model ko return karta hai.
`tool_choice` ke main options kya hain aur har ek kab use karte ho?
Char options: `"auto"` (model decide karta, default), `"none"` (kabhi tool call mat karo), `"required"` (kuch na kuch call karna zaroori), ya ek specific tool name (sirf wo tool force karo). Normally `"auto"`, tool use block karne ke liye `"none"`, action guarantee karne ke liye `"required"`, aur structured JSON extract karne ke liye specific names use karo.
In simple terms: Tool_choice ko traffic light samjho: green (auto) matlab model kahi bhi ja sakta hai, red (none) sab block karta hai, yellow (required) ek move force karta hai, aur specific street name ek direction force karta hai. Jaise resume ko JSON me extract karne ke liye schema tool ko force karo `tool_choice: "structured_resume"` se.
121+ more Function Calling aur Tools questions inside
Create a free account to read the full question bank, learn every topic, and practise with an AI mock interview.
Unlock all questions — freeReady to practise Function Calling aur Tools?
Unlock every topic free, then face an AI interviewer that asks follow-ups and grades your answers.