Agents & Tools Interview Prep

Course Content

Agents & Tools Interview Prep

6 sections · 40 lessons

How does function calling work in LLM systems from start to finish?


The messages in one flight searchuser: flightsDEL to GOI, 20 Decassistant:tool_use toolu_01Ayour code runssearch_flightsuser:tool_result toolu_01Aassistant:fares from 5,890nullstop_reason tool_useonly code executessame id,is_error if failedOpenAI names the same steps function_call and function_call_output, linked by call_id.
The id is the thread that ties each result to its request, and it is why the model's call must stay in the history.

What you need to know

  1. Declare — send tools (name, description, input_schema) with the messages.
  2. Decide — the model returns text, one or more tool_use blocks, or both.
  3. Execute — your code validates the arguments and runs the real function.
  4. Return — a user message with a tool_result for every tool_use id.
  5. Continue — call the model again; repeat until stop_reason is end_turn.

The request

Python
tools = [{    "name": "search_flights",    "description": "Search one-way flights. Use when the user wants flight options or fares.",    "input_schema": {        "type": "object",        "properties": {            "origin": {"type": "string", "description": "IATA code, e.g. DEL"},            "destination": {"type": "string", "description": "IATA code, e.g. GOI"},            "date": {"type": "string", "format": "date", "description": "YYYY-MM-DD"},            "adults": {"type": "integer", "minimum": 1, "maximum": 9},        },        "required": ["origin", "destination", "date", "adults"],    },}]messages = [{"role": "user", "content": "Flights Delhi to Goa on 20 Dec for 2 people?"}]resp = llm.call(messages, tools)   # our wrapper around client.messages.create

The model's reply

JSON
{  "role": "assistant",  "stop_reason": "tool_use",  "content": [    {"type": "text", "text": "I'll search flights for that date."},    {"type": "tool_use", "id": "toolu_01A",     "name": "search_flights",     "input": {"origin": "DEL", "destination": "GOI", "date": "2026-12-20", "adults": 2}}  ]}

In the Anthropic API, input is already a parsed JSON object.

Sending the result back

Python
messages.append({"role": "assistant", "content": resp.content})messages.append({"role": "user", "content": [{    "type": "tool_result",    "tool_use_id": "toolu_01A",    "content": '[{"flight": "6E-2134", "dep": "06:10", "fare_inr": 5890}, ...]',}]})resp = llm.call(messages, tools)   # now the model can answer with real fares

Rules that trip people up:

  • The tool_result goes in a user message, and it must directly follow the assistant message that contained the tool_use.
  • If the model made several calls in one turn (parallel tool use), return all results in one user message, one block per id.
  • If a tool failed, still return a result: {"type": "tool_result", "tool_use_id": "...", "is_error": true, "content": "Airline API timed out after 10 s; try again or search a different date."}. The model can then recover or explain.

The OpenAI equivalent

StepAnthropic Messages APIOpenAI Responses APIOpenAI Chat Completions
Declaretools[].input_schematools[] with type: "function", parameterstools[].function.parameters
Model's calltool_use block, input objectfunction_call item, arguments JSON stringtool_calls[], function.arguments string
Signalstop_reason: "tool_use"output contains function_call itemsfinish_reason: "tool_calls"
Resulttool_result block with tool_use_idfunction_call_output item with call_idmessage with role: "tool", tool_call_id

The concepts are identical; only field names differ. OpenAI returns arguments as a string, so you must json.loads them and handle a parse error.

A real-life example

A travel-booking assistant receives: "Find me a flight Delhi to Goa on 20 December and a hotel in Candolim for 3 nights, two adults."

Turn 1: the model returns two tool_use blocks in one reply — search_flights(...) and search_hotels(area="Candolim", check_in="2026-12-20", nights=3, adults=2). The harness runs both at the same time (1.8 s instead of 3.5 s) and returns both tool_result blocks in one message.

The hotel API responds with HTTP 503. The harness does not crash and does not silently drop it; it returns is_error: true with "Hotel search is temporarily unavailable." Turn 2: the model shows the three cheapest flights (from ₹5,890) and says hotel search failed and it will retry. It calls search_hotels again, which succeeds. Turn 3: a final answer with flights and four hotels, stop_reason: "end_turn".

Three model calls, three tool executions, and every step is visible in the logs.

Follow-up questions to expect

  • "Where does the model's text before the tool call go?" — It is part of the assistant content. Keep it in history; you may show it to the user as a progress message.
  • "What happens if you forget a tool_result for one of the ids?" — The Anthropic API rejects the next request, because every tool_use must be answered.
  • "Can a tool return an image or a document?" — Yes. tool_result content can be a list of blocks, including text and images.