Interview Questions — Function Calling and Tool Use¶
Q1: How does function calling differ from JSON mode, and when should you use each?
Show answer
JSON mode (response_format={"type": "json_object"}) guarantees that the model's output is valid JSON, but does not guarantee the structure, field names, or types inside that JSON. The model can return any JSON it decides to generate.
Function calling defines a schema (JSON Schema) and guarantees the model's output conforms to it: correct field names, correct types, enum constraints respected, required fields present. The model's output is a structured tool call, not free-form JSON.
Use JSON mode when: - You need valid JSON but the exact schema is flexible or unknown in advance - You're collecting open-ended data where structure varies per response - Latency is critical and you want to avoid the schema overhead
Use function calling when:
- The output feeds code that expects specific field names and types
- You have enum constraints (e.g., status must be one of: paid/pending/overdue)
- You need reliable extraction across thousands of documents
- You're building an agent that dispatches to real functions
Q2: Walk through the full request-response cycle for a function call that requires executing a tool and returning the result.
Show answer
- Define tools: Pass a list of tool definitions (JSON Schema) in the
toolsparameter - First API call: Send user message + tools; model responds with
finish_reason="tool_calls"and atool_callslist - Parse call: Extract
function.nameandjson.loads(function.arguments)from each tool call - Execute: Call the actual function with the parsed arguments
- Build results: Create one
{"role": "tool", "tool_call_id": ..., "content": json.dumps(result)}message per tool call - Second API call: Send original messages + assistant's tool_calls message + all tool results; model responds with
finish_reason="stop"and generates the final answer
Critical constraint: every tool_call_id in the assistant's response must have a corresponding tool role message. Missing one causes an API error.
Q3: What is the key API difference between OpenAI tool calls and Anthropic tool use?
Show answer
Several differences:
| Aspect | OpenAI | Anthropic |
|---|---|---|
| Schema key | parameters (JSON Schema) |
input_schema (JSON Schema) |
| Stop signal | finish_reason == "tool_calls" |
stop_reason == "tool_use" |
| Arguments format | JSON string (must json.loads()) |
Python dict (already parsed) |
| Tool call location | message.tool_calls (list) |
response.content (mixed blocks) |
| Result message role | "tool" role with tool_call_id |
"user" role with tool_result content blocks |
| Force tool | tool_choice={"type": "function", "name": "..."} |
tool_choice={"type": "tool", "name": "..."} |
The conceptual loop is identical: define → call → execute → return result. Only the Python API surface differs.
Q4: When should you disable parallel tool calls with parallel_tool_calls=False?
Show answer
Disable parallel tool calls when tool outputs have dependencies — when tool call B needs to use the result of tool call A.
Example: Step 1 calls search_products(query) → returns product IDs. Step 2 calls get_product_details(product_id) using an ID from step 1's result. These must be sequential.
Also disable when tools have ordered side effects: writing to a database, sending emails, or any stateful operation where order matters.
Keep parallel calls enabled (the default) for independent lookups: weather in multiple cities, stock prices for multiple tickers, extracting entities from multiple documents. These have no dependency and benefit from the reduced latency (all results arrive in one round trip instead of N sequential round trips).
Q5: What does tool_choice="required" do, and when would you use it?
Show answer
tool_choice="required" forces the model to call at least one tool in every response. It cannot generate a plain text answer — it must produce a tool call.
Use it for extraction-only pipelines: When you're using tool calling purely as a structured extraction mechanism (not for real side effects), you don't want the model to sometimes answer with plain text for "simple" inputs. With required, you always get a tool call back — your code can skip the round 2 entirely.
Don't use it in conversational agents where some user messages don't require tool use (e.g., "thank you" or "what did you just say?"). Forcing a tool call on conversational turns produces awkward behavior.
Anthropic's equivalent: tool_choice={"type": "any"} (requires a tool call) or {"type": "tool", "name": "specific_tool"} (requires a specific tool).
Q6: How do you use a Pydantic model as a tool schema?
Show answer
Pydantic models can generate JSON Schema automatically via .model_json_schema(). This schema can be passed directly as the parameters field in an OpenAI tool definition:
from pydantic import BaseModel, Field
from typing import Optional
class ExtractedEntity(BaseModel):
name: str
entity_type: str
confidence: float = Field(ge=0.0, le=1.0)
context: Optional[str] = None
tool = {
"type": "function",
"function": {
"name": "extract_entity",
"description": "Extract a named entity from text.",
"parameters": ExtractedEntity.model_json_schema()
}
}
After the tool call, parse the arguments back into the Pydantic model for type validation:
raw = json.loads(tool_call.function.arguments)
entity = ExtractedEntity(**raw) # Pydantic validates types
LangChain's with_structured_output(MyModel) does all of this automatically — schema generation, tool call, parsing, and validation — in one line.
Q7: What can go wrong with function calling in production, and how do you handle it?
Show answer
Common failure modes:
-
Model hallucinates a function name — requests a function that doesn't exist. Fix: validate
function.nameagainst yourFUNCTION_MAPbefore calling; return an error tool result if not found. -
Malformed arguments —
json.loads(function.arguments)raisesJSONDecodeError. Rare with modern models, but wrap in try/except and retry. -
Type mismatch — model passes
"49.99"(string) for aprice: floatfield. Fix: use Pydantic validation when parsing; cast where possible. -
Missing required arguments — model omits a required field. Fix: Pydantic raises
ValidationError; catch it and retry with a correction message. -
Infinite tool loop — model keeps calling tools without reaching
finish_reason="stop". Fix: add amax_roundscounter; break after 5–10 rounds. -
Missing tool result for a tool_call_id — API error if any tool call is unmatched. Fix: always iterate
choice.message.tool_callscompletely; never break early.